当前位置:   article > 正文

Linux驱动学习笔记:spi设备驱动_linux spi驱动

linux spi驱动

目录

一、spi协议概述

二、spi驱动框架

三、spi设备树

四、分析spidev.c


一、spi协议概述

       1、硬件原理:

        SPI接口共有4根信号线,分别是:设备选择线(CS)、时钟线(SCLK)、串行输出数据线(MOSI)、串行输入数据线(MISO);spi控制器可以同时连接多个spi设备,但同一时刻只能有一个 SPI 设备处于工作状态,即多个 CS 信号中某时间只能有一个有效。

2、通讯方式

        通过 CS 片选信号使能外部 SPI 设备后,SCLK 同步数据传输。MOSI 和 MISO 信号在 SCLK 上升沿变化,在下降沿锁存数据。

 

3.工作模式

        spi有四种工作模式,具体由CPOL,CPHA决定;

        CPOL:时钟极性,SCLK信号初始电平;CPHA:数据是在第一个上升沿采集数据还是第二个上升沿采集数据;

CPOLSCLK模式含义
001初始电平为低电平,在第一个时钟沿采样数据
012初始电平为低电平,在第二个时钟沿采样数据
103初始电平为高电平,在第一个时钟沿采样数据
114初始电平为高电平,在第二个时钟沿采样数据

        常用的是模式 0 和模式 3,因为它们都是 在上升沿采样数据,不用去在乎时钟的初始电平是什么,只要在上升沿采集数据 就行。

二、spi驱动框架

         spi驱动框架大致分为spi核心层,spi控制器驱动和spi设备驱动。

        spi核心层向控制器驱动层提供注册SPI控制器驱动的接口,并提供一些需要控制器驱动实现的回调函数。核心层向上,对SPI设备驱动,提供标准的SPI收发API,以及设备注册函数。

        spi控制器驱动会根据设备树里面描述的spi控制器的硬件信息注册一个spi_master,还会解析设备树中该spi控制器的子节点,创建spi_device结构体。驱动实现在Linux/drivers/spi/spi.c。

        spi设备驱动框架主要是注册具体设备,实现硬件相关的读写接口,参考内核代码Linuc/drivers/spi/spidev.c 。

