From cb383d1c2e9989ae23d1b810f452d9398d3e928b Mon Sep 17 00:00:00 2001 From: kfx Date: Wed, 15 Feb 2012 20:17:42 +0800 Subject: [PATCH] RK30 I2C drivers: new adapter(rk30) support --- arch/arm/mach-rk30/devices.c | 110 ++++--- drivers/i2c/busses/Makefile | 2 +- drivers/i2c/busses/i2c-rk29-adapter.c | 60 +--- drivers/i2c/busses/i2c-rk30-adapter.c | 457 +++++++++++++++++++++++++- drivers/i2c/busses/i2c-rk30-test.c | 128 ++++++++ drivers/i2c/busses/i2c-rk30.c | 18 +- drivers/i2c/busses/i2c-rk30.h | 22 +- 7 files changed, 703 insertions(+), 94 deletions(-) create mode 100644 drivers/i2c/busses/i2c-rk30-test.c diff --git a/arch/arm/mach-rk30/devices.c b/arch/arm/mach-rk30/devices.c index f597d442b503..f587b0739825 100755 --- a/arch/arm/mach-rk30/devices.c +++ b/arch/arm/mach-rk30/devices.c @@ -265,16 +265,66 @@ static void __init rk30_init_uart(void) } // 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, +#define I2C0_ADAP_TYPE I2C_RK29_ADAP +#define I2C0_START RK30_I2C0_PHYS +#define I2C0_END RK30_I2C0_PHYS + SZ_4K - 1 #endif #ifdef CONFIG_I2C0_CONTROLLER_RK30 - .adap_type = I2C_RK30_ADAP, +#define I2C0_ADAP_TYPE I2C_RK30_ADAP +#define I2C0_START RK30_I2C0_PHYS + SZ_4K +#define I2C0_END RK30_I2C0_PHYS + SZ_8K - 1 +#endif + +#ifdef CONFIG_I2C1_CONTROLLER_RK29 +#define I2C1_ADAP_TYPE I2C_RK29_ADAP +#define I2C1_START RK30_I2C1_PHYS +#define I2C1_END RK30_I2C1_PHYS + SZ_4K - 1 +#endif +#ifdef CONFIG_I2C1_CONTROLLER_RK30 +#define I2C1_ADAP_TYPE I2C_RK30_ADAP +#define I2C1_START RK30_I2C1_PHYS + SZ_4K +#define I2C1_END RK30_I2C1_PHYS + SZ_8K - 1 +#endif + +#ifdef CONFIG_I2C2_CONTROLLER_RK29 +#define I2C2_ADAP_TYPE I2C_RK29_ADAP +#define I2C2_START RK30_I2C2_PHYS +#define I2C2_END RK30_I2C2_PHYS + SZ_4K - 1 #endif +#ifdef CONFIG_I2C2_CONTROLLER_RK30 +#define I2C2_ADAP_TYPE I2C_RK30_ADAP +#define I2C2_START RK30_I2C2_PHYS + SZ_4K +#define I2C2_END RK30_I2C2_PHYS + SZ_8K - 1 +#endif + +#ifdef CONFIG_I2C3_CONTROLLER_RK29 +#define I2C3_ADAP_TYPE I2C_RK29_ADAP +#define I2C3_START RK30_I2C3_PHYS +#define I2C3_END RK30_I2C3_PHYS + SZ_4K - 1 +#endif +#ifdef CONFIG_I2C3_CONTROLLER_RK30 +#define I2C3_ADAP_TYPE I2C_RK30_ADAP +#define I2C3_START RK30_I2C3_PHYS + SZ_4K +#define I2C3_END RK30_I2C3_PHYS + SZ_8K - 1 +#endif + +#ifdef CONFIG_I2C4_CONTROLLER_RK29 +#define I2C4_ADAP_TYPE I2C_RK29_ADAP +#define I2C4_START RK30_I2C4_PHYS +#define I2C4_END RK30_I2C4_PHYS + SZ_4K - 1 +#endif +#ifdef CONFIG_I2C4_CONTROLLER_RK30 +#define I2C4_ADAP_TYPE I2C_RK30_ADAP +#define I2C4_START RK30_I2C4_PHYS + SZ_4K +#define I2C4_END RK30_I2C4_PHYS + SZ_8K - 1 +#endif + +#ifdef CONFIG_I2C0_RK30 +static struct rk30_i2c_platform_data default_i2c0_data = { + .bus_num = 0, + .is_div_from_arm = 1, + .adap_type = I2C0_ADAP_TYPE, }; static struct resource resources_i2c0[] = { @@ -284,8 +334,8 @@ static struct resource resources_i2c0[] = { .flags = IORESOURCE_IRQ, }, { - .start = RK30_I2C0_PHYS, - .end = RK30_I2C0_PHYS + RK30_I2C0_SIZE - 1, + .start = I2C0_START, + .end = I2C0_END, .flags = IORESOURCE_MEM, }, }; @@ -305,12 +355,7 @@ static struct platform_device device_i2c0 = { 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 + .adap_type = I2C1_ADAP_TYPE, }; static struct resource resources_i2c1[] = { @@ -320,8 +365,8 @@ static struct resource resources_i2c1[] = { .flags = IORESOURCE_IRQ, }, { - .start = RK30_I2C1_PHYS, - .end = RK30_I2C1_PHYS + RK30_I2C1_SIZE - 1, + .start = I2C1_START, + .end = I2C1_END, .flags = IORESOURCE_MEM, }, }; @@ -341,12 +386,7 @@ static struct platform_device device_i2c1 = { 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 + .adap_type = I2C2_ADAP_TYPE, }; static struct resource resources_i2c2[] = { @@ -356,8 +396,8 @@ static struct resource resources_i2c2[] = { .flags = IORESOURCE_IRQ, }, { - .start = RK30_I2C2_PHYS, - .end = RK30_I2C2_PHYS + RK30_I2C2_SIZE - 1, + .start = I2C2_START, + .end = I2C2_END, .flags = IORESOURCE_MEM, }, }; @@ -377,12 +417,7 @@ static struct platform_device device_i2c2 = { 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 + .adap_type = I2C3_ADAP_TYPE, }; static struct resource resources_i2c3[] = { @@ -392,8 +427,8 @@ static struct resource resources_i2c3[] = { .flags = IORESOURCE_IRQ, }, { - .start = RK30_I2C3_PHYS, - .end = RK30_I2C3_PHYS + RK30_I2C3_SIZE - 1, + .start = I2C3_START, + .end = I2C3_END, .flags = IORESOURCE_MEM, }, }; @@ -413,12 +448,7 @@ static struct platform_device device_i2c3 = { 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 + .adap_type = I2C4_ADAP_TYPE, }; static struct resource resources_i2c4[] = { @@ -428,8 +458,8 @@ static struct resource resources_i2c4[] = { .flags = IORESOURCE_IRQ, }, { - .start = RK30_I2C4_PHYS, - .end = RK30_I2C4_PHYS + RK30_I2C4_SIZE - 1, + .start = I2C4_START, + .end = I2C4_END, .flags = IORESOURCE_MEM, }, }; diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index bb71ae214ec3..3948ba06a19c 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -3,7 +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-$(CONFIG_I2C_RK30) += i2c-rk30.o i2c-rk29-adapter.o i2c-rk30-adapter.o i2c-rk30-test.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 index a560db3ce150..edae8e907693 100755 --- a/drivers/i2c/busses/i2c-rk29-adapter.c +++ b/drivers/i2c/busses/i2c-rk29-adapter.c @@ -70,10 +70,6 @@ #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) { @@ -171,7 +167,7 @@ static void rk29_i2c_set_clk(struct rk30_i2c *i2c, unsigned long scl_rate) return; } -static void rk29_i2c_init_hw(struct rk30_i2c *i2c) +static void rk29_i2c_init_hw(struct rk30_i2c *i2c, unsigned long scl_rate) { unsigned long opr = readl(i2c->regs + I2C_OPR); @@ -183,7 +179,7 @@ static void rk29_i2c_init_hw(struct rk30_i2c *i2c) opr &= ~I2C_OPR_RESET_STATUS; writel(opr, i2c->regs + I2C_OPR); - rk29_i2c_set_clk(i2c, 100000); + rk29_i2c_set_clk(i2c, scl_rate); rk29_i2c_disable_irq(i2c); writel(0, i2c->regs + I2C_LCMR); @@ -242,19 +238,9 @@ static void rk29_i2c_message_start(struct rk30_i2c *i2c, 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; @@ -306,16 +292,14 @@ static int rk29_i2c_irq_nextbyte(struct rk30_i2c *i2c, unsigned long isr) 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); + rk29_i2c_stop(i2c, -EAGAIN); dev_err(i2c->dev, "START: addr[0x%02x] ack was not received(lsr)\n", i2c->addr); goto out; } - if (i2c->msg->flags & I2C_M_RD) i2c->state = STATE_READ; else @@ -474,6 +458,7 @@ static int rk29_i2c_set_master(struct rk30_i2c *i2c) lsr = readl(i2c->regs + I2C_LSR); if (!(lsr & I2C_LSR_BUSY)) return 0; + writel(I2C_LCMR_STOP|I2C_LCMR_RESUME, i2c->regs + I2C_LCMR); msleep(1); } return -ETIMEDOUT; @@ -512,7 +497,7 @@ static int rk29_i2c_doxfer(struct rk30_i2c *i2c, rk29_i2c_message_start(i2c, msgs); spin_unlock_irq(&i2c->lock); - timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, msecs_to_jiffies(I2C_WAIT_TIMEOUT)); ret = i2c->msg_idx; @@ -520,15 +505,19 @@ static int rk29_i2c_doxfer(struct rk30_i2c *i2c, 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) + msleep(1); + writel(I2C_LCMR_STOP|I2C_LCMR_RESUME, i2c->regs + I2C_LCMR); + if((readl(i2c->regs + I2C_LSR) & I2C_LSR_BUSY) || + readl(i2c->regs + I2C_LCMR) & I2C_LCMR_STOP ){ + dev_warn(i2c->dev, "WARNING: STOP abnormal, addr[0x%02x] isr = 0x%x, lsr = 0x%x, lcmr = 0x%x\n", + msgs[0].addr, + readl(i2c->regs + I2C_ISR), + readl(i2c->regs + I2C_LSR), + readl(i2c->regs + I2C_LCMR) ); + } } out: return ret; @@ -544,8 +533,7 @@ 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; + int ret = 0; unsigned long scl_rate; if(msgs[0].scl_rate <= 400000 && msgs[0].scl_rate >= 10000) @@ -567,23 +555,12 @@ static int rk29_i2c_xfer(struct i2c_adapter *adap, rk29_i2c_enable_mport(i2c); 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); + ret = rk29_i2c_doxfer(i2c, msgs, num); - msleep(1); - } - rk29_i2c_disable_mport(i2c); if(i2c->is_div_from_arm[i2c->adap.nr]) wake_unlock(&i2c->idlelock[i2c->adap.nr]); - return -EREMOTEIO; + return ret; } /* declare our i2c functionality */ @@ -605,7 +582,6 @@ int i2c_add_rk29_adapter(struct i2c_adapter *adap) struct rk30_i2c *i2c = (struct rk30_i2c *)adap->algo_data; adap->algo = &rk29_i2c_algorithm; - adap->retries = 2; i2c->i2c_init_hw = &rk29_i2c_init_hw; i2c->i2c_set_clk = &rk29_i2c_set_clk; diff --git a/drivers/i2c/busses/i2c-rk30-adapter.c b/drivers/i2c/busses/i2c-rk30-adapter.c index ea1e350c84f3..d7459c31333d 100755 --- a/drivers/i2c/busses/i2c-rk30-adapter.c +++ b/drivers/i2c/busses/i2c-rk30-adapter.c @@ -14,26 +14,477 @@ */ #include "i2c-rk30.h" +/* Control register */ +#define I2C_CON 0X000 +enum{ + I2C_EN_BIT = 0, + I2C_MOD_BIT = 1, + I2C_START_BIT = 3, + I2C_STOP_BIT = 4, + I2C_LAST_ACK_BIT = 5, + I2C_ACT2ACK_BIT = 6, +}; +//send ACK to slave when the last byte received in RX only mode +#define LAST_SEND_ACK 0 +//send NAK to slave when the last byte received in RX only mode +#define LAST_SEND_NAK 1 +#define LAST_SEND_TYPE LAST_SEND_ACK //LAST_SEND_NAK + +#define I2C_MOD_MASK (3 << I2C_MOD_BIT) +enum{ + I2C_MOD_TX = 0, + I2C_MOD_TRX, + I2C_MOD_RX, + I2C_MOD_RRX, +}; +/* Clock dividor register */ +#define I2C_CLKDIV 0x004 +#define I2C_CLKDIV_VAL(divl, divh) (((divl) & 0xffff) | (((divh) << 16) & 0xffff0000)) +/* the slave address accessed for master rx mode */ +#define I2C_MRXADDR 0x008 +#define I2C_MRXADDR_LOW (1 << 24) +#define I2C_MRXADDR_MID (1 << 25) +#define I2C_MRXADDR_HIGH (1 << 26) +/* the slave register address accessed for master rx mode */ +#define I2C_MRXRADDR 0x00c +#define I2C_MRXRADDR_LOW (1 << 24) +#define I2C_MRXRADDR_MID (1 << 25) +#define I2C_MRXRADDR_HIGH (1 << 26) +/* master tx count */ +#define I2C_MTXCNT 0x010 +/* master rx count */ +#define I2C_MRXCNT 0x014 +/* interrupt enable register */ +#define I2C_IEN 0x018 +#define I2C_BTFIEN (1 << 0) +#define I2C_BRFIEN (1 << 1) +#define I2C_MBTFIEN (1 << 2) +#define I2C_MBRFIEN (1 << 3) +#define I2C_STARTIEN (1 << 4) +#define I2C_STOPIEN (1 << 5) +#define I2C_NAKRCVIEN (1 << 6) +#define IRQ_MST_ENABLE (I2C_MBTFIEN | I2C_MBRFIEN | I2C_NAKRCVIEN | I2C_STARTIEN | I2C_STOPIEN) +#define IRQ_ALL_DISABLE 0 +/* interrupt pending register */ +#define I2C_IPD 0x01c +#define I2C_BTFIPD (1 << 0) +#define I2C_BRFIPD (1 << 1) +#define I2C_MBTFIPD (1 << 2) +#define I2C_MBRFIPD (1 << 3) +#define I2C_STARTIPD (1 << 4) +#define I2C_STOPIPD (1 << 5) +#define I2C_NAKRCVIPD (1 << 6) +/* finished count */ +#define I2C_FCNT 0x020 +/* I2C tx data register */ +#define I2C_TXDATA_BASE 0X100 +/* I2C rx data register */ +#define I2C_RXDATA_BASE 0x200 +static void rk30_show_regs(struct rk30_i2c *i2c) +{ + i2c_dbg(i2c->dev, "I2C_CON: 0x%08x\n", readl(i2c->regs + I2C_CON)); + i2c_dbg(i2c->dev, "I2C_CLKDIV: 0x%08x\n", readl(i2c->regs + I2C_CLKDIV)); + i2c_dbg(i2c->dev, "I2C_MRXADDR: 0x%08x\n", readl(i2c->regs + I2C_MRXADDR)); + i2c_dbg(i2c->dev, "I2C_MRXRADDR: 0x%08x\n", readl(i2c->regs + I2C_MRXRADDR)); + i2c_dbg(i2c->dev, "I2C_MTXCNT: 0x%08x\n", readl(i2c->regs + I2C_MTXCNT)); + i2c_dbg(i2c->dev, "I2C_MRXCNT: 0x%08x\n", readl(i2c->regs + I2C_MRXCNT)); + i2c_dbg(i2c->dev, "I2C_IEN: 0x%08x\n", readl(i2c->regs + I2C_IEN)); + i2c_dbg(i2c->dev, "I2C_IPD: 0x%08x\n", readl(i2c->regs + I2C_IPD)); + i2c_dbg(i2c->dev, "I2C_FCNT: 0x%08x\n", readl(i2c->regs + I2C_FCNT)); + i2c_dbg(i2c->dev, "I2C_TXDATA0: 0x%08x\n", readl(i2c->regs + I2C_TXDATA_BASE + 0)); + i2c_dbg(i2c->dev, "I2C_RXDATA0: 0x%08x\n", readl(i2c->regs + I2C_RXDATA_BASE + 0)); +} +static inline void rk30_i2c_last_ack(struct rk30_i2c *i2c, int enable) +{ + unsigned int p = readl(i2c->regs + I2C_CON); + + writel(rk30_set_bit(p, enable, I2C_LAST_ACK_BIT), i2c->regs + I2C_CON); +} +static inline void rk30_i2c_act2ack(struct rk30_i2c *i2c, int enable) +{ + unsigned int p = readl(i2c->regs + I2C_CON); + + writel(rk30_set_bit(p, enable, I2C_ACT2ACK_BIT), i2c->regs + I2C_CON); +} +static inline void rk30_i2c_enable(struct rk30_i2c *i2c, int enable) +{ + unsigned int p = readl(i2c->regs + I2C_CON); + + writel(rk30_set_bit(p, enable, I2C_EN_BIT), i2c->regs + I2C_CON); +} +static inline void rk30_i2c_set_mode(struct rk30_i2c *i2c) +{ + unsigned int p = readl(i2c->regs + I2C_CON); + + writel(rk30_set_bits(p, i2c->mode, I2C_MOD_BIT, I2C_MOD_MASK), i2c->regs + I2C_CON); +} +static inline void rk30_i2c_disable_irq(struct rk30_i2c *i2c) +{ + writel(IRQ_ALL_DISABLE, i2c->regs + I2C_IEN); +} + +static inline void rk30_i2c_enable_irq(struct rk30_i2c *i2c) +{ + writel(IRQ_MST_ENABLE, i2c->regs + I2C_IEN); +} + +static inline void rk30_i2c_send_start(struct rk30_i2c *i2c) +{ + unsigned int p = readl(i2c->regs + I2C_CON); + + p = rk30_set_bit(p, 1, I2C_START_BIT); + p = rk30_set_bit(p, 0, I2C_STOP_BIT); + writel(p, i2c->regs + I2C_CON); +} +static inline void rk30_i2c_send_stop(struct rk30_i2c *i2c) +{ + unsigned int p = readl(i2c->regs + I2C_CON); + + p = rk30_set_bit(p, 0, I2C_START_BIT); + p = rk30_set_bit(p, 1, I2C_STOP_BIT); + writel(p, i2c->regs + I2C_CON); + +} +/* SCL Divisor = CLKDIVL + CLKDIVH + * SCL = i2c_rate/ 8*SCLK Divisor +*/ static void rk30_i2c_set_clk(struct rk30_i2c *i2c, unsigned long scl_rate) { + unsigned long i2c_rate = clk_get_rate(i2c->clk); + + unsigned int div, divl, divh; + + if((scl_rate == i2c->scl_rate) && (i2c_rate == i2c->i2c_rate)) + return; + i2c->i2c_rate = i2c_rate; + i2c->scl_rate = scl_rate; + div = rk30_ceil(i2c_rate, scl_rate * 8); + divh = divl = rk30_ceil(div, 2); + writel(I2C_CLKDIV_VAL(divl, divh), i2c->regs + I2C_CLKDIV); return; } -static void rk30_i2c_init_hw(struct rk30_i2c *i2c) +static void rk30_i2c_init_hw(struct rk30_i2c *i2c, unsigned long scl_rate) { + rk30_i2c_set_clk(i2c, scl_rate); return; } +/* returns TRUE if we this is the last byte in the current message */ +static inline int is_msglast(struct rk30_i2c *i2c) +{ + return i2c->msg_ptr == i2c->msg->len-1; +} + +/* returns TRUE if we reached the end of the current message */ +static inline int is_msgend(struct rk30_i2c *i2c) +{ + return i2c->msg_ptr >= i2c->msg->len; +} +static void rk30_i2c_stop(struct rk30_i2c *i2c, int ret) +{ + + i2c->msg_ptr = 0; + i2c->msg = NULL; + i2c->msg_idx++; + i2c->msg_num = 0; + if (ret) + i2c->msg_idx = ret; + + i2c->state = STATE_STOP; + rk30_i2c_send_stop(i2c); +} +static void rk30_irq_read_prepare(struct rk30_i2c *i2c) +{ + unsigned int cnt, len = i2c->msg->len - i2c->msg_ptr; + if(is_msgend(i2c)) { + rk30_i2c_stop(i2c, 0); + return; + } + if(len > 32) + cnt = 32; + else + cnt = len; + + writel(cnt, i2c->regs + I2C_MRXCNT); +} +static void rk30_irq_read_get_data(struct rk30_i2c *i2c) +{ + unsigned int i, len = i2c->msg->len - i2c->msg_ptr; + unsigned int p; + + len = (len >= 32)?32:len; + + for(i = 0; i < len; i++){ + if(i%4 == 0) + p = readl(i2c->regs + I2C_RXDATA_BASE + (i/4) * 4); + i2c->msg->buf[i2c->msg_ptr++] = (p >>((i%4) * 8)) & 0xff; + } + + return; +} +static void rk30_irq_write_prepare(struct rk30_i2c *i2c) +{ + unsigned int data = 0, cnt = 0, i, j; + unsigned char byte; + + if(is_msgend(i2c)) { + rk30_i2c_stop(i2c, 0); + return; + } + for(i = 0; i < 8; i++){ + data = 0; + for(j = 0; j < 4; j++) { + if(is_msgend(i2c)) + break; + if(i2c->msg_ptr == 0 && cnt == 0) + byte = (i2c->addr & 0x7f) << 1; + else + byte = i2c->msg->buf[i2c->msg_ptr++]; + cnt++; + data |= (byte << (j * 8)); + } + writel(data, i2c->regs + I2C_TXDATA_BASE + 4 * i); + if(is_msgend(i2c)) + break; + } + writel(cnt, i2c->regs + I2C_MTXCNT); +} +static void rk30_i2c_irq_nextblock(struct rk30_i2c *i2c, unsigned int ipd) +{ + switch (i2c->state) { + case STATE_IDLE: + dev_err(i2c->dev, "Addr[0x%02x] called in STATE_IDLE\n", i2c->addr); + goto out; + case STATE_START: + if(!(ipd & I2C_STARTIPD)){ + if(ipd & I2C_STOPIPD){ + writel(I2C_STOPIPD, i2c->regs + I2C_IPD); + rk30_i2c_send_start(i2c); + } + else { + rk30_i2c_stop(i2c, -ENXIO); + dev_err(i2c->dev, "Addr[0x%02x] no start irq in STATE_START\n", i2c->addr); + rk30_show_regs(i2c); + } + goto out; + } + writel(I2C_STARTIPD, i2c->regs + I2C_IPD); + if(i2c->mode == I2C_MOD_TX){ + i2c->state = STATE_WRITE; + goto prepare_write; + } + else { + i2c->state = STATE_READ; + goto prepare_read; + } + case STATE_WRITE: + if(!(ipd & I2C_MBTFIPD)){ + goto out; + } + writel(I2C_MBTFIPD, i2c->regs + I2C_IPD); +prepare_write: + rk30_irq_write_prepare(i2c); + break; + case STATE_READ: + if(!(ipd & I2C_MBRFIPD)){ + goto out; + } + writel(I2C_MBRFIPD, i2c->regs + I2C_IPD); + rk30_irq_read_get_data(i2c); +prepare_read: + rk30_irq_read_prepare(i2c); + break; + case STATE_STOP: + if(ipd & I2C_STOPIPD || i2c->msg_idx < 0){ + writel(0xff, i2c->regs + I2C_IPD); + rk30_i2c_disable_irq(i2c); + wake_up(&i2c->wait); + } + break; + default: + break; + } +out: + return; +} static irqreturn_t rk30_i2c_irq(int irq, void *dev_id) { + struct rk30_i2c *i2c = dev_id; + unsigned int ipd; + spin_lock(&i2c->lock); + ipd = readl(i2c->regs + I2C_IPD); + + if(ipd & I2C_NAKRCVIPD) { + writel(I2C_NAKRCVIPD, i2c->regs + I2C_IPD); + rk30_i2c_stop(i2c, -EAGAIN); + dev_err(i2c->dev, "Addr[0x%02x] ack was not received\n", i2c->addr); + rk30_show_regs(i2c); + goto out; + } + + rk30_i2c_irq_nextblock(i2c, ipd); +out: + spin_unlock(&i2c->lock); return IRQ_HANDLED; } +static int rk30_i2c_set_master(struct rk30_i2c *i2c, struct i2c_msg *msgs, int num) +{ + unsigned int addr = (msgs[0].addr & 0x7f) << 1; + unsigned int reg_valid_bits = 0; + unsigned int reg_addr = 0; + + if(num == 1) { + if(!(msgs[0].flags & I2C_M_RD)){ + i2c->msg = &msgs[0]; + i2c->mode = I2C_MOD_TX; + } + else { + addr |= 1; + i2c->msg = &msgs[0]; + writel(addr | I2C_MRXADDR_LOW, i2c->regs + I2C_MRXADDR); + i2c->mode = I2C_MOD_RX; + } + } + else if(num == 2) { + switch(msgs[0].len){ + case 1: + reg_addr = msgs[0].buf[0]; + reg_valid_bits |= I2C_MRXADDR_LOW; + break; + case 2: + reg_addr = msgs[0].buf[0] | (msgs[0].buf[1] << 8); + reg_valid_bits |= I2C_MRXADDR_LOW | I2C_MRXADDR_MID; + break; + case 3: + reg_addr = msgs[0].buf[0] | (msgs[0].buf[1] << 8) | (msgs[0].buf[2] << 16); + reg_valid_bits |= I2C_MRXADDR_LOW | I2C_MRXADDR_MID | I2C_MRXADDR_HIGH; + break; + default: + return -EIO; + } + if((msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD)) { + addr |= 1; + i2c->msg = &msgs[1]; + writel(addr | I2C_MRXADDR_LOW, i2c->regs + I2C_MRXADDR); + writel(reg_addr | reg_valid_bits, i2c->regs + I2C_MRXRADDR); + i2c->mode = I2C_MOD_RRX; + } + else if(!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD)) { + i2c->msg = &msgs[1]; + writel(addr | I2C_MRXADDR_LOW, i2c->regs + I2C_MRXADDR); + writel(reg_addr | reg_valid_bits, i2c->regs + I2C_MRXRADDR); + i2c->mode = I2C_MOD_TRX; + } + else + return -EIO; + } + else { + dev_err(i2c->dev, "This case(num > 2) has not been support now\n"); + return -EIO; + } + rk30_i2c_set_mode(i2c); + rk30_i2c_last_ack(i2c, LAST_SEND_TYPE); + if(msgs[0].flags & I2C_M_IGNORE_NAK) + rk30_i2c_act2ack(i2c, 0); + else + rk30_i2c_act2ack(i2c, 1); + + return 0; +} +/* rk30_i2c_doxfer + * + * this starts an i2c transfer +*/ +static int rk30_i2c_doxfer(struct rk30_i2c *i2c, + struct i2c_msg *msgs, int num) +{ + unsigned long timeout; + int ret = 0; + + if (i2c->suspended) + return -EIO; + + ret = rk30_i2c_set_master(i2c, msgs, num); + if (ret != 0) { + dev_err(i2c->dev, "addr[0x%02x] set master error\n", msgs[0].addr); + return ret; + } + spin_lock_irq(&i2c->lock); + + i2c->addr = msgs[0].addr; + i2c->msg_num = num; + i2c->msg_ptr = 0; + i2c->msg_idx = 0; + i2c->state = STATE_START; + + spin_unlock_irq(&i2c->lock); + + rk30_i2c_enable_irq(i2c); + rk30_i2c_send_start(i2c); + + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, msecs_to_jiffies(I2C_WAIT_TIMEOUT)); + + ret = i2c->msg_idx; + + if (timeout == 0){ + dev_err(i2c->dev, "addr[0x%02x] wait event timeout, state = %d\n", msgs[0].addr, i2c->state); + rk30_show_regs(i2c); + rk30_i2c_send_stop(i2c); + if(ret >= 0) + ret = -ETIMEDOUT; + goto out; + } + if(ret > 0) + ret = num; + out: + writel(0xff, i2c->regs + I2C_IPD); + rk30_i2c_disable_irq(i2c); + return ret; +} + +/* rk30_i2c_xfer + * + * first port of call from the i2c bus code when an message needs + * transferring across the i2c bus. +*/ + static int rk30_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { - return 0; + int ret = 0; + unsigned long scl_rate; + struct rk30_i2c *i2c = (struct rk30_i2c *)adap->algo_data; + + 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]); + + rk30_i2c_enable(i2c, 1); + rk30_i2c_set_clk(i2c, scl_rate); + udelay(i2c->tx_setup); + + i2c_dbg(i2c->dev, "i2c transfer: addr[0x%x], scl_reate[%ldKhz]\n", msgs[0].addr, scl_rate/1000); + ret = rk30_i2c_doxfer(i2c, msgs, num); + + rk30_i2c_enable(i2c, 0); + i2c->state = STATE_IDLE; + if(i2c->is_div_from_arm[i2c->adap.nr]) + wake_unlock(&i2c->idlelock[i2c->adap.nr]); + return ret; } /* declare our i2c functionality */ @@ -55,7 +506,6 @@ int i2c_add_rk30_adapter(struct i2c_adapter *adap) 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; @@ -66,4 +516,3 @@ int i2c_add_rk30_adapter(struct i2c_adapter *adap) return ret; } - diff --git a/drivers/i2c/busses/i2c-rk30-test.c b/drivers/i2c/busses/i2c-rk30-test.c new file mode 100644 index 000000000000..d86b3e94e874 --- /dev/null +++ b/drivers/i2c/busses/i2c-rk30-test.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include "i2c-rk30.h" + +#if 0 +#define TEST_SCL_RATE (100 * 1000) +#define I2C_NUM 1 + +struct rw_info { + unsigned short addr; + unsigned int reg; + unsigned int reg_bytes; + unsigned char buf[100]; + unsigned int len; +}; + +static int test_write(struct i2c_client *client, struct rw_info *info) +{ + int i, ret = 0; + struct i2c_msg msg; + + char *buf = kzalloc(info->reg_bytes + info->len, GFP_KERNEL); + for(i = 0; i < info->reg_bytes; i++) + buf[i] = (info->reg >> i)& 0xff; + for(i = info->reg_bytes; i < info->len; i++) + buf[i] = info->buf[i - info->reg_bytes]; + + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = buf; + msg.len = info->reg_bytes + info->len; + msg.scl_rate = TEST_SCL_RATE; + ret = i2c_transfer(client->adapter, &msg, 1); + kfree(buf); + + return ret; + +} + +static int test_read(struct i2c_client *client, struct rw_info *info) +{ + int i, ret = 0, msg_num = 0; + char buf[4]; + struct i2c_msg msgs[2]; + if(info->reg_bytes == 0){ + msgs[0].addr = client->addr; + msgs[0].flags = client->flags|I2C_M_RD; + msgs[0].buf = info->buf; + msgs[0].len = info->len; + msgs[0].scl_rate = TEST_SCL_RATE; + msg_num = 1; + }else { + for(i = 0; i < info->reg_bytes; i++) { + buf[i] = (info->reg >> i) & 0xff; + } + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].buf = buf; + msgs[0].len = info->reg_bytes; + msgs[0].scl_rate = TEST_SCL_RATE; + + msgs[1].addr = client->addr; + msgs[1].flags = client->flags|I2C_M_RD; + msgs[1].buf = info->buf; + msgs[1].len = info->len; + msgs[1].scl_rate = TEST_SCL_RATE; + msg_num = 2; + } + + + ret = i2c_transfer(client->adapter, msgs, msg_num); + return ret; +} + +static void test_set_client(struct i2c_client *client, __u16 addr, int nr) +{ + client->flags = 0; + client->addr = addr; + client->adapter = i2c_get_adapter(nr); +} +static int __init test_init(void) +{ + int nr = 0, ret = 0; + struct i2c_client *client = NULL; + struct rw_info info = { + .addr = 0x51, + .reg = 0x01, + .reg_bytes = 1, + .len = 8, + .buf = + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }, + }; + + client = kzalloc(sizeof(struct i2c_client) * I2C_NUM, GFP_KERNEL); + + printk("%s: start\n", __func__); + while(1) { + for(nr = 0; nr < I2C_NUM; nr++){ + test_set_client(&client[nr], info.addr, nr); + ret = test_write(&client[nr], &info); + printk("i2c-%d: write val [0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x], ret = %d\n", + nr, info.buf[0], info.buf[1], info.buf[2], info.buf[3], info.buf[4], info.buf[5], info.buf[6], info.buf[7], ret); + if(ret < 0) + break; + ret = test_read(&client[nr], &info); + printk("i2c-%d: read val [0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x], ret = %d\n", + nr, info.buf[0], info.buf[1], info.buf[2], info.buf[3], info.buf[4], info.buf[5], info.buf[6], info.buf[7], ret); + if(ret < 0) + break; + } + } + kfree(client); + return 0; +} + +static void __exit test_exit(void) +{ + return; +} + +subsys_initcall_sync(test_init); +module_exit(test_exit); + +#endif + diff --git a/drivers/i2c/busses/i2c-rk30.c b/drivers/i2c/busses/i2c-rk30.c index e738fbaabc22..82f3073e2e47 100755 --- a/drivers/i2c/busses/i2c-rk30.c +++ b/drivers/i2c/busses/i2c-rk30.c @@ -15,6 +15,12 @@ #include "i2c-rk30.h" #define TX_SETUP 1 //unit us +void i2c_adap_sel(struct rk30_i2c *i2c, int nr, int adap_type) +{ + unsigned int p = readl(i2c->con_base); + + writel(rk30_set_bit(p, adap_type, I2C_ADAP_SEL_BIT(nr)), i2c->con_base); +} #ifdef CONFIG_CPU_FREQ @@ -86,19 +92,25 @@ static int rk30_i2c_probe(struct platform_device *pdev) 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; } + i2c->con_base = (void __iomem *)GRF_I2C_CON_BASE; + i2c_adap_sel(i2c, pdata->bus_num, pdata->adap_type); + + if(pdata->io_init) + pdata->io_init(); strlcpy(i2c->adap.name, "rk30_i2c", sizeof(i2c->adap.name)); i2c->adap.owner = THIS_MODULE; i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; i2c->tx_setup = TX_SETUP; + i2c->adap.retries = 5; + i2c->adap.timeout = msecs_to_jiffies(500); spin_lock_init(&i2c->lock); init_waitqueue_head(&i2c->wait); @@ -192,7 +204,7 @@ static int rk30_i2c_probe(struct platform_device *pdev) 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); + i2c->i2c_init_hw(i2c, 100 * 1000); dev_info(&pdev->dev, "%s: RK30 I2C adapter\n", dev_name(&i2c->adap.dev)); return 0; //err_none: diff --git a/drivers/i2c/busses/i2c-rk30.h b/drivers/i2c/busses/i2c-rk30.h index 23f0751bac61..05db8a4c22a6 100755 --- a/drivers/i2c/busses/i2c-rk30.h +++ b/drivers/i2c/busses/i2c-rk30.h @@ -18,6 +18,7 @@ #include #include +#include #include #if 0 @@ -27,7 +28,19 @@ #define i2c_dbg(dev, format, arg...) #endif +#define I2C_WAIT_TIMEOUT 100 //100ms +#define rk30_set_bit(p, v, b) (((p) & ~(1 << (b))) | ((v) << (b))) +#define rk30_get_bit(p, b) (((p) & (1 << (b))) >> (b)) + +#define rk30_set_bits(p, v, b, m) (((p) & ~(m)) | ((v) << (b))) +#define rk30_get_bits(p, b, m) (((p) & (m)) >> (b)) + +#define rk30_ceil(x, y) \ + ({ unsigned long __x = (x), __y = (y); (__x + __y - 1) / __y; }) + +#define GRF_I2C_CON_BASE (RK30_GRF_BASE + GRF_SOC_CON1) +#define I2C_ADAP_SEL_BIT(nr) ((nr) + 11) enum rk30_i2c_state { STATE_IDLE, STATE_START, @@ -35,9 +48,8 @@ enum rk30_i2c_state { STATE_WRITE, STATE_STOP }; - struct rk30_i2c { - spinlock_t lock; + spinlock_t lock; wait_queue_head_t wait; unsigned int suspended:1; @@ -53,6 +65,7 @@ struct rk30_i2c { unsigned long clkrate; void __iomem *regs; + void __iomem *con_base; struct clk *clk; struct device *dev; struct resource *ioarea; @@ -61,6 +74,7 @@ struct rk30_i2c { unsigned long scl_rate; unsigned long i2c_rate; unsigned int addr; + unsigned int mode; struct wake_lock idlelock[5]; int is_div_from_arm[5]; @@ -69,11 +83,11 @@ struct rk30_i2c { struct notifier_block freq_transition; #endif - void (*i2c_init_hw)(struct rk30_i2c *); + void (*i2c_init_hw)(struct rk30_i2c *, unsigned long scl_rate); void (*i2c_set_clk)(struct rk30_i2c *, unsigned long); irqreturn_t (*i2c_irq)(int, void *); }; - +void i2c_adap_sel(struct rk30_i2c *i2c, int nr, int adap_type); int i2c_add_rk29_adapter(struct i2c_adapter *); int i2c_add_rk30_adapter(struct i2c_adapter *); #endif -- 2.34.1