static struct platform_device *devices[] __initdata = {
};
+// i2c
+#ifdef CONFIG_I2C0_RK30
+static struct i2c_board_info __initdata i2c0_info[] = {
+};
+#endif
+#ifdef CONFIG_I2C1_RK30
+static struct i2c_board_info __initdata i2c1_info[] = {
+};
+#endif
+#ifdef CONFIG_I2C2_RK30
+static struct i2c_board_info __initdata i2c2_info[] = {
+};
+#endif
+#ifdef CONFIG_I2C3_RK30
+static struct i2c_board_info __initdata i2c3_info[] = {
+};
+#endif
+#ifdef CONFIG_I2C4_RK30
+static struct i2c_board_info __initdata i2c4_info[] = {
+};
+#endif
+static void __init rk30_i2c_register_board_info(void)
+{
+#ifdef CONFIG_I2C0_RK30
+ i2c_register_board_info(0, i2c0_info, ARRAY_SIZE(i2c0_info));
+#endif
+#ifdef CONFIG_I2C1_RK30
+ i2c_register_board_info(1, i2c1_info, ARRAY_SIZE(i2c1_info));
+#endif
+#ifdef CONFIG_I2C2_RK30
+ i2c_register_board_info(2, i2c2_info, ARRAY_SIZE(i2c2_info));
+#endif
+#ifdef CONFIG_I2C3_RK30
+ i2c_register_board_info(3, i2c3_info, ARRAY_SIZE(i2c3_info));
+#endif
+#ifdef CONFIG_I2C4_RK30
+ i2c_register_board_info(4, i2c4_info, ARRAY_SIZE(i2c4_info));
+#endif
+}
+//end of i2c
+
static void __init machine_rk30_board_init(void)
{
- platform_add_devices(devices, ARRAY_SIZE(devices));
+ rk30_i2c_register_board_info();
+ platform_add_devices(devices, ARRAY_SIZE(devices));
}
static void __init rk30_reserve(void)
#endif
}
+// i2c
+#ifdef CONFIG_I2C0_RK30
+static struct rk30_i2c_platform_data default_i2c0_data = {
+ .bus_num = 0,
+ .is_div_from_arm = 1,
+ #ifdef CONFIG_I2C0_CONTROLLER_RK29
+ .adap_type = I2C_RK29_ADAP,
+ #endif
+ #ifdef CONFIG_I2C0_CONTROLLER_RK30
+ .adap_type = I2C_RK30_ADAP,
+ #endif
+};
+static struct resource resources_i2c0[] = {
+ {
+ .start = IRQ_I2C0,
+ .end = IRQ_I2C0,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = RK30_I2C0_PHYS,
+ .end = RK30_I2C0_PHYS + RK30_I2C0_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device device_i2c0 = {
+ .name = "i2c-rk30",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(resources_i2c0),
+ .resource = resources_i2c0,
+ .dev = {
+ .platform_data = &default_i2c0_data,
+ },
+};
+#endif
+#ifdef CONFIG_I2C1_RK30
+static struct rk30_i2c_platform_data default_i2c1_data = {
+ .bus_num = 1,
+ .is_div_from_arm = 1,
+ #ifdef CONFIG_I2C1_CONTROLLER_RK29
+ .adap_type = I2C_RK29_ADAP,
+ #endif
+ #ifdef CONFIG_I2C1_CONTROLLER_RK30
+ .adap_type = I2C_RK30_ADAP,
+ #endif
+};
+static struct resource resources_i2c1[] = {
+ {
+ .start = IRQ_I2C1,
+ .end = IRQ_I2C1,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = RK30_I2C1_PHYS,
+ .end = RK30_I2C1_PHYS + RK30_I2C1_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device device_i2c1 = {
+ .name = "i2c-rk30",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(resources_i2c1),
+ .resource = resources_i2c1,
+ .dev = {
+ .platform_data = &default_i2c1_data,
+ },
+};
+#endif
+#ifdef CONFIG_I2C2_RK30
+static struct rk30_i2c_platform_data default_i2c2_data = {
+ .bus_num = 2,
+ .is_div_from_arm = 0,
+ #ifdef CONFIG_I2C2_CONTROLLER_RK29
+ .adap_type = I2C_RK29_ADAP,
+ #endif
+ #ifdef CONFIG_I2C2_CONTROLLER_RK30
+ .adap_type = I2C_RK30_ADAP,
+ #endif
+};
+static struct resource resources_i2c2[] = {
+ {
+ .start = IRQ_I2C2,
+ .end = IRQ_I2C2,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = RK30_I2C2_PHYS,
+ .end = RK30_I2C2_PHYS + RK30_I2C2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device device_i2c2 = {
+ .name = "i2c-rk30",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(resources_i2c2),
+ .resource = resources_i2c2,
+ .dev = {
+ .platform_data = &default_i2c2_data,
+ },
+};
+#endif
+
+#ifdef CONFIG_I2C3_RK30
+static struct rk30_i2c_platform_data default_i2c3_data = {
+ .bus_num = 3,
+ .is_div_from_arm = 0,
+ #ifdef CONFIG_I2C3_CONTROLLER_RK29
+ .adap_type = I2C_RK29_ADAP,
+ #endif
+ #ifdef CONFIG_I2C3_CONTROLLER_RK30
+ .adap_type = I2C_RK30_ADAP,
+ #endif
+};
+static struct resource resources_i2c3[] = {
+ {
+ .start = IRQ_I2C3,
+ .end = IRQ_I2C3,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = RK30_I2C3_PHYS,
+ .end = RK30_I2C3_PHYS + RK30_I2C3_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device device_i2c3 = {
+ .name = "i2c-rk30",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(resources_i2c3),
+ .resource = resources_i2c3,
+ .dev = {
+ .platform_data = &default_i2c3_data,
+ },
+};
+#endif
+#ifdef CONFIG_I2C4_RK30
+static struct rk30_i2c_platform_data default_i2c4_data = {
+ .bus_num = 4,
+ .is_div_from_arm = 0,
+ #ifdef CONFIG_I2C4_CONTROLLER_RK29
+ .adap_type = I2C_RK29_ADAP,
+ #endif
+ #ifdef CONFIG_I2C4_CONTROLLER_RK30
+ .adap_type = I2C_RK30_ADAP,
+ #endif
+};
+static struct resource resources_i2c4[] = {
+ {
+ .start = IRQ_I2C4,
+ .end = IRQ_I2C4,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = RK30_I2C4_PHYS,
+ .end = RK30_I2C4_PHYS + RK30_I2C4_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device device_i2c4 = {
+ .name = "i2c-rk30",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(resources_i2c4),
+ .resource = resources_i2c4,
+ .dev = {
+ .platform_data = &default_i2c4_data,
+ },
+};
+#endif
+
+static void __init rk30_init_i2c(void)
+{
+#ifdef CONFIG_I2C0_RK30
+ platform_device_register(&device_i2c0);
+#endif
+#ifdef CONFIG_I2C1_RK30
+ platform_device_register(&device_i2c1);
+#endif
+#ifdef CONFIG_I2C2_RK30
+ platform_device_register(&device_i2c2);
+#endif
+#ifdef CONFIG_I2C3_RK30
+ platform_device_register(&device_i2c3);
+#endif
+#ifdef CONFIG_I2C4_RK30
+ platform_device_register(&device_i2c4);
+#endif
+}
+//end of i2c
+
+
#ifdef CONFIG_MTD_NAND_RK29XX
static struct resource resources_nand[] = {
{
static int __init rk30_init_devices(void)
{
rk30_init_uart();
+ rk30_init_i2c();
#ifdef CONFIG_MTD_NAND_RK29XX
platform_device_register(&device_nand);
#endif
#define BOOT_MODE_OFFMODE_CHARGING 5
#define BOOT_MODE_REBOOT 6
#define BOOT_MODE_PANIC 7
+
+struct rk30_i2c_platform_data {
+ char *name;
+ int bus_num;
+#define I2C_RK29_ADAP 0
+#define I2C_RK30_ADAP 1
+ int adap_type:1;
+ int is_div_from_arm:1;
+ u32 flags;
+ int (*io_init)(void);
+ int (*io_deinit)(void);
+};
+
int board_boot_mode(void);
/* for USB detection */
depends on SCx200_GPIO
select I2C_ALGOBIT
+config I2C_RK30
+ tristate "RK I2C Adapter"
+ depends on PLAT_RK && !ARCH_RK29
+ default y
+ help
+ This supports I2C Adapter on RK Soc.
+
+if I2C_RK30
+ comment "Now, there are five selectable I2C channels."
+
+ config I2C0_RK30
+ bool "I2C0 Channel Support"
+ default y
+ help
+ This supports the use of the I2C0 channel on RK Soc.
+ if I2C0_RK30
+ choice
+ prompt "I2C Controller Select"
+ config I2C0_CONTROLLER_RK29
+ bool "With RK29 I2C Controller"
+ config I2C0_CONTROLLER_RK30
+ bool "With RK30 I2C Controller"
+ endchoice
+ endif
+ config I2C1_RK30
+ bool "I2C1 Channel Support"
+ default y
+ help
+ This supports the use of the I2C1 channel on RK Soc.
+ if I2C1_RK30
+ choice
+ prompt "I2C Controller Select"
+ config I2C1_CONTROLLER_RK29
+ bool "With RK29 I2C Controller"
+ config I2C1_CONTROLLER_RK30
+ bool "With RK30 I2C Controller"
+ endchoice
+ endif
+ config I2C2_RK30
+ bool "I2C2 Channel Support"
+ default y
+ help
+ This supports the use of the I2C2 channel on RK Soc.
+ if I2C2_RK30
+ choice
+ prompt "I2C Controller Select"
+ config I2C2_CONTROLLER_RK29
+ bool "With RK29 I2C Controller"
+ config I2C2_CONTROLLER_RK30
+ bool "With RK30 I2C Controller"
+ endchoice
+ endif
+ config I2C3_RK30
+ bool "I2C3 Channel Support"
+ default y
+ help
+ This supports the use of the I2C3 channel on RK Soc.
+ if I2C3_RK30
+ choice
+ prompt "I2C Controller Select"
+ config I2C3_CONTROLLER_RK29
+ bool "With RK29 I2C Controller"
+ config I2C3_CONTROLLER_RK30
+ bool "With RK30 I2C Controller"
+ endchoice
+ endif
+ config I2C4_RK30
+ bool "I2C4 Channel Support"
+ default y
+ help
+ This supports the use of the I2C4 channel on RK Soc.
+ if I2C4_RK30
+ choice
+ prompt "I2C Controller Select"
+ config I2C4_CONTROLLER_RK29
+ bool "With RK29 I2C Controller"
+ config I2C4_CONTROLLER_RK30
+ bool "With RK30 I2C Controller"
+ endchoice
+ endif
+endif
config I2C_RK29
tristate "RK29 i2c interface (I2C)"
depends on ARCH_RK29
#
obj-$(CONFIG_I2C_RK29) += i2c-rk29.o
obj-$(CONFIG_I2C_DEV_RK29) += i2c-dev-rk29.o
+obj-$(CONFIG_I2C_RK30) += i2c-rk30.o i2c-rk29-adapter.o i2c-rk30-adapter.o
obj-y += i2c-gpio.o
# ACPI drivers
--- /dev/null
+/* 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
+#define ABNORMAL_STOP_DELAY 200 //unit us
+static unsigned int abnormal_stop_addr[] = {
+ 0x2d,
+};
+
+int i2c_suspended(struct i2c_adapter *adap)
+{
+ return 1;
+}
+EXPORT_SYMBOL(i2c_suspended);
+
+static inline void rk29_i2c_disable_ack(void __iomem *regs)
+{
+ unsigned long conr = readl(regs + I2C_CONR);
+
+ conr |= I2C_CONR_NAK;
+ writel(conr, regs + I2C_CONR);
+}
+
+static inline void rk29_i2c_enable_ack(void __iomem *regs)
+{
+ unsigned long conr = readl(regs + I2C_CONR);
+
+ conr &= I2C_CONR_ACK;
+ writel(conr, regs + I2C_CONR);
+}
+static inline void rk29_i2c_disable_mport(void __iomem *regs)
+{
+ unsigned long conr = readl(regs + I2C_CONR);
+
+ conr &= I2C_CONR_MPORT_DISABLE;
+ writel(conr, regs + I2C_CONR);
+}
+
+static inline void rk29_i2c_enable_mport(void __iomem *regs)
+{
+ unsigned long conr = readl(regs + I2C_CONR);
+
+ conr |= I2C_CONR_MPORT_ENABLE;
+ writel(conr, regs + I2C_CONR);
+}
+
+static inline void rk29_i2c_disable_irq(void __iomem *regs)
+{
+ writel(IRQ_ALL_DISABLE, regs + I2C_IER);
+}
+
+static inline void rk29_i2c_enable_irq(void __iomem *regs)
+{
+ writel(IRQ_MST_ENABLE, 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 = 24000000;//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 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, 100000);
+
+ 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->regs);
+ 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)
+{
+ unsigned int n, i;
+
+ i2c_dbg(i2c->dev, "STOP\n");
+ udelay(i2c->tx_setup);
+
+ n = sizeof(abnormal_stop_addr)/sizeof(abnormal_stop_addr[0]);
+ for(i = 0; i < n; i++){
+ if(i2c->addr == abnormal_stop_addr[i])
+ udelay(ABNORMAL_STOP_DELAY);
+ }
+
+
+
+ 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->regs);
+}
+
+/* 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->regs);
+ 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, -ENXIO);
+ 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;
+ 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, HZ * 5);
+
+ 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);
+ msleep(1);
+ 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 retry;
+ int ret;
+ unsigned long scl_rate;
+
+ 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->regs);
+ udelay(i2c->tx_setup);
+
+ for (retry = 0; retry < adap->retries; retry++) {
+
+ ret = rk29_i2c_doxfer(i2c, msgs, num);
+
+ if (ret != -EAGAIN) {
+ return ret;
+ }
+
+ i2c_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
+
+ msleep(1);
+ }
+
+ rk29_i2c_disable_mport(i2c->regs);
+ if(i2c->is_div_from_arm[i2c->adap.nr])
+ wake_unlock(&i2c->idlelock[i2c->adap.nr]);
+ return -EREMOTEIO;
+}
+
+/* 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;
+ adap->retries = 3;
+
+ 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;
+}
+
+
--- /dev/null
+/* 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"
+
+static void rk30_i2c_set_clk(struct rk30_i2c *i2c, unsigned long scl_rate)
+{
+ return;
+}
+static void rk30_i2c_init_hw(struct rk30_i2c *i2c)
+{
+ return;
+}
+
+
+static irqreturn_t rk30_i2c_irq(int irq, void *dev_id)
+{
+ return IRQ_HANDLED;
+}
+
+
+static int rk30_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ return 0;
+}
+
+/* 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;
+ adap->retries = 3;
+
+ 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;
+}
+
+
--- /dev/null
+/* 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 //unit us
+
+#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);
+ 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->lock, flags);
+ i2c->i2c_set_clk(i2c, i2c->scl_rate);
+ spin_unlock_irqrestore(&i2c->lock, flags);
+ }
+ return 0;
+}
+
+static inline int rk30_i2c_register_cpufreq(struct rk30_i2c *i2c)
+{
+ if (i2c->adap.nr != 0)
+ 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->adap.nr != 0)
+ 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;
+ char name[5];
+ int ret;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -EINVAL;
+ }
+ if(pdata->io_init)
+ pdata->io_init();
+
+ i2c = kzalloc(sizeof(struct rk30_i2c), GFP_KERNEL);
+ if (!i2c) {
+ dev_err(&pdev->dev, "no memory for state\n");
+ return -ENOMEM;
+ }
+
+ 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;
+
+ spin_lock_init(&i2c->lock);
+ init_waitqueue_head(&i2c->wait);
+
+ /* 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);
+
+ sprintf(name, "%s%d", "i2c", i2c->adap.nr);
+ i2c->is_div_from_arm[i2c->adap.nr] = pdata->is_div_from_arm;
+ if(i2c->is_div_from_arm[i2c->adap.nr])
+ wake_lock_init(&i2c->idlelock[i2c->adap.nr], WAKE_LOCK_IDLE, name);
+
+ i2c->i2c_init_hw(i2c);
+ 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);
+
+ 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);
+
+MODULE_DESCRIPTION("Driver for RK30 I2C Bus");
+MODULE_AUTHOR("kfx, kfx@rock-chips.com");
+MODULE_LICENSE("GPL");
--- /dev/null
+#ifndef __RK30_I2C_H__
+#define __RK30_I2C_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 <mach/board.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
+
+
+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;
+ unsigned int suspended:1;
+
+ struct i2c_msg *msg;
+ unsigned int msg_num;
+ unsigned int msg_idx;
+ unsigned int msg_ptr;
+
+ unsigned int tx_setup;
+ unsigned int irq;
+
+ enum rk30_i2c_state state;
+ unsigned long clkrate;
+
+ void __iomem *regs;
+ struct clk *clk;
+ struct device *dev;
+ struct resource *ioarea;
+ struct i2c_adapter adap;
+
+ unsigned long scl_rate;
+ unsigned long i2c_rate;
+ unsigned int addr;
+
+ 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 *);
+ void (*i2c_set_clk)(struct rk30_i2c *, unsigned long);
+ irqreturn_t (*i2c_irq)(int, void *);
+};
+
+int i2c_add_rk29_adapter(struct i2c_adapter *);
+int i2c_add_rk30_adapter(struct i2c_adapter *);
+#endif