第一部分,将对SPI子系统整体进行描述,同时给出SPI的相关数据结构,最后描述SPI总线的注册。
第二部分,即本篇文章,该文将对SPI的主控制器(master)驱动进行描述。
第三部分,该文将对SPI设备驱动,也称protocol 驱动,进行讲解。
第四部分,通过SPI设备驱动留给用户层的API,我们将从上到下描述数据是如何通过SPI的protocol 驱动,由bitbang中转,最后由master驱动将数据传输出去。
本文属于第二部分4. 主控制器驱动程序
SPI控制器的配置信息和其驱动函数是没有在一起的,关于控制器的配置信息是在/kernel3.0/arch/arm/mach-exynos目录下,而关于控制器的驱动程序则是在/kernel3.0/driver/spi目录下。
4.1 定义 platform device1、SPI控制器中关于gpio口的配置信息(clk、miso、mos),所在位置/kernel3.0/arch/arm/mach-exynos/dev-spi.c
其中没有包含片选口(cs)的配置,因为片选信号可以直接通过IO口设置也可以通过连接其它芯片来获取,所以下面的函数在配置SPI控制器IO口的信息的时候没有配置片选,片选的配置在后面介绍。
static int exynos_spi_cfg_gpio(struct platform_device *pdev) { int gpio; switch(pdev->id){ //通过id来选在属于哪个spi控制器,itop4412中有3个spi控制 case 0: s3c_gpio_cfgpin(EXYNOS5_CPA2(0),S3C_GPIO_SFN(2)); s3c_gpio_cfgpin(EXYNOS5_CPA2(2),S3C_GPIO_SFN(2)); s3c_gpio_cfgpin(EXYNOS5_CPA2(3),S3C_GPIO_SFN(2)); s3c_gpio_setpull(EXYNOS5_GPA2(0),S3C_GPIO_PULL_UP); s3c_gpio_setpull(EXYNOS5_GPA2(2),S3C_GPIO_PULL_UP); s3c_gpio_setpull(EXYNOS5_GPA2(3),S3C_GPIO_PULL_UP); for(gpio = EXYNOS5_GPA2(0); gpio < EXYNOS5_GPA2(4);gpio++) s5p_gpio_set_drvstr(gpio,S5P_GPIO_DRVSTR_LV3); break; case 1: ........ case 2: ........ default: dev_err(&pdev->dev,"Invalid SPI Controller number!"); return -EINVAL; } return 0; }
2、SPI控制器用到资源的配置,所在位置/kernel3.0/arch/arm/mach-exynos/dev-spi.c
SPI控制器会用到一些硬件资源,例如内存缓冲区、DMA、中断等等。
static struct resource exynos_spi0_resource[] = { [0] = { .start = EXYNOS_PA_SPI0, //资源的寄存器起始地址 .end = EXYNOS_PA_SPI0 + 0x100 - 1, //资源的寄存器结束地址 .flags = IORESOURCE_MEM, //资源的类型(内存缓冲区资源) }, }, [1] = { .start = DMACH_SPI0_TX, .end = DMACH_SPI0_TX, .flags = IORESOURCE_DMA, //DMA资源 }, [2] = { .start = DMACH_SPI0_RX, .end = DMACH_SPI0_RX, .flags = IORESOURCE_DMA, //DMA资源 }, [3] = { .start = IRQ_SPI0, .end = IRQ_SPI0, .flags = IORESOURCE_IRQ, //中断资源 }, };
3、SPI控制器的数据信息,所在位置/kernel3.0/arch/arm/mach-exynos/dev-spi.c
在SPI控制器中引用了关于IO口的配置以及支持的从设备的个数等一些信息。
static struct s3c64xx_spi_info exynos_spi0_pdata = { .cfg_gpio = exynos_spi_cfg_gpio, //spi控制器IO口的配置信息 .fifo_lvl_mask = 0x1ff, .rx_lvl_offset = 15, .high_speed = 1, //支持HIGH_SPEED_EN .clk_from_cmu = true, //clk来自时钟管理单元,而不是spi控制器 .tx_st_done = 25, // .num_cs //控制器支持的从设备个数 };
4、SPI控制器的定义以及注册,所在位置/kernel3.0/arch/arm/mach-exynos/dev-spi.c
static u64 spi_dmamask = DMA_BIT_MASK(32); struct platform_device exynos_device_spi0 = { .name = "s3c64xx-spi", //SPI控制器和驱动匹配的标识 .id = 0; //SPI控制器的ID .num_resources = ARRAY_SIZE(exynos_spi0_resource), //SPI控制用到的资源的数目 .resource = exynos_spi0_resource, //SPI控制器用到的硬件资源 .dev = { .dma_mask = &spi_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = &exynos_spi0_pdata, }, };
4.2 定义platform driver
SPI控制器驱动的定义、注册以及卸载,所在位置/kernel3.0/driver/spi/spi_s3c64xx.c
static struct platform_driver s3c64xx_spi_driver = { .driver = { .name = "s3c64xx-spi", .owner = THIS_MODULE, }, .remove = s3c64xx_spi_remove, .suspend = s3c64xx_spi_suspend, .resume = s3c64xx_spi_resume, }; MODULE_ALIAS("platform:s3c64xx-spi") //SPI控制器驱动的定义 static int __init s3c64xx_spi_init(void) { return platform_driver_probe(&s3c64xx_spi_driver,s3c64xx_spi_probe); }; subsys_initcall(s3c64xx_spi_init); //SPI控制器驱动的注册 static void __exit s3c64xx_spi_exit(void) { platform_driver_unregister(&s3c64xx_spi_driver); }; module_exit(s3c64xx_spi_exit); //SPI控制器驱动的卸载
注意:
一、s3c64xx_spi_init函数通过调用plaform_driver_probe函数主要实现两个功能:
1、注册SPI控制器驱动(s3c64xx_spi_driver);
2、调用s3c64xx_spi_probe函数,完成驱动的初始化。
二、plarformI_driver的name要和plarform_device的name要相同。
上面实现了关于SPI控制器信息(platform_device)的定义和SPI控制器驱动信息(platform_driver)的定义以及注册,下面详细分析下SPI控制器驱动代码。
4.3 分析platform_driver函数
1、s3c64xx_spi_probe函数,所在位置/kernel3.0/driver/spi/spi_s3c64xx.c
static int __init s3c64xx_spi_probe(struct platform_device *pdev) { struct resource *mem_res,*dmatx_res,*damrx_res; struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_info *sci; struct spi_master *master; int ret; printk("%s(%d)\n",__FUNCTION__,__LINE__); //打印文件以及函数信息 if(pdev->id < 0) //platform_device定义的时候是从0开始 { dev_err(&pdev->dev,"Invalid platform device id-%d\n",pdev->id); return -ENODEV; } if(pdev->dev.platform_data == NULL) //检测定义的platform_device的信息是否配置 { dev_err(&pdev->dev,"plaform_data missing\n"); return -ENODEV; } sci = pdev->dev.platform_data; if(!sci->src_clk_name) //src_clk_name的初始化是在s3c64xx_spi_set_info()函数传参实现的 { dev_err(dev_err(&pdev->dev,"Board init must call s3c64xx_spi_set_info()\n")); return -EINVAL; } dmatx_res = platform_get_resource(pdev,IORESOURCE_DMA,0); if(dmatx_res == NULL) //检测是否有IORESOURCE_DMA资源DMACH_SPI0_TX { dev_err(&pdev->dev,"Unable to get SPI-Tx dma resource\n"); return -ENXIO; } dmatx_res = platform_get_resource(pdev,IORESOURCE_DMA,0); if(dmarx_res == NULL) //检测是否有IORESOURCE_DMA资源DMACH_SPI0_RX { dev_err(&pdev->dev,"Unable to get SPI-Rx dma resource\n"); return -ENXIO; } mem_res = platform_get_resource(pdev,IORESOURCE_MEM,0); if(mem_res == NULL) //检测是否有IORESOURCE_MEM资源 { dev_err(&pdev->dev,"Unable to get SPI MEM resource\n"); return -ENOMEM; } master = spi_alloc_master(&pdev->dev,sizeof(struct s3c64xx_spi_driver_data)); if(master == NULL) //为s3c64xx_spi_dreiver_data和spi_master申请空间,并进行初始化。 { dev_err(&pdev->dev,"Unable to allocate SPI Master\n"); return -ENOMEM; } //将master放到pdev->dev->p->driver_data(将信息放到驱动的私有指针里面,后面会添加到一个链表) platform_set_drvdata(pdev,master); //sdd获取master->dev->p->driver_data里面的驱动信息 sdd = spi_master_get_devdata(master); //sdd结构体变量的初始化 sdd->master = master; sdd->cntrlr_info = sci; sdd->pdev =pdev; sdd->sfr_start = mem_res->start; sdd->tx_dmach = dmatx_res->start; sdd->rx_damch = dmarx_res->start; sdd->cur_bpw = 8; //master结构体变量的初始化 master->bus_num = pdev->id; //一个platform_device(SPI控制器对应一个master) master->set_up = s3c64xx_spi_transfer; //SPI控制器驱动中的set_up函数赋给master的函数指针set_up master->transfer = s3c64xx_spi_transfer; //SPI控制器驱动中的transfer函数数赋给master的函数指针transfer master->num_chipselect = sci->num_cs; //将SPI控制器定义的从设备的个数放入master master->dma_alignment = 8; //SPI控制器中DMA缓冲区的对齐方式 master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; //SPI控制器的模式 //申请MEM的IO资源 if(request_mem_region(mem_res->start,resource_size(mem_res),pdev->name)==NULL) { dev_err(&pdev->dev,"Req mem region failed\n"); ret = -ENXIO; goto err0; } //建立映射 sdd->regs = ioremap(mem_res->start,resource_size(mem_res)); if(sdd->regs == NULL) { dev_err(&pdev->dev,"Uable to remap IO\n"); ret = -ENXIO; goto err1; } //检测GPIO if(sci->cfg_gpio == NULL || sci->cfg_gpio(pdev)) { dev_err(&pdev->dev,"Unable to config gpio\n"); ret = -EBUSY; goto err2; } //申请spi时钟clk sdd->clk = clk_get(&pdev->dev,"spi"); if(IS_ERR(sdd->clk)) { dev_err(&pdev->dev,"Unable to acquire clock 'spi'\n"); ret = PTR_ERR(sdd->clk); goto err3; } //使能spi时钟clk if(clk_enable(sdd->clk)) { dev_err(&pdev->dev,"Couldn't enable clock 'spi'\n"); ret = -EBUSY; goto err4; } //申请平台时钟 sdd->src_clk = clk_get(&pdev->dev,sci->src_clk_name); if(IS_ERR(sdd->src_clk)) { dev_err(&pdev->dev,"Unable to acquire clock '%s'\n",sci->src_clk_name); ret = PTR_ERR(sdd->src_clk); goto err5; } //使能平台时钟 if(clk_enable(sdd->src_clk)) { dev_err(&pdev->dev,"Couldn't enable clock '%s'\n",sci->src_clk_name); ret -EBUSY; goto err6; } //创建工作队列 sdd->workqueue = create_singlethread_workqueue(dev_name(master->dev.parent)); if(sdd->workqueue == NULL) { dev_err(&pdev->dev,"Unable to create workqueue\n"); ret = -ENOMEM; goto err7; } printk("%s(%d)\n",__FUNCTION__,__LINE__); //对应SPI控制器硬件初始化 s3c64xx_spi_hwinit(sdd,pdev->id); spin_lock_init(&sdd->lock); init_completion(&sdd->xfer_completion); INIT_WORK(&sdd->work,s3c64xx_spi_work); INIT_LIST_HEAD(&sdd->queue); //初始化链表头,前驱后继指向自己 //master信息的检测以及注册设备的设备树(of_register_spi_devices(master)) if(spi_register_master(master)) { dev_err(&pdev->dev,"cannot register SPI master\n"); ret = -EBUSY; goto err8; } dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d ""with %d Slaves attached\n", pdev->id, master->num_chipselect); dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",mem_res->end, mem_res->start, sdd->rx_dmach, sdd->tx_dmach); printk("%s(%d)\n", __FUNCTION__, __LINE__); return 0; err8: //销毁工作队列 destroy_workqueue(sdd->workqueue); err7: //关闭平台时钟 clk_disable(sdd->src_clk); err6: //回退平台时钟 clk_put(sdd->src_clk); err5: //关闭spi时钟 clk_disable(sdd->clk); err4: //回退spi时钟 clk_put(sdd->clk); err3: err2: //取消IO映射 iounmap((void *) sdd->regs); err1: //释放MEM内存缓冲区 release_mem_region(mem_res->start, resource_size(mem_res)); err0: //将pdev里面dri_data置空 platform_set_drvdata(pdev, NULL); spi_master_put(master); //回收master return ret; };
2、spi_alloc_master函数,所在位置/kernel3.0/driver/spi/spi.c
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; master->dev.parent = get_device(dev); spi_master_set_devdata(master, &master[1]); //将s3c64xx_spi_driver_data的首地址 //赋给master->dev->p->driver_data return master; } EXPORT_SYMBOL_GPL(spi_alloc_master);
该函数首先为spi_master结构体以及s3c64xx_spi_driver_data结构体分配了空间,同时将spi_master.dev.driver_data指向了s3c64xx_spi_driver_data。
3、s3c64xx_spi_driver_data结构体
struct s3c64xx_spi_driver_data { void __iomem *regs; //指向IO重映射后的控制器寄存器地址的指针 struct clk *clk; //SPI时钟 struct clk *src_clk; //平台时钟 struct platform_device *pdev; //平台设备 struct spi_master *master; //SPI控制器 struct workqueue_struct *workqueue; //SPI请求的工作队列 struct s3c64xx_spi_info *cntrlr_info; //特定平台的SPI控制器的信息 struct spi_device *tgl_spi; //指向最后一次片选选定的spi从设备 struct work_struct work; // struct list_head queue; //工作队列用来存放SPI请求 spinlock_t lock; //控制器特定的锁 enum dma_ch rx_dmach; //控制器的Rx的DMA通道 enum dma_ch tx_dmach; //控制器的Tx的DMA通道 unsigned long sfr_start; //SPI控制器总线的地址 struct completion xfer_completion; //完成转送的任务 unsigned state; //显示状态的标志 unsigned cur_mode,cur_bpw; //存储控制器的活动配置 存储每个字的活动位 unsigned cur_speed; //存储转送的时钟速率 }
该结构体包含了SPI驱动模块的所有信息,包含SPI模块用到的时钟、平台设备的信息、SPI控制器master、SPI添加的工作队列、SPI控制器的配置信息、添加的SPI从设备信息等。
4、platform_set_drvdata函数
static inline void platform_set_drvdata(struct platform_device *pdev,void *data) { dev_set_drvdata(&pdev->dev,data); } int dev_set_drvdata(struct device *dev,void *data) { int error; if(!dev->p) { error = device_private_init(dev); if(error) reuturn error; } dev->p->driver_data = data; return 0; } EXPORT_SYMBOL(dev_set_drvdata); int device_private_init(struct device *dev) { dev->p = kzalloc(sizeof(*dev->p),GFP_KERNEL); if(!dev->p) return -ENOMEM; dev->p->device = dev; klist_init(&dev->p->klist_children,klist_children_get,klist_children_put); return 0; } void klist_init(struct klist *k,void(*get)(struct klist_node*),void(*put)(struct klist_node*)) { INIT_LIST_HEAD(&k->k_list); spin_lock_init(&k->k_lock); k->get = get; k->put = put; } EXPORT_SYMBOL_GPL(klist_init); static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; }
该函数实现将pdev->dev->p->driver_data = master。
5、spi_master_get_devdata函数
static inline void *spi_master_get_devdata(struct spi_master *master) { return dev_get_drvdata(&master->dev); } void *dev_get_drvdata(const struct device *dev) { if(dev && dev->p) return dev->p->driver_data; return NULL; } EXPORT_SYMBOL(dev_get_drvdata);
该函数实现将sdd = master->dev->p->driver_data。
6、s3c64xx_spi_hwinit函数
static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd,int channel) { struct s3c64xx_spi_info *sci = sdd->cntrlr_info; void __iomem *regs = sdd->regs; unsigned int val; sdd->cur_speed = 0; S3C64XX_SPI_DEACT(sdd); //禁用中断,如果不是DMA模式,使用轮询 writel(0,reg + S3C64XX_SPI_INT_EN); if(!sci->clk_from_cmu) writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT,regs + S3C64XX_SPI_CLK_CFG); writel(0,regs + S3C64XX_SPI_MODE_CFG); writel(0,regs + S3C64XX_SPI_PACKET_CNT); //清除任何中断的暂挂位 writel(readl(regs + S3C64XX_SPI_PENDING_CLR),regs + S3C64XX_SPI_PENDING_CLR); writel(0,regs + S3C64XX_SPI_SWAP_CFG); //SPI模式的设置 val = readl(regs + S3C64XX_SPI_MODE_CFG); val &= ~S3C64XX_SPI_MODE_4BURST; val &= ~(S3C64XX_SPI_MAX_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF); val |= (S3C64XX_SPI_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF); writel(val,regs + S3C64XX_SPI_MODE_CFG); flush_fifo(sdd); }
该函数进行硬件的初始化
7、flush_fifo函数
static void flush_fifo(struct s3c64xx_spi_driver_data *sdd) { struct s3c64xx_spi_info *sci = sdd->cntrlr_info; void __iomem *regs = sdd->regs; unsigned long loops; u32 val; writel(0,regs + S3C64XX_SPI_PACKET_CNT); val = readl(regs + S3C64XX_SPI_CH_CFG); val |= S3C64XX_SPI_CH_SW_RST; val &= ~S3C64XX_SPI_CH_HS_EN; writel(val,regs + S3C64XX_SPI_CH_CFG); //刷新Tx fifo loops = msecs_to_loops(1); do{ val = readl(regs + S3C64XX_SPI_STATUS); }while(TX_FIFO_LVL(val,sci) && loops--); if(loops == 0) dev_warn(&sdd->pdev->dev,"Timed out flushing TX FIFO\n"); //刷新Rx fifo loops = msecs_to_loops(1); do{ val = readl(regs + S3C64XX_SPI_STATUS); if(RX_FIFO_LVL(val,sci)) readl(regs + S3C64XX_SPI_RX_DATA); else break; }while(loops--); if(loops = 0) dev_warn(&sdd->pdev->dev,"Timed out flushing RX FIFO\n"); val = readl(regs + S3C64XX_SPI_CH_CFG); val &= ~S3C64XX_SPI_CH_SW_RST; writel(val,regs + S3C64XX_SPI_CH_CFG); val = readl(regs + S3C64XX_SPI_MODE_CFG); val &= ~(S3C64XX_SPI_RXCH_ON | S3C64XX_SPI_CH_TXCH_ON); writel(val,regs + S3C64XX_SPI_CH_CFG); }
该函数将FIFO的RX、TX刷新
8、init_completion函数
static inline void init_completion(struct completion *x) { x->done = 0; init_waitqueue_head(&x->wait); } #define init_waitqueue_head(q) \ do{ static struct lock_class_key __key; __init_waitqueue_head((q),&__key); }while(0); void __init_waitqueue_head(wait_queue_head_t *q,struct lock_class_key *key) { spin_lock_init(&q->lock); lockdep_set_class(&q->lock,key); INIT_LIST_HEAD(&q->task_list); } static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; }
9、spi_register_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; struct boardinfo *bi; int status = -ENODEV; int dynamic = 0; if(!dev) return -ENODEV; //如果master设置的片选从设备个数为0则报错 if(master->num_chipselect == 0) return -EINVAL; //SPI控制器ID不能小于0 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; //为SPI控制器设置名字,eg:spi0、spi1 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)":""); mutex_lock(&board_lock); //将master->list添加到spi_master_list列表 list_add_tail(&master->list,&spi_master_list); //循环遍历spi设备配置结构体,然后与spi控制的总线号匹配,成功则生成新spi设备 list_for_each_entry(bi,&board_list,list) spi_match_master_to_boardinfo(master,&bi->board_info); mutex_unlock(&board_lock); status = 0; //将master控制器下对应的spi设备添加到设备树 of_register_spi_devices(master); done: return status; }
该函数生成了控制器master
int device_add(struct device *dev) { struct device *parent = NULL; struct class_interface *class_intf; int error = -EINVAL; dev = get_device(dev); if(!dev) goto done; //如果dev->p为空,则将dev->p->device = dev if(!dev->p) { error = device_private_init(dev); if(error) goto done; } //初始化名字 if(dev->init_name) { dev_set_name(dev,"%s",dev->init_name); dev->init_name = NULL; } if(!dev_name(dev)) { error = -EINVAL; goto name_error; } pr_debug("device:'%s':'%s\n'",dev_name(dev),__func__); parent = get_device(dev->parent); setup_parent(dev,parent); if(parent) set_dev_node(dev,dev_to_node(parent)); error = kobject_add(&dev->kobj,dev->kobj.parent,NULL); if(error) goto Error; if(platform_notify) platform_notify(dev); error = device_create_file(dev,&uevent_attr); if(error) goto attrError; if(MAJOR(dev->devt)) { error = device_create_file(dev,&devt_attr); if(error) goto ueventattrError; error = device_create_sys_dev_entry(dev); if(error) goto devtattrError; devtmpfs_create_node(dev); } error = device_add_class_symlinks(dev); if(error) goto SymlinkError; error = device_add_attrs(dev); if(error) goto AttrsError; error = bus_add_device(dev); if(error) goto BusError; error = dpm_sysfs_add(dev); if(error) goto DPMError; device_pm_add(dev); if(dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE,dev); kobject_uevent(&dev->kobj,KOBJ_ADD); bus_probe_device(dev); if(parent) klist_add_tail(&dev->p->knode_parent,&parent->p->klist_children); if(dev->class) { mutex_lock(&dev->class->p->class_mutex); klist_add_tail(&dev->knode_class,&dev->class->p->klist_devices); list_for_each_entry(class_intf,&dev->class->p->class_interfaces,node) if(class_intf->add_dev) class_intf->add_dev(dev,class_intf); mutex_unlock(&dev->class->p->class_mutex); } done: put_device(dev); return error; DPMError: bus_remove_device(dev); BusError: device_remove_attrs(dev); AttrsError: device_remove_class_symlinks(dev); SymlinkError: if(MAJOR(dev->devt)) devtmpfs_delete_node(dev); if(MAJOR(dev->devt)) device_remove_sys_dev_entry(dev); devtattrError: if(MAJOR(dev->devt)) device_remove_file(dev,&devt_attr); ueventattrError: device_remove_file(dev,&uevent_attr); attrError: kobject_uevent(&dev->kobj,KOBJ_REMOVE); kobject_del(&dev->kobj); Error: cleanup_device_parent(dev); if(parent) put_device(parent); name_error: kfree(dev->p); dev->p = NULL; goto done; }
static void spi_match_master_to_boardinfo(struct spi_master *master, struct spi_board_info *bi) { struct spi_device *dev; //master控制器上总线号和板级信息总线号匹配 if(master->bus_num != bi->bus_num) return; //生成spidev dev = spi_new_device(master,bi); if(!dev) dev_err(master->dev.parent,"can't create new device for %s\n",bi->modalias); }
struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip) { struct spi_device *proxy; int status; //通过传入的master来生成一个spidev proxy = spi_alloc_device(master); if(!proxy) return NULL; WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); //根据板级信息配置新spidev 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; } EXPORT_SYMBOL_GPL(spi_new_device);
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); //从spi总线上查找该名字是否被设置,也就是该片选号是否被用 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; } //spidev进行一些模式设置 status = spi_setup(spi); if(status < 0) { dev_err(dev,"can't setup %s,status %d\n",dev_name(&spi->dev),status); goto done; } //spidev设备绑定驱动后添加进内核,生成各种文件 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);
至此,master 驱动的大体结构都已分析完毕,随后第三篇文章将介绍spi设备驱动。