一、相关文件
Hi3519v101 i2c 总线驱动文件路径:
drivers/i2c/busses/i2c-hisi-v110.c
二、调试测试
1、在调试过程中遇到一个小问题,详见链接
2、驱动只实现了读秒寄存器
3、i2c/i2c_rtc_dev.c
#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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
// gpio1_6 → i2c2_sda 0x1204005c set 0x01
// gpio1_7 → i2c2_scl 0x12040060 set 0x01
#define i2c2_sda_base 0x1204005c //gpio管脚复用物理地址
#define i2c2_scl_base 0x12040060 //gpio管脚复用物理地址
#define gpio_phy_data 0x010 //gpio管脚数据偏移地址,0b00_0001_0000
#define gpio_phy_dir 0x400 //管脚方向寄存器偏移地址
#define gpio_out 0x4 //gpio3_2 配置为输出,1输出 0输入def
#define gpio_out_h 0xFF
#define gpio_out_l 0x00
static void __iomem *i2c2_sda; //寄存器基地址
static void __iomem *i2c2_scl; //寄存器基地址
static struct i2c_board_info r8010_dev_info = {
//所支持的i2c设备的列表
I2C_BOARD_INFO("r8010", 0x32), //一项代表一个支持的设备,它的名字叫做“r8010”,器件的地址是0x32,注意这是7位地址!
//在海思i2c 0x0004 I2C_TAR[9:0]位有说明,如果slave地址长度位设置为7bit,则仅bit[6:0]有效!
//例如,r8010 8bit读地址为0x64,则7bit地址为0x32
};
static struct i2c_client *r8010_client;
static int __init r8010_dev_init(void)
{
struct i2c_adapter *i2c_adap; //分配一个适配器的指针
i2c_adap = i2c_get_adapter(2); //调用core层的函数,获得一个i2c总线。这里我们已经知道新增的器件挂接在编号为0的i2c总线上
r8010_client = i2c_new_device(i2c_adap, &r8010_dev_info); // 把i2c适配器和新增的I2C器件关联起来,这个用了i2c总线2,地址是0x64。这就组成了一个客户端
i2c_put_adapter(i2c_adap);
//管脚复用配置
i2c2_sda = ioremap(i2c2_sda_base, 4);//4个字节,32位
i2c2_scl = ioremap(i2c2_scl_base, 4);
// val = ioread32(i2c2_sda);
// val &= ~(0x01 << 0)
iowrite32(0x01, i2c2_sda);
iowrite32(0x01, i2c2_scl);
return 0;
}
static void __exit r8010_dev_exit(void)
{
i2c_unregister_device(r8010_client);
}
module_init(r8010_dev_init);
module_exit(r8010_dev_exit);
MODULE_LICENSE("GPL");
4、i2c/i2c_rtc_drv.c
#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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define r8010_cnt 1
#define r8010_name "r8010_rtc"
#define r8010_esc_reg 0x10 //秒数寄存器
struct r8010_dev
{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
struct device_node *nd; /* 设备节点 */
int major; /* 主设备号 */
void *private_data; /* 私有数据 */
u8 val; /* 三个光传感器数据 */
};
struct r8010_dev r8010dev;
//从r8010读取多个寄存器数据
static int r8010_read_regs(struct r8010_dev *dev, u8 reg, void *val, int len)
{
int ret;
struct i2c_msg msg[2];
struct i2c_client *client = (struct i2c_client *)dev->private_data;
//msg[0]为发送要读取的首地址
msg[0].addr = client->addr; //r8010 地址
msg[0].flags = 0; //标志为发送数据
msg[0].buf = ® //读取的首地址
msg[0].len = 1; //reg长度
//msg[1]为读取数据
msg[1].addr = client->addr; //r8010 地址
msg[1].flags = I2C_M_RD; //标志为读数据
msg[1].buf = val; //读取数据缓冲区
msg[1].len = len; //要读取数据长度
// printk("r8010 addr= %x\n", client->addr);
ret = i2c_transfer(client->adapter, msg, 2);
if (ret == 2)
ret = 0;
else
{
printk("i2c read failed=%d reg=%06x len=%d\n", ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
}
//从r8010写入多个寄存器数据
static s32 r8010_write_regs(struct r8010_dev *dev, u8 reg, u8 *buf, int len)
{
u8 b[256];
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *)dev->private_data;
b[0] = reg; //寄存器首地址
memcpy(&b[1], buf, len); //将要写入数据拷贝到b
msg.addr = client->addr; //r8010 地址
msg.flags = 0; //标志为发送数据
msg.buf = b; //要写入的数据
msg.len = len + 1; //reg长度
return i2c_transfer(client->adapter, &msg, 1);
}
//从r8010读取一个寄存器数据
static unsigned char r8010_read_reg(struct r8010_dev *dev, u8 reg)
{
u8 data;
r8010_read_regs(dev, reg, &data, 1);
return data;
}
static unsigned char r8010_write_reg(struct r8010_dev *dev, u8 reg, u8 *buf)
{
r8010_write_regs(dev, reg, buf, 1);
return 0;
}
static ssize_t r8010_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
struct r8010_dev *dev = (struct r8010_dev *)filp->private_data;
r8010dev.val = r8010_read_reg(dev, r8010_esc_reg);
// printk("SEC reg data = %d\n", r8010dev.val);
__copy_to_user(buf, &r8010dev.val, cnt);
return 0;
}
static ssize_t r8010_write(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
struct r8010_dev *dev = (struct r8010_dev *)filp->private_data;
__copy_from_user(&r8010dev.val, buf, 1);
r8010_write_reg(dev, r8010_esc_reg, &r8010dev.val);
return 0;
}
static int r8010_open(struct inode *inode, struct file *filp)
{
filp->private_data = &r8010dev;
return 0;
}
static int r8010_release(struct inode *inode, struct file *filp)
{
return 0;
}
static const struct file_operations r8010_ops = {
.owner = THIS_MODULE,
.open = r8010_open,
.write = r8010_write,
.read = r8010_read,
.release = r8010_release,
};
static int r8010_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
//设备号
if (r8010dev.major)
{
r8010dev.devid = MKDEV(r8010dev.major, 0);
register_chrdev_region(r8010dev.devid, r8010_cnt, r8010_name);
}
else
{
alloc_chrdev_region(&r8010dev.devid, 0, r8010_cnt, r8010_name);
r8010dev.major = MAJOR(r8010dev.devid);
}
//注册设备
cdev_init(&r8010dev.cdev, &r8010_ops);
cdev_add(&r8010dev.cdev, r8010dev.devid, r8010_cnt);
//创建类
r8010dev.class = class_create(THIS_MODULE, r8010_name);
if (IS_ERR(r8010dev.class))
{
return PTR_ERR(r8010dev.class);
}
//创建设备
r8010dev.devid = device_create(r8010dev.class, NULL, r8010dev.devid, NULL, r8010_name);
if (IS_ERR(r8010dev.device))
{
return PTR_ERR(r8010dev.device);
}
r8010dev.private_data = client;
return 0;
}
static int r8010_remove(struct i2c_client *client)
{
//删除设备
cdev_del(&r8010dev.cdev);
unregister_chrdev_region(r8010dev.devid, r8010_cnt);
// 注销掉类和设备
device_destroy(r8010dev.class, r8010dev.devid);
class_destroy(r8010dev.class);
return 0;
}
/* 传统匹配方式ID列表 */
static const struct i2c_device_id r8010_id[] = {
{"r8010", 0},
{}};
static struct i2c_driver r8010_driver = {
.probe = r8010_probe,
.remove = r8010_remove,
.driver = {
.owner = THIS_MODULE,
.name = "r8010",
},
.id_table = r8010_id,
};
static int __init r8010_drv_init(void)
{
int ret;
ret = i2c_add_driver(&r8010_driver);
printk("ret = %d\n", ret);
return 0;
}
static void __exit r8010_drv_exit(void)
{
i2c_del_driver(&r8010_driver);
}
module_init(r8010_drv_init);
module_exit(r8010_drv_exit);
MODULE_LICENSE("GPL");
5、i2c/test_i2c_rtc.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int fd;
unsigned char val;
fd = open("/dev/r8010_rtc", O_RDWR); //打开设备
if(fd < 0)
printf("can`t open!\n");
if(argc != 2)
{
printf("Usage :\n");
printf("%s <read|write>\n", argv[0]);
return 0;
}
if (strcmp(argv[1], "read") == 0)
{
read(fd, &val, 1);
printf("read r8010 sec reg val= %x\n", val);
}
else
{
val = 0x10;
write(fd, &val, 1);
read(fd, &val, 1);
printf("r8010 sec reg:\nwrite 0x10\n");
printf("read 0x%x\n", val);
}
printf("\n\n");
printf("sec = \33[s\33[?25l");//\33[s 保存现在光标的位置,\33[?25l 隐藏光标
while (1)
{
read(fd, &val, 1);//这里直接死循环读取秒寄存器
printf("\33[u%x", val);//\33[u 恢复光标位置
}
return 0;
}
6、测试
/ # insmod i2c_rtc_drv.ko
ret = 0
/ # insmod i2c_rtc_dev.ko
/ #
/ # ls /sys/bus/i2c/
devices/ drivers_autoprobe uevent
drivers/ drivers_probe
/ # ls /sys/bus/i2c/devices/
2-0032/ i2c-0/ i2c-1/ i2c-2/ i2c-3/
/ # ls /sys/bus/i2c/devices/2-0032/name
/sys/bus/i2c/devices/2-0032/name
/ # cat /sys/bus/i2c/devices/2-0032/name
r8010
/ # ls /dev/r8010_rtc
/dev/r8010_rtc
/ # ./test_i2c_rtc read
read r8010 sec reg val= 15
sec = 19
/ #
/ # ./test_i2c_rtc write
r8010 sec reg:
write 0x10
read 0x10
sec = 13
/ #