I2C设备驱动之设备树和i2c_driver接口
一 原理图
三轴加速度传感器原理图:
图中:
I2C_SCL_GSENSOR对应imx6的 UART1_TX_DATA
I2C_SDA_GSENSOR对应imx6的UART1_RX_DATA
G_INT2对应imx6的SNVS_TAMPER5
二 设备树配置
将 UART1_TX_DATA,UART1_RX_DATA配置为I2C3模式,将SNVS_TAMPER5配置为GPIO模式。
&i2c3 {
clock_frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c3>;
status = "okay";
gsensor: bma250@18 {
compatible = "bosch,bma250";
//从设备地址
reg = <0x18>;
position = <0>;
//表示处于哪一组中断,第5组
interrupt-parent = <&gpio5>;
//表示处于GPIO5的第5个io,高电平触发
interrupts = <5 IRQ_TYPE_LEVEL_HIGH>;
};
};
pinctrl_i2c3: i2c3grp {
fsl,pins = <
MX6UL_PAD_UART1_TX_DATA__I2C3_SCL 0x4001b8b0
MX6UL_PAD_UART1_RX_DATA__I2C3_SDA 0x4001b8b0
MX6UL_PAD_SNVS_TAMPER5__GPIO5_IO05 0x80000000 // gsenser int1
>;
};
三 i2c_driver接口
/*
* bma250-i2c.c - Linux kernel modules for 3-Axis Smart Orientation
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include "bma250.h"
/***注:i2c_client一般被包含在设备的私有信息结构体yyy_data中,在本例中,被包含在bma250_data结构中。
*** 而i2c_driver则适合被定义为全局变量并初始化
***/
static s32 bma250_i2c_write(struct bma250_data *pdata, u8 reg, u8 val)
{
struct i2c_client *client = (struct i2c_client *)pdata->bus_priv;
//向I2C设备发送一个写控制字+控制指令(寄存器地址),紧接着发送指令内容(寄存器内容,单字节)。
return i2c_smbus_write_byte_data(client, reg, val);
}
static int bma250_i2c_read(struct bma250_data *pdata, u8 reg)
{
struct i2c_client *client = (struct i2c_client *)pdata->bus_priv;
//向I2C设备发送一个写控制字+控制指令(寄存器地址),再发送一个读控制字,
//此时I2C从设备内部的读写指针转移到指定的位置,并返回一个字节,最后返回一个无应答NA。
return i2c_smbus_read_byte_data(client, reg);
}
static int bma250_i2c_read_block(struct bma250_data *pdata, u8 reg, u8 len, u8 *val)
{
struct i2c_client *client = (struct i2c_client *)pdata->bus_priv;
//向I2C设备发送一个写控制字+控制指令(寄存器地址),再发送一个读控制字,
//此时I2C从设备内部的读写指针转移到指定的位置,并连续返回多个字节,
//I2C主机读取到一定数量字节内容之后发送无应答NA。
//通过I2C总线从reg寄存器开始连续读len个数据到val中。
return i2c_smbus_read_i2c_block_data(client, reg, len, val);
}
static int bma250_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret;
//client数据结构由系统根据设备树信息自动填充
bma250_dev.bus_priv = client;
bma250_dev.bus_type = BUS_I2C;
bma250_dev.irq = client->irq;
bma250_dev.read = bma250_i2c_read;
bma250_dev.write = bma250_i2c_write;
bma250_dev.read_block = bma250_i2c_read_block;
//将设备驱动的私有数据(bma250_dev)赋值给设备驱动client的私有指针中,方便通过client取得设备私有数据
//对于与 i2c_get_clientdata(client); 从i2c_client获取设备私有数据
i2c_set_clientdata(client, &bma250_dev);
//初始化bma250,注册misc和input设备驱动(具体代码在下节)
ret = bma250_driver_init(&bma250_dev);
return ret;
}
//注销设备驱动
static int bma250_i2c_remove(struct i2c_client *client)
{
return bma250_driver_remove(&bma250_dev);
}
/*********
**电源管理部分
***********/
#ifdef CONFIG_PM
static int bma250_i2c_suspend(struct device *dev)
{
//#define to_i2c_client(d) container_of(d, struct i2c_client, dev)
//通常使用device设备模型进行操作,可以通过to_i2c_client找到对应client指针
struct i2c_client *client = to_i2c_client(dev); // 从dev 获取 i2c client
return bma250_driver_suspend(i2c_get_clientdata(client));
}
static int bma250_i2c_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
return bma250_driver_resume(i2c_get_clientdata(client));
}
#else
#define bma250_i2c_suspend NULL
#define bma250_i2c_resume NULL
#endif
static SIMPLE_DEV_PM_OPS(bma250_pm_ops, bma250_i2c_suspend, bma250_i2c_resume);
/*********
**设备与驱动匹配部分
***********/
//方法一:设备与驱动匹配
static const struct i2c_device_id bma250_i2c_id[] = {
{"bma250", 0},
{ }
};
//MODULE_DEVICE_TABLE(platform, xx_driver_ids);
//将xx_driver_ids结构输出到用户空间,这样模块加载系统在加载模块时,就知道了什么模块对应什么硬件设备。
MODULE_DEVICE_TABLE(i2c, bma250_i2c_id);
//方法二:设备与驱动匹配
/*
static const struct of_device_id bma250_i2c_of_match[] = {
{.compatible = "bosch,bma250",},
{}
};
MODULE_DEVICE_TABLE(of, bma250_i2c_of_match);
*/
/*********
**驱动注册部分
***********/
/*适合被定义为全局变量并初始化*/
static struct i2c_driver bma250_i2c_driver = {
.driver = {
.name = "bma250",
.owner = THIS_MODULE,
//用于电源管理
.pm = &bma250_pm_ops,
//of_match_table = bma250_i2c_of_match,
},
.probe = bma250_i2c_probe,
.remove = bma250_i2c_remove,
.id_table = bma250_i2c_id,
};
module_i2c_driver(bma250_i2c_driver);
MODULE_AUTHOR("chao zhang");
MODULE_DESCRIPTION("BMA250 3-Axis Acc Sensor driver");
MODULE_LICENSE("GPL");