添加文档:04_SPI设备树处理过程

This commit is contained in:
weidongshan
2022-03-04 15:17:54 +08:00
parent ab0f13cb2e
commit d668740f7f
9 changed files with 439 additions and 7 deletions

View File

@@ -3,16 +3,223 @@
参考资料:
* 内核头文件:`include\linux\spi\spi.h`
* 内核文档:`Documentation\devicetree\bindings\spi\spi-bus.txt`
* 内核源码:`drivers\spi\spi.c`
## 1. 设备树示例
## 1. spi_device结构体
```c
/**
* struct spi_device - Master side proxy for an SPI slave device
* @dev: Driver model representation of the device.
* @master: SPI controller used with the device.
* @max_speed_hz: Maximum clock rate to be used with this chip
* (on this board); may be changed by the device's driver.
* The spi_transfer.speed_hz can override this for each transfer.
* @chip_select: Chipselect, distinguishing chips handled by @master.
* @mode: The spi mode defines how data is clocked out and in.
* This may be changed by the device's driver.
* The "active low" default for chipselect mode can be overridden
* (by specifying SPI_CS_HIGH) as can the "MSB first" default for
* each word in a transfer (by specifying SPI_LSB_FIRST).
* @bits_per_word: Data transfers involve one or more words; word sizes
* like eight or 12 bits are common. In-memory wordsizes are
* powers of two bytes (e.g. 20 bit samples use 32 bits).
* This may be changed by the device's driver, or left at the
* default (0) indicating protocol words are eight bit bytes.
* The spi_transfer.bits_per_word can override this for each transfer.
* @irq: Negative, or the number passed to request_irq() to receive
* interrupts from this device.
* @controller_state: Controller's runtime state
* @controller_data: Board-specific definitions for controller, such as
* FIFO initialization parameters; from board_info.controller_data
* @modalias: Name of the driver to use with this device, or an alias
* for that name. This appears in the sysfs "modalias" attribute
* for driver coldplugging, and in uevents used for hotplugging
* @cs_gpio: gpio number of the chipselect line (optional, -ENOENT when
* when not using a GPIO line)
*
* @statistics: statistics for the spi_device
*
* A @spi_device is used to interchange data between an SPI slave
* (usually a discrete chip) and CPU memory.
*
* In @dev, the platform_data is used to hold information about this
* device that's meaningful to the device's protocol driver, but not
* to its controller. One example might be an identifier for a chip
* variant with slightly different functionality; another might be
* information about how this particular board wires the chip's pins.
*/
struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz;
u8 chip_select;
u8 bits_per_word;
u16 mode;
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
#define SPI_READY 0x80 /* slave pulls low to pause */
#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
int irq;
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];
int cs_gpio; /* chip select gpio */
/* the statistics */
struct spi_statistics statistics;
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
* - drop chipselect after each word
* - chipselect delays
* - ...
*/
};
```
各个成员含义如下:
* max_speed_hz该设备能支持的SPI时钟最大值
* chip_select是这个spi_master下的第几个设备
* 在spi_master中有一个cs_gpios数组里面存放有下面各个spi设备的片选引脚
* spi_device的片选引脚就是cs_gpios[spi_device.chip_select]
* cs_gpio这是可选项也可以把spi_device的片选引脚记录在这里
* bits_per_word每个基本的SPI传输涉及多少位
* word我们使用SPI控制器时一般是往某个寄存器里写入数据SPI控制器就会把这些数据一位一位地发送出去
* 一个寄存器是32位的被称为一个word(有时候也称为double word)
* 这个寄存器里多少位会被发送出去使用bits_per_word来表示
* 扩展bits_per_word是可以大于32的也就是每次SPI传输可能会发送多于32位的数据这适用于DMA突发传输
* mode含义广泛看看结构体里那些宏
* SPI_CPHA在第1个周期采样在第2个周期采样
* SPI_CPOL平时时钟极性
* SPI_CPHA和SPI_CPOL组合起来就可以得到4种模式
* SPI_MODE_0平时SCK为低(SPI_CPOL为0)在第1个周期采样(SPI_CPHA为0)
* SPI_MODE_1平时SCK为低(SPI_CPOL为0)在第2个周期采样(SPI_CPHA为1)
* SPI_MODE_2平时SCK为高(SPI_CPOL为1)在第1个周期采样(SPI_CPHA为0)
* SPI_MODE_3平时SCK为高(SPI_CPOL为1)在第2个周期采样(SPI_CPHA为1)
* SPI_CS_HIGH一般来说片选引脚时低电平有效SPI_CS_HIGH表示高电平有效
* SPI_LSB_FIRST
* 一般来说先传输MSB(最高位)SPI_LSB_FIRST表示先传LSB(最低位)
* 很多SPI控制器并不支持SPI_LSB_FIRST
* SPI_3WIRESO、SI共用一条线
* SPI_LOOP回环模式就是SO、SI连接在一起
* SPI_NO_CS只有一个SPI设备没有片选信号也不需要片选信号
* SPI_READYSPI从设备可以拉低信号表示暂停、表示未就绪
* SPI_TX_DUAL发送数据时有2条信号线
* SPI_TX_QUAD发送数据时有4条信号线
* SPI_RX_DUAL接收数据时有2条信号线
* SPI_RX_QUAD接收数据时有4条信号线
## 2. SPI设备树格式
![](pic/01_hardware_block.jpg)
对于SPI Master就是SPI控制器它下面可以连接多个SPI设备。
在设备树里使用一个节点来表示SPI Master使用子节点来表示挂在下面的SPI设备。
### 2.1 SPI Master
在设备树中对于SPI Master必须的属性如下
* #address-cells这个SPI Master下的SPI设备需要多少个cell来表述它的片选引脚
* #size-cells必须设置为0
* compatible根据它找到SPI Master驱动
可选的属性如下:
* cs-gpiosSPI Master可以使用多个GPIO当做片选可以在这个属性列出那些GPIO
* num-cs片选引脚总数
其他属性都是驱动程序相关的不同的SPI Master驱动程序要求的属性可能不一样。
### 2.2 SPI Device
在SPI Master对应的设备树节点下每一个子节点都对应一个SPI设备这个SPI设备连接在该SPI Master下面。
这些子节点中,必选的属性如下:
* compatible根据它找到SPI Device驱动
* reg用来表示它使用哪个片选引脚
* spi-max-frequency必选该SPI设备支持的最大SPI时钟
可选的属性如下:
* spi-cpol这是一个空属性(没有值)表示CPOL为1即平时SPI时钟为低电平
* spi-cpha这是一个空属性(没有值)表示CPHA为1)即在时钟的第2个边沿采样数据
* spi-cs-high这是一个空属性(没有值),表示片选引脚高电平有效
* spi-3wire这是一个空属性(没有值)表示使用SPI 三线模式
* spi-lsb-first这是一个空属性(没有值)表示使用SPI传输数据时先传输最低位(LSB)
* spi-tx-bus-width表示有几条MOSI引脚没有这个属性时默认只有1条MOSI引脚
* spi-rx-bus-width表示有几条MISO引脚没有这个属性时默认只有1条MISO引脚
* spi-rx-delay-us单位是毫秒表示每次读传输后要延时多久
* spi-tx-delay-us单位是毫秒表示每次写传输后要延时多久
### 2.3 设备树示例
```shell
spi@f00 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,mpc5200b-spi","fsl,mpc5200-spi";
reg = <0xf00 0x20>;
interrupts = <2 13 0 2 14 0>;
interrupt-parent = <&mpc5200_pic>;
ethernet-switch@0 {
compatible = "micrel,ks8995m";
spi-max-frequency = <1000000>;
reg = <0>;
};
codec@1 {
compatible = "ti,tlv320aic26";
spi-max-frequency = <100000>;
reg = <1>;
};
};
```
## 3. 设备树实例
在设备树里会有一个节点用来表示SPI控制器。
在这个SPI控制器下面连接有哪些SPI设备会在设备树里使用子节点来描述SPI设备。
### 1.1 使用GPIO模拟的SPI控制器
### 3.1 使用GPIO模拟的SPI控制器
![image-20220217181848873](pic/11_spi_master_device_tree.png)
@@ -20,7 +227,7 @@
### 1.2 IMX6ULL SPI控制器
### 3.2 IMX6ULL SPI控制器
内核文件arch/arm/boot/dts/imx6ull.dtsi
@@ -32,7 +239,7 @@
### 1.3 STM32MP157 SPI控制器
### 3.3 STM32MP157 SPI控制器
内核文件arch/arm/boot/dts/stm32mp151.dtsi
@@ -44,9 +251,11 @@
## 2. 设备树处理过程
## 4. 设备树处理过程
内核源码:`drivers\spi\spi.c`
![image-20220304144624084](pic/22_porcess_spi_devicetree.png)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

