Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1710094
  • 博文数量: 143
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1462
  • 用 户 组: 普通用户
  • 注册时间: 2016-08-23 11:14
文章分类

全部博文(143)

文章存档

2022年(3)

2021年(13)

2020年(21)

2019年(8)

2018年(28)

2017年(7)

2016年(63)

我的朋友

分类: 嵌入式

2016-09-12 19:04:04

背景介绍:ICETEK OMAPL138开发板中有2个spi控制器,其中spi0与网口公用而不能使用,而由于当前我们项目需要挂多个spi外设,但是仅剩的spi1最多能挂载8个spi外设,即spi1只能片选8个spi外设,所以本文旨在研究使用gpio pin实现第9个片选信号涉及修改的代码。
1 分析初始化外设时需要修改的地方
   分析:在linux内核的spi代码中,用chip_select来表示spi外设究竟挂载在哪个spi控制器上,所以我们搜索kernel/driver/下的chip_select,分析与chip_select有关系的地方,能否修改为控制gpio pin。

点击(此处)折叠或打开

  1. spi/spi.c:263:    if (spi->chip_select >= spi->master->num_chipselect) {
  2. spi/spi.c:265:            spi->chip_select,
  3. spi/spi.c:273:            spi->chip_select);
  4. spi/spi.c:285:                spi->chip_select);
  5. spi/spi.c:350:    proxy->chip_select = chip->chip_select;

  6. spi/spidev.c:590:                 spi->master->bus_num, spi->chip_select);

  7. spi/davinci_spi.c:204:        davinci_spi->slave[spi->chip_select].bytes_per_word = 1;
  8. spi/davinci_spi.c:208:        davinci_spi->slave[spi->chip_select].bytes_per_word = 2;
  9. spi/davinci_spi.c:216:    davinci_spi->cs_num = spi->chip_select;                                                           ////2 最终是调用clear_fmt_bits和set_fmt_bits,见分析2。
  10. spi/davinci_spi.c:219:            spi->chip_select);                                                                        ////2 clear_fmt_bits
  11. spi/davinci_spi.c:221:            spi->chip_select);                                                                        ////2 set_fmt_bits
  12. spi/davinci_spi.c:236:    davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]);
  13. spi/davinci_spi.c:259:    davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]);
  14. spi/davinci_spi.c:283:    davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
  15. spi/davinci_spi.c:331:    davinci_spi->slave[spi->chip_select].cmd_to_write = 0;
  16. spi/davinci_spi.c:334:        davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
  17. spi/davinci_spi.c:367:                spi->chip_select);
  18. spi/davinci_spi.c:370:                spi->chip_select);
  19. spi/davinci_spi.c:374:                spi->chip_select);
  20. spi/davinci_spi.c:377:                spi->chip_select);
  21. spi/davinci_spi.c:381:                spi->chip_select);
  22. spi/davinci_spi.c:384:                spi->chip_select);
  23. spi/davinci_spi.c:401:                spi->chip_select);
  24. spi/davinci_spi.c:406:                spi->chip_select);
  25. spi/davinci_spi.c:411:                    spi->chip_select);
  26. spi/davinci_spi.c:415:                    spi->chip_select);
  27. spi/davinci_spi.c:420:                    spi->chip_select);
  28. spi/davinci_spi.c:424:                    spi->chip_select);
  29. spi/davinci_spi.c:429:                    spi->chip_select);
  30. spi/davinci_spi.c:433:                    spi->chip_select);
  31. spi/davinci_spi.c:438:                    spi->chip_select);
  32. spi/davinci_spi.c:442:                    spi->chip_select);
  33. spi/davinci_spi.c:456:    davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
  34. spi/davinci_spi.c:459:        davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
  35. spi/davinci_spi.c:485:        op_mode |= 1 << spi->chip_select;                                                            ////3 从代码来看与SPIPC0有关系,见分析3:
  36. spi/davinci_spi.c:572:    conv = davinci_spi->slave[spi->chip_select].bytes_per_word;
  37. spi/davinci_spi.c:591:    tmp = ~(0x1 << spi->chip_select);                                                                ////4 从代码来看与SPIDEF和SPIDAT1有关系,见分析4:
  38. spi/davinci_spi.c:714:    davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
  39. spi/davinci_spi.c:723:    conv = davinci_spi->slave[spi->chip_select].bytes_per_word;
  40. spi/davinci_spi.c:756:    tmp = ~(0x1 << spi->chip_select);                                                                ////5 这个函数davinci_spi_bufs_dma和4中的davinci_spi_bufs_pio逻辑实现是一样的,所以看4即可。

