From: kfx Date: Thu, 11 Nov 2010 08:51:12 +0000 (+0800) Subject: add i2c drivers X-Git-Tag: firefly_0821_release~11031^2 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=9d8af509346f956a5f2e4991f2fbe48a4657989d;p=firefly-linux-kernel-4.4.55.git add i2c drivers --- diff --git a/arch/arm/mach-rk29/board-rk29sdk.c b/arch/arm/mach-rk29/board-rk29sdk.c index 26201e6ab342..8ba184163f5b 100755 --- a/arch/arm/mach-rk29/board-rk29sdk.c +++ b/arch/arm/mach-rk29/board-rk29sdk.c @@ -332,6 +332,90 @@ static struct platform_device android_pmem_vpu_device = { .platform_data = &android_pmem_vpu_pdata, }, }; +/***************************************************************************************** + * i2c devices + * author: kfx@rock-chips.com +*****************************************************************************************/ +static int rk29_i2c0_io_init(void) +{ + rk29_mux_api_set(GPIO2B7_I2C0SCL_NAME, GPIO2L_I2C0_SCL); + rk29_mux_api_set(GPIO2B6_I2C0SDA_NAME, GPIO2L_I2C0_SDA); + return 0; +} + +static int rk29_i2c1_io_init(void) +{ + rk29_mux_api_set(GPIO1A7_I2C1SCL_NAME, GPIO1L_I2C1_SCL); + rk29_mux_api_set(GPIO1A6_I2C1SDA_NAME, GPIO1L_I2C1_SDA); + return 0; +} +static int rk29_i2c2_io_init(void) +{ + rk29_mux_api_set(GPIO5D4_I2C2SCL_NAME, GPIO5H_I2C2_SCL); + rk29_mux_api_set(GPIO5D3_I2C2SDA_NAME, GPIO5H_I2C2_SDA); + return 0; +} + +static int rk29_i2c3_io_init(void) +{ + rk29_mux_api_set(GPIO2B5_UART3RTSN_I2C3SCL_NAME, GPIO2L_I2C3_SCL); + rk29_mux_api_set(GPIO2B4_UART3CTSN_I2C3SDA_NAME, GPIO2L_I2C3_SDA); + return 0; +} + +struct rk29_i2c_platform_data default_i2c0_data = { + .bus_num = 0, + .flags = 0, + .slave_addr = 0xff, + .scl_rate = 400*1000, + .mode = I2C_MODE_IRQ, + .io_init = rk29_i2c0_io_init, +}; +struct rk29_i2c_platform_data default_i2c1_data = { + .bus_num = 1, + .flags = 0, + .slave_addr = 0xff, + .scl_rate = 400*1000, + .mode = I2C_MODE_POLL, + .io_init = rk29_i2c1_io_init, +}; +struct rk29_i2c_platform_data default_i2c2_data = { + .bus_num = 2, + .flags = 0, + .slave_addr = 0xff, + .scl_rate = 400*1000, + .mode = I2C_MODE_IRQ, + .io_init = rk29_i2c2_io_init, +}; +struct rk29_i2c_platform_data default_i2c3_data = { + .bus_num = 3, + .flags = 0, + .slave_addr = 0xff, + .scl_rate = 400*1000, + .mode = I2C_MODE_POLL, + .io_init = rk29_i2c3_io_init, +}; + + +static struct i2c_board_info __initdata board_i2c0_devices[] = { +#if defined (CONFIG_RK1000_CONTROL) + { + .type = "rk1000_control", + .addr = 0x40, + .flags = 0, + }, +#endif +}; +static struct i2c_board_info __initdata board_i2c1_devices[] = { + +}; +static struct i2c_board_info __initdata board_i2c2_devices[] = { + +}; +static struct i2c_board_info __initdata board_i2c3_devices[] = { + +}; + /***************************************************************************************** * SDMMC devices @@ -345,7 +429,7 @@ void rk29_sdmmc0_cfg_gpio(struct platform_device *dev) rk29_mux_api_set(GPIO1D3_SDMMC0DATA1_NAME, GPIO1H_SDMMC0_DATA1); rk29_mux_api_set(GPIO1D4_SDMMC0DATA2_NAME, GPIO1H_SDMMC0_DATA2); rk29_mux_api_set(GPIO1D5_SDMMC0DATA3_NAME, GPIO1H_SDMMC0_DATA3); - rk29_mux_api_set(GPIO2A2_SDMMC0DETECTN_NAME, GPIO2L_SDMMC0_DETECT_N); + rk29_mux_api_set(GPIO2A2_SDMMC0DETECTN_NAME, GPIO2L_SDMMC0_DETECT_N); } #define CONFIG_SDMMC0_USE_DMA @@ -373,7 +457,7 @@ void rk29_sdmmc1_cfg_gpio(struct platform_device *dev) rk29_mux_api_set(GPIO1C3_SDMMC1DATA0_NAME, GPIO1H_SDMMC1_DATA0); rk29_mux_api_set(GPIO1C4_SDMMC1DATA1_NAME, GPIO1H_SDMMC1_DATA1); rk29_mux_api_set(GPIO1C5_SDMMC1DATA2_NAME, GPIO1H_SDMMC1_DATA2); - rk29_mux_api_set(GPIO1C6_SDMMC1DATA3_NAME, GPIO1H_SDMMC1_DATA3); + rk29_mux_api_set(GPIO1C6_SDMMC1DATA3_NAME, GPIO1H_SDMMC1_DATA3); } struct rk29_sdmmc_platform_data default_sdmmc1_data = { @@ -457,7 +541,21 @@ static void __init rk29_board_iomux_init(void) static struct platform_device *devices[] __initdata = { #ifdef CONFIG_UART1_RK29 &rk29_device_uart1, -#endif +#endif + +#ifdef CONFIG_I2C0_RK29 + &rk29_device_i2c0, +#endif +#ifdef CONFIG_I2C1_RK29 + &rk29_device_i2c1, +#endif +#ifdef CONFIG_I2C2_RK29 + &rk29_device_i2c2, +#endif +#ifdef CONFIG_I2C3_RK29 + &rk29_device_i2c3, +#endif + #ifdef CONFIG_SDMMC0_RK29 &rk29_device_sdmmc0, #endif @@ -492,7 +590,23 @@ static void __init machine_rk29_init_irq(void) } static void __init machine_rk29_board_init(void) -{ +{ +#ifdef CONFIG_I2C0_RK29 + i2c_register_board_info(default_i2c0_data.bus_num, board_i2c0_devices, + ARRAY_SIZE(board_i2c0_devices)); +#endif +#ifdef CONFIG_I2C1_RK29 + i2c_register_board_info(default_i2c1_data.bus_num, board_i2c1_devices, + ARRAY_SIZE(board_i2c1_devices)); +#endif +#ifdef CONFIG_I2C2_RK29 + i2c_register_board_info(default_i2c2_data.bus_num, board_i2c2_devices, + ARRAY_SIZE(board_i2c2_devices)); +#endif +#ifdef CONFIG_I2C3_RK29 + i2c_register_board_info(default_i2c3_data.bus_num, board_i2c3_devices, + ARRAY_SIZE(board_i2c3_devices)); +#endif platform_add_devices(devices, ARRAY_SIZE(devices)); rk29_board_iomux_init(); } diff --git a/arch/arm/mach-rk29/devices.c b/arch/arm/mach-rk29/devices.c index 492fc69ea471..521f7c4f176f 100755 --- a/arch/arm/mach-rk29/devices.c +++ b/arch/arm/mach-rk29/devices.c @@ -20,6 +20,93 @@ #include #include #include "devices.h" +#ifdef CONFIG_I2C_RK29 +static struct resource resources_i2c0[] = { + { + .start = IRQ_I2C0, + .end = IRQ_I2C0, + .flags = IORESOURCE_IRQ, + }, + { + .start = RK29_I2C0_PHYS, + .end = RK29_I2C0_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; +static struct resource resources_i2c1[] = { + { + .start = IRQ_I2C1, + .end = IRQ_I2C1, + .flags = IORESOURCE_IRQ, + }, + { + .start = RK29_I2C1_PHYS, + .end = RK29_I2C1_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; +static struct resource resources_i2c2[] = { + { + .start = IRQ_I2C2, + .end = IRQ_I2C2, + .flags = IORESOURCE_IRQ, + }, + { + .start = RK29_I2C2_PHYS, + .end = RK29_I2C2_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; +static struct resource resources_i2c3[] = { + { + .start = IRQ_I2C3, + .end = IRQ_I2C3, + .flags = IORESOURCE_IRQ, + }, + { + .start = RK29_I2C3_PHYS, + .end = RK29_I2C3_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device rk29_device_i2c0 = { + .name = "rk29_i2c", + .id = 0, + .num_resources = ARRAY_SIZE(resources_i2c0), + .resource = resources_i2c0, + .dev = { + .platform_data = &default_i2c0_data, + }, +}; +struct platform_device rk29_device_i2c1 = { + .name = "rk29_i2c", + .id = 1, + .num_resources = ARRAY_SIZE(resources_i2c1), + .resource = resources_i2c1, + .dev = { + .platform_data = &default_i2c1_data, + }, +}; +struct platform_device rk29_device_i2c2 = { + .name = "rk29_i2c", + .id = 2, + .num_resources = ARRAY_SIZE(resources_i2c2), + .resource = resources_i2c2, + .dev = { + .platform_data = &default_i2c2_data, + }, +}; +struct platform_device rk29_device_i2c3 = { + .name = "rk29_i2c", + .id = 3, + .num_resources = ARRAY_SIZE(resources_i2c3), + .resource = resources_i2c3, + .dev = { + .platform_data = &default_i2c3_data, + }, +}; +#endif #ifdef CONFIG_SDMMC0_RK29 static struct resource resources_sdmmc0[] = { diff --git a/arch/arm/mach-rk29/devices.h b/arch/arm/mach-rk29/devices.h index fd1fe6e5d97b..a39938aa953b 100755 --- a/arch/arm/mach-rk29/devices.h +++ b/arch/arm/mach-rk29/devices.h @@ -17,7 +17,14 @@ #define __ARCH_ARM_MACH_RK29_DEVICES_H extern struct rk29_nand_platform_data rk29_nand_data; - +extern struct rk29_i2c_platform_data default_i2c0_data; +extern struct rk29_i2c_platform_data default_i2c1_data; +extern struct rk29_i2c_platform_data default_i2c2_data; +extern struct rk29_i2c_platform_data default_i2c3_data; +extern struct platform_device rk29_device_i2c0; +extern struct platform_device rk29_device_i2c1; +extern struct platform_device rk29_device_i2c2; +extern struct platform_device rk29_device_i2c3; extern struct platform_device rk29_device_uart0; extern struct platform_device rk29_device_uart1; extern struct platform_device rk29_device_uart2; diff --git a/arch/arm/mach-rk29/include/mach/board.h b/arch/arm/mach-rk29/include/mach/board.h index bb575a0e2311..b4772b7148b4 100755 --- a/arch/arm/mach-rk29/include/mach/board.h +++ b/arch/arm/mach-rk29/include/mach/board.h @@ -60,6 +60,17 @@ struct rk29_sdmmc_platform_data { int (*status)(struct device *); int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); }; +struct rk29_i2c_platform_data { + int bus_num; + unsigned int flags; + unsigned int slave_addr; + unsigned long scl_rate; +#define I2C_MODE_IRQ 0 +#define I2C_MODE_POLL 1 + unsigned int mode:1; + int (*io_init)(void); + int (*io_deinit)(void); +}; void __init rk29_map_common_io(void); void __init rk29_clock_init(void); diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 53b67a9eb073..241ec4b3ed6f 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -7,23 +7,63 @@ menu "I2C Hardware Bus support" config I2C_RK2818 tristate "RK2818 i2c interface (I2C)" depends on ARCH_RK2818 + default y help This supports the use of the I2C interface on rk2818 processors. if I2C_RK2818 - comment "Now, there are two I2C interfaces selected by developer, I2C0 and I2C1." + comment "Now, there are two I2C interfaces selected by developer." - menuconfig I2C0_RK2818 - tristate "RK2818 I2C0 interface support" + config I2C0_RK2818 + bool "RK2818 I2C0 interface support" default y depends on ARCH_RK2818 help This supports the use of the I2C0 interface on rk2818 processors. - menuconfig I2C1_RK2818 - tristate "RK2818 I2C1 interface support" + config I2C1_RK2818 + bool "RK2818 I2C1 interface support" default y depends on ARCH_RK2818 help This supports the use of the I2C1 interface on rk2818 processors. endif +config I2C_RK29 + tristate "RK29 i2c interface (I2C)" + depends on ARCH_RK29 + default y + help + This supports the use of the I2C interface(i2c0 ~ i2c3) on rk29 processors. + +if I2C_RK29 + comment "Now, there are four I2C interfaces selected by developer." + + config I2C0_RK29 + bool "RK29 I2C0 interface support" + default y + depends on ARCH_RK29 + help + This supports the use of the I2C0 interface on rk29 processors. + + config I2C1_RK29 + bool "RK29 I2C1 interface support" + default y + depends on ARCH_RK29 + help + This supports the use of the I2C1 interface on rk29 processors. + + config I2C2_RK29 + bool "RK29 I2C2 interface support" + default y + depends on ARCH_RK29 + help + This supports the use of the I2C2 interface on rk29 processors. + + config I2C3_RK29 + bool "RK29 I2C3 interface support" + default y + depends on ARCH_RK29 && !UART3_CTS_RTS_RK29 + help + This supports the use of the I2C3 interface on rk29 processors. +endif + endmenu diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 479f8a8ed714..98e13476d3b6 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -2,6 +2,7 @@ # Makefile for the i2c bus drivers. # obj-$(CONFIG_I2C_RK2818) += i2c-rk2818.o +obj-$(CONFIG_I2C_RK29) += i2c-rk29.o ifeq ($(CONFIG_I2C_DEBUG_BUS),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/i2c/busses/i2c-rk2818.c b/drivers/i2c/busses/i2c-rk2818.c index 545ebbfebfe1..4913063d7d68 100755 --- a/drivers/i2c/busses/i2c-rk2818.c +++ b/drivers/i2c/busses/i2c-rk2818.c @@ -1,4 +1,4 @@ -/* drivers/i2c/busses/i2c_rk2818.c +/* drivers/i2c/busses/i2c_rockchip.c * * Copyright (C) 2010 ROCKCHIP, Inc. * @@ -29,8 +29,8 @@ #include #include -#include "i2c-rk2818.h" -#define DRV_NAME "rk2818_i2c" +#include "i2c-rockchip.h" +#define DRV_NAME "rockchip_i2c" #define RK2818_I2C_TIMEOUT (msecs_to_jiffies(500)) #define RK2818_DELAY_TIME 2 @@ -42,13 +42,13 @@ #define i2c_dbg(dev, format, arg...) #endif -enum rk2818_error { +enum rockchip_error { RK2818_ERROR_NONE = 0, RK2818_ERROR_ARBITR_LOSE, RK2818_ERROR_UNKNOWN }; -enum rk2818_event { +enum rockchip_event { RK2818_EVENT_NONE = 0, /* master has received ack(MTX mode) means that data has been sent to slave. @@ -61,7 +61,7 @@ enum rk2818_event { RK2818_EVENT_MAX }; -struct rk2818_i2c_data { +struct rockchip_i2c_data { struct device *dev; struct i2c_adapter adap; void __iomem *regs; @@ -78,8 +78,8 @@ struct rk2818_i2c_data { spinlock_t cmd_lock; struct completion cmd_complete; - enum rk2818_event cmd_event; - enum rk2818_error cmd_err; + enum rockchip_event cmd_event; + enum rockchip_error cmd_err; unsigned int msg_idx; unsigned int msg_num; @@ -88,16 +88,16 @@ struct rk2818_i2c_data { #endif }; -static void rk2818_i2c_init_hw(struct rk2818_i2c_data *i2c); +static void rockchip_i2c_init_hw(struct rockchip_i2c_data *i2c); -static inline void rk2818_i2c_disable_irqs(struct rk2818_i2c_data *i2c) +static inline void rockchip_i2c_disable_irqs(struct rockchip_i2c_data *i2c) { unsigned long tmp; tmp = readl(i2c->regs + I2C_IER); writel(tmp & IRQ_ALL_DISABLE, i2c->regs + I2C_IER); } -static inline void rk2818_i2c_enable_irqs(struct rk2818_i2c_data *i2c) +static inline void rockchip_i2c_enable_irqs(struct rockchip_i2c_data *i2c) { unsigned long tmp; @@ -106,7 +106,7 @@ static inline void rk2818_i2c_enable_irqs(struct rk2818_i2c_data *i2c) } /* scl = pclk/(5 *(rem+1) * 2^(exp+1)) */ -static void rk2818_i2c_calcdivisor(unsigned long pclk, +static void rockchip_i2c_calcdivisor(unsigned long pclk, unsigned long scl_rate, unsigned long *real_rate, unsigned int *rem, unsigned int *exp) @@ -131,9 +131,13 @@ static void rk2818_i2c_calcdivisor(unsigned long pclk, return; } /* set i2c bus scl rate */ -static void rk2818_i2c_clockrate(struct rk2818_i2c_data *i2c) +static void rockchip_i2c_clockrate(struct rockchip_i2c_data *i2c) { +#ifdef CONFIG_ARCH_RK2818 struct rk2818_i2c_platform_data *pdata = i2c->dev->platform_data; +#else + struct rk29_i2c_platform_data *pdata = i2c->dev->platform_data; +#endif unsigned int rem = 0, exp = 0; unsigned long scl_rate, real_rate = 0, tmp; @@ -141,7 +145,7 @@ static void rk2818_i2c_clockrate(struct rk2818_i2c_data *i2c) scl_rate = (i2c->scl_rate) ? i2c->scl_rate : ((pdata->scl_rate)? pdata->scl_rate:100000); - rk2818_i2c_calcdivisor(i2c->i2c_rate, scl_rate, &real_rate, &rem, &exp); + rockchip_i2c_calcdivisor(i2c->i2c_rate, scl_rate, &real_rate, &rem, &exp); tmp = readl(i2c->regs + I2C_OPR); tmp |= exp; @@ -155,7 +159,7 @@ static void rk2818_i2c_clockrate(struct rk2818_i2c_data *i2c) i2c->i2c_rate/1000, scl_rate/1000, real_rate/1000); return; } -static int rk2818_event_occurred(struct rk2818_i2c_data *i2c) +static int rockchip_event_occurred(struct rockchip_i2c_data *i2c) { unsigned long isr; @@ -195,28 +199,28 @@ static int rk2818_event_occurred(struct rk2818_i2c_data *i2c) return 0; } -static irqreturn_t rk2818_i2c_irq(int irq, void *data) +static irqreturn_t rockchip_i2c_irq(int irq, void *data) { - struct rk2818_i2c_data *i2c = (struct rk2818_i2c_data *)data; + struct rockchip_i2c_data *i2c = (struct rockchip_i2c_data *)data; int res; - rk2818_i2c_disable_irqs(i2c); + rockchip_i2c_disable_irqs(i2c); spin_lock(&i2c->cmd_lock); - res = rk2818_event_occurred(i2c); + res = rockchip_event_occurred(i2c); if(res) //if(res || i2c->cmd_err != RK2818_ERROR_NONE) complete(&i2c->cmd_complete); spin_unlock(&i2c->cmd_lock); return IRQ_HANDLED; } -static int wait_for_completion_poll_timeout(struct rk2818_i2c_data *i2c, unsigned long timeout) +static int wait_for_completion_poll_timeout(struct rockchip_i2c_data *i2c, unsigned long timeout) { unsigned int time = RK2818_DELAY_TIME; int res; while(!time_after(jiffies, jiffies + timeout)) { - res = rk2818_event_occurred(i2c); + res = rockchip_event_occurred(i2c); if(res) //if(res || (i2c->cmd_err != RK2818_ERROR_NONE && i2c->cmd_err != RK2818_ERROR_UNKNOWN)) return 1; @@ -226,8 +230,8 @@ static int wait_for_completion_poll_timeout(struct rk2818_i2c_data *i2c, unsigne return 0; } -static int rk2818_wait_event(struct rk2818_i2c_data *i2c, - enum rk2818_event mr_event) +static int rockchip_wait_event(struct rockchip_i2c_data *i2c, + enum rockchip_event mr_event) { int ret = 0; unsigned long flags; @@ -247,7 +251,7 @@ static int rk2818_wait_event(struct rk2818_i2c_data *i2c, spin_unlock_irqrestore(&i2c->cmd_lock,flags); - rk2818_i2c_enable_irqs(i2c); + rockchip_i2c_enable_irqs(i2c); if(i2c->mode == I2C_MODE_IRQ) ret = wait_for_completion_interruptible_timeout(&i2c->cmd_complete, RK2818_I2C_TIMEOUT); @@ -266,7 +270,7 @@ static int rk2818_wait_event(struct rk2818_i2c_data *i2c, return 0; } -static int rk2818_wait_while_busy(struct rk2818_i2c_data *i2c) +static int rockchip_wait_while_busy(struct rockchip_i2c_data *i2c) { unsigned long timeout = jiffies + RK2818_I2C_TIMEOUT; unsigned long lsr; @@ -282,7 +286,7 @@ static int rk2818_wait_while_busy(struct rk2818_i2c_data *i2c) } return -ETIMEDOUT; } -static int rk2818_i2c_stop(struct rk2818_i2c_data *i2c) +static int rockchip_i2c_stop(struct rockchip_i2c_data *i2c) { unsigned long timeout = jiffies + RK2818_I2C_TIMEOUT; unsigned int time = RK2818_DELAY_TIME; @@ -301,7 +305,7 @@ static int rk2818_i2c_stop(struct rk2818_i2c_data *i2c) return -ETIMEDOUT; } -static int rk2818_send_2nd_addr(struct rk2818_i2c_data *i2c, +static int rockchip_send_2nd_addr(struct rockchip_i2c_data *i2c, struct i2c_msg *msg, int start) { int ret = 0; @@ -315,14 +319,14 @@ static int rk2818_send_2nd_addr(struct rk2818_i2c_data *i2c, i2c_dbg(i2c->dev, "i2c send addr_2nd: %lx\n", addr_2nd); writel(addr_2nd, i2c->regs + I2C_MTXR); writel(I2C_LCMR_RESUME, i2c->regs + I2C_LCMR); - if((ret = rk2818_wait_event(i2c, RK2818_EVENT_MTX_RCVD_ACK)) != 0) + if((ret = rockchip_wait_event(i2c, RK2818_EVENT_MTX_RCVD_ACK)) != 0) { dev_err(i2c->dev, "after sent addr_2nd, i2c wait for ACK timeout\n"); return ret; } return ret; } -static int rk2818_send_address(struct rk2818_i2c_data *i2c, +static int rockchip_send_address(struct rockchip_i2c_data *i2c, struct i2c_msg *msg, int start) { unsigned long addr_1st; @@ -348,7 +352,7 @@ static int rk2818_send_address(struct rk2818_i2c_data *i2c, if (start) { - if((ret = rk2818_wait_while_busy(i2c)) != 0) + if((ret = rockchip_wait_while_busy(i2c)) != 0) { dev_err(i2c->dev, "i2c is busy, when send address\n"); return ret; @@ -358,17 +362,17 @@ static int rk2818_send_address(struct rk2818_i2c_data *i2c, else writel(I2C_LCMR_START|I2C_LCMR_RESUME, i2c->regs + I2C_LCMR); - if((ret = rk2818_wait_event(i2c, RK2818_EVENT_MTX_RCVD_ACK)) != 0) + if((ret = rockchip_wait_event(i2c, RK2818_EVENT_MTX_RCVD_ACK)) != 0) { dev_err(i2c->dev, "after sent addr_1st, i2c wait for ACK timeout\n"); return ret; } if(start && (msg->flags & I2C_M_TEN)) - ret = rk2818_send_2nd_addr(i2c, msg, start); + ret = rockchip_send_2nd_addr(i2c, msg, start); return ret; } -static int rk2818_i2c_send_msg(struct rk2818_i2c_data *i2c, struct i2c_msg *msg) +static int rockchip_i2c_send_msg(struct rockchip_i2c_data *i2c, struct i2c_msg *msg) { int i, ret = 0; unsigned long conr = readl(i2c->regs + I2C_CONR); @@ -387,12 +391,12 @@ static int rk2818_i2c_send_msg(struct rk2818_i2c_data *i2c, struct i2c_msg *msg) */ writel(I2C_LCMR_RESUME, i2c->regs + I2C_LCMR); - if((ret = rk2818_wait_event(i2c, RK2818_EVENT_MTX_RCVD_ACK)) != 0) + if((ret = rockchip_wait_event(i2c, RK2818_EVENT_MTX_RCVD_ACK)) != 0) return ret; } return ret; } -static int rk2818_i2c_recv_msg(struct rk2818_i2c_data *i2c, struct i2c_msg *msg) +static int rockchip_i2c_recv_msg(struct rockchip_i2c_data *i2c, struct i2c_msg *msg) { int i, ret = 0; unsigned long conr = readl(i2c->regs + I2C_CONR); @@ -404,7 +408,7 @@ static int rk2818_i2c_recv_msg(struct rk2818_i2c_data *i2c, struct i2c_msg *msg) for(i = 0; i < msg->len; i++) { writel(I2C_LCMR_RESUME, i2c->regs + I2C_LCMR); - if((ret = rk2818_wait_event(i2c, RK2818_EVENT_MRX_NEED_ACK)) != 0) + if((ret = rockchip_wait_event(i2c, RK2818_EVENT_MRX_NEED_ACK)) != 0) return ret; conr = readl(i2c->regs + I2C_CONR); conr &= I2C_CONR_ACK; @@ -414,10 +418,10 @@ static int rk2818_i2c_recv_msg(struct rk2818_i2c_data *i2c, struct i2c_msg *msg) } return ret; } -static int rk2818_xfer_msg(struct i2c_adapter *adap, +static int rockchip_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int start, int stop) { - struct rk2818_i2c_data *i2c = (struct rk2818_i2c_data *)adap->algo_data; + struct rockchip_i2c_data *i2c = (struct rockchip_i2c_data *)adap->algo_data; unsigned long conr = readl(i2c->regs + I2C_CONR); int ret = 0; @@ -432,24 +436,24 @@ static int rk2818_xfer_msg(struct i2c_adapter *adap, goto exit; } - if((ret = rk2818_send_address(i2c, msg, start))!= 0) + if((ret = rockchip_send_address(i2c, msg, start))!= 0) { - dev_err(i2c->dev, "rk2818_send_address timeout\n"); + dev_err(i2c->dev, "rockchip_send_address timeout\n"); goto exit; } if(msg->flags & I2C_M_RD) { - if((ret = rk2818_i2c_recv_msg(i2c, msg)) != 0) + if((ret = rockchip_i2c_recv_msg(i2c, msg)) != 0) { - dev_err(i2c->dev, "rk2818_i2c_recv_msg timeout\n"); + dev_err(i2c->dev, "rockchip_i2c_recv_msg timeout\n"); goto exit; } } else { - if((ret = rk2818_i2c_send_msg(i2c, msg)) != 0) + if((ret = rockchip_i2c_send_msg(i2c, msg)) != 0) { - dev_err(i2c->dev, "rk2818_i2c_send_msg timeout\n"); + dev_err(i2c->dev, "rockchip_i2c_send_msg timeout\n"); goto exit; } } @@ -460,9 +464,9 @@ exit: conr = readl(i2c->regs + I2C_CONR); conr |= I2C_CONR_NAK; writel(conr, i2c->regs + I2C_CONR); - if((ret = rk2818_i2c_stop(i2c)) != 0) + if((ret = rockchip_i2c_stop(i2c)) != 0) { - dev_err(i2c->dev, "rk2818_i2c_stop timeout\n"); + dev_err(i2c->dev, "rockchip_i2c_stop timeout\n"); } //not I2C code,add by sxj,used for extend gpio intrrupt,set SCL and SDA pin. @@ -480,12 +484,12 @@ exit: } -static int rk2818_i2c_xfer(struct i2c_adapter *adap, +static int rockchip_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { int ret = -1; int i; - struct rk2818_i2c_data *i2c = (struct rk2818_i2c_data *)adap->algo_data; + struct rockchip_i2c_data *i2c = (struct rockchip_i2c_data *)adap->algo_data; unsigned long conr = readl(i2c->regs + I2C_CONR); /* if(i2c->suspended ==1) @@ -499,7 +503,7 @@ static int rk2818_i2c_xfer(struct i2c_adapter *adap, conr |= I2C_CONR_MPORT_ENABLE; writel(conr, i2c->regs + I2C_CONR); - rk2818_i2c_init_hw(i2c); + rockchip_i2c_init_hw(i2c); conr = readl(i2c->regs + I2C_CONR); conr &= I2C_CONR_ACK; @@ -507,11 +511,11 @@ static int rk2818_i2c_xfer(struct i2c_adapter *adap, for (i = 0; i < num; i++) { - ret = rk2818_xfer_msg(adap, &msgs[i], (i == 0), (i == (num - 1))); + ret = rockchip_xfer_msg(adap, &msgs[i], (i == 0), (i == (num - 1))); if (ret != 0) { num = ret; - dev_err(i2c->dev, "rk2818_xfer_msg error, ret = %d\n", ret); + dev_err(i2c->dev, "rockchip_xfer_msg error, ret = %d\n", ret); break; } } @@ -521,19 +525,19 @@ static int rk2818_i2c_xfer(struct i2c_adapter *adap, return num; } -static u32 rk2818_i2c_func(struct i2c_adapter *adap) +static u32 rockchip_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; } -static const struct i2c_algorithm rk2818_i2c_algorithm = { - .master_xfer = rk2818_i2c_xfer, - .functionality = rk2818_i2c_func, +static const struct i2c_algorithm rockchip_i2c_algorithm = { + .master_xfer = rockchip_i2c_xfer, + .functionality = rockchip_i2c_func, }; int i2c_suspended(struct i2c_adapter *adap) { - struct rk2818_i2c_data *i2c = (struct rk2818_i2c_data *)adap->algo_data; + struct rockchip_i2c_data *i2c = (struct rockchip_i2c_data *)adap->algo_data; if(adap->nr > 1) return 1; if(i2c == NULL) @@ -542,7 +546,7 @@ int i2c_suspended(struct i2c_adapter *adap) } EXPORT_SYMBOL(i2c_suspended); -static void rk2818_i2c_init_hw(struct rk2818_i2c_data *i2c) +static void rockchip_i2c_init_hw(struct rockchip_i2c_data *i2c) { unsigned long lcmr = 0x00000000; @@ -552,9 +556,9 @@ static void rk2818_i2c_init_hw(struct rk2818_i2c_data *i2c) writel(lcmr, i2c->regs + I2C_LCMR); - rk2818_i2c_disable_irqs(i2c); + rockchip_i2c_disable_irqs(i2c); - rk2818_i2c_clockrate(i2c); + rockchip_i2c_clockrate(i2c); opr = readl(i2c->regs + I2C_OPR); opr |= I2C_OPR_CORE_ENABLE; @@ -565,12 +569,12 @@ static void rk2818_i2c_init_hw(struct rk2818_i2c_data *i2c) #ifdef CONFIG_CPU_FREQ -#define freq_to_i2c(_n) container_of(_n, struct rk2818_i2c_data, freq_transition) +#define freq_to_i2c(_n) container_of(_n, struct rockchip_i2c_data, freq_transition) -static int rk2818_i2c_cpufreq_transition(struct notifier_block *nb, +static int rockchip_i2c_cpufreq_transition(struct notifier_block *nb, unsigned long val, void *data) { - struct rk2818_i2c_data *i2c = freq_to_i2c(nb); + struct rockchip_i2c_data *i2c = freq_to_i2c(nb); unsigned long flags; int delta_f; delta_f = clk_get_rate(i2c->clk) - i2c->i2c_rate; @@ -579,41 +583,45 @@ static int rk2818_i2c_cpufreq_transition(struct notifier_block *nb, (val == CPUFREQ_PRECHANGE && delta_f > 0)) { spin_lock_irqsave(&i2c->cmd_lock, flags); - rk2818_i2c_clockrate(i2c); + rockchip_i2c_clockrate(i2c); spin_unlock_irqrestore(&i2c->cmd_lock, flags); } return 0; } -static inline int rk2818_i2c_register_cpufreq(struct rk2818_i2c_data *i2c) +static inline int rockchip_i2c_register_cpufreq(struct rockchip_i2c_data *i2c) { - i2c->freq_transition.notifier_call = rk2818_i2c_cpufreq_transition; + i2c->freq_transition.notifier_call = rockchip_i2c_cpufreq_transition; return cpufreq_register_notifier(&i2c->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); } -static inline void rk2818_i2c_unregister_cpufreq(struct rk2818_i2c_data *i2c) +static inline void rockchip_i2c_unregister_cpufreq(struct rockchip_i2c_data *i2c) { cpufreq_unregister_notifier(&i2c->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); } #else -static inline int rk2818_i2c_register_cpufreq(struct rk2818_i2c_data *i2c) +static inline int rockchip_i2c_register_cpufreq(struct rockchip_i2c_data *i2c) { return 0; } -static inline void rk2818_i2c_unregister_cpufreq(struct rk2818_i2c_data *i2c) +static inline void rockchip_i2c_unregister_cpufreq(struct rockchip_i2c_data *i2c) { return; } #endif -static int rk2818_i2c_probe(struct platform_device *pdev) +static int rockchip_i2c_probe(struct platform_device *pdev) { - struct rk2818_i2c_data *i2c; - struct rk2818_i2c_platform_data *pdata; + struct rockchip_i2c_data *i2c; +#ifdef CONFIG_ARCH_RK2818 + struct rk2818_i2c_platform_data *pdata = NULL; +#else + struct rk29_i2c_platform_data *pdata = NULL; +#endif struct resource *res; int ret; @@ -623,7 +631,7 @@ static int rk2818_i2c_probe(struct platform_device *pdev) dev_err(&pdev->dev, "no platform data\n"); return -EINVAL; } - i2c = kzalloc(sizeof(struct rk2818_i2c_data), GFP_KERNEL); + i2c = kzalloc(sizeof(struct rockchip_i2c_data), GFP_KERNEL); if (!i2c) { dev_err(&pdev->dev, "no memory for state\n"); @@ -634,7 +642,7 @@ static int rk2818_i2c_probe(struct platform_device *pdev) strlcpy(i2c->adap.name, DRV_NAME, sizeof(i2c->adap.name)); i2c->adap.owner = THIS_MODULE; - i2c->adap.algo = &rk2818_i2c_algorithm; + i2c->adap.algo = &rockchip_i2c_algorithm; i2c->adap.class = I2C_CLASS_HWMON; spin_lock_init(&i2c->cmd_lock); @@ -678,7 +686,7 @@ static int rk2818_i2c_probe(struct platform_device *pdev) if(pdata->io_init) pdata->io_init(); - rk2818_i2c_init_hw(i2c); + rockchip_i2c_init_hw(i2c); i2c->irq = ret = platform_get_irq(pdev, 0); if (ret <= 0) { @@ -687,7 +695,7 @@ static int rk2818_i2c_probe(struct platform_device *pdev) } if(i2c->mode == I2C_MODE_IRQ) { - ret = request_irq(i2c->irq, rk2818_i2c_irq, IRQF_DISABLED, + ret = request_irq(i2c->irq, rockchip_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c); if (ret != 0) { @@ -695,7 +703,7 @@ static int rk2818_i2c_probe(struct platform_device *pdev) goto err_iomap; } } - ret = rk2818_i2c_register_cpufreq(i2c); + ret = rockchip_i2c_register_cpufreq(i2c); if (ret < 0) { dev_err(&pdev->dev, "failed to register cpufreq notifier\n"); goto err_irq; @@ -714,7 +722,7 @@ static int rk2818_i2c_probe(struct platform_device *pdev) return 0; err_cpufreq: - rk2818_i2c_unregister_cpufreq(i2c); + rockchip_i2c_unregister_cpufreq(i2c); err_irq: if(i2c->mode == I2C_MODE_IRQ) @@ -737,11 +745,11 @@ static int rk2818_i2c_probe(struct platform_device *pdev) } -static int rk2818_i2c_remove(struct platform_device *pdev) +static int rockchip_i2c_remove(struct platform_device *pdev) { - struct rk2818_i2c_data *i2c = platform_get_drvdata(pdev); + struct rockchip_i2c_data *i2c = platform_get_drvdata(pdev); - rk2818_i2c_unregister_cpufreq(i2c); + rockchip_i2c_unregister_cpufreq(i2c); i2c_del_adapter(&i2c->adap); if(i2c->mode == I2C_MODE_IRQ) @@ -761,51 +769,51 @@ static int rk2818_i2c_remove(struct platform_device *pdev) #ifdef CONFIG_PM -static int rk2818_i2c_suspend(struct platform_device *pdev, pm_message_t state) +static int rockchip_i2c_suspend(struct platform_device *pdev, pm_message_t state) { - struct rk2818_i2c_data *i2c = platform_get_drvdata(pdev); + struct rockchip_i2c_data *i2c = platform_get_drvdata(pdev); i2c->suspended = 1; return 0; } -static int rk2818_i2c_resume(struct platform_device *pdev) +static int rockchip_i2c_resume(struct platform_device *pdev) { - struct rk2818_i2c_data *i2c = platform_get_drvdata(pdev); + struct rockchip_i2c_data *i2c = platform_get_drvdata(pdev); i2c->suspended = 0; - rk2818_i2c_init_hw(i2c); + rockchip_i2c_init_hw(i2c); return 0; } #else -#define rk2818_i2c_suspend NULL -#define rk2818_i2c_resume NULL +#define rockchip_i2c_suspend NULL +#define rockchip_i2c_resume NULL #endif -static struct platform_driver rk2818_i2c_driver = { - .probe = rk2818_i2c_probe, - .remove = rk2818_i2c_remove, - .suspend = rk2818_i2c_suspend, - .resume = rk2818_i2c_resume, +static struct platform_driver rockchip_i2c_driver = { + .probe = rockchip_i2c_probe, + .remove = rockchip_i2c_remove, + .suspend = rockchip_i2c_suspend, + .resume = rockchip_i2c_resume, .driver = { .owner = THIS_MODULE, .name = DRV_NAME, }, }; -static int __init rk2818_i2c_adap_init(void) +static int __init rockchip_i2c_adap_init(void) { - return platform_driver_register(&rk2818_i2c_driver); + return platform_driver_register(&rockchip_i2c_driver); } -static void __exit rk2818_i2c_adap_exit(void) +static void __exit rockchip_i2c_adap_exit(void) { - platform_driver_unregister(&rk2818_i2c_driver); + platform_driver_unregister(&rockchip_i2c_driver); } -subsys_initcall(rk2818_i2c_adap_init); -module_exit(rk2818_i2c_adap_exit); +subsys_initcall(rockchip_i2c_adap_init); +module_exit(rockchip_i2c_adap_exit); MODULE_DESCRIPTION("Driver for RK2818 I2C Bus"); MODULE_AUTHOR("kfx, kfx@rock-chips.com"); diff --git a/drivers/i2c/busses/i2c-rk29.c b/drivers/i2c/busses/i2c-rk29.c new file mode 100755 index 000000000000..b5b82271f81e --- /dev/null +++ b/drivers/i2c/busses/i2c-rk29.c @@ -0,0 +1,821 @@ +/* drivers/i2c/busses/i2c_rk29.c + * + * Copyright (C) 2010 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-rk29.h" +#define DRV_NAME "rk29_i2c" + +#define RK2818_I2C_TIMEOUT (msecs_to_jiffies(500)) +#define RK2818_DELAY_TIME 2 + +#if 1 +#define i2c_dbg(dev, format, arg...) \ + dev_printk(KERN_INFO , dev , format , ## arg) +#else +#define i2c_dbg(dev, format, arg...) +#endif + +enum rk29_error { + RK2818_ERROR_NONE = 0, + RK2818_ERROR_ARBITR_LOSE, + RK2818_ERROR_UNKNOWN +}; + +enum rk29_event { + RK2818_EVENT_NONE = 0, + /* master has received ack(MTX mode) + means that data has been sent to slave. + */ + RK2818_EVENT_MTX_RCVD_ACK, + /* master needs to send ack to slave(MRX mode) + means that data has been received from slave. + */ + RK2818_EVENT_MRX_NEED_ACK, + RK2818_EVENT_MAX +}; + +struct rk29_i2c_data { + struct device *dev; + struct i2c_adapter adap; + void __iomem *regs; + struct resource *ioarea; + + unsigned int suspended:1; + unsigned long scl_rate; + unsigned long i2c_rate; + struct clk *clk; + + unsigned int mode; + + unsigned int irq; + + spinlock_t cmd_lock; + struct completion cmd_complete; + enum rk29_event cmd_event; + enum rk29_error cmd_err; + + unsigned int msg_idx; + unsigned int msg_num; +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif +}; + +static void rk29_i2c_init_hw(struct rk29_i2c_data *i2c); + +static inline void rk29_i2c_disable_irqs(struct rk29_i2c_data *i2c) +{ + unsigned long tmp; + + tmp = readl(i2c->regs + I2C_IER); + writel(tmp & IRQ_ALL_DISABLE, i2c->regs + I2C_IER); +} +static inline void rk29_i2c_enable_irqs(struct rk29_i2c_data *i2c) +{ + unsigned long tmp; + + tmp = readl(i2c->regs + I2C_IER); + writel(tmp | IRQ_MST_ENABLE, i2c->regs + I2C_IER); +} + +/* scl = pclk/(5 *(rem+1) * 2^(exp+1)) */ +static void rk29_i2c_calcdivisor(unsigned long pclk, + unsigned long scl_rate, + unsigned long *real_rate, + unsigned int *rem, unsigned int *exp) +{ + unsigned int calc_rem = 0; + unsigned int calc_exp = 0; + + for(calc_exp = 0; calc_exp < I2CCDVR_EXP_MAX; calc_exp++) + { + calc_rem = pclk / (5 * scl_rate * (1 <<(calc_exp +1))); + if(calc_rem < I2CCDVR_REM_MAX) + break; + } + if(calc_rem >= I2CCDVR_REM_MAX || calc_exp >= I2CCDVR_EXP_MAX) + { + calc_rem = I2CCDVR_REM_MAX - 1; + calc_exp = I2CCDVR_EXP_MAX - 1; + } + *rem = calc_rem; + *exp = calc_exp; + *real_rate = pclk/(5 * (calc_rem + 1) * (1 <<(calc_exp +1))); + return; +} +/* set i2c bus scl rate */ +static void rk29_i2c_clockrate(struct rk29_i2c_data *i2c) +{ +#ifdef CONFIG_ARCH_RK2818 + struct rk2818_i2c_platform_data *pdata = i2c->dev->platform_data; +#else + struct rk29_i2c_platform_data *pdata = i2c->dev->platform_data; +#endif + unsigned int rem = 0, exp = 0; + unsigned long scl_rate, real_rate = 0, tmp; + + i2c->i2c_rate = clk_get_rate(i2c->clk); + + scl_rate = (i2c->scl_rate) ? i2c->scl_rate : ((pdata->scl_rate)? pdata->scl_rate:100000); + + rk29_i2c_calcdivisor(i2c->i2c_rate, scl_rate, &real_rate, &rem, &exp); + + tmp = readl(i2c->regs + I2C_OPR); + tmp |= exp; + tmp |= rem<regs + I2C_OPR); + if(real_rate > 400000) + dev_warn(i2c->dev, "i2c_rate[%luKhz], scl_rate[%luKhz], real_rate[%luKhz] > 400Khz\n", + i2c->i2c_rate/1000, scl_rate/1000, real_rate/1000); + else + i2c_dbg(i2c->dev, "i2c_rate[%luKhz], scl_rate[%luKhz], real_rate[%luKhz]\n", + i2c->i2c_rate/1000, scl_rate/1000, real_rate/1000); + return; +} +static int rk29_event_occurred(struct rk29_i2c_data *i2c) +{ + unsigned long isr; + + isr = readl(i2c->regs + I2C_ISR); + i2c_dbg(i2c->dev,"event occurred, isr = %lx\n", isr); + if(isr & I2C_ISR_ARBITR_LOSE) + { + isr &= ~I2C_ISR_ARBITR_LOSE; + writel(isr, i2c->regs + I2C_ISR); + i2c->cmd_err = RK2818_ERROR_ARBITR_LOSE; + dev_err(i2c->dev, "arbitration loss\n"); + return 1; + } + + switch(i2c->cmd_event) + { + case RK2818_EVENT_MTX_RCVD_ACK: + if(isr & I2C_ISR_MTX_RCVD_ACK) + { + isr &= ~I2C_ISR_MTX_RCVD_ACK; + writel(isr, i2c->regs + I2C_ISR); + return 1; + } + break; + case RK2818_EVENT_MRX_NEED_ACK: + if(isr & I2C_ISR_MRX_NEED_ACK) + { + isr &= ~I2C_ISR_MRX_NEED_ACK; + writel(isr, i2c->regs + I2C_ISR); + return 1; + } + break; + default: + break; + } + i2c->cmd_err = RK2818_ERROR_UNKNOWN; + return 0; +} + +static irqreturn_t rk29_i2c_irq(int irq, void *data) +{ + struct rk29_i2c_data *i2c = (struct rk29_i2c_data *)data; + int res; + + rk29_i2c_disable_irqs(i2c); + spin_lock(&i2c->cmd_lock); + res = rk29_event_occurred(i2c); + if(res) + //if(res || i2c->cmd_err != RK2818_ERROR_NONE) + complete(&i2c->cmd_complete); + spin_unlock(&i2c->cmd_lock); + return IRQ_HANDLED; +} +static int wait_for_completion_poll_timeout(struct rk29_i2c_data *i2c, unsigned long timeout) +{ + unsigned int time = RK2818_DELAY_TIME; + int res; + + while(!time_after(jiffies, jiffies + timeout)) + { + res = rk29_event_occurred(i2c); + if(res) + //if(res || (i2c->cmd_err != RK2818_ERROR_NONE && i2c->cmd_err != RK2818_ERROR_UNKNOWN)) + return 1; + udelay(time); + time *= 2; + } + return 0; + +} +static int rk29_wait_event(struct rk29_i2c_data *i2c, + enum rk29_event mr_event) +{ + int ret = 0; + unsigned long flags; + if(i2c->mode == I2C_MODE_IRQ) + { + if(unlikely(irqs_disabled())) + { + dev_err(i2c->dev, "irqs are disabled on this system!\n"); + return -EIO; + } + } + spin_lock_irqsave(&i2c->cmd_lock,flags); + i2c->cmd_err = RK2818_ERROR_NONE; + i2c->cmd_event = mr_event; + + init_completion(&i2c->cmd_complete); + + spin_unlock_irqrestore(&i2c->cmd_lock,flags); + + rk29_i2c_enable_irqs(i2c); + if(i2c->mode == I2C_MODE_IRQ) + ret = wait_for_completion_interruptible_timeout(&i2c->cmd_complete, + RK2818_I2C_TIMEOUT); + else + ret = wait_for_completion_poll_timeout(i2c, RK2818_I2C_TIMEOUT); + + if(ret < 0) + { + dev_err(i2c->dev, "i2c wait for event %04x, retrun %d \n", mr_event, ret); + return ret; + } + if(ret == 0) + { + return -ETIMEDOUT; + } + return 0; +} + +static int rk29_wait_while_busy(struct rk29_i2c_data *i2c) +{ + unsigned long timeout = jiffies + RK2818_I2C_TIMEOUT; + unsigned long lsr; + unsigned int time = RK2818_DELAY_TIME; + + while(!time_after(jiffies, timeout)) + { + lsr = readl(i2c->regs + I2C_LSR); + if(!(lsr & I2C_LSR_BUSY)) + return 0; + udelay(time); + time *= 2; + } + return -ETIMEDOUT; +} +static int rk29_i2c_stop(struct rk29_i2c_data *i2c) +{ + unsigned long timeout = jiffies + RK2818_I2C_TIMEOUT; + unsigned int time = RK2818_DELAY_TIME; + + writel(I2C_LCMR_STOP|I2C_LCMR_RESUME, i2c->regs + I2C_LCMR); + while(!time_after(jiffies, timeout)) + { + if(!(readl(i2c->regs + I2C_LCMR) & I2C_LCMR_STOP)) + { + i2c_dbg(i2c->dev, "i2c stop successfully\n"); + return 0; + } + udelay(time); + time *= 2; + } + return -ETIMEDOUT; + +} +static int rk29_send_2nd_addr(struct rk29_i2c_data *i2c, + struct i2c_msg *msg, int start) +{ + int ret = 0; + unsigned long addr_2nd = msg->addr & 0xff; + unsigned long conr = readl(i2c->regs + I2C_CONR); + + conr |= I2C_CONR_MTX_MODE; + //conr &= I2C_CONR_ACK; + writel(conr, i2c->regs + I2C_CONR); + + i2c_dbg(i2c->dev, "i2c send addr_2nd: %lx\n", addr_2nd); + writel(addr_2nd, i2c->regs + I2C_MTXR); + writel(I2C_LCMR_RESUME, i2c->regs + I2C_LCMR); + if((ret = rk29_wait_event(i2c, RK2818_EVENT_MTX_RCVD_ACK)) != 0) + { + dev_err(i2c->dev, "after sent addr_2nd, i2c wait for ACK timeout\n"); + return ret; + } + return ret; +} +static int rk29_send_address(struct rk29_i2c_data *i2c, + struct i2c_msg *msg, int start) +{ + unsigned long addr_1st; + unsigned long conr = readl(i2c->regs + I2C_CONR); + int ret = 0; + + if(msg->flags & I2C_M_TEN) + addr_1st = (0xf0 | (((unsigned long) msg->addr & 0x300) >> 7)) & 0xff; + else + addr_1st = ((msg->addr << 1) & 0xff); + + if (msg->flags & I2C_M_RD) + addr_1st |= 0x01; + else + addr_1st &= (~0x01); + + conr |= I2C_CONR_MTX_MODE; + //conr &= I2C_CONR_ACK; + writel(conr, i2c->regs + I2C_CONR); + i2c_dbg(i2c->dev, "i2c send addr_1st: %lx\n", addr_1st); + + writel(addr_1st, i2c->regs + I2C_MTXR); + + if (start) + { + if((ret = rk29_wait_while_busy(i2c)) != 0) + { + dev_err(i2c->dev, "i2c is busy, when send address\n"); + return ret; + } + writel(I2C_LCMR_START, i2c->regs + I2C_LCMR); + } + else + writel(I2C_LCMR_START|I2C_LCMR_RESUME, i2c->regs + I2C_LCMR); + + if((ret = rk29_wait_event(i2c, RK2818_EVENT_MTX_RCVD_ACK)) != 0) + { + dev_err(i2c->dev, "after sent addr_1st, i2c wait for ACK timeout\n"); + return ret; + } + if(start && (msg->flags & I2C_M_TEN)) + ret = rk29_send_2nd_addr(i2c, msg, start); + return ret; +} + +static int rk29_i2c_send_msg(struct rk29_i2c_data *i2c, struct i2c_msg *msg) +{ + int i, ret = 0; + unsigned long conr = readl(i2c->regs + I2C_CONR); + + conr = readl(i2c->regs + I2C_CONR); + conr |= I2C_CONR_MTX_MODE; + writel(conr, i2c->regs + I2C_CONR); + for(i = 0; i < msg->len; i++) + { + i2c_dbg(i2c->dev, "i2c send buf[%d]: %x\n", i, msg->buf[i]); + writel(msg->buf[i], i2c->regs + I2C_MTXR); + /* + conr = readl(i2c->regs + I2C_CONR); + conr &= I2C_CONR_ACK; + writel(conr, i2c->regs + I2C_CONR); + */ + writel(I2C_LCMR_RESUME, i2c->regs + I2C_LCMR); + + if((ret = rk29_wait_event(i2c, RK2818_EVENT_MTX_RCVD_ACK)) != 0) + return ret; + } + return ret; +} +static int rk29_i2c_recv_msg(struct rk29_i2c_data *i2c, struct i2c_msg *msg) +{ + int i, ret = 0; + unsigned long conr = readl(i2c->regs + I2C_CONR); + + conr = readl(i2c->regs + I2C_CONR); + conr &= I2C_CONR_MRX_MODE; + writel(conr, i2c->regs + I2C_CONR); + + for(i = 0; i < msg->len; i++) + { + writel(I2C_LCMR_RESUME, i2c->regs + I2C_LCMR); + if((ret = rk29_wait_event(i2c, RK2818_EVENT_MRX_NEED_ACK)) != 0) + return ret; + conr = readl(i2c->regs + I2C_CONR); + conr &= I2C_CONR_ACK; + writel(conr, i2c->regs + I2C_CONR); + msg->buf[i] = (uint8_t)readl(i2c->regs + I2C_MRXR); + i2c_dbg(i2c->dev, "i2c recv >>>>>>>>>>>> buf[%d]: %x\n", i, msg->buf[i]); + } + return ret; +} +static int rk29_xfer_msg(struct i2c_adapter *adap, + struct i2c_msg *msg, int start, int stop) +{ + struct rk29_i2c_data *i2c = (struct rk29_i2c_data *)adap->algo_data; + unsigned long conr = readl(i2c->regs + I2C_CONR); + int ret = 0; + + #if defined (CONFIG_IOEXTEND_TCA6424) + struct tca6424_platform_data *pdata = adap->dev.platform_data; + #endif + + if(msg->len == 0) + { + ret = -EINVAL; + dev_err(i2c->dev, "msg->len = %d\n", msg->len); + goto exit; + } + + if((ret = rk29_send_address(i2c, msg, start))!= 0) + { + dev_err(i2c->dev, "rk29_send_address timeout\n"); + goto exit; + } + if(msg->flags & I2C_M_RD) + { + if((ret = rk29_i2c_recv_msg(i2c, msg)) != 0) + { + dev_err(i2c->dev, "rk29_i2c_recv_msg timeout\n"); + goto exit; + } + } + else + { + if((ret = rk29_i2c_send_msg(i2c, msg)) != 0) + { + dev_err(i2c->dev, "rk29_i2c_send_msg timeout\n"); + goto exit; + } + } + +exit: + if(stop) + { + conr = readl(i2c->regs + I2C_CONR); + conr |= I2C_CONR_NAK; + writel(conr, i2c->regs + I2C_CONR); + if((ret = rk29_i2c_stop(i2c)) != 0) + { + dev_err(i2c->dev, "rk29_i2c_stop timeout\n"); + } +#if 0 + //not I2C code,add by sxj,used for extend gpio intrrupt,set SCL and SDA pin. + if(msg->flags & I2C_M_RD) + { + #if defined (CONFIG_IOEXTEND_TCA6424) + if (pdata && pdata->reseti2cpin) { + pdata->reseti2cpin(); + } + #endif + } + #endif + } + return ret; + +} + +static int rk29_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + int ret = -1; + int i; + struct rk29_i2c_data *i2c = (struct rk29_i2c_data *)adap->algo_data; + unsigned long conr = readl(i2c->regs + I2C_CONR); + /* + if(i2c->suspended ==1) + return -EIO; + */ + if(msgs[0].scl_rate <= 400000 && msgs[0].scl_rate > 0) + i2c->scl_rate = msgs[0].scl_rate; + else + i2c->scl_rate = 400000; + + conr |= I2C_CONR_MPORT_ENABLE; + writel(conr, i2c->regs + I2C_CONR); + + rk29_i2c_init_hw(i2c); + + conr = readl(i2c->regs + I2C_CONR); + conr &= I2C_CONR_ACK; + writel(conr, i2c->regs + I2C_CONR); + + for (i = 0; i < num; i++) + { + ret = rk29_xfer_msg(adap, &msgs[i], (i == 0), (i == (num - 1))); + if (ret != 0) + { + num = ret; + dev_err(i2c->dev, "rk29_xfer_msg error, ret = %d\n", ret); + break; + } + } + conr = readl(i2c->regs + I2C_CONR); + conr &= I2C_CONR_MPORT_DISABLE; + writel(conr, i2c->regs + I2C_CONR); + return num; +} + +static u32 rk29_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_algorithm rk29_i2c_algorithm = { + .master_xfer = rk29_i2c_xfer, + .functionality = rk29_i2c_func, +}; + +int i2c_suspended(struct i2c_adapter *adap) +{ + struct rk29_i2c_data *i2c = (struct rk29_i2c_data *)adap->algo_data; + if(adap->nr > 1) + return 1; + if(i2c == NULL) + return 1; + return i2c->suspended; +} +EXPORT_SYMBOL(i2c_suspended); + +static void rk29_i2c_init_hw(struct rk29_i2c_data *i2c) +{ + unsigned long lcmr = 0x00000000; + + unsigned long opr = readl(i2c->regs + I2C_OPR); + opr |= I2C_OPR_RESET_STATUS; + writel(opr, i2c->regs + I2C_OPR); + + writel(lcmr, i2c->regs + I2C_LCMR); + + rk29_i2c_disable_irqs(i2c); + + rk29_i2c_clockrate(i2c); + + opr = readl(i2c->regs + I2C_OPR); + opr |= I2C_OPR_CORE_ENABLE; + writel(opr, i2c->regs + I2C_OPR); + + return; +} + +#ifdef CONFIG_CPU_FREQ + +#define freq_to_i2c(_n) container_of(_n, struct rk29_i2c_data, freq_transition) + +static int rk29_i2c_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct rk29_i2c_data *i2c = freq_to_i2c(nb); + unsigned long flags; + int delta_f; + delta_f = clk_get_rate(i2c->clk) - i2c->i2c_rate; + + if ((val == CPUFREQ_POSTCHANGE && delta_f < 0) || + (val == CPUFREQ_PRECHANGE && delta_f > 0)) + { + spin_lock_irqsave(&i2c->cmd_lock, flags); + rk29_i2c_clockrate(i2c); + spin_unlock_irqrestore(&i2c->cmd_lock, flags); + } + return 0; +} + +static inline int rk29_i2c_register_cpufreq(struct rk29_i2c_data *i2c) +{ + i2c->freq_transition.notifier_call = rk29_i2c_cpufreq_transition; + + return cpufreq_register_notifier(&i2c->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void rk29_i2c_unregister_cpufreq(struct rk29_i2c_data *i2c) +{ + cpufreq_unregister_notifier(&i2c->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else +static inline int rk29_i2c_register_cpufreq(struct rk29_i2c_data *i2c) +{ + return 0; +} + +static inline void rk29_i2c_unregister_cpufreq(struct rk29_i2c_data *i2c) +{ + return; +} +#endif + +static int rk29_i2c_probe(struct platform_device *pdev) +{ + struct rk29_i2c_data *i2c; +#ifdef CONFIG_ARCH_RK2818 + struct rk2818_i2c_platform_data *pdata = NULL; +#else + struct rk29_i2c_platform_data *pdata = NULL; +#endif + struct resource *res; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) + { + dev_err(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + i2c = kzalloc(sizeof(struct rk29_i2c_data), GFP_KERNEL); + if (!i2c) + { + dev_err(&pdev->dev, "no memory for state\n"); + return -ENOMEM; + } + i2c->mode = pdata->mode; + i2c->scl_rate = (pdata->scl_rate) ? pdata->scl_rate : 100000; + + strlcpy(i2c->adap.name, DRV_NAME, sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &rk29_i2c_algorithm; + i2c->adap.class = I2C_CLASS_HWMON; + spin_lock_init(&i2c->cmd_lock); + + 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; + } + + clk_enable(i2c->clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "cannot find IO resource\n"); + ret = -ENOENT; + goto err_clk; + } + + i2c->ioarea = request_mem_region(res->start, res->end - res->start + 1, + pdev->name); + + if (i2c->ioarea == NULL) { + dev_err(&pdev->dev, "cannot request IO\n"); + ret = -ENXIO; + goto err_clk; + } + + i2c->regs = ioremap(res->start, res->end - res->start + 1); + + if (i2c->regs == NULL) { + dev_err(&pdev->dev, "annot map IO\n"); + ret = -ENXIO; + goto err_ioarea; + } + i2c->adap.algo_data = i2c; + i2c->adap.dev.parent = &pdev->dev; + + if(pdata->io_init) + pdata->io_init(); + + rk29_i2c_init_hw(i2c); + + i2c->irq = ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "cannot find IRQ\n"); + goto err_iomap; + } + if(i2c->mode == I2C_MODE_IRQ) + { + ret = request_irq(i2c->irq, rk29_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_iomap; + } + } + ret = rk29_i2c_register_cpufreq(i2c); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register cpufreq notifier\n"); + goto err_irq; + } + + i2c->adap.nr = pdata->bus_num; + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add bus to i2c core\n"); + goto err_cpufreq; + } + + platform_set_drvdata(pdev, i2c); + + dev_info(&pdev->dev, "%s: RK2818 I2C adapter\n", dev_name(&i2c->adap.dev)); + return 0; + + err_cpufreq: + rk29_i2c_unregister_cpufreq(i2c); + + err_irq: + if(i2c->mode == I2C_MODE_IRQ) + free_irq(i2c->irq, i2c); + + err_iomap: + iounmap(i2c->regs); + + err_ioarea: + release_resource(i2c->ioarea); + kfree(i2c->ioarea); + + err_clk: + clk_disable(i2c->clk); + clk_put(i2c->clk); + + err_noclk: + kfree(i2c); + return ret; +} + + +static int rk29_i2c_remove(struct platform_device *pdev) +{ + struct rk29_i2c_data *i2c = platform_get_drvdata(pdev); + + rk29_i2c_unregister_cpufreq(i2c); + + i2c_del_adapter(&i2c->adap); + if(i2c->mode == I2C_MODE_IRQ) + free_irq(i2c->irq, i2c); + + clk_disable(i2c->clk); + clk_put(i2c->clk); + + iounmap(i2c->regs); + + release_resource(i2c->ioarea); + kfree(i2c->ioarea); + kfree(i2c); + + return 0; +} + +#ifdef CONFIG_PM + +static int rk29_i2c_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct rk29_i2c_data *i2c = platform_get_drvdata(pdev); + + i2c->suspended = 1; + return 0; +} +static int rk29_i2c_resume(struct platform_device *pdev) +{ + struct rk29_i2c_data *i2c = platform_get_drvdata(pdev); + + i2c->suspended = 0; + rk29_i2c_init_hw(i2c); + + return 0; +} +#else +#define rk29_i2c_suspend NULL +#define rk29_i2c_resume NULL +#endif + + +static struct platform_driver rk29_i2c_driver = { + .probe = rk29_i2c_probe, + .remove = rk29_i2c_remove, + .suspend = rk29_i2c_suspend, + .resume = rk29_i2c_resume, + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + }, +}; + +static int __init rk29_i2c_adap_init(void) +{ + return platform_driver_register(&rk29_i2c_driver); +} + +static void __exit rk29_i2c_adap_exit(void) +{ + platform_driver_unregister(&rk29_i2c_driver); +} + +subsys_initcall(rk29_i2c_adap_init); +module_exit(rk29_i2c_adap_exit); + +MODULE_DESCRIPTION("Driver for RK2818 I2C Bus"); +MODULE_AUTHOR("kfx, kfx@rock-chips.com"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-rk29.h b/drivers/i2c/busses/i2c-rk29.h new file mode 100755 index 000000000000..97bf03cd8fc2 --- /dev/null +++ b/drivers/i2c/busses/i2c-rk29.h @@ -0,0 +1,73 @@ +/* drivers/i2c/busses/i2c_rk2818.h + * + * Copyright (C) 2010 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __RK2818_I2C_H +#define __RK2818_I2C_H + +/* master transmit */ +#define I2C_MTXR (0x0000) +/* master receive */ +#define I2C_MRXR (0x0004) +/* slave address */ +#define I2C_SADDR (0x0010) +/* interrupt enable control */ +#define I2C_IER (0x0014) +#define I2C_IER_ARBITR_LOSE (1<<7) +#define I2C_IER_MRX_NEED_ACK (1<<1) +#define I2C_IER_MTX_RCVD_ACK (1<<0) + +#define IRQ_MST_ENABLE (I2C_IER_ARBITR_LOSE | \ + I2C_IER_MRX_NEED_ACK | \ + I2C_IER_MTX_RCVD_ACK) +#define IRQ_ALL_DISABLE (0x00) + +/* interrupt status, write 0 to clear */ +#define I2C_ISR (0x0018) +#define I2C_ISR_ARBITR_LOSE (1<<7) +#define I2C_ISR_MRX_NEED_ACK (1<<1) +#define I2C_ISR_MTX_RCVD_ACK (1<<0) + +/* stop/start/resume command, write 1 to set */ +#define I2C_LCMR (0x001c) +#define I2C_LCMR_RESUME (1<<2) +#define I2C_LCMR_STOP (1<<1) +#define I2C_LCMR_START (1<<0) + +/* i2c core status */ +#define I2C_LSR (0x0020) +#define I2C_LSR_RCV_NAK (1<<1) +#define I2C_LSR_RCV_ACK (~(1<<1)) +#define I2C_LSR_BUSY (1<<0) + +/* i2c config */ +#define I2C_CONR (0x0024) +#define I2C_CONR_NAK (1<<4) +#define I2C_CONR_ACK (~(1<<4)) +#define I2C_CONR_MTX_MODE (1<<3) +#define I2C_CONR_MRX_MODE (~(1<<3)) +#define I2C_CONR_MPORT_ENABLE (1<<2) +#define I2C_CONR_MPORT_DISABLE (~(1<<2)) + +/* i2c core config */ +#define I2C_OPR (0x0028) +#define I2C_OPR_RESET_STATUS (1<<7) +#define I2C_OPR_CORE_ENABLE (1<<6) + +#define I2CCDVR_REM_BITS (0x03) +#define I2CCDVR_REM_MAX (1<<(I2CCDVR_REM_BITS)) +#define I2CCDVR_EXP_BITS (0x03) +#define I2CCDVR_EXP_MAX (1<<(I2CCDVR_EXP_BITS)) + +#endif diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 5a3264f4abff..fac7cedadc73 100755 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1091,7 +1091,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : ""); } #endif -#ifdef CONFIG_I2C_RK2818 +#if defined (CONFIG_I2C_RK2818) || defined(CONFIG_I2C_RK29) if (!(i2c_suspended(adap)) && (in_atomic() || irqs_disabled())) { #else if (in_atomic() || irqs_disabled()) { @@ -1123,7 +1123,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) } } EXPORT_SYMBOL(i2c_transfer); -#if defined (CONFIG_I2C_RK2818) +#if defined (CONFIG_I2C_RK2818) || defined(CONFIG_I2C_RK29) int i2c_master_send(struct i2c_client *client,const char *buf ,int count) { int ret; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 63276d3f88f0..28af1f86cdc8 100755 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -54,7 +54,7 @@ struct i2c_board_info; * transmit one message at a time, a more complex version can be used to * transmit an arbitrary number of messages without interruption. */ -#if defined (CONFIG_I2C_RK2818) +#if defined (CONFIG_I2C_RK2818) || defined(CONFIG_I2C_RK29) /* If everything went ok, return 'count' transmitted, else error code. */ extern int i2c_master_normal_send(struct i2c_client *client,const char *buf ,int count, int scl_rate); extern int i2c_master_normal_recv(struct i2c_client *client, char *buf ,int count, int scl_rate);