summaryrefslogtreecommitdiff
path: root/target/linux/xburst/patches-2.6.37/050-i2c.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/xburst/patches-2.6.37/050-i2c.patch')
-rw-r--r--target/linux/xburst/patches-2.6.37/050-i2c.patch472
1 files changed, 472 insertions, 0 deletions
diff --git a/target/linux/xburst/patches-2.6.37/050-i2c.patch b/target/linux/xburst/patches-2.6.37/050-i2c.patch
new file mode 100644
index 0000000..5cfa912
--- /dev/null
+++ b/target/linux/xburst/patches-2.6.37/050-i2c.patch
@@ -0,0 +1,472 @@
+From 5af1734d9fc79c2d08853c703d1edfef2a4cae16 Mon Sep 17 00:00:00 2001
+From: Lars-Peter Clausen <lars@metafoo.de>
+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 <lars@metafoo.de>
+---
+ 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 <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/err.h>
++#include <linux/clk.h>
++#include <linux/platform_device.h>
++#include <linux/i2c.h>
++#include <linux/slab.h>
++#include <linux/interrupt.h>
++
++#include <linux/gpio.h>
++#include <linux/delay.h>
++
++#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_i2c, uint8_t
++mask, uint8_t value)
++{
++ int ret;
++
++ ret = wait_event_interruptible_timeout(jz47xx_i2c->wait_queue,
++ jz47xx_i2c_test_event(jz47xx_i2c, mask, value), 30 * HZ);
++
++/* while (!jz47xx_i2c_test_event(jz47xx_i2c, mask, value));
++
++ ret = 1;*/
++
++ printk("wait event or nack: %d %x\n", ret, readb(jz47xx_i2c->base +
++ JZ47XX_REG_I2C_STATUS));
++
++ if (ret == 0)
++ ret = -ETIMEDOUT;
++ else if(ret > 0) {
++ if (readb(jz47xx_i2c->base + JZ47XX_REG_I2C_STATUS) & JZ47XX_I2C_STATUS_NACK)
++ ret = -EIO;
++ else
++ ret = 0;
++ }
++
++ return ret;
++}
++
++static int jz47xx_i2c_wait_event(struct jz47xx_i2c *jz47xx_i2c, uint8_t event)
++{
++ int ret;
++
++ ret = wait_event_interruptible_timeout(jz47xx_i2c->wait_queue,
++ jz47xx_i2c_test_event(jz47xx_i2c, event, event), 30 * HZ);
++
++ if (ret == 0)
++ ret = -ETIMEDOUT;
++ else if(ret > 0)
++ ret = 0;
++
++ return ret;
++}
++
++
++static int jz47xx_i2c_write_msg(struct jz47xx_i2c *jz47xx_i2c,
++ struct i2c_msg *msg)
++{
++ int ret;
++ int i;
++
++ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__);
++ for (i = 0; i < msg->len; ++i) {
++ writeb(msg->buf[i], jz47xx_i2c->base + JZ47XX_REG_I2C_DATA);
++ jz47xx_i2c_set_data_valid(jz47xx_i2c, true);
++ ret = jz47xx_i2c_wait_event_or_nack(jz47xx_i2c,
++ JZ47XX_I2C_STATUS_DATA_VALID, 0);
++ if (ret)
++ break;
++ }
++ jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_STOP,
++ JZ47XX_I2C_CTRL_STOP);
++
++ if (!ret)
++ ret = jz47xx_i2c_wait_event_or_nack(jz47xx_i2c, JZ47XX_I2C_STATUS_TEND,
++ JZ47XX_I2C_STATUS_TEND);
++
++ return ret;
++}
++
++static int jz47xx_i2c_read_msg(struct jz47xx_i2c *jz47xx_i2c,
++ struct i2c_msg *msg)
++{
++ int i;
++ int ret;
++ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__);
++
++ jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_NACK,
++ msg->len == 1 ? JZ47XX_I2C_CTRL_NACK : 0);
++
++ for (i = 0; i < msg->len; ++i) {
++ ret = jz47xx_i2c_wait_event(jz47xx_i2c, JZ47XX_I2C_STATUS_DATA_VALID);
++ if (ret) {
++ jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_NACK,
++ JZ47XX_I2C_CTRL_NACK);
++ break;
++ }
++
++ if (i == msg->len - 2) {
++ jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_NACK,
++ JZ47XX_I2C_CTRL_NACK);
++ }
++
++ msg->buf[i] = readb(jz47xx_i2c->base + JZ47XX_REG_I2C_DATA);
++ printk("read: %x\n", msg->buf[i]);
++ jz47xx_i2c_set_data_valid(jz47xx_i2c, false);
++ }
++
++ jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_STOP,
++ JZ47XX_I2C_CTRL_STOP);
++
++ return ret;
++}
++
++static int jz47xx_i2c_xfer_msg(struct jz47xx_i2c *jz47xx_i2c,
++ struct i2c_msg *msg)
++{
++ uint8_t addr;
++ int ret;
++
++ addr = msg->addr << 1;
++ if (msg->flags & I2C_M_RD)
++ addr |= 1;
++
++ jz47xx_i2c_set_ctrl(jz47xx_i2c, JZ47XX_I2C_CTRL_START,
++ JZ47XX_I2C_CTRL_START);
++ writeb(addr, jz47xx_i2c->base + JZ47XX_REG_I2C_DATA);
++ jz47xx_i2c_set_data_valid(jz47xx_i2c, true);
++
++ if (msg->flags & I2C_M_RD) {
++ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__);
++ ret = jz47xx_i2c_wait_event_or_nack(jz47xx_i2c,
++ JZ47XX_I2C_STATUS_TEND, JZ47XX_I2C_STATUS_TEND);
++ if (!ret)
++ ret = jz47xx_i2c_read_msg(jz47xx_i2c, msg);
++ } else {
++ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__);
++ ret = jz47xx_i2c_wait_event_or_nack(jz47xx_i2c,
++ JZ47XX_I2C_STATUS_DATA_VALID, 0);
++ if (!ret)
++ ret = jz47xx_i2c_write_msg(jz47xx_i2c, msg);
++ }
++
++ return ret;
++}
++
++static int jz47xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int
++num)
++{
++ struct jz47xx_i2c *jz47xx_i2c = adapter_to_jz47xx_i2c(adap);
++ int ret = 0;
++ int i;
++ int mask = JZ47XX_I2C_CTRL_ENABLE;
++
++ printk("xfer: %d %x\n", num, readb(jz47xx_i2c->base +
++ JZ47XX_REG_I2C_STATUS));
++
++ clk_enable(jz47xx_i2c->clk);
++ jz47xx_i2c_set_ctrl(jz47xx_i2c, mask, mask);
++
++ for (i = 0; i < num; ++i) {
++ ret = jz47xx_i2c_xfer_msg(jz47xx_i2c, &msgs[i]);
++ if (ret)
++ break;
++ }
++
++ jz47xx_i2c_set_ctrl(jz47xx_i2c, mask, 0);
++ clk_disable(jz47xx_i2c->clk);
++
++ printk("xfer ret: %d\n", ret);
++
++ return ret;
++}
++
++static u32 jz47xx_i2c_functionality(struct i2c_adapter *adap)
++{
++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
++}
++
++static const struct i2c_algorithm jz47xx_i2c_algorithm = {
++ .master_xfer = jz47xx_i2c_xfer,
++ .functionality = jz47xx_i2c_functionality,
++};
++
++const static struct jz_gpio_bulk_request jz47xx_i2c_pins[] = {
++ JZ_GPIO_BULK_PIN(I2C_SDA),
++ JZ_GPIO_BULK_PIN(I2C_SCK),
++};
++
++static int __devinit jz47xx_i2c_probe(struct platform_device *pdev)
++{
++ struct jz47xx_i2c *jz47xx_i2c;
++ struct resource *mem;
++ void __iomem *base;
++ struct clk *clk;
++ int irq;
++ int ret;
++
++ irq = platform_get_irq(pdev, 0);
++ if (!irq) {
++ dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
++ return irq;
++ }
++
++ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!mem) {
++ dev_err(&pdev->dev, "Failed to get iomem region\n");
++ return -ENXIO;
++ }
++
++ mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
++ if (!mem) {
++ dev_err(&pdev->dev, "Failed to request iomem region\n");
++ return -EBUSY;
++ }
++
++ base = ioremap(mem->start, resource_size(mem));
++ if (!base) {
++ dev_err(&pdev->dev, "Failed to ioremap iomem\n");
++ ret = -EBUSY;
++ goto err_release_mem_region;
++ }
++
++ clk = clk_get(&pdev->dev, "i2c");
++ if (IS_ERR(clk)) {
++ ret = PTR_ERR(clk);
++ goto err_iounmap;
++ }
++
++ jz47xx_i2c = kzalloc(sizeof(*jz47xx_i2c), GFP_KERNEL);
++ if (!jz47xx_i2c) {
++ ret = -ENOMEM;
++ goto err_clk_put;
++ }
++
++ jz47xx_i2c->adapter.owner = THIS_MODULE;
++ jz47xx_i2c->adapter.algo = &jz47xx_i2c_algorithm;
++ jz47xx_i2c->adapter.dev.parent = &pdev->dev;
++ jz47xx_i2c->adapter.nr = pdev->id < 0 ?: 0;
++ strcpy(jz47xx_i2c->adapter.name, pdev->name);
++
++ jz47xx_i2c->mem = mem;
++ jz47xx_i2c->base = base;
++ jz47xx_i2c->clk = clk;
++ jz47xx_i2c->irq = irq;
++
++ init_waitqueue_head(&jz47xx_i2c->wait_queue);
++
++ ret = request_irq(irq, jz47xx_i2c_irq_handler, 0, pdev->name, jz47xx_i2c);
++ if (ret) {
++ dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
++ goto err_free;
++ }
++
++ ret = jz_gpio_bulk_request(jz47xx_i2c_pins, ARRAY_SIZE(jz47xx_i2c_pins));
++ if (ret) {
++ dev_err(&pdev->dev, "Failed to request i2c pins: %d\n", ret);
++ goto err_free_irq;
++ }
++
++ writew(0x10, jz47xx_i2c->base + JZ47XX_REG_I2C_CLOCK);
++
++ ret = i2c_add_numbered_adapter(&jz47xx_i2c->adapter);
++ if (ret) {
++ dev_err(&pdev->dev, "Failed to add i2c adapter: %d\n", ret);
++ goto err_free_gpios;
++ }
++
++ platform_set_drvdata(pdev, jz47xx_i2c);
++
++ printk("JZ4740 I2C\n");
++
++ return 0;
++
++err_free_gpios:
++ jz_gpio_bulk_free(jz47xx_i2c_pins, ARRAY_SIZE(jz47xx_i2c_pins));
++err_free_irq:
++ free_irq(irq, jz47xx_i2c);
++err_free:
++ kfree(jz47xx_i2c);
++err_clk_put:
++ clk_put(clk);
++err_iounmap:
++ iounmap(base);
++err_release_mem_region:
++ release_mem_region(mem->start, resource_size(mem));
++ return ret;
++}
++
++static int __devexit jz47xx_i2c_remove(struct platform_device *pdev)
++{
++ struct jz47xx_i2c *jz47xx_i2c = platform_get_drvdata(pdev);
++
++ platform_set_drvdata(pdev, NULL);
++ i2c_del_adapter(&jz47xx_i2c->adapter);
++
++ jz_gpio_bulk_free(jz47xx_i2c_pins, ARRAY_SIZE(jz47xx_i2c_pins));
++
++ free_irq(jz47xx_i2c->irq, jz47xx_i2c);
++ clk_put(jz47xx_i2c->clk);
++
++ iounmap(jz47xx_i2c->base);
++ release_mem_region(jz47xx_i2c->mem->start, resource_size(jz47xx_i2c->mem));
++
++ kfree(jz47xx_i2c);
++
++ return 0;
++}
++
++static struct platform_driver jz47xx_i2c_driver = {
++ .probe = jz47xx_i2c_probe,
++ .remove = jz47xx_i2c_remove,
++ .driver = {
++ .name = "jz47xx-i2c",
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __init jz47xx_i2c_init(void)
++{
++ return platform_driver_register(&jz47xx_i2c_driver);
++}
++module_init(jz47xx_i2c_init);
++
++static void jz47xx_i2c_exit(void)
++{
++ platform_driver_unregister(&jz47xx_i2c_driver);
++}
++module_exit(jz47xx_i2c_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
++MODULE_DESCRIPTION("I2C adapter driver for JZ47XX SoCs");
++MODULE_ALIAS("platform:jz47xx-i2c");
++