分析 2:

  很明显,这里是配置FMT的,而硬件说FMT0-FMT4与chip_select本身没有固定的关系,每个chip_select都可以从FMT0-4中选取一个作为收发格式,所以硬件认为这个地方代码本身有bug。那么,这里要怎么配置呢?我是将CS0配置使用FMT0(即内核原来就有的spi1.0保持不变),我自己新增的CS1-CS7 CS8配置仍使用FMT0。事实上,spi format是指spi设备收发消息的格式,是通过在davinci_spi_setup()中设置spi FMT寄存器来配置不同的消息格式,包含了SPI_LSB_FIRST等等信息的配置。并且在ICETEK OMAPL138开发板中允许同时可配置4个FTM。而spi设备收发消息时实际使用哪个FMT的消息格式,是通过配置 SPIDAT1 的bit24-bit25来确定,可参考下面章节中,关于如何通过chip_select配置 SPIDAT1 寄存器。
  另外,你会发现在BSP代码(arch/arm/mach-davinci)下搜索chip_select,结果除了与spi name有关的外,剩下就是作为clear_fmt_bits()和set_fmt_bits()的入参了。所以,对BSP代码(arch/arm/mach-davinci)下的chip_select的分析,其实这里的分析2已经涵盖到了。

分析 3:

点击(此处)折叠或打开

  1. static int davinci_spi_bufs_prep(struct spi_device *spi,
  2.                                  struct davinci_spi *davinci_spi)
  3. {
  4.         int op_mode = 0;

  5.         /*
  6.          * REVISIT unless devices disagree about SPI_LOOP or
  7.          * SPI_READY (SPI_NO_CS only allows one this
  8.          * should not need to be done before each message...
  9.          * optimize for both flags staying cleared.
  10.          */

  11.         op_mode = SPIPC0_DIFUN_MASK        //#define SPIPC0_DIFUN_MASK    BIT(11)        /* MISO */
  12.                 | SPIPC0_DOFUN_MASK        //#define SPIPC0_DOFUN_MASK    BIT(10)        /* MOSI */
  13.                 | SPIPC0_CLKFUN_MASK;    //#define SPIPC0_CLKFUN_MASK    BIT(9)        /* CLK */
  14.         if (!(spi->mode & SPI_NO_CS))    //#define    SPI_NO_CS    0x40            /* 1 dev/bus, no chipselect */
  15.                 op_mode |= 1 << spi->chip_select;
  16.         if (spi->mode & SPI_READY)
  17.                 op_mode |= SPIPC0_SPIENA_MASK;    //#define SPIPC0_SPIENA_MASK    BIT(8)        /* nREADY */

  18.         iowrite32(op_mode, davinci_spi->base + SPIPC0);
  19. ...
  20. }
  很明显,这里对于SPI1的CS0-CS7,配置 SPIPC0 的bit0-bit7为相应的片选值为1。与硬件讨论后,CS8的 SPIPC0 的bit0-bit7不配置。

分析 4:

