mirror of
https://github.com/torvalds/linux.git
synced 2026-06-07 22:14:04 +02:00
i2c: remove unused rk drivers
Change-Id: I7e198d811ec2163c28ac095d60e95f4e9ac702e3 Signed-off-by: Tao Huang <huangtao@rock-chips.com>
This commit is contained in:
parent
8f9e364c9c
commit
052b8beaa3
|
|
@ -1,398 +0,0 @@
|
|||
#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 = {
|
||||
.done = -1,
|
||||
};
|
||||
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];
|
||||
}
|
||||
}
|
||||
if(i2c_dev_complete.done == 0)
|
||||
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");
|
||||
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#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 */
|
||||
|
|
@ -1,598 +0,0 @@
|
|||
/* drivers/i2c/busses/i2c-rk29-adapter.c
|
||||
*
|
||||
* Copyright (C) 2012 ROCKCHIP, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
#include "i2c-rk30.h"
|
||||
|
||||
/* master transmit */
|
||||
#define I2C_MTXR (0x0000)
|
||||
/* master receive */
|
||||
#define I2C_MRXR (0x0004)
|
||||
/* slave address */
|
||||
#define I2C_SADDR (0x0010)
|
||||
/* interrupt enable control */
|
||||
#define I2C_IER (0x0014)
|
||||
#define I2C_IER_ARBITR_LOSE (1<<7)
|
||||
#define I2C_IER_MRX_NEED_ACK (1<<1)
|
||||
#define I2C_IER_MTX_RCVD_ACK (1<<0)
|
||||
|
||||
#define IRQ_MST_ENABLE (I2C_IER_ARBITR_LOSE | \
|
||||
I2C_IER_MRX_NEED_ACK | \
|
||||
I2C_IER_MTX_RCVD_ACK)
|
||||
#define IRQ_ALL_DISABLE (0x00)
|
||||
|
||||
/* interrupt status, write 0 to clear */
|
||||
#define I2C_ISR (0x0018)
|
||||
#define I2C_ISR_ARBITR_LOSE (1<<7)
|
||||
#define I2C_ISR_MRX_NEED_ACK (1<<1)
|
||||
#define I2C_ISR_MTX_RCVD_ACK (1<<0)
|
||||
|
||||
/* stop/start/resume command, write 1 to set */
|
||||
#define I2C_LCMR (0x001c)
|
||||
#define I2C_LCMR_RESUME (1<<2)
|
||||
#define I2C_LCMR_STOP (1<<1)
|
||||
#define I2C_LCMR_START (1<<0)
|
||||
|
||||
/* i2c core status */
|
||||
#define I2C_LSR (0x0020)
|
||||
#define I2C_LSR_RCV_NAK (1<<1)
|
||||
#define I2C_LSR_RCV_ACK (~(1<<1))
|
||||
#define I2C_LSR_BUSY (1<<0)
|
||||
|
||||
/* i2c config */
|
||||
#define I2C_CONR (0x0024)
|
||||
#define I2C_CONR_NAK (1<<4)
|
||||
#define I2C_CONR_ACK (~(1<<4))
|
||||
#define I2C_CONR_MTX_MODE (1<<3)
|
||||
#define I2C_CONR_MRX_MODE (~(1<<3))
|
||||
#define I2C_CONR_MPORT_ENABLE (1<<2)
|
||||
#define I2C_CONR_MPORT_DISABLE (~(1<<2))
|
||||
|
||||
/* i2c core config */
|
||||
#define I2C_OPR (0x0028)
|
||||
#define I2C_OPR_RESET_STATUS (1<<7)
|
||||
#define I2C_OPR_CORE_ENABLE (1<<6)
|
||||
|
||||
#define I2CCDVR_REM_BITS (0x03)
|
||||
#define I2CCDVR_REM_MAX (1<<(I2CCDVR_REM_BITS))
|
||||
#define I2CCDVR_EXP_BITS (0x03)
|
||||
#define I2CCDVR_EXP_MAX (1<<(I2CCDVR_EXP_BITS))
|
||||
|
||||
|
||||
#define RK29_I2C_START_TMO_COUNT 100 // msleep 1 * 100
|
||||
|
||||
int i2c_suspended(struct i2c_adapter *adap)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(i2c_suspended);
|
||||
|
||||
static inline void rk29_i2c_disable_ack(struct rk30_i2c *i2c)
|
||||
{
|
||||
unsigned long conr = readl(i2c->regs + I2C_CONR);
|
||||
|
||||
conr |= I2C_CONR_NAK;
|
||||
writel(conr,i2c->regs + I2C_CONR);
|
||||
}
|
||||
|
||||
static inline void rk29_i2c_enable_ack(struct rk30_i2c *i2c)
|
||||
{
|
||||
unsigned long conr = readl(i2c->regs + I2C_CONR);
|
||||
|
||||
conr &= I2C_CONR_ACK;
|
||||
writel(conr,i2c->regs + I2C_CONR);
|
||||
}
|
||||
static inline void rk29_i2c_disable_mport(struct rk30_i2c *i2c)
|
||||
{
|
||||
unsigned long conr = readl(i2c->regs + I2C_CONR);
|
||||
|
||||
conr &= I2C_CONR_MPORT_DISABLE;
|
||||
writel(conr,i2c->regs + I2C_CONR);
|
||||
}
|
||||
|
||||
static inline void rk29_i2c_enable_mport(struct rk30_i2c *i2c)
|
||||
{
|
||||
unsigned long conr = readl(i2c->regs + I2C_CONR);
|
||||
|
||||
conr |= I2C_CONR_MPORT_ENABLE;
|
||||
writel(conr,i2c->regs + I2C_CONR);
|
||||
}
|
||||
|
||||
static inline void rk29_i2c_disable_irq(struct rk30_i2c *i2c)
|
||||
{
|
||||
writel(IRQ_ALL_DISABLE, i2c->regs + I2C_IER);
|
||||
}
|
||||
|
||||
static inline void rk29_i2c_enable_irq(struct rk30_i2c *i2c)
|
||||
{
|
||||
writel(IRQ_MST_ENABLE, i2c->regs + I2C_IER);
|
||||
}
|
||||
|
||||
/* scl = pclk/(5 *(rem+1) * 2^(exp+1)) */
|
||||
static void rk29_i2c_calcdivisor(unsigned long pclk,
|
||||
unsigned long scl_rate,
|
||||
unsigned long *real_rate,
|
||||
unsigned int *rem, unsigned int *exp)
|
||||
{
|
||||
unsigned int calc_rem = 0;
|
||||
unsigned int calc_exp = 0;
|
||||
|
||||
for(calc_exp = 0; calc_exp < I2CCDVR_EXP_MAX; calc_exp++)
|
||||
{
|
||||
calc_rem = pclk / (5 * scl_rate * (1 <<(calc_exp +1)));
|
||||
if(calc_rem < I2CCDVR_REM_MAX)
|
||||
break;
|
||||
}
|
||||
if(calc_rem >= I2CCDVR_REM_MAX || calc_exp >= I2CCDVR_EXP_MAX)
|
||||
{
|
||||
calc_rem = I2CCDVR_REM_MAX - 1;
|
||||
calc_exp = I2CCDVR_EXP_MAX - 1;
|
||||
}
|
||||
*rem = calc_rem;
|
||||
*exp = calc_exp;
|
||||
*real_rate = pclk/(5 * (calc_rem + 1) * (1 <<(calc_exp +1)));
|
||||
return;
|
||||
}
|
||||
static void rk29_i2c_set_clk(struct rk30_i2c *i2c, unsigned long scl_rate)
|
||||
{
|
||||
|
||||
unsigned int rem = 0, exp = 0;
|
||||
unsigned long real_rate = 0, tmp;
|
||||
|
||||
unsigned long i2c_rate = clk_get_rate(i2c->clk);
|
||||
|
||||
if((scl_rate == i2c->scl_rate) && (i2c_rate == i2c->i2c_rate))
|
||||
return;
|
||||
|
||||
i2c->i2c_rate = i2c_rate;
|
||||
i2c->scl_rate = scl_rate;
|
||||
|
||||
rk29_i2c_calcdivisor(i2c->i2c_rate, i2c->scl_rate, &real_rate, &rem, &exp);
|
||||
|
||||
tmp = readl(i2c->regs + I2C_OPR);
|
||||
tmp &= ~0x3f;
|
||||
tmp |= exp;
|
||||
tmp |= rem<<I2CCDVR_EXP_BITS;
|
||||
writel(tmp, i2c->regs + I2C_OPR);
|
||||
return;
|
||||
}
|
||||
|
||||
static void rk29_i2c_init_hw(struct rk30_i2c *i2c, unsigned long scl_rate)
|
||||
{
|
||||
unsigned long opr = readl(i2c->regs + I2C_OPR);
|
||||
|
||||
opr |= I2C_OPR_RESET_STATUS;
|
||||
writel(opr, i2c->regs + I2C_OPR);
|
||||
|
||||
udelay(10);
|
||||
opr = readl(i2c->regs + I2C_OPR);
|
||||
opr &= ~I2C_OPR_RESET_STATUS;
|
||||
writel(opr, i2c->regs + I2C_OPR);
|
||||
|
||||
rk29_i2c_set_clk(i2c, scl_rate);
|
||||
|
||||
rk29_i2c_disable_irq(i2c);
|
||||
writel(0, i2c->regs + I2C_LCMR);
|
||||
writel(0, i2c->regs + I2C_LCMR);
|
||||
|
||||
opr = readl(i2c->regs + I2C_OPR);
|
||||
opr |= I2C_OPR_CORE_ENABLE;
|
||||
writel(opr, i2c->regs + I2C_OPR);
|
||||
udelay(i2c->tx_setup);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void rk29_i2c_master_complete(struct rk30_i2c *i2c, int ret)
|
||||
{
|
||||
i2c_dbg(i2c->dev, "master_complete %d\n", ret);
|
||||
|
||||
i2c->msg_ptr = 0;
|
||||
i2c->msg = NULL;
|
||||
i2c->msg_idx++;
|
||||
i2c->msg_num = 0;
|
||||
if (ret)
|
||||
i2c->msg_idx = ret;
|
||||
|
||||
wake_up(&i2c->wait);
|
||||
}
|
||||
|
||||
static void rk29_i2c_message_start(struct rk30_i2c *i2c,
|
||||
struct i2c_msg *msg)
|
||||
{
|
||||
unsigned int addr = (msg->addr & 0x7f) << 1;
|
||||
unsigned long stat, conr;
|
||||
|
||||
stat = 0;
|
||||
|
||||
i2c->addr = msg->addr & 0x7f;
|
||||
|
||||
if (msg->flags & I2C_M_RD)
|
||||
addr |= 1;
|
||||
|
||||
if (msg->flags & I2C_M_REV_DIR_ADDR)
|
||||
addr ^= 1;
|
||||
|
||||
rk29_i2c_enable_ack(i2c);
|
||||
i2c_dbg(i2c->dev, "START: set addr 0x%02x to DS\n", addr);
|
||||
|
||||
conr = readl(i2c->regs + I2C_CONR);
|
||||
conr |= I2C_CONR_MTX_MODE;
|
||||
writel(conr, i2c->regs + I2C_CONR);
|
||||
writel(addr, i2c->regs + I2C_MTXR);
|
||||
|
||||
udelay(i2c->tx_setup);
|
||||
writel(I2C_LCMR_START|I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
|
||||
|
||||
}
|
||||
|
||||
static inline void rk29_i2c_stop(struct rk30_i2c *i2c, int ret)
|
||||
{
|
||||
i2c_dbg(i2c->dev, "STOP\n");
|
||||
udelay(i2c->tx_setup);
|
||||
|
||||
writel(I2C_LCMR_STOP|I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
|
||||
|
||||
i2c->state = STATE_STOP;
|
||||
|
||||
rk29_i2c_master_complete(i2c, ret);
|
||||
rk29_i2c_disable_irq(i2c);
|
||||
}
|
||||
|
||||
/* returns TRUE if the current message is the last in the set */
|
||||
static inline int is_lastmsg(struct rk30_i2c *i2c)
|
||||
{
|
||||
return i2c->msg_idx >= (i2c->msg_num - 1);
|
||||
}
|
||||
|
||||
/* returns TRUE if we this is the last byte in the current message */
|
||||
static inline int is_msglast(struct rk30_i2c *i2c)
|
||||
{
|
||||
return i2c->msg_ptr == i2c->msg->len-1;
|
||||
}
|
||||
|
||||
/* returns TRUE if we reached the end of the current message */
|
||||
static inline int is_msgend(struct rk30_i2c *i2c)
|
||||
{
|
||||
return i2c->msg_ptr >= i2c->msg->len;
|
||||
}
|
||||
|
||||
static int rk29_i2c_irq_nextbyte(struct rk30_i2c *i2c, unsigned long isr)
|
||||
{
|
||||
unsigned long lsr, conr;
|
||||
unsigned char byte;
|
||||
int ret = 0;
|
||||
|
||||
lsr = readl(i2c->regs + I2C_LSR);
|
||||
|
||||
switch (i2c->state) {
|
||||
case STATE_IDLE:
|
||||
dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__);
|
||||
goto out;
|
||||
|
||||
case STATE_STOP:
|
||||
dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__);
|
||||
rk29_i2c_disable_irq(i2c);
|
||||
goto out;
|
||||
|
||||
case STATE_START:
|
||||
if(!(isr & I2C_ISR_MTX_RCVD_ACK)){
|
||||
writel(isr & ~I2C_ISR_MTX_RCVD_ACK, i2c->regs + I2C_ISR);
|
||||
rk29_i2c_stop(i2c, -ENXIO);
|
||||
dev_err(i2c->dev, "START: addr[0x%02x] ack was not received(isr)\n", i2c->addr);
|
||||
goto out;
|
||||
}
|
||||
if ((lsr & I2C_LSR_RCV_NAK) &&
|
||||
!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
|
||||
writel(isr & ~I2C_ISR_MTX_RCVD_ACK, i2c->regs + I2C_ISR);
|
||||
|
||||
rk29_i2c_stop(i2c, -EAGAIN);
|
||||
dev_err(i2c->dev, "START: addr[0x%02x] ack was not received(lsr)\n", i2c->addr);
|
||||
goto out;
|
||||
}
|
||||
if (i2c->msg->flags & I2C_M_RD)
|
||||
i2c->state = STATE_READ;
|
||||
else
|
||||
i2c->state = STATE_WRITE;
|
||||
|
||||
/* terminate the transfer if there is nothing to do
|
||||
* as this is used by the i2c probe to find devices. */
|
||||
if (is_lastmsg(i2c) && i2c->msg->len == 0) {
|
||||
writel(isr & ~I2C_ISR_MTX_RCVD_ACK, i2c->regs + I2C_ISR);
|
||||
rk29_i2c_stop(i2c, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (i2c->state == STATE_READ){
|
||||
writel(isr & ~I2C_ISR_MTX_RCVD_ACK, i2c->regs + I2C_ISR);
|
||||
goto prepare_read;
|
||||
}
|
||||
|
||||
case STATE_WRITE:
|
||||
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
|
||||
if (!(isr & I2C_ISR_MTX_RCVD_ACK)){
|
||||
|
||||
rk29_i2c_stop(i2c, -ECONNREFUSED);
|
||||
dev_err(i2c->dev, "WRITE: addr[0x%02x] No Ack\n", i2c->addr);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
writel(isr & ~I2C_ISR_MTX_RCVD_ACK, i2c->regs + I2C_ISR);
|
||||
|
||||
retry_write:
|
||||
|
||||
if (!is_msgend(i2c)) {
|
||||
byte = i2c->msg->buf[i2c->msg_ptr++];
|
||||
conr = readl(i2c->regs + I2C_CONR);
|
||||
conr |= I2C_CONR_MTX_MODE;
|
||||
writel(conr, i2c->regs + I2C_CONR);
|
||||
writel(byte, i2c->regs + I2C_MTXR);
|
||||
|
||||
writel(I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
|
||||
|
||||
} else if (!is_lastmsg(i2c)) {
|
||||
/* we need to go to the next i2c message */
|
||||
|
||||
i2c_dbg(i2c->dev, "WRITE: Next Message\n");
|
||||
|
||||
i2c->msg_ptr = 0;
|
||||
i2c->msg_idx++;
|
||||
i2c->msg++;
|
||||
|
||||
/* check to see if we need to do another message */
|
||||
if (i2c->msg->flags & I2C_M_NOSTART) {
|
||||
if (i2c->msg->flags & I2C_M_RD) {
|
||||
/* cannot do this, the controller
|
||||
* forces us to send a new START
|
||||
* when we change direction */
|
||||
|
||||
rk29_i2c_stop(i2c, -EINVAL);
|
||||
}
|
||||
|
||||
goto retry_write;
|
||||
} else {
|
||||
/* send the new start */
|
||||
rk29_i2c_message_start(i2c, i2c->msg);
|
||||
i2c->state = STATE_START;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* send stop */
|
||||
|
||||
rk29_i2c_stop(i2c, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_READ:
|
||||
if(!(isr & I2C_ISR_MRX_NEED_ACK)){
|
||||
rk29_i2c_stop(i2c, -ENXIO);
|
||||
dev_err(i2c->dev, "READ: addr[0x%02x] not recv need ack interrupt\n", i2c->addr);
|
||||
goto out;
|
||||
}
|
||||
writel(isr & ~I2C_ISR_MRX_NEED_ACK, i2c->regs + I2C_ISR);
|
||||
byte = readl(i2c->regs + I2C_MRXR);
|
||||
i2c_dbg(i2c->dev, "READ: byte = %d\n", byte);
|
||||
i2c->msg->buf[i2c->msg_ptr++] = byte;
|
||||
prepare_read:
|
||||
if (is_msgend(i2c)) {
|
||||
if (is_lastmsg(i2c)) {
|
||||
/* last message, send stop and complete */
|
||||
rk29_i2c_disable_ack(i2c);
|
||||
i2c_dbg(i2c->dev, "READ: Send Stop\n");
|
||||
|
||||
rk29_i2c_stop(i2c, 0);
|
||||
} else {
|
||||
/* go to the next transfer */
|
||||
i2c_dbg(i2c->dev, "READ: Next Transfer\n");
|
||||
|
||||
i2c->msg_ptr = 0;
|
||||
i2c->msg_idx++;
|
||||
i2c->msg++;
|
||||
rk29_i2c_message_start(i2c, i2c->msg);
|
||||
i2c->state = STATE_START;
|
||||
}
|
||||
}else{
|
||||
conr = readl(i2c->regs + I2C_CONR);
|
||||
conr &= I2C_CONR_MRX_MODE;
|
||||
writel(conr, i2c->regs + I2C_CONR);
|
||||
|
||||
writel(I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t rk29_i2c_irq(int irqno, void *dev_id)
|
||||
{
|
||||
struct rk30_i2c *i2c = dev_id;
|
||||
unsigned long isr;
|
||||
|
||||
spin_lock(&i2c->lock);
|
||||
udelay(i2c->tx_setup);
|
||||
|
||||
isr = readl(i2c->regs + I2C_ISR);
|
||||
|
||||
if (isr & I2C_ISR_ARBITR_LOSE) {
|
||||
writel(isr & ~I2C_ISR_ARBITR_LOSE, i2c->regs + I2C_ISR);
|
||||
dev_err(i2c->dev, "deal with arbitration loss\n");
|
||||
}
|
||||
|
||||
if (i2c->state == STATE_IDLE) {
|
||||
i2c_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rk29_i2c_irq_nextbyte(i2c, isr);
|
||||
|
||||
out:
|
||||
spin_unlock(&i2c->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/* rk29_i2c_set_master
|
||||
*
|
||||
* get the i2c bus for a master transaction
|
||||
*/
|
||||
|
||||
static int rk29_i2c_set_master(struct rk30_i2c *i2c)
|
||||
{
|
||||
int tmo = RK29_I2C_START_TMO_COUNT;
|
||||
unsigned long lsr;
|
||||
|
||||
while (tmo-- > 0) {
|
||||
lsr = readl(i2c->regs + I2C_LSR);
|
||||
if (!(lsr & I2C_LSR_BUSY))
|
||||
return 0;
|
||||
writel(I2C_LCMR_STOP|I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
|
||||
msleep(1);
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* rk29_i2c_doxfer
|
||||
*
|
||||
* this starts an i2c transfer
|
||||
*/
|
||||
|
||||
static int rk29_i2c_doxfer(struct rk30_i2c *i2c,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
if (i2c->suspended)
|
||||
return -EIO;
|
||||
|
||||
ret = rk29_i2c_set_master(i2c);
|
||||
if (ret != 0) {
|
||||
dev_err(i2c->dev, "addr[0x%02x] cannot get bus (error %d)\n", msgs[0].addr, ret);
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_irq(&i2c->lock);
|
||||
|
||||
i2c->msg = msgs;
|
||||
i2c->msg_num = num;
|
||||
i2c->msg_ptr = 0;
|
||||
i2c->msg_idx = 0;
|
||||
i2c->state = STATE_START;
|
||||
|
||||
rk29_i2c_enable_irq(i2c);
|
||||
rk29_i2c_message_start(i2c, msgs);
|
||||
spin_unlock_irq(&i2c->lock);
|
||||
|
||||
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, msecs_to_jiffies(I2C_WAIT_TIMEOUT));
|
||||
|
||||
ret = i2c->msg_idx;
|
||||
|
||||
if (timeout == 0)
|
||||
i2c_dbg(i2c->dev, "addr[0x%02x] wait event timeout\n", msgs[0].addr);
|
||||
else if (ret != num)
|
||||
i2c_dbg(i2c->dev, "addr[0x%02x ]incomplete xfer (%d)\n", msgs[0].addr, ret);
|
||||
if((readl(i2c->regs + I2C_LSR) & I2C_LSR_BUSY) ||
|
||||
readl(i2c->regs + I2C_LCMR) & I2C_LCMR_STOP ){
|
||||
msleep(1);
|
||||
writel(I2C_LCMR_STOP|I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
|
||||
if((readl(i2c->regs + I2C_LSR) & I2C_LSR_BUSY) ||
|
||||
readl(i2c->regs + I2C_LCMR) & I2C_LCMR_STOP ){
|
||||
dev_warn(i2c->dev, "WARNING: STOP abnormal, addr[0x%02x] isr = 0x%x, lsr = 0x%x, lcmr = 0x%x\n",
|
||||
msgs[0].addr,
|
||||
readl(i2c->regs + I2C_ISR),
|
||||
readl(i2c->regs + I2C_LSR),
|
||||
readl(i2c->regs + I2C_LCMR)
|
||||
);
|
||||
}
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* rk29_i2c_xfer
|
||||
*
|
||||
* first port of call from the i2c bus code when an message needs
|
||||
* transferring across the i2c bus.
|
||||
*/
|
||||
|
||||
static int rk29_i2c_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct rk30_i2c *i2c = (struct rk30_i2c *)adap->algo_data;
|
||||
int ret = 0;
|
||||
unsigned long scl_rate;
|
||||
|
||||
clk_enable(i2c->clk);
|
||||
|
||||
if(msgs[0].scl_rate <= 400000 && msgs[0].scl_rate >= 10000)
|
||||
scl_rate = msgs[0].scl_rate;
|
||||
else if(msgs[0].scl_rate > 400000){
|
||||
dev_warn(i2c->dev, "Warning: addr[0x%x] msg[0].scl_rate( = %dKhz) is too high!",
|
||||
msgs[0].addr, msgs[0].scl_rate/1000);
|
||||
scl_rate = 400000;
|
||||
}
|
||||
else{
|
||||
dev_warn(i2c->dev, "Warning: addr[0x%x] msg[0].scl_rate( = %dKhz) is too low!",
|
||||
msgs[0].addr, msgs[0].scl_rate/1000);
|
||||
scl_rate = 10000;
|
||||
}
|
||||
if(i2c->is_div_from_arm[i2c->adap.nr])
|
||||
wake_lock(&i2c->idlelock[i2c->adap.nr]);
|
||||
|
||||
rk29_i2c_set_clk(i2c, scl_rate);
|
||||
rk29_i2c_enable_mport(i2c);
|
||||
udelay(i2c->tx_setup);
|
||||
|
||||
ret = rk29_i2c_doxfer(i2c, msgs, num);
|
||||
|
||||
rk29_i2c_disable_mport(i2c);
|
||||
if(i2c->is_div_from_arm[i2c->adap.nr])
|
||||
wake_unlock(&i2c->idlelock[i2c->adap.nr]);
|
||||
clk_disable(i2c->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* declare our i2c functionality */
|
||||
static u32 rk29_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
|
||||
}
|
||||
|
||||
/* i2c bus registration info */
|
||||
|
||||
static const struct i2c_algorithm rk29_i2c_algorithm = {
|
||||
.master_xfer = rk29_i2c_xfer,
|
||||
.functionality = rk29_i2c_func,
|
||||
};
|
||||
|
||||
int i2c_add_rk29_adapter(struct i2c_adapter *adap)
|
||||
{
|
||||
int ret = 0;
|
||||
struct rk30_i2c *i2c = (struct rk30_i2c *)adap->algo_data;
|
||||
|
||||
adap->algo = &rk29_i2c_algorithm;
|
||||
|
||||
i2c->i2c_init_hw = &rk29_i2c_init_hw;
|
||||
i2c->i2c_set_clk = &rk29_i2c_set_clk;
|
||||
i2c->i2c_irq = &rk29_i2c_irq;
|
||||
|
||||
ret = i2c_add_numbered_adapter(adap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,912 +0,0 @@
|
|||
/* drivers/i2c/busses/i2c_rk29.c
|
||||
*
|
||||
* Copyright (C) 2010 ROCKCHIP, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <mach/board.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "i2c-rk29.h"
|
||||
#define DRV_NAME "rk29_i2c"
|
||||
#define RETRY_NUM 1
|
||||
|
||||
#define RK29_UDELAY_TIME(scl_rate) ((400*1000)/(scl_rate))
|
||||
/*max ACK delay time = RK29_I2C_ACK_TIMEOUT_COUNT * RK29_UDELAY_TIME(scl_rate) us */
|
||||
#define RK29_I2C_ACK_TIMEOUT_COUNT (100 * 1000)
|
||||
/*max STOP delay time = RK29_I2C_STOP_TIMEOUT_COUNT * RK29_UDELAY_TIME(scl_rate) us */
|
||||
#define RK29_I2C_STOP_TIMEOUT_COUNT 70//1000
|
||||
/*max START delay time = RK29_I2C_START_TIMEOUT_COUNT * RK29_UDELAY_TIME(scl_rate) us */
|
||||
#define RK29_I2C_START_TIMEOUT_COUNT 1000
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
#define i2c_dbg(dev, format, arg...) \
|
||||
dev_printk(KERN_INFO , dev , format , ## arg)
|
||||
#define i2c_err(dev, format, arg...) \
|
||||
dev_printk(KERN_ERR , dev , format , ## arg)
|
||||
|
||||
#else
|
||||
#define i2c_dbg(dev, format, arg...)
|
||||
#define i2c_err(dev, format, arg...)
|
||||
|
||||
#endif
|
||||
|
||||
enum rk29_error {
|
||||
RK29_ERROR_NONE = 0,
|
||||
RK29_ERROR_ARBITR_LOSE,
|
||||
RK29_ERROR_UNKNOWN
|
||||
};
|
||||
|
||||
enum rk29_event {
|
||||
RK29_EVENT_NONE = 0,
|
||||
/* master has received ack(MTX mode)
|
||||
means that data has been sent to slave.
|
||||
*/
|
||||
RK29_EVENT_MTX_RCVD_ACK,
|
||||
/* master needs to send ack to slave(MRX mode)
|
||||
means that data has been received from slave.
|
||||
*/
|
||||
RK29_EVENT_MRX_NEED_ACK,
|
||||
RK29_EVENT_MAX
|
||||
};
|
||||
|
||||
struct rk29_i2c_data {
|
||||
struct device *dev;
|
||||
struct i2c_adapter adap;
|
||||
void __iomem *regs;
|
||||
struct resource *ioarea;
|
||||
|
||||
unsigned int ack_timeout; //unit: us
|
||||
unsigned int udelay_time; //unit: us
|
||||
|
||||
unsigned int suspended:1;
|
||||
unsigned long scl_rate;
|
||||
unsigned long i2c_rate;
|
||||
struct clk *clk;
|
||||
|
||||
unsigned int mode;
|
||||
int retry;
|
||||
|
||||
int poll_status;
|
||||
|
||||
unsigned int irq;
|
||||
|
||||
spinlock_t cmd_lock;
|
||||
struct completion cmd_complete;
|
||||
enum rk29_event cmd_event;
|
||||
enum rk29_error cmd_err;
|
||||
|
||||
unsigned int msg_idx;
|
||||
unsigned int msg_num;
|
||||
int udelay;
|
||||
int (*io_init)(void);
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
struct notifier_block freq_transition;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(CONFIG_ARCH_RK29) || defined(CONFIG_ARCH_RK30)
|
||||
static struct wake_lock idlelock; /* only for i2c0 */
|
||||
#endif
|
||||
|
||||
static void rk29_set_ack(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
unsigned long conr = readl(i2c->regs + I2C_CONR);
|
||||
|
||||
conr &= I2C_CONR_ACK;
|
||||
writel(conr,i2c->regs + I2C_CONR);
|
||||
return;
|
||||
}
|
||||
static void rk29_set_nak(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
unsigned long conr = readl(i2c->regs + I2C_CONR);
|
||||
|
||||
conr |= I2C_CONR_NAK;
|
||||
writel(conr,i2c->regs + I2C_CONR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static inline void rk29_i2c_disable_irqs(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
tmp = readl(i2c->regs + I2C_IER);
|
||||
writel(tmp & IRQ_ALL_DISABLE, i2c->regs + I2C_IER);
|
||||
}
|
||||
static inline void rk29_i2c_enable_irqs(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
tmp = readl(i2c->regs + I2C_IER);
|
||||
writel(tmp | IRQ_MST_ENABLE, i2c->regs + I2C_IER);
|
||||
}
|
||||
|
||||
/* scl = pclk/(5 *(rem+1) * 2^(exp+1)) */
|
||||
static void rk29_i2c_calcdivisor(unsigned long pclk,
|
||||
unsigned long scl_rate,
|
||||
unsigned long *real_rate,
|
||||
unsigned int *rem, unsigned int *exp)
|
||||
{
|
||||
unsigned int calc_rem = 0;
|
||||
unsigned int calc_exp = 0;
|
||||
|
||||
for(calc_exp = 0; calc_exp < I2CCDVR_EXP_MAX; calc_exp++)
|
||||
{
|
||||
calc_rem = pclk / (5 * scl_rate * (1 <<(calc_exp +1)));
|
||||
if(calc_rem < I2CCDVR_REM_MAX)
|
||||
break;
|
||||
}
|
||||
if(calc_rem >= I2CCDVR_REM_MAX || calc_exp >= I2CCDVR_EXP_MAX)
|
||||
{
|
||||
calc_rem = I2CCDVR_REM_MAX - 1;
|
||||
calc_exp = I2CCDVR_EXP_MAX - 1;
|
||||
}
|
||||
*rem = calc_rem;
|
||||
*exp = calc_exp;
|
||||
*real_rate = pclk/(5 * (calc_rem + 1) * (1 <<(calc_exp +1)));
|
||||
return;
|
||||
}
|
||||
/* set i2c bus scl rate */
|
||||
static void rk29_i2c_clockrate(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
|
||||
struct rk29_i2c_platform_data *pdata = i2c->dev->platform_data;
|
||||
unsigned int rem = 0, exp = 0;
|
||||
unsigned long scl_rate, real_rate = 0, tmp;
|
||||
|
||||
i2c->i2c_rate = clk_get_rate(i2c->clk);
|
||||
|
||||
scl_rate = (i2c->scl_rate) ? i2c->scl_rate : ((pdata->scl_rate)? pdata->scl_rate:100000);
|
||||
|
||||
rk29_i2c_calcdivisor(i2c->i2c_rate, scl_rate, &real_rate, &rem, &exp);
|
||||
|
||||
tmp = readl(i2c->regs + I2C_OPR);
|
||||
tmp &= ~0x3f;
|
||||
tmp |= exp;
|
||||
tmp |= rem<<I2CCDVR_EXP_BITS;
|
||||
writel(tmp, i2c->regs + I2C_OPR);
|
||||
if(real_rate > 400000)
|
||||
dev_warn(i2c->dev, "i2c_rate[%luKhz], scl_rate[%luKhz], real_rate[%luKhz] > 400Khz\n",
|
||||
i2c->i2c_rate/1000, scl_rate/1000, real_rate/1000);
|
||||
else
|
||||
i2c_dbg(i2c->dev, "i2c_rate[%luKhz], scl_rate[%luKhz], real_rate[%luKhz]\n",
|
||||
i2c->i2c_rate/1000, scl_rate/1000, real_rate/1000);
|
||||
return;
|
||||
}
|
||||
static int rk29_event_occurred(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
unsigned long isr, lsr;
|
||||
|
||||
isr = readl(i2c->regs + I2C_ISR);
|
||||
lsr = readl(i2c->regs + I2C_LSR);
|
||||
i2c_dbg(i2c->dev,"event occurred, isr = %lx, lsr = %lx\n", isr, lsr);
|
||||
if(isr & I2C_ISR_ARBITR_LOSE)
|
||||
{
|
||||
writel(0, i2c->regs + I2C_ISR);
|
||||
i2c->cmd_err = RK29_ERROR_ARBITR_LOSE;
|
||||
i2c_err(i2c->dev, "<error>arbitration loss\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch(i2c->cmd_event)
|
||||
{
|
||||
case RK29_EVENT_MTX_RCVD_ACK:
|
||||
if(isr & I2C_ISR_MTX_RCVD_ACK)
|
||||
{
|
||||
isr &= ~I2C_ISR_MTX_RCVD_ACK;
|
||||
writel(isr, i2c->regs + I2C_ISR);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case RK29_EVENT_MRX_NEED_ACK:
|
||||
if(isr & I2C_ISR_MRX_NEED_ACK)
|
||||
{
|
||||
isr &= ~I2C_ISR_MRX_NEED_ACK;
|
||||
writel(isr, i2c->regs + I2C_ISR);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
writel(0, i2c->regs + I2C_ISR);
|
||||
i2c->cmd_err = RK29_ERROR_UNKNOWN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t rk29_i2c_irq(int irq, void *data)
|
||||
{
|
||||
struct rk29_i2c_data *i2c = (struct rk29_i2c_data *)data;
|
||||
int res;
|
||||
|
||||
rk29_i2c_disable_irqs(i2c);
|
||||
spin_lock(&i2c->cmd_lock);
|
||||
res = rk29_event_occurred(i2c);
|
||||
if(res)
|
||||
{
|
||||
if(i2c->mode == I2C_MODE_IRQ)
|
||||
complete(&i2c->cmd_complete);
|
||||
else
|
||||
i2c->poll_status = 1;
|
||||
}
|
||||
spin_unlock(&i2c->cmd_lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
static int wait_for_completion_poll_timeout(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
int tmo = RK29_I2C_ACK_TIMEOUT_COUNT;
|
||||
|
||||
while(--tmo)
|
||||
{
|
||||
if(i2c->poll_status == 1)
|
||||
return 1;
|
||||
udelay(i2c->udelay_time);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int rk29_wait_event(struct rk29_i2c_data *i2c,
|
||||
enum rk29_event mr_event)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if(unlikely(irqs_disabled()))
|
||||
{
|
||||
i2c_err(i2c->dev, "irqs are disabled on this system!\n");
|
||||
return -EIO;
|
||||
}
|
||||
i2c->cmd_err = RK29_ERROR_NONE;
|
||||
i2c->cmd_event = mr_event;
|
||||
rk29_i2c_enable_irqs(i2c);
|
||||
if(i2c->mode == I2C_MODE_IRQ)
|
||||
{
|
||||
ret = wait_for_completion_interruptible_timeout(&i2c->cmd_complete,
|
||||
usecs_to_jiffies(i2c->ack_timeout));
|
||||
}
|
||||
else
|
||||
{
|
||||
i2c->poll_status = 0;
|
||||
ret = wait_for_completion_poll_timeout(i2c);
|
||||
}
|
||||
if(ret < 0)
|
||||
{
|
||||
i2c_err(i2c->dev, "i2c wait for event %04x, retrun %d \n", mr_event, ret);
|
||||
return ret;
|
||||
}
|
||||
if(ret == 0)
|
||||
{
|
||||
i2c_err(i2c->dev, "i2c wait for envent timeout, but not return -ETIMEDOUT\n");
|
||||
return 0;
|
||||
//return -ETIMEDOUT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rk29_i2c_stop(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
int tmo = RK29_I2C_STOP_TIMEOUT_COUNT;
|
||||
|
||||
writel(I2C_LCMR_STOP|I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
|
||||
while(--tmo && !(readl(i2c->regs + I2C_LCMR) & I2C_LCMR_STOP))
|
||||
{
|
||||
udelay(i2c->udelay_time);
|
||||
}
|
||||
writel(0, i2c->regs + I2C_ISR);
|
||||
rk29_i2c_disable_irqs(i2c);
|
||||
|
||||
udelay(tmo);
|
||||
return;
|
||||
}
|
||||
static void rk29_wait_while_busy(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
int tmo = RK29_I2C_START_TIMEOUT_COUNT;
|
||||
|
||||
while(--tmo && (readl(i2c->regs + I2C_LSR) & I2C_LSR_BUSY))
|
||||
{
|
||||
udelay(i2c->udelay_time);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int rk29_send_2nd_addr(struct rk29_i2c_data *i2c,
|
||||
struct i2c_msg *msg, int start)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long lsr;
|
||||
unsigned long addr_2nd = msg->addr & 0xff;
|
||||
|
||||
i2c_dbg(i2c->dev, "i2c send addr_2nd: %lx\n", addr_2nd);
|
||||
writel(addr_2nd, i2c->regs + I2C_MTXR);
|
||||
if(i2c->mode == I2C_MODE_IRQ)
|
||||
INIT_COMPLETION(i2c->cmd_complete);
|
||||
writel(I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
|
||||
rk29_set_ack(i2c);
|
||||
|
||||
if((ret = rk29_wait_event(i2c, RK29_EVENT_MTX_RCVD_ACK)) != 0)
|
||||
{
|
||||
i2c_err(i2c->dev, "after sent addr_2nd, i2c wait for ACK timeout\n");
|
||||
return ret;
|
||||
}
|
||||
lsr = readl(i2c->regs + I2C_LSR);
|
||||
if((lsr & I2C_LSR_RCV_NAK) && !(msg->flags & I2C_M_IGNORE_NAK))
|
||||
return -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
static int rk29_send_address(struct rk29_i2c_data *i2c,
|
||||
struct i2c_msg *msg, int start)
|
||||
{
|
||||
unsigned long addr_1st;
|
||||
unsigned long conr,lsr;
|
||||
int ret = 0;
|
||||
|
||||
if(msg->flags & I2C_M_TEN)
|
||||
addr_1st = (0xf0 | (((unsigned long) msg->addr & 0x300) >> 7)) & 0xff;
|
||||
else
|
||||
addr_1st = ((msg->addr << 1) & 0xff);
|
||||
|
||||
if (msg->flags & I2C_M_RD)
|
||||
addr_1st |= 0x01;
|
||||
else
|
||||
addr_1st &= (~0x01);
|
||||
|
||||
if(start)
|
||||
rk29_wait_while_busy(i2c);
|
||||
|
||||
writel(0, i2c->regs + I2C_ISR);
|
||||
conr = readl(i2c->regs + I2C_CONR);
|
||||
conr |= I2C_CONR_MTX_MODE;
|
||||
conr |= I2C_CONR_MPORT_ENABLE;
|
||||
writel(conr, i2c->regs + I2C_CONR);
|
||||
|
||||
i2c_dbg(i2c->dev, "i2c send addr_1st: %lx\n", addr_1st);
|
||||
writel(addr_1st, i2c->regs + I2C_MTXR);
|
||||
rk29_set_ack(i2c);
|
||||
if(i2c->mode == I2C_MODE_IRQ)
|
||||
INIT_COMPLETION(i2c->cmd_complete);
|
||||
writel(I2C_LCMR_START|I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
|
||||
|
||||
if((ret = rk29_wait_event(i2c, RK29_EVENT_MTX_RCVD_ACK)) != 0)
|
||||
{
|
||||
i2c_err(i2c->dev, "after sent addr_1st, i2c wait for ACK timeout\n");
|
||||
return ret;
|
||||
}
|
||||
lsr = readl(i2c->regs + I2C_LSR);
|
||||
if((lsr & I2C_LSR_RCV_NAK) && !(msg->flags & I2C_M_IGNORE_NAK))
|
||||
{
|
||||
dev_info(i2c->dev, "addr: 0x%x receive no ack\n", msg->addr);
|
||||
return -EAGAIN;
|
||||
}
|
||||
if(start && (msg->flags & I2C_M_TEN))
|
||||
ret = rk29_send_2nd_addr(i2c, msg, start);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rk29_i2c_send_msg(struct rk29_i2c_data *i2c, struct i2c_msg *msg)
|
||||
{
|
||||
int i, ret = 0;
|
||||
unsigned long conr, lsr;
|
||||
|
||||
conr = readl(i2c->regs + I2C_CONR);
|
||||
conr |= I2C_CONR_MTX_MODE;
|
||||
//conr |= I2C_CONR_MPORT_ENABLE;
|
||||
writel(conr, i2c->regs + I2C_CONR);
|
||||
|
||||
for(i = 0; i < msg->len; i++)
|
||||
{
|
||||
i2c_dbg(i2c->dev, "i2c send buf[%d]: %x\n", i, msg->buf[i]);
|
||||
writel(msg->buf[i], i2c->regs + I2C_MTXR);
|
||||
rk29_set_ack(i2c);
|
||||
if(i2c->mode == I2C_MODE_IRQ)
|
||||
INIT_COMPLETION(i2c->cmd_complete);
|
||||
writel(I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
|
||||
|
||||
if((ret = rk29_wait_event(i2c, RK29_EVENT_MTX_RCVD_ACK)) != 0)
|
||||
return ret;
|
||||
lsr = readl(i2c->regs + I2C_LSR);
|
||||
if((lsr & I2C_LSR_RCV_NAK) && (i != msg->len -1) && !(msg->flags & I2C_M_IGNORE_NAK))
|
||||
return -EINVAL;
|
||||
udelay(i2c->udelay);
|
||||
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static int rk29_i2c_recv_msg(struct rk29_i2c_data *i2c, struct i2c_msg *msg)
|
||||
{
|
||||
int i, ret = 0;
|
||||
unsigned long conr;
|
||||
|
||||
conr = readl(i2c->regs + I2C_CONR);
|
||||
conr &= I2C_CONR_MRX_MODE;
|
||||
//conr |= I2C_CONR_MPORT_ENABLE;
|
||||
writel(conr, i2c->regs + I2C_CONR);
|
||||
|
||||
for(i = 0; i < msg->len; i++)
|
||||
{
|
||||
if(i2c->mode == I2C_MODE_IRQ)
|
||||
INIT_COMPLETION(i2c->cmd_complete);
|
||||
writel(I2C_LCMR_RESUME, i2c->regs + I2C_LCMR);
|
||||
if((ret = rk29_wait_event(i2c, RK29_EVENT_MRX_NEED_ACK)) != 0)
|
||||
return ret;
|
||||
msg->buf[i] = (uint8_t)readl(i2c->regs + I2C_MRXR);
|
||||
|
||||
if( i == msg->len -1)
|
||||
rk29_set_nak(i2c);
|
||||
else
|
||||
rk29_set_ack(i2c);
|
||||
udelay(i2c->udelay);
|
||||
i2c_dbg(i2c->dev, "i2c recv >>>>>>>>>>>> buf[%d]: %x\n", i, msg->buf[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static int rk29_xfer_msg(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msg, int start, int stop)
|
||||
{
|
||||
struct rk29_i2c_data *i2c = (struct rk29_i2c_data *)adap->algo_data;
|
||||
int ret = 0;
|
||||
|
||||
if(msg->len == 0)
|
||||
{
|
||||
ret = -EINVAL;
|
||||
i2c_err(i2c->dev, "<error>msg->len = %d\n", msg->len);
|
||||
goto exit;
|
||||
}
|
||||
if(msg->flags & I2C_M_NEED_DELAY)
|
||||
i2c->udelay = msg->udelay;
|
||||
else
|
||||
i2c->udelay = 0;
|
||||
if((ret = rk29_send_address(i2c, msg, start))!= 0)
|
||||
{
|
||||
rk29_set_nak(i2c);
|
||||
i2c_err(i2c->dev, "<error>rk29_send_address timeout\n");
|
||||
goto exit;
|
||||
}
|
||||
if(msg->flags & I2C_M_RD)
|
||||
{
|
||||
if(msg->flags & I2C_M_REG8_DIRECT)
|
||||
{
|
||||
struct i2c_msg msg1 = *msg;
|
||||
struct i2c_msg msg2 = *msg;
|
||||
msg1.len = 1;
|
||||
msg2.len = msg->len - 1;
|
||||
msg2.buf = msg->buf + 1;
|
||||
|
||||
if((ret = rk29_i2c_send_msg(i2c, &msg1)) != 0)
|
||||
i2c_err(i2c->dev, "<error>rk29_i2c_send_msg timeout\n");
|
||||
if((ret = rk29_i2c_recv_msg(i2c, &msg2)) != 0)
|
||||
{
|
||||
i2c_err(i2c->dev, "<error>rk29_i2c_recv_msg timeout\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
}
|
||||
else if((ret = rk29_i2c_recv_msg(i2c, msg)) != 0)
|
||||
{
|
||||
i2c_err(i2c->dev, "<error>rk29_i2c_recv_msg timeout\n");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if((ret = rk29_i2c_send_msg(i2c, msg)) != 0)
|
||||
{
|
||||
rk29_set_nak(i2c);
|
||||
i2c_err(i2c->dev, "<error>rk29_i2c_send_msg timeout\n");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if(stop || ret < 0)
|
||||
{
|
||||
rk29_i2c_stop(i2c);
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int rk29_i2c_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
int ret = -1;
|
||||
int i;
|
||||
struct rk29_i2c_data *i2c = (struct rk29_i2c_data *)adap->algo_data;
|
||||
|
||||
//int retry = i2c->retry;
|
||||
/*
|
||||
if(i2c->suspended ==1)
|
||||
return -EIO;
|
||||
*/
|
||||
// 400k > scl_rate > 10k
|
||||
if(msgs[0].scl_rate <= 400000 && msgs[0].scl_rate >= 10000)
|
||||
i2c->scl_rate = msgs[0].scl_rate;
|
||||
else if(msgs[0].scl_rate > 400000){
|
||||
dev_info(i2c->dev, "Warning: msg[0].scl_rate( = %dKhz) is too high!",
|
||||
msgs[0].scl_rate/1000);
|
||||
i2c->scl_rate = 400000;
|
||||
}
|
||||
else{
|
||||
dev_info(i2c->dev, "Warning: msg[0].scl_rate( = %dKhz) is too low!",
|
||||
msgs[0].scl_rate/1000);
|
||||
i2c->scl_rate = 10000;
|
||||
}
|
||||
rk29_i2c_clockrate(i2c);
|
||||
|
||||
i2c->udelay_time = RK29_UDELAY_TIME(i2c->scl_rate);
|
||||
i2c->ack_timeout = RK29_I2C_ACK_TIMEOUT_COUNT * i2c->udelay_time;
|
||||
|
||||
#if defined(CONFIG_ARCH_RK29) || defined(CONFIG_ARCH_RK30)
|
||||
if (adap->nr == 0)
|
||||
wake_lock(&idlelock);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
ret = rk29_xfer_msg(adap, &msgs[i], (i == 0), (i == (num - 1)));
|
||||
if (ret != 0)
|
||||
{
|
||||
num = ret;
|
||||
i2c_err(i2c->dev, "rk29_xfer_msg error, ret = %d\n", ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ARCH_RK29) || defined(CONFIG_ARCH_RK30)
|
||||
if (adap->nr == 0)
|
||||
wake_unlock(&idlelock);
|
||||
#endif
|
||||
|
||||
/*
|
||||
if( --retry && num < 0)
|
||||
{
|
||||
udelay(10000 * 1000/i2c->scl_rate);
|
||||
goto retry;
|
||||
}
|
||||
*/
|
||||
if(num < 0)
|
||||
dev_err(i2c->dev, "i2c transfer err, client address is 0x%x [20110106]\n", msgs[0].addr);
|
||||
return num;
|
||||
}
|
||||
|
||||
static u32 rk29_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm rk29_i2c_algorithm = {
|
||||
.master_xfer = rk29_i2c_xfer,
|
||||
.functionality = rk29_i2c_func,
|
||||
};
|
||||
|
||||
int i2c_suspended(struct i2c_adapter *adap)
|
||||
{
|
||||
struct rk29_i2c_data *i2c = (struct rk29_i2c_data *)adap->algo_data;
|
||||
if(adap->nr > 1)
|
||||
return 1;
|
||||
if(i2c == NULL)
|
||||
return 1;
|
||||
return i2c->suspended;
|
||||
}
|
||||
EXPORT_SYMBOL(i2c_suspended);
|
||||
|
||||
static void rk29_i2c_deinit_hw(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
unsigned long opr = readl(i2c->regs + I2C_OPR);
|
||||
|
||||
opr &= ~I2C_OPR_RESET_STATUS;
|
||||
writel(opr, i2c->regs);
|
||||
return;
|
||||
}
|
||||
static void rk29_i2c_init_hw(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
unsigned long opr = readl(i2c->regs + I2C_OPR);
|
||||
|
||||
opr |= I2C_OPR_RESET_STATUS;
|
||||
writel(opr, i2c->regs + I2C_OPR);
|
||||
|
||||
udelay(10);
|
||||
opr = readl(i2c->regs + I2C_OPR);
|
||||
opr &= ~I2C_OPR_RESET_STATUS;
|
||||
writel(opr, i2c->regs + I2C_OPR);
|
||||
|
||||
rk29_i2c_clockrate(i2c);
|
||||
|
||||
rk29_i2c_disable_irqs(i2c);
|
||||
writel(0, i2c->regs + I2C_LCMR);
|
||||
writel(0, i2c->regs + I2C_LCMR);
|
||||
|
||||
opr = readl(i2c->regs + I2C_OPR);
|
||||
opr |= I2C_OPR_CORE_ENABLE;
|
||||
writel(opr, i2c->regs + I2C_OPR);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
|
||||
#define freq_to_i2c(_n) container_of(_n, struct rk29_i2c_data, freq_transition)
|
||||
|
||||
static int rk29_i2c_cpufreq_transition(struct notifier_block *nb,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct rk29_i2c_data *i2c = freq_to_i2c(nb);
|
||||
unsigned long flags;
|
||||
int delta_f;
|
||||
delta_f = clk_get_rate(i2c->clk) - i2c->i2c_rate;
|
||||
|
||||
if ((val == CPUFREQ_POSTCHANGE && delta_f < 0) ||
|
||||
(val == CPUFREQ_PRECHANGE && delta_f > 0))
|
||||
{
|
||||
spin_lock_irqsave(&i2c->cmd_lock, flags);
|
||||
rk29_i2c_clockrate(i2c);
|
||||
spin_unlock_irqrestore(&i2c->cmd_lock, flags);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int rk29_i2c_register_cpufreq(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
if (i2c->adap.nr != 0)
|
||||
return 0;
|
||||
i2c->freq_transition.notifier_call = rk29_i2c_cpufreq_transition;
|
||||
|
||||
return cpufreq_register_notifier(&i2c->freq_transition,
|
||||
CPUFREQ_TRANSITION_NOTIFIER);
|
||||
}
|
||||
|
||||
static inline void rk29_i2c_unregister_cpufreq(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
if (i2c->adap.nr != 0)
|
||||
return;
|
||||
cpufreq_unregister_notifier(&i2c->freq_transition,
|
||||
CPUFREQ_TRANSITION_NOTIFIER);
|
||||
}
|
||||
|
||||
#else
|
||||
static inline int rk29_i2c_register_cpufreq(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void rk29_i2c_unregister_cpufreq(struct rk29_i2c_data *i2c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int rk29_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rk29_i2c_data *i2c;
|
||||
struct rk29_i2c_platform_data *pdata = NULL;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (!pdata)
|
||||
{
|
||||
i2c_err(&pdev->dev, "<error>no platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
i2c = kzalloc(sizeof(struct rk29_i2c_data), GFP_KERNEL);
|
||||
if (!i2c)
|
||||
{
|
||||
i2c_err(&pdev->dev, "<error>no memory for state\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
init_completion(&i2c->cmd_complete);
|
||||
i2c->retry = RETRY_NUM;
|
||||
i2c->mode = pdata->mode;
|
||||
i2c->scl_rate = (pdata->scl_rate) ? pdata->scl_rate : 100000;
|
||||
|
||||
strlcpy(i2c->adap.name, DRV_NAME, sizeof(i2c->adap.name));
|
||||
i2c->adap.owner = THIS_MODULE;
|
||||
i2c->adap.algo = &rk29_i2c_algorithm;
|
||||
i2c->adap.class = I2C_CLASS_HWMON;
|
||||
i2c->adap.nr = pdata->bus_num;
|
||||
i2c->adap.retries = 3;
|
||||
i2c->adap.timeout = msecs_to_jiffies(500);
|
||||
|
||||
spin_lock_init(&i2c->cmd_lock);
|
||||
|
||||
i2c->dev = &pdev->dev;
|
||||
|
||||
i2c->clk = clk_get(&pdev->dev, "i2c");
|
||||
if (IS_ERR(i2c->clk)) {
|
||||
i2c_err(&pdev->dev, "<error>cannot get clock\n");
|
||||
ret = -ENOENT;
|
||||
goto err_noclk;
|
||||
}
|
||||
|
||||
clk_enable(i2c->clk);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res == NULL) {
|
||||
i2c_err(&pdev->dev, "<error>cannot find IO resource\n");
|
||||
ret = -ENOENT;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
i2c->ioarea = request_mem_region(res->start, res->end - res->start + 1,
|
||||
pdev->name);
|
||||
|
||||
if (i2c->ioarea == NULL) {
|
||||
i2c_err(&pdev->dev, "<error>cannot request IO\n");
|
||||
ret = -ENXIO;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
i2c->regs = ioremap(res->start, res->end - res->start + 1);
|
||||
|
||||
if (i2c->regs == NULL) {
|
||||
i2c_err(&pdev->dev, "<error>annot map IO\n");
|
||||
ret = -ENXIO;
|
||||
goto err_ioarea;
|
||||
}
|
||||
i2c->adap.algo_data = i2c;
|
||||
i2c->adap.dev.parent = &pdev->dev;
|
||||
|
||||
if(pdata->io_init)
|
||||
{
|
||||
i2c->io_init = pdata->io_init;
|
||||
pdata->io_init();
|
||||
}
|
||||
|
||||
i2c->irq = ret = platform_get_irq(pdev, 0);
|
||||
if (ret <= 0) {
|
||||
i2c_err(&pdev->dev, "cannot find IRQ\n");
|
||||
goto err_iomap;
|
||||
}
|
||||
ret = request_irq(i2c->irq, rk29_i2c_irq, IRQF_DISABLED,
|
||||
dev_name(&pdev->dev), i2c);
|
||||
|
||||
if (ret != 0) {
|
||||
i2c_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
|
||||
goto err_iomap;
|
||||
}
|
||||
ret = rk29_i2c_register_cpufreq(i2c);
|
||||
if (ret < 0) {
|
||||
i2c_err(&pdev->dev, "failed to register cpufreq notifier\n");
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
ret = i2c_add_numbered_adapter(&i2c->adap);
|
||||
if (ret < 0) {
|
||||
i2c_err(&pdev->dev, "failed to add bus to i2c core\n");
|
||||
goto err_cpufreq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, i2c);
|
||||
rk29_i2c_init_hw(i2c);
|
||||
|
||||
dev_info(&pdev->dev, "%s: RK29 I2C adapter\n", dev_name(&i2c->adap.dev));
|
||||
return 0;
|
||||
|
||||
err_cpufreq:
|
||||
rk29_i2c_unregister_cpufreq(i2c);
|
||||
|
||||
err_irq:
|
||||
free_irq(i2c->irq, i2c);
|
||||
|
||||
err_iomap:
|
||||
iounmap(i2c->regs);
|
||||
|
||||
err_ioarea:
|
||||
release_resource(i2c->ioarea);
|
||||
kfree(i2c->ioarea);
|
||||
|
||||
err_clk:
|
||||
clk_disable(i2c->clk);
|
||||
clk_put(i2c->clk);
|
||||
|
||||
err_noclk:
|
||||
kfree(i2c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int rk29_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rk29_i2c_data *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
|
||||
rk29_i2c_deinit_hw(i2c);
|
||||
rk29_i2c_unregister_cpufreq(i2c);
|
||||
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
free_irq(i2c->irq, i2c);
|
||||
|
||||
clk_disable(i2c->clk);
|
||||
clk_put(i2c->clk);
|
||||
|
||||
iounmap(i2c->regs);
|
||||
|
||||
release_resource(i2c->ioarea);
|
||||
kfree(i2c->ioarea);
|
||||
kfree(i2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int rk29_i2c_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct rk29_i2c_data *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
i2c->suspended = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk29_i2c_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct rk29_i2c_data *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
i2c->suspended = 0;
|
||||
rk29_i2c_init_hw(i2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_pm_ops rk29_i2c_pm_ops = {
|
||||
.suspend_noirq = rk29_i2c_suspend_noirq,
|
||||
.resume_noirq = rk29_i2c_resume_noirq,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct platform_driver rk29_i2c_driver = {
|
||||
.probe = rk29_i2c_probe,
|
||||
.remove = rk29_i2c_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRV_NAME,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &rk29_i2c_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static int __init rk29_i2c_adap_init(void)
|
||||
{
|
||||
#if defined(CONFIG_ARCH_RK29) || defined(CONFIG_ARCH_RK30)
|
||||
wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "i2c0");
|
||||
#endif
|
||||
return platform_driver_register(&rk29_i2c_driver);
|
||||
}
|
||||
|
||||
static void __exit rk29_i2c_adap_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&rk29_i2c_driver);
|
||||
}
|
||||
|
||||
subsys_initcall(rk29_i2c_adap_init);
|
||||
module_exit(rk29_i2c_adap_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Driver for RK29 I2C Bus");
|
||||
MODULE_AUTHOR("kfx, kfx@rock-chips.com");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/* drivers/i2c/busses/i2c_rk2818.h
|
||||
*
|
||||
* Copyright (C) 2010 ROCKCHIP, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __RK2818_I2C_H
|
||||
#define __RK2818_I2C_H
|
||||
|
||||
/* master transmit */
|
||||
#define I2C_MTXR (0x0000)
|
||||
/* master receive */
|
||||
#define I2C_MRXR (0x0004)
|
||||
/* slave address */
|
||||
#define I2C_SADDR (0x0010)
|
||||
/* interrupt enable control */
|
||||
#define I2C_IER (0x0014)
|
||||
#define I2C_IER_ARBITR_LOSE (1<<7)
|
||||
#define I2C_IER_MRX_NEED_ACK (1<<1)
|
||||
#define I2C_IER_MTX_RCVD_ACK (1<<0)
|
||||
|
||||
#define IRQ_MST_ENABLE (I2C_IER_ARBITR_LOSE | \
|
||||
I2C_IER_MRX_NEED_ACK | \
|
||||
I2C_IER_MTX_RCVD_ACK)
|
||||
#define IRQ_ALL_DISABLE (0x00)
|
||||
|
||||
/* interrupt status, write 0 to clear */
|
||||
#define I2C_ISR (0x0018)
|
||||
#define I2C_ISR_ARBITR_LOSE (1<<7)
|
||||
#define I2C_ISR_MRX_NEED_ACK (1<<1)
|
||||
#define I2C_ISR_MTX_RCVD_ACK (1<<0)
|
||||
|
||||
/* stop/start/resume command, write 1 to set */
|
||||
#define I2C_LCMR (0x001c)
|
||||
#define I2C_LCMR_RESUME (1<<2)
|
||||
#define I2C_LCMR_STOP (1<<1)
|
||||
#define I2C_LCMR_START (1<<0)
|
||||
|
||||
/* i2c core status */
|
||||
#define I2C_LSR (0x0020)
|
||||
#define I2C_LSR_RCV_NAK (1<<1)
|
||||
#define I2C_LSR_RCV_ACK (~(1<<1))
|
||||
#define I2C_LSR_BUSY (1<<0)
|
||||
|
||||
/* i2c config */
|
||||
#define I2C_CONR (0x0024)
|
||||
#define I2C_CONR_NAK (1<<4)
|
||||
#define I2C_CONR_ACK (~(1<<4))
|
||||
#define I2C_CONR_MTX_MODE (1<<3)
|
||||
#define I2C_CONR_MRX_MODE (~(1<<3))
|
||||
#define I2C_CONR_MPORT_ENABLE (1<<2)
|
||||
#define I2C_CONR_MPORT_DISABLE (~(1<<2))
|
||||
|
||||
/* i2c core config */
|
||||
#define I2C_OPR (0x0028)
|
||||
#define I2C_OPR_RESET_STATUS (1<<7)
|
||||
#define I2C_OPR_CORE_ENABLE (1<<6)
|
||||
|
||||
#define I2CCDVR_REM_BITS (0x03)
|
||||
#define I2CCDVR_REM_MAX (1<<(I2CCDVR_REM_BITS))
|
||||
#define I2CCDVR_EXP_BITS (0x03)
|
||||
#define I2CCDVR_EXP_MAX (1<<(I2CCDVR_EXP_BITS))
|
||||
|
||||
#endif
|
||||
|
|
@ -1,708 +0,0 @@
|
|||
/* drivers/i2c/busses/i2c-rk30-adapter.c
|
||||
*
|
||||
* Copyright (C) 2012 ROCKCHIP, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
#include "i2c-rk30.h"
|
||||
|
||||
#define COMPLETE_READ (1<<STATE_START|1<<STATE_READ|1<<STATE_STOP)
|
||||
#define COMPLETE_WRITE (1<<STATE_START|1<<STATE_WRITE|1<<STATE_STOP)
|
||||
|
||||
/* Control register */
|
||||
#define I2C_CON 0x000
|
||||
#define I2C_CON_EN (1 << 0)
|
||||
#define I2C_CON_MOD(mod) ((mod) << 1)
|
||||
#define I2C_CON_MASK (3 << 1)
|
||||
enum{
|
||||
I2C_CON_MOD_TX = 0,
|
||||
I2C_CON_MOD_TRX,
|
||||
I2C_CON_MOD_RX,
|
||||
I2C_CON_MOD_RRX,
|
||||
};
|
||||
#define I2C_CON_START (1 << 3)
|
||||
#define I2C_CON_STOP (1 << 4)
|
||||
#define I2C_CON_LASTACK (1 << 5)
|
||||
#define I2C_CON_ACTACK (1 << 6)
|
||||
|
||||
/* Clock dividor register */
|
||||
#define I2C_CLKDIV 0x004
|
||||
#define I2C_CLKDIV_VAL(divl, divh) (((divl) & 0xffff) | (((divh) << 16) & 0xffff0000))
|
||||
|
||||
/* the slave address accessed for master rx mode */
|
||||
#define I2C_MRXADDR 0x008
|
||||
#define I2C_MRXADDR_LOW (1 << 24)
|
||||
#define I2C_MRXADDR_MID (1 << 25)
|
||||
#define I2C_MRXADDR_HIGH (1 << 26)
|
||||
|
||||
/* the slave register address accessed for master rx mode */
|
||||
#define I2C_MRXRADDR 0x00c
|
||||
#define I2C_MRXRADDR_LOW (1 << 24)
|
||||
#define I2C_MRXRADDR_MID (1 << 25)
|
||||
#define I2C_MRXRADDR_HIGH (1 << 26)
|
||||
|
||||
/* master tx count */
|
||||
#define I2C_MTXCNT 0x010
|
||||
|
||||
/* master rx count */
|
||||
#define I2C_MRXCNT 0x014
|
||||
|
||||
/* interrupt enable register */
|
||||
#define I2C_IEN 0x018
|
||||
#define I2C_BTFIEN (1 << 0)
|
||||
#define I2C_BRFIEN (1 << 1)
|
||||
#define I2C_MBTFIEN (1 << 2)
|
||||
#define I2C_MBRFIEN (1 << 3)
|
||||
#define I2C_STARTIEN (1 << 4)
|
||||
#define I2C_STOPIEN (1 << 5)
|
||||
#define I2C_NAKRCVIEN (1 << 6)
|
||||
#define IRQ_MST_ENABLE (I2C_MBTFIEN | I2C_MBRFIEN | I2C_NAKRCVIEN | I2C_STARTIEN | I2C_STOPIEN)
|
||||
#define IRQ_ALL_DISABLE 0
|
||||
|
||||
/* interrupt pending register */
|
||||
#define I2C_IPD 0x01c
|
||||
#define I2C_BTFIPD (1 << 0)
|
||||
#define I2C_BRFIPD (1 << 1)
|
||||
#define I2C_MBTFIPD (1 << 2)
|
||||
#define I2C_MBRFIPD (1 << 3)
|
||||
#define I2C_STARTIPD (1 << 4)
|
||||
#define I2C_STOPIPD (1 << 5)
|
||||
#define I2C_NAKRCVIPD (1 << 6)
|
||||
|
||||
#define I2C_HOLD_SCL (1 << 7)
|
||||
#define I2C_IPD_ALL_CLEAN 0x7f
|
||||
|
||||
/* finished count */
|
||||
#define I2C_FCNT 0x020
|
||||
|
||||
/* I2C tx data register */
|
||||
#define I2C_TXDATA_BASE 0X100
|
||||
|
||||
/* I2C rx data register */
|
||||
#define I2C_RXDATA_BASE 0x200
|
||||
|
||||
|
||||
static void rk30_show_regs(struct rk30_i2c *i2c)
|
||||
{
|
||||
int i;
|
||||
dev_info(i2c->dev, "i2c->clk = %lu\n", clk_get_rate(i2c->clk));
|
||||
dev_info(i2c->dev, "i2c->start = %d\n", i2c->state);
|
||||
dev_info(i2c->dev, "I2C_CON: 0x%08x\n", i2c_readl(i2c->regs + I2C_CON));
|
||||
dev_info(i2c->dev, "I2C_CLKDIV: 0x%08x\n", i2c_readl(i2c->regs + I2C_CLKDIV));
|
||||
dev_info(i2c->dev, "I2C_MRXADDR: 0x%08x\n", i2c_readl(i2c->regs + I2C_MRXADDR));
|
||||
dev_info(i2c->dev, "I2C_MRXRADDR: 0x%08x\n", i2c_readl(i2c->regs + I2C_MRXRADDR));
|
||||
dev_info(i2c->dev, "I2C_MTXCNT: 0x%08x\n", i2c_readl(i2c->regs + I2C_MTXCNT));
|
||||
dev_info(i2c->dev, "I2C_MRXCNT: 0x%08x\n", i2c_readl(i2c->regs + I2C_MRXCNT));
|
||||
dev_info(i2c->dev, "I2C_IEN: 0x%08x\n", i2c_readl(i2c->regs + I2C_IEN));
|
||||
dev_info(i2c->dev, "I2C_IPD: 0x%08x\n", i2c_readl(i2c->regs + I2C_IPD));
|
||||
dev_info(i2c->dev, "I2C_FCNT: 0x%08x\n", i2c_readl(i2c->regs + I2C_FCNT));
|
||||
for( i = 0; i < 8; i ++)
|
||||
dev_info(i2c->dev, "I2C_TXDATA%d: 0x%08x\n", i, i2c_readl(i2c->regs + I2C_TXDATA_BASE + i * 4));
|
||||
for( i = 0; i < 8; i ++)
|
||||
dev_info(i2c->dev, "I2C_RXDATA%d: 0x%08x\n", i, i2c_readl(i2c->regs + I2C_RXDATA_BASE + i * 4));
|
||||
}
|
||||
|
||||
static int rk30_i2c_check_idle(struct rk30_i2c *i2c)
|
||||
{
|
||||
int ret = 0;
|
||||
int sda_io, scl_io;
|
||||
int sda_lev, scl_lev;
|
||||
|
||||
sda_io = iomux_mode_to_gpio(i2c->sda_mode);
|
||||
scl_io = iomux_mode_to_gpio(i2c->scl_mode);
|
||||
|
||||
ret = gpio_request(sda_io, NULL);
|
||||
if(unlikely(ret < 0)){
|
||||
dev_err(i2c->dev, "Failed to request gpio: SDA_GPIO\n");
|
||||
return ret;
|
||||
}
|
||||
ret = gpio_request(scl_io, NULL);
|
||||
if(unlikely(ret < 0)){
|
||||
dev_err(i2c->dev, "Failed to request gpio: SCL_GPIO\n");
|
||||
gpio_free(sda_io);
|
||||
return ret;
|
||||
}
|
||||
gpio_direction_input(sda_io);
|
||||
gpio_direction_input(scl_io);
|
||||
|
||||
sda_lev = gpio_get_value(sda_io);
|
||||
scl_lev = gpio_get_value(scl_io);
|
||||
|
||||
gpio_free(sda_io);
|
||||
gpio_free(scl_io);
|
||||
|
||||
iomux_set(i2c->sda_mode);
|
||||
iomux_set(i2c->scl_mode);
|
||||
|
||||
if(sda_lev == 1 && scl_lev == 1)
|
||||
return I2C_IDLE;
|
||||
else if(sda_lev == 0 && scl_lev == 1)
|
||||
return I2C_SDA_LOW;
|
||||
else if(sda_lev == 1 && scl_lev == 0)
|
||||
return I2C_SCL_LOW;
|
||||
else
|
||||
return BOTH_LOW;
|
||||
}
|
||||
static inline void rk30_i2c_enable(struct rk30_i2c *i2c, unsigned int lastnak)
|
||||
{
|
||||
unsigned int con = 0;
|
||||
|
||||
con |= I2C_CON_EN;
|
||||
con |= I2C_CON_MOD(i2c->mode);
|
||||
if(lastnak)
|
||||
con |= I2C_CON_LASTACK;
|
||||
con |= I2C_CON_START;
|
||||
i2c_writel(con, i2c->regs + I2C_CON);
|
||||
}
|
||||
static inline void rk30_i2c_disable(struct rk30_i2c *i2c)
|
||||
{
|
||||
i2c_writel( 0, i2c->regs + I2C_CON);
|
||||
}
|
||||
|
||||
static inline void rk30_i2c_clean_start(struct rk30_i2c *i2c)
|
||||
{
|
||||
unsigned int con = i2c_readl(i2c->regs + I2C_CON);
|
||||
|
||||
con &= ~I2C_CON_START;
|
||||
i2c_writel(con, i2c->regs + I2C_CON);
|
||||
}
|
||||
static inline void rk30_i2c_send_start(struct rk30_i2c *i2c)
|
||||
{
|
||||
unsigned int con = i2c_readl(i2c->regs + I2C_CON);
|
||||
|
||||
con |= I2C_CON_START;
|
||||
if(con & I2C_CON_STOP)
|
||||
dev_warn(i2c->dev, "I2C_CON: stop bit is set\n");
|
||||
|
||||
i2c_writel(con, i2c->regs + I2C_CON);
|
||||
}
|
||||
static inline void rk30_i2c_send_stop(struct rk30_i2c *i2c)
|
||||
{
|
||||
unsigned int con = i2c_readl(i2c->regs + I2C_CON);
|
||||
|
||||
con |= I2C_CON_STOP;
|
||||
if(con & I2C_CON_START)
|
||||
dev_warn(i2c->dev, "I2C_CON: start bit is set\n");
|
||||
|
||||
i2c_writel(con, i2c->regs + I2C_CON);
|
||||
}
|
||||
static inline void rk30_i2c_clean_stop(struct rk30_i2c *i2c)
|
||||
{
|
||||
unsigned int con = i2c_readl(i2c->regs + I2C_CON);
|
||||
|
||||
con &= ~I2C_CON_STOP;
|
||||
i2c_writel(con, i2c->regs + I2C_CON);
|
||||
}
|
||||
|
||||
static inline void rk30_i2c_disable_irq(struct rk30_i2c *i2c)
|
||||
{
|
||||
i2c_writel(IRQ_ALL_DISABLE, i2c->regs + I2C_IEN);
|
||||
}
|
||||
|
||||
static inline void rk30_i2c_enable_irq(struct rk30_i2c *i2c)
|
||||
{
|
||||
i2c_writel(IRQ_MST_ENABLE, i2c->regs + I2C_IEN);
|
||||
}
|
||||
static void rk30_get_div(int div, int *divh, int *divl)
|
||||
{
|
||||
if(div % 2 == 0){
|
||||
*divh = div/2;
|
||||
*divl = div/2;
|
||||
}else{
|
||||
*divh = rk30_ceil(div, 2);
|
||||
*divl = div/2;
|
||||
}
|
||||
}
|
||||
/* SCL Divisor = 8 * (CLKDIVL+1 + CLKDIVH+1)
|
||||
* SCL = i2c_rate/ SCLK Divisor
|
||||
*/
|
||||
static void rk30_i2c_set_clk(struct rk30_i2c *i2c, unsigned long scl_rate)
|
||||
{
|
||||
unsigned long i2c_rate = clk_get_rate(i2c->clk);
|
||||
|
||||
int div, divl, divh;
|
||||
|
||||
if((scl_rate == i2c->scl_rate) && (i2c_rate == i2c->i2c_rate))
|
||||
return;
|
||||
i2c->i2c_rate = i2c_rate;
|
||||
i2c->scl_rate = scl_rate;
|
||||
div = rk30_ceil(i2c_rate, (scl_rate * 8)) - 2;
|
||||
if(unlikely(div < 0)){
|
||||
dev_warn(i2c->dev, "Divisor(%d) is negative, set divl = divh = 0\n", div);
|
||||
divh =divl = 0;
|
||||
}else{
|
||||
rk30_get_div(div, &divh, &divl);
|
||||
}
|
||||
i2c_writel(I2C_CLKDIV_VAL(divl, divh), i2c->regs + I2C_CLKDIV);
|
||||
i2c_dbg(i2c->dev, "set clk(I2C_CLKDIV: 0x%08x)\n", i2c_readl(i2c->regs + I2C_CLKDIV));
|
||||
return;
|
||||
}
|
||||
static void rk30_i2c_init_hw(struct rk30_i2c *i2c, unsigned long scl_rate)
|
||||
{
|
||||
i2c->scl_rate = 0;
|
||||
rk30_i2c_set_clk(i2c, scl_rate);
|
||||
return;
|
||||
}
|
||||
/* returns TRUE if we this is the last byte in the current message */
|
||||
static inline int is_msglast(struct rk30_i2c *i2c)
|
||||
{
|
||||
return i2c->msg_ptr == i2c->msg->len-1;
|
||||
}
|
||||
|
||||
/* returns TRUE if we reached the end of the current message */
|
||||
static inline int is_msgend(struct rk30_i2c *i2c)
|
||||
{
|
||||
return i2c->msg_ptr >= i2c->msg->len;
|
||||
}
|
||||
|
||||
static void rk30_i2c_stop(struct rk30_i2c *i2c, int ret)
|
||||
{
|
||||
|
||||
i2c->msg_ptr = 0;
|
||||
i2c->msg = NULL;
|
||||
if(ret == -EAGAIN){
|
||||
i2c->state = STATE_IDLE;
|
||||
i2c->is_busy = 0;
|
||||
wake_up(&i2c->wait);
|
||||
return;
|
||||
}
|
||||
i2c->error = ret;
|
||||
i2c_writel(I2C_STOPIEN, i2c->regs + I2C_IEN);
|
||||
i2c->state = STATE_STOP;
|
||||
rk30_i2c_send_stop(i2c);
|
||||
return;
|
||||
}
|
||||
static inline void rk30_set_rx_mode(struct rk30_i2c *i2c, unsigned int lastnak)
|
||||
{
|
||||
unsigned long con = i2c_readl(i2c->regs + I2C_CON);
|
||||
|
||||
con &= (~I2C_CON_MASK);
|
||||
con |= (I2C_CON_MOD_RX << 1);
|
||||
if(lastnak)
|
||||
con |= I2C_CON_LASTACK;
|
||||
i2c_writel(con, i2c->regs + I2C_CON);
|
||||
}
|
||||
static void rk30_irq_read_prepare(struct rk30_i2c *i2c)
|
||||
{
|
||||
unsigned int cnt, len = i2c->msg->len - i2c->msg_ptr;
|
||||
|
||||
if(len <= 32 && i2c->msg_ptr != 0)
|
||||
rk30_set_rx_mode(i2c, 1);
|
||||
else if(i2c->msg_ptr != 0)
|
||||
rk30_set_rx_mode(i2c, 0);
|
||||
|
||||
if(is_msgend(i2c)) {
|
||||
rk30_i2c_stop(i2c, i2c->error);
|
||||
return;
|
||||
}
|
||||
if(len > 32)
|
||||
cnt = 32;
|
||||
else
|
||||
cnt = len;
|
||||
i2c_writel(cnt, i2c->regs + I2C_MRXCNT);
|
||||
}
|
||||
static void rk30_irq_read_get_data(struct rk30_i2c *i2c)
|
||||
{
|
||||
unsigned int i, len = i2c->msg->len - i2c->msg_ptr;
|
||||
unsigned int p = 0;
|
||||
|
||||
len = (len >= 32)?32:len;
|
||||
|
||||
for(i = 0; i < len; i++){
|
||||
if(i%4 == 0)
|
||||
p = i2c_readl(i2c->regs + I2C_RXDATA_BASE + (i/4) * 4);
|
||||
i2c->msg->buf[i2c->msg_ptr++] = (p >>((i%4) * 8)) & 0xff;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
static void rk30_irq_write_prepare(struct rk30_i2c *i2c)
|
||||
{
|
||||
unsigned int data = 0, cnt = 0, i, j;
|
||||
unsigned char byte;
|
||||
|
||||
if(is_msgend(i2c)) {
|
||||
rk30_i2c_stop(i2c, i2c->error);
|
||||
return;
|
||||
}
|
||||
for(i = 0; i < 8; i++){
|
||||
data = 0;
|
||||
for(j = 0; j < 4; j++) {
|
||||
if(is_msgend(i2c))
|
||||
break;
|
||||
if((i2c->msg_ptr == 0) && (cnt == 0))
|
||||
byte = (i2c->addr_1st & 0x7f) << 1;
|
||||
else if((i2c->msg_ptr == 0) && (cnt == 1) && (i2c->msg->flags & I2C_M_TEN))
|
||||
byte = i2c->addr_2nd;
|
||||
else
|
||||
byte = i2c->msg->buf[i2c->msg_ptr++];
|
||||
cnt++;
|
||||
data |= (byte << (j * 8));
|
||||
}
|
||||
i2c_writel(data, i2c->regs + I2C_TXDATA_BASE + 4 * i);
|
||||
if(is_msgend(i2c))
|
||||
break;
|
||||
}
|
||||
i2c_writel(cnt, i2c->regs + I2C_MTXCNT);
|
||||
}
|
||||
static void rk30_i2c_irq_nextblock(struct rk30_i2c *i2c, unsigned int ipd)
|
||||
{
|
||||
switch (i2c->state) {
|
||||
case STATE_START:
|
||||
if(!(ipd & I2C_STARTIPD)){
|
||||
rk30_i2c_stop(i2c, -ENXIO);
|
||||
dev_err(i2c->dev, "Addr[0x%04x] no start irq in STATE_START\n", i2c->addr);
|
||||
rk30_show_regs(i2c);
|
||||
i2c_writel(I2C_IPD_ALL_CLEAN, i2c->regs + I2C_IPD);
|
||||
goto out;
|
||||
}
|
||||
i2c->complete_what |= 1<<i2c->state;
|
||||
i2c_writel(I2C_STARTIPD, i2c->regs + I2C_IPD);
|
||||
rk30_i2c_clean_start(i2c);
|
||||
if(i2c->mode == I2C_CON_MOD_TX){
|
||||
i2c_writel(I2C_MBTFIEN | I2C_NAKRCVIEN, i2c->regs + I2C_IEN);
|
||||
i2c->state = STATE_WRITE;
|
||||
goto prepare_write;
|
||||
} else {
|
||||
i2c_writel(I2C_MBRFIEN | I2C_NAKRCVIEN, i2c->regs + I2C_IEN);
|
||||
i2c->state = STATE_READ;
|
||||
goto prepare_read;
|
||||
}
|
||||
case STATE_WRITE:
|
||||
if(!(ipd & I2C_MBTFIPD)){
|
||||
rk30_i2c_stop(i2c, -ENXIO);
|
||||
dev_err(i2c->dev, "Addr[0x%04x] no mbtf irq in STATE_WRITE\n", i2c->addr);
|
||||
rk30_show_regs(i2c);
|
||||
i2c_writel(I2C_IPD_ALL_CLEAN, i2c->regs + I2C_IPD);
|
||||
goto out;
|
||||
}
|
||||
i2c->complete_what |= 1<<i2c->state;
|
||||
i2c_writel(I2C_MBTFIPD, i2c->regs + I2C_IPD);
|
||||
prepare_write:
|
||||
rk30_irq_write_prepare(i2c);
|
||||
break;
|
||||
case STATE_READ:
|
||||
if(!(ipd & I2C_MBRFIPD)){
|
||||
rk30_i2c_stop(i2c, -ENXIO);
|
||||
dev_err(i2c->dev, "Addr[0x%04x] no mbrf irq in STATE_READ, ipd = 0x%x\n", i2c->addr, ipd);
|
||||
rk30_show_regs(i2c);
|
||||
i2c_writel(I2C_IPD_ALL_CLEAN, i2c->regs + I2C_IPD);
|
||||
goto out;
|
||||
}
|
||||
i2c->complete_what |= 1<<i2c->state;
|
||||
i2c_writel(I2C_MBRFIPD, i2c->regs + I2C_IPD);
|
||||
rk30_irq_read_get_data(i2c);
|
||||
prepare_read:
|
||||
rk30_irq_read_prepare(i2c);
|
||||
break;
|
||||
case STATE_STOP:
|
||||
if(!(ipd & I2C_STOPIPD)){
|
||||
rk30_i2c_stop(i2c, -ENXIO);
|
||||
dev_err(i2c->dev, "Addr[0x%04x] no stop irq in STATE_STOP\n", i2c->addr);
|
||||
rk30_show_regs(i2c);
|
||||
i2c_writel(I2C_IPD_ALL_CLEAN, i2c->regs + I2C_IPD);
|
||||
goto out;
|
||||
}
|
||||
rk30_i2c_clean_stop(i2c);
|
||||
i2c_writel(I2C_STOPIPD, i2c->regs + I2C_IPD);
|
||||
i2c->is_busy = 0;
|
||||
i2c->complete_what |= 1<<i2c->state;
|
||||
i2c->state = STATE_IDLE;
|
||||
wake_up(&i2c->wait);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
out:
|
||||
return;
|
||||
}
|
||||
static irqreturn_t rk30_i2c_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct rk30_i2c *i2c = dev_id;
|
||||
unsigned int ipd;
|
||||
|
||||
spin_lock(&i2c->lock);
|
||||
ipd = i2c_readl(i2c->regs + I2C_IPD);
|
||||
if(i2c->state == STATE_IDLE){
|
||||
dev_info(i2c->dev, "Addr[0x%04x] irq in STATE_IDLE, ipd = 0x%x\n", i2c->addr, ipd);
|
||||
i2c_writel(I2C_IPD_ALL_CLEAN, i2c->regs + I2C_IPD);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(ipd & I2C_NAKRCVIPD){
|
||||
i2c_writel(I2C_NAKRCVIPD, i2c->regs + I2C_IPD);
|
||||
i2c->error = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
rk30_i2c_irq_nextblock(i2c, ipd);
|
||||
out:
|
||||
spin_unlock(&i2c->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
static int rk30_i2c_set_master(struct rk30_i2c *i2c, struct i2c_msg *msgs, int num)
|
||||
{
|
||||
unsigned int reg_valid_bits = 0;
|
||||
unsigned int reg_addr = 0;
|
||||
unsigned int addr = (i2c->addr_1st << 1) | 1;
|
||||
|
||||
if(num == 1) {
|
||||
i2c->count = msgs[0].len;
|
||||
if(!(msgs[0].flags & I2C_M_RD)){
|
||||
i2c->msg = &msgs[0];
|
||||
i2c->mode = I2C_CON_MOD_TX;
|
||||
}
|
||||
else {
|
||||
i2c->msg = &msgs[0];
|
||||
i2c_writel(addr | I2C_MRXADDR_LOW, i2c->regs + I2C_MRXADDR);
|
||||
i2c_writel(0, i2c->regs + I2C_MRXRADDR);
|
||||
i2c->mode = I2C_CON_MOD_TRX;
|
||||
//i2c->mode = I2C_CON_MOD_RX;
|
||||
}
|
||||
}
|
||||
else if(num == 2) {
|
||||
i2c->count = msgs[1].len;
|
||||
|
||||
if(msgs[0].flags & I2C_M_TEN){
|
||||
switch(msgs[0].len){
|
||||
case 1:
|
||||
reg_addr = i2c->addr_2nd | (msgs[0].buf[0] << 8);
|
||||
reg_valid_bits |= I2C_MRXADDR_LOW | I2C_MRXADDR_MID;
|
||||
break;
|
||||
case 2:
|
||||
reg_addr = i2c->addr_2nd | (msgs[0].buf[0] << 8) | (msgs[0].buf[1] << 16);
|
||||
reg_valid_bits |= I2C_MRXADDR_LOW | I2C_MRXADDR_MID | I2C_MRXADDR_HIGH;
|
||||
break;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}else{
|
||||
switch(msgs[0].len){
|
||||
case 1:
|
||||
reg_addr = msgs[0].buf[0];
|
||||
reg_valid_bits |= I2C_MRXADDR_LOW;
|
||||
break;
|
||||
case 2:
|
||||
reg_addr = msgs[0].buf[0] | (msgs[0].buf[1] << 8);
|
||||
reg_valid_bits |= I2C_MRXADDR_LOW | I2C_MRXADDR_MID;
|
||||
break;
|
||||
case 3:
|
||||
reg_addr = msgs[0].buf[0] | (msgs[0].buf[1] << 8) | (msgs[0].buf[2] << 16);
|
||||
reg_valid_bits |= I2C_MRXADDR_LOW | I2C_MRXADDR_MID | I2C_MRXADDR_HIGH;
|
||||
break;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
if((msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD)) {
|
||||
i2c->msg = &msgs[1];
|
||||
i2c_writel(addr | I2C_MRXADDR_LOW, i2c->regs + I2C_MRXADDR);
|
||||
i2c_writel(reg_addr | reg_valid_bits, i2c->regs + I2C_MRXRADDR);
|
||||
i2c->mode = I2C_CON_MOD_RRX;
|
||||
}
|
||||
else if(!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD)) {
|
||||
i2c->msg = &msgs[1];
|
||||
i2c_writel(addr | I2C_MRXADDR_LOW, i2c->regs + I2C_MRXADDR);
|
||||
i2c_writel(reg_addr | reg_valid_bits, i2c->regs + I2C_MRXRADDR);
|
||||
i2c->mode = I2C_CON_MOD_TRX;
|
||||
}
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
else {
|
||||
dev_err(i2c->dev, "This case(num > 2) has not been support now\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* rk30_i2c_doxfer
|
||||
*
|
||||
* this starts an i2c transfer
|
||||
*/
|
||||
static int rk30_i2c_doxfer(struct rk30_i2c *i2c,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
unsigned long timeout, flags;
|
||||
int error = 0;
|
||||
/* 32 -- max transfer bytes
|
||||
* 2 -- addr bytes * 2
|
||||
* 3 -- max reg addr bytes
|
||||
* 9 -- cycles per bytes
|
||||
* max cycles: (32 + 2 + 3) * 9 --> 400 cycles
|
||||
*/
|
||||
int msleep_time = 400 * 1000/ i2c->scl_rate; // ms
|
||||
|
||||
if (i2c->suspended){
|
||||
dev_err(i2c->dev, "i2c is suspended\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&i2c->lock, flags);
|
||||
i2c->addr = msgs[0].addr;
|
||||
if(msgs[0].flags & I2C_M_TEN){
|
||||
i2c->addr_1st = ((i2c->addr & 0x0300)>>8) | 0x78;
|
||||
i2c->addr_2nd = i2c->addr & 0x00ff;
|
||||
}else{
|
||||
i2c->addr_1st = i2c->addr & 0x007f;
|
||||
i2c->addr_2nd = 0;
|
||||
}
|
||||
i2c_dbg(i2c->dev, "addr: 0x%04x, addr_1st: 0x%02x, addr_2nd: 0x%02x\n",
|
||||
i2c->addr, i2c->addr_1st, i2c->addr_2nd);
|
||||
|
||||
if(rk30_i2c_set_master(i2c, msgs, num) < 0){
|
||||
spin_unlock_irqrestore(&i2c->lock, flags);
|
||||
dev_err(i2c->dev, "addr[0x%04x] set master error\n", msgs[0].addr);
|
||||
return -EIO;
|
||||
}
|
||||
i2c->msg_ptr = 0;
|
||||
i2c->error = 0;
|
||||
i2c->is_busy = 1;
|
||||
i2c->state = STATE_START;
|
||||
i2c->complete_what = 0;
|
||||
i2c_writel(I2C_STARTIEN, i2c->regs + I2C_IEN);
|
||||
spin_unlock_irqrestore(&i2c->lock, flags);
|
||||
|
||||
rk30_i2c_enable(i2c, (i2c->count > 32)?0:1); //if count > 32, byte(32) send ack
|
||||
|
||||
if (in_atomic()){
|
||||
int tmo = I2C_WAIT_TIMEOUT * USEC_PER_MSEC;
|
||||
while(tmo-- && i2c->is_busy != 0)
|
||||
udelay(1);
|
||||
timeout = (tmo <= 0)?0:1;
|
||||
}else
|
||||
timeout = wait_event_timeout(i2c->wait, (i2c->is_busy == 0), msecs_to_jiffies(I2C_WAIT_TIMEOUT));
|
||||
|
||||
spin_lock_irqsave(&i2c->lock, flags);
|
||||
i2c->state = STATE_IDLE;
|
||||
error = i2c->error;
|
||||
spin_unlock_irqrestore(&i2c->lock, flags);
|
||||
|
||||
if (timeout == 0){
|
||||
unsigned int ipd = i2c_readl(i2c->regs + I2C_IPD);
|
||||
if(error < 0)
|
||||
i2c_dbg(i2c->dev, "error = %d\n", error);
|
||||
else if((i2c->complete_what !=COMPLETE_READ && i2c->complete_what != COMPLETE_WRITE)){
|
||||
if(ipd & I2C_HOLD_SCL)
|
||||
dev_err(i2c->dev, "SCL was hold by slave\n");
|
||||
dev_err(i2c->dev, "Addr[0x%04x] wait event timeout, state: %d, is_busy: %d, error: %d, complete_what: 0x%x, ipd: 0x%x\n",
|
||||
msgs[0].addr, i2c->state, i2c->is_busy, error, i2c->complete_what, ipd);
|
||||
//rk30_show_regs(i2c);
|
||||
error = -ETIMEDOUT;
|
||||
if(in_atomic())
|
||||
mdelay(msleep_time);
|
||||
else
|
||||
msleep(msleep_time);
|
||||
rk30_i2c_send_stop(i2c);
|
||||
if(in_atomic())
|
||||
mdelay(1);
|
||||
else
|
||||
msleep(1);
|
||||
}
|
||||
else
|
||||
i2c_dbg(i2c->dev, "Addr[0x%02x] wait event timeout, but transfer complete\n", i2c->addr);
|
||||
}
|
||||
i2c_writel(I2C_IPD_ALL_CLEAN, i2c->regs + I2C_IPD);
|
||||
rk30_i2c_disable_irq(i2c);
|
||||
rk30_i2c_disable(i2c);
|
||||
|
||||
if(error == -EAGAIN)
|
||||
i2c_dbg(i2c->dev, "No ack(complete_what: 0x%x), Maybe slave(addr: 0x%04x) not exist or abnormal power-on\n",
|
||||
i2c->complete_what, i2c->addr);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* rk30_i2c_xfer
|
||||
*
|
||||
* first port of call from the i2c bus code when an message needs
|
||||
* transferring across the i2c bus.
|
||||
*/
|
||||
|
||||
static int rk30_i2c_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
int ret = 0, state, retry = 10;
|
||||
unsigned long scl_rate;
|
||||
struct rk30_i2c *i2c = (struct rk30_i2c *)adap->algo_data;
|
||||
|
||||
clk_enable(i2c->clk);
|
||||
#ifdef I2C_CHECK_IDLE
|
||||
while(retry-- && ((state = rk30_i2c_check_idle(i2c)) != I2C_IDLE)){
|
||||
if(in_atomic())
|
||||
mdelay(10);
|
||||
else
|
||||
msleep(10);
|
||||
}
|
||||
if(retry == 0){
|
||||
dev_err(i2c->dev, "i2c is not in idle(state = %d)\n", state);
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(msgs[0].scl_rate <= 400000 && msgs[0].scl_rate >= 10000)
|
||||
scl_rate = msgs[0].scl_rate;
|
||||
else if(msgs[0].scl_rate > 400000){
|
||||
dev_warn(i2c->dev, "Warning: addr[0x%04x] msg[0].scl_rate( = %dKhz) is too high!",
|
||||
msgs[0].addr, msgs[0].scl_rate/1000);
|
||||
scl_rate = 400000;
|
||||
}
|
||||
else{
|
||||
dev_warn(i2c->dev, "Warning: addr[0x%04x] msg[0].scl_rate( = %dKhz) is too low!",
|
||||
msgs[0].addr, msgs[0].scl_rate/1000);
|
||||
scl_rate = 10000;
|
||||
}
|
||||
if(i2c->is_div_from_arm[i2c->adap.nr]){
|
||||
mutex_lock(&i2c->m_lock);
|
||||
}
|
||||
|
||||
rk30_i2c_set_clk(i2c, scl_rate);
|
||||
i2c_dbg(i2c->dev, "i2c transfer start: addr: 0x%04x, scl_reate: %ldKhz, len: %d\n", msgs[0].addr, scl_rate/1000, num);
|
||||
ret = rk30_i2c_doxfer(i2c, msgs, num);
|
||||
i2c_dbg(i2c->dev, "i2c transfer stop: addr: 0x%04x, state: %d, ret: %d\n", msgs[0].addr, ret, i2c->state);
|
||||
|
||||
if(i2c->is_div_from_arm[i2c->adap.nr]){
|
||||
mutex_unlock(&i2c->m_lock);
|
||||
}
|
||||
|
||||
clk_disable(i2c->clk);
|
||||
return (ret < 0)?ret:num;
|
||||
}
|
||||
|
||||
/* declare our i2c functionality */
|
||||
static u32 rk30_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
|
||||
}
|
||||
|
||||
/* i2c bus registration info */
|
||||
|
||||
static const struct i2c_algorithm rk30_i2c_algorithm = {
|
||||
.master_xfer = rk30_i2c_xfer,
|
||||
.functionality = rk30_i2c_func,
|
||||
};
|
||||
|
||||
int i2c_add_rk30_adapter(struct i2c_adapter *adap)
|
||||
{
|
||||
int ret = 0;
|
||||
struct rk30_i2c *i2c = (struct rk30_i2c *)adap->algo_data;
|
||||
|
||||
adap->algo = &rk30_i2c_algorithm;
|
||||
|
||||
i2c->i2c_init_hw = &rk30_i2c_init_hw;
|
||||
i2c->i2c_set_clk = &rk30_i2c_set_clk;
|
||||
i2c->i2c_irq = &rk30_i2c_irq;
|
||||
|
||||
ret = i2c_add_numbered_adapter(adap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1,407 +0,0 @@
|
|||
/* drivers/i2c/busses/i2c-rk30.c
|
||||
*
|
||||
* Copyright (C) 2012 ROCKCHIP, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
#include "i2c-rk30.h"
|
||||
#define TX_SETUP 1
|
||||
|
||||
static int i2c_max_adap = 0;
|
||||
void i2c_adap_sel(struct rk30_i2c *i2c, int nr, int adap_type)
|
||||
{
|
||||
i2c_writel((1 << I2C_ADAP_SEL_BIT(nr)) | (1 << I2C_ADAP_SEL_MASK(nr)) ,
|
||||
i2c->con_base);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
|
||||
#define freq_to_i2c(_n) container_of(_n, struct rk30_i2c, freq_transition)
|
||||
|
||||
static int rk30_i2c_cpufreq_transition(struct notifier_block *nb,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct rk30_i2c *i2c = freq_to_i2c(nb);
|
||||
struct cpufreq_freqs *freqs = data;
|
||||
|
||||
if (freqs->cpu)
|
||||
return 0;
|
||||
|
||||
if(val == CPUFREQ_PRECHANGE)
|
||||
mutex_lock(&i2c->m_lock);
|
||||
else if(val == CPUFREQ_POSTCHANGE)
|
||||
mutex_unlock(&i2c->m_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int rk30_i2c_register_cpufreq(struct rk30_i2c *i2c)
|
||||
{
|
||||
if(!i2c->is_div_from_arm[i2c->adap.nr])
|
||||
return 0;
|
||||
i2c->freq_transition.notifier_call = rk30_i2c_cpufreq_transition;
|
||||
|
||||
return cpufreq_register_notifier(&i2c->freq_transition,
|
||||
CPUFREQ_TRANSITION_NOTIFIER);
|
||||
}
|
||||
|
||||
static inline void rk30_i2c_deregister_cpufreq(struct rk30_i2c *i2c)
|
||||
{
|
||||
if(!i2c->is_div_from_arm[i2c->adap.nr])
|
||||
return;
|
||||
cpufreq_unregister_notifier(&i2c->freq_transition,
|
||||
CPUFREQ_TRANSITION_NOTIFIER);
|
||||
}
|
||||
|
||||
#else
|
||||
static inline int rk30_i2c_register_cpufreq(struct rk30_i2c *i2c)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void rk30_i2c_deregister_cpufreq(struct rk30_i2c *i2c)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* rk30_i2c_probe
|
||||
*
|
||||
* called by the bus driver when a suitable device is found
|
||||
*/
|
||||
|
||||
static int rk30_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rk30_i2c *i2c = NULL;
|
||||
struct rk30_i2c_platform_data *pdata = NULL;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "no platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
i2c = kzalloc(sizeof(struct rk30_i2c), GFP_KERNEL);
|
||||
if (!i2c) {
|
||||
dev_err(&pdev->dev, "no memory for state\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_ARCH_RK319X)
|
||||
i2c->con_base = (void __iomem *)GRF_I2C_CON_BASE;
|
||||
i2c_adap_sel(i2c, pdata->bus_num, pdata->adap_type);
|
||||
#endif
|
||||
|
||||
if(pdata->io_init)
|
||||
pdata->io_init();
|
||||
|
||||
i2c->sda_mode = pdata->sda_mode;
|
||||
i2c->scl_mode = pdata->scl_mode;
|
||||
|
||||
strlcpy(i2c->adap.name, "rk30_i2c", sizeof(i2c->adap.name));
|
||||
i2c->adap.owner = THIS_MODULE;
|
||||
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
i2c->tx_setup = TX_SETUP;
|
||||
i2c->adap.retries = 2;
|
||||
i2c->adap.timeout = msecs_to_jiffies(100);
|
||||
|
||||
spin_lock_init(&i2c->lock);
|
||||
init_waitqueue_head(&i2c->wait);
|
||||
mutex_init(&i2c->m_lock);
|
||||
|
||||
/* find the clock and enable it */
|
||||
|
||||
i2c->dev = &pdev->dev;
|
||||
i2c->clk = clk_get(&pdev->dev, "i2c");
|
||||
if (IS_ERR(i2c->clk)) {
|
||||
dev_err(&pdev->dev, "cannot get clock\n");
|
||||
ret = -ENOENT;
|
||||
goto err_noclk;
|
||||
}
|
||||
|
||||
i2c_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
|
||||
|
||||
clk_enable(i2c->clk);
|
||||
|
||||
/* map the registers */
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res == NULL) {
|
||||
dev_err(&pdev->dev, "cannot find IO resource\n");
|
||||
ret = -ENOENT;
|
||||
goto err_get_resource;
|
||||
}
|
||||
|
||||
i2c->ioarea = request_mem_region(res->start, resource_size(res),
|
||||
pdev->name);
|
||||
|
||||
if (i2c->ioarea == NULL) {
|
||||
dev_err(&pdev->dev, "cannot request IO\n");
|
||||
ret = -ENXIO;
|
||||
goto err_ioarea;
|
||||
}
|
||||
|
||||
i2c->regs = ioremap(res->start, resource_size(res));
|
||||
|
||||
if (i2c->regs == NULL) {
|
||||
dev_err(&pdev->dev, "cannot map IO\n");
|
||||
ret = -ENXIO;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
i2c_dbg(&pdev->dev, "registers %p (%p, %p)\n",
|
||||
i2c->regs, i2c->ioarea, res);
|
||||
|
||||
/* setup info block for the i2c core */
|
||||
|
||||
i2c->adap.algo_data = i2c;
|
||||
i2c->adap.dev.parent = &pdev->dev;
|
||||
i2c->adap.nr = pdata->bus_num;
|
||||
if(pdata->adap_type == I2C_RK29_ADAP)
|
||||
ret = i2c_add_rk29_adapter(&i2c->adap);
|
||||
else // I2C_RK30_ADAP
|
||||
ret = i2c_add_rk30_adapter(&i2c->adap);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to add adapter\n");
|
||||
goto err_add_adapter;
|
||||
}
|
||||
|
||||
/* find the IRQ for this unit (note, this relies on the init call to
|
||||
* ensure no current IRQs pending
|
||||
*/
|
||||
|
||||
i2c->irq = ret = platform_get_irq(pdev, 0);
|
||||
if (ret <= 0) {
|
||||
dev_err(&pdev->dev, "cannot find IRQ\n");
|
||||
goto err_get_irq;
|
||||
}
|
||||
|
||||
ret = request_irq(i2c->irq, i2c->i2c_irq, IRQF_DISABLED,
|
||||
dev_name(&pdev->dev), i2c);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
|
||||
goto err_request_irq;
|
||||
}
|
||||
|
||||
ret = rk30_i2c_register_cpufreq(i2c);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
|
||||
goto err_register_cpufreq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, i2c);
|
||||
|
||||
i2c->is_div_from_arm[i2c->adap.nr] = pdata->is_div_from_arm;
|
||||
|
||||
i2c->i2c_init_hw(i2c, 100 * 1000);
|
||||
i2c_max_adap++;
|
||||
dev_info(&pdev->dev, "%s: RK30 I2C adapter\n", dev_name(&i2c->adap.dev));
|
||||
return 0;
|
||||
//err_none:
|
||||
// rk30_i2c_deregister_cpufreq(i2c);
|
||||
err_register_cpufreq:
|
||||
free_irq(i2c->irq, i2c);
|
||||
err_request_irq:
|
||||
err_get_irq:
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
err_add_adapter:
|
||||
iounmap(i2c->regs);
|
||||
err_ioremap:
|
||||
kfree(i2c->ioarea);
|
||||
err_ioarea:
|
||||
release_resource(i2c->ioarea);
|
||||
err_get_resource:
|
||||
clk_put(i2c->clk);
|
||||
err_noclk:
|
||||
kfree(i2c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* rk30_i2c_remove
|
||||
*
|
||||
* called when device is removed from the bus
|
||||
*/
|
||||
|
||||
static int rk30_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rk30_i2c *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
rk30_i2c_deregister_cpufreq(i2c);
|
||||
free_irq(i2c->irq, i2c);
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
iounmap(i2c->regs);
|
||||
kfree(i2c->ioarea);
|
||||
release_resource(i2c->ioarea);
|
||||
clk_put(i2c->clk);
|
||||
kfree(i2c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int rk30_i2c_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct rk30_i2c *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
i2c->suspended = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk30_i2c_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct rk30_i2c *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
i2c->suspended = 0;
|
||||
i2c->i2c_init_hw(i2c, i2c->scl_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops rk30_i2c_dev_pm_ops = {
|
||||
.suspend_noirq = rk30_i2c_suspend_noirq,
|
||||
.resume_noirq = rk30_i2c_resume_noirq,
|
||||
};
|
||||
|
||||
#define rk30_DEV_PM_OPS (&rk30_i2c_dev_pm_ops)
|
||||
#else
|
||||
#define rk30_DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
MODULE_DEVICE_TABLE(platform, rk30_driver_ids);
|
||||
|
||||
static struct platform_driver rk30_i2c_driver = {
|
||||
.probe = rk30_i2c_probe,
|
||||
.remove = rk30_i2c_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "rk30_i2c",
|
||||
.pm = rk30_DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init i2c_adap_init(void)
|
||||
{
|
||||
return platform_driver_register(&rk30_i2c_driver);
|
||||
}
|
||||
subsys_initcall(i2c_adap_init);
|
||||
|
||||
static void __exit i2c_adap_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&rk30_i2c_driver);
|
||||
}
|
||||
module_exit(i2c_adap_exit);
|
||||
|
||||
static int detect_read(struct i2c_client *client, char *buf, int len)
|
||||
{
|
||||
struct i2c_msg msg;
|
||||
|
||||
msg.addr = client->addr;
|
||||
msg.flags = client->flags | I2C_M_RD;
|
||||
msg.buf = buf;
|
||||
msg.len = len;
|
||||
msg.scl_rate = 100 * 1000;
|
||||
|
||||
return i2c_transfer(client->adapter, &msg, 1);
|
||||
}
|
||||
|
||||
static void detect_set_client(struct i2c_client *client, __u16 addr, int nr)
|
||||
{
|
||||
client->flags = 0;
|
||||
client->addr = addr;
|
||||
client->adapter = i2c_get_adapter(nr);
|
||||
}
|
||||
static void slave_detect(int nr)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned short addr;
|
||||
char val[8];
|
||||
char buf[6 * 0x80 + 20];
|
||||
struct i2c_client client;
|
||||
|
||||
memset(buf, 0, 6 * 0x80 + 20);
|
||||
|
||||
sprintf(buf, "I2c%d slave list: ", nr);
|
||||
do {
|
||||
for(addr = 0x01; addr < 0x80; addr++){
|
||||
detect_set_client(&client, addr, nr);
|
||||
ret = detect_read(&client, val, 1);
|
||||
if(ret > 0)
|
||||
sprintf(buf, "%s 0x%02x", buf, addr);
|
||||
}
|
||||
printk("%s\n", buf);
|
||||
}
|
||||
while(0);
|
||||
}
|
||||
static ssize_t i2c_detect_write(struct file *file,
|
||||
const char __user *buf, size_t count, loff_t *offset)
|
||||
{
|
||||
char nr_buf[8];
|
||||
int nr = 0, ret;
|
||||
|
||||
if(count > 4)
|
||||
return -EFAULT;
|
||||
ret = copy_from_user(nr_buf, buf, count);
|
||||
if(ret < 0)
|
||||
return -EFAULT;
|
||||
|
||||
sscanf(nr_buf, "%d", &nr);
|
||||
if(nr >= 5 || nr < 0)
|
||||
return -EFAULT;
|
||||
|
||||
slave_detect(nr);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations i2c_detect_fops = {
|
||||
.write = i2c_detect_write,
|
||||
};
|
||||
static struct miscdevice i2c_detect_device = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "i2c_detect",
|
||||
.fops = &i2c_detect_fops,
|
||||
};
|
||||
static int __init i2c_detect_init(void)
|
||||
{
|
||||
return misc_register(&i2c_detect_device);
|
||||
}
|
||||
|
||||
static void __exit i2c_detect_exit(void)
|
||||
{
|
||||
misc_deregister(&i2c_detect_device);
|
||||
}
|
||||
module_init(i2c_detect_init);
|
||||
module_exit(i2c_detect_exit);
|
||||
|
||||
static int __init i2c_detect_rk610(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < i2c_max_adap; i++){
|
||||
i2c_check_rk610_ex(i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
late_initcall(i2c_detect_rk610);
|
||||
|
||||
|
||||
|
||||
|
||||
MODULE_DESCRIPTION("Driver for RK30 I2C Bus");
|
||||
MODULE_AUTHOR("kfx, kfx@rock-chips.com");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
#ifndef __RK30_I2C_H__
|
||||
#define __RK30_I2C_H__
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <mach/board.h>
|
||||
#include <mach/iomux.h>
|
||||
#include <mach/gpio.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#if 0
|
||||
#define i2c_dbg(dev, format, arg...) \
|
||||
dev_printk(KERN_INFO , dev , format , ## arg)
|
||||
#else
|
||||
#define i2c_dbg(dev, format, arg...)
|
||||
#endif
|
||||
|
||||
#define I2C_CHECK_IDLE
|
||||
|
||||
#define i2c_writel writel_relaxed
|
||||
#define i2c_readl readl_relaxed
|
||||
|
||||
#define I2C_WAIT_TIMEOUT 200 //200ms
|
||||
|
||||
#define rk30_set_bit(p, v, b) (((p) & ~(1 << (b))) | ((v) << (b)))
|
||||
#define rk30_get_bit(p, b) (((p) & (1 << (b))) >> (b))
|
||||
|
||||
#define rk30_set_bits(p, v, b, m) (((p) & ~(m)) | ((v) << (b)))
|
||||
#define rk30_get_bits(p, b, m) (((p) & (m)) >> (b))
|
||||
|
||||
#define rk30_ceil(x, y) \
|
||||
({ unsigned long __x = (x), __y = (y); (__x + __y - 1) / __y; })
|
||||
#if defined(CONFIG_ARCH_RK30) || defined(CONFIG_ARCH_RK3188)
|
||||
#define GRF_I2C_CON_BASE (RK30_GRF_BASE + GRF_SOC_CON1)
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_RK2928) || defined(CONFIG_ARCH_RK3026)
|
||||
#define GRF_I2C_CON_BASE (RK2928_GRF_BASE + GRF_SOC_CON1)
|
||||
#endif
|
||||
#define I2C_ADAP_SEL_BIT(nr) ((nr) + 11)
|
||||
#define I2C_ADAP_SEL_MASK(nr) ((nr) + 27)
|
||||
enum rk30_i2c_state {
|
||||
STATE_IDLE,
|
||||
STATE_START,
|
||||
STATE_READ,
|
||||
STATE_WRITE,
|
||||
STATE_STOP
|
||||
};
|
||||
struct rk30_i2c {
|
||||
spinlock_t lock;
|
||||
wait_queue_head_t wait;
|
||||
struct mutex m_lock;
|
||||
unsigned int suspended:1;
|
||||
|
||||
struct i2c_msg *msg;
|
||||
union {
|
||||
unsigned int msg_num;
|
||||
unsigned int is_busy;
|
||||
};
|
||||
union {
|
||||
unsigned int msg_idx;
|
||||
int error;
|
||||
};
|
||||
unsigned int msg_ptr;
|
||||
|
||||
unsigned int tx_setup;
|
||||
unsigned int irq;
|
||||
|
||||
enum rk30_i2c_state state;
|
||||
unsigned int complete_what;
|
||||
unsigned long clkrate;
|
||||
|
||||
void __iomem *regs;
|
||||
void __iomem *con_base;
|
||||
struct clk *clk;
|
||||
struct device *dev;
|
||||
struct resource *ioarea;
|
||||
struct i2c_adapter adap;
|
||||
|
||||
unsigned long scl_rate;
|
||||
unsigned long i2c_rate;
|
||||
unsigned int addr;
|
||||
unsigned char addr_1st, addr_2nd;
|
||||
unsigned int mode;
|
||||
unsigned int count;
|
||||
|
||||
int sda_mode, scl_mode;
|
||||
|
||||
struct wake_lock idlelock[5];
|
||||
int is_div_from_arm[5];
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
struct notifier_block freq_transition;
|
||||
#endif
|
||||
|
||||
void (*i2c_init_hw)(struct rk30_i2c *, unsigned long scl_rate);
|
||||
void (*i2c_set_clk)(struct rk30_i2c *, unsigned long);
|
||||
int (*check_idle)(int);
|
||||
irqreturn_t (*i2c_irq)(int, void *);
|
||||
};
|
||||
void i2c_adap_sel(struct rk30_i2c *i2c, int nr, int adap_type);
|
||||
int i2c_add_rk29_adapter(struct i2c_adapter *);
|
||||
int i2c_add_rk30_adapter(struct i2c_adapter *);
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user