一、Regmap概述
Regmap
机制主要目的是减少慢速 I/O
驱动上的重复逻辑,提供一种通用的接口来操作底层硬件上的寄存器。Regmap
除了能做到统一的 I/O
接口,还可以在驱动和硬件 IC
之间做一层缓存,从而能减少底层 I/O
的操作次数。
未使用 Regmap
机制的驱动框图如下:
使用 Regmap
机制的驱动框图如下:
说明:regmap
机制是对 SPI
子系统、I2C
子系统等进行封装,为上层应用提供统一接口。
二、Regmap 驱动框架
regmap
分为 3
层:
1、物理总线层:regmap
对不同的物理总线进行封装。
2、regmap
核心层:实现 regmap
机制。
3、regmap API
抽象层:向驱动提供 API
接口。
三、regmap 中重要对象
1、regmap 对象
struct regmap {
union {
struct mutex mutex;
struct {
spinlock_t spinlock;
unsigned long spinlock_flags;
};
};
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg; /* This is passed to lock/unlock functions */
struct device *dev; /* Device we do I/O on */
void *work_buf; /* Scratch buffer used to format I/O */
struct regmap_format format; /* Buffer format */
const struct regmap_bus *bus;
void *bus_context;
const char *name;
bool async;
spinlock_t async_lock;
wait_queue_head_t async_waitq;
struct list_head async_list;
struct list_head async_free;
int async_ret;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
const char *debugfs_name;
unsigned int debugfs_reg_len;
unsigned int debugfs_val_len;
unsigned int debugfs_tot_len;
struct list_head debugfs_off_cache;
struct mutex cache_lock;
#endif
unsigned int max_register;
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
bool (*precious_reg)(struct device *dev, unsigned int reg);
const struct regmap_access_table *wr_table;
const struct regmap_access_table *rd_table;
const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table;
int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
int (*reg_write)(void *context, unsigned int reg, unsigned int val);
bool defer_caching;
u8 read_flag_mask;
u8 write_flag_mask;
/* number of bits to (left) shift the reg value when formatting*/
int reg_shift;
int reg_stride;
/* regcache specific members */
const struct regcache_ops *cache_ops;
enum regcache_type cache_type;
/* number of bytes in reg_defaults_raw */
unsigned int cache_size_raw;
/* number of bytes per word in reg_defaults_raw */
unsigned int cache_word_size;
/* number of entries in reg_defaults */
unsigned int num_reg_defaults;
/* number of entries in reg_defaults_raw */
unsigned int num_reg_defaults_raw;
/* if set, only the cache is modified not the HW */
u32 cache_only;
/* if set, only the HW is modified not the cache */
u32 cache_bypass;
/* if set, remember to free reg_defaults_raw */
bool cache_free;
struct reg_default *reg_defaults;
const void *reg_defaults_raw;
void *cache;
/* if set, the cache contains newer data than the HW */
u32 cache_dirty;
/* if set, the HW registers are known to match map->reg_defaults */
bool no_sync_defaults;
struct reg_default *patch;
int patch_regs;
/* if set, converts bulk rw to single rw */
bool use_single_rw;
/* if set, the device supports multi write mode */
bool can_multi_write;
struct rb_root range_tree;
void *selector_work_buf; /* Scratch buffer used for selector */
};
向驱动层抽象 regmap
,为驱动层提供一个 regmap
对象。
2、设备的寄存器配置信息
/**
* Configuration for the register map of a device.
*
* @name: Optional name of the regmap. Useful when a device has multiple
* register regions.
*
* @reg_bits: Number of bits in a register address, mandatory.
* @reg_stride: The register address stride. Valid register addresses are a
* multiple of this value. If set to 0, a value of 1 will be
* used.
* @pad_bits: Number of bits of padding between register and value.
* @val_bits: Number of bits in a register value, mandatory.
*
* @writeable_reg: Optional callback returning true if the register
* can be written to. If this field is NULL but wr_table
* (see below) is not, the check is performed on such table
* (a register is writeable if it belongs to one of the ranges
* specified by wr_table).
* @readable_reg: Optional callback returning true if the register
* can be read from. If this field is NULL but rd_table
* (see below) is not, the check is performed on such table
* (a register is readable if it belongs to one of the ranges
* specified by rd_table).
* @volatile_reg: Optional callback returning true if the register
* value can't be cached. If this field is NULL but
* volatile_table (see below) is not, the check is performed on
* such table (a register is volatile if it belongs to one of
* the ranges specified by volatile_table).
* @precious_reg: Optional callback returning true if the register
* should not be read outside of a call from the driver
* (e.g., a clear on read interrupt status register). If this
* field is NULL but precious_table (see below) is not, the
* check is performed on such table (a register is precious if
* it belongs to one of the ranges specified by precious_table).
* @lock: Optional lock callback (overrides regmap's default lock
* function, based on spinlock or mutex).
* @unlock: As above for unlocking.
* @lock_arg: this field is passed as the only argument of lock/unlock
* functions (ignored in case regular lock/unlock functions
* are not overridden).
* @reg_read: Optional callback that if filled will be used to perform
* all the reads from the registers. Should only be provided for
* devices whose read operation cannot be represented as a simple
* read operation on a bus such as SPI, I2C, etc. Most of the
* devices do not need this.
* @reg_write: Same as above for writing.
* @fast_io: Register IO is fast. Use a spinlock instead of a mutex
* to perform locking. This field is ignored if custom lock/unlock
* functions are used (see fields lock/unlock of struct regmap_config).
* This field is a duplicate of a similar file in
* 'struct regmap_bus' and serves exact same purpose.
* Use it only for "no-bus" cases.
* @max_register: Optional, specifies the maximum valid register index.
* @wr_table: Optional, points to a struct regmap_access_table specifying
* valid ranges for write access.
* @rd_table: As above, for read access.
* @volatile_table: As above, for volatile registers.
* @precious_table: As above, for precious registers.
* @reg_defaults: Power on reset values for registers (for use with
* register cache support).
* @num_reg_defaults: Number of elements in reg_defaults.
*
* @read_flag_mask: Mask to be set in the top byte of the register when doing
* a read.
* @write_flag_mask: Mask to be set in the top byte of the register when doing
* a write. If both read_flag_mask and write_flag_mask are
* empty the regmap_bus default masks are used.
* @use_single_rw: If set, converts the bulk read and write operations into
* a series of single read and write operations. This is useful
* for device that does not support bulk read and write.
* @can_multi_write: If set, the device supports the multi write mode of bulk
* write operations, if clear multi write requests will be
* split into individual write operations
*
* @cache_type: The actual cache type.
* @reg_defaults_raw: Power on reset values for registers (for use with
* register cache support).
* @num_reg_defaults_raw: Number of elements in reg_defaults_raw.
* @reg_format_endian: Endianness for formatted register addresses. If this is
* DEFAULT, the @reg_format_endian_default value from the
* regmap bus is used.
* @val_format_endian: Endianness for formatted register values. If this is
* DEFAULT, the @reg_format_endian_default value from the
* regmap bus is used.
*
* @ranges: Array of configuration entries for virtual address ranges.
* @num_ranges: Number of range configuration entries.
*/
struct regmap_config {
const char *name; // 名字
int reg_bits; // 寄存器地址位数
int reg_stride; // 寄存器地址步长
int pad_bits; // 寄存器和值之间的填充位数
int val_bits; // 寄存器值位数
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
bool (*precious_reg)(struct device *dev, unsigned int reg);
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg;
int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
int (*reg_write)(void *context, unsigned int reg, unsigned int val);
bool fast_io;
unsigned int max_register;
const struct regmap_access_table *wr_table;
const struct regmap_access_table *rd_table;
const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table;
const struct reg_default *reg_defaults;
unsigned int num_reg_defaults;
enum regcache_type cache_type;
const void *reg_defaults_raw;
unsigned int num_reg_defaults_raw;
u8 read_flag_mask; // 读标志掩码
u8 write_flag_mask; // 写标志掩码
bool use_single_rw;
bool can_multi_write;
enum regmap_endian reg_format_endian;
enum regmap_endian val_format_endian;
const struct regmap_range_cfg *ranges;
unsigned int num_ranges;
};
在 Regmap
初始化时,驱动就需要把这个结构体传给 Regmap
。这个结构体的定义在 include/linux/regmap.h
,其中包含该设备的寄存器数量,寄存器位宽,缓存类型,读写属性等。
这一层是直接和驱动对接的。Regmap
根据传进来的 regmap_config
初始化对应的缓存和总线操作接口,驱动就可以正常调用 regmap_write
和 regmap_read
函数。
3、regmap 缓存类型
struct regcache_ops {
const char *name;
enum regcache_type type;
int (*init)(struct regmap *map);
int (*exit)(struct regmap *map);
#ifdef CONFIG_DEBUG_FS
void (*debugfs_init)(struct regmap *map);
#endif
int (*read)(struct regmap *map, unsigned int reg, unsigned int *value);
int (*write)(struct regmap *map, unsigned int reg, unsigned int value);
int (*sync)(struct regmap *map, unsigned int min, unsigned int max);
int (*drop)(struct regmap *map, unsigned int min, unsigned int max);
};
在最新 Linux 4.0
版本中,已经有 3
种缓存类型,分别是数组(flat
)、LZO
压缩和红黑树(rbtree
)。
缓存的类型是在 Regmap
初始化时,由 .cache_type = REGCACHE_RBTREE
来指定的。对于 regmap_read
来说,会先判断当前缓存是否有值,然后再检查是否需要 bypass
,若没有,则可以直接从缓存里面取值,调用 regcache_read
来获取值,若需要从硬件上读取,则调用具体协议的读写函数。
4、regmap 总线
/**
* Description of a hardware bus for the register map infrastructure.
*
* @fast_io: Register IO is fast. Use a spinlock instead of a mutex
* to perform locking. This field is ignored if custom lock/unlock
* functions are used (see fields lock/unlock of
* struct regmap_config).
* @write: Write operation.
* @gather_write: Write operation with split register/value, return -ENOTSUPP
* if not implemented on a given device.
* @async_write: Write operation which completes asynchronously, optional and
* must serialise with respect to non-async I/O.
* @read: Read operation. Data is returned in the buffer used to transmit
* data.
* @async_alloc: Allocate a regmap_async() structure.
* @read_flag_mask: Mask to be set in the top byte of the register when doing
* a read.
* @reg_format_endian_default: Default endianness for formatted register
* addresses. Used when the regmap_config specifies DEFAULT. If this is
* DEFAULT, BIG is assumed.
* @val_format_endian_default: Default endianness for formatted register
* values. Used when the regmap_config specifies DEFAULT. If this is
* DEFAULT, BIG is assumed.
* @async_size: Size of struct used for async work.
*/
struct regmap_bus {
bool fast_io;
regmap_hw_write write;
regmap_hw_gather_write gather_write;
regmap_hw_async_write async_write;
regmap_hw_reg_write reg_write;
regmap_hw_read read;
regmap_hw_reg_read reg_read;
regmap_hw_free_context free_context;
regmap_hw_async_alloc async_alloc;
u8 read_flag_mask;
enum regmap_endian reg_format_endian_default;
enum regmap_endian val_format_endian_default;
};
在 Lernel 4.0
中,已经支持了 I2C
、SPI
、AC97
、MMIO
和 SPMI
五种总线类型。
四、Regmap API
struct regmap *regmap_init(struct device *dev,
const struct regmap_bus *bus,
void *bus_context,
const struct regmap_config *config);
int regmap_attach_dev(struct device *dev, struct regmap *map,
const struct regmap_config *config);
struct regmap *regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config);
struct regmap *regmap_init_spi(struct spi_device *dev,
const struct regmap_config *config);
struct regmap *regmap_init_spmi_base(struct spmi_device *dev,
const struct regmap_config *config);
struct regmap *regmap_init_spmi_ext(struct spmi_device *dev,
const struct regmap_config *config);
struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs,
const struct regmap_config *config);
struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
const struct regmap_config *config);
struct regmap *devm_regmap_init(struct device *dev,
const struct regmap_bus *bus,
void *bus_context,
const struct regmap_config *config);
struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config);
struct regmap *devm_regmap_init_spi(struct spi_device *dev,
const struct regmap_config *config);
struct regmap *devm_regmap_init_spmi_base(struct spmi_device *dev,
const struct regmap_config *config);
struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *dev,
const struct regmap_config *config);
struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs,
const struct regmap_config *config);
struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
const struct regmap_config *config);
bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
五、修改ap3216c驱动程序(I2C子系统)
#include "linux/init.h"
#include "linux/module.h"
#include "linux/printk.h"
#include "linux/i2c.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"
#include "linux/delay.h"
#include "asm-generic/uaccess.h"
#include "linux/regmap.h"
#define AP3216C_ADDR 0X1E /* AP3216C器件地址 */
/* AP3316C寄存器 */
#define AP3216C_SYSTEMCONG 0x00 /* 配置寄存器 */
#define AP3216C_INTSTATUS 0X01 /* 中断状态寄存器 */
#define AP3216C_INTCLEAR 0X02 /* 中断清除寄存器 */
#define AP3216C_IRDATALOW 0x0A /* IR数据低字节 */
#define AP3216C_IRDATAHIGH 0x0B /* IR数据高字节 */
#define AP3216C_ALSDATALOW 0x0C /* ALS数据低字节 */
#define AP3216C_ALSDATAHIGH 0X0D /* ALS数据高字节 */
#define AP3216C_PSDATALOW 0X0E /* PS数据低字节 */
#define AP3216C_PSDATAHIGH 0X0F /* PS数据高字节 */
#define NEWCHRDEV_MINOR 255 /* 次设备号(让MISC自动分配) */
#define NEWCHRDEV_NAME "ap3216c" /* 名子 */
typedef struct{
struct i2c_client *client; /* i2C设备 */
unsigned short ir, als, ps; /* 传感器数据 */
struct regmap *regmap;
struct regmap_config regmap_config;
}newchrdev_t;
newchrdev_t newchrdev;
/*
* @description : 从ap3216c读取多个寄存器数据
* @param - dev: ap3216c设备
* @param - reg: 要读取的寄存器首地址
* @param - val: 读取到的数据
* @param - len: 要读取的数据长度
* @return : 操作结果
*/
static int ap3216c_read_regs(newchrdev_t *dev, unsigned char reg, void *val, int len)
{
#if 0
int ret;
struct i2c_msg msg[2];
struct i2c_client *client = dev->client;
/* msg[0]为发送要读取的首地址 */
msg[0].addr = client->addr; /* ap3216c地址 */
msg[0].flags = 0; /* 标记为发送数据 */
msg[0].buf = ® /* 读取的首地址 */
msg[0].len = 1; /* reg长度*/
/* msg[1]读取数据 */
msg[1].addr = client->addr; /* ap3216c地址 */
msg[1].flags = I2C_M_RD; /* 标记为读取数据*/
msg[1].buf = val; /* 读取数据缓冲区 */
msg[1].len = len; /* 要读取的数据长度*/
ret = i2c_transfer(client->adapter, msg, 2);
if(ret == 2) {
ret = 0;
} else {
printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
#endif
int ret = 0;
ret = regmap_read(dev->regmap, reg, (unsigned int*)val);
return ret;
}
/*
* @description : 向ap3216c多个寄存器写入数据
* @param - dev: ap3216c设备
* @param - reg: 要写入的寄存器首地址
* @param - val: 要写入的数据缓冲区
* @param - len: 要写入的数据长度
* @return : 操作结果
*/
static int ap3216c_write_regs(newchrdev_t *dev, unsigned char reg, unsigned char *buf, unsigned char len)
{
#if 0
unsigned char b[256];
struct i2c_msg msg;
struct i2c_client *client = dev->client;
b[0] = reg; /* 寄存器首地址 */
memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组b里面 */
msg.addr = client->addr; /* ap3216c地址 */
msg.flags = 0; /* 标记为写数据 */
msg.buf = b; /* 要写入的数据缓冲区 */
msg.len = len + 1; /* 要写入的数据长度 */
return i2c_transfer(client->adapter, &msg, 1);
#endif
regmap_write(dev->regmap, reg, (unsigned int)*buf);
return 0;
}
/*
* @description : 读取AP3216C的数据,读取原始数据,包括ALS,PS和IR, 注意!
* : 如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms
* @param - ir : ir数据
* @param - ps : ps数据
* @param - ps : als数据
* @return : 无。
*/
void ap3216c_read_data(newchrdev_t *dev)
{
unsigned char i =0;
unsigned char buf[6];
/* 循环读取所有传感器数据 */
for(i = 0; i < 6; i++)
{
ap3216c_read_regs(dev, AP3216C_IRDATALOW + i, buf+i, 1);
}
if(buf[0] & 0X80) /* IR_OF位为1,则数据无效 */
dev->ir = 0;
else /* 读取IR传感器的数据 */
dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);
dev->als = ((unsigned short)buf[3] << 8) | buf[2]; /* 读取ALS传感器的数据 */
if(buf[4] & 0x40) /* IR_OF位为1,则数据无效 */
dev->ps = 0;
else /* 读取PS传感器的数据 */
dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
}
static int lcs_ap3216c_init(newchrdev_t *dev)
{
unsigned char buf = 0;
/* 1、复位ap3216c */
buf = 0x04;
ap3216c_write_regs(dev, AP3216C_SYSTEMCONG, &buf, 1);
/* 2、至少延时10ms */
mdelay(50);
/* 3、开启ALS、PS+IR */
buf = 0x03;
ap3216c_write_regs(dev, AP3216C_SYSTEMCONG, &buf, 1);
return 0;
}
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int ap3216c_open(struct inode *inode, struct file *filp)
{
/* 1、设置私有数据 */
filp->private_data = &newchrdev;
/* 2、初始化ap3216c */
lcs_ap3216c_init(&newchrdev);
return 0;
}
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
short data[3];
long err = 0;
newchrdev_t *dev = (newchrdev_t*)filp->private_data;
ap3216c_read_data(dev);
data[0] = dev->ir;
data[1] = dev->als;
data[2] = dev->ps;
err = copy_to_user(buf, data, sizeof(data));
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int ap3216c_release(struct inode *inode, struct file *filp)
{
return 0;
}
static const struct file_operations ap3216c_ops = {
.owner = THIS_MODULE,
.open = ap3216c_open,
.read = ap3216c_read,
.release = ap3216c_release,
};
/* MISC设备结构体 */
static struct miscdevice ap3216c_miscdev = {
.minor = NEWCHRDEV_MINOR,
.name = NEWCHRDEV_NAME,
.fops = &ap3216c_ops,
};
/*
* @description : i2c驱动的probe函数,当驱动与
* 设备匹配以后此函数就会执行
* @param - client : i2c设备
* @param - id : i2c设备ID
* @return : 0,成功;其他负值,失败
*/
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
/* 1、注册 MISC 子系统 */
ret = misc_register(&ap3216c_miscdev);
if(ret < 0){
printk("ap3216c misc device register failed!\r\n");
ret = -EFAULT;
}
/* 2、提取 i2c 设备 */
newchrdev.client = client;
/* 3、初始化 regmap */
/* 3.1、初始化regmap_config设置 */
newchrdev.regmap_config.reg_bits = 8; /* 寄存器长度8bit */
newchrdev.regmap_config.val_bits = 8; /* 值长度8bit */
/* 3.2、初始化IIC接口的regmap */
newchrdev.regmap = regmap_init_i2c(client, &newchrdev.regmap_config);
if (IS_ERR(newchrdev.regmap)) {
ret = -EFAULT;
goto ap3216c_probe_fail;
}
return 0;
ap3216c_probe_fail:
misc_deregister(&ap3216c_miscdev);
return ret;
}
/*
* @description : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
* @param - client : i2c设备
* @return : 0,成功;其他负值,失败
*/
static int ap3216c_remove(struct i2c_client *client)
{
int ret = 0;
/* 释放regmap */
regmap_exit(newchrdev.regmap);
/* MISC 驱动框架卸载 */
misc_deregister(&ap3216c_miscdev);
return ret;
}
/* 传统匹配方式ID列表 */
static const struct i2c_device_id ap3216c_id[] = {
{"lsc,ap3216c", 0},
{}
};
/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[] = {
{ .compatible = "lsc,ap3216c" },
{ /* Sentinel */ }
};
/* i2c驱动结构体 */
static struct i2c_driver ap3216c_driver = {
.probe = ap3216c_probe,
.remove = ap3216c_remove,
.driver = {
.owner = THIS_MODULE,
.name = "ap3216c",
.of_match_table = ap3216c_of_match,
},
.id_table = ap3216c_id,
};
/*
* @description : 驱动入口函数
* @param : 无
* @return : 无
*/
static int __init ap3216c_init(void)
{
int ret = 0;
ret = i2c_add_driver(&ap3216c_driver);
return ret;
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit ap3216c_exit(void)
{
i2c_del_driver(&ap3216c_driver);
}
/* module_i2c_driver(ap3216c_driver) */
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
测试:
# ls
ap3216c_app ap3216c_regmap.ko
#
# ls -l /dev/ap3216c
ls: /dev/ap3216c: No such file or directory
#
# insmod ap3216c_regmap.ko
#
# ls -l /dev/ap3216c
crw-rw---- 1 root root 10, 55 Jan 1 00:33 /dev/ap3216c
#
# rmmod ap3216c_regmap.ko
#
# ls -l /dev/ap3216c
ls: /dev/ap3216c: No such file or directory
#
# insmod ap3216c_regmap.ko
#
# ./ap3216c_app /dev/ap3216c
ir = 0, als = 0, ps = 0
ir = 4, als = 194, ps = 909
ir = 0, als = 182, ps = 805
ir = 12, als = 192, ps = 835
ir = 0, als = 7, ps = 1023
ir = 11, als = 0, ps = 1023
ir = 2, als = 0, ps = 1023
ir = 0, als = 205, ps = 834
ir = 13, als = 192, ps = 838
ir = 9, als = 193, ps = 852
ir = 12, als = 0, ps = 1023
ir = 0, als = 0, ps = 1023
ir = 0, als = 5, ps = 1023
ir = 0, als = 1, ps = 1023
ir = 3, als = 189, ps = 844
ir = 0, als = 190, ps = 808
^C
#
# rmmod ap3216c_regmap.ko
六、修改icm20608驱动程序(SPI子系统)
#include "linux/init.h"
#include "linux/module.h"
#include "linux/spi/spi.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"
#include "linux/printk.h"
#include "linux/delay.h"
#include "asm-generic/uaccess.h"
#include "linux/regmap.h"
#define ICM20608G_ID 0XAF /* ID值 */
#define ICM20608D_ID 0XAE /* ID值 */
/* 陀螺仪和加速度自测(出产时设置,用于与用户的自检输出值比较) */
#define ICM20_SELF_TEST_X_GYRO 0x00
#define ICM20_SELF_TEST_Y_GYRO 0x01
#define ICM20_SELF_TEST_Z_GYRO 0x02
#define ICM20_SELF_TEST_X_ACCEL 0x0D
#define ICM20_SELF_TEST_Y_ACCEL 0x0E
#define ICM20_SELF_TEST_Z_ACCEL 0x0F
/* 陀螺仪静态偏移 */
#define ICM20_XG_OFFS_USRH 0x13
#define ICM20_XG_OFFS_USRL 0x14
#define ICM20_YG_OFFS_USRH 0x15
#define ICM20_YG_OFFS_USRL 0x16
#define ICM20_ZG_OFFS_USRH 0x17
#define ICM20_ZG_OFFS_USRL 0x18
#define ICM20_SMPLRT_DIV 0x19
#define ICM20_CONFIG 0x1A
#define ICM20_GYRO_CONFIG 0x1B
#define ICM20_ACCEL_CONFIG 0x1C
#define ICM20_ACCEL_CONFIG2 0x1D
#define ICM20_LP_MODE_CFG 0x1E
#define ICM20_ACCEL_WOM_THR 0x1F
#define ICM20_FIFO_EN 0x23
#define ICM20_FSYNC_INT 0x36
#define ICM20_INT_PIN_CFG 0x37
#define ICM20_INT_ENABLE 0x38
#define ICM20_INT_STATUS 0x3A
/* 加速度输出 */
#define ICM20_ACCEL_XOUT_H 0x3B
#define ICM20_ACCEL_XOUT_L 0x3C
#define ICM20_ACCEL_YOUT_H 0x3D
#define ICM20_ACCEL_YOUT_L 0x3E
#define ICM20_ACCEL_ZOUT_H 0x3F
#define ICM20_ACCEL_ZOUT_L 0x40
/* 温度输出 */
#define ICM20_TEMP_OUT_H 0x41
#define ICM20_TEMP_OUT_L 0x42
/* 陀螺仪输出 */
#define ICM20_GYRO_XOUT_H 0x43
#define ICM20_GYRO_XOUT_L 0x44
#define ICM20_GYRO_YOUT_H 0x45
#define ICM20_GYRO_YOUT_L 0x46
#define ICM20_GYRO_ZOUT_H 0x47
#define ICM20_GYRO_ZOUT_L 0x48
#define ICM20_SIGNAL_PATH_RESET 0x68
#define ICM20_ACCEL_INTEL_CTRL 0x69
#define ICM20_USER_CTRL 0x6A
#define ICM20_PWR_MGMT_1 0x6B
#define ICM20_PWR_MGMT_2 0x6C
#define ICM20_FIFO_COUNTH 0x72
#define ICM20_FIFO_COUNTL 0x73
#define ICM20_FIFO_R_W 0x74
#define ICM20_WHO_AM_I 0x75
/* 加速度静态偏移 */
#define ICM20_XA_OFFSET_H 0x77
#define ICM20_XA_OFFSET_L 0x78
#define ICM20_YA_OFFSET_H 0x7A
#define ICM20_YA_OFFSET_L 0x7B
#define ICM20_ZA_OFFSET_H 0x7D
#define ICM20_ZA_OFFSET_L 0x7E
#define NEWCHRDEV_MINOR 255 /* 次设备号(让MISC自动分配) */
#define NEWCHRDEV_NAME "icm20608" /* 名子 */
typedef struct{
void *private_data; /* 私有数据(保存SPI设备指针)*/
signed int gyro_x_adc; /* 陀螺仪X轴原始值 */
signed int gyro_y_adc; /* 陀螺仪Y轴原始值 */
signed int gyro_z_adc; /* 陀螺仪Z轴原始值 */
signed int accel_x_adc; /* 加速度计X轴原始值 */
signed int accel_y_adc; /* 加速度计Y轴原始值 */
signed int accel_z_adc; /* 加速度计Z轴原始值 */
signed int temp_adc; /* 温度原始值 */
struct regmap *regmap;
struct regmap_config regmap_config;
}newchrdev_t;
newchrdev_t newchrdev;
/*
* @description : 从icm20608读取多个寄存器数据
* @param - dev: icm20608设备
* @param - reg: 要读取的寄存器首地址
* @param - val: 读取到的数据
* @param - len: 要读取的数据长度
* @return : 操作结果
* 说明:icm20608_read_regs函数内部操作SPI子系统已经封装,封装好的函数为spi_read
*/
static int icm20608_read_regs(newchrdev_t *dev, unsigned char reg, void *buf, int len)
{
int ret = -1;
unsigned char txdata[1];
unsigned char * rxdata;
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = (struct spi_device *)dev->private_data;
t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); /* 申请内存 */
if(!t) {
return -ENOMEM;
}
rxdata = kzalloc(sizeof(char) * len, GFP_KERNEL); /* 申请内存 */
if(!rxdata) {
goto out1;
}
/* 一共发送len+1个字节的数据,第一个字节为
寄存器首地址,一共要读取len个字节长度的数据,*/
txdata[0] = reg | 0x80; /* 写数据的时候首寄存器地址bit8要置1 */
t->tx_buf = txdata; /* 要发送的数据 */
t->rx_buf = rxdata; /* 要读取的数据 */
t->len = len+1; /* t->len=发送的长度+读取的长度 */
spi_message_init(&m); /* 初始化spi_message */
spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
ret = spi_sync(spi, &m); /* 同步发送 */
if(ret) {
goto out2;
}
memcpy(buf , rxdata+1, len); /* 只需要读取的数据 */
out2:
kfree(rxdata); /* 释放内存 */
out1:
kfree(t); /* 释放内存 */
return ret;
}
/*
* @description : 向icm20608多个寄存器写入数据
* @param - dev: icm20608设备
* @param - reg: 要写入的寄存器首地址
* @param - val: 要写入的数据缓冲区
* @param - len: 要写入的数据长度
* @return : 操作结果
* 说明:icm20608_write_regs函数内部操作SPI子系统已经封装,封装好的函数为spi_write
*/
static signed int icm20608_write_regs(newchrdev_t *dev, unsigned char reg, unsigned char *buf, unsigned char len)
{
int ret = -1;
unsigned char *txdata;
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = (struct spi_device *)dev->private_data;
t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); /* 申请内存 */
if(!t) {
return -ENOMEM;
}
txdata = kzalloc(sizeof(char)+len, GFP_KERNEL);
if(!txdata) {
goto out1;
}
/* 一共发送len+1个字节的数据,第一个字节为
寄存器首地址,len为要写入的寄存器的集合,*/
*txdata = reg & ~0x80; /* 写数据的时候首寄存器地址bit8要清零 */
memcpy(txdata+1, buf, len); /* 把len个寄存器拷贝到txdata里,等待发送 */
t->tx_buf = txdata; /* 要发送的数据 */
t->len = len+1; /* t->len=发送的长度+读取的长度 */
spi_message_init(&m); /* 初始化spi_message */
spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
ret = spi_sync(spi, &m); /* 同步发送 */
if(ret) {
goto out2;
}
out2:
kfree(txdata); /* 释放内存 */
out1:
kfree(t); /* 释放内存 */
return ret;
}
/*
* @description : 读取icm20608指定寄存器值,读取一个寄存器
* @param - dev: icm20608设备
* @param - reg: 要读取的寄存器
* @return : 读取到的寄存器值
*/
static unsigned char icm20608_read_onereg(newchrdev_t *dev, unsigned char reg)
{
unsigned char data = 0;
#if 0
icm20608_read_regs(dev, reg, &data, 1);
#endif
regmap_read(dev->regmap, reg, (unsigned int*)&data);
return data;
}
/*
* @description : 向icm20608指定寄存器写入指定的值,写一个寄存器
* @param - dev: icm20608设备
* @param - reg: 要写的寄存器
* @param - data: 要写入的值
* @return : 无
*/
static void icm20608_write_onereg(newchrdev_t *dev, unsigned char reg, unsigned char value)
{
#if 0
unsigned char buf = value;
icm20608_write_regs(dev, reg, &buf, 1);
#endif
regmap_write(dev->regmap, reg, (unsigned int)value);
}
/*
* ICM20608内部寄存器初始化函数
* @param : 无
* @return : 无
*/
void icm20608_reginit(void)
{
unsigned char value = 0;
icm20608_write_onereg(&newchrdev, ICM20_PWR_MGMT_1, 0x80);
mdelay(50);
icm20608_write_onereg(&newchrdev, ICM20_PWR_MGMT_1, 0x01);
mdelay(50);
value = icm20608_read_onereg(&newchrdev, ICM20_WHO_AM_I);
printk("ICM20608 ID = %#X\r\n", value);
icm20608_write_onereg(&newchrdev, ICM20_SMPLRT_DIV, 0x00); /* 输出速率是内部采样率 */
icm20608_write_onereg(&newchrdev, ICM20_GYRO_CONFIG, 0x18); /* 陀螺仪±2000dps量程 */
icm20608_write_onereg(&newchrdev, ICM20_ACCEL_CONFIG, 0x18); /* 加速度计±16G量程 */
icm20608_write_onereg(&newchrdev, ICM20_CONFIG, 0x04); /* 陀螺仪低通滤波BW=20Hz */
icm20608_write_onereg(&newchrdev, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz */
icm20608_write_onereg(&newchrdev, ICM20_PWR_MGMT_2, 0x00); /* 打开加速度计和陀螺仪所有轴 */
icm20608_write_onereg(&newchrdev, ICM20_LP_MODE_CFG, 0x00); /* 关闭低功耗 */
icm20608_write_onereg(&newchrdev, ICM20_FIFO_EN, 0x00); /* 关闭FIFO */
}
/*
* @description : 读取ICM20608的数据,读取原始数据,包括三轴陀螺仪、
* : 三轴加速度计和内部温度。
* @param - dev : ICM20608设备
* @return : 无。
*/
void icm20608_readdata(newchrdev_t *dev)
{
unsigned char data[14] = "";
#if 0
icm20608_read_regs(dev, ICM20_ACCEL_XOUT_H, data, 14);
#endif
regmap_bulk_read(dev->regmap, ICM20_ACCEL_XOUT_H, (void*)data, 14);
dev->accel_x_adc = (signed short)((data[0] << 8) | data[1]);
dev->accel_y_adc = (signed short)((data[2] << 8) | data[3]);
dev->accel_z_adc = (signed short)((data[4] << 8) | data[5]);
dev->temp_adc = (signed short)((data[6] << 8) | data[7]);
dev->gyro_x_adc = (signed short)((data[8] << 8) | data[9]);
dev->gyro_y_adc = (signed short)((data[10] << 8) | data[11]);
dev->gyro_z_adc = (signed short)((data[12] << 8) | data[13]);
}
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int icm20608_open(struct inode *inode, struct file *filp)
{
filp->private_data = &newchrdev; /* 设置私有数据 */
return 0;
}
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t icm20608_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
newchrdev_t *dev = (newchrdev_t *)filp->private_data;
signed int data[7];
long err = 0;
icm20608_readdata(dev);
data[0] = dev->gyro_x_adc;
data[1] = dev->gyro_y_adc;
data[2] = dev->gyro_z_adc;
data[3] = dev->accel_x_adc;
data[4] = dev->accel_y_adc;
data[5] = dev->accel_z_adc;
data[6] = dev->temp_adc;
printk("data[0] = %d data[1] = %d data[2] = %d data[3] = %d data[4] = %d data[5] = %d\r\n",data[0],data[1],data[2],data[3],data[4],data[5]);
err = copy_to_user(buf, data, sizeof(data));
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int icm20608_release(struct inode *inode, struct file *filp)
{
return 0;
}
static const struct file_operations icm20608_ops = {
.owner = THIS_MODULE,
.open = icm20608_open,
.read = icm20608_read,
.release = icm20608_release,
};
/* MISC设备结构体 */
static struct miscdevice icm20608_miscdev = {
.minor = NEWCHRDEV_MINOR,
.name = NEWCHRDEV_NAME,
.fops = &icm20608_ops,
};
/*
* @description : spi驱动的probe函数,当驱动与
* 设备匹配以后此函数就会执行
* @param - client : spi设备
* @param - id : spi设备ID
*
*/
static int icm20608_probe(struct spi_device *spi)
{
int ret = 0;
/* 使用 MISC 注册字符设备驱动 */
ret = misc_register(&icm20608_miscdev);
if(ret < 0){
printk("icm20608 misc device register failed!\r\n");
ret = -EFAULT;
}
/* 初始化regmap_config设置 */
newchrdev.regmap_config.reg_bits = 8; /* 寄存器长度8bit */
newchrdev.regmap_config.val_bits = 8; /* 值长度8bit */
newchrdev.regmap_config.read_flag_mask = 0x80; /* 读掩码设置为0X80,ICM20608使用SPI接口读的时候寄存器最高位应该为1 */
/* 初始化IIC接口的regmap */
newchrdev.regmap = regmap_init_spi(spi, &newchrdev.regmap_config);
if (IS_ERR(newchrdev.regmap)) {
ret = -EFAULT;
goto icm20608_probe_fail;
}
newchrdev.private_data = spi; /* 设置私有数据 */
/* 初始化icm20608 */
icm20608_reginit();
return 0;
icm20608_probe_fail:
misc_deregister(&icm20608_miscdev);
return ret;
}
/*
* @description : spi驱动的remove函数,移除spi驱动的时候此函数会执行
* @param - client : spi设备
* @return : 0,成功;其他负值,失败
*/
static int icm20608_remove(struct spi_device *spi)
{
int ret = 0;
/* 删除regmap */
regmap_exit(newchrdev.regmap);
/* MISC 驱动框架卸载 */
misc_deregister(&icm20608_miscdev);
return ret;
}
/* 传统匹配方式ID列表 */
static const struct spi_device_id icm20608_id[] = {
{"invensense,icm20608", 0},
{}
};
/* 设备树匹配列表 */
static const struct of_device_id icm20608_of_match[] = {
{ .compatible = "invensense,icm20608" },
{ /* Sentinel */ }
};
/* SPI驱动结构体 */
static struct spi_driver icm20608_driver = {
.probe = icm20608_probe,
.remove = icm20608_remove,
.driver = {
.owner = THIS_MODULE,
.name = "icm20608",
.of_match_table = icm20608_of_match,
},
.id_table = icm20608_id,
};
/*
* @description : 驱动入口函数
* @param : 无
* @return : 无
*/
static int __init icm20608_init(void)
{
int ret = 0;
ret = spi_register_driver(&icm20608_driver);
return ret;
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit icm20608_exit(void)
{
spi_unregister_driver(&icm20608_driver);
}
/* module_i2c_driver(ap3216c_driver) */
module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_LICENSE("GPL");
测试:
# ls
ap3216c_app icm20608_app
ap3216c_regmap.ko icm20608_regmap.ko
#
# insmod icm20608_regmap.ko
ICM20608 ID = 0XAE
#
# ls -l /dev/icm20608
crw-rw---- 1 root root 10, 56 Jan 1 04:39 /dev/icm20608
#
# rmmod icm20608_regmap.ko
#
# ls -l /dev/icm20608
ls: /dev/icm20608: No such file or directory
#
# insmod icm20608_regmap.ko
ICM20608 ID = 0XAE
#
# ./icm20608_app /dev/icm20608
data[0] = 14 data[1] = 8 data[2] = -2 data[3] = 45 data[4] = 7 data[5] = 2073
原始值:
gx = 14, gy = 8, gz = -2
ax = 45, ay = 7, az = 2073
temp = 759
实际值:act gx = 0.85°/S, act gy = 0.49°/S, act gz = -0.12°/S
act ax = 0.02g, act ay = 0.00g, act az = 1.01g
act temp = 27.25°C
data[0] = 11 data[1] = 10 data[2] = -1 data[3] = 44 data[4] = 11 data[5] = 2073
原始值:
gx = 11, gy = 10, gz = -1
ax = 44, ay = 11, az = 2073
temp = 765
实际值:act gx = 0.67°/S, act gy = 0.61°/S, act gz = -0.06°/S
act ax = 0.02g, act ay = 0.01g, act az = 1.01g
act temp = 27.26°C
data[0] = 596 data[1] = 95 data[2] = -193 data[3] = 53 data[4] = 1623 data[5] = 1281
原始值:
gx = 596, gy = 95, gz = -193
ax = 53, ay = 1623, az = 1281
temp = 756
实际值:act gx = 36.34°/S, act gy = 5.79°/S, act gz = -11.77°/S
act ax = 0.03g, act ay = 0.79g, act az = 0.63g
act temp = 27.24°C
data[0] = 549 data[1] = -117 data[2] = 2 data[3] = 208 data[4] = 2005 data[5] = 486
原始值:
gx = 549, gy = -117, gz = 2
ax = 208, ay = 2005, az = 486
temp = 761
实际值:act gx = 33.48°/S, act gy = -7.13°/S, act gz = 0.12°/S
act ax = 0.10g, act ay = 0.98g, act az = 0.24g
act temp = 27.25°C
data[0] = -2424 data[1] = 137 data[2] = 171 data[3] = 15 data[4] = 1678 data[5] = 1001
原始值:
gx = -2424, gy = 137, gz = 171
ax = 15, ay = 1678, az = 1001
temp = 766
实际值:act gx = -147.80°/S, act gy = 8.35°/S, act gz = 10.43°/S
act ax = 0.01g, act ay = 0.82g, act az = 0.49g
act temp = 27.27°C
data[0] = 15 data[1] = 6 data[2] = -2 data[3] = 49 data[4] = 5 data[5] = 2071
原始值:
gx = 15, gy = 6, gz = -2
ax = 49, ay = 5, az = 2071
temp = 766
实际值:act gx = 0.91°/S, act gy = 0.37°/S, act gz = -0.12°/S
act ax = 0.02g, act ay = 0.00g, act az = 1.01g
act temp = 27.27°C
^C
# random: nonblocking pool is initialized
# rmmod icm20608_regmap.ko
#