深入浅出spi驱动之控制器驱动(二)

1010阅读 0评论2015-07-31 hushup
分类:LINUX

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。

[csharp]
  1. struct bus_type spi_bus_type = {        //spi_device和spi_driver都向其注册 
  2.     .name       = "spi"
  3.     .dev_attrs  = spi_dev_attrs, 
  4.     .match      = spi_match_device, //总线的match函数 
  5.     .uevent     = spi_uevent, 
  6.     .suspend    = spi_suspend, 
  7.     .resume     = spi_resume, 
  8. }; 
  9.  
  10. static int __init spi_init(void)        //初始化函数 
  11.     int status; 
  12.  
  13.     buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); 
  14.     if (!buf) { 
  15.         status = -ENOMEM; 
  16.         goto err0; 
  17.     } 
  18.  
  19.     status = bus_register(&spi_bus_type);           //注册spi_bus_type 
  20.     if (status < 0) 
  21.         goto err1; 
  22.  
  23.     status = class_register(&spi_master_class);     //创建spi_master_class类 
  24.     if (status < 0) 
  25.         goto err2; 
  26.     return 0; 
  27.  
  28. err2: 
  29.     bus_unregister(&spi_bus_type); 
  30. err1: 
  31.     kfree(buf); 
  32.     buf = NULL; 
  33. err0: 
  34.     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函数实现),看其初始化函数

[csharp]
  1. static struct platform_driver s3c24xx_spi_driver = { 
  2.     .remove     = __exit_p(s3c24xx_spi_remove), 
  3.     .driver     = { 
  4.         .name   = "s3c2410-spi"
  5.         .owner  = THIS_MODULE, 
  6.         .pm = S3C24XX_SPI_PMOPS, 
  7.     }, 
  8. }; 
  9. static int __init s3c24xx_spi_init(void
  10.         return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe); 


Master controller设备需要在BSP中向platfor_bus注册,具体是在/arch/arm/plat-s3c24xx/devs.c中定义:


[csharp]
  1. static struct resource s3c_spi0_resource[] = {       
  2.     [0] = { 
  3.         .start = S3C24XX_PA_SPI, 
  4.         .end   = S3C24XX_PA_SPI + 0x1f, 
  5.         .flags = IORESOURCE_MEM, 
  6.     }, 
  7.     [1] = { 
  8.         .start = IRQ_SPI0, 
  9.         .end   = IRQ_SPI0, 
  10.         .flags = IORESOURCE_IRQ, 
  11.     } 
  12.  
  13. }; 
  14.  
  15. static u64 s3c_device_spi0_dmamask = 0xffffffffUL; 
  16.  
  17. struct platform_device s3c_device_spi0 = {              //spi控制器信息 
  18.     .name         = "s3c2410-spi"
  19.     .id       = 0, 
  20.     .num_resources    = ARRAY_SIZE(s3c_spi0_resource), 
  21.     .resource     = s3c_spi0_resource, 
  22.         .dev              = { 
  23.                 .dma_mask = &s3c_device_spi0_dmamask, 
  24.                 .coherent_dma_mask = 0xffffffffUL 
  25.         } 
  26. }; 


并在/arch/arm/mach-s3c2440/mach-mini2440.c中向platform_bus进行注册

