一.项目需求
要求读取GVS2715这个IIC设置寄存器的值来获取版本号,GVS2715这个芯片是十六位寄存器。
当使用i2ctool工具读取十六位寄存器的时候,发现无法读取出来,读取的都是XXXX。
二.从零开始写IIC设备驱动读取十六位寄存器的状态
#include <linux/miscdevice.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/printk.h>
#include <linux/kobject.h>
#include <linux/version.h>#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00)
#define DRIVER_NAME "gsv2715"/*define logging*/
#define gsv2715_DEBUG 1
#if gsv2715_DEBUG
#define DBG(format, args...) \printk(KERN_DEBUG "%s: " format, DRIVER_NAME, ##args)
#define ERR(format, args...) \printk(KERN_ERR "%s: " format, DRIVER_NAME, ##args)
#define WARNING(format, args...) \printk(KERN_WARN "%s: " format, DRIVER_NAME, ##args)
#define INFO(format, args...) \printk(KERN_INFO "%s: " format, DRIVER_NAME, ##args)
#else
#define DBG(format, args...)
#define ERR(format, args...)
#define WARNING(format, args...)
#define INFO(format, args...)
#endifstruct gsv2715_dev {struct device *dev;struct miscdevice miscdev;struct i2c_client *client;struct mutex confctl_mutex;u32 hdmi_rx_sel;
};struct gsv2715_dev *g_gsv2715;
//struct gsv2715_dev *gsv2715;
static int i2c_write(struct gsv2715_dev *gsv2715, u16 reg, u8 *val, u32 n)
{struct i2c_msg msg;struct i2c_client *client = gsv2715->client;int err;u8 data[128];data[0] = (reg >> 8) & 0xFF;data[1] = reg & 0xFF;data[2] = *val;msg.addr = client->addr;msg.flags = !I2C_M_RD;msg.buf = &data[0];msg.len = 3;INFO("I2C ready write 0x%04x = (data[0])0x%02x (data[1])0x%02x (data[2])0x%02x\n", reg , data[0], data[1], data[2]);err = i2c_transfer(client->adapter, &msg, 1);if (err != 1) {ERR("%s: writing register 0x%x from 0x%x failed\n", __func__,reg, client->addr);return -1;} else {switch (n) {case 1:INFO("I2C write 0x%02X%02X = 0x%02X\n", data[0], data[1], data[2]);break;case 2:INFO("I2C write 0x%02x = 0x%02x%02x\n", reg, data[2],data[1]);break;case 4:INFO("I2C write 0x%02x = 0x%02x%02x%02x%02x\n", reg,data[4], data[3], data[2], data[1]);break;default:INFO("I2C write %d bytes from address 0x%02x\n", n,reg);}}return 0;
}static void i2c_read(struct gsv2715_dev *gsv2715, u16 reg, u8 *val, u32 n)
{struct i2c_msg msg[2];struct i2c_client *client = gsv2715->client;int err;u8 buf[2] = { 0x00 };buf[0] = (reg >> 8) & 0xFF;buf[1] = reg & 0xFF;INFO("gsv2715-i2c_read - reg = 0x%04X ;buf = 0x%02X 0x%02X\n", reg, buf[0],buf[1]);/*msg[0] addr to read*/msg[0].addr = client->addr;msg[0].flags = !I2C_M_RD;msg[0].buf = &buf[0];msg[0].len = 2;/*msg[1] read data*/msg[1].addr = client->addr;msg[1].flags = I2C_M_RD;msg[1].buf = val;msg[1].len = n;err = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));if (err != ARRAY_SIZE(msg)) {ERR("%s: reading register 0x%x from 0x%x failed , ARRAY_SIZE(msg) = %d , err =%d\n", __func__,reg, client->addr,ARRAY_SIZE(msg),err);}INFO("gsv2715-i2c_read end - reg = 0x%04X ;val = 0x%02X\n", reg, *val);
}/*** 从16位地址寄存器中读取8位数据,并将结果存储到指定的变量中。** @param gsv2715 指向gsv2715设备结构体的指针* @param reg 要读取的寄存器地址* @param val 指向用于存储读取结果的变量的指针*/
static void i2c_read_16bit(struct gsv2715_dev *gsv2715, u16 reg, u8 *val)
{i2c_read(gsv2715, reg, val, sizeof(*val));
}static void i2c_write_16bit(struct gsv2715_dev *gsv2715, u16 reg, u8 buf)
{i2c_write(gsv2715, reg, &buf, 1);
}static long gsv2715_ioctl(struct file *file, uint32_t cmd, unsigned long arg)
{return 0;
}static ssize_t gsv2715_write(struct file *file, const char __user *buf,size_t size, loff_t *ppos)
{return 1;
}static ssize_t gsv2715_read(struct file *file, char __user *buf, size_t size,loff_t *ppos)
{return 1;
}int gsv2715_read_version(struct device *dev, u8 *version)
{int ret = 0 ;struct gsv2715_dev *gsv2715 = g_gsv2715;u8 buffer[8];i2c_read_16bit(gsv2715, 0xFF3C, buffer);sprintf(version, "20%02d%02d%02d%02d",buffer[1],buffer[2],buffer[3],buffer[0]);dev_info(dev, "get verison: %s \n",version);return ret;
}static ssize_t gsv2715version_read(struct device *dev,struct device_attribute *attr, char *buf)
{u8 version[12];memset(version, 0 ,sizeof(version));gsv2715_read_version(dev, version);sprintf(buf, "%s\n", version);return strlen(buf);
}static ssize_t gsv2715version_write(struct device *dev,struct device_attribute *attr,const char *buf, size_t count)
{struct gsv2715_dev *gsv2715 = g_gsv2715;u32 hdmirxstate = 0;int ret;ret = kstrtouint(buf, 10, &hdmirxstate);if (!ret) {dev_dbg(gsv2715->dev, "gsv2715sw1_hdmirxsel_store: %d\n",hdmirxstate);} else {dev_err(gsv2715->dev, "write hdmi_rx_sel failed!!!\n");}i2c_write_16bit(gsv2715, 0xFF3C, hdmirxstate);DBG("%s: hdmi rx select state: %d,ret = %d,hdmirxstate = %d\n", __func__, gsv2715->hdmi_rx_sel,ret,hdmirxstate);return count;
}static DEVICE_ATTR(gsv2715version, 0644,gsv2715version_read, gsv2715version_write);static int gsv2715_init(struct gsv2715_dev *gsv2715)
{int ret = 0;gsv2715->hdmi_rx_sel = 0;DBG("%s \n", __func__, gsv2715->hdmi_rx_sel,ret);return ret;
}static const struct file_operations gsv2715_fops = {.owner = THIS_MODULE,.read = gsv2715_read,.write = gsv2715_write,.unlocked_ioctl = gsv2715_ioctl,
};struct miscdevice gsv2715_miscdev = {.minor = MISC_DYNAMIC_MINOR,.name = "gsv2715_dev",.fops = &gsv2715_fops,
};static int gsv2715_probe(struct i2c_client *client,const struct i2c_device_id *id)
{struct gsv2715_dev *gsv2715;struct device *dev = &client->dev;int ret;dev_info(dev, "driver version: %02x.%02x.%02x",DRIVER_VERSION >> 16,(DRIVER_VERSION & 0xff00) >> 8,DRIVER_VERSION & 0x00ff);if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)){ERR("FILE:%s Func:%s @Line:%d \n",__FILE__,__func__,__LINE__);return -EIO;}DBG("chip found @ 0x%x (%s)\n", client->addr << 1,client->adapter->name);gsv2715 = devm_kzalloc(dev, sizeof(struct gsv2715_dev), GFP_KERNEL);if (!gsv2715)return -ENOMEM;DBG("FILE:%s Func:%s @Line:%d \n",__FILE__,__func__,__LINE__);gsv2715->client = client;ret = misc_register(&gsv2715_miscdev);if (ret) {ERR("gsv2715 ERROR: could not register gsv2715 device\n");return ret;}mutex_init(&gsv2715->confctl_mutex);ret = device_create_file(gsv2715_miscdev.this_device,&dev_attr_gsv2715version);if (ret) {dev_err(gsv2715->dev, "failed to create attr hdmirxsel!\n");return ret;} gsv2715_init(gsv2715);g_gsv2715 = gsv2715;INFO("%s found @ 0x%x (%s)\n", client->name, client->addr << 1,client->adapter->name);return 0;
}static int gsv2715_remove(struct i2c_client *client)
{mutex_destroy(&g_gsv2715->confctl_mutex);misc_deregister(&gsv2715_miscdev);kfree(g_gsv2715);return 0;
}static void gsv2715_shutdown(struct i2c_client *client)
{struct gsv2715_dev *gsv2715 = g_gsv2715;u32 data = 0xA1;i2c_write_16bit(gsv2715, 0XFF09, data);
}static const struct of_device_id gsv2715_of_match[] = {{ .compatible = "gscoolink,gsv2715" },{}
};
MODULE_DEVICE_TABLE(of, gsv2715_of_match);static struct i2c_driver gsv2715_driver = {.probe = gsv2715_probe,.remove = gsv2715_remove,.shutdown = gsv2715_shutdown,.driver = {.owner = THIS_MODULE,.name = DRIVER_NAME,.of_match_table = of_match_ptr(gsv2715_of_match),},
};static int __init gsv2715_driver_init(void)
{return i2c_add_driver(&gsv2715_driver);
}static void __exit gsv2715_driver_exit(void)
{i2c_del_driver(&gsv2715_driver);
}device_initcall_sync(gsv2715_driver_init);
module_exit(gsv2715_driver_exit);MODULE_DESCRIPTION("gsv2715 4 HDMI in switch driver");
MODULE_LICENSE("GPL v2");