三、spi设备树

        在设备树里,使用一个节点来表示SPI Master,使用子节点来表示挂在下面的SPI设备。设备树节点参考如下代码:

  1. spi {
  2. /*spi控制器*/
  3. #address-cells = <1>;
  4. #size-cells = <0>;
  5. compatible = "fsl,spi";
  6. reg = <0xf00 0x20>;
  7. interrupts = <2 13 0 2 14 0>;
  8. interrupt-parent = <&mpc5200_pic>;
  9. /*spi设备节点1*/
  10. ethernet-switch@0 {
  11. compatible = "micrel,ks8995m";
  12. spi-max-frequency = <1000000>;
  13. reg = <0>;
  14. };
  15. /*spi设备节点2*/
  16. codec@1 {
  17. compatible = "ti,tlv320aic26";
  18. spi-max-frequency = <100000>;
  19. reg = <1>;
  20. };
  21. };

        对于spi master,必须的属性如下:

  • #address-cells:这个spi master的SPI设备,需要多少个cell来表述它的片选引脚。
  • #size-cells:必须设置为0。
  • compatible:根据它找到spi master驱动。

        可选的属性如下:

  • cs-gpios:spi master可以使用多个GPIO当做片选,可以在这个属性列出那些GPIO。

        对于spi设备,必选的属性如下:

  • compatible:根据它找到SPI Device驱动。
  • reg:用来表示它使用哪个片选引脚。
  • spi-max-frequency:必选,该SPI设备支持的最大SPI时钟。

        可选的属性如下:

  • spi-cpol:这是一个空属性(没有值),表示CPOL为1,即平时SPI时钟为低电平。
  • spi-cpha:这是一个空属性(没有值),表示CPHA为1),即在时钟的第2个边沿采样数据。

   四、分析spidev.c

        先从入口函数看起,在入口函数中主要注册了一个字符设备spidev_fops,和spidev_spi_driver;

         spidev.c是一个通用的spi设备驱动程序,设备树某个spi节点下的子设备的compatible属性为下边spidev_dt_ids的某个,就会跟spidev匹配,既然是通用的spi驱动,那么肯定不止支持下面的几个,根据匹配关系发现,只要是设备树的compatible属性为"spidev",就能跟spidev匹配成功。

         匹配之后,spidev.c的spidev_probe会被调用,在probe里面首先分配一个spidev_data结构体,用来记录对应的spi_device, spidev_data会被记录在一个链表里;分配一个次设备号,以后可以根据这个次设备号在链表里找到spidev_data;生成一个设备节点 /dev/spidevB.D ,B表示总线号,D表示它是这个SPI Master下第几个设备,以后,我们就可以通过/dev/spidevB.D来访问spidev驱动程序。

  1. static int spidev_probe(struct spi_device *spi)
  2. {
  3. struct spidev_data *spidev;
  4. int status;
  5. unsigned long minor;
  6. /*
  7. * spidev should never be referenced in DT without a specific
  8. * compatible string, it is a Linux implementation thing
  9. * rather than a description of the hardware.
  10. */
  11. WARN(spi->dev.of_node &&
  12. of_device_is_compatible(spi->dev.of_node, "spidev"),
  13. "%pOF: buggy DT: spidev listed directly in DT\n", spi->dev.of_node);
  14. spidev_probe_acpi(spi);
  15. /* Allocate driver data */
  16. spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);/*给spidev申请私有内存空间*/
  17. if (!spidev)
  18. return -ENOMEM;
  19. /* Initialize the driver data */
  20. spidev->spi = spi; /*记录spi*/
  21. spin_lock_init(&spidev->spi_lock);
  22. mutex_init(&spidev->buf_lock);
  23. INIT_LIST_HEAD(&spidev->device_entry);
  24. /* If we can allocate a minor number, hook up this device.
  25. * Reusing minors is fine so long as udev or mdev is working.
  26. */
  27. mutex_lock(&device_list_lock);
  28. minor = find_first_zero_bit(minors, N_SPI_MINORS);
  29. if (minor < N_SPI_MINORS) {
  30. struct device *dev;
  31. spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
  32. /*生成一个设备节点 /dev/spidevB.D */
  33. dev = device_create(spidev_class, &spi->dev, spidev->devt,
  34. spidev, "spidev%d.%d",
  35. spi->master->bus_num, spi->chip_select);
  36. status = PTR_ERR_OR_ZERO(dev);
  37. } else {
  38. dev_dbg(&spi->dev, "no minor number available!\n");
  39. status = -ENODEV;
  40. }
  41. if (status == 0) {
  42. set_bit(minor, minors);
  43. list_add(&spidev->device_entry, &device_list);/*把spidev_data放入链表*/
  44. }
  45. mutex_unlock(&device_list_lock);
  46. spidev->speed_hz = spi->max_speed_hz;/*记录时钟频率*/
  47. if (status == 0)
  48. spi_set_drvdata(spi, spidev);
  49. else
  50. kfree(spidev);
  51. return status;
  52. }

        然后看看spidev_fops,它提供了open,read,write,ioctl等读写API。

  spidev_read函数 

  1. /* Read-only message with current device setup */
  2. static ssize_t
  3. spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
  4. {
  5. struct spidev_data *spidev;
  6. ssize_t status = 0;
  7. /* chipselect only toggles at start or end of operation */
  8. if (count > bufsiz)
  9. return -EMSGSIZE;
  10. spidev = filp->private_data;
  11. mutex_lock(&spidev->buf_lock);
  12. status = spidev_sync_read(spidev, count);
  13. if (status > 0) {
  14. unsigned long missing;
  15. missing = copy_to_user(buf, spidev->rx_buffer, status);
  16. if (missing == status)
  17. status = -EFAULT;
  18. else
  19. status = status - missing;
  20. }
  21. mutex_unlock(&spidev->buf_lock);
  22. return status;
  23. }
  24. static inline ssize_t
  25. spidev_sync_read(struct spidev_data *spidev, size_t len)
  26. {
  27. struct spi_transfer t = {
  28. .rx_buf = spidev->rx_buffer,
  29. .len = len,
  30. .speed_hz = spidev->speed_hz,
  31. };
  32. struct spi_message m;
  33. spi_message_init(&m);
  34. spi_message_add_tail(&t, &m);
  35. return spidev_sync(spidev, &m);
  36. }
  37. /*-------------------------------------------------------------------------*/
  38. spidev_sync(struct spidev_data *spidev, struct spi_message *message)
  39. {
  40. int status;
  41. struct spi_device *spi;
  42. spin_lock_irq(&spidev->spi_lock);
  43. spi = spidev->spi;
  44. spin_unlock_irq(&spidev->spi_lock);
  45. if (spi == NULL)
  46. status = -ESHUTDOWN;
  47. else
  48. status = spi_sync(spi, message);
  49. if (status == 0)
  50. status = message->actual_length;
  51. return status;
  52. }

        在spidev_read函数中,调用 spidev_sync_read,在spidev_sync_read中定义spi_transfer和spi_message,然后通过spidev_sync发送,spidev_sync最终会调用内核提供的spi_sync来实现读数据,得到数据后使用copy_to_user把内核空间的数据发给应用程序;

  spidev_write函数 

  1. static ssize_t
  2. spidev_write(struct file *filp, const char __user *buf,
  3. size_t count, loff_t *f_pos)
  4. {
  5. struct spidev_data *spidev;
  6. ssize_t status = 0;
  7. unsigned long missing;
  8. if (count > bufsiz)
  9. return -EMSGSIZE;
  10. spidev = filp->private_data;
  11. mutex_lock(&spidev->buf_lock);
  12. missing = copy_from_user(spidev->tx_buffer, buf, count);
  13. if (missing == 0)
  14. status = spidev_sync_write(spidev, count);
  15. status = -EFAULT;
  16. mutex_unlock(&spidev->buf_lock);
  17. return status;
  18. }
  19. static inline ssize_t
  20. spidev_sync_write(struct spidev_data *spidev, size_t len)
  21. {
  22. struct spi_transfer t = {
  23. .tx_buf = spidev->tx_buffer,
  24. .len = len,
  25. .speed_hz = spidev->speed_hz,
  26. };
  27. struct spi_message m;
  28. spi_message_init(&m);
  29. spi_message_add_tail(&t, &m);
  30. return spidev_sync(spidev, &m);
  31. }
  32. static ssize_t
  33. spidev_sync(struct spidev_data *spidev, struct spi_message *message)
  34. {
  35. int status;
  36. struct spi_device *spi;
  37. spin_lock_irq(&spidev->spi_lock);
  38. spi = spidev->spi;
  39. spin_unlock_irq(&spidev->spi_lock);
  40. if (spi == NULL)
  41. status = -ESHUTDOWN;
  42. else
  43. status = spi_sync(spi, message);
  44. if (status == 0)
  45. status = message->actual_length;
  46. return status;
  47. }

        在spi_write函数中,首先要把写的数据拷贝到内核空间,然后把数据传到spi_sync_write函数,在spi_sync_write定义spi_transfer和spi_message,然后通过spidev_sync发送,spidev_sync最终会调用内核提供的spi_sync来实现写数据。

        spi_write和spi_read只能读、写,这是半双工方式,要实现全双工模式的话要使用spi_ioctl。

