主机控制器驱动:SOC的spi外设驱动,是半导体厂家编写号的,为spi-imx.c当spi控制器的设备和驱动匹配之后,spi_imx_probe函数就会执行。
SPI控制驱动核心就是spi_master的构建,spi_master
spi_master->transfer
spi_master-> transfer_one_message 6ULL主机控制器使用此函数,
设备驱动:具体的SPI芯片驱动
SPI_Driver 非常重要,重点是申请或者定义一个spi_driver 然后初始化spi_driver的各个成员变量,
SPI四个引脚的 pinctrl的电气属性分配:SCLK、NS、MOSI、MISO


@后面的0表示接到哪一个硬件片选信号!
参考文档是document下的devicetree下的设计文档。

1、修改设备树,添加IO相关信息
ECSPI3_SCLK -> UART2_RXD
ECSPI3_MOSI -> UART2_CTS
ECSPI3_SS0 -> UART2_TXD
ECSPI3_MISO -> UART2_RTS
片选信号不作为硬件片选,而是作为普通的GPIO,我们在程序里面自行控制片选引脚。
2、在ECSPI3节点下创建icm20608子节点
3、需要初始化icm20608芯片、然后从里面读取原始数据!这个过程就要用到如何使用linux内的SPI驱动API来读写ICM20608
用到两个重要的结构体:spi_transfer和spi_message
spi_transfer用来构建收发数据内容。
构建spi_transfer,然后将其打包到spi_message里面,需要使用spi_message_init初始化spi_message,然后在使用spi_message_add_tail将spi_transfer添加到spi_message里面,最终使用spi_sync和spi_async来发送。
获取设备树的父节点:


