这是一个关于pxa2xx_spi驱动程序的迷你HOWTO。驱动程序将PXA2xx同步串行端口转换为SPI主控制器。该驱动程序具有以下特点:
- 支持任何PXA2xx和兼容SSP。
- SSP PIO和SSP DMA数据传输。
- 外部和内部(SSPFRM)芯片选择。
- 每个从设备(芯片)配置。
- 完全暂停,冻结,恢复支持。
驱动程序是围绕一个由内核线程服务的&struct spi_message FIFO构建的。内核线程spi_pump_messages()驱动消息FIFO,并负责对SPI事务进行排队,设置和启动DMA或中断驱动的传输。
声明 PXA2xx 主控制器
通常,对于传统平台,SPI主机在arch/…/mach-/board-.c中被定义为“平台设备”。主配置通过include/linux/spi/pxa2xx_spi.h中的表传递给驱动程序:
struct pxa2xx_spi_controller { u16 num_chipselect; u8 enable_dma; ... };
“pxa2xx_spi_controller.num_chipselect”字段用于确定附加到这个SPI主机的从设备(芯片)号。
“pxa2xx_spi_controller.enable_dma "字段告知驱动程序应该使用SSP DMA。这导致驱动程序获取两个DMA通道:Rx通道和Tx通道。Rx通道的DMA服务优先级高于Tx通道。参见“PXA2xx开发人员手册”“DMA控制器”一节。
对于新平台,控制器和外围设备的描述来自设备树或ACPI。
NSSP MASTER SAMPLE
下面是一个使用PXA255 NSSP的遗留平台的配置示例:
static struct resource pxa_spi_nssp_resources[] = { [0] = { .start = __PREG(SSCR0_P(2)), /* Start address of NSSP */ .end = __PREG(SSCR0_P(2)) + 0x2c, /* Range of registers */ .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_NSSP, /* NSSP IRQ */ .end = IRQ_NSSP, .flags = IORESOURCE_IRQ, }, }; static struct pxa2xx_spi_controller pxa_nssp_master_info = { .num_chipselect = 1, /* Matches the number of chips attached to NSSP */ .enable_dma = 1, /* Enables NSSP DMA */ }; static struct platform_device pxa_spi_nssp = { .name = "pxa2xx-spi", /* MUST BE THIS VALUE, so device match driver */ .id = 2, /* Bus number, MUST MATCH SSP number 1..n */ .resource = pxa_spi_nssp_resources, .num_resources = ARRAY_SIZE(pxa_spi_nssp_resources), .dev = { .platform_data = &pxa_nssp_master_info, /* Passed to driver */ }, }; static struct platform_device *devices[] __initdata = { &pxa_spi_nssp, }; static void __init board_init(void) { (void)platform_add_device(devices, ARRAY_SIZE(devices)); }
Declaring Slave Devices
通常,对于一个遗留平台,每个SPI从(芯片)在arch/…/mach-/board-.c中定义,使用“linux/spi/spi.h”中的“spi_board_info”结构。
每个连接到PXA的从设备必须通过“include/linux/spi/pxa2xx_spi.h"里的 pxa2xx_spi_chip 结构提供从设备特定的配置信息。pxa2xx_spi主控制器驱动程序将在驱动程序与从设备通信时使用该配置。所有字段都是可选的。
struct pxa2xx_spi_chip { u8 tx_threshold; u8 rx_threshold; u8 dma_burst_size; u32 timeout; u8 enable_loopback; void (*cs_control)(u32 command); };
“pxa2xx_spi_chip.tx_threshold” 和 “pxa2xx_spi_chip.rx_threshold”字段用于配置SSP硬件FIFO。这些字段对pxa2xx_spi驱动程序的性能至关重要,错误配置将导致rx FIFO溢出(特别是在PIO模式传输中)。好的默认值是:
.tx_threshold = 8, .rx_threshold = 8,
取值范围为1 ~ 16,其中0表示“使用default”。
“pxa2xx_spi_chip.dma_burst_size”字段用于配置PXA2xx DMA引擎 并且与“spi_device.bits_per_word”字段相关。阅读和理解PXA2xx“开发人员手册”部分的DMA控制器和SSP控制器,以确定正确的值。配置为字节范围传输的SSP将使用值8。如果dma_burst_size == 0,驱动程序将确定一个合理的默认值。
“pxa2xx_spi_chip.timeout“字段被用来有效地处理SSP接收器FIFO中的尾随字节。该字段的正确值取决于SPI总线速度(“spi_board_info.max_speed_hz”)和特定的从设备。请注意,PXA2xx SSP 1不支持尾随字节超时,必须忙碌等待任何尾随字节。
“pxa2xx_spi_chip.enable_loopback "字段用于将SSP移植到内部环回模式。在这种模式下,SSP控制器内部连接SSPTX引脚到SSPRX引脚。这对于初始设置测试很有用。
“pxa2xx_spi_chip.cs_control”字段用于指向一个单板特定的函数,用于asserting/deasserting从设备芯片选择。如果该字段为NULL, pxa2xx_spi主控制器驱动程序假设SSP端口被配置为使用GPIO或SSPFRM。
注意:如果使用SSPFRM, SPI驱动程序不能控制芯片选择,所以每次spi_transfer之后,chipselect被丢弃。大多数设备都需要在完整的消息周围进行芯片选择。使用SSPFRM作为GPIO(通过描述符)来适应这些芯片。
NSSP SLAVE SAMPLE
对于遗留平台或在其他情况下,pxa2xx_spi_chip结构被传递到pxa2xx_spi驱动程序中的“spi_board_info.controller_data”字段。下面是使用PXA255 NSSP的配置示例。
/* Chip Select control for the CS8415A SPI slave device */ static void cs8415a_cs_control(u32 command) { if (command & PXA2XX_CS_ASSERT) GPCR(2) = GPIO_bit(2); else GPSR(2) = GPIO_bit(2); } /* Chip Select control for the CS8405A SPI slave device */ static void cs8405a_cs_control(u32 command) { if (command & PXA2XX_CS_ASSERT) GPCR(3) = GPIO_bit(3); else GPSR(3) = GPIO_bit(3); } static struct pxa2xx_spi_chip cs8415a_chip_info = { .tx_threshold = 8, /* SSP hardward FIFO threshold */ .rx_threshold = 8, /* SSP hardward FIFO threshold */ .dma_burst_size = 8, /* Byte wide transfers used so 8 byte bursts */ .timeout = 235, /* See Intel documentation */ .cs_control = cs8415a_cs_control, /* Use external chip select */ }; static struct pxa2xx_spi_chip cs8405a_chip_info = { .tx_threshold = 8, /* SSP hardward FIFO threshold */ .rx_threshold = 8, /* SSP hardward FIFO threshold */ .dma_burst_size = 8, /* Byte wide transfers used so 8 byte bursts */ .timeout = 235, /* See Intel documentation */ .cs_control = cs8405a_cs_control, /* Use external chip select */ }; static struct spi_board_info streetracer_spi_board_info[] __initdata = { { .modalias = "cs8415a", /* Name of spi_driver for this device */ .max_speed_hz = 3686400, /* Run SSP as fast a possbile */ .bus_num = 2, /* Framework bus number */ .chip_select = 0, /* Framework chip select */ .platform_data = NULL; /* No spi_driver specific config */ .controller_data = &cs8415a_chip_info, /* Master chip config */ .irq = STREETRACER_APCI_IRQ, /* Slave device interrupt */ }, { .modalias = "cs8405a", /* Name of spi_driver for this device */ .max_speed_hz = 3686400, /* Run SSP as fast a possbile */ .bus_num = 2, /* Framework bus number */ .chip_select = 1, /* Framework chip select */ .controller_data = &cs8405a_chip_info, /* Master chip config */ .irq = STREETRACER_APCI_IRQ, /* Slave device interrupt */ }, }; static void __init streetracer_init(void) { spi_register_board_info(streetracer_spi_board_info, ARRAY_SIZE(streetracer_spi_board_info)); }
DMA and PIO I/O Support
pxa2xx_spi驱动程序支持DMA和中断驱动PIO消息传输。驱动程序默认为PIO模式,必须通过在“pxa2xx_spi_controller”结构中设置“enable_dma”标志来启用DMA传输。对于已知支持DMA的较新的平台,驱动程序将自动启用它,并首先尝试它,并可能回退到PIO。DMA模式既支持一致性DMA映射,也支持流式DMA映射。
下面的逻辑用于确定在每个“spi_transfer”基础上使用的I/O类型:
if !enable_dma then always use PIO transfers if spi_message.len > 8191 then print "rate limited" warning use PIO transfers if spi_message.is_dma_mapped and rx_dma_buf != 0 and tx_dma_buf != 0 then use coherent DMA mode if rx_buf and tx_buf are aligned on 8 byte boundary then use streaming DMA mode otherwise use PIO transfer