背景介绍: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。
-
spi/spi.c:263: if (spi->chip_select >= spi->master->num_chipselect) {
-
spi/spi.c:265: spi->chip_select,
-
spi/spi.c:273: spi->chip_select);
-
spi/spi.c:285: spi->chip_select);
-
spi/spi.c:350: proxy->chip_select = chip->chip_select;
-
-
spi/spidev.c:590: spi->master->bus_num, spi->chip_select);
-
-
spi/davinci_spi.c:204: davinci_spi->slave[spi->chip_select].bytes_per_word = 1;
-
spi/davinci_spi.c:208: davinci_spi->slave[spi->chip_select].bytes_per_word = 2;
-
spi/davinci_spi.c:216: davinci_spi->cs_num = spi->chip_select; ////2 最终是调用clear_fmt_bits和set_fmt_bits,见分析2。
-
spi/davinci_spi.c:219: spi->chip_select); ////2 clear_fmt_bits
-
spi/davinci_spi.c:221: spi->chip_select); ////2 set_fmt_bits
-
spi/davinci_spi.c:236: davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]);
-
spi/davinci_spi.c:259: davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]);
-
spi/davinci_spi.c:283: davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
-
spi/davinci_spi.c:331: davinci_spi->slave[spi->chip_select].cmd_to_write = 0;
-
spi/davinci_spi.c:334: davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
-
spi/davinci_spi.c:367: spi->chip_select);
-
spi/davinci_spi.c:370: spi->chip_select);
-
spi/davinci_spi.c:374: spi->chip_select);
-
spi/davinci_spi.c:377: spi->chip_select);
-
spi/davinci_spi.c:381: spi->chip_select);
-
spi/davinci_spi.c:384: spi->chip_select);
-
spi/davinci_spi.c:401: spi->chip_select);
-
spi/davinci_spi.c:406: spi->chip_select);
-
spi/davinci_spi.c:411: spi->chip_select);
-
spi/davinci_spi.c:415: spi->chip_select);
-
spi/davinci_spi.c:420: spi->chip_select);
-
spi/davinci_spi.c:424: spi->chip_select);
-
spi/davinci_spi.c:429: spi->chip_select);
-
spi/davinci_spi.c:433: spi->chip_select);
-
spi/davinci_spi.c:438: spi->chip_select);
-
spi/davinci_spi.c:442: spi->chip_select);
-
spi/davinci_spi.c:456: davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
-
spi/davinci_spi.c:459: davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
-
spi/davinci_spi.c:485: op_mode |= 1 << spi->chip_select; ////3 从代码来看与SPIPC0有关系,见分析3:
-
spi/davinci_spi.c:572: conv = davinci_spi->slave[spi->chip_select].bytes_per_word;
-
spi/davinci_spi.c:591: tmp = ~(0x1 << spi->chip_select); ////4 从代码来看与SPIDEF和SPIDAT1有关系,见分析4:
-
spi/davinci_spi.c:714: davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select];
-
spi/davinci_spi.c:723: conv = davinci_spi->slave[spi->chip_select].bytes_per_word;
-
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:
-
static int davinci_spi_bufs_prep(struct spi_device *spi,
-
struct davinci_spi *davinci_spi)
-
{
-
int op_mode = 0;
-
-
/*
-
* REVISIT unless devices disagree about SPI_LOOP or
-
* SPI_READY (SPI_NO_CS only allows one this
-
* should not need to be done before each message...
-
* optimize for both flags staying cleared.
-
*/
-
-
op_mode = SPIPC0_DIFUN_MASK //#define SPIPC0_DIFUN_MASK BIT(11) /* MISO */
-
| SPIPC0_DOFUN_MASK //#define SPIPC0_DOFUN_MASK BIT(10) /* MOSI */
-
| SPIPC0_CLKFUN_MASK; //#define SPIPC0_CLKFUN_MASK BIT(9) /* CLK */
-
if (!(spi->mode & SPI_NO_CS)) //#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
-
op_mode |= 1 << spi->chip_select;
-
if (spi->mode & SPI_READY)
-
op_mode |= SPIPC0_SPIENA_MASK; //#define SPIPC0_SPIENA_MASK BIT(8) /* nREADY */
-
-
iowrite32(op_mode, davinci_spi->base + SPIPC0);
-
...
-
}
很明显,这里对于SPI1的CS0-CS7,配置 SPIPC0 的bit0-bit7为相应的片选值为1。与硬件讨论后,CS8的 SPIPC0 的bit0-bit7不配置。
分析 4:
-
static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t)
-
{
-
...
-
data1_reg_val = pdata->cs_hold << SPIDAT1_CSHOLD_SHIFT; //#define SPIDAT1_CSHOLD_SHIFT 28
-
tmp = ~(0x1 << spi->chip_select);
-
clear_io_bits(davinci_spi->base + SPIDEF, ~tmp);
-
-
data1_reg_val |= tmp << SPIDAT1_CSNR_SHIFT; //#define SPIDAT1_CSNR_SHIFT 16
-
while ((ioread32(davinci_spi->base + SPIBUF)
-
& SPIBUF_RXEMPTY_MASK) == 0)
-
cpu_relax();
-
/* Determine the command to execute READ or WRITE */
-
if (t->tx_buf) {
-
clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL);
-
-
while (1) {
-
tx_data = davinci_spi->get_tx(davinci_spi);
-
-
data1_reg_val &= ~(0xFFFF);
-
data1_reg_val |= (0xFFFF & tx_data);
-
-
buf_val = ioread32(davinci_spi->base + SPIBUF);
-
if ((buf_val & SPIBUF_TXFULL_MASK) == 0) {
-
iowrite32(data1_reg_val,
-
davinci_spi->base + SPIDAT1);
-
-
count--;
-
}
-
while (ioread32(davinci_spi->base + SPIBUF)
-
& SPIBUF_RXEMPTY_MASK)
-
cpu_relax();
-
-
/* getting the returned byte */
-
if (t->rx_buf) {
-
buf_val = ioread32(davinci_spi->base + SPIBUF);
-
davinci_spi->get_rx(buf_val, davinci_spi);
-
}
-
if (count <= 0)
-
break;
-
}
-
} else {
-
if (pdata->poll_mode) {
-
while (1) {
-
/* keeps the serial clock going */
-
if ((ioread32(davinci_spi->base + SPIBUF)
-
& SPIBUF_TXFULL_MASK) == 0)
-
iowrite32(data1_reg_val,
-
davinci_spi->base + SPIDAT1);
-
-
while (ioread32(davinci_spi->base + SPIBUF) &
-
SPIBUF_RXEMPTY_MASK)
-
cpu_relax();
-
-
flg_val = ioread32(davinci_spi->base + SPIFLG);
-
buf_val = ioread32(davinci_spi->base + SPIBUF);
-
-
davinci_spi->get_rx(buf_val, davinci_spi);
-
-
count--;
-
if (count <= 0)
-
break;
-
}
-
} else { /* Receive in Interrupt mode */
-
int i;
-
-
for (i = 0; i < davinci_spi->count; i++) {
-
set_io_bits(davinci_spi->base + SPIINT,
-
SPIINT_BITERR_INTR
-
| SPIINT_OVRRUN_INTR
-
| SPIINT_RX_INTR);
-
-
iowrite32(data1_reg_val,
-
davinci_spi->base + SPIDAT1);
-
-
while (ioread32(davinci_spi->base + SPIINT) &
-
SPIINT_RX_INTR)
-
cpu_relax();
-
}
-
iowrite32((data1_reg_val & 0x0ffcffff),
-
davinci_spi->base + SPIDAT1);
-
}
-
}
-
...
-
}
很明显,这里对于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:
-
/*
-
* Interface to control the chip select signal
-
*/
-
static void davinci_spi_chipselect(struct spi_device *spi, int value)
-
{
-
struct davinci_spi *davinci_spi;
-
struct davinci_spi_platform_data *pdata;
-
u32 data1_reg_val = 0;
-
-
davinci_spi = spi_master_get_devdata(spi->master);
-
pdata = davinci_spi->pdata;
-
-
/*
-
* Board specific chip select logic decides the polarity and cs
-
* line for the controller
-
*/
-
if (value == BITBANG_CS_INACTIVE) {
-
set_io_bits(davinci_spi->base + SPIDEF, CS_DEFAULT); //#define CS_DEFAULT 0xFF
-
-
data1_reg_val |= CS_DEFAULT << SPIDAT1_CSNR_SHIFT;
-
iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1);
-
-
while ((ioread32(davinci_spi->base + SPIBUF)
-
& SPIBUF_RXEMPTY_MASK) == 0) //#define SPIBUF_RXEMPTY_MASK BIT(31)
-
cpu_relax();
-
}
-
}
很明显,这里对于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管脚,并为其设置输入输入模式。
-
#define DA850_SPI1_CS_8_PIN GPIO_TO_PIN(3, 12)
-
static __init void da850_evm_init(void)
-
{
-
...
-
ret = da8xx_pinmux_setup(da850_spi1_pins);
-
if (ret)
-
pr_warning("da850_evm_init: spi1 mux setup failed: %d\n",
-
ret);
-
-
da850_spi_board_info[0].irq = gpio_to_irq(DA850_TS_INT);
-
-
da850_init_spi1(BIT(1), da850_spi_board_info,
-
ARRAY_SIZE(da850_spi_board_info));
-
ret = gpio_request(DA850_SPI1_CS_8_PIN, "SPI1 CS8\n");
-
if (ret) {
-
printk(KERN_ERR "%s: failed to request GPIO for SPI 1.1 port "
-
"status: %d\n", __func__, ret);
-
return;
-
}
-
gpio_direction_output(DA850_SPI1_CS_8_PIN, 0);
-
printk("<0>da850_evm_init da850_init_spi1 \n");
-
...
-
}
然后,后面就可以直接调用gpio_set_value()控制gpio拉高或拉低了。所以我们接着看上面分析的代码如何修改:
对分析3中代码修改:见下。因为查询芯片手册中, SPIPC0 的bit0-bit7表示:0,配置此pin为gpio pin;1,配置此pin为spi cs pin。而与CS8无关。
-
static int davinci_spi_bufs_prep(struct spi_device *spi,
-
struct davinci_spi *davinci_spi)
-
{
-
int op_mode = 0;
-
-
/*
-
* REVISIT unless devices disagree about SPI_LOOP or
-
* SPI_READY (SPI_NO_CS only allows one this
-
* should not need to be done before each message...
-
* optimize for both flags staying cleared.
-
*/
-
-
op_mode = SPIPC0_DIFUN_MASK
-
| SPIPC0_DOFUN_MASK
-
| SPIPC0_CLKFUN_MASK;
-
if (!(spi->mode & SPI_NO_CS) && spi->chip_select < 8)
-
op_mode |= 1 << spi->chip_select;
-
if (spi->mode & SPI_READY)
-
op_mode |= SPIPC0_SPIENA_MASK;
-
-
iowrite32(op_mode, davinci_spi->base + SPIPC0);
-
...
-
}
对分析4中代码修改:见下。这里是“拉低”操作。
-
#define DA850_SPI1_CS_8_PIN GPIO_TO_PIN(3, 12)
-
static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t)
-
{
-
...
-
data1_reg_val = pdata->cs_hold << SPIDAT1_CSHOLD_SHIFT;
-
if (spi->chip_select < 8)
-
{
-
tmp = ~(0x1 << spi->chip_select);
-
clear_io_bits(davinci_spi->base + SPIDEF, ~tmp);
-
-
data1_reg_val |= tmp << SPIDAT1_CSNR_SHIFT;
-
}
-
else if (spi->chip_select == 8)
-
{
-
gpio_set_value(DA850_SPI1_CS_8_PIN, 0);//shezhi CS8 ladi
-
}
-
while ((ioread32(davinci_spi->base + SPIBUF)
-
& SPIBUF_RXEMPTY_MASK) == 0)
-
cpu_relax();
-
/* Determine the command to execute READ or WRITE */
-
if (t->tx_buf) {
-
clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL);
-
-
while (1) {
-
tx_data = davinci_spi->get_tx(davinci_spi);
-
-
data1_reg_val &= ~(0xFFFF);
-
data1_reg_val |= (0xFFFF & tx_data);
-
-
buf_val = ioread32(davinci_spi->base + SPIBUF);
-
if ((buf_val & SPIBUF_TXFULL_MASK) == 0) {
-
iowrite32(data1_reg_val,
-
davinci_spi->base + SPIDAT1);
-
-
count--;
-
}
-
while (ioread32(davinci_spi->base + SPIBUF)
-
& SPIBUF_RXEMPTY_MASK)
-
cpu_relax();
-
-
/* getting the returned byte */
-
if (t->rx_buf) {
-
buf_val = ioread32(davinci_spi->base + SPIBUF);
-
davinci_spi->get_rx(buf_val, davinci_spi);
-
}
-
if (count <= 0)
-
break;
-
}
-
} else {
-
if (pdata->poll_mode) {
-
while (1) {
-
/* keeps the serial clock going */
-
if ((ioread32(davinci_spi->base + SPIBUF)
-
& SPIBUF_TXFULL_MASK) == 0)
-
iowrite32(data1_reg_val,
-
davinci_spi->base + SPIDAT1);
-
-
while (ioread32(davinci_spi->base + SPIBUF) &
-
SPIBUF_RXEMPTY_MASK)
-
cpu_relax();
-
-
flg_val = ioread32(davinci_spi->base + SPIFLG);
-
buf_val = ioread32(davinci_spi->base + SPIBUF);
-
-
davinci_spi->get_rx(buf_val, davinci_spi);
-
-
count--;
-
if (count <= 0)
-
break;
-
}
-
} else { /* Receive in Interrupt mode */
-
int i;
-
-
for (i = 0; i < davinci_spi->count; i++) {
-
set_io_bits(davinci_spi->base + SPIINT,
-
SPIINT_BITERR_INTR
-
| SPIINT_OVRRUN_INTR
-
| SPIINT_RX_INTR);
-
-
iowrite32(data1_reg_val,
-
davinci_spi->base + SPIDAT1);
-
-
while (ioread32(davinci_spi->base + SPIINT) &
-
SPIINT_RX_INTR)
-
cpu_relax();
-
}
-
iowrite32((data1_reg_val & 0x0ffcffff),
-
davinci_spi->base + SPIDAT1);
-
}
-
}
-
...
-
}
对分析5中代码修改:见下。这里是“拉高”操作。
-
/*
-
* Interface to control the chip select signal
-
*/
-
#define DA850_SPI1_CS_8_PIN GPIO_TO_PIN(3, 12)
-
static void davinci_spi_chipselect(struct spi_device *spi, int value)
-
{
-
struct davinci_spi *davinci_spi;
-
struct davinci_spi_platform_data *pdata;
-
u32 data1_reg_val = 0;
-
-
davinci_spi = spi_master_get_devdata(spi->master);
-
pdata = davinci_spi->pdata;
-
-
/*
-
* Board specific chip select logic decides the polarity and cs
-
* line for the controller
-
*/
-
if (value == BITBANG_CS_INACTIVE) {
-
if (spi->chip_select < 8)
-
{
-
set_io_bits(davinci_spi->base + SPIDEF, CS_DEFAULT);
-
-
data1_reg_val |= CS_DEFAULT << SPIDAT1_CSNR_SHIFT;
-
}
-
else if (spi->chip_select == 8)
-
{
-
gpio_set_value(DA850_SPI1_CS_8_PIN, 1);//shezhi CS8 lagao
-
}
-
iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1);
-
-
while ((ioread32(davinci_spi->base + SPIBUF)
-
& SPIBUF_RXEMPTY_MASK) == 0)
-
cpu_relax();
-
}
-
}
4 实际
根据如上分析,我将CS8用gpio控制,然后在串口下用spi1.8去读写数据,并用示波器抓取CS Pin波形,与其他spi1的片选一致,即达到了我们的目标。
阅读(3076) | 评论(0) | 转发(0) |