点击(此处)折叠或打开

  1. static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t)
  2. {
  3. ...
  4.         data1_reg_val = pdata->cs_hold << SPIDAT1_CSHOLD_SHIFT;        //#define SPIDAT1_CSHOLD_SHIFT    28
  5.         tmp = ~(0x1 << spi->chip_select);
  6.         clear_io_bits(davinci_spi->base + SPIDEF, ~tmp);
  7.         
  8.         data1_reg_val |= tmp << SPIDAT1_CSNR_SHIFT;                    //#define SPIDAT1_CSNR_SHIFT    16
  9.         while ((ioread32(davinci_spi->base + SPIBUF)
  10.                                 & SPIBUF_RXEMPTY_MASK) == 0)
  11.                 cpu_relax();
  12.         /* Determine the command to execute READ or WRITE */
  13.         if (t->tx_buf) {
  14.                 clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL);

  15.                 while (1) {
  16.                         tx_data = davinci_spi->get_tx(davinci_spi);

  17.                         data1_reg_val &= ~(0xFFFF);
  18.                         data1_reg_val |= (0xFFFF & tx_data);

  19.                         buf_val = ioread32(davinci_spi->base + SPIBUF);
  20.                         if ((buf_val & SPIBUF_TXFULL_MASK) == 0) {
  21.                                 iowrite32(data1_reg_val,
  22.                                                 davinci_spi->base + SPIDAT1);

  23.                                 count--;
  24.                         }
  25.                         while (ioread32(davinci_spi->base + SPIBUF)
  26.                                         & SPIBUF_RXEMPTY_MASK)
  27.                                 cpu_relax();

  28.                         /* getting the returned byte */
  29.                         if (t->rx_buf) {
  30.                                 buf_val = ioread32(davinci_spi->base + SPIBUF);
  31.                                 davinci_spi->get_rx(buf_val, davinci_spi);
  32.                         }
  33.                         if (count <= 0)
  34.                                 break;
  35.                 }
  36.         } else {
  37.                 if (pdata->poll_mode) {
  38.                         while (1) {
  39.                                 /* keeps the serial clock going */
  40.                                 if ((ioread32(davinci_spi->base + SPIBUF)
  41.                                                 & SPIBUF_TXFULL_MASK) == 0)
  42.                                         iowrite32(data1_reg_val,
  43.                                                 davinci_spi->base + SPIDAT1);

  44.                                 while (ioread32(davinci_spi->base + SPIBUF) &
  45.                                                 SPIBUF_RXEMPTY_MASK)
  46.                                         cpu_relax();

  47.                                 flg_val = ioread32(davinci_spi->base + SPIFLG);
  48.                                 buf_val = ioread32(davinci_spi->base + SPIBUF);

  49.                                 davinci_spi->get_rx(buf_val, davinci_spi);

  50.                                 count--;
  51.                                 if (count <= 0)
  52.                                         break;
  53.                         }
  54.                 } else { /* Receive in Interrupt mode */
  55.                         int i;

  56.                         for (i = 0; i < davinci_spi->count; i++) {
  57.                                 set_io_bits(davinci_spi->base + SPIINT,
  58.                                                 SPIINT_BITERR_INTR
  59.                                                 | SPIINT_OVRRUN_INTR
  60.                                                 | SPIINT_RX_INTR);

  61.                                 iowrite32(data1_reg_val,
  62.                                                 davinci_spi->base + SPIDAT1);

  63.                                 while (ioread32(davinci_spi->base + SPIINT) &
  64.                                                 SPIINT_RX_INTR)
  65.                                         cpu_relax();
  66.                         }
  67.                         iowrite32((data1_reg_val & 0x0ffcffff),
  68.                                         davinci_spi->base + SPIDAT1);
  69.                 }
  70.         }
  71. ...
  72. }
  很明显,这里对于SPI1的CS0-CS7,配置 SPIDEF 的bit0-bit7为相应的片选值为0;配置 SPIDAT1 的bit16-bit23为相应的片选值为0。与硬件讨论后,CS8的 SPIDEF 不配置;且 SPIDAT1 的bit16-bit23不配置。

2 分析读写外设时片选选中需要修改的地方
  分析:用户在外设驱动中通过读写字符设备文件完成对spi外设的访问,而读写过程中就会有片选操作,最终的函数实现就是davinci_spi_chipselect,所以我们来研究一下这个函数。分析参见:http://blog.csdn.net/alleincao/article/details/7525977
分析 5:

点击(此处)折叠或打开

  1. /*
  2.  * Interface to control the chip select signal
  3.  */
  4. static void davinci_spi_chipselect(struct spi_device *spi, int value)
  5. {
  6.         struct davinci_spi *davinci_spi;
  7.         struct davinci_spi_platform_data *pdata;
  8.         u32 data1_reg_val = 0;

  9.         davinci_spi = spi_master_get_devdata(spi->master);
  10.         pdata = davinci_spi->pdata;

  11.         /*
  12.          * Board specific chip select logic decides the polarity and cs
  13.          * line for the controller
  14.          */
  15.         if (value == BITBANG_CS_INACTIVE) {
  16.                 set_io_bits(davinci_spi->base + SPIDEF, CS_DEFAULT);    //#define CS_DEFAULT    0xFF

  17.                 data1_reg_val |= CS_DEFAULT << SPIDAT1_CSNR_SHIFT;
  18.                 iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1);

  19.                 while ((ioread32(davinci_spi->base + SPIBUF)
  20.                                         & SPIBUF_RXEMPTY_MASK) == 0)    //#define SPIBUF_RXEMPTY_MASK    BIT(31)
  21.                         cpu_relax();
  22.         }
  23. }
  很明显,这里对于SPI1的CS0-CS7,仅仅当拉高时:配置 SPIDEF 的bit0-bit7为初始值为0xff;配置 SPIDAT1 的bit16-bit23为初始值为0xff,那么当拉低时究竟配置了哪些寄存器呢?

