本文主要是介绍dw ssi(spi)驱动调试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
1.芯片简介
1.1 模块与接口
1.2 非DMA传输
1.3 DMA传输
1.3.1 DMA寄存器
1.3.3 DMA水线设置
2.调试问题
2.1 uboot
2.1.1 Read Flash ID失败
2.1.2 一次只能读出27B有效内容
2.2 kernel
2.2.1 spi访问读取内容无效
2.2.2 dma传输rx超时
3. 内核SPI驱动
3.1 SPI框架
3.2 spi-dw驱动
4.调试命令
4.1 uboot
4.2 kernel
4.2.1 mtd_debug
4.2.2 debugfs
4.2.3 module parameter
1.芯片简介
1.1 模块与接口
dw_apb_ssi支持全双工的master和slave模式,支持dma传输,支持SPI(Motorola Serial Peripheral Interface),SSP(Texas Instruments Serial Protocol),National Semiconductor Microwire三种接口类型。支持Transmit and Receive,Transmit Only,Receive Only,EEPROM Read四种传输模式。
APB Interface用于寄存器访问和数据收发。
DMA Interface用于跟DMA控制器通信,进行握手/传输请求/传输应答等控制信号的交互。
1.2 非DMA传输
master模式数据传输流程流程如下:
(1) 软件配置控制寄存器设置传输模式,分频系数,FIFO收发门限,中断使能,选择device片选,然后使能控制器,发起传输。
(2) 软件写Tx FIFO,SPI传输的特点决定了有发送才有接收,发送多少数据,接收多少数据。如果传输数据较长,那么要考虑根据FIFO的长度将数据分片。如果只是发送数据,那么直接发送有效的数据内容即可。接收数据时一般配置为Transmit and Receive模式,此时为了接收数据,需要持续在发送方向发送dummy数据。
(3) 软件持续从Rx FIFO中读出数据。
非DMA传输可以采取轮询或者中断方式,uboot下采用轮询模式,内核驱动两种方式都支持。
1.3 DMA传输
1.3.1 DMA寄存器
DMA传输方式需要通用DMA控制器驱动配合。dw ssi引出一组dma控制信号与DMAC交互。
dma_tx_req/dma_rx_req/dma_tx_single/dma_rx_single 是ssi发给dmac的请求信号。
dma_tx_ack/dma_rx_ack 是dmac发给ssi的应答信号。
此外,dw ssi还提供了3个DMA相关的寄存器用于使能DMA收发和设置DMA水线。
DMACR用于使能DMA传输,DMATDLR配置tx fifo水线,DMARDLR配置rx fifo水线。
当tx fifo中的待发数据个数向下减少到水线值时,ssi向dmac发起dma_tx_req,此时dmac应该往tx fifo中注入数据,使得待发数据个数重新达到水线之上。
然后随着ssi逐步发出数据,待发数据个数又开始减少到达水线,于是再次发起dma_tx_req,提示dmac再次注入数据,如此循环。
水线设置低,一次burst发送的数据个数较多,ssi向dmac发起dma_tx_req的频率相对较低;水线设置高,一次burst发送的数据个数较少,ssi向dmac发起dma_tx_req的频率相对较高。
dmac应该及时注入数据,如果水线设置较低,tx fifo很快就会将fifo中剩余的数据发完,而dmac还没来得及注入足够的数据,就会发生下溢(ssi无数据可发)。
当rx fifo中的待收数据个数向上增长到水线值时,发起dma_rx_req,此时dmac应该从rx fifo中取走数据,使得待收数据个数降到水线之下。
然后随着ssi逐步接收数据,待收数据个数又开始增长到达水线,于是再次发起dma_rx_req,提示dmac再次取走数据,如此循环。
水线设置低,一次burst接收的数据个数较少,ssi向dmac发起dma_rx_req的频率相对较高;水线设置高,一次burst接收的数据个数较多,ssi向dmac发起dma_rx_req的频率相对较低。
dmac应该及时取走数据,如果水线设置较高,rx fifo空闲空间很快将被耗尽,而dmac还没来得及取走数据,就会发生上溢(ssi无空间保存新接收的数据)。
1.3.2 DMA传输分片
一次block传输12个data item,分为3次burst,每次burst传输4个data item。
一次block传输15个data item,分为3次burst和3次single,每次burst传输4个data item,每次single传输1个data item。
1.3.3 DMA水线设置
dw_ssi通常用于MEM <-> DEVICE 这种对接场景,发送方向DST device侧发送速率较低,接收方向SRC device侧接收速率较低。以下是手册建议的水线配置:
DMA.CTLx.DEST_MSIZE = FIFO_DEPTH - SSI.DMATDLR
DMA.CTLx.SRC_MSIZE = SSI.DMARDLR + 1
2.调试问题
2.1 uboot
2.1.1 Read Flash ID失败
【问题现象】
uboot命令行视图执行sf probe命令,未读到有效数据。
先确认SPI相关引脚的复用配置,确认为SPI模式。
然后对比A53验证代码,可以读到ID,说明硬件没问题。不同在于收发数据流程:A53中将opcode和dummy data合并在一起,在一个transfer里发送到device,但uboot没有这样的处理。
【问题原因】
使用dw ssi控制device片选信号时,包括READ ID在内的read reg/read data流程要求
opcode/addr/dummy一起发给device,如果是读取数据,tx方向需要继续发送dummy数据,接收方向在接收数据后需要过滤掉无效数据(对应opcode/addr/dummy收到的内容)。
2.1.2 一次只能读出27B有效内容
【问题现象】
从offset=0开始连续读取37B,只有前面27B有效,后面10B内容为0xff。
从offset=0开始读0x25(37B)数据,因为fifo为32B,所以软件中读操作拆分成2段:
第一段:tx opcode=0bh, addr=0x000000, dummy=0xff, tx padding 27B, 同时rx 27B data
第二段:tx opcode=0bh, addr=0x00001b, dummy=0xff, tx padding 10B, 同时rx 10B data
但从实际结果看,第一段读到的内容是正确的,从0x00 - 0x1a;
第二段读到的内容是全FF,正确的数据应该是0x1b - 0x24。
【问题原因】
Read data使用的address必须以小端方式发送给device,offset=0x00时,大小端都一样,所以可以读到正确的内容,offset=0x1b时,实际发送的addr是0x1b0000,该地址实际上未被写入内容,读到的就是0xff。正确的做法是将地址转成小端。
2.2 kernel
2.2.1 spi访问读取内容无效
【问题现象】
内核spi框架传输opcode/addr/dummy与后面的data传输,分成2次transfer完成,这样无法得到正确的数据。像uboot那样将2次transfer拼接成一个,可以得到正确的结果。但是这样一来在spi访问前后增加了2次内存拷贝,开销很大。
抓波形可以看到,分2次传输时,cs中间自动拉高了,接下来opcode=0x9f对应的接收内容是0x00。
而合并成1次transfer时,只出一次片选,opcode=0x9f读到的内容确实是FLASH的JEDEC ID。
【问题原因】
控制器驱动cs时,2次transfer中间cs自动拉高,相当于中断了完整的SPI访问流程,导致得不到正确的数据。将cs引脚配置为gpio模式,由软件控制,2次tranfer过程全程使能cs不拉高,可以得到正确的数据内容。
对应的dts新增cs-gpios配置项:
spi0: spi@f0da0000 {compatible = "snps,dw-apb-ssi";pinctrl-names = "default";pinctrl-0 = <&PA26_pinctrl>;cs-gpios = <&porta 26 GPIO_ACTIVE_LOW>;
};
2.2.2 dma传输rx超时
【问题现象】
dma方式从flash读48 byte内容,dma tx结束,dma rx超时。
【问题原因】
dw DMAC控制器的硬件实现限制最大发送/接收的Burst Len为4,而dw ssi DMARDLR配置接收水线为16,大于DMA通道的Burst Len,使得传输过程最后rx fifo存有数据,但是因为达不到水线,无法触发dma_rx_req,导致数据残留在rx fifo中未被软件接收,dma rx超时。
修改dts中DMAC的burst能力,限制为4,这样相应的dw ssi rx fifo接收水线DMARDLR也配置为4,可以保证rx fifo中数据被完整接收。
注意:虽然ssi DR的宽度为32bits,但一次只能收发8bit数据。
tx方向,传输一个data item意味着从DDR取8bit数据,填入ssi fifo 32bits entry(DR寄存器)的低8位,一次burst传4个data item,也就是4B;
rx方向,传输一个data item意味着ssi fifo 32bits entry(DR寄存器)读入32位数据,取低8位填入DDR,一次burst传4个data item,也就是4B。
DMA的DST和SRC虽然数据宽度不同,但是一次传输的有效数据都是4B,所以dma_tx_req和dma_rx_req最终的次数也是一致的。
下面分析软件从FLASH offset = 0处读取48B数据的dma流程。
有问题的配置对应的DMA处理流程。
发送方向:
(1) ssi tx fifo空,ssi发起dma_tx_req,dmac响应请求,发起一次tx burst,注入DEST_MSIZE个数据(4个),一共写入4*8bits = 4B。
(2)此时仍然tx fifo有效数据仍低于DMATDLR水线,于是ssi再次发起若干次dma_tx_req,dmac响应请求,每次都注入DEST_MSIZE个数据,直到tx fifo中数据个数高于DMATDLR水线。
(3) 随着ssi发送数据,当tx fifo中数据个数下降到DMATDLR水线,ssi再次发起dma_tx_req,dmac响应请求,发起一次tx burst,再注入DEST_MSIZE个数据。
下面重复步骤(3)直到所有数据均写入tx fifo,等待ssi陆续发完。
接收方向:
(1) 由于tx方向持续发送数据,在接收方向上也会持续接收数据。
当ssi rx fifo中数据上涨到达(DMARDLR + 1)水线,ssi发起dma_rx_req,dmac响应请求,发起一次rx burst,读出SRC_MSIZE个数据(4个),一共读出4*8bits = 4B。
(2) 如果此时ssi rx fifo中数据个数仍高于(DMARDLR + 1)水线,那么ssi仍会持续发起dma_rx_req,dmac也会持续读出数据,直到ssi rx fifo中数据个数低于(DMARDLR + 1)水线。
(3) 这里DMARDLR + 1 = 16,而SRC_MSIZE = 4,这就使得ssi rx fifo中会残留12B数据,因为达不到水线,无法通知dmac取走,于是导致rx方向最终超时失败。
正确的配置对应的DMA处理流程。
发送方向:跟之前相同
接收方向:DMARDLR + 1 = SRC_MSIZE = 4,保证了一次rx burst读走rx fifo中所有的数据,没有残留,所以rx 方向可以顺利结束。
残留数据的验证:dma超时后读取RXFLR,确认此时rx fifo中是否有数据,有的话直接dump DR寄存器内容,确认正好就是最后12B数据内容。
3. 内核SPI驱动
3.1 SPI框架
Linux SPI 驱动结构中,将 SPI 相关的驱动分为了几部分:
(1) SPI 主机以及主机驱动:SoC 的 SPI Controller 部分的驱动
(2) SPI 外设驱动描述:比如 SPI Flash 驱动
(3) SPI 从设备描述:比如 SPI Flash 设备
(4) SPI 传输层描述:spi_transfer 和 spi_message 组成
SPI 主机控制器部分是整个 SPI 系统的核心存在,它并不属于 SPI 下的 bus、device、drvier 这一组结构,因为他并不是挂接到 bus 上的 device,更不是对应挂接在 bus 上 device 的 driver,而是相对独立的一个存在,所以 SPI 控制器部分,是连接到 platform 下的,并执行 platform 的 probe。
在数据发送的结构部分,内核将其抽象如下,一次传输封装为message,一个message包含一个或多个transfer。
3.2 spi-dw驱动
spi读写流程,device驱动封装读写接口,调用spi框架的传输API spi_sync(),执行device所属的控制器驱动挂接的transfer_one()接口,实现总线层面的数据访问,传输完成后,spi_sync()返回阻塞的读写进程,得到spi访问的结果。
以SPI FLASH驱动m25p80为例:
read流程
(1) mtd_debug 或者其它应用程序 -> m25p80_read_reg()
-> spi_sync(spi, &message)
-> spi_transfer_one_message() -> dw_spi_transfer_one()
-> wait_for_completion_timeout() 阻塞
(2) dw ssi控制器spi传输
dw_spi_irq() -> dw_reader() -> complete() 通知阻塞流程继续往下执行
(3) mtd_debug 或者其它应用程序得到spi访问数据。
其中dw ssi控制器的传输接口dw_spi_transfer_one()支持三种传输方式:轮询,中断,dma。
(1)轮询
调用dw_spi_poll_transfer(dws, transfer)完成传输,边发边收。
函数返回0。
(2)中断
调用dw_spi_irq_setup(dws)设置中断,后续数据收发在ssi中断处理中完成。
函数返回1。
(3)dma
调用dw_spi_dma_transfer()触发dma传输,后续传输由dma完成,dma中断确认完成状态。spi-dw驱动在初始化时需要事先从通用dma申请通道资源。
- spi框架中先map buf,再传输message,提交dma描述符,挂接回调,传输发起后阻塞等待completion;
- dma传输,dma中断处理确认通道传输完成,调度vchan tasklet;
- vchan tasklet调用dma描述符的callback回调,complete传输;
- spi框架从阻塞处继续执行,unmap buf,message传输完成。
函数返回0。
4.调试命令
4.1 uboot
(1) sf 相关命令
(2) sspi [<bus>:]<cs>[.<mode>][@<freq>] <bit_len> <dout>
4.2 kernel
4.2.1 mtd_debug
SPI FLASH读/擦/写。
# mtd_debug
usage: mtd_debug info <device>
mtd_debug read <device> <offset> <len> <dest-filename>
mtd_debug write <device> <offset> <len> <source-filename>
mtd_debug erase <device> <offset> <len>
4.2.2 debugfs
查看ssi寄存器内容。
# mount -t debugfs none /sys/kernel/debug
# cat /sys/kernel/debug/dw_spi0/registers
CTRLR0 = 0x00070000
.........................
4.2.3 module parameter
使能/去使能dma传输。
# echo 1 > /sys/module/spi_dw/parameters/dma_enable
# echo 0 > /sys/module/spi_dw/parameters/dma_enable
这篇关于dw ssi(spi)驱动调试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!