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来改变驱动的行为。