[csharp]
  1. static struct platform_device *mini2440_devices[] __initdata = { 
  2.     &s3c_device_usb, 
  3.     &s3c_device_rtc, 
  4.     &s3c_device_lcd, 
  5.     &s3c_device_wdt, 
  6.     &s3c_device_i2c0, 
  7.     &s3c_device_iis, 
  8.     &mini2440_device_eth, 
  9.     &s3c24xx_uda134x, 
  10.     &s3c_device_nand, 
  11.     &s3c_device_sdi, 
  12.     &s3c_device_usbgadget, 
  13. &s3c_device_spi0, 
  14. }; 
  15.  
  16. static void __init mini2440_machine_init(void
  17. #if defined (LCD_WIDTH) 
  18.     s3c24xx_fb_set_platdata(&mini2440_fb_info); 
  19. #endif 
  20.     s3c_i2c0_set_platdata(NULL); 
  21.  
  22.     s3c2410_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND); 
  23.  
  24.     s3c_device_nand.dev.platform_data = &friendly_arm_nand_info; 
  25.     s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg; 
  26.     platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices)); 
  27.     s3c_pm_init(); 
  28.  
  29. MACHINE_START(MINI2440, "FriendlyARM Mini2440 development board"
  30.     .phys_io    = S3C2410_PA_UART, 
  31.     .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc, 
  32.     .boot_params    = S3C2410_SDRAM_PA + 0x100, 
  33.  
  34.     .init_irq   = s3c24xx_init_irq, 
  35.     .map_io     = mini2440_map_io, 
  36.     .init_machine   = mini2440_machine_init, 
  37.     .timer      = &s3c24xx_timer, 
  38. MACHINE_END 


我们回头看platform_bus_type的match函数:

[csharp]
  1. struct bus_type platform_bus_type = { 
  2.     .name       = "platform"
  3.     .dev_attrs  = platform_dev_attrs, 
  4.     .match      = platform_match, 
  5.     .uevent     = platform_uevent, 
  6.     .pm     = &platform_dev_pm_ops, 
  7. }; 
  8.  
  9. static int platform_match(struct device *dev, struct device_driver *drv) 
  10.     struct platform_device *pdev = to_platform_device(dev); 
  11.     struct platform_driver *pdrv = to_platform_driver(drv); 
  12.  
  13.     /* match against the id table first */ 
  14.     if (pdrv->id_table) 
  15.         return platform_match_id(pdrv->id_table, pdev) != NULL; 
  16.  
  17.     /* fall-back to driver name match */ 
  18.     return (strcmp(pdev->name, drv->name) == 0); 
  19. }    



分析可知BSP中注册的控制器名字和spi_s3c24xx.c注册的驱动名字可以通过名字进行匹配,紧接着会调用s3c24xx_spi_probe函数:

