Skip to the content.

PXA2xx SPI on SSP driver HOWTO

1. Overview

这是关于 pxa2xx_spi 驱动程序的迷你 HOWTO。 该驱动程序将 PXA2xx 同步串口转变为 SPI 主机控制器(请参阅Linux 内核 SPI 支持概述)。 该驱动程序具有以下特点:

该驱动程序围绕由内核线程提供服务的 &struct spi_message FIFO 构建。 内核线程 spi_pump_messages() 驱动消息 FIFO,并负责对 SPI 事务进行排队以及设置和启动 DMA 或中断驱动的传输。

2. Declaring PXA2xx host controllers

通常,对于旧平台,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 通道比 Tx 通道具有更高的 DMA 服务优先级。 请参阅“PXA2xx 开发人员手册”中的“DMA 控制器”部分。

对于新平台,控制器和外围设备的描述来自设备树或 ACPI。

3. NSSP HOST 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_controller_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_controller_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));
}

4. Declaring peripheral devices

通常,对于传统平台,每个 SPI 外围设备(芯片)都使用“linux/spi/spi.h”中的“spi_board_info”结构在 arch/…/mach-/board-.c 中定义。 有关更多信息,请参阅“Linux 内核 SPI 支持概述”。

连接到 PXA2xx 的每个外围设备(芯片)必须通过“include/linux/spi/pxa2xx_spi.h”中的结构“pxa2xx_spi_chip”提供特定的芯片配置信息。 每当驱动程序与外围设备通信时,PXA2xx 主机控制器驱动程序都会使用该配置。 所有字段都是可选的。

struct pxa2xx_spi_chip {
      u8 tx_threshold;
      u8 rx_threshold;
      u8 dma_burst_size;
      u32 timeout;
};

“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,其中零表示“使用默认值”。

“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 不支持尾随字节超时,并且必须忙等待任何尾随字节。

注意:如果使用 SSPFRM,SPI 驱动程序无法控制片选,因此每次 spi_transfer 后片选都会被丢弃。 大多数设备需要在完整消息周围断言芯片选择。 使用 SSPFRM 作为 GPIO(通过描述符)来容纳这些芯片。

5. NSSP PERIPHERAL SAMPLE

对于旧平台或在某些其他情况下,pxa2xx_spi_chip 结构将传递到“spi_board_info.controller_data”字段中的 pxa2xx_spi 驱动程序。 以下是使用 PXA255 NSSP 的示例配置。

static struct pxa2xx_spi_chip cs8415a_chip_info = {
      .tx_threshold = 8, /* SSP hardware FIFO threshold */
      .rx_threshold = 8, /* SSP hardware FIFO threshold */
      .dma_burst_size = 8, /* Byte wide transfers used so 8 byte bursts */
      .timeout = 235, /* See Intel documentation */
};

static struct pxa2xx_spi_chip cs8405a_chip_info = {
      .tx_threshold = 8, /* SSP hardware FIFO threshold */
      .rx_threshold = 8, /* SSP hardware FIFO threshold */
      .dma_burst_size = 8, /* Byte wide transfers used so 8 byte bursts */
      .timeout = 235, /* See Intel documentation */
};

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 possible */
              .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, /* Host controller config */
              .irq = STREETRACER_APCI_IRQ, /* Peripheral device interrupt */
      },
      {
              .modalias = "cs8405a", /* Name of spi_driver for this device */
              .max_speed_hz = 3686400, /* Run SSP as fast a possible */
              .bus_num = 2, /* Framework bus number */
              .chip_select = 1, /* Framework chip select */
              .controller_data = &cs8405a_chip_info, /* Host controller config */
              .irq = STREETRACER_APCI_IRQ, /* Peripheral device interrupt */
      },
};

static void __init streetracer_init(void)
{
      spi_register_board_info(streetracer_spi_board_info,
                              ARRAY_SIZE(streetracer_spi_board_info));
}

6. DMA and PIO I/O Support

pxa2xx_spi 驱动程序支持 DMA 和中断驱动的 PIO 消息传输。 驱动程序默认为 PIO 模式,并且必须通过设置“pxa2xx_spi_controller”结构中的“enable_dma”标志来启用 DMA 传输。 对于已知支持 DMA 的较新平台,驱动程序将自动启用它并首先尝试它,并可能回退到 PIO。 DMA 模式支持相干和基于流的 DMA 映射。

以下逻辑用于确定每个“spi_transfer”要使用的 I/O 类型:

if spi_message.len > 65536 then
      if spi_message.is_dma_mapped or rx_dma_buf != 0 or tx_dma_buf != 0 then
              reject premapped transfers

      print "rate limited" warning
      use PIO transfers

if enable_dma and the size is in the range [DMA burst size..65536] then
      use streaming DMA mode

otherwise
      use PIO transfer

7. THANKS TO

David Brownell 和其他人指导了该驱动程序的开发。