歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

基於S3C2440的嵌入式Linux驅動——SPI子系統解讀(二)

該系列文章將分為四個部分:

第一部分,將對SPI子系統整體進行描述,同時給出SPI的相關數據結構,最後描述SPI總線的注冊。基於S3C2440的嵌入式Linux驅動——SPI子系統解讀(一) http://www.linuxidc.com/Linux/2012-08/68402.htm

第二部分,即本篇文章,該文將對SPI的主控制器(master)驅動進行描述。

第三部分,該文將對SPI設備驅動,也稱protocol 驅動,進行講解。基於S3C2440的嵌入式Linux驅動——SPI子系統解讀(三) http://www.linuxidc.com/Linux/2012-08/68405.htm

第四部分,通過SPI設備驅動留給用戶層的API,我們將從上到下描述數據是如何通過SPI的protocol 驅動,由bitbang中轉,最後由master驅動將數據傳輸出去。 基於S3C2440的嵌入式Linux驅動——SPI子系統解讀(四) http://www.linuxidc.com/Linux/2012-08/68406.htm

本文屬於第二部分。

4. 主控制器驅動程序

4.1 定義 platform device

下列數據結構位於arch/arm/plat-s3c24XX/devs.c

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

platform設備給出了spi0接口的寄存器地址資源以及IRQ資源。注意其設備名為s3c2410-spi。

4.2 定義platform driver

下列函數位於deivers/spi/s3c24xx.c。

  1. MODULE_ALIAS("platform:s3c2410-spi");  
  2. static struct platform_driver s3c24xx_spi_driver = {  
  3.     .remove        = __exit_p(s3c24xx_spi_remove),  
  4.     .suspend    = s3c24xx_spi_suspend,  
  5.     .resume        = s3c24xx_spi_resume,  
  6.     .driver        = {  
  7.         .name    = "s3c2410-spi",  
  8.         .owner    = THIS_MODULE,  
  9.     },  
  10. };  
  11.   
  12. static int __init s3c24xx_spi_init(void)  
  13. {  
  14.         return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);//設備不可熱插拔,所以使用該函數,而不是platform_driver_register   
  15. }  
  16.   
  17. static void __exit s3c24xx_spi_exit(void)  
  18. {  
  19.         platform_driver_unregister(&s3c24xx_spi_driver);  
  20. }  
  21.   
  22. module_init(s3c24xx_spi_init);  
  23. module_exit(s3c24xx_spi_exit);  

調用了platform_driver_probe注冊platform驅動,注冊完成以後將會調用platform的s3c24xx_spi_probe函數。

NOTE:platform驅動的name和platform device的name是相同的。

4.2.1  s3c24xx_spi_probe函數

