brcm2708: update against latest rpi-3.10.y branch
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-3.10 / 015-bcm2708-i2c-driver.patch
diff --git a/target/linux/brcm2708/patches-3.10/015-bcm2708-i2c-driver.patch b/target/linux/brcm2708/patches-3.10/015-bcm2708-i2c-driver.patch
deleted file mode 100644 (file)
index 44e380b..0000000
+++ /dev/null
@@ -1,449 +0,0 @@
---- /dev/null
-+++ b/drivers/i2c/busses/i2c-bcm2708.c
-@@ -0,0 +1,408 @@
-+/*
-+ * Driver for Broadcom BCM2708 BSC Controllers
-+ *
-+ * Copyright (C) 2012 Chris Boot & Frank Buss
-+ *
-+ * This driver is inspired by:
-+ * i2c-ocores.c, by Peter Korsgaard <jacmet@sunsite.dk>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * 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.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/module.h>
-+#include <linux/spinlock.h>
-+#include <linux/clk.h>
-+#include <linux/err.h>
-+#include <linux/platform_device.h>
-+#include <linux/io.h>
-+#include <linux/slab.h>
-+#include <linux/i2c.h>
-+#include <linux/interrupt.h>
-+#include <linux/sched.h>
-+#include <linux/wait.h>
-+
-+/* BSC register offsets */
-+#define BSC_C                 0x00
-+#define BSC_S                 0x04
-+#define BSC_DLEN              0x08
-+#define BSC_A                 0x0c
-+#define BSC_FIFO              0x10
-+#define BSC_DIV                       0x14
-+#define BSC_DEL                       0x18
-+#define BSC_CLKT              0x1c
-+
-+/* Bitfields in BSC_C */
-+#define BSC_C_I2CEN           0x00008000
-+#define BSC_C_INTR            0x00000400
-+#define BSC_C_INTT            0x00000200
-+#define BSC_C_INTD            0x00000100
-+#define BSC_C_ST              0x00000080
-+#define BSC_C_CLEAR_1         0x00000020
-+#define BSC_C_CLEAR_2         0x00000010
-+#define BSC_C_READ            0x00000001
-+
-+/* Bitfields in BSC_S */
-+#define BSC_S_CLKT            0x00000200
-+#define BSC_S_ERR             0x00000100
-+#define BSC_S_RXF             0x00000080
-+#define BSC_S_TXE             0x00000040
-+#define BSC_S_RXD             0x00000020
-+#define BSC_S_TXD             0x00000010
-+#define BSC_S_RXR             0x00000008
-+#define BSC_S_TXW             0x00000004
-+#define BSC_S_DONE            0x00000002
-+#define BSC_S_TA              0x00000001
-+
-+#define I2C_TIMEOUT_MS        150
-+
-+#define DRV_NAME      "bcm2708_i2c"
-+
-+static unsigned int baudrate = CONFIG_I2C_BCM2708_BAUDRATE;
-+module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
-+MODULE_PARM_DESC(baudrate, "The I2C baudrate");
-+
-+
-+struct bcm2708_i2c {
-+      struct i2c_adapter adapter;
-+
-+      spinlock_t lock;
-+      void __iomem *base;
-+      int irq;
-+      struct clk *clk;
-+
-+      struct completion done;
-+
-+      struct i2c_msg *msg;
-+      int pos;
-+      int nmsgs;
-+      bool error;
-+};
-+
-+/*
-+ * This function sets the ALT mode on the I2C pins so that we can use them with
-+ * the BSC hardware.
-+ *
-+ * FIXME: This is a hack. Use pinmux / pinctrl.
-+ */
-+static void bcm2708_i2c_init_pinmode(int id)
-+{
-+#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
-+#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
-+
-+      int pin;
-+      u32 *gpio = ioremap(0x20200000, SZ_16K);
-+
-+        BUG_ON(id != 0 && id != 1);
-+      /* BSC0 is on GPIO 0 & 1, BSC1 is on GPIO 2 & 3 */
-+      for (pin = id*2+0; pin <= id*2+1; pin++) {
-+printk("bcm2708_i2c_init_pinmode(%d,%d)\n", id, pin);
-+              INP_GPIO(pin);          /* set mode to GPIO input first */
-+              SET_GPIO_ALT(pin, 0);   /* set mode to ALT 0 */
-+      }
-+
-+      iounmap(gpio);
-+
-+#undef INP_GPIO
-+#undef SET_GPIO_ALT
-+}
-+
-+static inline u32 bcm2708_rd(struct bcm2708_i2c *bi, unsigned reg)
-+{
-+      return readl(bi->base + reg);
-+}
-+
-+static inline void bcm2708_wr(struct bcm2708_i2c *bi, unsigned reg, u32 val)
-+{
-+      writel(val, bi->base + reg);
-+}
-+
-+static inline void bcm2708_bsc_reset(struct bcm2708_i2c *bi)
-+{
-+      bcm2708_wr(bi, BSC_C, 0);
-+      bcm2708_wr(bi, BSC_S, BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE);     
-+}
-+
-+static inline void bcm2708_bsc_fifo_drain(struct bcm2708_i2c *bi)
-+{
-+      while ((bcm2708_rd(bi, BSC_S) & BSC_S_RXD) && (bi->pos < bi->msg->len))
-+              bi->msg->buf[bi->pos++] = bcm2708_rd(bi, BSC_FIFO);
-+}
-+
-+static inline void bcm2708_bsc_fifo_fill(struct bcm2708_i2c *bi)
-+{
-+      while ((bcm2708_rd(bi, BSC_S) & BSC_S_TXD) && (bi->pos < bi->msg->len))
-+              bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]);
-+}
-+
-+static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi)
-+{
-+      unsigned long bus_hz;
-+      u32 cdiv;
-+      u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1;
-+
-+      bus_hz = clk_get_rate(bi->clk);
-+      cdiv = bus_hz / baudrate;
-+
-+      if (bi->msg->flags & I2C_M_RD)
-+              c |= BSC_C_INTR | BSC_C_READ;
-+      else
-+              c |= BSC_C_INTT;
-+
-+      bcm2708_wr(bi, BSC_DIV, cdiv);
-+      bcm2708_wr(bi, BSC_A, bi->msg->addr);
-+      bcm2708_wr(bi, BSC_DLEN, bi->msg->len);
-+      bcm2708_wr(bi, BSC_C, c);
-+}
-+
-+static irqreturn_t bcm2708_i2c_interrupt(int irq, void *dev_id)
-+{
-+      struct bcm2708_i2c *bi = dev_id;
-+      bool handled = true;
-+      u32 s;
-+
-+      spin_lock(&bi->lock);
-+
-+      /* we may see camera interrupts on the "other" I2C channel
-+           Just return if we've not sent anything */
-+        if (!bi->nmsgs || !bi->msg )
-+              goto early_exit;
-+
-+      s = bcm2708_rd(bi, BSC_S);
-+
-+      if (s & (BSC_S_CLKT | BSC_S_ERR)) {
-+              bcm2708_bsc_reset(bi);
-+              bi->error = true;
-+
-+              /* wake up our bh */
-+              complete(&bi->done);
-+      } else if (s & BSC_S_DONE) {
-+              bi->nmsgs--;
-+
-+              if (bi->msg->flags & I2C_M_RD)
-+                      bcm2708_bsc_fifo_drain(bi);
-+
-+              bcm2708_bsc_reset(bi);
-+
-+              if (bi->nmsgs) {
-+                      /* advance to next message */
-+                      bi->msg++;
-+                      bi->pos = 0;
-+                      bcm2708_bsc_setup(bi);
-+              } else {
-+                      /* wake up our bh */
-+                      complete(&bi->done);
-+              }
-+      } else if (s & BSC_S_TXW) {
-+              bcm2708_bsc_fifo_fill(bi);
-+      } else if (s & BSC_S_RXR) {
-+              bcm2708_bsc_fifo_drain(bi);
-+      } else {
-+              handled = false;
-+      }
-+
-+early_exit:
-+      spin_unlock(&bi->lock);
-+
-+      return handled ? IRQ_HANDLED : IRQ_NONE;
-+}
-+
-+static int bcm2708_i2c_master_xfer(struct i2c_adapter *adap,
-+      struct i2c_msg *msgs, int num)
-+{
-+      struct bcm2708_i2c *bi = adap->algo_data;
-+      unsigned long flags;
-+      int ret;
-+
-+      spin_lock_irqsave(&bi->lock, flags);
-+
-+      INIT_COMPLETION(bi->done);
-+      bi->msg = msgs;
-+      bi->pos = 0;
-+      bi->nmsgs = num;
-+      bi->error = false;
-+
-+      spin_unlock_irqrestore(&bi->lock, flags);
-+
-+      bcm2708_bsc_setup(bi);
-+
-+      ret = wait_for_completion_timeout(&bi->done,
-+                      msecs_to_jiffies(I2C_TIMEOUT_MS));
-+      if (ret == 0) {
-+              dev_err(&adap->dev, "transfer timed out\n");
-+              spin_lock_irqsave(&bi->lock, flags);
-+              bcm2708_bsc_reset(bi);
-+              spin_unlock_irqrestore(&bi->lock, flags);
-+              return -ETIMEDOUT;
-+      }
-+
-+      return bi->error ? -EIO : num;
-+}
-+
-+static u32 bcm2708_i2c_functionality(struct i2c_adapter *adap)
-+{
-+      return I2C_FUNC_I2C | /*I2C_FUNC_10BIT_ADDR |*/ I2C_FUNC_SMBUS_EMUL;
-+}
-+
-+static struct i2c_algorithm bcm2708_i2c_algorithm = {
-+      .master_xfer = bcm2708_i2c_master_xfer,
-+      .functionality = bcm2708_i2c_functionality,
-+};
-+
-+static int bcm2708_i2c_probe(struct platform_device *pdev)
-+{
-+      struct resource *regs;
-+      int irq, err = -ENOMEM;
-+      struct clk *clk;
-+      struct bcm2708_i2c *bi;
-+      struct i2c_adapter *adap;
-+
-+      regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+      if (!regs) {
-+              dev_err(&pdev->dev, "could not get IO memory\n");
-+              return -ENXIO;
-+      }
-+
-+      irq = platform_get_irq(pdev, 0);
-+      if (irq < 0) {
-+              dev_err(&pdev->dev, "could not get IRQ\n");
-+              return irq;
-+      }
-+
-+      clk = clk_get(&pdev->dev, NULL);
-+      if (IS_ERR(clk)) {
-+              dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk));
-+              return PTR_ERR(clk);
-+      }
-+
-+      bcm2708_i2c_init_pinmode(pdev->id);
-+
-+      bi = kzalloc(sizeof(*bi), GFP_KERNEL);
-+      if (!bi)
-+              goto out_clk_put;
-+
-+      platform_set_drvdata(pdev, bi);
-+
-+      adap = &bi->adapter;
-+      adap->class = I2C_CLASS_HWMON | I2C_CLASS_DDC;
-+      adap->algo = &bcm2708_i2c_algorithm;
-+      adap->algo_data = bi;
-+      adap->dev.parent = &pdev->dev;
-+      adap->nr = pdev->id;
-+      strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
-+
-+      switch (pdev->id) {
-+      case 0:
-+              adap->class = I2C_CLASS_HWMON;
-+              break;
-+      case 1:
-+              adap->class = I2C_CLASS_DDC;
-+              break;
-+      default:
-+              dev_err(&pdev->dev, "can only bind to BSC 0 or 1\n");
-+              err = -ENXIO;
-+              goto out_free_bi;
-+      }
-+
-+      spin_lock_init(&bi->lock);
-+      init_completion(&bi->done);
-+
-+      bi->base = ioremap(regs->start, resource_size(regs));
-+      if (!bi->base) {
-+              dev_err(&pdev->dev, "could not remap memory\n");
-+              goto out_free_bi;
-+      }
-+
-+      bi->irq = irq;
-+      bi->clk = clk;
-+
-+      err = request_irq(irq, bcm2708_i2c_interrupt, IRQF_SHARED,
-+                      dev_name(&pdev->dev), bi);
-+      if (err) {
-+              dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
-+              goto out_iounmap;
-+      }
-+
-+      bcm2708_bsc_reset(bi);
-+
-+      err = i2c_add_numbered_adapter(adap);
-+      if (err < 0) {
-+              dev_err(&pdev->dev, "could not add I2C adapter: %d\n", err);
-+              goto out_free_irq;
-+      }
-+
-+      dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %dk)\n",
-+              pdev->id, (unsigned long)regs->start, irq, baudrate/1000);
-+
-+      return 0;
-+
-+out_free_irq:
-+      free_irq(bi->irq, bi);
-+out_iounmap:
-+      iounmap(bi->base);
-+out_free_bi:
-+      kfree(bi);
-+out_clk_put:
-+      clk_put(clk);
-+      return err;
-+}
-+
-+static int bcm2708_i2c_remove(struct platform_device *pdev)
-+{
-+      struct bcm2708_i2c *bi = platform_get_drvdata(pdev);
-+
-+      platform_set_drvdata(pdev, NULL);
-+
-+      i2c_del_adapter(&bi->adapter);
-+      free_irq(bi->irq, bi);
-+      iounmap(bi->base);
-+      clk_disable(bi->clk);
-+      clk_put(bi->clk);
-+      kfree(bi);
-+
-+      return 0;
-+}
-+
-+static struct platform_driver bcm2708_i2c_driver = {
-+      .driver         = {
-+              .name   = DRV_NAME,
-+              .owner  = THIS_MODULE,
-+      },
-+      .probe          = bcm2708_i2c_probe,
-+      .remove         = bcm2708_i2c_remove,
-+};
-+
-+// module_platform_driver(bcm2708_i2c_driver);
-+
-+
-+static int __init bcm2708_i2c_init(void)
-+{
-+      return platform_driver_register(&bcm2708_i2c_driver);
-+}
-+
-+static void __exit bcm2708_i2c_exit(void)
-+{
-+      platform_driver_unregister(&bcm2708_i2c_driver);
-+}
-+
-+module_init(bcm2708_i2c_init);
-+module_exit(bcm2708_i2c_exit);
-+
-+
-+
-+MODULE_DESCRIPTION("BSC controller driver for Broadcom BCM2708");
-+MODULE_AUTHOR("Chris Boot <bootc@bootc.net>");
-+MODULE_LICENSE("GPL v2");
-+MODULE_ALIAS("platform:" DRV_NAME);
---- a/drivers/i2c/busses/Kconfig
-+++ b/drivers/i2c/busses/Kconfig
-@@ -345,6 +345,25 @@ config I2C_BCM2835
-         This support is also available as a module.  If so, the module
-         will be called i2c-bcm2835.
-+config I2C_BCM2708
-+      tristate "BCM2708 BSC"
-+      depends on MACH_BCM2708
-+      help
-+        Enabling this option will add BSC (Broadcom Serial Controller)
-+        support for the BCM2708. BSC is a Broadcom proprietary bus compatible
-+        with I2C/TWI/SMBus.
-+
-+config I2C_BCM2708_BAUDRATE
-+    prompt "BCM2708 I2C baudrate"
-+      depends on I2C_BCM2708
-+    int
-+    default 100000
-+    help
-+      Set the I2C baudrate. This will alter the default value. A
-+      different baudrate can be set by using a module parameter as well. If
-+      no parameter is provided when loading, this is the value that will be
-+      used.
-+
- config I2C_BLACKFIN_TWI
-       tristate "Blackfin TWI I2C support"
-       depends on BLACKFIN
---- a/drivers/i2c/busses/Makefile
-+++ b/drivers/i2c/busses/Makefile
-@@ -32,6 +32,7 @@ obj-$(CONFIG_I2C_POWERMAC)   += i2c-powerm
- obj-$(CONFIG_I2C_AT91)                += i2c-at91.o
- obj-$(CONFIG_I2C_AU1550)      += i2c-au1550.o
- obj-$(CONFIG_I2C_BCM2835)     += i2c-bcm2835.o
-+obj-$(CONFIG_I2C_BCM2708)     += i2c-bcm2708.o
- obj-$(CONFIG_I2C_BLACKFIN_TWI)        += i2c-bfin-twi.o
- obj-$(CONFIG_I2C_CBUS_GPIO)   += i2c-cbus-gpio.o
- obj-$(CONFIG_I2C_CPM)         += i2c-cpm.o