歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 基於S3C2440的嵌入式Linux驅動——SPI子系統解讀(二)

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

日期:2017/3/1 10:12:25   编辑:Linux編程

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

第一部分,將對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. static struct resource s3c_spi0_resource[] = {
  3. [0] = {
  4. .start = S3C24XX_PA_SPI,
  5. .end = S3C24XX_PA_SPI + 0x1f,
  6. .flags = IORESOURCE_MEM,
  7. },
  8. [1] = {
  9. .start = IRQ_SPI0,
  10. .end = IRQ_SPI0,
  11. .flags = IORESOURCE_IRQ,
  12. }
  13. };
  14. static u64 s3c_device_spi0_dmamask = 0xffffffffUL;
  15. struct platform_device s3c_device_spi0 = {
  16. .name = "s3c2410-spi",
  17. .id = 0,
  18. .num_resources = ARRAY_SIZE(s3c_spi0_resource),
  19. .resource = s3c_spi0_resource,
  20. .dev = {
  21. .dma_mask = &s3c_device_spi0_dmamask,
  22. .coherent_dma_mask = 0xffffffffUL
  23. }
  24. };

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

該函數首先為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. void __iomem *regs;
  6. int irq;
  7. int len;
  8. int count;
  9. void (*set_cs)(struct s3c2410_spi_info *spi,
  10. int cs, int pol);
  11. /* data buffers */
  12. const unsigned char *tx;
  13. unsigned char *rx;
  14. struct clk *clk;
  15. struct resource *ioarea;
  16. struct spi_master *master;
  17. struct spi_device *curdev;
  18. struct device *dev;
  19. struct s3c2410_spi_info *pdata;
  20. };

接著執行了該條語句:

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