#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/slab.h> /*kzalloc*/
#define debug(format, args...) do{\
printk(KERN_ERR "[ap3216c]%s:%d:" format " \n", __FUNCTION__, __LINE__, ##args);\
}while(0)
#define AP3216C_ADDR 0x1e
#define AP3216C_NAME "ap3216c"
#define AP3216C_CNT 1
#define SYSTEM_CONFIG 0x00
#define INT_STATUS 0x01
#define INT_CLR_MANNER 0x02
#define IR_DATA_LOW 0x0A
#define IR_DATA_HIGH 0x0B
#define ALS_DATA_LOW 0x0C
#define ALS_DATA_HIGH 0x0D
#define PS_DATA_LOW 0x0E
#define PS_DATA_HIGH 0x0F
struct ap3216c_reg_info
{
u8 reg_addr;
u8 reg_val;
};
static struct i2c_client *ap3216c_client;
struct ap3216c_device {
struct i2c_client *client;
int major;
int minor;
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
unsigned short ir;
unsigned short als;
unsigned short ps;
bool sys_create;
};
static int ap3216c_i2c_read_regs(struct i2c_client *client,u8 addr,u8 reg,char *buf,u8 len)
{
int ret;
struct i2c_msg msg[2];
bool retry = false;
if(!client||!buf)
{
debug("%s:Invaild params\n",__FUNCTION__);
return -EINVAL;
}
msg[0].addr = addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = ®
msg[1].addr = addr;
msg[1].flags = I2C_M_RD;
msg[1].len = len;
msg[1].buf = buf;
retry_read:
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if(ret != ARRAY_SIZE(msg))
{
if(!retry)
{
debug("ap3216c i2c data read retry (%d)\n",ret);
msleep(20);
retry = true;
goto retry_read;
}else
{
debug("adapter[%s],i2c read addr[0x%02x] reg [0x%02x] failed\n",client->adapter->name,addr,reg);
return -EIO;
}
}
//debug("%s:client[%s],i2c read addr[0x%02x] reg [0x%02x] sucess\n",__FUNCTION__,client->name,addr,reg);
return 0;
}
static int ap3216c_i2c_write_regs(struct i2c_client *client,u8 addr,u8 reg,u8 *buf,u8 len)
{
int ret;
struct i2c_msg msg[1];
bool retry = false;
u8 b[256];
if(!client)
{
debug("%s:Invaild params\n",__FUNCTION__);
return -EINVAL;
}
b[0] = reg;
memcpy(&b[1],buf,len);
msg[0].addr = addr;
msg[0].flags = 0;
msg[0].buf = b;
msg[0].len = len + 1;
retry_write:
ret = i2c_transfer(client->adapter,msg, ARRAY_SIZE(msg));
if(ret != ARRAY_SIZE(msg))
{
if(!retry)
{
debug("ap3216c i2c data write retry (%d)\n",ret);
msleep(20);
retry = true;
goto retry_write;
}else
{
debug("adapter[%s],i2c write addr[0x%02x] failed\n",client->adapter->name,addr);
return -EIO;
}
}
//debug("client[%s],i2c write addr[0x%02x] reg [0x%02x] sucess\n",client->name,addr,reg);
return 0;
}
static int ap3216c_i2c_read_reg(struct i2c_client *client,u8 addr,u8 reg,u8 *value)
{
return ap3216c_i2c_read_regs(client,addr,reg,value,1);
}
static int ap3216c_i2c_write_reg(struct i2c_client *client, u8 addr, u8 reg, u8 value)
{
u8 buf[1];
buf[0] = value;
return ap3216c_i2c_write_regs(client,addr,reg,buf,1);
}
static void ap3216c_readdata(struct ap3216c_device *data)
{
u8 buf[6];
int i = 0;
struct ap3216c_device *ap3216c_data = i2c_get_clientdata(data->client);
for(i = 0 ;i < 6;i++)
{
ap3216c_i2c_read_reg(ap3216c_data->client, AP3216C_ADDR,IR_DATA_LOW + i, &buf[i]);
}
if(buf[0] & 0x80)
{
debug("ir ap3216c_data invailed\n");
ap3216c_data->ir = 0;
}else
{
ap3216c_data->ir = ((buf[0] & 0x03)| ((unsigned short)buf[1] << 2));
ap3216c_data->als = (((unsigned short)buf[3]<<8 )| buf[2]);
}
if(buf[4]&0x40 || buf[5]&0x40)
{
debug("ps data invailed\n");
ap3216c_data->ps = 0;
}
ap3216c_data->ps = ((unsigned short)(buf[5] & 0x3F)<<4)|
(buf[4] & 0x0F);
}
static ssize_t ap3216c_read(struct file *file,char __user *buf,size_t cnt, loff_t *loff)
{
int ret;
unsigned short raw_data[3];
struct ap3216c_device *data = i2c_get_clientdata(ap3216c_client);
ap3216c_readdata(data);
raw_data[0] = data->ir;
raw_data[1] = data->als;
raw_data[2] = data->ps;
debug("ir = %d, als = %d, ps = %d\r\n", raw_data[0], raw_data[1], raw_data[2]);
ret = copy_to_user(buf, raw_data,sizeof(raw_data));
return 0;
}
static ssize_t ir_value_show(struct device *dev,struct device_attribute *attr,char *buf)
{
int ret;
unsigned char ir_low = 0x00;
unsigned char ir_high = 0x00;
unsigned short ir = 0x00;
struct ap3216c_device *data = i2c_get_clientdata(ap3216c_client);
if(data == NULL)
{
return -EINVAL;
}
ret = ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,IR_DATA_LOW ,&ir_low);
ret |= ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,IR_DATA_HIGH ,&ir_high);
if(ret != 0 )
{
return sprintf(buf, "%d\n", ret);;
}
if(ir_low & 0x80)
{
debug("ir ap3216c_data invailed\n");
ir = 0;
}else
{
ir = ((ir_low & 0x03)| ((unsigned short)ir_high << 2));
}
return sprintf(buf,"%d\n",ir);
}
static DEVICE_ATTR(ir_value, S_IRUGO, ir_value_show, NULL);
static ssize_t als_value_show(struct device *dev,struct device_attribute *attr,char *buf)
{
int ret;
unsigned char ir_low = 0x00;
unsigned char als_low = 0x00;
unsigned char als_high = 0x00;
unsigned short als = 0x00;
//struct ap3216c_device *data = dev_get_drvdata(dev);
struct ap3216c_device *data = i2c_get_clientdata(ap3216c_client);
if(data == NULL)
{
return -EINVAL;
}
ret = ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,IR_DATA_LOW ,&ir_low);
ret |= ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,ALS_DATA_LOW ,&als_low);
ret |= ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,ALS_DATA_HIGH ,&als_high);
if(ret != 0 )
{
return sprintf(buf, "%d\n", ret);;
}
if(ir_low & 0x80)
{
debug("ir ap3216c_data invailed\n");
return sprintf(buf,"%d\n",ir_low);
}else
{
als = (((unsigned short)als_high<<8 )|als_low);
}
return sprintf(buf,"%d\n",als);
}
static DEVICE_ATTR(als_value, S_IRUGO, als_value_show, NULL);
static ssize_t ps_value_show(struct device *dev,struct device_attribute *attr,char *buf)
{
int ret;
unsigned char ps_low = 0x00;
unsigned char ps_high = 0x00;
unsigned short ps = 0x00;
struct ap3216c_device *data = i2c_get_clientdata(ap3216c_client);
if(data == NULL)
{
debug("ps data is null\n");
return -EINVAL;
}
ret = ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,PS_DATA_LOW ,&ps_low);
ret |= ap3216c_i2c_read_reg(data->client, AP3216C_ADDR,PS_DATA_HIGH ,&ps_high);
if(ret != 0 )
{
return sprintf(buf, "%d\n", ret);;
}
if(ps_low & 0x40 || ps_high & 0x40)
{
debug("ps data invailed\n");
return -EINVAL;
}else
{
ps = ((unsigned short)(ps_high & 0x3F)<<4)|
(ps_low & 0x0F);
}
return sprintf(buf,"%d\n",ps);
}
static DEVICE_ATTR(ps_value, S_IRUGO, ps_value_show, NULL);
static const struct attribute *ap3216c_attributes[] = {
&dev_attr_ir_value.attr,
&dev_attr_als_value.attr,
&dev_attr_ps_value.attr,
NULL
};
static int ap3216c_open(struct inode *inode, struct file *file)
{
return 0;
}
static int ap3216c_release(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations ap3216c_fops = {
.owner = THIS_MODULE,
.read = ap3216c_read,
.open = ap3216c_open,
.release = ap3216c_release,
};
static int register_ap3216c_cdev(struct ap3216c_device * data)
{
int ret;
if(data->major)
{
data->devid = MKDEV(data->major,data->minor);
ret = register_chrdev_region(data->devid,AP3216C_CNT,AP3216C_NAME);
if(ret < 0)
{
debug("++klz register ap3216c region failed\n");
}
}else
{
alloc_chrdev_region(&data->devid,0,AP3216C_CNT,AP3216C_NAME);
data->major = MAJOR(data->devid);
data->minor = MINOR(data->devid);
}
data->cdev.owner = THIS_MODULE;
cdev_init(&data->cdev,&ap3216c_fops);
ret = cdev_add(&data->cdev,data->devid,AP3216C_CNT);
if(ret < 0)
{
debug("ap3216c cdev add failed\n");
goto fail_cdev_add;
}
data->class = class_create(THIS_MODULE,AP3216C_NAME);
if(IS_ERR(data->class))
{
debug("class create failed\n");
goto fail_class_create;
}
data->device = device_create(data->class,NULL,data->devid,NULL,AP3216C_NAME);
if(IS_ERR(data->device))
{
debug(
"device create failed\n");
goto fail_device_create;
}
return 0;
fail_device_create:
class_destroy(data->class);
cdev_del(&data->cdev);
unregister_chrdev_region(data->devid,AP3216C_CNT);
return -1;
fail_class_create:
cdev_del(&data->cdev);
unregister_chrdev_region(data->devid,AP3216C_CNT);
return -1;
fail_cdev_add:
unregister_chrdev_region(data->devid,AP3216C_CNT);
return -1;
}
static int ap3216c_init(struct ap3216c_device *data)
{
int ret;
ret = ap3216c_i2c_write_reg(data->client, AP3216C_ADDR,SYSTEM_CONFIG,0x04);
if(ret < 0)
{
debug("sw reset ap3216c failed\n");
return -1;
}
mdelay(50);
ret = ap3216c_i2c_write_reg(data->client, AP3216C_ADDR,SYSTEM_CONFIG,0x03);
if(ret < 0)
{
debug("setting ap3216c failed\n");
return -1;
}
return 0;
}
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret;
struct ap3216c_device *data = NULL;
data = kmalloc(sizeof(struct ap3216c_device), GFP_KERNEL);
//data =devm_kmalloc(&client->dev,sizeof(ap3216c_device), GFP_KERNEL);
if(!data)
{
printk(KERN_ERR"++klz,kmalloc ap3216c failed,return err\n");
return -ENOMEM;
}
i2c_set_clientdata(client, data);
data->client = client;
ap3216c_client = client;
data->sys_create = false;
ret = register_ap3216c_cdev(data);
if(ret < 0)
{
debug("++klz register ap3216c fops failed\n");
return ret;
}
ret = ap3216c_init(data);
if(ret < 0)
{
debug("++klz ap3216c init failed\n");
return ret;
}
ret = sysfs_create_files(&data->device->kobj,ap3216c_attributes);
if(ret)
{
debug("failed to create sys files\n");
return -EINVAL;
}
data->sys_create = true;
debug("ap3216c init sucess\n");
return 0;
}
static int ap3216c_remove(struct i2c_client *client)
{
struct ap3216c_device * data = i2c_get_clientdata(client);
cdev_del(&data->cdev);
unregister_chrdev_region(data->devid,AP3216C_CNT);
device_destroy(data->class,data->devid);
class_destroy(data->class);
kfree(data);
return 0;
}
static const struct of_device_id ap3216c_of_match[] = {
{ .compatible = "alientek,ap3216c", },
{/*Sentinel*/}
};
MODULE_DEVICE_TABLE(of, ap3216c_of_match);
static const struct i2c_device_id ap3216c_id[] = {
{ "alientek,ap3216c", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ap3216c_id);
static struct i2c_driver ap3216c_driver = {
.driver = {
.name = "ap3216c_driver",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ap3216c_of_match),
},
.probe = ap3216c_probe,
.remove = ap3216c_remove,
.id_table = ap3216c_id,
};
module_i2c_driver(ap3216c_driver);
MODULE_AUTHOR("klz <1255713178@qq.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("ap3216c driver of atk imx6ull");
140.Linux驱动-IIC驱动(基于AP3216C)
最新推荐文章于 2024-04-05 14:00:48 发布