文章目录
- 参考资料:
- 一、SPI设备树节点构成
- 二、SPI设备树示例
- 2.1 SPI控制器节点属性
- 2.2 SPI设备节点属性
- 三、SPI设备树处理过程
- 四、总结
参考资料:
- 内核头文件:
include\linux\spi\spi.h
- 内核文档:
Documentation\devicetree\bindings\spi\spi-bus.txt
- 内核源码:
drivers\spi\spi.c
- 内核源码:
drivers\spi\spi-gpio.c
一、SPI设备树节点构成
根据SPI的硬件连接框图,我们知道,当SPI控制器作为主设备时,SPI控制器下面可以挂接多个从设备。对应,设备树中就用一个SPI控制器节点作为父节点,在其下面包含子节点来表示从设备。设备树构成如下:
//根节点
/{
//spi控制器节点
xxx_spi{
...
//子节点1
flash{
...
};
//子节点2
oled {
...
};
};
}
二、SPI设备树示例
参考Documentation\devicetree\bindings\spi\spi-bus.txt
示例:
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>;
cs-gpios = <&gpio1 20 0>, <&gpio1 21 0>;
//子节点
ethernet-switch@0 {
compatible = "micrel,ks8995m";
spi-max-frequency = <1000000>;
reg = <0>;
};
//子节点
codec@1 {
compatible = "ti,tlv320aic26";
spi-max-frequency = <100000>;
reg = <1>;
};
};
2.1 SPI控制器节点属性
必须的属性:
#address-cells
:这个SPI Master下的SPI设备,需要多少个cell来表述它的片选引脚,1就表示用一个32位数据来表示。#size-cells
:必须设置为0。compatible
:和SPI Master驱动进行比较配备。
可选的属性:
reg
: 表示SPI Master 的寄存器地址和大小。interrupts
: 描述中断。cs-gpios
:SPI Master可以使用多个GPIO引脚当做片选,可以在这个属性列出那些GPIO引脚。- num-cs:片选引脚总数。
注:还有一些其他和驱动程序相关的属性,不同的SPI Master驱动程序要求的属性可能不一样。
2.2 SPI设备节点属性
必须的属性:
compatible
:和SPI Device驱动进行比较匹配。reg
:用来表示它使用哪个片选引脚。spi-max-frequency
:该SPI设备支持的最大SPI时钟频率。
可选的属性:
spi-cpol
:这是一个空属性(没有值),设置时钟起始电平,如果设置了表示值为1(高电平),没设置则默认0(低电平)。spi-cpha
:这是一个空属性(没有值),设置第几个时钟沿采集数据。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
:单位是毫秒,表示每次写传输后要延时多久。
三、SPI设备树处理过程
对于SPI控制器节点,会对应有一个SPI控制器驱动程序,SPI控制器驱动程序会解析父节点,构造出一个spi_master
结构体并注册它,另外还会解析子节点,每个子节点会生成一个spi_device
结构体。因此,整个SPI设备树节点,都是由SPI控制器驱动程序来解析的 。下面以GPIO模拟的SPI控制器:spi-gpio
为例,看代码处理过程:
spi-gpio 设备树:
spi-gpio {
compatible = "spi-gpio";
#address-cells = <0x1>;
gpio-sck = <&gpio 95 0>;
gpio-miso = <&gpio 98 0>;
gpio-mosi = <&gpio 97 0>;
cs-gpios = <&gpio 125 0>;
num-chipselects = <1>;
/* clients */ };
codec@1 {
compatible = "ti,tlv320aic26";
spi-max-frequency = <100000>;
reg = <1>;
};
驱动程序 spi-gpio.c:
- 从入口函数进入,这里的入口函数在这个宏
module_platform_driver
里面。入口函数里面注册了一个platform_driver
结构体。
//这是一个宏
module_platform_driver(spi_gpio_driver);
//宏展开后
static int __init spi_gpio_driver_init(void)
{
return platform_driver_register(&spi_gpio_driver);
}
module_init(spi_gpio_driver_init);
static void __exit xxx_init(void)
{
return platform_driver_unregister(&spi_gpio_driver);
}
module_exit(spi_gpio_driver_exit);
platform_driver
和设备树匹配成功之后调用probe
函数。
//platform_driver结构
static struct platform_driver spi_gpio_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(spi_gpio_dt_ids),
},
.probe = spi_gpio_probe, //和设备树匹配成功后调用
.remove = spi_gpio_remove,
};
//of_match_table
static const struct of_device_id spi_gpio_dt_ids[] = {
{ .compatible = "spi-gpio" }, //和设备树进行比较
{}
};
probe
函数会分配、设置、注册一个spi_master
static int spi_gpio_probe(struct platform_device *pdev)
{
...
//分配spi_master
master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio) +
(sizeof(unsigned long) * num_devices));
...
//设置spi_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;
...
//调用这个函数,里面会注册spi_master
status = spi_bitbang_start(&spi_gpio->bitbang);
...
}
spi_bitbang_start
函数里面注册spi_master
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
...
//注册spi_master
ret = spi_register_master(spi_master_get(master));
...
}
spi_register_master
里面调用of_spi_register_master
解析设备树,并调用of_register_spi_devices
注册spi_device
。
int spi_register_master(struct spi_master *master)
{
...
//解析设备树
status = of_spi_register_master(master);
...
//注册spi_device
of_register_spi_devices(master);
}
of_register_spi_devices
是复数,它里面会遍历调用of_register_spi_device
注册spi_device
。
tatic void of_register_spi_devices(struct spi_master *master)
{
...
//遍历注册每一个子节点
for_each_available_child_of_node(master->dev.of_node, nc) {
...
//注册单个spi_device
spi = of_register_spi_device(master, nc);
...
}
}
}
of_register_spi_device
里面解析子节点并调用spi_add_device
添加spi_device
。
static struct spi_device *
of_register_spi_device(struct spi_master *master, struct device_node *nc)
{
struct spi_device *spi;
int rc;
u32 value;
/* Alloc an spi_device */
spi = spi_alloc_device(master); //分配spi_device
if (!spi) {
dev_err(&master->dev, "spi_device alloc error for %s\n",
nc->full_name);
rc = -ENOMEM;
goto err_out;
}
/* Select device driver */
rc = of_modalias_node(nc, spi->modalias,
sizeof(spi->modalias));
if (rc < 0) {
dev_err(&master->dev, "cannot find modalias for %s\n",
nc->full_name);
goto err_out;
}
/* Device address */
rc = of_property_read_u32(nc, "reg", &value); //取出reg属性
if (rc) {
dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",
nc->full_name, rc);
goto err_out;
}
spi->chip_select = value; //chip_select值来之reg属性
/* Mode (clock phase/polarity/etc.) */ //模式设置
if (of_find_property(nc, "spi-cpha", NULL))
spi->mode |= SPI_CPHA;
if (of_find_property(nc, "spi-cpol", NULL))
spi->mode |= SPI_CPOL;
if (of_find_property(nc, "spi-cs-high", NULL))
spi->mode |= SPI_CS_HIGH;
if (of_find_property(nc, "spi-3wire", NULL))
spi->mode |= SPI_3WIRE;
if (of_find_property(nc, "spi-lsb-first", NULL))
spi->mode |= SPI_LSB_FIRST;
/* Device DUAL/QUAD mode */
if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
switch (value) {
case 1:
break;
case 2:
spi->mode |= SPI_TX_DUAL;
break;
case 4:
spi->mode |= SPI_TX_QUAD;
break;
default:
dev_warn(&master->dev,
"spi-tx-bus-width %d not supported\n",
value);
break;
}
}
if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {
switch (value) {
case 1:
break;
case 2:
spi->mode |= SPI_RX_DUAL;
break;
case 4:
spi->mode |= SPI_RX_QUAD;
break;
default:
dev_warn(&master->dev,
"spi-rx-bus-width %d not supported\n",
value);
break;
}
}
/* Device speed */ //最大频率
rc = of_property_read_u32(nc, "spi-max-frequency", &value);
if (rc) {
dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",
nc->full_name, rc);
goto err_out;
}
spi->max_speed_hz = value;
/* Store a pointer to the node in the device structure */
of_node_get(nc);
spi->dev.of_node = nc;
/* Register the new device */ //注册spi_device
rc = spi_add_device(spi);
if (rc) {
dev_err(&master->dev, "spi_device register error %s\n",
nc->full_name);
goto err_of_node_put;
}
return spi;
err_of_node_put:
of_node_put(nc);
err_out:
spi_dev_put(spi);
return ERR_PTR(rc);
}
四、总结
本文介绍分析了SPI设备树及其处理过程。