mirror of
https://github.com/hardkernel/linux.git
synced 2026-04-19 12:00:46 +09:00
add i2c-dev interface support
This commit is contained in:
@@ -65,5 +65,10 @@ if I2C_RK29
|
||||
help
|
||||
This supports the use of the I2C3 interface on rk29 processors.
|
||||
endif
|
||||
|
||||
config I2C_DEV_RK29
|
||||
tristate "RK29 I2C device interface support"
|
||||
default n
|
||||
depends on I2C_RK29
|
||||
help
|
||||
Nothing
|
||||
endmenu
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#
|
||||
obj-$(CONFIG_I2C_RK2818) += i2c-rk2818.o
|
||||
obj-$(CONFIG_I2C_RK29) += i2c-rk29.o
|
||||
obj-$(CONFIG_I2C_DEV_RK29) += i2c-dev-rk29.o
|
||||
|
||||
|
||||
ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
|
||||
395
drivers/i2c/busses/i2c-dev-rk29.c
Executable file
395
drivers/i2c/busses/i2c-dev-rk29.c
Executable file
@@ -0,0 +1,395 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/rwsem.h>
|
||||
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "i2c-dev-rk29.h"
|
||||
#include "../i2c-core.h"
|
||||
|
||||
|
||||
#define I2C_DEV_SCL_RATE 100 * 1000
|
||||
|
||||
struct completion i2c_dev_complete;
|
||||
struct i2c_dump_info g_dump;
|
||||
|
||||
static void i2c_dev_get_list(struct i2c_list_info *list)
|
||||
{
|
||||
struct i2c_devinfo *devinfo;
|
||||
struct i2c_adapter *adap = NULL;
|
||||
int index;
|
||||
|
||||
memset(list, 0, sizeof(struct i2c_list_info));
|
||||
|
||||
down_read(&__i2c_board_lock);
|
||||
list_for_each_entry(devinfo, &__i2c_board_list, list) {
|
||||
if(devinfo->busnum >= MAX_I2C_BUS) {
|
||||
list->adap_nr = -1;
|
||||
up_read(&__i2c_board_lock);
|
||||
return;
|
||||
}
|
||||
adap = i2c_get_adapter(devinfo->busnum);
|
||||
if(adap != NULL) {
|
||||
list->adap[devinfo->busnum].id = adap->nr;
|
||||
strcpy(list->adap[devinfo->busnum].name, adap->name);
|
||||
|
||||
index = list->adap[devinfo->busnum].client_nr++;
|
||||
if(index >= MAX_CLIENT_NUM || index == -1)
|
||||
list->adap[devinfo->busnum].client_nr = -1;
|
||||
else {
|
||||
list->adap[devinfo->busnum].client[index].addr = devinfo->board_info.addr;
|
||||
strcpy(list->adap[devinfo->busnum].client[index].name,
|
||||
devinfo->board_info.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
list->adap_nr = MAX_I2C_BUS;
|
||||
up_read(&__i2c_board_lock);
|
||||
return;
|
||||
}
|
||||
void i2c_dev_dump_start(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
memset(&g_dump, 0, sizeof(struct i2c_dump_info));
|
||||
g_dump.id = adap->nr;
|
||||
g_dump.addr = msgs[0].addr;
|
||||
|
||||
for(i = 0; i < num; i++) {
|
||||
if(msgs[i].flags & I2C_M_RD) {
|
||||
if(msgs[i].len >= MAX_VALUE_NUM)
|
||||
g_dump.get_num = -1;
|
||||
else
|
||||
g_dump.get_num = msgs[i].len;
|
||||
}
|
||||
else {
|
||||
if(msgs[i].len >= MAX_VALUE_NUM)
|
||||
g_dump.set_num = -1;
|
||||
else {
|
||||
g_dump.set_num = msgs[i].len;
|
||||
for(j = 0; j < msgs[i].len; j++)
|
||||
g_dump.set_value[j] = msgs[i].buf[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(i2c_dev_dump_start);
|
||||
|
||||
void i2c_dev_dump_stop(struct i2c_adapter *adap, struct i2c_msg *msgs, int num, int ret)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if(ret < 0) {
|
||||
g_dump.get_num = 0;
|
||||
g_dump.set_num = 0;
|
||||
}
|
||||
for(i = 0; i < num; i++) {
|
||||
if((msgs[i].flags & I2C_M_RD) && (g_dump.get_num > 0)) {
|
||||
for(j = 0; j < msgs[i].len; j++)
|
||||
g_dump.get_value[j] = msgs[i].buf[j];
|
||||
}
|
||||
}
|
||||
complete(&i2c_dev_complete);
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(i2c_dev_dump_stop);
|
||||
|
||||
static void i2c_dev_get_dump(struct i2c_dump_info *dump)
|
||||
{
|
||||
init_completion(&i2c_dev_complete);
|
||||
wait_for_completion_killable(&i2c_dev_complete);
|
||||
*dump = g_dump;
|
||||
return;
|
||||
}
|
||||
static int i2c_dev_get_normal(struct i2c_adapter *adap, struct i2c_get_info *get)
|
||||
{
|
||||
struct i2c_msg msg;
|
||||
char buf[MAX_VALUE_NUM];
|
||||
int ret, i;
|
||||
|
||||
msg.addr = (__u16)get->addr;
|
||||
msg.flags = I2C_M_RD;
|
||||
msg.len = get->num;
|
||||
msg.buf = buf;
|
||||
msg.scl_rate = I2C_DEV_SCL_RATE;
|
||||
|
||||
ret = i2c_transfer(adap, &msg, 1);
|
||||
if(ret == 1) {
|
||||
for(i = 0; i < get->num; i++)
|
||||
get->value[i] = buf[i];
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
static int i2c_dev_set_normal(struct i2c_adapter *adap, struct i2c_set_info *set)
|
||||
{
|
||||
struct i2c_msg msg;
|
||||
char buf[MAX_VALUE_NUM];
|
||||
int ret;
|
||||
|
||||
msg.addr = (__u16)set->addr;
|
||||
msg.flags = 0;
|
||||
msg.len = set->num;
|
||||
msg.buf = buf;
|
||||
msg.scl_rate = I2C_DEV_SCL_RATE;
|
||||
|
||||
ret = i2c_transfer(adap, &msg, 1);
|
||||
return(ret == 1)? 0: -1;
|
||||
}
|
||||
static int i2c_dev_get_reg8(struct i2c_adapter *adap, struct i2c_get_info *get)
|
||||
{
|
||||
int ret, i;
|
||||
struct i2c_msg msgs[2];
|
||||
char reg = get->reg;
|
||||
char buf[MAX_VALUE_NUM];
|
||||
|
||||
msgs[0].addr = (__u16)get->addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = 1;
|
||||
msgs[0].buf = ®
|
||||
msgs[0].scl_rate = I2C_DEV_SCL_RATE;
|
||||
|
||||
msgs[1].addr = get->addr;
|
||||
msgs[1].flags = I2C_M_RD;
|
||||
msgs[1].len = get->num;
|
||||
msgs[1].buf = buf;
|
||||
msgs[1].scl_rate = I2C_DEV_SCL_RATE;
|
||||
|
||||
ret = i2c_transfer(adap, msgs, 2);
|
||||
if(ret == 2) {
|
||||
for(i = 0; i < get->num; i++)
|
||||
get->value[i] = buf[i];
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
static int i2c_dev_set_reg8(struct i2c_adapter *adap, struct i2c_set_info *set)
|
||||
{
|
||||
int ret, i;
|
||||
struct i2c_msg msg;
|
||||
char buf[MAX_VALUE_NUM + 1];
|
||||
|
||||
buf[0] = (char)set->reg;
|
||||
for(i = 0; i < set->num; i++)
|
||||
buf[i+1] = (char)set->value[i];
|
||||
|
||||
msg.addr = (__u16)set->addr;
|
||||
msg.flags = 0;
|
||||
msg.len = set->num + 1;
|
||||
msg.buf = buf;
|
||||
msg.scl_rate = I2C_DEV_SCL_RATE;
|
||||
|
||||
|
||||
ret = i2c_transfer(adap, &msg, 1);
|
||||
return (ret == 1)? 0: -1;
|
||||
}
|
||||
|
||||
static int i2c_dev_get_reg16(struct i2c_adapter *adap, struct i2c_get_info *get)
|
||||
{
|
||||
int ret, i;
|
||||
struct i2c_msg msgs[2];
|
||||
char reg[2];
|
||||
char buf[MAX_VALUE_NUM * 2];
|
||||
|
||||
reg[0] = (char)(get->reg & 0xff);
|
||||
reg[1] = (char)((get->reg >>8) & 0xff);
|
||||
|
||||
msgs[0].addr = (__u16)get->addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = 2;
|
||||
msgs[0].buf = reg;
|
||||
msgs[0].scl_rate = I2C_DEV_SCL_RATE;
|
||||
|
||||
msgs[1].addr = get->addr;
|
||||
msgs[1].flags = I2C_M_RD;
|
||||
msgs[1].len = get->num * 2;
|
||||
msgs[1].buf = buf;
|
||||
msgs[1].scl_rate = I2C_DEV_SCL_RATE;
|
||||
|
||||
ret = i2c_transfer(adap, msgs, 2);
|
||||
if(ret == 2) {
|
||||
for(i = 0; i < get->num; i++)
|
||||
get->value[i] = buf[2*i] & (buf[2*i+1]<<8);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
|
||||
}
|
||||
static int i2c_dev_set_reg16(struct i2c_adapter *adap, struct i2c_set_info *set)
|
||||
{
|
||||
struct i2c_msg msg;
|
||||
int ret, i;
|
||||
char buf[2 * (MAX_VALUE_NUM + 1)];
|
||||
|
||||
buf[0] = (char)(set->reg & 0xff);
|
||||
buf[1] = (char)((set->reg >>8) & 0xff);
|
||||
|
||||
for(i = 0; i < set->num; i++) {
|
||||
buf[2 * (i + 1)] = (char)(set->value[i] & 0xff);
|
||||
buf[2 * (i + 1) + 1] = (char)((set->value[i]>>8) & 0xff);
|
||||
}
|
||||
|
||||
msg.addr = set->addr;
|
||||
msg.flags = 0;
|
||||
msg.len = 2 * (set->num + 1);
|
||||
msg.buf = buf;
|
||||
msg.scl_rate = I2C_DEV_SCL_RATE;
|
||||
|
||||
ret = i2c_transfer(adap, &msg, 1);
|
||||
return (ret == 1)? 0: -1;
|
||||
}
|
||||
|
||||
static int i2c_dev_get_value(struct i2c_get_info *get)
|
||||
{
|
||||
int ret = 0;
|
||||
struct i2c_adapter *adap = NULL;
|
||||
|
||||
if(get->num >= MAX_VALUE_NUM)
|
||||
return -1;
|
||||
adap = i2c_get_adapter(get->id);
|
||||
if(adap == NULL)
|
||||
return -1;
|
||||
switch(get->mode) {
|
||||
case 'b':
|
||||
ret = i2c_dev_get_reg8(adap, get);
|
||||
break;
|
||||
case 's':
|
||||
ret = i2c_dev_get_reg16(adap, get);
|
||||
break;
|
||||
case 'o':
|
||||
ret = -1;
|
||||
break;
|
||||
default:
|
||||
ret = i2c_dev_get_normal(adap, get);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_dev_set_value(struct i2c_set_info *set)
|
||||
{
|
||||
int ret = 0;
|
||||
struct i2c_adapter *adap = NULL;
|
||||
|
||||
printk("id=%d, addr=0x%x, mode = %c, num = %d, reg = 0x%x, value[0] = %d,",set->id, set->addr, set->mode, set->num, set->reg, set->value[0]);
|
||||
if(set->num >= MAX_VALUE_NUM)
|
||||
return -1;
|
||||
adap = i2c_get_adapter(set->id);
|
||||
if(adap == NULL)
|
||||
return -1;
|
||||
switch(set->mode) {
|
||||
case 'b':
|
||||
ret = i2c_dev_set_reg8(adap, set);
|
||||
break;
|
||||
case 's':
|
||||
ret = i2c_dev_set_reg16(adap, set);
|
||||
break;
|
||||
case 'o':
|
||||
ret = -1;
|
||||
break;
|
||||
default:
|
||||
ret = i2c_dev_set_normal(adap, set);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_dev_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static long i2c_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct i2c_list_info *list = NULL;
|
||||
struct i2c_dump_info dump;
|
||||
struct i2c_get_info get;
|
||||
struct i2c_set_info set;
|
||||
|
||||
switch(cmd) {
|
||||
case I2C_LIST:
|
||||
list = kzalloc(sizeof(struct i2c_list_info), GFP_KERNEL);
|
||||
if(list == NULL) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
i2c_dev_get_list(list);
|
||||
if(copy_to_user((void __user *)arg, (void *)list, sizeof(struct i2c_list_info)))
|
||||
ret = -EFAULT;
|
||||
kfree(list);
|
||||
break;
|
||||
case I2C_DUMP:
|
||||
i2c_dev_get_dump(&dump);
|
||||
if(copy_to_user((void __user *)arg, (void *)&dump, sizeof(struct i2c_dump_info)))
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
case I2C_GET:
|
||||
if(copy_from_user((void *)&get, (void __user *)arg, sizeof(struct i2c_get_info))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if(i2c_dev_get_value(&get) < 0) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if(copy_to_user((void __user *)arg, (void *)&get, sizeof(struct i2c_get_info)))
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
case I2C_SET:
|
||||
if(copy_from_user((void *)&set, (void __user *)arg, sizeof(struct i2c_set_info))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
ret = i2c_dev_set_value(&set);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static int i2c_dev_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct file_operations i2c_dev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = i2c_dev_open,
|
||||
.unlocked_ioctl = i2c_dev_ioctl,
|
||||
.release = i2c_dev_release,
|
||||
};
|
||||
static struct miscdevice i2c_misc_dev = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = I2C_DEV_NAME,
|
||||
.fops = &i2c_dev_fops,
|
||||
};
|
||||
static int __init i2c_dev_init(void)
|
||||
{
|
||||
return misc_register(&i2c_misc_dev);
|
||||
}
|
||||
static void __exit i2c_dev_exit(void)
|
||||
{
|
||||
misc_deregister(&i2c_misc_dev);
|
||||
}
|
||||
module_init(i2c_dev_init);
|
||||
module_exit(i2c_dev_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Driver for RK29 I2C Device");
|
||||
MODULE_AUTHOR("kfx, kfx@rock-chips.com");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
62
drivers/i2c/busses/i2c-dev-rk29.h
Executable file
62
drivers/i2c/busses/i2c-dev-rk29.h
Executable file
@@ -0,0 +1,62 @@
|
||||
#ifndef _I2C_DEV_H
|
||||
#define _I2C_DEV_H
|
||||
|
||||
#define I2C_DEV_NAME "i2c-dev"
|
||||
#define I2C_DEV_PATH "/dev/"I2C_DEV_NAME
|
||||
|
||||
#define MAX_ADAP_LENG 48
|
||||
#define MAX_CLIENT_LENG 20
|
||||
#define MAX_VALUE_NUM 16
|
||||
#define MAX_I2C_BUS 4
|
||||
#define MAX_CLIENT_NUM 32
|
||||
|
||||
|
||||
|
||||
#define I2C_LIST 0X7000
|
||||
#define I2C_DUMP 0x7010
|
||||
#define I2C_GET 0x7020
|
||||
#define I2C_SET 0x7030
|
||||
|
||||
|
||||
struct i2c_client_info {
|
||||
int addr;
|
||||
char name[MAX_CLIENT_LENG];
|
||||
};
|
||||
struct i2c_adap_info {
|
||||
int id;
|
||||
char name[MAX_ADAP_LENG];
|
||||
int client_nr;
|
||||
struct i2c_client_info client[MAX_CLIENT_NUM];
|
||||
};
|
||||
struct i2c_list_info {
|
||||
int adap_nr;
|
||||
struct i2c_adap_info adap[MAX_I2C_BUS];
|
||||
};
|
||||
|
||||
struct i2c_dump_info {
|
||||
int id;
|
||||
int addr;
|
||||
int get_num;
|
||||
int get_value[MAX_VALUE_NUM];
|
||||
int set_num;
|
||||
int set_value[MAX_VALUE_NUM];
|
||||
};
|
||||
|
||||
struct i2c_get_info {
|
||||
char mode;
|
||||
int id;
|
||||
int addr;
|
||||
int reg;
|
||||
int num;
|
||||
int value[MAX_VALUE_NUM];
|
||||
};
|
||||
struct i2c_set_info {
|
||||
char mode;
|
||||
int id;
|
||||
int addr;
|
||||
int reg;
|
||||
int num;
|
||||
int value[MAX_VALUE_NUM];
|
||||
};
|
||||
|
||||
#endif /* _I2CDEV_H */
|
||||
@@ -35,7 +35,7 @@
|
||||
#define RK2818_I2C_TIMEOUT (msecs_to_jiffies(500))
|
||||
#define RK2818_DELAY_TIME 2
|
||||
|
||||
#if 0
|
||||
#if 1
|
||||
#define i2c_dbg(dev, format, arg...) \
|
||||
dev_printk(KERN_INFO , dev , format , ## arg)
|
||||
#else
|
||||
|
||||
3
drivers/i2c/i2c-boardinfo.c
Normal file → Executable file
3
drivers/i2c/i2c-boardinfo.c
Normal file → Executable file
@@ -65,7 +65,7 @@ i2c_register_board_info(int busnum,
|
||||
int status;
|
||||
|
||||
down_write(&__i2c_board_lock);
|
||||
|
||||
printk("busnum = %d, len = %d>>>>>>>>>>>>>>>>>>\n", busnum, len);
|
||||
/* dynamic bus numbers will be assigned after the last static one */
|
||||
if (busnum >= __i2c_first_dynamic_bus_num)
|
||||
__i2c_first_dynamic_bus_num = busnum + 1;
|
||||
@@ -82,6 +82,7 @@ i2c_register_board_info(int busnum,
|
||||
|
||||
devinfo->busnum = busnum;
|
||||
devinfo->board_info = *info;
|
||||
printk("busnum = %d>>>>>>>>>>>>>>>>>>\n", busnum);
|
||||
list_add_tail(&devinfo->list, &__i2c_board_list);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,11 @@ static int i2c_check_addr(struct i2c_adapter *adapter, int addr);
|
||||
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
#ifdef CONFIG_I2C_DEV_RK29
|
||||
extern struct completion i2c_dev_complete;
|
||||
extern void i2c_dev_dump_start(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
|
||||
extern void i2c_dev_dump_stop(struct i2c_adapter *adap, struct i2c_msg *msgs, int num, int ret);
|
||||
#endif
|
||||
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
|
||||
const struct i2c_client *client)
|
||||
{
|
||||
@@ -1017,6 +1021,10 @@ static int __init i2c_init(void)
|
||||
retval = i2c_add_driver(&dummy_driver);
|
||||
if (retval)
|
||||
goto class_err;
|
||||
#ifdef CONFIG_I2C_DEV_RK29
|
||||
init_completion(&i2c_dev_complete);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
class_err:
|
||||
@@ -1107,6 +1115,9 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
|
||||
/* Retry automatically on arbitration loss */
|
||||
orig_jiffies = jiffies;
|
||||
#ifdef CONFIG_I2C_DEV_RK29
|
||||
i2c_dev_dump_start(adap, msgs, num);
|
||||
#endif
|
||||
for (ret = 0, try = 0; try <= adap->retries; try++) {
|
||||
ret = adap->algo->master_xfer(adap, msgs, num);
|
||||
if (ret != -EAGAIN)
|
||||
@@ -1114,6 +1125,9 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
if (time_after(jiffies, orig_jiffies + adap->timeout))
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_I2C_DEV_RK29
|
||||
i2c_dev_dump_stop(adap, msgs, num ,ret);
|
||||
#endif
|
||||
mutex_unlock(&adap->bus_lock);
|
||||
|
||||
return ret;
|
||||
|
||||
Reference in New Issue
Block a user