下列函數位於deivers/spi/s3c24xx.c。

  1. static int __init s3c24xx_spi_probe(struct platform_device *pdev)  
  2. {  
  3.     struct s3c2410_spi_info *pdata;  
  4.     struct s3c24xx_spi *hw;  
  5.     struct spi_master *master;  
  6.     struct resource *res;  
  7.     int err = 0;  
  8.   
  9.     /*分配master結構體,其中���括s3c24xx_spi結構的內存空間,使用master.dev.driver_data指向它*/  
  10.     master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));  
  11.     if (master == NULL) {  
  12.         dev_err(&pdev->dev, "No memory for spi_master\n");  
  13.         err = -ENOMEM;  
  14.         goto err_nomem;  
  15.     }  
  16.     /*獲得s3c24xx_spi結構,並清0該結構*/  
  17.     hw = spi_master_get_devdata(master);  
  18.     memset(hw, 0, sizeof(struct s3c24xx_spi));  
  19.       
  20.     hw->master = spi_master_get(master); /*保存master結構體,同時增加引用計數*/  
  21.     hw->pdata = pdata = pdev->dev.platform_data;  /*獲取s3c2410_spi_info結構體指針*/  
  22.     hw->dev = &pdev->dev;                 /*保存platform設備的dev*/  
  23.   
  24.     if (pdata == NULL) {  
  25.         dev_err(&pdev->dev, "No platform data supplied\n");  
  26.         err = -ENOENT;  
  27.         goto err_no_pdata;  
  28.     }  
  29.   
  30.     platform_set_drvdata(pdev, hw); /*讓platform_device.dev.driver_data 指向 s3c24xx_spi*/  
  31.     init_completion(&hw->done);      /*初始化completion*/  
  32.   
  33.     /* setup the master state. */ /*填充master結構體的兩個字段*/  
  34.       
  35.     master->num_chipselect = hw->pdata->num_cs;  
  36.     master->bus_num = pdata->bus_num;  
  37.   
  38.     /* setup the state for the bitbang driver */    /*填充bitbang字段*/  
  39.   
  40.     hw->bitbang.master         = hw->master;            
  41.     hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;  
  42.     hw->bitbang.chipselect     = s3c24xx_spi_chipsel;  
  43.     hw->bitbang.txrx_bufs      = s3c24xx_spi_txrx;  
  44.     hw->bitbang.master->setup  = s3c24xx_spi_setup;  
  45.   
  46.     dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);  
  47.   
  48.     /* find and map our resources */  
  49.   
  50.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);   /*獲取IO資源*/  
  51.     if (res == NULL) {  
  52.         dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");  
  53.         err = -ENOENT;  
  54.         goto err_no_iores;  
  55.     }  
  56.   
  57.     hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1,  /*申請IO內存*/  
  58.                     pdev->name);  
  59.   
  60.     if (hw->ioarea == NULL) {  
  61.         dev_err(&pdev->dev, "Cannot reserve region\n");  
  62.         err = -ENXIO;  
  63.         goto err_no_iores;  
  64.     }  
  65.   
  66.     hw->regs = ioremap(res->start, (res->end - res->start)+1);      /*建立映射*/  
  67.     if (hw->regs == NULL) {  
  68.         dev_err(&pdev->dev, "Cannot map IO\n");  
  69.         err = -ENXIO;  
  70.         goto err_no_iomap;  
  71.     }  
  72.   
  73.     hw->irq = platform_get_irq(pdev, 0);         /*獲取irq號*/  
  74.     if (hw->irq < 0) {  
  75.         dev_err(&pdev->dev, "No IRQ specified\n");  
  76.         err = -ENOENT;  
  77.         goto err_no_irq;  
  78.     }  
  79.   
  80.     err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw);   /*申請spi中斷,ISR為 s3c24xx_spi_irq*/  
  81.     if (err) {  
  82.         dev_err(&pdev->dev, "Cannot claim IRQ\n");  
  83.         goto err_no_irq;  
  84.     }  
  85.   
  86.     hw->clk = clk_get(&pdev->dev, "spi"); /*獲取spi時鐘*/  
  87.     if (IS_ERR(hw->clk)) {  
  88.         dev_err(&pdev->dev, "No clock for device\n");  
  89.         err = PTR_ERR(hw->clk);  
  90.         goto err_no_clk;  
  91.     }  
  92.   
  93.     /* setup any gpio we can */  
  94.   
  95.     if (!pdata->set_cs) {        /*沒有定義分配CS管腳的函數*/  
  96.         if (pdata->pin_cs < 0) {  /*pin_cs為cs管腳*/  
  97.             dev_err(&pdev->dev, "No chipselect pin\n");  
  98.             goto err_register;  
  99.         }  
  100.   
  101.         err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev));/*申請IO地址*/  
  102.         if (err) {  
  103.             dev_err(&pdev->dev, "Failed to get gpio for cs\n");  
  104.             goto err_register;  
  105.         }  
  106.   
  107.         hw->set_cs = s3c24xx_spi_gpiocs; /*給出分配cs管腳函數*/  
  108.         gpio_direction_output(pdata->pin_cs, 1);/*設置該管腳為輸出模式*/  
  109.     } else  
  110.         hw->set_cs = pdata->set_cs;  
  111.   
  112.     s3c24xx_spi_initialsetup(hw);   /*spi控制器初始化*/  
  113.   
  114.     /* register our spi controller */  
  115.   
  116.     err = spi_bitbang_start(&hw->bitbang);  
  117.     if (err) {  
  118.         dev_err(&pdev->dev, "Failed to register SPI master\n");  
  119.         goto err_register;  
  120.     }  
  121.   
  122.     return 0;  
  123.   
  124.  err_register:  
  125.     if (hw->set_cs == s3c24xx_spi_gpiocs)  
  126.         gpio_free(pdata->pin_cs);  
  127.   
  128.     clk_disable(hw->clk);  
  129.     clk_put(hw->clk);  
  130.   
  131.  err_no_clk:  
  132.     free_irq(hw->irq, hw);  
  133.   
  134.  err_no_irq:  
  135.     iounmap(hw->regs);  
  136.   
  137.  err_no_iomap:  
  138.     release_resource(hw->ioarea);    /*先釋放資源*/  
  139.     kfree(hw->ioarea);               /*再釋放空間*/  
  140.   
  141.  err_no_iores:  
  142.  err_no_pdata:  
  143.     spi_master_put(hw->master);; /*減少引用計數*/  
  144.   
  145.  err_nomem:  
  146.     return err;  
  147. }  

該函數首先為spi_master結構體以及s3c24xx_spi結構體分配了空間,同時,spi_master.dev.driver_data指向了s3c24xx_spi。

s3c24xx_spi結構如下:

  1. struct s3c24xx_spi {  
  2.     /* bitbang has to be first */  
  3.     struct spi_bitbang   bitbang;  
  4.     struct completion    done;  
  5.   
  6.     void __iomem        *regs;  
  7.     int          irq;  
  8.     int          len;  
  9.     int          count;  
  10.   
  11.     void            (*set_cs)(struct s3c2410_spi_info *spi,  
  12.                       int cs, int pol);  
  13.   
  14.     /* data buffers */  
  15.     const unsigned char *tx;  
  16.     unsigned char       *rx;  
  17.   
  18.     struct clk      *clk;  
  19.     struct resource     *ioarea;  
  20.     struct spi_master   *master;  
  21.     struct spi_device   *curdev;  
  22.     struct device       *dev;  
  23.     struct s3c2410_spi_info *pdata;  
  24. };  

接著執行了該條語句:

hw->pdata = pdata = pdev->dev.platform_data;    /*獲取s3c2410_spi_info結構體指針*/

NOTE:在這裡獲取platform_device.dev.platform_data,也就是平台設備的相關數據,而在4.1小結中的arch/arm/plat-s3c24XX/devs.c文件中並沒有發現platform_data的身影,因此這正式需要我們移植的地方。

Copyright © Linux教程網 All Rights Reserved