View File

@@ -0,0 +1,223 @@
# 临时笔记 #
参考资料:
* 内核头文件:`include\linux\spi\spi.h`
## 1. SPI Master
文件:`drivers\spi\spi-gpio.c`
```c
spi_gpio = spi_master_get_devdata(master);
master = spi_alloc_master(...);
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
master->flags = master_flags;
master->bus_num = pdev->id;
master->num_chipselect = num_devices;
master->setup = spi_gpio_setup;
master->cleanup = spi_gpio_cleanup;
status = of_get_named_gpio(np, "cs-gpios", i);
spi_gpio->cs_gpios[i] = status;
spi_gpio->bitbang.master = master;
spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) {
spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;
spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;
spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;
spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;
} else {
spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0;
spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1;
spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2;
spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3;
}
spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer;
spi_gpio->bitbang.flags = SPI_CS_HIGH;
status = spi_bitbang_start(&spi_gpio->bitbang);
```
## 2. spi_bitbang_start
```c
if (master->transfer || master->transfer_one_message)
return -EINVAL;
master->prepare_transfer_hardware = spi_bitbang_prepare_hardware;
master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware;
master->transfer_one = spi_bitbang_transfer_one;
master->set_cs = spi_bitbang_set_cs;
if (!bitbang->txrx_bufs) {
bitbang->use_dma = 0;
bitbang->txrx_bufs = spi_bitbang_bufs;
if (!master->setup) {
if (!bitbang->setup_transfer)
bitbang->setup_transfer =
spi_bitbang_setup_transfer;
master->setup = spi_bitbang_setup;
master->cleanup = spi_bitbang_cleanup;
}
}
ret = spi_register_master(spi_master_get(master));
```
## 3. spi_register_master
```c
status = of_spi_register_master(master);
/* If we're using a queued driver, start the queue */
if (master->transfer)
dev_info(dev, "master is unqueued, this is deprecated\n");
else {
status = spi_master_initialize_queue(master);
if (status) {
device_del(&master->dev);
goto done;
}
}
```
## 4. spi_master_initialize_queue
```c
static int spi_master_initialize_queue(struct spi_master *master)
{
int ret;
master->transfer = spi_queued_transfer;
if (!master->transfer_one_message)
master->transfer_one_message = spi_transfer_one_message;
/* Initialize and start queue */
ret = spi_init_queue(master);
if (ret) {
dev_err(&master->dev, "problem initializing queue\n");
goto err_init_queue;
}
master->queued = true;
ret = spi_start_queue(master);
if (ret) {
dev_err(&master->dev, "problem starting queue\n");
goto err_start_queue;
}
return 0;
err_start_queue:
spi_destroy_queue(master);
err_init_queue:
return ret;
}
```
## 5. spi_init_queue
```c
kthread_init_worker(&master->kworker);
master->kworker_task = kthread_run(kthread_worker_fn,
&master->kworker, "%s",
dev_name(&master->dev));
kthread_init_work(&master->pump_messages, spi_pump_messages);
```
## 6. spi_start_queue
```c
kthread_queue_work(&master->kworker, &master->pump_messages);
```
## 7. SPI传输
```c
spi_sync
__spi_sync
message->complete = spi_complete;
if (master->transfer == spi_queued_transfer) {
status = __spi_queued_transfer(spi, message, false);
list_add_tail(&msg->queue, &master->queue);
__spi_pump_messages(master, false);
kthread_queue_work(&master->kworker,
&master->pump_messages);
spi_pump_messages
__spi_pump_messages(master, true);
ret = master->transfer_one_message(master, master->cur_msg);
spi_transfer_one_message
spi_set_cs(msg->spi, true);
reinit_completion(&master->xfer_completion);
ret = master->transfer_one(master, msg->spi, xfer);
spi_bitbang_transfer_one
status = bitbang->txrx_bufs(spi, transfer);
spi_bitbang_bufs
ms = wait_for_completion_timeout(&master->xfer_completion,
msecs_to_jiffies(ms)); spi_finalize_current_message(master);
wait_for_completion(&done);
} else {
status = spi_async_locked(spi, message);
__spi_async
return master->transfer(spi, message);
}
```
## 8. spi_bitbang_bufs
```c
return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t);
```
![image-20220303143723585](pic/17_txrx_bufs.png)
![image-20220303144759926](pic/18_set_txrx_word.png)
## 9. bitbang_txrx_16
![image-20220303145227558](pic/19_bitbang_txrx_16.png)
上图里的txrx_word来自哪里spi_master在传输spi_transfer之前要设置。
对于SPI GPIO
![image-20220303145416724](pic/20_spi_gpio_probe.png)
对于IMX
![image-20220303150143475](pic/21_spi_imx_setupxfer.png)