===============================
本文系本站原创,欢迎转载!
转载请注明出处:http://blog.csdn.net/gdt_a20
===============================
相比于前面介绍的i2c子系统,spi子系统相对简单,和i2c的结构也很相似,这里主要介绍一下平台无关部分的代码。先概括的说一下,spi总线或者说
是spi控制器用结构体struct
spi_master来表述,而一般不会明显的主动实现这个结构而是借助板级的一些信息结构,利用spi的模型填充,存在板级信息的一条链表
board_list,上面挂接着板级spi设备的描述信息,其挂接的结构是struct
boardinfo,这个结构内部嵌入了具体的板级spi设备所需信息结构struct
spi_board_info,对于要操控的spi设备,用struct spidev_data来描述,内嵌了具体设备信息结构struct
spi_device,并通过struct
spi_device->device_entry成员挂接到全局spi设备链表device_list,结构struct
spi_device就是根据前面board_list上所挂的信息填充的,而driver端比较简单,用struct
spi_driver来描述,一会儿会看到该结构和标准的platform非常相似,总括的说下一般编写流程:对于soc,spi控制器一般注册成
platform,当driver匹配后,会根据platform_device等信息构造出spi_master,一般发生在driver的probe
函数,并且注册该master,在master的注册过程中会去遍历board_list找到bus号相同的spi设备信息,并实例化它,好了先就说这么
多,下面先看一下具体的数据结构。
一、spi相关的数据结构
先看一下spi设备的板级信息填充的结构:
- struct boardinfo {
- struct list_head list;
- unsigned n_board_info;
- struct spi_board_info board_info[0];
- };
-
- struct spi_board_info {
-
-
-
-
-
-
-
- char modalias[SPI_NAME_SIZE];
- const void *platform_data;
- void *controller_data;
- int irq;
-
- u32 max_speed_hz;
-
-
-
-
-
-
- u16 bus_num;
- u16 chip_select;
-
-
-
- u8 mode;
-
-
-
-
-
- };
利用boardinfo->list成员会将自身挂接到全局的board_list链表上。
再来看一下spi控制器的表述结构:
- struct spi_master {
- struct device dev;
-
-
-
-
-
-
- s16 bus_num;
-
-
-
- u16 num_chipselect;
-
-
-
- u16 dma_alignment;
-
- u16 mode_bits;
-
- u16 flags;
- #define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
- #define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */
- #define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */
-
- spinlock_t bus_lock_spinlock;
- struct mutex bus_lock_mutex;
-
- bool bus_lock_flag;
-
-
-
-
-
-
- int (*setup)(struct spi_device *spi);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- int (*transfer)(struct spi_device *spi,
- struct spi_message *mesg);
-
- void (*cleanup)(struct spi_device *spi);
- };
而对于要操控的spi总线上的设备,其表述结构为:
- struct spi_device {
- struct device dev;
- struct spi_master *master;
- u32 max_speed_hz;
- u8 chip_select;
- u8 mode;
- #define SPI_CPHA 0x01 /* clock phase */
- #define SPI_CPOL 0x02 /* clock polarity */
- #define SPI_MODE_0 (0|0) /* (original MicroWire) */
- #define SPI_MODE_1 (0|SPI_CPHA)
- #define SPI_MODE_2 (SPI_CPOL|0)
- #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
- #define SPI_CS_HIGH 0x04 /* chipselect active high? */
- #define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
- #define SPI_3WIRE 0x10 /* SI/SO signals shared */
- #define SPI_LOOP 0x20 /* loopback mode */
- #define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
- #define SPI_READY 0x80 /* slave pulls low to pause */
- u8 bits_per_word;
- int irq;
- void *controller_state;
- void *controller_data;
- char modalias[SPI_NAME_SIZE];
-
-
-
-
-
-
-
-
-
- };
再看一下对于spi设备结构更高层次的表述结构:
- struct spidev_data {
- dev_t devt;
- spinlock_t spi_lock;
- struct spi_device *spi;
- struct list_head device_entry;
-
- struct mutex buf_lock;
- unsigned users;
- u8 *buffer;
- };
而driver端的表述结构struct spi_driver,具体如下:
- struct spi_driver {
- const struct spi_device_id *id_table;
- int (*probe)(struct spi_device *spi);
- int (*remove)(struct spi_device *spi);
- void (*shutdown)(struct spi_device *spi);
- int (*suspend)(struct spi_device *spi, pm_message_t mesg);
- int (*resume)(struct spi_device *spi);
- struct device_driver driver;
- };
是不是很标准?哈,好了,关于相关的数据结构就先介绍这么多。
二、spi核心代码的初始化分析
首先看一下spi总线的注册代码很简单,位于driver/spi/spi.c下:
- static int __init spi_init(void)
- {
- int status;
-
-
-
- buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
- if (!buf) {
- status = -ENOMEM;
- goto err0;
- }
-
-
- status = bus_register(&spi_bus_type);
- if (status < 0)
- goto err1;
-
-
-
- status = class_register(&spi_master_class);
- if (status < 0)
- goto err2;
- return 0;
- err2:
- bus_unregister(&spi_bus_type);
- err1:
- kfree(buf);
- buf = NULL;
- err0:
- return status;
- }
-
- 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,
- };
-
- static int spi_match_device(struct device *dev, struct device_driver *drv)
- {
- const struct spi_device *spi = to_spi_device(dev);
- const struct spi_driver *sdrv = to_spi_driver(drv);
-
-
-
- if (of_driver_match_device(dev, drv))
- return 1;
-
-
- if (sdrv->id_table)
- return !!spi_match_id(sdrv->id_table, spi);
-
-
- return strcmp(spi->modalias, drv->name) == 0;
- }
再看一下driver的注册,spi_register_driver函数:
- int spi_register_driver(struct spi_driver *sdrv)
- {
-
-
-
- sdrv->driver.bus = &spi_bus_type;
- if (sdrv->probe)
- sdrv->driver.probe = spi_drv_probe;
- if (sdrv->remove)
- sdrv->driver.remove = spi_drv_remove;
- if (sdrv->shutdown)
- sdrv->driver.shutdown = spi_drv_shutdown;
-
-
-
- return driver_register(&sdrv->driver);
- }
还有个板级信息的添加函数:
- int __init
- spi_register_board_info(struct spi_board_info const *info, unsigned n)
- {
- struct boardinfo *bi;
- bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);
- if (!bi)
- return -ENOMEM;
- bi->n_board_info = n;
- memcpy(bi->board_info, info, n * sizeof *info);
- mutex_lock(&board_lock);
- list_add_tail(&bi->list, &board_list);
- mutex_unlock(&board_lock);
- return 0;
- }
函数很简单,利用定义的spi_board_info信息,填充了boardinfo结构,并挂到board_list链表。
最后来看一下一个重量级函数,master的注册函数:
- int spi_register_master(struct spi_master *master)
- {
- static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
- struct device *dev = master->dev.parent;
- int status = -ENODEV;
- int dynamic = 0;
-
- if (!dev)
- return -ENODEV;
-
-
-
-
-
- if (master->num_chipselect == 0)
- return -EINVAL;
-
-
- if (master->bus_num < 0) {
-
-
-
-
- master->bus_num = atomic_dec_return(&dyn_bus_id);
- dynamic = 1;
- }
- spin_lock_init(&master->bus_lock_spinlock);
- mutex_init(&master->bus_lock_mutex);
- master->bus_lock_flag = 0;
-
-
-
-
-
- dev_set_name(&master->dev, "spi%u", master->bus_num);
-
-
- status = device_add(&master->dev);
- if (status < 0)
- goto done;
- dev_dbg(dev, "registered master %s%s/n", dev_name(&master->dev),
- dynamic ? " (dynamic)" : "");
-
-
-
- scan_boardinfo(master);
- status = 0;
-
- of_register_spi_devices(master);
- done:
- return status;
- }
-
- static void scan_boardinfo(struct spi_master *master)
- {
- struct boardinfo *bi;
- mutex_lock(&board_lock);
-
-
-
- list_for_each_entry(bi, &board_list, list) {
- struct spi_board_info *chip = bi->board_info;
- unsigned n;
-
-
-
- for (n = bi->n_board_info; n > 0; n--, chip++) {
- if (chip->bus_num != master->bus_num)
- continue;
-
-
-
-
-
- (void) spi_new_device(master, chip);
- }
- }
- mutex_unlock(&board_lock);
- }
-
- struct spi_device *spi_new_device(struct spi_master *master,
- struct spi_board_info *chip)
- {
- struct spi_device *proxy;
- int status;
-
-
-
-
-
-
-
-
-
-
- proxy = spi_alloc_device(master);
- if (!proxy)
- return NULL;
- WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
-
- 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;
-
-
- status = spi_add_device(proxy);
- if (status < 0) {
- spi_dev_put(proxy);
- return NULL;
- }
- return proxy;
- }
-
- int spi_add_device(struct spi_device *spi)
- {
- static DEFINE_MUTEX(spi_add_lock);
- struct device *dev = spi->master->dev.parent;
- struct device *d;
- int status;
-
-
-
- if (spi->chip_select >= spi->master->num_chipselect) {
- dev_err(dev, "cs%d >= max %d/n",
- spi->chip_select,
- spi->master->num_chipselect);
- return -EINVAL;
- }
-
-
- dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
- spi->chip_select);
-
-
-
-
-
- mutex_lock(&spi_add_lock);
-
-
- d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev));
- if (d != NULL) {
- dev_err(dev, "chipselect %d already in use/n",
- spi->chip_select);
- put_device(d);
- status = -EBUSY;
- goto done;
- }
-
-
-
-
-
-
-
- status = spi_setup(spi);
- if (status < 0) {
- dev_err(dev, "can't %s %s, status %d/n",
- "setup", dev_name(&spi->dev), status);
- goto done;
- }
-
-
-
- status = device_add(&spi->dev);
- if (status < 0)
- dev_err(dev, "can't %s %s, status %d/n",
- "add", dev_name(&spi->dev), status);
- else
- dev_dbg(dev, "registered child %s/n", dev_name(&spi->dev));
- done:
- mutex_unlock(&spi_add_lock);
- return status;
- }
-
- int spi_setup(struct spi_device *spi)
- {
- unsigned bad_bits;
- int status;
-
-
-
-
-
- bad_bits = spi->mode & ~spi->master->mode_bits;
- if (bad_bits) {
- dev_dbg(&spi->dev, "setup: unsupported mode bits %x/n",
- bad_bits);
- return -EINVAL;
- }
- if (!spi->bits_per_word)
- spi->bits_per_word = 8;
-
-
- status = spi->master->setup(spi);
- dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s"
- "%u bits/w, %u Hz max --> %d/n",
- (int) (spi->mode & (SPI_CPOL | SPI_CPHA)),
- (spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",
- (spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",
- (spi->mode & SPI_3WIRE) ? "3wire, " : "",
- (spi->mode & SPI_LOOP) ? "loopback, " : "",
- spi->bits_per_word, spi->max_speed_hz,
- status);
- return status;
- }
-
- void of_register_spi_devices(struct spi_master *master)
- {
- struct spi_device *spi;
- struct device_node *nc;
- const __be32 *prop;
- int rc;
- int len;
-
- if (!master->dev.of_node)
- return;
-
-
-
- for_each_child_of_node(master->dev.of_node, nc) {
-
- spi = spi_alloc_device(master);
- if (!spi) {
- dev_err(&master->dev, "spi_device alloc error for %s/n",
- nc->full_name);
- spi_dev_put(spi);
- continue;
- }
-
- if (of_modalias_node(nc, spi->modalias,
- sizeof(spi->modalias)) < 0) {
- dev_err(&master->dev, "cannot find modalias for %s/n",
- nc->full_name);
- spi_dev_put(spi);
- continue;
- }
-
- prop = of_get_property(nc, "reg", &len);
- if (!prop || len < sizeof(*prop)) {
- dev_err(&master->dev, "%s has no 'reg' property/n",
- nc->full_name);
- spi_dev_put(spi);
- continue;
- }
- spi->chip_select = be32_to_cpup(prop);
-
- if (of_find_property(nc, "spi-cpha", NULL))
- spi->mode |= SPI_CPHA;
- if (of_find_property(nc, "spi-cpol", NULL))
- spi->mode |= SPI_CPOL;
- if (of_find_property(nc, "spi-cs-high", NULL))
- spi->mode |= SPI_CS_HIGH;
-
- prop = of_get_property(nc, "spi-max-frequency", &len);
- if (!prop || len < sizeof(*prop)) {
- dev_err(&master->dev, "%s has no 'spi-max-frequency' property/n",
- nc->full_name);
- spi_dev_put(spi);
- continue;
- }
- spi->max_speed_hz = be32_to_cpup(prop);
-
- spi->irq = irq_of_parse_and_map(nc, 0);
-
- of_node_get(nc);
- spi->dev.of_node = nc;
-
- request_module(spi->modalias);
- rc = spi_add_device(spi);
- if (rc) {
- dev_err(&master->dev, "spi_device register error %s/n",
- nc->full_name);
- spi_dev_put(spi);
- }
- }
- }
三、spi设备文件的自动产生代码分析
这部分代码相当于注册了一个spi实际driver,既是核心平台无关代码,又是个具体实例,我们来看下一下具体做了什么,也好学习一spi_driver的各部分具体都需要做些什么,代码位于driver/spi/spidev.c下:
- static int __init spidev_init(void)
- {
- int status;
-
-
-
-
- BUILD_BUG_ON(N_SPI_MINORS > 256);
-
-
-
- status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
- if (status < 0)
- return status;
-
-
- spidev_class = class_create(THIS_MODULE, "spidev");
- if (IS_ERR(spidev_class)) {
- unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
- return PTR_ERR(spidev_class);
- }
-
- status = spi_register_driver(&spidev_spi_driver);
- if (status < 0) {
- class_destroy(spidev_class);
- unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
- }
- return status;
- }
-
- static struct spi_driver spidev_spi_driver = {
- .driver = {
- .name = "spidev",
- .owner = THIS_MODULE,
- },
- .probe = spidev_probe,
- .remove = __devexit_p(spidev_remove),
-
-
-
-
-
- };
-
- static int __devinit spidev_probe(struct spi_device *spi)
- {
- struct spidev_data *spidev;
- int status;
- unsigned long minor;
-
-
-
- spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
- if (!spidev)
- return -ENOMEM;
-
-
- spidev->spi = spi;
- spin_lock_init(&spidev->spi_lock);
- mutex_init(&spidev->buf_lock);
- INIT_LIST_HEAD(&spidev->device_entry);
-
-
-
- mutex_lock(&device_list_lock);
-
-
-
-
- minor = find_first_zero_bit(minors, N_SPI_MINORS);
- if (minor < N_SPI_MINORS) {
- struct device *dev;
-
-
-
-
- spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
- dev = device_create(spidev_class, &spi->dev, spidev->devt,
- spidev, "spidev%d.%d",
- spi->master->bus_num, spi->chip_select);
- status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
- } else {
- dev_dbg(&spi->dev, "no minor number available!/n");
- status = -ENODEV;
- }
-
-
-
- if (status == 0) {
- set_bit(minor, minors);
- list_add(&spidev->device_entry, &device_list);
- }
- mutex_unlock(&device_list_lock);
-
- if (status == 0)
- spi_set_drvdata(spi, spidev);
- else
- kfree(spidev);
- return status;
- }
当我们利用板级信息添加一个设备的时候,该driver如果匹配到这个设备,那么就会自动为其创建设备节点,封装spidev_data
信息,并且挂到全局设备链表device_list。
最后看一下刚才注册的字符设备的统一操作集:
- static const struct file_operations spidev_fops = {
- .owner = THIS_MODULE,
-
-
-
-
- .write = spidev_write,
- .read = spidev_read,
- .unlocked_ioctl = spidev_ioctl,
- .open = spidev_open,
- .release = spidev_release,
- };
-
- static int spidev_open(struct inode *inode, struct file *filp)
- {
- struct spidev_data *spidev;
- int status = -ENXIO;
- mutex_lock(&device_list_lock);
-
-
- list_for_each_entry(spidev, &device_list, device_entry) {
- if (spidev->devt == inode->i_rdev) {
- status = 0;
- break;
- }
- }
- if (status == 0) {
-
- if (!spidev->buffer) {
- spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);
- if (!spidev->buffer) {
- dev_dbg(&spidev->spi->dev, "open/ENOMEM/n");
- status = -ENOMEM;
- }
- }
-
-
- if (status == 0) {
- spidev->users++;
-
-
- filp->private_data = spidev;
- nonseekable_open(inode, filp);
- }
- } else
- pr_debug("spidev: nothing for minor %d/n", iminor(inode));
- mutex_unlock(&device_list_lock);
- return status;
- }
-
- static ssize_t
- spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
- {
- struct spidev_data *spidev;
- ssize_t status = 0;
-
- if (count > bufsiz)
- return -EMSGSIZE;
-
-
- spidev = filp->private_data;
- mutex_lock(&spidev->buf_lock);
-
-
- status = spidev_sync_read(spidev, count);
- if (status > 0) {
- unsigned long missing;
-
- missing = copy_to_user(buf, spidev->buffer, status);
- if (missing == status)
- status = -EFAULT;
- else
- status = status - missing;
- }
- mutex_unlock(&spidev->buf_lock);
- return status;
- }
-
-
- static inline ssize_t
- spidev_sync_read(struct spidev_data *spidev, size_t len)
- {
-
- struct spi_transfer t = {
- .rx_buf = spidev->buffer,
- .len = len,
- };
-
-
- struct spi_message m;
-
- spi_message_init(&m);
-
- spi_message_add_tail(&t, &m);
-
- return spidev_sync(spidev, &m);
- }
-
- static ssize_t
- spidev_sync(struct spidev_data *spidev, struct spi_message *message)
- {
- DECLARE_COMPLETION_ONSTACK(done);
- int status;
- message->complete = spidev_complete;
-
- message->context = &done;
- spin_lock_irq(&spidev->spi_lock);
- if (spidev->spi == NULL)
- status = -ESHUTDOWN;
- else
-
- status = spi_async(spidev->spi, message);
- spin_unlock_irq(&spidev->spi_lock);
- if (status == 0) {
-
- wait_for_completion(&done);
- status = message->status;
- if (status == 0)
- status = message->actual_length;
- }
- return status;
- }
-
- int spi_async(struct spi_device *spi, struct spi_message *message)
- {
- struct spi_master *master = spi->master;
- int ret;
- unsigned long flags;
- spin_lock_irqsave(&master->bus_lock_spinlock, flags);
- if (master->bus_lock_flag)
- ret = -EBUSY;
- else
-
- ret = __spi_async(spi, message);
- spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
- return ret;
- }
-
- static int __spi_async(struct spi_device *spi, struct spi_message *message)
- {
- struct spi_master *master = spi->master;
-
-
-
-
-
- if ((master->flags & SPI_MASTER_HALF_DUPLEX)
- || (spi->mode & SPI_3WIRE)) {
- struct spi_transfer *xfer;
- unsigned flags = master->flags;
- list_for_each_entry(xfer, &message->transfers, transfer_list) {
- if (xfer->rx_buf && xfer->tx_buf)
- return -EINVAL;
- if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)
- return -EINVAL;
- if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)
- return -EINVAL;
- }
- }
- message->spi = spi;
- message->status = -EINPROGRESS;
-
-
- return master->transfer(spi, message);
- }
read函数就分析到此,至于write函数就不具体分析了,流程是相似的!
四、总结
根据前面的积累,这次简要分析了下spi设备驱动的模型,由于和i2c很相似,就不给出具体框图了,后面有时间会结合具体平台分析一下spi的实例,这次就到这里了 @^.^@
阅读(352) | 评论(0) | 转发(0) |