spi_ioctl函数

  1. spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  2. {
  3. int retval = 0;
  4. struct spidev_data *spidev;
  5. struct spi_device *spi;
  6. u32 tmp;
  7. unsigned n_ioc;
  8. struct spi_ioc_transfer *ioc;
  9. /* Check type and command number */
  10. if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
  11. return -ENOTTY;
  12. /* guard against device removal before, or while,
  13. * we issue this ioctl.
  14. */
  15. spidev = filp->private_data;
  16. spin_lock_irq(&spidev->spi_lock);
  17. spi = spi_dev_get(spidev->spi);
  18. spin_unlock_irq(&spidev->spi_lock);
  19. if (spi == NULL)
  20. return -ESHUTDOWN;
  21. /* use the buffer lock here for triple duty:
  22. * - prevent I/O (from us) so calling spi_setup() is safe;
  23. * - prevent concurrent SPI_IOC_WR_* from morphing
  24. * data fields while SPI_IOC_RD_* reads them;
  25. * - SPI_IOC_MESSAGE needs the buffer locked "normally".
  26. */
  27. mutex_lock(&spidev->buf_lock);
  28. switch (cmd) {
  29. /* read requests */
  30. case SPI_IOC_RD_MODE:
  31. retval = put_user(spi->mode & SPI_MODE_MASK,
  32. (__u8 __user *)arg);
  33. break;
  34. case SPI_IOC_RD_MODE32:
  35. retval = put_user(spi->mode & SPI_MODE_MASK,
  36. (__u32 __user *)arg);
  37. break;
  38. case SPI_IOC_RD_LSB_FIRST:
  39. retval = put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
  40. (__u8 __user *)arg);
  41. break;
  42. case SPI_IOC_RD_BITS_PER_WORD:
  43. retval = put_user(spi->bits_per_word, (__u8 __user *)arg);
  44. break;
  45. case SPI_IOC_RD_MAX_SPEED_HZ:
  46. retval = put_user(spidev->speed_hz, (__u32 __user *)arg);
  47. break;
  48. /* write requests */
  49. case SPI_IOC_WR_MODE:
  50. case SPI_IOC_WR_MODE32:
  51. if (cmd == SPI_IOC_WR_MODE)
  52. retval = get_user(tmp, (u8 __user *)arg);
  53. else
  54. retval = get_user(tmp, (u32 __user *)arg);
  55. if (retval == 0) {
  56. struct spi_controller *ctlr = spi->controller;
  57. u32 save = spi->mode;
  58. if (tmp & ~SPI_MODE_MASK) {
  59. retval = -EINVAL;
  60. break;
  61. }
  62. if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods &&
  63. ctlr->cs_gpiods[spi->chip_select])
  64. tmp |= SPI_CS_HIGH;
  65. tmp |= spi->mode & ~SPI_MODE_MASK;
  66. spi->mode = (u16)tmp;
  67. retval = spi_setup(spi);
  68. if (retval < 0)
  69. spi->mode = save;
  70. else
  71. dev_dbg(&spi->dev, "spi mode %x\n", tmp);
  72. }
  73. break;
  74. case SPI_IOC_WR_LSB_FIRST:
  75. retval = get_user(tmp, (__u8 __user *)arg);
  76. if (retval == 0) {
  77. u32 save = spi->mode;
  78. if (tmp)
  79. spi->mode |= SPI_LSB_FIRST;
  80. else
  81. spi->mode &= ~SPI_LSB_FIRST;
  82. retval = spi_setup(spi);
  83. if (retval < 0)
  84. spi->mode = save;
  85. else
  86. dev_dbg(&spi->dev, "%csb first\n",
  87. tmp ? 'l' : 'm');
  88. }
  89. break;
  90. case SPI_IOC_WR_BITS_PER_WORD:
  91. retval = get_user(tmp, (__u8 __user *)arg);
  92. if (retval == 0) {
  93. u8 save = spi->bits_per_word;
  94. spi->bits_per_word = tmp;
  95. retval = spi_setup(spi);
  96. if (retval < 0)
  97. spi->bits_per_word = save;
  98. else
  99. dev_dbg(&spi->dev, "%d bits per word\n", tmp);
  100. }
  101. break;
  102. case SPI_IOC_WR_MAX_SPEED_HZ:
  103. retval = get_user(tmp, (__u32 __user *)arg);
  104. if (retval == 0) {
  105. u32 save = spi->max_speed_hz;
  106. spi->max_speed_hz = tmp;
  107. retval = spi_setup(spi);
  108. if (retval >= 0)
  109. spidev->speed_hz = tmp;
  110. else
  111. dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
  112. spi->max_speed_hz = save;
  113. }
  114. break;
  115. default:
  116. /* segmented and/or full-duplex I/O request */
  117. /* Check message and copy into scratch area */
  118. ioc = spidev_get_ioc_message(cmd,
  119. (struct spi_ioc_transfer __user *)arg, &n_ioc);
  120. if (IS_ERR(ioc)) {
  121. retval = PTR_ERR(ioc);
  122. break;
  123. }
  124. if (!ioc)
  125. break; /* n_ioc is also 0 */
  126. /* translate to spi_message, execute */
  127. retval = spidev_message(spidev, ioc, n_ioc);
  128. kfree(ioc);
  129. break;
  130. }
  131. mutex_unlock(&spidev->buf_lock);
  132. spi_dev_put(spi);
  133. return retval;
  134. }

        

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号