[csharp]
  1. static int __init s3c24xx_spi_probe(struct platform_device *pdev) 
  2.     struct s3c2410_spi_info *pdata; 
  3.     struct s3c24xx_spi *hw; 
  4.     struct spi_master *master; 
  5.     struct resource *res; 
  6.     int err = 0; 
  7.  
  8.     master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));   //分配spi_master,见下面分析 
  9.     if (master == NULL) { 
  10.         dev_err(&pdev->dev, "No memory for spi_master\n"); 
  11.         err = -ENOMEM; 
  12.         goto err_nomem; 
  13.     } 
  14.  
  15.     hw = spi_master_get_devdata(master);        //获得struct s3c24xx_spi 
  16.     memset(hw, 0, sizeof(struct s3c24xx_spi)); 
  17.  
  18.     hw->master = spi_master_get(master); 
  19.     hw->pdata = pdata = pdev->dev.platform_data;  //获取平台设备信息,需要我们自己在BSP添加 
  20.     hw->dev = &pdev->dev; 
  21.  
  22.     if (pdata == NULL) { 
  23.         dev_err(&pdev->dev, "No platform data supplied\n"); 
  24.         err = -ENOENT; 
  25.         goto err_no_pdata; 
  26.     } 
  27.  
  28.     platform_set_drvdata(pdev, hw); 
  29.     init_completion(&hw->done); 
  30.  
  31.     /* setup the master state. */ 
  32.  
  33.     /* the spi->mode bits understood by this driver: */ 
  34.     master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; //spi_device.mode flags understood by this controller driver 
  35.     master->num_chipselect = hw->pdata->num_cs;//支持的spi外设个数 
  36.     master->bus_num = pdata->bus_num;         //总线号 
  37.  
  38.     /* setup the state for the bitbang driver */ 
  39.     hw->bitbang.master         = hw->master; 
  40.     hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer; 
  41.     hw->bitbang.chipselect     = s3c24xx_spi_chipsel; 
  42.     hw->bitbang.txrx_bufs      = s3c24xx_spi_txrx; 
  43.  
  44.     hw->master->setup  = s3c24xx_spi_setup; 
  45.     hw->master->cleanup = s3c24xx_spi_cleanup; 
  46.  
  47.     dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); 
  48.  
  49.     /* find and map our resources */ 
  50.  
  51.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);   //获取资源 
  52.     if (res == NULL) { 
  53.         dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); 
  54.         err = -ENOENT; 
  55.         goto err_no_iores; 
  56.     } 
  57.  
  58.     hw->ioarea = request_mem_region(res->start, resource_size(res), 
  59.                     pdev->name); 
  60.  
  61.     if (hw->ioarea == NULL) { 
  62.         dev_err(&pdev->dev, "Cannot reserve region\n"); 
  63.         err = -ENXIO; 
  64.         goto err_no_iores; 
  65.     } 
  66.  
  67.     hw->regs = ioremap(res->start, resource_size(res)); 
  68.     if (hw->regs == NULL) { 
  69.         dev_err(&pdev->dev, "Cannot map IO\n"); 
  70.         err = -ENXIO; 
  71.         goto err_no_iomap; 
  72.     } 
  73.  
  74.     hw->irq = platform_get_irq(pdev, 0); 
  75.     if (hw->irq < 0) { 
  76.         dev_err(&pdev->dev, "No IRQ specified\n"); 
  77.         err = -ENOENT; 
  78.         goto err_no_irq; 
  79.     } 
  80.  
  81.     err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw); //申请中断 
  82.     if (err) { 
  83.         dev_err(&pdev->dev, "Cannot claim IRQ\n"); 
  84.         goto err_no_irq; 
  85.     } 
  86.  
  87.     hw->clk = clk_get(&pdev->dev, "spi"); //获取时钟 
  88.     if (IS_ERR(hw->clk)) { 
  89.         dev_err(&pdev->dev, "No clock for device\n"); 
  90.         err = PTR_ERR(hw->clk); 
  91.         goto err_no_clk; 
  92.     } 
  93.  
  94.     /* setup any gpio we can */ 
  95.  
  96.     if (!pdata->set_cs) {        //设置“设置片选函数”,可以是单独的GPIO,在BSP中配置 
  97.         if (pdata->pin_cs < 0) { 
  98.             dev_err(&pdev->dev, "No chipselect pin\n"); 
  99.             goto err_register; 
  100.         } 
  101.  
  102.         err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev)); 
  103.         if (err) { 
  104.             dev_err(&pdev->dev, "Failed to get gpio for cs\n"); 
  105.             goto err_register; 
  106.         } 
  107.  
  108.         hw->set_cs = s3c24xx_spi_gpiocs; 
  109.         gpio_direction_output(pdata->pin_cs, 1); 
  110.     } else 
  111.         hw->set_cs = pdata->set_cs; 
  112.  
  113.     s3c24xx_spi_initialsetup(hw);//初始化设置寄存器,包括对SPIMOSI,SPIMISO,SPICLK引脚的设置 
  114.  
  115.     /* register our spi controller */ 
  116.  
  117.     err = spi_bitbang_start(&hw->bitbang);       //见下面分析 
  118.     if (err) { 
  119.         dev_err(&pdev->dev, "Failed to register SPI master\n"); 
  120.         goto err_register; 
  121.     } 
  122.  
  123.     return 0; 
  124. …………………………………………………………………………………………………………………………………………………. 


其中:

[csharp]
  1. struct spi_master *spi_alloc_master(struct device *dev, unsigned size) 
  2.     struct spi_master   *master; 
  3.  
  4.     if (!dev) 
  5.         return NULL; 
  6.  
  7.     master = kzalloc(size + sizeof *master, GFP_KERNEL);    //分配空间 
  8.     if (!master) 
  9.         return NULL; 
  10.  
  11.     device_initialize(&master->dev); 
  12.     master->dev.class = &spi_master_class; 
  13.     master->dev.parent = get_device(dev); 
  14.     spi_master_set_devdata(master, &master[1]);// master[1]即struct s3c24xx_spi,作为spi_master的私有数据,可以通过spi_master_get_devdata(master)获得其指针。 
  15.  
  16.     return master; 


spi_bitbang_start主要实现master->hw->bitbang的初始化设置,包括初始化bitbang中的work_struct、建立工作队列、注册master等等:

[csharp]
  1. int spi_bitbang_start(struct spi_bitbang *bitbang) 
  2.     int status; 
  3.  
  4.     if (!bitbang->master || !bitbang->chipselect) 
  5.         return -EINVAL; 
  6.  
  7.     INIT_WORK(&bitbang->work, bitbang_work);//初始化bitbang->work,其工作函数是bitbang_work 
  8.     spin_lock_init(&bitbang->lock);  //初始化操作 
  9.     INIT_LIST_HEAD(&bitbang->queue); 
  10.  
  11.     if (!bitbang->master->mode_bits) 
  12.         bitbang->master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags; 
  13.  
  14.     if (!bitbang->master->transfer) 
  15.         bitbang->master->transfer = spi_bitbang_transfer;//真正的数据传输函数 
  16.     if (!bitbang->txrx_bufs) {       //此处为 s3c24xx_spi_txrx,见s3c24xx_spi_probe函数 
  17.         bitbang->use_dma = 0; 
  18.         bitbang->txrx_bufs = spi_bitbang_bufs; 
  19.         if (!bitbang->master->setup) { 
  20.             if (!bitbang->setup_transfer) 
  21.                 bitbang->setup_transfer = 
  22.                      spi_bitbang_setup_transfer; 
  23.             bitbang->master->setup = spi_bitbang_setup; 
  24.             bitbang->master->cleanup = spi_bitbang_cleanup; 
  25.         } 
  26.     } else if (!bitbang->master->setup) 
  27.         return -EINVAL; 
  28.  
  29.     /* this task is the only thing to touch the SPI bits */ 
  30.     bitbang->busy = 0; 
  31.     bitbang->workqueue = create_singlethread_workqueue(  //建立工作队列 
  32.             dev_name(bitbang->master->dev.parent)); 
  33.     if (bitbang->workqueue == NULL) { 
  34.         status = -EBUSY; 
  35.         goto err1; 
  36.     } 
  37.  
  38.     /* driver may get busy before register() returns, especially
  39.      * if someone registered boardinfo for devices
  40.      */ 
  41.     status = spi_register_master(bitbang->master);   //将spi master注册到spi_bus_type,见下面分析 
  42.     if (status < 0) 
  43.         goto err2; 
  44.  
  45.     return status; 
  46.  
  47. err2: 
  48.     destroy_workqueue(bitbang->workqueue); 
  49. err1: 
  50.     return status; 



spi_register_master函数:

[csharp]
  1. int spi_register_master(struct spi_master *master) 
  2.     static atomic_t     dyn_bus_id = ATOMIC_INIT((1<<15) - 1); 
  3.     struct device       *dev = master->dev.parent; 
  4.     int         status = -ENODEV; 
  5.     int         dynamic = 0; 
  6.  
  7.     if (!dev) 
  8.         return -ENODEV; 
  9.  
  10.     /* even if it's just one always-selected device, there must
  11.      * be at least one chipselect
  12.      */ 
  13.     if (master->num_chipselect == 0) 
  14.         return -EINVAL; 
  15.  
  16.     /* convention:  dynamically assigned bus IDs count down from the max */ 
  17.     if (master->bus_num < 0) { 
  18.         /* FIXME switch to an IDR based scheme, something like
  19.          * I2C now uses, so we can't run out of "dynamic" IDs
  20.          */ 
  21.         master->bus_num = atomic_dec_return(&dyn_bus_id); 
  22.         dynamic = 1; 
  23.     } 
  24.  
  25.     /* register the device, then userspace will see it.
  26.      * registration fails if the bus ID is in use.
  27.      */ 
  28.     dev_set_name(&master->dev, "spi%u", master->bus_num); 
  29.     status = device_add(&master->dev);   //将spi master注册到spi_bus_type 
  30.     if (status < 0) 
  31.         goto done; 
  32.     dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), 
  33.             dynamic ? " (dynamic)" : ""); 
  34.  
  35.     /* populate children from any spi device tables */ 
  36.     scan_boardinfo(master);  //扫描设备链表,产生spi_device并注册到spi_bus_type 
  37.     status = 0; 
  38. done: 
  39.     return status; 


scan_boardinfo函数:

