Linux系统SPI驱动总结
平台:rk3288
系统:Android7.1 kernel4.4.143
linux spi 驱动分为三部分:
- SPI外设驱动:我们写。oled,spi flash 等
- linux spi核心层:drivers/spi/spi.c
- 芯片的SPI控制器驱动:drivers/spi/spi-rockchip.c
Linux SPI 框架理解
1、首先看dts:arch/arm/boot/dts/rk3288.dtsi
spi0: spi@ff110000 {compatible = "rockchip,rk3288-spi", "rockchip,rk3066-spi";clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>;clock-names = "spiclk", "apb_pclk";dmas = <&dmac_peri 11>, <&dmac_peri 12>;dma-names = "tx", "rx";interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;pinctrl-names = "default";pinctrl-0 = <&spi0_clk &spi0_tx &spi0_rx &spi0_cs0>;reg = <0x0 0xff110000 0x0 0x1000>;#address-cells = <1>;#size-cells = <0>;status = "disabled";
};
2、根据这个属性匹配瑞芯微SPI控制器驱动:drivers/spi/spi-rockchip.c
static int rockchip_spi_probe(struct platform_device *pdev)
{...ret = devm_spi_register_master(&pdev->dev, master);if (ret) {dev_err(&pdev->dev, "Failed to register master\n");goto err_register_master;}return 0;...return ret;
}
3、调用 devm_spi_register_master 函数注册master 然后进入到 linux SPI的核心层,进入到 drivers/spi/spi.c 文件
drivers/spi/spi.c:
devm_spi_register_master/* 注册SPI主控制器 */ret = spi_register_master(master);/* 从设备树中注册设备 */of_register_spi_devices(master);/* 遍历可用的子项然后注册到SPI总线上 */for_each_available_child_of_node(master->dev.of_node, nc)/* 从dts里获取配置信息,reg片选属性:0、1,mode,spi-max-frequency等 */spi = of_register_spi_device(master, nc);spi_alloc_device/* 选择设备驱动程序 */of_modalias_nodecompatible = of_get_property(node, "compatible", &cplen);/* 注册新的设备 */spi_add_devicespi_setup(spi);device_add /* 会绑定到一个spi_driver */
drivers/spi/spi.c 文件里的#if defined(CONFIG_OF) 这个判断成立的函数都是为设备树接口函数去服务的
4、SPI收发
include/linux/spi/spi.h:
spi_writespi_message_init(&m);初始化一个spi_message /* 一个不可打断的SPI传输过程: cs=0,传数据,cs=1 *//* 一个spi_message由多个spi_transfer组成 */spi_message_add_tail(&t, &m); /* spi_transfe是SPI上传输的单方向1个或多个字节 */spi_sync(spi, &m); /* 启动传输并等待完成 */spi_readspi_message_init(&m);初始化一个spi_message /* 一个不可打断的SPI传输过程: cs=0,传数据,cs=1 收数据 *//* 一个spi_message由多个spi_transfer组成 */spi_message_add_tail(&t, &m); /* spi_transfe是SPI上传输的单方向1个或多个字节 */spi_sync(spi, &m); /* 启动传输并等待完成 */
5、spi_driver如何调用spi_controller
drivers/spi/spi.c:
spi_sync__spi_sync(spi, message, 0);status = spi_async_locked(spi, message);ret = __spi_async(spi, message);master->transfer(spi, message);/* 等待传输完成 */wait_for_completion
不确定,因为看了控制器的传输函数名是master->transfer_one
自己写的DM412驱动,使用SPI控制器的驱动
1、驱动
/** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.*/#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/fs.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#include <linux/hrtimer.h>
#include <linux/platform_data/spi-rockchip.h>
#include <asm/uaccess.h>
#include <linux/syscalls.h>static struct spi_device *g_spidev = NULL;
/*
static void dm412_read(struct spi_device *spi, void *buf, size_t len)
{int ret;ret = spi_read(spi, buf, len);if(ret)printk("spi read failed! ret = %d\n",ret);
}
*/
static void dm412_write(struct spi_device *spi, const void *buf, size_t len)
{int ret;ret = spi_write(spi, buf, len);if(ret)printk("spi write failed! ret = %d\n",ret);
}static ssize_t dm412Data_show(struct device *dev, struct device_attribute *attr, char *buf)
{return sprintf(buf, "111111\n");
}static ssize_t dm412Data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{unsigned long dm412Data;int rc = kstrtoul(buf, 0, &dm412Data);if (rc)return rc;if (dm412Data)dm412_write(g_spidev,&dm412Data,sizeof(dm412Data));return count;
}
static DEVICE_ATTR(dm412Data, 0664, dm412Data_show, dm412Data_store);static int dm412_probe(struct spi_device *spi)
{int ret;unsigned char tx_buf[6];
// int r = 65535;
// int g = 0;
// int b = 0;printk("%s\n", __func__);if (!spi)return -ENOMEM;g_spidev = spi;
/*spi->mode |= SPI_CS_HIGH;ret = spi_setup(spi);if (ret < 0) {dev_err(&spi->dev, "ERR: fail to setup spi\n");return -1;}
*/printk("%s:name=%s,bus_num=%d,cs=%d,mode=%d,speed=%d,bits_per_word=%d\n", __func__, spi->modalias, spi->master->bus_num, spi->chip_select, spi->mode, spi->max_speed_hz, spi->bits_per_word);ret = device_create_file(&spi->dev, &dev_attr_dm412Data);if (ret)printk("dm412Data device_create_file_error\n");elseprintk("dm412Data device_create_file_success\n");tx_buf[0] = 0xff;tx_buf[1] = 0xff;tx_buf[2] = 0;tx_buf[3] = 0;tx_buf[4] = 0;tx_buf[5] = 0;dm412_write(g_spidev,tx_buf,6);return ret;
}static int dm412_remove(struct spi_device *spi)
{printk("%s\n", __func__);return 0;
}static const struct of_device_id dm412_of_match[] = {{ .compatible = "siti,dm412", },{ /* Sentinel */ },
};
MODULE_DEVICE_TABLE(of, dm412_of_match);static struct spi_driver dm412_driver = {.driver = {.name = "dm412",.owner = THIS_MODULE,.of_match_table = of_match_ptr(dm412_of_match),},.probe = dm412_probe,.remove = dm412_remove,
};static int __init dm412_init(void)
{return spi_register_driver(&dm412_driver);
}
module_init(dm412_init);static void __exit dm412_exit(void)
{return spi_unregister_driver(&dm412_driver);
}
module_exit(dm412_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Wu Tao <wt.relax@foxmail.com>");
MODULE_DESCRIPTION("dm412 led Driver");
2、DTS配置
&spi0 {status = "okay";max-freq = <48000000>;dm412@00 {status = "okay";compatible = "siti,dm412";reg = <0>;spi-max-frequency = <20000000>;};
};
一个SPI上有两个 片选引脚 CS0,CS1。这里连接的CS0。所以配置reg为0。如果不太知道dts里面怎么配置,可以查看drivers/spi/spi.c文件里of_register_spi_device函数获取了哪一些属性。还有参考瑞芯微提供的开发文档。
3、驱动最后无法使用,发现 dem412 片选脚是高电平有效。 而rk3288是低电平有效。
使用GPIO口模拟可以正常使用的驱动
1、驱动
/** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.*/#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/fs.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>struct dm412_desc{struct gpio_desc *spi_sdi_gpio;struct gpio_desc *spi_scl_gpio;struct gpio_desc *spi_cs_gpio;
};struct dm412_desc *g_of_desc = NULL;static void dm412_spi0_send_cmd(struct dm412_desc *of_desc, unsigned int r, unsigned int g, unsigned int b)
{int i;gpiod_direction_output(of_desc->spi_cs_gpio, 1);for(i = 0; i < 16; i++){if(r&0x8000)gpiod_direction_output(of_desc->spi_sdi_gpio, 1);elsegpiod_direction_output(of_desc->spi_sdi_gpio, 0);gpiod_direction_output(of_desc->spi_scl_gpio, 0);udelay(20);gpiod_direction_output(of_desc->spi_scl_gpio, 1); // 时钟上升沿读取数据r <<= 1;udelay(20);}for(i = 0; i < 16; i++){if(g&0x8000)gpiod_direction_output(of_desc->spi_sdi_gpio, 1);elsegpiod_direction_output(of_desc->spi_sdi_gpio, 0);gpiod_direction_output(of_desc->spi_scl_gpio, 0);udelay(20);gpiod_direction_output(of_desc->spi_scl_gpio, 1); // 时钟上升沿读取数据g <<= 1;udelay(20);}for(i = 0; i < 16; i++){if(b&0x8000)gpiod_direction_output(of_desc->spi_sdi_gpio, 1);elsegpiod_direction_output(of_desc->spi_sdi_gpio, 0);gpiod_direction_output(of_desc->spi_scl_gpio, 0);udelay(20);gpiod_direction_output(of_desc->spi_scl_gpio, 1); // 时钟上升沿读取数据b <<= 1;udelay(20);}for(i = 0; i < 8; i++) //时钟保持高电平,连发8个脉冲启动自动锁存机制{gpiod_direction_output(of_desc->spi_sdi_gpio, 0);udelay(10);gpiod_direction_output(of_desc->spi_sdi_gpio, 1);udelay(10); }gpiod_direction_output(of_desc->spi_cs_gpio, 0);gpiod_direction_output(of_desc->spi_scl_gpio, 0);gpiod_direction_output(of_desc->spi_sdi_gpio, 0);printk("%s\n", __func__);}static ssize_t dm412Data_show(struct device *dev, struct device_attribute *attr, char *buf)
{return sprintf(buf, "please input 49bit data:(r<<32|g<<16|b).\n");
}static ssize_t dm412Data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{unsigned long long dm412Data;unsigned int r,g,b;int rc = kstrtou64(buf, 0, &dm412Data); // 字符串转整形if (rc)return rc;r = (dm412Data>>32)&0xffff;g = (dm412Data>>16)&0xffff;b = dm412Data&0xffff;//printk("\n spi0:\n r = %d g = %d b = %d\n ",r,g,b);dm412_spi0_send_cmd(g_of_desc, r, g, b);return count;
}
static DEVICE_ATTR(dm412Data, 0664, dm412Data_show, dm412Data_store);static int dm412_probe(struct platform_device *pdev)
{int ret;int err;struct dm412_desc *of_desc;printk("%s\n", __func__);of_desc = devm_kzalloc(&pdev->dev, sizeof(struct dm412_desc), GFP_KERNEL);if (!of_desc)return -ENOMEM;g_of_desc = of_desc;of_desc->spi_scl_gpio = devm_gpiod_get_optional(&pdev->dev, "spi-scl", 0);if (IS_ERR(of_desc->spi_scl_gpio)) {err = PTR_ERR(of_desc->spi_scl_gpio);dev_err(&pdev->dev, "failed to request spi_scl: %d\n", err);return err;}of_desc->spi_cs_gpio = devm_gpiod_get_optional(&pdev->dev, "spi-cs", 0);if (IS_ERR(of_desc->spi_cs_gpio)) {err = PTR_ERR(of_desc->spi_cs_gpio);dev_err(&pdev->dev, "failed to request spi_cs: %d\n", err);return err;}of_desc->spi_sdi_gpio = devm_gpiod_get_optional(&pdev->dev, "spi-sdi", 0);if (IS_ERR(of_desc->spi_sdi_gpio)) {err = PTR_ERR(of_desc->spi_sdi_gpio);dev_err(&pdev->dev, "failed to request spi_sdi: %d\n", err);return err;}gpiod_direction_output(of_desc->spi_cs_gpio, 0);gpiod_direction_output(of_desc->spi_sdi_gpio, 0);gpiod_direction_output(of_desc->spi_scl_gpio, 0);ret = device_create_file(&pdev->dev, &dev_attr_dm412Data);if (ret)printk("dm412Data device_create_file_error\n");elseprintk("dm412Data device_create_file_success\n");dm412_spi0_send_cmd(of_desc,168,2556,4690);return ret;
}static int dm412_remove(struct platform_device *pdev)
{printk("%s\n", __func__);return 0;
}static const struct of_device_id dm412_of_match[] = {{ .compatible = "siti,dm412", },{ /* Sentinel */ },
};
MODULE_DEVICE_TABLE(of, dm412_of_match);static struct platform_driver dm412_driver = {.driver = {.name = "dm412",.owner = THIS_MODULE,.of_match_table = of_match_ptr(dm412_of_match),},.probe = dm412_probe,.remove = dm412_remove,
};static int __init dm412_init(void)
{return platform_driver_register(&dm412_driver);
}
module_init(dm412_init);static void __exit dm412_exit(void)
{return platform_driver_unregister(&dm412_driver);
}
module_exit(dm412_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Wu Tao <wt.relax@foxmail.com>");
MODULE_DESCRIPTION("dm412 led Driver");
创建的文件接口提供给APK使用
2、dts配置
dm412 {compatible = "siti,dm412";pinctrl-naems = "default";pinctrl-0 = <&spi0_init_cmd>;spi-scl-gpios = <&gpio5 12 GPIO_ACTIVE_HIGH>;spi-cs-gpios = <&gpio5 13 GPIO_ACTIVE_HIGH>;spi-sdi-gpios = <&gpio5 14 GPIO_ACTIVE_HIGH>;status = "okay";
};
Linux系统SPI驱动总结相关推荐
- Linux系统SPI驱动总结(一)
SPI是"Serial Peripheral Interface"的缩写,串行外设接口,是一种四线制的同步串行通信接口,用来连接MCU.传感器.存储设备,SPI设备分为主设备和从设 ...
- 转载:Linux kernel SPI驱动解释
From: http://www.cnblogs.com/liugf05/archive/2012/12/03/2800457.html 下面有两个大的模块: 一个是SPI总线驱动的分析 ...
- linux中spi驱动框架
原 linux中spi驱动框架 2016年09月14日 15:57:06 andylauren 阅读数:403 <span class="tags-box artic-tag-box& ...
- linux系统网络驱动简介
网络设备驱动简介 网络设备驱动是linux内核中三大类设备驱动之一,它用来完成高层网络协议的底层数据传输及设备控制. 网络设备与其他两种设备的区别: 网络接口不存在于linux的文件系统中,及/dev ...
- linux系统LCD驱动(三):mtk lcd驱动lcm的加载以及初始化
上一篇博文(linux系统LCD驱动(二):mtk lcd驱动fb_info初始化)https://blog.csdn.net/Ian22l/article/details/105929192 提到m ...
- Linux下spi驱动分析与测试【详细流程】
驱动是基于ARM的pl022的SSP控制器,其支持三种通信格式:SPI.SSI以及Microwrite,llinux5.4内核下,SSP控制器驱动的路径为/drivers/spi/spi-pl022. ...
- Linux下SPI驱动详解
更多嵌入式原创文章,请关注公众号:一口Linux 1. SPI总线 1.1. SPI总线概述 SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口. ...
- Linux下SPI驱动详解(干货)
关注.星标公众号,直达精彩内容 本文由嵌入式大牛:蒙工投稿! 1. SPI总线 1.1. SPI总线概述 SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外 ...
- linux系统无线驱动在哪下载,在linux上怎么安装无线网卡驱动?
在linux上怎么安装无线网卡驱动? 在linux上安装无线网卡驱动的方法: (1)先确定无线网卡型号,因驱动安装和型号是密切相关的,不同的型号,安装和下载驱动有所不同,但原理是一样的.图例为无线网卡 ...
最新文章
- MongoDB分析工具之三:db.currentOp()
- MySQL 之 explain
- visual studio2017调用SDK各个操作步骤的作用
- 阿里云ACE认证之理解CDN技术
- Android中的EditText默认时不弹出软键盘的方法
- sql关联查询子表的第一条_SQLAlchemy(8)惰性查询
- Harmony OS — ListContainer列表
- 中画图title函数_Matlab对量子力学中的一维无限深势阱的模拟计算
- 基于matlab的中值滤波算法浅析
- 16QAM-调制解调MATLAB
- 三面网易,四面阿里,五年开发经验程序员剑指大厂,稳拿offer
- 图基(Tukey)检验
- js 禁止鼠标菜单键及键盘快捷键
- 提高github下载速度的方法
- Python 一百多行实现抢票助手
- 标志Logo设计的起源和艺术特点
- 2009谷歌全球热门搜索关键词排行
- 如何在网上轻松赚钱,三个非常靠谱的副业项目,一定要收藏起来看
- 利用python开发的flappy bird 游戏
- STM32L0系列之【串口收发】