一.spi设备
struct spi_device { struct device dev; //设备文件 struct spi_master *master; //spi主机 u32 max_speed_hz; //最大速率 u8 chip_select; //片选 u8 mode; //模式 u8 bits_per_word; //一个字有多少位 int irq; //中断号 void *controller_state; //控制器状态 void *controller_data; //控制器数据 char modalias[SPI_NAME_SIZE];//名字 };
2.spi传输模式:
#define SPI_CPHA 0x01 //时钟相位 #define SPI_CPOL 0x02 //时钟继续 #define SPI_MODE_0 (0|0) //模式0 #define SPI_MODE_1 (0|SPI_CPHA) //模式1 #define SPI_MODE_2 (SPI_CPOL|0) //模式2 #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) //模式3 #define SPI_CS_HIGH 0x04 //片选高电平 #define SPI_LSB_FIRST 0x08 //LSB #define SPI_3WIRE 0x10 //3线模式 SI和SO同一根线 #define SPI_LOOP 0x20 //回送模式 #define SPI_NO_CS 0x40 //单个设备占用一根SPI总线,所以没片选 #define SPI_READY 0x80 //从机拉低电平停止数据传输
3.spi设备的添加spi_new_device
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); //3.1 spi设备初始化 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); //3.2 添加spi设备 if (status < 0) { spi_dev_put(proxy); //增加spi设备引用计数 return NULL; } return proxy; } EXPORT_SYMBOL_GPL(spi_new_device);
3.1.分配spi设备
struct spi_device *spi_alloc_device(struct spi_master *master) { struct spi_device *spi; struct device *dev = master->dev.parent; if (!spi_master_get(master)) //判断spi主机是否存在 return NULL; spi = kzalloc(sizeof *spi, GFP_KERNEL); //分配内存 if (!spi) { dev_err(dev, "cannot alloc spi_device\n"); spi_master_put(master); //增加主机引用计数 return NULL; } spi->master = master; //设置spi主机 spi->dev.parent = dev; //spi设备文件的父设备为spi主机设备文件的父设备 spi->dev.bus = &spi_bus_type; //总线类型 spi->dev.release = spidev_release; //释放方法 device_initialize(&spi->dev); //设备初始化 return spi; } EXPORT_SYMBOL_GPL(spi_alloc_device);
3.2 添加spi设备
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)); //查找总线上的spi设备 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); //调用spi主机 setup方法 if (status < 0) { dev_err(dev, "can't setup %s, status %d\n",dev_name(&spi->dev), status); goto done; } status = device_add(&spi->dev); //添加设备 if (status < 0) dev_err(dev, "can't add %s, status %d\n",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; } EXPORT_SYMBOL_GPL(spi_add_device);
3.2.1 spi setup方法
int spi_setup(struct spi_device *spi) { unsigned bad_bits; int status; bad_bits = spi->mode & ~spi->master->mode_bits; //比较spi设备的模式和spi主机支持的模式 if (bad_bits) { //存在不支持的模式 dev_err(&spi->dev, "setup: unsupported mode bits %x\n",bad_bits); return -EINVAL; } if (!spi->bits_per_word) //若没设置设备的每个字含多少位 spi->bits_per_word = 8; //则默认设置为8 status = spi->master->setup(spi); //调用spi主机的setup方法 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; } EXPORT_SYMBOL_GPL(spi_setup);
二.spi板级设备
1.板级设备结构体
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; //spi总线编号 u16 chip_select; //片选 u8 mode; //模式 };
2.板级设备注册(静态注册,一般在板级初始化函数中调用)
int __init spi_register_board_info(struct spi_board_info const *info, unsigned n) { struct boardinfo *bi; int i; bi = kzalloc(n * sizeof(*bi), GFP_KERNEL); //分配内存 if (!bi) return -ENOMEM; for (i = 0; i < n; i++, bi++, info++) { struct spi_master *master; memcpy(&bi->board_info, info, sizeof(*info)); //设置bi的板级信息 mutex_lock(&board_lock); list_add_tail(&bi->list, &board_list); //添加bi->list到全局board_list链表 list_for_each_entry(master, &spi_master_list, list) //遍历spi主机链表 spi_match_master_to_boardinfo(master, &bi->board_info); mutex_unlock(&board_lock); } return 0; }
2.1.spi板级设备与spi主机匹配
static void spi_match_master_to_boardinfo(struct spi_master *master,struct spi_board_info *bi) { struct spi_device *dev; if (master->bus_num != bi->bus_num) return; dev = spi_new_device(master, bi); if (!dev) dev_err(master->dev.parent, "can't create new device for %s\n",bi->modalias); }
三.spi设备驱动
1.spi设备驱动结构体
struct spi_driver { const struct spi_device_id *id_table; //spi设备id表 int (*probe)(struct spi_device *spi); //probe方法(探测到设备) int (*remove)(struct spi_device *spi); //remove方法(设备移除) void (*shutdown)(struct spi_device *spi); //shutdown方法(关闭设备) int (*suspend)(struct spi_device *spi, pm_message_t mesg); //suspend方法(挂起设备) int (*resume)(struct spi_device *spi); //resume方法(唤醒设备) struct device_driver driver; //设备驱动文件 };
2.spi设备驱动注册
int spi_register_driver(struct spi_driver *sdrv) { sdrv->driver.bus = &spi_bus_type; //总线类型 if (sdrv->probe) //若存在probe方法 sdrv->driver.probe = spi_drv_probe; //设置其设备驱动文件的probe方法为spi_drv_probe if (sdrv->remove) //若存在remove方法 sdrv->driver.remove = spi_drv_remove; //设置其设备驱动文件的remove方法为spi_drv_remove if (sdrv->shutdown) ////若存在shutdown方法 sdrv->driver.shutdown = spi_drv_shutdown; //设置其设备驱动文件的shutdown方法为spi_drv_shutdown return driver_register(&sdrv->driver); //注册设备驱动 } EXPORT_SYMBOL_GPL(spi_register_driver);
这里的probe方法会在设备与驱动匹配的时候给调用
参看really_probe函数的部分代码
if (dev->bus->probe) { //若总线有probe方法(spi子系统的没有) ret = dev->bus->probe(dev); //则调用总线的probe方法 if (ret) goto probe_failed; } else if (drv->probe) { //若存在设备驱动的probe方法 ret = drv->probe(dev); //则调用设备驱动的probe方法 if (ret) goto probe_failed; }
2.1 spi_drv_probe
static int spi_drv_probe(struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); //根据设备文件的设备驱动找到spi设备驱动 return sdrv->probe(to_spi_device(dev)); //调用spi设备驱动的probe方法 }
3.spi设备驱动注销
static inline void spi_unregister_driver(struct spi_driver *sdrv) { if (sdrv) driver_unregister(&sdrv->driver); //注销设备驱动 }
四.spi主机
1.spi主机结构体
struct spi_master { struct device dev; //spi主机设备文件 struct list_head list; s16 bus_num; //spi总线号 u16 num_chipselect; //片选号 u16 dma_alignment; //dma算法 u16 mode_bits; //模式位 u16 flags; //传输类型标志 spinlock_t bus_lock_spinlock; //spi总线自旋锁 struct mutex bus_lock_mutex; //spi总线互斥锁 bool bus_lock_flag; //上锁标志 int (*setup)(struct spi_device *spi); //setup方法 int (*transfer)(struct spi_device *spi,struct spi_message *mesg); //传输方法 void (*cleanup)(struct spi_device *spi); //cleanup方法 };
1.2.flags标志
#define SPI_MASTER_HALF_DUPLEX BIT(0) //半双工 #define SPI_MASTER_NO_RX BIT(1) //不读 #define SPI_MASTER_NO_TX BIT(2) //不写
2.spi主机初始化spi_alloc_master
struct spi_master *spi_alloc_master(struct device *dev, unsigned size) { struct spi_master *master; if (!dev) return NULL; master = kzalloc(size + sizeof *master, GFP_KERNEL); //分配内存 if (!master) return NULL; device_initialize(&master->dev); //初始化主机设备文件 master->dev.class = &spi_master_class; //指定设备类spi_master_class master->dev.parent = get_device(dev); //设置spi主机设备的父设备 spi_master_set_devdata(master, &master[1]); //设置设备数据 return master; } EXPORT_SYMBOL_GPL(spi_alloc_master);
3.注册spi主机
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; //获得spi主机设备的父设备 struct boardinfo *bi; int status = -ENODEV; int dynamic = 0; if (!dev) return -ENODEV; if (master->num_chipselect == 0) //判断片选个数 return -EINVAL; if (master->bus_num < 0) { //验证spi总线编号 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); //设置spi主机设备名 status = device_add(&master->dev); //添加spi主机设备 if (status < 0) goto done; dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),dynamic ? " (dynamic)" : ""); mutex_lock(&board_lock); list_add_tail(&master->list, &spi_master_list); //spi主机list链表添加进全局spi_master_list链表 list_for_each_entry(bi, &board_list, list) //遍历全局board_list查找bi结构体 spi_match_master_to_boardinfo(master, &bi->board_info); //找到匹配的板级spi设备 mutex_unlock(&board_lock); status = 0; of_register_spi_devices(master); done: return status; } EXPORT_SYMBOL_GPL(spi_register_master);
3.1 匹配spi主机和板级spi设备
static void spi_match_master_to_boardinfo(struct spi_master *master,struct spi_board_info *bi) { struct spi_device *dev; if (master->bus_num != bi->bus_num) //判断是否所属的spi总线 return; dev = spi_new_device(master, bi); //添加新的spi设备 if (!dev) dev_err(master->dev.parent, "can't create new device for %s\n",bi->modalias); }
在注册板级设备或主机设备的时候都会添加
spi板级设备添加进board_list链表,spi主机设备添加进spi_master_list链表
不管是先注册spi板级设备还是先注册spi主机设备
都会调用list_for_each_entry遍历对应的要匹配的设备的链表,查找是否有匹配的例子
若找到都会调用spi_match_master_to_boardinfo函数添加spi设备
4.注销spi主机
void spi_unregister_master(struct spi_master *master) { int dummy; mutex_lock(&board_lock); list_del(&master->list); //删除链表 mutex_unlock(&board_lock); dummy = device_for_each_child(&master->dev, NULL, __unregister); //调用__unregister函数注销子设备 device_unregister(&master->dev); //注销设备 } EXPORT_SYMBOL_GPL(spi_unregister_master);
4.1 注销挂载该总线上的spi子设备
static int __unregister(struct device *dev, void *null) { spi_unregister_device(to_spi_device(dev)); return 0; }
5.spi主机设备类
static struct class spi_master_class = { .name = "spi_master", .owner = THIS_MODULE, .dev_release = spi_master_release, };
五.spi总线
1.spi总线结构体
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, }; EXPORT_SYMBOL_GPL(spi_bus_type);
2.设备匹配方法spi_match_device
前面的匹配方法是spi板级设备与spi主机设备的匹配方法,匹配的结果是添加新spi设备spi_new_device
这里的匹配是spi设备和spi驱动的匹配,匹配的结果是会调用spi驱动的设备驱动文件probe方法,既spi_drv_probe
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) //spi设备驱动存在支持id表 return !!spi_match_id(sdrv->id_table, spi); //spi设备驱动表的匹配 return strcmp(spi->modalias, drv->name) == 0; //比较spi设备的名字和spi设备驱动的名字 }
2.1 of_driver_match_device
static inline int of_driver_match_device(const struct device *dev,const struct device_driver *drv) { return of_match_device(drv->of_match_table, dev) != NULL; //调用of_match_device函数 }
2.1.1 of_match_device
const struct of_device_id *of_match_device(const struct of_device_id *matches,const struct device *dev) { if ((!matches) || (!dev->of_node)) //id表和设备节点都不存在 return NULL; //则返回 return of_match_node(matches, dev->of_node); //调用of_match_node函数 } EXPORT_SYMBOL(of_match_device);
2.1.1.1 of_match_node //drv->of_match_table,dev->of_node
const struct of_device_id *of_match_node(const struct of_device_id *matches,const struct device_node *node) { while (matches->name[0] || matches->type[0] || matches->compatible[0]) { //名字,类型,兼容方法有一个存在 int match = 1; if (matches->name[0]) //判断名字 match &= node->name && !strcmp(matches->name, node->name); if (matches->type[0]) //判断类型 match &= node->type && !strcmp(matches->type, node->type); if (matches->compatible[0]) //兼容方法 match &= of_device_is_compatible(node,matches->compatible); if (match) //匹配 return matches; //返回匹配的id matches++; //matches指针++,指向下一个id } return NULL; } EXPORT_SYMBOL(of_match_node);
2.2 spi_match_id
static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,const struct spi_device *sdev) { while (id->name[0]) { //id表的成员的名字域不为空 if (!strcmp(sdev->modalias, id->name)) //则判断其名字是否与spi设备的名字一样 return id; //一样则返回该id id++; //id表指针++,指向下一个id } return NULL; }
六 spi消息和spi传输
1.spi消息结构体
struct spi_message { struct list_head transfers; //spi传输事务链表头 struct spi_device *spi; //所属spi设备 unsigned is_dma_mapped:1; void (*complete)(void *context); void *context; unsigned actual_length; int status; //传输状态 struct list_head queue; void *state; };
2.初始化spi消息
static inline void spi_message_init(struct spi_message *m) { memset(m, 0, sizeof *m); INIT_LIST_HEAD(&m->transfers); //初始化spi消息的事务链表头 }
3.添加传输事务到spi传输链表
static inline void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m) { list_add_tail(&t->transfer_list, &m->transfers); }
4.spi传输结构体
struct spi_transfer { const void *tx_buf; //发送缓冲区指针 void *rx_buf; //接收缓冲区指针 unsigned len; //消息长度 dma_addr_t tx_dma; //DMA发送地址 dma_addr_t rx_dma; //DMA接收地址 unsigned cs_change:1; u8 bits_per_word; //一个字多少位 u16 delay_usecs; //毫秒级延时 u32 speed_hz; //速率 struct list_head transfer_list; //传输链表头 };
七.spi子系统的初始化spi_init
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); //注册spi总线 if (status < 0) goto err1; status = class_register(&spi_master_class); //注册spi主机类 "/sys/class/spi_master" if (status < 0) goto err2; return 0; err2: bus_unregister(&spi_bus_type); err1: kfree(buf); buf = NULL; err0: return status; } postcore_initcall(spi_init); //入口声明 #define postcore_initcall(fn) __define_initcall("2",fn,2)
八.spi子系统的API
1.spi读 spi_read
static inline int spi_read(struct spi_device *spi, u8 *buf, size_t len) { struct spi_transfer t = { .rx_buf = buf, .len = len, }; struct spi_message m; spi_message_init(&m); //spi消息初始化(初始化传输事务链表头) spi_message_add_tail(&t, &m); //添加spi传输到spi消息传输链表 return spi_sync(spi, &m); //spi同步传输 }
2.spi写 spi_write
static inline int spi_write(struct spi_device *spi, const u8 *buf, size_t len) { struct spi_transfer t = { .tx_buf = buf, .len = len, }; struct spi_message m; spi_message_init(&m); //spi消息初始化(初始化传输事务链表头) spi_message_add_tail(&t, &m); //添加spi传输到spi消息传输链表 return spi_sync(spi, &m); //spi同步传输 }
spi的读写操作都是初始化一个spi_transfer传输结构体,并将其添加进spi消息传输事务链表中
然后通过spi_sync来同步读写操作,接着看下spi_sync的具体代码
2.1 spi_sync
int spi_sync(struct spi_device *spi, struct spi_message *message) { return __spi_sync(spi, message, 0); //调用__spi_sync函数 } EXPORT_SYMBOL_GPL(spi_sync);
2.1.1 __spi_sync函数
static int __spi_sync(struct spi_device *spi, struct spi_message *message,int bus_locked) { DECLARE_COMPLETION_ONSTACK(done); //声明一个工作队列 int status; struct spi_master *master = spi->master; //获取spi主机 message->complete = spi_complete; //设置spi消息的complete方法为spi_complete message->context = &done; //设置spi消息的context if (!bus_locked) mutex_lock(&master->bus_lock_mutex); //上锁 status = spi_async_locked(spi, message); if (!bus_locked) mutex_unlock(&master->bus_lock_mutex); //解锁 if (status == 0) { wait_for_completion(&done); //等待完成 status = message->status; //设置spi消息传输状态 } message->context = NULL; return status; }
2.1.1.1 spi_async_locked 函数
int spi_async_locked(struct spi_device *spi, struct spi_message *message) { struct spi_master *master = spi->master; //获取spi主机 int ret; unsigned long flags; spin_lock_irqsave(&master->bus_lock_spinlock, flags); //上自旋锁 ret = __spi_async(spi, message); //调用了spi异步同步方法 spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); //解自旋锁 return ret; } EXPORT_SYMBOL_GPL(spi_async_locked);
2.1.1.1.1 __spi_async函数(异步)
static int __spi_async(struct spi_device *spi, struct spi_message *message) { struct spi_master *master = spi->master; //主机为半双工或spi设备为3线设备 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) { //遍历spi消息的传输事务链表 if (xfer->rx_buf && xfer->tx_buf) //判断接收或发送缓冲区是否为空 return -EINVAL; if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf) //检验无spi数据发送的情况 return -EINVAL; if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf) //检验无spi数据接收的情况 return -EINVAL; } } message->spi = spi; //设置spi消息所属的spi设备 message->status = -EINPROGRESS; //设置spi消息的传输状态 return master->transfer(spi, message); //调用spi主机的transfer方法,收发spi信息给spi设备 }
3.spi先写后读 spi_write_then_read
int spi_write_then_read(struct spi_device *spi,const u8 *txbuf, unsigned n_tx,u8 *rxbuf, unsigned n_rx) { static DEFINE_MUTEX(lock); int status; struct spi_message message; struct spi_transfer x[2]; //声明两个spi传输结构体 u8 *local_buf; if ((n_tx + n_rx) > SPI_BUFSIZ) //验证发送和接收的数据总和是否溢出 return -EINVAL; spi_message_init(&message); //spi消息初始化(初始化传输事务链表头) memset(x, 0, sizeof x); // if (n_tx) { x[0].len = n_tx; spi_message_add_tail(&x[0], &message); //添加spi传输到spi消息传输链表 } if (n_rx) { x[1].len = n_rx; spi_message_add_tail(&x[1], &message); //添加spi传输到spi消息传输链表 } if (!mutex_trylock(&lock)) { //尝试上锁 失败 local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); //则分配local_buf内存 if (!local_buf) return -ENOMEM; } else local_buf = buf; //指向默认分配好内存的缓冲区(spi_init中初始化的) memcpy(local_buf, txbuf, n_tx); //发送缓冲区的内容复制到local_buf中 x[0].tx_buf = local_buf; //发送的spi传输结构体 x[1].rx_buf = local_buf + n_tx; //接收的spi传输结构体 status = spi_sync(spi, &message); //spi同步传输--发送数据 if (status == 0) memcpy(rxbuf, x[1].rx_buf, n_rx); //接收返回的数据复制到rxbuf中 if (x[0].tx_buf == buf) mutex_unlock(&lock); //解锁 else kfree(local_buf); //释放内存 return status; } EXPORT_SYMBOL_GPL(spi_write_then_read);
4.spi写8位后读8位数据 spi_w8r8
static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd) { ssize_t status; u8 result; status = spi_write_then_read(spi, &cmd, 1, &result, 1); //写8位读8位 return (status < 0) ? status : result; }
5.spi写8位数据后读16位数据 spi_w8r16
static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd) { ssize_t status; u16 result; status = spi_write_then_read(spi, &cmd, 1, (u8 *) &result, 2); //写8位读16位 return (status < 0) ? status : result; }
这里的API是内核空间使用的接口,设备驱动程序调用这些API直接操作spi的读写操作,来完成任务
spi用户空间的接口由spidev.c中的spi驱动提供,见
-->二 spi 子系统(spidev.c)