[csharp]
  1. static void scan_boardinfo(struct spi_master *master) 
  2.     struct boardinfo    *bi; 
  3.  
  4.     mutex_lock(&board_lock); 
  5.     list_for_each_entry(bi, &board_list, list) {    //遍历board_list链表中的每个boardinfo结构 
  6.         struct spi_board_info   *chip = bi->board_info; 
  7.         unsigned        n; 
  8.         //读取每个boardinfo中的n_board_info个spi_board_info信息分别产生一个spi_device 
  9.         for (n = bi->n_board_info; n > 0; n--, chip++) { 
  10.             if (chip->bus_num != master->bus_num)  //判断是否由当前spi master控制 
  11.                 continue
  12.             /* NOTE: this relies on spi_new_device to
  13.              * issue diagnostics when given bogus inputs
  14.              */ 
  15.             (void) spi_new_device(master, chip);    //产生spi device并注册到spi_bus_type 
  16.         } 
  17.     } 
  18.     mutex_unlock(&board_lock); 


现在问题出来了,既然是扫描board_list链表产生spi device,那么是在哪里往board_list注册信息的呢?答案是BSP中通过调用spi.c中的spi_register_board_info函数实现的,见下面分析。

[csharp]
  1. int __init 
  2. spi_register_board_info(struct spi_board_info const *info, unsigned n) 
  3.     struct boardinfo    *bi; 
  4.  
  5.     bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL); //添加n个spi_board_info信息 
  6.     if (!bi) 
  7.         return -ENOMEM; 
  8.     bi->n_board_info = n; 
  9.     memcpy(bi->board_info, info, n * sizeof *info); 
  10.  
  11.     mutex_lock(&board_lock); 
  12.     list_add_tail(&bi->list, &board_list);   //添加到board_list列表 
  13.     mutex_unlock(&board_lock); 
  14.     return 0; 


spi_new_device函数用于读取已经注册的spi设备信息,产生spi_device并注册到spi_bus_type:

[csharp]
  1. struct spi_device *spi_new_device(struct spi_master *master, 
  2.                   struct spi_board_info *chip) 
  3.     struct spi_device   *proxy; 
  4.     int         status; 
  5.  
  6.     /* NOTE:  caller did any chip->bus_num checks necessary.
  7.      *
  8.      * Also, unless we change the return value convention to use
  9.      * error-or-pointer (not NULL-or-pointer), troubleshootability
  10.      * suggests syslogged diagnostics are best here (ugh).
  11.      */ 
  12.  
  13.     proxy = spi_alloc_device(master);   //分配spi_device,总线类型为spi_bus_type,见下面分析 
  14.     if (!proxy) 
  15.         return NULL; 
  16.  
  17.     WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); 
  18.     // 根据BSP信息,设置spi_device对应字段 
  19.     proxy->chip_select = chip->chip_select; 
  20.     proxy->max_speed_hz = chip->max_speed_hz; 
  21.     proxy->mode = chip->mode; 
  22.     proxy->irq = chip->irq; 
  23.     strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); 
  24.     proxy->dev.platform_data = (void *) chip->platform_data; 
  25.     proxy->controller_data = chip->controller_data; 
  26.     proxy->controller_state = NULL; 
  27.  
  28.     status = spi_add_device(proxy); 
  29.     if (status < 0) { 
  30.         spi_dev_put(proxy); 
  31.         return NULL; 
  32.     } 
  33.  
  34.     return proxy; 


spi_alloc_device函数

[csharp]
  1. struct spi_device *spi_alloc_device(struct spi_master *master) 
  2.     struct spi_device   *spi; 
  3.     struct device       *dev = master->dev.parent; 
  4.  
  5.     if (!spi_master_get(master)) 
  6.         return NULL; 
  7.  
  8.     spi = kzalloc(sizeof *spi, GFP_KERNEL); 
  9.     if (!spi) { 
  10.         dev_err(dev, "cannot alloc spi_device\n"); 
  11.         spi_master_put(master); 
  12.         return NULL; 
  13.     } 
  14.  
  15.     spi->master = master; 
  16.     spi->dev.parent = dev; 
  17.     spi->dev.bus = &spi_bus_type;    //在spi.c注册的spi_bus_type 
  18.     spi->dev.release = spidev_release; 
  19.     device_initialize(&spi->dev); 
  20.     return spi; 


