This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

官网释出的Linux 2.6.37内核代码 自带的SPI驱动是否有BUG? 附相关代码分析

Other Parts Discussed in Thread: AM1808, LINUXEZSDK-SITARA

TI及个各位AM1808用户,大家好:
我在使用AM1808的linux SPI驱动的时候,遇到了以下疑似的代码BUG,(代码来自www.ti.com/.../linuxezsdk-sitara,内核版本2.6.37)
现在贴出对代码的分析,希望得到反馈。
文件路径am180x-evm-sdk-src-06.00.00.00\board-support\linux-2.6.37-psp03.21.00.04.sdk\drivers\spi\davinci_spi.c

static int davinci_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
{
    struct davinci_spi *dspi;
    struct davinci_spi_config ****;
    u8 bits_per_word = 0;
   u32 hz = 0, spifmt = 0, prescale = 0;

  /*以下省略*/

   prescale = davinci_spi_get_prescale(dspi, hz);
   if (prescale < 0)
      return prescale;

  spifmt = (prescale << SPIFMT_PRESCALE_SHIFT) | (bits_per_word & 0x1f);

  if (spi->mode & SPI_LSB_FIRST)
     spifmt |= SPIFMT_SHIFTDIR_MASK;

  if (spi->mode & SPI_CPOL)
    spifmt |= SPIFMT_POLARITY_MASK;

  if (!(spi->mode & SPI_CPHA))
    spifmt |= SPIFMT_PHASE_MASK;

/*以下省略*/
}

因为prescale是定义为u32的,所以函数中这个if语句之后的return永远不会执行

prescale = davinci_spi_get_prescale(dspi, hz);
if (prescale < 0)
   return prescale;

同样我们必须注意到davinci_spi_get_prescale的定义

static inline int davinci_spi_get_prescale(struct davinci_spi *dspi, u32 max_speed_hz)
{
   int ret;

   ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz);

   if (ret < 3 || ret > 256)
     return -EINVAL;

   return ret - 1;
}

函数的注释说的很明白,函数返回的是SPI模块的时钟(clk_get_rate(dspi->clk))与SPI的传输时钟(max_speed_hz)的频率比。如果该比率在3与256之间,返回一个正确的值,在这个范围外返回-EINVAL(-22)。因此在一些特殊的情况下,例如SPI模块的输入时钟为152M,而SPI的传输时钟为500K,davinci_spi_get_prescale会返回-EINVAL。
回到davinci_spi_setup_transfer,如果prescale为-EINVAL,则它会被重新解释成一个无符号32位整形数字。这样prescae的高24位全部为1,执行下列语句后

spifmt = (prescale << SPIFMT_PRESCALE_SHIFT) | (bits_per_word & 0x1f);

spifmt的高16位全部为1(SPIFMT_PRESCALE_SHIFT定义为8)。
接下来的两个if (spi->mode & SPI_CPOL),if (!(spi->mode & SPI_CPHA))的本意是通过用户程序传到驱动的spi->mode设置spfmt的16,17位(参看AM1808AM1810 ARM Microprocessor Technical Reference Manual (Rev. A) spi章节关于SPIFMT0寄存器的介绍),但是由于之前spifmt的高16位已经是全1了
所以两个if判断的结果改变不了什么。

从用户角度看,就是在这种情况下如果SPI模块的输入时钟是152M,而SPI的传输时钟为500K时,应用程序无法通过设置spi-mode中SPI_CPOL和SPI_CPHA来改变驱动的行为。