3 小结
  现在假设,以上我们想到的地方已经涵盖了所有CS拉高和拉低的地方,那么我们在以上分析2、3、4、5的函数中加打印,然后在串口下用spi1.1去读写数据,就会发现每次读或写:先调用分析3中的davinci_spi_bufs_prep和分析4中的davinci_spi_bufs_pio做拉低,(然后读写数),最后调用分析5中的davinci_spi_chipselect恢复拉高。具体当拉低和拉高时,对哪些寄存器的bit位做了配置,在以上分析3、4、5中已经分析的很清楚了,这里不再重复。这里我们来看CS8采用gpio后,如何在内核的spi总线驱动中实现gpio控制?由于此Linux内核源码中gpio子系统已经被初始化好了,那么可以直接调用gpio内核态接口:gpio_request/gpio_direction_output/gpio_set_value,来控制gpio拉高或拉低。
  首先要申请gpio管脚,并为其设置输入输入模式。

点击(此处)折叠或打开

  1. #define DA850_SPI1_CS_8_PIN        GPIO_TO_PIN(3, 12)
  2. static __init void da850_evm_init(void)
  3. {
  4. ...
  5.     ret = da8xx_pinmux_setup(da850_spi1_pins);
  6.     if (ret)
  7.         pr_warning("da850_evm_init: spi1 mux setup failed: %d\n",
  8.                 ret);

  9.     da850_spi_board_info[0].irq = gpio_to_irq(DA850_TS_INT);

  10.     da850_init_spi1(BIT(1), da850_spi_board_info,
  11.             ARRAY_SIZE(da850_spi_board_info));
  12.     ret = gpio_request(DA850_SPI1_CS_8_PIN, "SPI1 CS8\n");
  13.     if (ret) {
  14.         printk(KERN_ERR "%s: failed to request GPIO for SPI 1.1 port "
  15.          "status: %d\n", __func__, ret);
  16.         return;
  17.     }
  18.     gpio_direction_output(DA850_SPI1_CS_8_PIN, 0);
  19.     printk("<0>da850_evm_init da850_init_spi1 \n");
  20. ...
  21. }
  然后,后面就可以直接调用gpio_set_value()控制gpio拉高或拉低了。所以我们接着看上面分析的代码如何修改:
  对分析3中代码修改:见下。因为查询芯片手册中, SPIPC0 的bit0-bit7表示:0,配置此pin为gpio pin;1,配置此pin为spi cs pin。而与CS8无关。

点击(此处)折叠或打开

  1. static int davinci_spi_bufs_prep(struct spi_device *spi,
  2.                                  struct davinci_spi *davinci_spi)
  3. {
  4.         int op_mode = 0;

  5.         /*
  6.          * REVISIT unless devices disagree about SPI_LOOP or
  7.          * SPI_READY (SPI_NO_CS only allows one this
  8.          * should not need to be done before each message...
  9.          * optimize for both flags staying cleared.
  10.          */

  11.         op_mode = SPIPC0_DIFUN_MASK
  12.                 | SPIPC0_DOFUN_MASK
  13.                 | SPIPC0_CLKFUN_MASK;
  14.         if (!(spi->mode & SPI_NO_CS) && spi->chip_select < 8)
  15.                 op_mode |= 1 << spi->chip_select;
  16.         if (spi->mode & SPI_READY)
  17.                 op_mode |= SPIPC0_SPIENA_MASK;

  18.         iowrite32(op_mode, davinci_spi->base + SPIPC0);
  19. ...
  20. }
  对分析4中代码修改:见下。这里是“拉低”操作。