spi_add_device函数,用于将spi_device注册到spi_bus_type

[csharp]
  1. int spi_add_device(struct spi_device *spi) 
  2.     static DEFINE_MUTEX(spi_add_lock); 
  3.     struct device *dev = spi->master->dev.parent; 
  4.     int status; 
  5.  
  6.     /* Chipselects are numbered 0..max; validate. */ 
  7.     if (spi->chip_select >= spi->master->num_chipselect) { //设备序号大于master支持的最大值? 
  8.         dev_err(dev, "cs%d >= max %d\n"
  9.             spi->chip_select, 
  10.             spi->master->num_chipselect); 
  11.         return -EINVAL; 
  12.     } 
  13.  
  14.     /* Set the bus ID string */ 
  15.     dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev), 
  16.             spi->chip_select); 
  17.  
  18.  
  19.     /* We need to make sure there's no other device with this
  20.      * chipselect **BEFORE** we call setup(), else we'll trash
  21.      * its configuration.  Lock against concurrent add() calls.
  22.      */ 
  23.     mutex_lock(&spi_add_lock); 
  24.     //该设备已经在spi_bus_type中注册? 
  25.     if (bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)) 
  26.             != NULL) { 
  27.         dev_err(dev, "chipselect %d already in use\n"
  28.                 spi->chip_select); 
  29.         status = -EBUSY; 
  30.         goto done; 
  31.     } 
  32.  
  33.     /* Drivers may modify this initial i/o setup, but will
  34.      * normally rely on the device being setup.  Devices
  35.      * using SPI_CS_HIGH can't coexist well otherwise...
  36.      */ 
  37.     status = spi_setup(spi);    //对设备模式及时钟进行设置,见下面分析 
  38.     if (status < 0) { 
  39.         dev_err(dev, "can't %s %s, status %d\n"
  40.                 "setup", dev_name(&spi->dev), status); 
  41.         goto done; 
  42.     } 
  43.  
  44.     /* Device may be bound to an active driver when this returns */ 
  45.     status = device_add(&spi->dev);  //将spi_device添加到系统中 
  46.     if (status < 0) 
  47.         dev_err(dev, "can't %s %s, status %d\n"
  48.                 "add", dev_name(&spi->dev), status); 
  49.     else 
  50.         dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev)); 
  51.  
  52. done: 
  53.     mutex_unlock(&spi_add_lock); 
  54.     return status; 


spi_setup函数:

[csharp]
  1. int spi_setup(struct spi_device *spi) 
  2.     unsigned    bad_bits; 
  3.     int     status; 
  4.  
  5.     /* help drivers fail *cleanly* when they need options
  6.      * that aren't supported with their current master
  7.      */ 
  8.     bad_bits = spi->mode & ~spi->master->mode_bits; //spi master支持该设备的模式位? 
  9.     if (bad_bits) { 
  10.         dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n"
  11.             bad_bits); 
  12.         return -EINVAL; 
  13.     } 
  14.  
  15.     if (!spi->bits_per_word) 
  16.         spi->bits_per_word = 8; 
  17.  
  18. //利用master的setup函数进行设置,其值为s3c24xx_spi_setup,在s3c24xx_spi_probe函数中设置 
  19.     status = spi->master->setup(spi); //见下面分析 
  20.  
  21.     dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s" 
  22.                 "%u bits/w, %u Hz max --> %d\n"
  23.             (int) (spi->mode & (SPI_CPOL | SPI_CPHA)), 
  24.             (spi->mode & SPI_CS_HIGH) ? "cs_high, " : ""
  25.             (spi->mode & SPI_LSB_FIRST) ? "lsb, " : ""
  26.             (spi->mode & SPI_3WIRE) ? "3wire, " : ""
  27.             (spi->mode & SPI_LOOP) ? "loopback, " : ""
  28.             spi->bits_per_word, spi->max_speed_hz, 
  29.             status); 
  30.  
  31.     return status; 


