一、Linux 下 SPI 驱动框架简介
1、SPI 主机驱动
SPI 主机驱动就是 SOC 的 SPI 控制器驱动,类似 I2C 驱动里面的适配器驱动。Linux 内核使用 spi_master 表示 SPI 主机驱动,spi_master 是个结构体,定义在 include/linux/spi/spi.h 文件中,内容如下(有缩减):
struct spi_master {
struct device dev;
struct list_head list;
......
s16 bus_num;
/* chipselects will be integral to many controllers; some others
* might use board-specific GPIOs.
*/
u16 num_chipselect;
/* some SPI controllers pose alignment requirements on DMAable
* buffers; let protocol drivers know about these requirements.
*/
u16 dma_alignment;
/* spi_device.mode flags understood by this controller driver */
u16 mode_bits;
/* bitmask of supported bits_per_word for transfers */
u32 bits_per_word_mask;
......
/* limits on transfer speed */
u32 min_speed_hz;
u32 max_speed_hz;
/* other constraints relevant to this driver */
u16 flags;
......
/* lock and mutex for SPI bus locking */
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex;
/* flag indicating that the SPI bus is locked for exclusive use */
bool bus_lock_flag;
......
int (*setup)(struct spi_device *spi);
......
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);
......
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg);
......
};
第 41 行:transfer 函数,和 i2c_algorithm 中的 master_xfer 函数一样,控制器数据传输函数。
第 45 行:transfer_one_message 函数,也用于 SPI 数据发送,用于发送一个 spi_message,SPI 的数据会打包成 spi_message,然后以队列方式发送出去。
也就是 SPI 主机端最终会通过 transfer 函数与 SPI 设备进行通信,因此对于 SPI 主机控制器的驱动编写者而言 transfer 函数是需要实现的,因为不同的 SOC 其 SPI 控制器不同,寄存器都不一样。
和 I2C 适配器驱动一样,SPI 主机驱动一般都是 SOC 厂商去编写的,SOC 的使用者不用操心。
SPI 主机驱动的核心就是申请 spi_master,然后初始化 spi_master,最后向 Linux 内核注册 spi_master。
1、spi_master 申请与释放
spi_alloc_master 函数用于申请 spi_master,函数原型如下:
struct spi_master *spi_alloc_master(struct device *dev,
unsigned size)
函数参数和返回值含义如下:
dev:设备,一般是 platform_device 中的 dev 成员变量。
size:私有数据大小,可通过 spi_master_get_devdata 函数获取这些私有数据。
返回值:申请到的 spi_master。
spi_master 的释放通过 spi_master_put 函数来完成,当我们删除一个 SPI 主机驱动的时候就需要释放掉前面申请的 spi_master,spi_master_put 函数原型如下:
void spi_master_put(struct spi_master *master)
函数参数和返回值含义如下:
master:要释放的 spi_master。
返回值:无。
2、spi_master 的注册与注销
注册:
当 spi_master 初始化完成以后就需要将其注册到 Linux 内核,spi_master 注册函数为spi_register_master,函数原型如下:
int spi_register_master(struct spi_master *master)
函数参数和返回值含义如下:
master:要注册的 spi_master。
返回值:0,成功;负值,失败。
I.MX6U 的 SPI 主机驱动会采用 spi_bitbang_start 这个 API 函数来完成 spi_master 的注册,spi_bitbang_start 函数内部其实也是通过调用 spi_register_master 函数来完成 spi_master 的注册。
注销:
如果要注销 spi_master 的话可以使用 spi_unregister_master 函数,此函数原型为:
void spi_unregister_master(struct spi_master *master)
函数参数和返回值含义如下:
master:要注销的 spi_master。
返回值:无。
如果使用 spi_bitbang_start 注册 spi_master 的话就要使用 spi_bitbang_stop 来注销掉 spi_master。
2、I.MX6U SPI 主机驱动分析
和 I2C 的适配器驱动一样,SPI 主机驱动一般都由 SOC 厂商编写好了,打开 imx6ull.dtsi 文件,找到如下所示内容:
ecspi3: ecspi@02010000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = 'fsl,imx6ul-ecspi', 'fsl,imx51-ecspi';
reg = <0x02010000 0x4000>;
interrupts = clocks = <&clks IMX6UL_CLK_ECSPI3>, <&clks IMX6UL_CLK_ECSPI3>; clock-names = 'ipg', 'per'; dmas = <&sdma 7 7 1>, <&sdma 8 7 2>; dma-names = 'rx', 'tx'; status = 'disabled'; }; 第 4 行:compatible 属性值,compatible 属性有两个值“fsl,imx6ul-ecspi”和“fsl,imx51-ecspi”,在 Linux 内核源码中搜素这两个属性值即可找到 I.MX6U 对应的 ECSPI(SPI)主机驱动。 I.MX6U 的 ECSPI 主机驱动文件为 drivers/spi/spi-imx.c,在此文件中找到如下内容: static struct platform_device_id spi_imx_devtype[] = { { .name = 'imx1-cspi', .driver_data = (kernel_ulong_t) &imx1_cspi_devtype_data, }, { .name = 'imx21-cspi', .driver_data = (kernel_ulong_t) &imx21_cspi_devtype_data, ...... }, { .name = 'imx6ul-ecspi', .driver_data = (kernel_ulong_t) &imx6ul_ecspi_devtype_data, }, { /* sentinel */ } }; static const struct of_device_id spi_imx_dt_ids[] = { { .compatible = 'fsl,imx1-cspi', .data = &imx1_cspi_devtype_data, }, ...... { .compatible = 'fsl,imx6ul-ecspi', .data = &imx6ul_ecspi_devtype_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, spi_imx_dt_ids); ...... ...... ...... static struct platform_driver spi_imx_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = spi_imx_dt_ids, .pm = IMX_SPI_PM, }, .id_table = spi_imx_devtype, .probe = spi_imx_probe, .remove = spi_imx_remove, }; module_platform_driver(spi_imx_driver); 第 10 行:spi_imx_devtype 为 SPI 无设备树匹配表。 第 17 行:spi_imx_dt_ids 为 SPI 设备树匹配表。 第 21 行:“fsl,imx6ul-ecspi”匹配项,因此可知 I.MX6U 的 ECSPI 驱动就是 spi-imx.c 这个文件。 第 29~38 行:platform_driver 驱动框架,和 I2C 的适配器驱动一样,SPI 主机驱动器采用了 platfom 驱动框架。当设备和驱动匹配成功以后 spi_imx_probe 函数就会执行。 spi_imx_probe 函数会从设备树中读取相应的节点属性值,申请并初始化 spi_master,定义在 drives/spi/spi-imx.c 中,内容如下: static int spi_imx_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; const struct of_device_id *of_id = of_match_device(spi_imx_dt_ids, &pdev->dev); struct spi_imx_master *mxc_platform_info = dev_get_platdata(&pdev->dev); struct spi_master *master; struct spi_imx_data *spi_imx; struct resource *res; int i, ret, num_cs, irq; if (!np && !mxc_platform_info) { dev_err(&pdev->dev, 'can't get the platform datan'); return -EINVAL; } ret = of_property_read_u32(np, 'fsl,spi-num-chipselects', &num_cs); if (ret < 0) { if (mxc_platform_info) num_cs = mxc_platform_info->num_chipselect; else return ret; } master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data) + sizeof(int) * num_cs); if (!master) return -ENOMEM; platform_set_drvdata(pdev, master); master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->bus_num = pdev->id; master->num_chipselect = num_cs; spi_imx = spi_master_get_devdata(master); spi_imx->bitbang.master = master; for (i = 0; i < master->num_chipselect; i++) { int cs_gpio = of_get_named_gpio(np, 'cs-gpios', i); if (!gpio_is_valid(cs_gpio) && mxc_platform_info) cs_gpio = mxc_platform_info->chipselect[i]; spi_imx->chipselect[i] = cs_gpio; if (!gpio_is_valid(cs_gpio)) continue; ret = devm_gpio_request(&pdev->dev, spi_imx->chipselect[i], DRIVER_NAME); if (ret) { dev_err(&pdev->dev, 'can't get cs gpiosn'); goto out_master_put; } } spi_imx->bitbang.chipselect = spi_imx_chipselect; spi_imx->bitbang.setup_transfer = spi_imx_setupxfer; spi_imx->bitbang.txrx_bufs = spi_imx_transfer; spi_imx->bitbang.master->setup = spi_imx_setup; spi_imx->bitbang.master->cleanup = spi_imx_cleanup; spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message; spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message; spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; init_completion(&spi_imx->xfer_done); spi_imx->devtype_data = of_id ? of_id->data : (struct spi_imx_devtype_data *) pdev->id_entry->driver_data; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); spi_imx->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(spi_imx->base)) { ret = PTR_ERR(spi_imx->base); goto out_master_put; } irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = irq; goto out_master_put; } ret = devm_request_irq(&pdev->dev, irq, spi_imx_isr, 0, dev_name(&pdev->dev), spi_imx); if (ret) { dev_err(&pdev->dev, 'can't get irq%d: %dn', irq, ret); goto out_master_put; } spi_imx->clk_ipg = devm_clk_get(&pdev->dev, 'ipg'); if (IS_ERR(spi_imx->clk_ipg)) { ret = PTR_ERR(spi_imx->clk_ipg); goto out_master_put; } spi_imx->clk_per = devm_clk_get(&pdev->dev, 'per'); if (IS_ERR(spi_imx->clk_per)) { ret = PTR_ERR(spi_imx->clk_per); goto out_master_put; } ret = clk_prepare_enable(spi_imx->clk_per); if (ret) goto out_master_put; ret = clk_prepare_enable(spi_imx->clk_ipg); if (ret) goto out_put_per; spi_imx->spi_clk = clk_get_rate(spi_imx->clk_per); /* * Only validated on i.mx6 now, can remove the constrain if validated on * other chips. */ if ((spi_imx->devtype_data == &imx51_ecspi_devtype_data || spi_imx->devtype_data == &imx6ul_ecspi_devtype_data) && spi_imx_sdma_init(&pdev->dev, spi_imx, master, res)) dev_err(&pdev->dev, 'dma setup error,use pio insteadn'); spi_imx->devtype_data->reset(spi_imx); spi_imx->devtype_data->intctrl(spi_imx, 0); master->dev.of_node = pdev->dev.of_node; ret = spi_bitbang_start(&spi_imx->bitbang); if (ret) { dev_err(&pdev->dev, 'bitbang start failed with %dn', ret); goto out_clk_put; } dev_info(&pdev->dev, 'probedn');
上一篇:【IMX6ULL学习笔记】二十、IIC驱动和设备
下一篇:【IMX6ULL学习笔记】十八、Platform 驱动框架
推荐阅读最新更新时间:2026-03-25 10:19
- 用于 7VIN 至 16VIN、1.5V 和 1.2V 输出的 LTM4628EV DC/DC 模块稳压器的典型应用电路
- 使用 Analog Devices 的 LTC3728LIGN 的参考设计
- DER-406 - 适用于 A19 灯的 5.76 W 高 PF 非隔离降压-升压型 TRIAC 调光 LED 驱动器
- ADR5045B 5V 输出精密微功率并联模式电压基准的典型应用
- LT3970EDDB-3.42 2.5V 降压转换器的典型应用
- MC78M08BDTG 8V 电流调节器的典型应用
- LT1021DCN8-5 精密电压基准的典型应用
- DER-282 - 100W, 扁平(11 mm), LLC DC-DC转换器
- REF193 低压差开尔文连接电压基准的典型应用电路
- LT3088EM 线性稳压器用于添加软启动的典型应用



DS1000Z数字示波器中文使用说明
现代雷达系统的信号设计
BFR340T






京公网安备 11010802033920号