From: kfx <kfx@rock-chips.com>
Date: Tue, 7 Feb 2012 10:06:57 +0000 (+0800)
Subject: add rk30 i2c drvier
X-Git-Tag: firefly_0821_release~9595^2~173
X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=8e579254f12b19f33d28fa233312d3b24dae889c;p=firefly-linux-kernel-4.4.55.git

add rk30 i2c drvier
---

diff --git a/arch/arm/mach-rk30/board-rk30-sdk.c b/arch/arm/mach-rk30/board-rk30-sdk.c
index d31641290823..73ee2a23da4f 100755
--- a/arch/arm/mach-rk30/board-rk30-sdk.c
+++ b/arch/arm/mach-rk30/board-rk30-sdk.c
@@ -39,9 +39,51 @@
 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)
diff --git a/arch/arm/mach-rk30/devices.c b/arch/arm/mach-rk30/devices.c
index 20c0b7294eb9..d3af4dead458 100644
--- a/arch/arm/mach-rk30/devices.c
+++ b/arch/arm/mach-rk30/devices.c
@@ -126,6 +126,200 @@ static void __init rk30_init_uart(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[] = {
 	{
@@ -146,6 +340,7 @@ static struct platform_device device_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
diff --git a/arch/arm/plat-rk/include/plat/board.h b/arch/arm/plat-rk/include/plat/board.h
index 8729fde11879..5761c138d628 100644
--- a/arch/arm/plat-rk/include/plat/board.h
+++ b/arch/arm/plat-rk/include/plat/board.h
@@ -11,6 +11,19 @@
 #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 */
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 01e5f49a0c83..2d7e414c4070 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -839,6 +839,87 @@ config SCx200_I2C
 	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
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 7c2fd1ae13df..bb71ae214ec3 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -3,6 +3,7 @@
 #
 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
diff --git a/drivers/i2c/busses/i2c-rk29-adapter.c b/drivers/i2c/busses/i2c-rk29-adapter.c
new file mode 100755
index 000000000000..5edf143f69a2
--- /dev/null
+++ b/drivers/i2c/busses/i2c-rk29-adapter.c
@@ -0,0 +1,619 @@
+/* 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;
+}
+
+
diff --git a/drivers/i2c/busses/i2c-rk30-adapter.c b/drivers/i2c/busses/i2c-rk30-adapter.c
new file mode 100755
index 000000000000..ea1e350c84f3
--- /dev/null
+++ b/drivers/i2c/busses/i2c-rk30-adapter.c
@@ -0,0 +1,69 @@
+/* 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;
+}
+
+
diff --git a/drivers/i2c/busses/i2c-rk30.c b/drivers/i2c/busses/i2c-rk30.c
new file mode 100755
index 000000000000..e738fbaabc22
--- /dev/null
+++ b/drivers/i2c/busses/i2c-rk30.c
@@ -0,0 +1,296 @@
+/* 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");
diff --git a/drivers/i2c/busses/i2c-rk30.h b/drivers/i2c/busses/i2c-rk30.h
new file mode 100755
index 000000000000..23f0751bac61
--- /dev/null
+++ b/drivers/i2c/busses/i2c-rk30.h
@@ -0,0 +1,79 @@
+#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