s3c24xx_spi_setup函数

[csharp]
  1. static int s3c24xx_spi_setup(struct spi_device *spi) 
  2.     struct s3c24xx_spi_devstate *cs = spi->controller_state; 
  3.     struct s3c24xx_spi *hw = to_hw(spi); 
  4.     int ret; 
  5.  
  6.     /* allocate settings on the first call */ 
  7.     if (!cs) { 
  8.         cs = kzalloc(sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL);   
  9.         if (!cs) { 
  10.             dev_err(&spi->dev, "no memory for controller state\n"); 
  11.             return -ENOMEM; 
  12.         } 
  13.  
  14.         cs->spcon = SPCON_DEFAULT; 
  15.         cs->hz = -1; 
  16.         spi->controller_state = cs; 
  17.     } 
  18.  
  19.     /* initialise the state from the device */ 
  20.     ret = s3c24xx_spi_update_state(spi, NULL);  //见下面分析 
  21.     if (ret) 
  22.         return ret; 
  23.  
  24.     spin_lock(&hw->bitbang.lock); 
  25.     if (!hw->bitbang.busy) { 
  26.         hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); 
  27.         /* need to ndelay for 0.5 clocktick ? */ 
  28.     } 
  29.     spin_unlock(&hw->bitbang.lock); 
  30.  
  31.     return 0; 



s3c24xx_spi_update_state主要用于根据spi_device或者spi_transfer设置spi_device->controller_state。

[csharp]
  1. static int s3c24xx_spi_update_state(struct spi_device *spi, 
  2.                     struct spi_transfer *t) 
  3.     struct s3c24xx_spi *hw = to_hw(spi); 
  4.     struct s3c24xx_spi_devstate *cs = spi->controller_state; 
  5.     unsigned int bpw; 
  6.     unsigned int hz; 
  7.     unsigned int div; 
  8.     unsigned long clk; 
  9.     //transfer存在,则以其作为设置标准 
  10.     bpw = t ? t->bits_per_word : spi->bits_per_word;  
  11.     hz  = t ? t->speed_hz : spi->max_speed_hz; 
  12.  
  13.     if (!bpw) 
  14.         bpw = 8; 
  15.  
  16.     if (!hz) 
  17.         hz = spi->max_speed_hz; 
  18.  
  19.     if (bpw != 8) { 
  20.         dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw); 
  21.         return -EINVAL; 
  22.     } 
  23.  
  24.     if (spi->mode != cs->mode) { 
  25.         u8 spcon = SPCON_DEFAULT; 
  26.  
  27.         if (spi->mode & SPI_CPHA) 
  28.             spcon |= S3C2410_SPCON_CPHA_FMTB; 
  29.  
  30.         if (spi->mode & SPI_CPOL) 
  31.             spcon |= S3C2410_SPCON_CPOL_HIGH; 
  32.  
  33.         cs->mode = spi->mode; 
  34.         cs->spcon = spcon; 
  35.     } 
  36.  
  37.     if (cs->hz != hz) { 
  38.         clk = clk_get_rate(hw->clk); 
  39.         div = DIV_ROUND_UP(clk, hz * 2) - 1; 
  40.  
  41.         if (div > 255) 
  42.             div = 255; 
  43.  
  44.         dev_dbg(&spi->dev, "pre-scaler=%d (wanted %d, got %ld)\n"
  45.             div, hz, clk / (2 * (div + 1))); 
  46.  
  47.         cs->hz = hz; 
  48.         cs->sppre = div; 
  49.     } 
  50.  
  51.     return 0; 



总结:

我们发现在设备驱动侧所有的设备信息都是借助于在BSP中提供的设备信息来创建对应的结构体,其实这是linux中的一种思想,将设备和驱动分离。linux内核这么做是为了提高可移植性,当我们添加一些新的设备的时候,只要在BSP中添加设备信息,当驱动注册的时候,会根据你配置的设备信息产生对应的结构,删除设备亦是如此,整个过程只需要我们改动设备侧而驱动是一模一样的,理所当然提高了驱动的可移植性!

上一篇:转一篇UBOOT调试的
下一篇:Mac与Phy组成原理的简单分析