Linux SPI框架(中)

来源:互联网 发布:c语言随机函数 编辑:程序博客网 时间:2024/04/30 00:36

水平有限,描述不当之处还请指出,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7734150      

     上节介绍了SPI子系统中的一些重要数据结构和SPI子系统初始化的第一步,也就是注册SPI总线。这节介绍针对于s3c24xx平台的SPI子系统初始化,在看具体的代码之前,先上一张自己画的图,帮助理清初始化的主要步骤

 

显然,SPI是一种平台特定的资源,所以它是以platform平台设备的方式注册进内核的,因此它的struct platform_device结构是已经静态定义好了的,现在只待它的struct platform_driver注册,然后和platform_device匹配。

 

初始化的入口:

static int __init s3c24xx_spi_init(void){        return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);}


platform_driver_probe()会调用platform_driver_register()来注册驱动,然后在注册的过程中寻求匹配的platform_device,一旦匹配成功,便会调用probe函数,也就是s3c24xx_spi_probe(),在看这个函数之前,还得介绍几个相关的数据结构。

struct s3c2410_spi_info是一个板级结构,也是在移植时就定义好的,在初始化spi_master时用到,platform_device-->dev-->platform_data会指向这个结构。

struct s3c2410_spi_info {int pin_cs;/* simple gpio cs */unsigned int num_cs;/* total chipselects */int bus_num;/* bus number to use. */void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable);void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);};

 

struct s3c24xx_spi用来具体描述s3c24xx平台上一个SPI控制器

struct s3c24xx_spi {/* bitbang has to be first */struct spi_bitbang bitbang;struct completion done;void __iomem*regs;int irq;int len;int count;void(*set_cs)(struct s3c2410_spi_info *spi,  int cs, int pol);/* data buffers */const unsigned char*tx;unsigned char*rx;struct clk*clk;struct resource*ioarea;struct spi_master*master;struct spi_device*curdev;struct device*dev;struct s3c2410_spi_info *pdata;};


struct spi_bitbang用于控制实际的数据传输

struct spi_bitbang {struct workqueue_struct*workqueue;  /*工作队列*/struct work_structwork;spinlock_tlock;struct list_headqueue;u8busy;u8use_dma;u8flags;/* extra spi->mode support */struct spi_master*master;         /*bitbang所属的master*/ /*用于设置设备传输时的时钟,字长等*/int(*setup_transfer)(struct spi_device *spi,struct spi_transfer *t);void(*chipselect)(struct spi_device *spi, int is_on);#defineBITBANG_CS_ACTIVE1/* normally nCS, active low */#defineBITBANG_CS_INACTIVE0/*针对于平台的传输控制函数*/int(*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);/* txrx_word[SPI_MODE_*]() just looks like a shift register */u32(*txrx_word[4])(struct spi_device *spi,unsigned nsecs,u32 word, u8 bits);};

 

下面来看s3c24xx_spi_probe()函数的实现

static int __init s3c24xx_spi_probe(struct platform_device *pdev){struct s3c2410_spi_info *pdata;struct s3c24xx_spi *hw;struct spi_master *master;struct resource *res;int err = 0;/*创建spi_master,并将spi_master->private_data指向s3c24xx_spi*/master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));if (master == NULL) {dev_err(&pdev->dev, "No memory for spi_master\n");err = -ENOMEM;goto err_nomem;}hw = spi_master_get_devdata(master);//获取s3c24xx_spimemset(hw, 0, sizeof(struct s3c24xx_spi));hw->master = spi_master_get(master);hw->pdata = pdata = pdev->dev.platform_data;hw->dev = &pdev->dev;if (pdata == NULL) {dev_err(&pdev->dev, "No platform data supplied\n");err = -ENOENT;goto err_no_pdata;}platform_set_drvdata(pdev, hw);init_completion(&hw->done);/* setup the master state. */         /*片选数和SPI主控制器编号是在platform_data中已经定义好了的*/master->num_chipselect = hw->pdata->num_cs;master->bus_num = pdata->bus_num;/* setup the state for the bitbang driver *//*设置bitbang的所属master和控制传输的相关函数*/hw->bitbang.master         = hw->master;hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;hw->bitbang.chipselect     = s3c24xx_spi_chipsel;hw->bitbang.txrx_bufs      = s3c24xx_spi_txrx;hw->bitbang.master->setup  = s3c24xx_spi_setup;dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);/* find and map our resources */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (res == NULL) {dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");err = -ENOENT;goto err_no_iores;}hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1,pdev->name);if (hw->ioarea == NULL) {dev_err(&pdev->dev, "Cannot reserve region\n");err = -ENXIO;goto err_no_iores;}/*映射SPI控制寄存器*/hw->regs = ioremap(res->start, (res->end - res->start)+1);if (hw->regs == NULL) {dev_err(&pdev->dev, "Cannot map IO\n");err = -ENXIO;goto err_no_iomap;}/*获取中断号*/hw->irq = platform_get_irq(pdev, 0);if (hw->irq < 0) {dev_err(&pdev->dev, "No IRQ specified\n");err = -ENOENT;goto err_no_irq;}/*注册中断*/err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw);if (err) {dev_err(&pdev->dev, "Cannot claim IRQ\n");goto err_no_irq;}hw->clk = clk_get(&pdev->dev, "spi");if (IS_ERR(hw->clk)) {dev_err(&pdev->dev, "No clock for device\n");err = PTR_ERR(hw->clk);goto err_no_clk;}/* setup any gpio we can */if (!pdata->set_cs) {if (pdata->pin_cs < 0) {dev_err(&pdev->dev, "No chipselect pin\n");goto err_register;}err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev));if (err) {dev_err(&pdev->dev, "Failed to get gpio for cs\n");goto err_register;}hw->set_cs = s3c24xx_spi_gpiocs;//设定片选函数gpio_direction_output(pdata->pin_cs, 1);} elsehw->set_cs = pdata->set_cs;s3c24xx_spi_initialsetup(hw);/* register our spi controller */         /* 注册主机SPI控制器 */err = spi_bitbang_start(&hw->bitbang);if (err) {dev_err(&pdev->dev, "Failed to register SPI master\n");goto err_register;}return 0; err_register:if (hw->set_cs == s3c24xx_spi_gpiocs)gpio_free(pdata->pin_cs);clk_disable(hw->clk);clk_put(hw->clk); err_no_clk:free_irq(hw->irq, hw); err_no_irq:iounmap(hw->regs);