#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "icm20608.h"
#define ICM20608_CNT 1
#define ICM20608_NAME "icm20608"
/*设备结构体*/
struct icm20608_dev {
struct cdev cdev;
struct class *class;/*类:为了自动创建节点*/
struct device *device;/*设备:为了自动创建节点*/
dev_t devid; //设备号
int major; //主设备号
int minor; //次设备号
void *private_data;
int cs_gpio;
struct device_node *nd;
};
struct icm20608_dev icm20608;
static int icm20608_open(struct inode *inode, struct file *filp)
{
/*设置私有数据*/
//filp->private_data = &icm20608;
return 0;
}
static ssize_t icm20608_read(struct file *filp, __user char *buf, size_t count,loff_t *ppos)
{
return 0;
}
static int icm20608_release(struct inode *inode, struct file *filp)
{
//struct icm20608_dev *dev = (struct icm20608_dev *)filp->private_data;
return 0;
}
/**
* 字符设备的操作集合
*/
const struct file_operations icm20608_fops = {
.owner = THIS_MODULE,
.open = icm20608_open,
.read = icm20608_read,
.release = icm20608_release,
};
/*spi读寄存器*/
static int icm20608_read_regs(struct icm20608_dev *dev,u8 reg,void *buf,int len)
{
int ret = 0;
unsigned char txdata[len];
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = (struct spi_device*)dev->private_data;
//片选拉低
gpio_set_value(dev->cs_gpio,0);
//构建spitransfer
t= kzalloc(sizeof(struct spi_transfer),GFP_KERNEL);
/*第一步:发送要读取的寄存器的地址*/
txdata[0] = reg|0x80;//读取寄存器地址的话,寄存器地址最高位要置1
t->tx_buf = txdata;
t->len = 1;
spi_message_init(&m);
spi_message_add_tail(t,&m);
ret = spi_sync(spi,&m);
//第二步 读取数据
txdata[0] = 0xff; //无效的
t->rx_buf = buf;
t->len = len;
spi_message_init(&m);
spi_message_add_tail(t,&m);
ret = spi_sync(spi,&m);
//释放内存
kfree(t);
gpio_set_value(dev->cs_gpio,1);
return ret;
}
/*spi写寄存器*/
static int icm20608_write_regs(struct icm20608_dev *dev,u8 reg,u8 *buf,int len)
{
int ret = 0;
unsigned char txdata[len];
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = (struct spi_device*)dev->private_data;
//片选拉低
gpio_set_value(dev->cs_gpio,0);
//构建spitransfer
t = kzalloc(sizeof(struct spi_transfer),GFP_KERNEL);
/*第一步:发送要读取的寄存器的地址*/
txdata[0] = reg&~0x80;//读取寄存器地址的话,寄存器地址最高位要置1
t->tx_buf = txdata;
t->len = 1;
spi_message_init(&m);
spi_message_add_tail(t,&m);
ret = spi_sync(spi,&m);
//第二步 读取数据
t->rx_buf = buf;
t->len = len;
spi_message_init(&m);
spi_message_add_tail(t,&m);
ret = spi_sync(spi,&m);
//释放内存
kfree(t);
gpio_set_value(dev->cs_gpio,1);
return ret;
}
/*
* @description : 读取 icm20608 指定寄存器值,读取一个寄存器
* @param – dev : icm20608 设备
* @param – reg : 要读取的寄存器
* @return : 读取到的寄存器值
*/
static unsigned char icm20608_read_onereg(struct icm20608_dev *dev,u8 reg)
{
u8 data = 0;
icm20608_read_regs(dev, reg, &data, 1);
return data;
}
/*
* @description : 向 icm20608 指定寄存器写入指定的值,写一个寄存器
* @param – dev : icm20608 设备
* @param – reg : 要写的寄存器
* @param – data : 要写入的值
* @return : 无
*/
static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg,u8 value)
{
u8 buf = value;
icm20608_write_regs(dev, reg, &buf, 1);
}
void icm20608reg_init(void)
{
u8 value = 0;
icm20608_write_onereg(&icm20608, ICM20_PWR_MGMT_1, 0x80);
mdelay(50);
icm20608_write_onereg(&icm20608, ICM20_PWR_MGMT_1, 0x01);
mdelay(50);
value = icm20608_read_onereg(&icm20608, ICM20_WHO_AM_I);
printk("ICM20608 ID = %#X\r\n", value);
}
static int icm20608_probe(struct spi_device *spi)
{
int ret = 0;
/*搭建字符设备驱动框架*/
/*2、注册字符设备*/
printk("icm20608_init ok\r\n");
icm20608.major = 0;//设置为0,表示由系统申请设备号
if(icm20608.major) //给定主设备号
{
icm20608.devid = MKDEV(icm20608.major,0);
ret = register_chrdev_region(icm20608.devid,ICM20608_CNT,ICM20608_NAME);
}else{
ret = alloc_chrdev_region(&icm20608.devid,0,ICM20608_CNT,ICM20608_NAME);
icm20608.major = MAJOR(icm20608.devid);
icm20608.minor = MINOR(icm20608.devid);
}
if(ret < 0){
printk("icm20608 chrdev_region err\r\n");
return -1;
}
printk("icm20608 majorid = %d,minorid = %d\r\n",icm20608.major,icm20608.minor);
/*3、注册字符设备*/
icm20608.cdev.owner = THIS_MODULE;
cdev_init(&icm20608.cdev, &icm20608_fops);
cdev_add(&icm20608.cdev,icm20608.devid,ICM20608_CNT);
/* 4、自动创建设备节点 */
icm20608.class = class_create(THIS_MODULE, ICM20608_NAME);
if (IS_ERR(icm20608.class)) {
return PTR_ERR(icm20608.class);
}
/* 5、创建设备 */
icm20608.device = device_create(icm20608.class, NULL, icm20608.devid, NULL, ICM20608_NAME);
if (IS_ERR(icm20608.device)) {
return PTR_ERR(icm20608.device);
}
//获取软件片选引脚
icm20608.nd = of_get_parent(spi->dev.of_node);
icm20608.cs_gpio = of_get_named_gpio(icm20608.nd,"cs-gpio",0);
if (icm20608.cs_gpio<0)
{
printk("can't get gpio\r\n");
return -1;
}
ret = gpio_request(icm20608.cs_gpio,"cs");
if (ret <0)
{
printk("gpio_request failed\n");
return -1;
}
ret = gpio_direction_output(icm20608.cs_gpio,1);//默认高电平
/*初始化spi_device*/
spi->mode = SPI_MODE_0;
spi_setup(spi);
icm20608.private_data = spi; //设置私有数据为spi
/*初始化icm20608*/
icm20608reg_init();
return 0;
}
static int icm20608_remove(struct spi_device *spi)
{
/*删除字符设备*/
cdev_del(&icm20608.cdev);
/*注销设备号*/
unregister_chrdev_region(icm20608.devid,ICM20608_CNT);
/*先摧毁设备再摧毁类*/
device_destroy(icm20608.class,icm20608.devid);
/*摧毁类*/
class_destroy(icm20608.class);
/*释放片选*/
gpio_free(icm20608.cs_gpio);
return 0;
}
/*传统的匹配表*/
static struct spi_device_id icm20608_id[] ={
{"alientek,icm20608",0},
{}
};
/*设备树匹配表*/
static struct of_device_id icm20608_of_match[] ={
{.compatible = "alientek,icm20608"},
{}
};
/*spi driver*/
static struct spi_driver icm20608_driver = {
.probe = icm20608_probe,
.remove = icm20608_remove,
.driver = {
.name = "icm20608",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(icm20608_of_match),
},
.id_table = icm20608_id,//传统的设备匹配!!!
};
static int __init icm20608_init(void)
{
int ret;
ret = spi_register_driver(&icm20608_driver);
return ret;
}
static void __exit icm20608_exit(void)
{
spi_unregister_driver(&icm20608_driver);
}
//模块加载函数
module_init(icm20608_init);
//模块卸载
module_exit(icm20608_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("qhy");