From 5af1734d9fc79c2d08853c703d1edfef2a4cae16 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 5 Sep 2010 03:19:10 +0200 Subject: [PATCH 10/23] i2c: Add i2c driver for JZ47XX SoCs This patch adds a driver for the i2c controller found in Ingenic JZ47XX based SoCs. Signed-off-by: Lars-Peter Clausen --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-jz47xx.c | 424 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 435 insertions(+), 0 deletions(-) create mode 100644 drivers/i2c/busses/i2c-jz47xx.c --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -431,6 +431,16 @@ config I2C_IXP2000 This driver is deprecated and will be dropped soon. Use i2c-gpio instead. +config I2C_JZ47XX + tristate "JZ4740 I2C Interface" + depends on ARCH_JZ4740 + help + Say Y here if you want support for the I2C controller found on Ingenic + JZ47XX based SoCs. + + This driver can also be built as a module. If so, the module will be + called i2c-jz47xx. + config I2C_MPC tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx" depends on PPC32 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_I2C_IMX) += i2c-imx.o obj-$(CONFIG_I2C_INTEL_MID) += i2c-intel-mid.o obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o +obj-$(CONFIG_I2C_JZ47XX) += i2c-jz47xx.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o --- /dev/null +++ b/drivers/i2c/busses/i2c-jz47xx.c @@ -0,0 +1,424 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define JZ47XX_REG_I2C_DATA 0x00 +#define JZ47XX_REG_I2C_CTRL 0x04 +#define JZ47XX_REG_I2C_STATUS 0x08 +#define JZ47XX_REG_I2C_CLOCK 0x0C + +#define JZ47XX_I2C_STATUS_FIFO_FULL BIT(4) +#define JZ47XX_I2C_STATUS_BUSY BIT(3) +#define JZ47XX_I2C_STATUS_TEND BIT(2) +#define JZ47XX_I2C_STATUS_DATA_VALID BIT(1) +#define JZ47XX_I2C_STATUS_NACK BIT(0) + +#define JZ47XX_I2C_CTRL_IRQ_ENABLE BIT(4) +#define JZ47XX_I2C_CTRL_START BIT(3) +#define JZ47XX_I2C_CTRL_STOP BIT(2) +#define JZ47XX_I2C_CTRL_NACK BIT(1) +#define JZ47XX_I2C_CTRL_ENABLE BIT(0) + +struct jz47xx_i2c { + struct resource *mem; + void __iomem *base; + int irq; + struct clk *clk; + + struct i2c_adapter adapter; + + wait_queue_head_t wait_queue; +}; + +static inline struct jz47xx_i2c *adapter_to_jz47xx_i2c(struct i2c_adapter *adap) +{ + return container_of(adap, struct jz47xx_i2c, adapter); +} + +static inline void jz47xx_i2c_set_ctrl(struct jz47xx_i2c *jz47xx_i2c, + uint8_t mask, uint8_t value) +{ + uint8_t ctrl; + ctrl = readb(jz47xx_i2c->base + JZ47XX_REG_I2C_CTRL); + ctrl &= ~mask; + ctrl |= value; + printk("ctrl: %x\n", ctrl); + writeb(ctrl, jz47xx_i2c->base + JZ47XX_REG_I2C_CTRL); +} + +static irqreturn_t jz47xx_i2c_irq_handler(int irq, void *devid) +{ + struct jz47xx_i2c *jz47xx_i2c = devid; + + printk("IRQ\n"); + + wake_up(&jz47xx_i2c->wait_queue); + + jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_IRQ_ENABLE, 0); + + return IRQ_HANDLED; +} + +static inline void jz47xx_i2c_set_data_valid(struct jz47xx_i2c *jz47xx_i2c, + bool valid) +{ + uint8_t val; + val = readb(jz47xx_i2c->base + JZ47XX_REG_I2C_STATUS); + if (valid) + val |= JZ47XX_I2C_STATUS_DATA_VALID; + else + val &= ~JZ47XX_I2C_STATUS_DATA_VALID; + writeb(val, jz47xx_i2c->base + JZ47XX_REG_I2C_STATUS); +} + +static int jz47xx_i2c_test_event(struct jz47xx_i2c *jz47xx_i2c, uint8_t mask, uint8_t value) +{ + uint8_t status; + + mask |= JZ47XX_I2C_STATUS_NACK; + value |= JZ47XX_I2C_STATUS_NACK; + + status = readb(jz47xx_i2c->base + JZ47XX_REG_I2C_STATUS); + printk("status: %x %x %x %x\n", status, mask, value, (status & mask) ^ + value); + if (((status & mask) ^ value) == mask) { + jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_IRQ_ENABLE, + JZ47XX_I2C_CTRL_IRQ_ENABLE); + return 0; + } + return 1; +} + +static int jz47xx_i2c_wait_event_or_nack(struct jz47xx_i2c *jz47xx_i2