int spi_bitbang_start(struct spi_bitbang *bitbang){intstatus;if (!bitbang->master || !bitbang->chipselect)return -EINVAL;/*初始化一个struct work,处理函数为bitbang_work*/INIT_WORK(&bitbang->work, bitbang_work);spin_lock_init(&bitbang->lock);INIT_LIST_HEAD(&bitbang->queue);/*检测bitbang中的函数是否都定义了,如果没定义,则默认使用spi_bitbang_xxx*/if (!bitbang->master->transfer)bitbang->master->transfer = spi_bitbang_transfer;if (!bitbang->txrx_bufs) {bitbang->use_dma = 0;bitbang->txrx_bufs = spi_bitbang_bufs;if (!bitbang->master->setup) {if (!bitbang->setup_transfer)bitbang->setup_transfer = spi_bitbang_setup_transfer;bitbang->master->setup = spi_bitbang_setup;bitbang->master->cleanup = spi_bitbang_cleanup;}} else if (!bitbang->master->setup)return -EINVAL;/* this task is the only thing to touch the SPI bits */bitbang->busy = 0;/*创建bitbang的工作队列*/bitbang->workqueue = create_singlethread_workqueue(dev_name(bitbang->master->dev.parent));if (bitbang->workqueue == NULL) {status = -EBUSY;goto err1;}/* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ /*注册spi_master*/status = spi_register_master(bitbang->master);if (status < 0)goto err2;return status;err2:destroy_workqueue(bitbang->workqueue);err1:return status;}

 

下一个关键函数就是spi_register_master(),用于注册spi_master

int spi_register_master(struct spi_master *master){static atomic_tdyn_bus_id = ATOMIC_INIT((1<<15) - 1);struct device*dev = master->dev.parent;intstatus = -ENODEV;intdynamic = 0;if (!dev)return -ENODEV;/* even if it's just one always-selected device, there must * be at least one chipselect */if (master->num_chipselect == 0)//片选数不能为0return -EINVAL;/* convention:  dynamically assigned bus IDs count down from the max */if (master->bus_num < 0) {/* FIXME switch to an IDR based scheme, something like * I2C now uses, so we can't run out of "dynamic" IDs */master->bus_num = atomic_dec_return(&dyn_bus_id);dynamic = 1;}/* register the device, then userspace will see it. * registration fails if the bus ID is in use. */dev_set_name(&master->dev, "spi%u", master->bus_num);status = device_add(&master->dev);//添加spi_master设备if (status < 0)goto done;dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),dynamic ? " (dynamic)" : "");/* populate children from any spi device tables */scan_boardinfo(master);//遍历板级信息,寻找可以挂接在该spi_master下的从设备status = 0;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;unsignedn;for (n = bi->n_board_info; n > 0; n--, chip++) {if (chip->bus_num != master->bus_num)continue;/* NOTE: this relies on spi_new_device to * issue diagnostics when given bogus inputs */ /*bus_num相等则创建新设备*/(void) spi_new_device(master, chip);}}mutex_unlock(&board_lock);}


spi_board_info是板级信息,是在移植时就写好的,并且要将其注册

struct spi_board_info {charmodalias[32];  /*名字*/const void*platform_data;void*controller_data;intirq;          /*中断号*/u32max_speed_hz; /*最高传输速率*/u16bus_num;      /*所属的spi_master编号*/u16chip_select;  /*片选号*/ u8mode;         /*传输模式*/};

 

最后一步就是将相应的从设备注册进内核

struct spi_device *spi_new_device(struct spi_master *master,  struct spi_board_info *chip){struct spi_device*proxy;intstatus;/* NOTE:  caller did any chip->bus_num checks necessary. * * Also, unless we change the return value convention to use * error-or-pointer (not NULL-or-pointer), troubleshootability * suggests syslogged diagnostics are best here (ugh). *//*创建SPI_device*/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;}




 

原创粉丝点击