一 linux spi子系统(框架)
一.spi设备
struct spi_device {struct devicedev;//设备文件struct spi_master*master;//spi主机u32max_speed_hz;//最大速率u8chip_select;//片选u8mode;//模式u8bits_per_word;//一个字有多少位intirq;//中断号void*controller_state;//控制器状态void*controller_data;//控制器数据charmodalias[SPI_NAME_SIZE];//名字};
2.spi传输模式:
#defineSPI_CPHA0x01//时钟相位#defineSPI_CPOL0x02//时钟继续#defineSPI_MODE_0(0|0)//模式0#defineSPI_MODE_1(0|SPI_CPHA)//模式1#defineSPI_MODE_2(SPI_CPOL|0)//模式2#defineSPI_MODE_3(SPI_CPOL|SPI_CPHA)//模式3#defineSPI_CS_HIGH0x04//片选高电平#defineSPI_LSB_FIRST0x08//LSB#defineSPI_3WIRE0x10//3线模式 SI和SO同一根线#defineSPI_LOOP0x20//回送模式#defineSPI_NO_CS0x40//单个设备占用一根SPI总线,所以没片选#defineSPI_READY0x80//从机拉低电平停止数据传输
3.spi设备的添加spi_new_device
struct spi_device *spi_new_device(struct spi_master *master,struct spi_board_info *chip){struct spi_device*proxy;intstatus;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);elsedev_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){unsignedbad_bits;intstatus;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;//则默认设置为8status = 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 {charmodalias[SPI_NAME_SIZE];//名字const void*platform_data;//平台数据void*controller_data;//控制器数据intirq;//中断号u32max_speed_hz;//最大速率u16bus_num;//spi总线编号u16chip_select;//片选u8mode;//模式};
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_driverdriver;//设备驱动文件};
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_probeif (sdrv->remove)//若存在remove方法sdrv->driver.remove = spi_drv_remove;//设置其设备驱动文件的remove方法为spi_drv_removeif (sdrv->shutdown)////若存在shutdown方法sdrv->driver.shutdown = spi_drv_shutdown;//设置其设备驱动文件的shutdown方法为spi_drv_shutdownreturn 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 devicedev;//spi主机设备文件struct list_head list;s16bus_num;//spi总线号u16num_chipselect;//片选号u16dma_alignment;//dma算法u16mode_bits;//模式位u16flags;//传输类型标志spinlock_tbus_lock_spinlock;//spi总线自旋锁struct mutexbus_lock_mutex;//spi总线互斥锁boolbus_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_DUPLEXBIT(0)//半双工#define SPI_MASTER_NO_RXBIT(1)//不读#define SPI_MASTER_NO_TXBIT(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_classmaster->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_tdyn_bus_id = ATOMIC_INIT((1<<15) - 1);struct device*dev = master->dev.parent;//获得spi主机设备的父设备struct boardinfo*bi;intstatus = -ENODEV;intdynamic = 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;//返回匹配的idmatches++;//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;//一样则返回该idid++;//id表指针++,指向下一个id}return NULL;}
六 spi消息和spi传输
1.spi消息结构体
struct spi_message {struct list_headtransfers;//spi传输事务链表头struct spi_device*spi;//所属spi设备unsignedis_dma_mapped:1;void(*complete)(void *context);void*context;unsignedactual_length;intstatus;//传输状态struct list_headqueue;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;//接收缓冲区指针unsignedlen;//消息长度dma_addr_ttx_dma;//DMA发送地址dma_addr_trx_dma;//DMA接收地址unsignedcs_change:1;u8bits_per_word;//一个字多少位u16delay_usecs;//毫秒级延时u32speed_hz;//速率struct list_head transfer_list;//传输链表头};
七.spi子系统的初始化spi_init
static int __init spi_init(void){intstatus;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_transfert = {.rx_buf= buf,.len= len,};struct spi_messagem;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_transfert = {.tx_buf= buf,.len= len,};struct spi_messagem;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_completemessage->context = &done;//设置spi消息的contextif (!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);intstatus;struct spi_messagemessage;struct spi_transferx[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;} elselocal_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);//解锁elsekfree(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;u8result;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_tstatus;u16result;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)