Allein.Cao原创作品,转载请注明出处:
http://blog.csdn.net/alleincao/article/details/7523169
内核版本:2.6.32.2
硬件:S3C2440
控制器驱动是与SOC密切相关的一个模块,在linux中,一般都会采用设备驱动和控制器驱动分离的思想,两者通过一个core进行关联,这样能够最大程度保证代码的可移植性,具体可以参考《linux设备驱动开发详解》
上面提到设备驱动和控制器驱动通过core进行关联,更具体地来说,它是通过一个自己的bus来实现的,在spi中,是通过core文件中注册的spi_bus_type实现,在i2c中,则是通过core中的i2c_bus_type实现(其实spi和i2c子系统是比较相像的),具体到spi的驱动中,这个core就是spi.c文件,所以在此我们简单看一下spi.c。
- struct bus_type spi_bus_type = { //spi_device和spi_driver都向其注册
- .name = "spi",
- .dev_attrs = spi_dev_attrs,
- .match = spi_match_device, //总线的match函数
- .uevent = spi_uevent,
- .suspend = spi_suspend,
- .resume = spi_resume,
- };
- 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_bus_type
- if (status < 0)
- goto err1;
- status = class_register(&spi_master_class); //创建spi_master_class类
- if (status < 0)
- goto err2;
- return 0;
- err2:
- bus_unregister(&spi_bus_type);
- err1:
- kfree(buf);
- buf = NULL;
- err0:
- return status;
- }
根据前面分析,我们知道控制器驱动对应文件spi_s3c24xx.c,其完成的主要工作就是从BSP中取出master controller的信息(通过系统platform_bus),产生master controller并注册到spi_bus_type,接着扫描BSP中注册的设备链表产生spi_device并一并将它们注册到spi_bus_type,这样,当spi_driver注册到spi_bus_type时就可以与spi_device进行匹配(具体是首先通过spi_bus_type的match函数(一般是通过比较两者的名字实现),进一步通过spi_driver的probe函数实现),看其初始化函数
- static struct platform_driver s3c24xx_spi_driver = {
- .remove = __exit_p(s3c24xx_spi_remove),
- .driver = {
- .name = "s3c2410-spi",
- .owner = THIS_MODULE,
- .pm = S3C24XX_SPI_PMOPS,
- },
- };
- static int __init s3c24xx_spi_init(void)
- {
- return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);
- }
Master controller设备需要在BSP中向platfor_bus注册,具体是在/arch/arm/plat-s3c24xx/devs.c中定义:
- static struct resource s3c_spi0_resource[] = {
- [0] = {
- .start = S3C24XX_PA_SPI,
- .end = S3C24XX_PA_SPI + 0x1f,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_SPI0,
- .end = IRQ_SPI0,
- .flags = IORESOURCE_IRQ,
- }
- };
- static u64 s3c_device_spi0_dmamask = 0xffffffffUL;
- struct platform_device s3c_device_spi0 = { //spi控制器信息
- .name = "s3c2410-spi",
- .id = 0,
- .num_resources = ARRAY_SIZE(s3c_spi0_resource),
- .resource = s3c_spi0_resource,
- .dev = {
- .dma_mask = &s3c_device_spi0_dmamask,
- .coherent_dma_mask = 0xffffffffUL
- }
- };
并在/arch/arm/mach-s3c2440/mach-mini2440.c中向platform_bus进行注册
- static struct platform_device *mini2440_devices[] __initdata = {
- &s3c_device_usb,
- &s3c_device_rtc,
- &s3c_device_lcd,
- &s3c_device_wdt,
- &s3c_device_i2c0,
- &s3c_device_iis,
- &mini2440_device_eth,
- &s3c24xx_uda134x,
- &s3c_device_nand,
- &s3c_device_sdi,
- &s3c_device_usbgadget,
- &s3c_device_spi0,
- };
- static void __init mini2440_machine_init(void)
- {
- #if defined (LCD_WIDTH)
- s3c24xx_fb_set_platdata(&mini2440_fb_info);
- #endif
- s3c_i2c0_set_platdata(NULL);
- s3c2410_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND);
- s3c_device_nand.dev.platform_data = &friendly_arm_nand_info;
- s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg;
- platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
- s3c_pm_init();
- }
- MACHINE_START(MINI2440, "FriendlyARM Mini2440 development board")
- .phys_io = S3C2410_PA_UART,
- .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
- .boot_params = S3C2410_SDRAM_PA + 0x100,
- .init_irq = s3c24xx_init_irq,
- .map_io = mini2440_map_io,
- .init_machine = mini2440_machine_init,
- .timer = &s3c24xx_timer,
- MACHINE_END
我们回头看platform_bus_type的match函数:
- struct bus_type platform_bus_type = {
- .name = "platform",
- .dev_attrs = platform_dev_attrs,
- .match = platform_match,
- .uevent = platform_uevent,
- .pm = &platform_dev_pm_ops,
- };
- static int platform_match(struct device *dev, struct device_driver *drv)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct platform_driver *pdrv = to_platform_driver(drv);
- /* match against the id table first */
- if (pdrv->id_table)
- return platform_match_id(pdrv->id_table, pdev) != NULL;
- /* fall-back to driver name match */
- return (strcmp(pdev->name, drv->name) == 0);
- }
分析可知BSP中注册的控制器名字和spi_s3c24xx.c注册的驱动名字可以通过名字进行匹配,紧接着会调用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;
- master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); //分配spi_master,见下面分析
- if (master == NULL) {
- dev_err(&pdev->dev, "No memory for spi_master\n");
- err = -ENOMEM;
- goto err_nomem;
- }
- hw = spi_master_get_devdata(master); //获得struct s3c24xx_spi
- memset(hw, 0, sizeof(struct s3c24xx_spi));
- hw->master = spi_master_get(master);
- hw->pdata = pdata = pdev->dev.platform_data; //获取平台设备信息,需要我们自己在BSP添加
- 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. */
- /* the spi->mode bits understood by this driver: */
- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; //spi_device.mode flags understood by this controller driver
- master->num_chipselect = hw->pdata->num_cs;//支持的spi外设个数
- master->bus_num = pdata->bus_num; //总线号
- /* setup the state for the bitbang driver */
- 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->master->setup = s3c24xx_spi_setup;
- hw->master->cleanup = s3c24xx_spi_cleanup;
- 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, resource_size(res),
- pdev->name);
- if (hw->ioarea == NULL) {
- dev_err(&pdev->dev, "Cannot reserve region\n");
- err = -ENXIO;
- goto err_no_iores;
- }
- hw->regs = ioremap(res->start, resource_size(res));
- 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) { //设置“设置片选函数”,可以是单独的GPIO,在BSP中配置
- 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);
- } else
- hw->set_cs = pdata->set_cs;
- s3c24xx_spi_initialsetup(hw);//初始化设置寄存器,包括对SPIMOSI,SPIMISO,SPICLK引脚的设置
- /* register our spi controller */
- err = spi_bitbang_start(&hw->bitbang); //见下面分析
- if (err) {
- dev_err(&pdev->dev, "Failed to register SPI master\n");
- goto err_register;
- }
- return 0;
- ………………………………………………………………………………………………………………………………………………….
- }
其中:
- 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]);// master[1]即struct s3c24xx_spi,作为spi_master的私有数据,可以通过spi_master_get_devdata(master)获得其指针。
- return master;
- }
spi_bitbang_start主要实现master->hw->bitbang的初始化设置,包括初始化bitbang中的work_struct、建立工作队列、注册master等等:
- int spi_bitbang_start(struct spi_bitbang *bitbang)
- {
- int status;
- if (!bitbang->master || !bitbang->chipselect)
- return -EINVAL;
- INIT_WORK(&bitbang->work, bitbang_work);//初始化bitbang->work,其工作函数是bitbang_work
- spin_lock_init(&bitbang->lock); //初始化操作
- INIT_LIST_HEAD(&bitbang->queue);
- if (!bitbang->master->mode_bits)
- bitbang->master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
- if (!bitbang->master->transfer)
- bitbang->master->transfer = spi_bitbang_transfer;//真正的数据传输函数
- if (!bitbang->txrx_bufs) { //此处为 s3c24xx_spi_txrx,见s3c24xx_spi_probe函数
- 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->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
- */
- status = spi_register_master(bitbang->master); //将spi master注册到spi_bus_type,见下面分析
- if (status < 0)
- goto err2;
- return status;
- err2:
- destroy_workqueue(bitbang->workqueue);
- err1:
- return status;
- }
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;
- int status = -ENODEV;
- int dynamic = 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)
- return -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注册到spi_bus_type
- 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_device并注册到spi_bus_type
- status = 0;
- done:
- return status;
- }
scan_boardinfo函数:
- static void scan_boardinfo(struct spi_master *master)
- {
- struct boardinfo *bi;
- mutex_lock(&board_lock);
- list_for_each_entry(bi, &board_list, list) { //遍历board_list链表中的每个boardinfo结构
- struct spi_board_info *chip = bi->board_info;
- unsigned n;
- //读取每个boardinfo中的n_board_info个spi_board_info信息分别产生一个spi_device
- for (n = bi->n_board_info; n > 0; n--, chip++) {
- if (chip->bus_num != master->bus_num) //判断是否由当前spi master控制
- continue;
- /* NOTE: this relies on spi_new_device to
- * issue diagnostics when given bogus inputs
- */
- (void) spi_new_device(master, chip); //产生spi device并注册到spi_bus_type
- }
- }
- mutex_unlock(&board_lock);
- }
现在问题出来了,既然是扫描board_list链表产生spi device,那么是在哪里往board_list注册信息的呢?答案是BSP中通过调用spi.c中的spi_register_board_info函数实现的,见下面分析。
- 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); //添加n个spi_board_info信息
- 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); //添加到board_list列表
- mutex_unlock(&board_lock);
- return 0;
- }
spi_new_device函数用于读取已经注册的spi设备信息,产生spi_device并注册到spi_bus_type:
- struct spi_device *spi_new_device(struct spi_master *master,
- struct spi_board_info *chip)
- {
- struct spi_device *proxy;
- int status;
- /* 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).
- */
- proxy = spi_alloc_device(master); //分配spi_device,总线类型为spi_bus_type,见下面分析
- if (!proxy)
- return NULL;
- WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
- // 根据BSP信息,设置spi_device对应字段
- 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;
- }
spi_alloc_device函数
- 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))
- 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->dev.parent = dev;
- spi->dev.bus = &spi_bus_type; //在spi.c注册的spi_bus_type
- spi->dev.release = spidev_release;
- device_initialize(&spi->dev);
- return spi;
- }
spi_add_device函数,用于将spi_device注册到spi_bus_type
- int spi_add_device(struct spi_device *spi)
- {
- static DEFINE_MUTEX(spi_add_lock);
- struct device *dev = spi->master->dev.parent;
- int status;
- /* Chipselects are numbered 0..max; validate. */
- if (spi->chip_select >= spi->master->num_chipselect) { //设备序号大于master支持的最大值?
- dev_err(dev, "cs%d >= max %d\n",
- spi->chip_select,
- spi->master->num_chipselect);
- return -EINVAL;
- }
- /* Set the bus ID string */
- dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
- spi->chip_select);
- /* We need to make sure there's no other device with this
- * chipselect **BEFORE** we call setup(), else we'll trash
- * its configuration. Lock against concurrent add() calls.
- */
- mutex_lock(&spi_add_lock);
- //该设备已经在spi_bus_type中注册?
- if (bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev))
- != NULL) {
- dev_err(dev, "chipselect %d already in use\n",
- spi->chip_select);
- status = -EBUSY;
- goto done;
- }
- /* Drivers may modify this initial i/o setup, but will
- * normally rely on the device being setup. Devices
- * using SPI_CS_HIGH can't coexist well otherwise...
- */
- 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;
- }
- /* Device may be bound to an active driver when this returns */
- status = device_add(&spi->dev); //将spi_device添加到系统中
- 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;
- }
spi_setup函数:
- int spi_setup(struct spi_device *spi)
- {
- unsigned bad_bits;
- int status;
- /* help drivers fail *cleanly* when they need options
- * that aren't supported with their current master
- */
- bad_bits = spi->mode & ~spi->master->mode_bits; //spi master支持该设备的模式位?
- 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;
- //利用master的setup函数进行设置,其值为s3c24xx_spi_setup,在s3c24xx_spi_probe函数中设置
- 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;
- }
s3c24xx_spi_setup函数
- static int s3c24xx_spi_setup(struct spi_device *spi)
- {
- struct s3c24xx_spi_devstate *cs = spi->controller_state;
- struct s3c24xx_spi *hw = to_hw(spi);
- int ret;
- /* allocate settings on the first call */
- if (!cs) {
- cs = kzalloc(sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL);
- if (!cs) {
- dev_err(&spi->dev, "no memory for controller state\n");
- return -ENOMEM;
- }
- cs->spcon = SPCON_DEFAULT;
- cs->hz = -1;
- spi->controller_state = cs;
- }
- /* initialise the state from the device */
- ret = s3c24xx_spi_update_state(spi, NULL); //见下面分析
- if (ret)
- return ret;
- spin_lock(&hw->bitbang.lock);
- if (!hw->bitbang.busy) {
- hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);
- /* need to ndelay for 0.5 clocktick ? */
- }
- spin_unlock(&hw->bitbang.lock);
- return 0;
- }
s3c24xx_spi_update_state主要用于根据spi_device或者spi_transfer设置spi_device->controller_state。
- static int s3c24xx_spi_update_state(struct spi_device *spi,
- struct spi_transfer *t)
- {
- struct s3c24xx_spi *hw = to_hw(spi);
- struct s3c24xx_spi_devstate *cs = spi->controller_state;
- unsigned int bpw;
- unsigned int hz;
- unsigned int div;
- unsigned long clk;
- //transfer存在,则以其作为设置标准
- bpw = t ? t->bits_per_word : spi->bits_per_word;
- hz = t ? t->speed_hz : spi->max_speed_hz;
- if (!bpw)
- bpw = 8;
- if (!hz)
- hz = spi->max_speed_hz;
- if (bpw != 8) {
- dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw);
- return -EINVAL;
- }
- if (spi->mode != cs->mode) {
- u8 spcon = SPCON_DEFAULT;
- if (spi->mode & SPI_CPHA)
- spcon |= S3C2410_SPCON_CPHA_FMTB;
- if (spi->mode & SPI_CPOL)
- spcon |= S3C2410_SPCON_CPOL_HIGH;
- cs->mode = spi->mode;
- cs->spcon = spcon;
- }
- if (cs->hz != hz) {
- clk = clk_get_rate(hw->clk);
- div = DIV_ROUND_UP(clk, hz * 2) - 1;
- if (div > 255)
- div = 255;
- dev_dbg(&spi->dev, "pre-scaler=%d (wanted %d, got %ld)\n",
- div, hz, clk / (2 * (div + 1)));
- cs->hz = hz;
- cs->sppre = div;
- }
- return 0;
- }
总结:
我们发现在设备驱动侧所有的设备信息都是借助于在BSP中提供的设备信息来创建对应的结构体,其实这是linux中的一种思想,将设备和驱动分离。linux内核这么做是为了提高可移植性,当我们添加一些新的设备的时候,只要在BSP中添加设备信息,当驱动注册的时候,会根据你配置的设备信息产生对应的结构,删除设备亦是如此,整个过程只需要我们改动设备侧而驱动是一模一样的,理所当然提高了驱动的可移植性!