add rk30 i2c drvier
authorkfx <kfx@rock-chips.com>
Tue, 7 Feb 2012 10:06:57 +0000 (18:06 +0800)
committerkfx <kfx@rock-chips.com>
Tue, 7 Feb 2012 10:06:57 +0000 (18:06 +0800)
arch/arm/mach-rk30/board-rk30-sdk.c
arch/arm/mach-rk30/devices.c
arch/arm/plat-rk/include/plat/board.h
drivers/i2c/busses/Kconfig
drivers/i2c/busses/Makefile
drivers/i2c/busses/i2c-rk29-adapter.c [new file with mode: 0755]
drivers/i2c/busses/i2c-rk30-adapter.c [new file with mode: 0755]
drivers/i2c/busses/i2c-rk30.c [new file with mode: 0755]
drivers/i2c/busses/i2c-rk30.h [new file with mode: 0755]

index d31641290823f76e530c1d3df7840bf13ab35e8d..73ee2a23da4f8074da5beb96a6cf9cca84882cb9 100755 (executable)
 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)
index 20c0b7294eb9ba4d63e5bb5aaeea46b2a3e94236..d3af4dead45896bcd7d9ae1d03f2174f48e39737 100644 (file)
@@ -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
index 8729fde11879c13aac41dda81bde1bd606a7fe6e..5761c138d6282030b534058a91b32454f8f04114 100644 (file)
 #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 */
index 01e5f49a0c836cdf3d2d7428220e973315cef762..2d7e414c40709f96a59807fd8b1fab5d914a574d 100644 (file)
@@ -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
index 7c2fd1ae13df41f734509311effae25ee5a04a35..bb71ae214ec346ffa03fab31edc3fb65c469d4c5 100644 (file)
@@ -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 (executable)
index 0000000..5edf143
--- /dev/null
@@ -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 (executable)
index 0000000..ea1e350
--- /dev/null
@@ -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 (executable)
index 0000000..e738fba
--- /dev/null
@@ -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 (executable)
index 0000000..23f0751
--- /dev/null
@@ -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