Here are some of my thoughts on SPI, kernel version 2.6.29.
SPI subsystemIn the spi subsystem, the spi device is described by struct spi_dev, and its driver is described by struct spi_driver. The spi bus device is described with struct spi_master. In addition, there are two important global variables:
Struct bus_type spi_bus_type = {
.name = "spi",
.dev_attrs = spi_dev_attrs,
.match = spi_match_device,
.uevent = spi_uevent,
.suspend = spi_suspend,
.resume = spi_resume,
};
staTIc struct class spi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.dev_release = spi_master_release,
};
Spi_bus_type corresponds to the spi bus in sys, and the Linux device model has a detailed introduction to this structure.
All spi_master spi bus belongs to spi_master_class, which means that it is a virtual device, its parent device may be a physical device, such as platform_device, etc., s3c2410 is the case.
The driver for the SPI device is registered into the SPI subsystem via spi_register_driver with the drive type struct spi_driver. A typical example is at25.c.
staTIc struct spi_driver at25_driver = {
.driver = {
.name = "at25",
.owner = THIS_MODULE,
},
.probe = at25_probe,
.remove = __devexit_p(at25_remove),
};
Because the spi bus does not support the automatic detection of the SPI device, it is generally not detected in the probe function of the spi, but the initialization of the spi device is performed.
The spi driver can call the following functions to transfer spi:
staTIc inline int spi_write(struct spi_device *spi, const u8 *buf, size_t len);
staTIc inline int spi_read(struct spi_device *spi, u8 *buf, size_t len);
Extern int spi_write_then_read(struct spi_device *spi, const u8 *txbuf, unsigned n_tx,
U8 *rxbuf, unsigned n_rx);
Static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd);
Static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd);
Since the spi device cannot be dynamically scanned by the spi bus, the spi subsystem uses another method of statically registering the spi device to the system via the spi_register_board_info function.
Int __init spi_register_board_info(struct spi_board_info const *info, unsigned n);
Struct spi_board_info {
Char modalias[32]; // device name
Const void *platform_data; // private data, will be set to spi_device.dev.platform_data
Void *controller_data; // private data, will be set to spi_device.controller_data
Int irq; // interrupt number
U32 max_speed_hz; // maximum rate
U16 bus_num; // used to associate spi_master
U16 chip_select; // related to chip selection
U8 mode; // spi_device.mode
};
In the platform file, you can define the structure of struct spi_board_info, then save these structures through the spi_register_board_info function, and finally create a spi device (spi_new_device) based on these saved structures in the scan_boardinfo function.
Spi_new_device is used to register the spi device, which is divided into two steps, the first is spi_alloc_device, then the spi_add_device.
Struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip)
Spi_dev* pdev = spi_alloc(master);
Proxy-"chip_select = chip-"chip_select;
Proxy-"max_speed_hz = chip-"max_speed_hz;
Proxy-"mode = chip-"mode;
Proxy-"irq = chip-"irq;
Strlcpy(proxy-"modalias, chip-"modalias, sizeof(proxy-"modalias));
Proxy-"dev.platform_data = (void *) chip-"platform_data;
Proxy-"controller_data = chip-"controller_data;
Proxy-"controller_state = NULL;
Spi_add_device(proxy);
Struct spi_device *spi_alloc_device(struct spi_master *master)
Struct device * dev = master-"dev.parent;
Struct spi_dev * spi = kzalloc(sizeof *spi, GFP_KERNEL);
Spi-"master = master;
Spi-"dev.parent = dev;
Spi-"dev.bus = &spi_bus_type;
Spi-"dev.release = spidev_release;
Device_initialize(&spi-"dev);
Here the parent device of spi_dev is designated as the parent of the master, and the master is the spi bus device, which has a class and is a virtual device. That is, the spi device and its corresponding bus device have the same parent device, which is generally a physical device.
Int spi_add_device(struct spi_device *spi)
Snprintf(spi-"dev.bus_id, sizeof spi-"dev.bus_id, "%s.%u", spi-"master-"dev.bus_id,
Spi-"chip_select);
Status = spi-"master-"setup(spi);
Status = device_add(&spi-"dev);
Spi busStruct spi_master {
Struct device dev;
S16 bus_num; // Bus number, if there are multiple spi buses on the board, it is distinguished by this field; in addition, there is bus_num in spi_dev, and spi_dev finds the bus to which it belongs through this field.
U16 num_chipselect; // Chip selection, if a spi bus has multiple devices,
/* setup mode and clock, etc (spi driver may call many times) */
Int (*setup)(struct spi_device *spi);
Int (*transfer)(struct spi_device *spi, struct spi_message *mesg);
/* called on release() to free memory provided by spi_master */
Void (*cleanup)(struct spi_device *spi);
};
Register spi busStruct spi_master *spi_alloc_master(struct device *dev, unsigned size);
Int spi_register_master(struct spi_master *master);
Scan_boardinfo(master);
Scan_boardinfo is called in spi_register_master. In scan_boardinfo, the previously saved boardinfo is scanned to see if the bus_num in the newly registered master matches the bus_num in boardinfo. If it matches, spi_new_device is called to create the spi device and registered in the spi subsystem.
Setup function
The setup function does some initialization work. For example, according to the rate of the spi device, the bit transfer timer of the device paster; set the spi transfer type; and so on.
In the spi_add_device function, the setup function is called first, and then device_add is called. This is because the device_add will call the driver's probe function, and the probe function may do IO operations on the spi device. So the spi subsystem first calls setup to prepare for possible IO operations.
However, in the code, the setup function seems to be called only in this place. The configuration work is also done when switching the spi device during the specific transmission process, but the configuration work here is determined by the implementation code of the specific transmission. You can look at the function bitbang_work in spi_bitbang.c.
Cleanup function
The cleanup function is called in the spidev_release function, and spidev_release is registered as the release function of spi dev.
Transfer function
The transfer function is used for IO transfer of spi. However, the transfer function generally does not perform the actual transfer operation, but puts the content to be transferred into a queue, and then calls a mechanism like the bottom half for real transmission. This is because the spi bus typically has multiple spi devices, and access between spi devices may be concurrent. If the transfer is implemented directly in the transfer function, then a race will occur and the spi devices will interfere with each other.
Therefore, the real spi transfer is related to the implementation of the specific spi controller, which is not covered in the spi framework code. Chip selections like spi devices, clock adjustments based on specific devices, etc. are all called in the code that implements the transfer.
The SPI transfer commands are defined by the structure spi_message. The device program calls the transfer function to hand spi_message to the spi bus driver, and the bus driver then passes the message to the bottom half to serialize the transmission.
Struct spi_message {
Struct list_head transfers;
Struct spi_device *spi;
Unsigned is_dma_mapped:1;
Void (*complete)(void *context);
Void *context;
Unsigned actual_length;
Int status;
Struct list_head queue;
Void *state;
};
In spi_message, there is a transfers queue through which the spi_transfer structure is attached to spi_message. A spi_message represents a single transfer session and spi_transfer represents a single IO operation. For example, some spi devices need to be read and written first, then the read and write process is a spi session, which includes two transferes, one defining the parameters of the write operation and the other defining the parameters of the read operation.
Spidev.c
If you don't want to write a driver for your SPI device, you can use the driver provided by Linux's own spidev.c. To use the driver of spidev.c, just set the device name to spidev when registering the device. Spidev.c will automatically create a device node for each matching SPI device in the device directory with the node name "spi%d". The user program can then control the SPI device through the generic interface of the character device.
It should be noted that the device created by spidev belongs to the virtual device in the device model, and its class is spidev_class. Its parent device is the spi device defined in boardinfo.
Pharmaceuticals,2-Methyl- Propanoic Acid Monohydrate Price,2-Methyl- Propanoic Acid Monohydrate Free Sample,Pure 2-Methyl- Propanoic Acid Monohydrate
Zhejiang Wild Wind Pharmaceutical Co., Ltd. , https://www.wild-windchem.com