分享

Linux下SPI从设备驱动的编写

 XeonGate 2015-08-18

 SPI(Serial Peripheral Interface) 是一个同步的四线制串行线,用于连接微控制器和传感器、存储器及外围设备。三条信号线持有时钟信号(SCLK,经常在10MHz左右)和并行数据线带有主出,从进(MOSI)”或是主进,从出(MISO)”信号。数据交换的时候有四种时钟模式,模式0和模式3是最经常使用的。每个时钟周期将会传递数据进和出。如果没有数据传递的话,时钟将不会循环

 

SPI驱动分为两类:

控制器驱动:它们通常内嵌于片上系统处理器,通常既支持主设备,又支持从设备。这些驱动涉及硬件寄存器,可能使用DMA。或它们使用GPIO引脚成为PIO bitbangers。这部分通常会由特定的开发板提供商提供,不用自己写。

协议驱动:它们通过控制器驱动,以SPI连接的方式在主从设备之间传递信息。这部分涉及具体的SPI从设备,通常需要自己编写。

那么特定的目标板如何让Linux 操控SPI设备?下面以AT91SAM9260系列CAN设备驱动为例,Linux内核版本为2.6.19。本文不涉及控制器驱动分析。

board_info提供足够的信息使得系统正常工作而不需要芯片驱动加载

  1. 在arch/arm/mach-at91rm9200/board-at91sam9260.c中有如下代码:  
  2. #include <linux/platform_device.h>  
  3. #include <linux/spi/spi.h>  
  4. …….  
  5. static struct spi_board_info ek_spi_devices[] = {  
  6. /* spi can ,add by mrz */  
  7. #if defined(CONFIG_CAN_MCP2515)    
  8.     {  
  9.         .modalias = "mcp2515",  
  10.         .chip_select = 0,  
  11. //      .controller_data = AT91_PIN_PB3,  
  12.         .irq = AT91_PIN_PC6, //AT91SAM9260_ID_IRQ0,  
  13.         .platform_data = &mcp251x_data,  
  14.         .max_speed_hz = 10 * 1000 * 1000,  
  15.         .bus_num = 1,  
  16.         .mode = 0,  
  17.     }  
  18.     #endif  
  19. };.  
  20. ………  
  21. static void __init ek_board_init(void)  
  22. {  
  23. ……  
  24. /* SPI */  
  25.     at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices));  
  26. }  

这样在Linux初始化时候就可以加载SPI CAN驱动。

 

下面来看MCP2515 CAN驱动的结构,协议驱动有点类似平台设备驱动,本文只列出框架,不涉及具体实现代码,在/driver/CAN/MCP2515.c中:

[c-sharp] view plaincopy
  1. static struct spi_driver mcp251x_driver = {  
  2.     .driver = {  
  3.         .name   = mcp2515,  
  4.         .bus    = &spi_bus_type,  
  5.         .owner  = THIS_MODULE,  
  6.     },  
  7.     .probe  = mcp251x_probe,  
  8.     .remove = __devexit_p(mcp251x_remove),  
  9. #ifdef CONFIG_PM  
  10.     .suspend    = mcp251x_suspend,  
  11.     .resume = mcp251x_resume,  
  12. #endif  
  13. };  
  14. 驱动将自动试图绑定驱动到任何board_info给定别名为" mcp2515"的SPI设备。  
  15. static int __devinit mcp251x_probe(struct spi_device *spi)  
  16. {  
  17.     struct mcp251x *chip;  
  18.     int ret = 0;  
  19.   
  20.     dev_dbg(&spi->dev, "%s: start/n",  __FUNCTION__);  
  21.   
  22.     chip = kmalloc(sizeof(struct mcp251x), GFP_KERNEL);  
  23.     if (!chip) {  
  24.         ret = -ENOMEM;  
  25.         goto error_alloc;  
  26.     }  
  27.   
  28.     dev_set_drvdata(&spi->dev, chip);  
  29.       
  30.     chip->txbin = chip->txbout = 0;  
  31.     chip->rxbin = chip->rxbout = 0;  
  32.     chip->count = 0;  
  33.     chip->spi = spi;  
  34.     init_MUTEX(&chip->lock);  
  35.     init_MUTEX(&chip->txblock);  
  36.     init_MUTEX(&chip->rxblock);  
  37.     init_waitqueue_head(&chip->wq);  
  38.      
  39. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))  
  40.     INIT_WORK(&chip->irq_work, mcp251x_irq_handler);  
  41. #else  
  42.     INIT_WORK(&chip->irq_work, mcp251x_irq_handler, spi);  
  43. #endif  
  44.       
  45.     chip->spi_transfer_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);  
  46.     if (!chip->spi_transfer_buf) {  
  47.         ret = -ENOMEM;  
  48.         goto error_buf;  
  49.     }  
  50.       
  51.     ret = request_irq(spi->irq, mcp251x_irq, SA_SAMPLE_RANDOM, DRIVER_NAME, spi);  
  52.     if (ret < 0) {  
  53.         dev_err(&spi->dev, "request irq %d failed (ret = %d)/n", spi->irq, ret);  
  54.         goto error_irq;  
  55.     }  
  56.       
  57.     cdev_init(&chip->cdev, &mcp251x_fops);  
  58.     chip->cdev.owner = THIS_MODULE;  
  59.     ret = cdev_add(&chip->cdev, MKDEV(MAJOR(can_devt), can_minor), 1);  
  60.     if (ret < 0) {  
  61.         dev_err(&spi->dev, "register char device failed (ret = %d)/n", ret);  
  62.         goto error_register;  
  63.     }  
  64.   
  65.     chip->class_dev = class_device_create(can_class, NULL,  
  66.                           MKDEV(MAJOR(can_devt), can_minor),  
  67.                           &spi->dev, "can%d", can_minor);  
  68.     if (IS_ERR(chip->class_dev)) {  
  69.         dev_err(&spi->dev, "cannot create CAN class device/n");  
  70.         ret = PTR_ERR(chip->class_dev);  
  71.         goto error_class_reg;  
  72.     }  
  73.       
  74.     dev_info(&spi->dev, "device register at dev(%d:%d)/n",  
  75.          MAJOR(can_devt), can_minor);  
  76.       
  77.     mcp251x_hw_init(spi);  
  78.     mcp251x_set_bit_rate(spi, 125000); /* A reasonable default */  
  79.     mcp251x_hw_sleep(spi);  
  80.   
  81.     can_minor++;  
  82.       
  83.     return 0;  
  84.       
  85. error_class_reg:  
  86.     cdev_del(&chip->cdev);  
  87. error_register:  
  88.     free_irq(spi->irq, spi);  
  89. error_irq:  
  90.     kfree(chip->spi_transfer_buf);  
  91. error_buf:  
  92.     kfree(chip);  
  93. error_alloc:  
  94.     return ret;  
  95. }  
  

一旦进入probe(),驱动使用"struct spi_message"SPI设备要求I/O。当remove()返回时,驱动保证将不会提交任何这种信息。
一个spi_message是协议操作序列,以一个原子序列执行。SPI驱动控制包括:
   
1)当双向读写开始,是根据spi_transfer要求序列是怎样安排的。
   
2)随意设定传递后的短延时,使用spi_transfer.delay_usecs设定。
   
3)在一次传递和任何延时之后,无论片选是否活跃,使用spi_transfer.cs_change标志,    暗示下条信息是否进入这个同样的设备,使用原子组中最后一次传输上的spi_transfer.cs_change标志位,可能节省芯片选择取消操作的成本。

 

 

 

 

 

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多