点击(此处)折叠或打开

  1. #define DA850_SPI1_CS_8_PIN GPIO_TO_PIN(3, 12)
  2. static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t)
  3. {
  4. ...
  5.         data1_reg_val = pdata->cs_hold << SPIDAT1_CSHOLD_SHIFT;
  6.         if (spi->chip_select < 8)
  7.         {
  8.         tmp = ~(0x1 << spi->chip_select);
  9.         clear_io_bits(davinci_spi->base + SPIDEF, ~tmp);
  10.         
  11.         data1_reg_val |= tmp << SPIDAT1_CSNR_SHIFT;
  12.         }
  13.         else if (spi->chip_select == 8)
  14.         {
  15.                 gpio_set_value(DA850_SPI1_CS_8_PIN, 0);//shezhi CS8 ladi
  16.         }
  17.         while ((ioread32(davinci_spi->base + SPIBUF)
  18.                                 & SPIBUF_RXEMPTY_MASK) == 0)
  19.                 cpu_relax();
  20.         /* Determine the command to execute READ or WRITE */
  21.         if (t->tx_buf) {
  22.                 clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL);

  23.                 while (1) {
  24.                         tx_data = davinci_spi->get_tx(davinci_spi);

  25.                         data1_reg_val &= ~(0xFFFF);
  26.                         data1_reg_val |= (0xFFFF & tx_data);

  27.                         buf_val = ioread32(davinci_spi->base + SPIBUF);
  28.                         if ((buf_val & SPIBUF_TXFULL_MASK) == 0) {
  29.                                 iowrite32(data1_reg_val,
  30.                                                 davinci_spi->base + SPIDAT1);

  31.                                 count--;
  32.                         }
  33.                         while (ioread32(davinci_spi->base + SPIBUF)
  34.                                         & SPIBUF_RXEMPTY_MASK)
  35.                                 cpu_relax();

  36.                         /* getting the returned byte */
  37.                         if (t->rx_buf) {
  38.                                 buf_val = ioread32(davinci_spi->base + SPIBUF);
  39.                                 davinci_spi->get_rx(buf_val, davinci_spi);
  40.                         }
  41.                         if (count <= 0)
  42.                                 break;
  43.                 }
  44.         } else {
  45.                 if (pdata->poll_mode) {
  46.                         while (1) {
  47.                                 /* keeps the serial clock going */
  48.                                 if ((ioread32(davinci_spi->base + SPIBUF)
  49.                                                 & SPIBUF_TXFULL_MASK) == 0)
  50.                                         iowrite32(data1_reg_val,
  51.                                                 davinci_spi->base + SPIDAT1);

  52.                                 while (ioread32(davinci_spi->base + SPIBUF) &
  53.                                                 SPIBUF_RXEMPTY_MASK)
  54.                                         cpu_relax();

  55.                                 flg_val = ioread32(davinci_spi->base + SPIFLG);
  56.                                 buf_val = ioread32(davinci_spi->base + SPIBUF);

  57.                                 davinci_spi->get_rx(buf_val, davinci_spi);

  58.                                 count--;
  59.                                 if (count <= 0)
  60.                                         break;
  61.                         }
  62.                 } else { /* Receive in Interrupt mode */
  63.                         int i;

  64.                         for (i = 0; i < davinci_spi->count; i++) {
  65.                                 set_io_bits(davinci_spi->base + SPIINT,
  66.                                                 SPIINT_BITERR_INTR
  67.                                                 | SPIINT_OVRRUN_INTR
  68.                                                 | SPIINT_RX_INTR);

  69.                                 iowrite32(data1_reg_val,
  70.                                                 davinci_spi->base + SPIDAT1);

  71.                                 while (ioread32(davinci_spi->base + SPIINT) &
  72.                                                 SPIINT_RX_INTR)
  73.                                         cpu_relax();
  74.                         }
  75.                         iowrite32((data1_reg_val & 0x0ffcffff),
  76.                                         davinci_spi->base + SPIDAT1);
  77.                 }
  78.         }
  79. ...
  80. }
  对分析5中代码修改:见下。这里是“拉高”操作。

点击(此处)折叠或打开

  1. /*
  2.  * Interface to control the chip select signal
  3.  */
  4. #define DA850_SPI1_CS_8_PIN GPIO_TO_PIN(3, 12)
  5. static void davinci_spi_chipselect(struct spi_device *spi, int value)
  6. {
  7.         struct davinci_spi *davinci_spi;
  8.         struct davinci_spi_platform_data *pdata;
  9.         u32 data1_reg_val = 0;

  10.         davinci_spi = spi_master_get_devdata(spi->master);
  11.         pdata = davinci_spi->pdata;

  12.         /*
  13.          * Board specific chip select logic decides the polarity and cs
  14.          * line for the controller
  15.          */
  16.         if (value == BITBANG_CS_INACTIVE) {
  17.                 if (spi->chip_select < 8)
  18.                 {
  19.                 set_io_bits(davinci_spi->base + SPIDEF, CS_DEFAULT);

  20.                 data1_reg_val |= CS_DEFAULT << SPIDAT1_CSNR_SHIFT;
  21.                 }
  22.                 else if (spi->chip_select == 8)
  23.                 {
  24.                         gpio_set_value(DA850_SPI1_CS_8_PIN, 1);//shezhi CS8 lagao
  25.                 }
  26.                 iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1);

  27.                 while ((ioread32(davinci_spi->base + SPIBUF)
  28.                                         & SPIBUF_RXEMPTY_MASK) == 0)
  29.                         cpu_relax();
  30.         }
  31. } 

4 实际
  根据如上分析,我将CS8用gpio控制,然后在串口下用spi1.8去读写数据,并用示波器抓取CS Pin波形,与其他spi1的片选一致,即达到了我们的目标。
阅读(2983) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~