平台:rk3288
系统:Android7.1 kernel4.4.143
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 = ;
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_node
compatible = of_get_property(node, "compatible", &cplen);
/* 注册新的设备 */
spi_add_device
spi_setup(spi);
device_add /* 会绑定到一个spi_driver */
drivers/spi/spi.c 文件里的#if defined(CONFIG_OF) 这个判断成立的函数都是为设备树接口函数去服务的
4、SPI收发
include/linux/spi/spi.h:
spi_write
spi_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_read
spi_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
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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");
else
printk("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 ");
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是低电平有效。
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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);
else
gpiod_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);
else
gpiod_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);
else
gpiod_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");
else
printk("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 ");
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";
};