平台: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驱动总结相关推荐

  1. Linux系统SPI驱动总结(一)

    SPI是"Serial Peripheral Interface"的缩写,串行外设接口,是一种四线制的同步串行通信接口,用来连接MCU.传感器.存储设备,SPI设备分为主设备和从设 ...

  2. 转载:Linux kernel SPI驱动解释

    From: http://www.cnblogs.com/liugf05/archive/2012/12/03/2800457.html 下面有两个大的模块: 一个是SPI总线驱动的分析        ...

  3. linux中spi驱动框架

    原 linux中spi驱动框架 2016年09月14日 15:57:06 andylauren 阅读数:403 <span class="tags-box artic-tag-box& ...

  4. linux系统网络驱动简介

    网络设备驱动简介 网络设备驱动是linux内核中三大类设备驱动之一,它用来完成高层网络协议的底层数据传输及设备控制. 网络设备与其他两种设备的区别: 网络接口不存在于linux的文件系统中,及/dev ...

  5. linux系统LCD驱动(三):mtk lcd驱动lcm的加载以及初始化

    上一篇博文(linux系统LCD驱动(二):mtk lcd驱动fb_info初始化)https://blog.csdn.net/Ian22l/article/details/105929192 提到m ...

  6. Linux下spi驱动分析与测试【详细流程】

    驱动是基于ARM的pl022的SSP控制器,其支持三种通信格式:SPI.SSI以及Microwrite,llinux5.4内核下,SSP控制器驱动的路径为/drivers/spi/spi-pl022. ...

  7. Linux下SPI驱动详解

    更多嵌入式原创文章,请关注公众号:一口Linux 1. SPI总线 1.1. SPI总线概述 SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口. ...

  8. Linux下SPI驱动详解(干货)

    关注.星标公众号,直达精彩内容 本文由嵌入式大牛:蒙工投稿! 1. SPI总线 1.1. SPI总线概述 SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外 ...

  9. linux系统无线驱动在哪下载,在linux上怎么安装无线网卡驱动?

    在linux上怎么安装无线网卡驱动? 在linux上安装无线网卡驱动的方法: (1)先确定无线网卡型号,因驱动安装和型号是密切相关的,不同的型号,安装和下载驱动有所不同,但原理是一样的.图例为无线网卡 ...

最新文章

  1. MongoDB分析工具之三:db.currentOp()
  2. MySQL 之 explain
  3. visual studio2017调用SDK各个操作步骤的作用
  4. 阿里云ACE认证之理解CDN技术
  5. Android中的EditText默认时不弹出软键盘的方法
  6. sql关联查询子表的第一条_SQLAlchemy(8)惰性查询
  7. Harmony OS — ListContainer列表
  8. 中画图title函数_Matlab对量子力学中的一维无限深势阱的模拟计算
  9. 基于matlab的中值滤波算法浅析
  10. 16QAM-调制解调MATLAB
  11. 三面网易,四面阿里,五年开发经验程序员剑指大厂,稳拿offer
  12. 图基(Tukey)检验
  13. js 禁止鼠标菜单键及键盘快捷键
  14. 提高github下载速度的方法
  15. Python 一百多行实现抢票助手
  16. 标志Logo设计的起源和艺术特点
  17. 2009谷歌全球热门搜索关键词排行
  18. 如何在网上轻松赚钱,三个非常靠谱的副业项目,一定要收藏起来看
  19. 利用python开发的flappy bird 游戏
  20. STM32L0系列之【串口收发】

热门文章

  1. awk(1) awk中的函数
  2. 存储过程中CreateParameter的参数说明
  3. PHP相册模块,支持Flash及JS模板插件安装。
  4. nginx请求超时设置
  5. JOST数据 日期转换
  6. 【JavaScript权威指南(第七版)】之阅读学习总结
  7. Yong Huang
  8. CollectionUtils取交集、并集和差集
  9. 重度抑郁症患者的脑功能老化加速:来自中国大规模fMRI证据
  10. Tiny pxe网络启动--远程安装、维护系统