]> git.kernelconcepts.de Git - karo-tx-uboot.git/commitdiff
i2c: Add support for Renesas rcar
authorNobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
Fri, 27 Sep 2013 07:58:30 +0000 (16:58 +0900)
committerHeiko Schocher <hs@denx.de>
Thu, 17 Oct 2013 05:20:25 +0000 (07:20 +0200)
This supports i2c controller for Renesas rcar.

Signed-off-by: Hisashi Nakamura <hisashi.nakamura.ak@renesas.com>
Signed-off-by: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
README
drivers/i2c/Makefile
drivers/i2c/rcar_i2c.c [new file with mode: 0644]

diff --git a/README b/README
index 7dc4f239afdf3f8c31abdc889b869d65274a807c..e209c89e215f7ca419cfa84b9c450a103532e628 100644 (file)
--- a/README
+++ b/README
@@ -2014,6 +2014,20 @@ CBFS (Coreboot Filesystem) support
                If thoses defines are not set, default value is 100000
                for speed, and 0 for slave.
 
+               - drivers/i2c/rcar_i2c.c:
+                 - activate this driver with CONFIG_SYS_I2C_RCAR
+                 - This driver adds 4 i2c buses
+
+                 - CONFIG_SYS_RCAR_I2C0_BASE for setting the register channel 0
+                 - CONFIG_SYS_RCAR_I2C0_SPEED for for the speed channel 0
+                 - CONFIG_SYS_RCAR_I2C1_BASE for setting the register channel 1
+                 - CONFIG_SYS_RCAR_I2C1_SPEED for for the speed channel 1
+                 - CONFIG_SYS_RCAR_I2C2_BASE for setting the register channel 2
+                 - CONFIG_SYS_RCAR_I2C2_SPEED for for the speed channel 2
+                 - CONFIG_SYS_RCAR_I2C3_BASE for setting the register channel 3
+                 - CONFIG_SYS_RCAR_I2C3_SPEED for for the speed channel 3
+                 - CONFIF_SYS_RCAR_I2C_NUM_CONTROLLERS for number of i2c buses
+
                additional defines:
 
                CONFIG_SYS_NUM_I2C_BUSES
index 2395dedfc9a3a8d17125ac9669c40ea300c9c72b..84a275474a7c01c1b65600f5dcdcdcfe2686da99 100644 (file)
@@ -29,6 +29,7 @@ COBJS-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o
 COBJS-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.o
 COBJS-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
 COBJS-$(CONFIG_SYS_I2C_PPC4XX) += ppc4xx_i2c.o
+COBJS-$(CONFIG_SYS_I2C_RCAR) += rcar_i2c.o
 COBJS-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o
 COBJS-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o
 COBJS-$(CONFIG_ZYNQ_I2C) += zynq_i2c.o
diff --git a/drivers/i2c/rcar_i2c.c b/drivers/i2c/rcar_i2c.c
new file mode 100644 (file)
index 0000000..ba2cadb
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * drivers/i2c/rcar_i2c.c
+ *
+ * Copyright (C) 2013 Renesas Electronics Corporation
+ * Copyright (C) 2013 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct rcar_i2c {
+       u32 icscr;
+       u32 icmcr;
+       u32 icssr;
+       u32 icmsr;
+       u32 icsier;
+       u32 icmier;
+       u32 icccr;
+       u32 icsar;
+       u32 icmar;
+       u32 icrxdtxd;
+       u32 icccr2;
+       u32 icmpr;
+       u32 ichpr;
+       u32 iclpr;
+};
+
+#define MCR_MDBS       0x80    /* non-fifo mode switch */
+#define MCR_FSCL       0x40    /* override SCL pin     */
+#define MCR_FSDA       0x20    /* override SDA pin     */
+#define MCR_OBPC       0x10    /* override pins        */
+#define MCR_MIE                0x08    /* master if enable     */
+#define MCR_TSBE       0x04
+#define MCR_FSB                0x02    /* force stop bit       */
+#define MCR_ESG                0x01    /* en startbit gen.     */
+
+#define MSR_MASK       0x7f
+#define MSR_MNR                0x40    /* nack received        */
+#define MSR_MAL                0x20    /* arbitration lost     */
+#define MSR_MST                0x10    /* sent a stop          */
+#define MSR_MDE                0x08
+#define MSR_MDT                0x04
+#define MSR_MDR                0x02
+#define MSR_MAT                0x01    /* slave addr xfer done */
+
+static const struct rcar_i2c *i2c_dev[CONFIF_SYS_RCAR_I2C_NUM_CONTROLLERS] = {
+       (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C0_BASE,
+       (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C1_BASE,
+       (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C2_BASE,
+       (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C3_BASE,
+};
+
+static void rcar_i2c_raw_rw_common(struct rcar_i2c *dev, u8 chip, uint addr)
+{
+       /* set slave address */
+       writel(chip << 1, &dev->icmar);
+       /* set register address */
+       writel(addr, &dev->icrxdtxd);
+       /* clear status */
+       writel(0, &dev->icmsr);
+       /* start master send */
+       writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr);
+
+       while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDE))
+               != (MSR_MAT | MSR_MDE))
+               udelay(10);
+
+       /* clear ESG */
+       writel(MCR_MDBS | MCR_MIE, &dev->icmcr);
+       /* start SCLclk */
+       writel(~(MSR_MAT | MSR_MDE), &dev->icmsr);
+
+       while (!(readl(&dev->icmsr) & MSR_MDE))
+               udelay(10);
+}
+
+static void rcar_i2c_raw_rw_finish(struct rcar_i2c *dev)
+{
+       while (!(readl(&dev->icmsr) & MSR_MST))
+               udelay(10);
+
+       writel(0, &dev->icmcr);
+}
+
+static int
+rcar_i2c_raw_write(struct rcar_i2c *dev, u8 chip, uint addr, u8 *val, int size)
+{
+       rcar_i2c_raw_rw_common(dev, chip, addr);
+
+       /* set send date */
+       writel(*val, &dev->icrxdtxd);
+       /* start SCLclk */
+       writel(~MSR_MDE, &dev->icmsr);
+
+       while (!(readl(&dev->icmsr) & MSR_MDE))
+               udelay(10);
+
+       /* set stop condition */
+       writel(MCR_MDBS | MCR_MIE | MCR_FSB, &dev->icmcr);
+       /* start SCLclk */
+       writel(~MSR_MDE, &dev->icmsr);
+
+       rcar_i2c_raw_rw_finish(dev);
+
+       return 0;
+}
+
+static u8
+rcar_i2c_raw_read(struct rcar_i2c *dev, u8 chip, uint addr)
+{
+       u8 ret;
+
+       rcar_i2c_raw_rw_common(dev, chip, addr);
+
+       /* set slave address, receive */
+       writel((chip << 1) | 1, &dev->icmar);
+       /* start master receive */
+       writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr);
+
+       while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDE))
+               != (MSR_MAT | MSR_MDE))
+               udelay(10);
+
+       /* clear ESG */
+       writel(MCR_MDBS | MCR_MIE, &dev->icmcr);
+       /* prepare stop condition */
+       writel(MCR_MDBS | MCR_MIE | MCR_FSB, &dev->icmcr);
+       /* start SCLclk */
+       writel(~(MSR_MAT | MSR_MDR), &dev->icmsr);
+
+       while (!(readl(&dev->icmsr) & MSR_MDR))
+               udelay(10);
+
+       /* get receive data */
+       ret = (u8)readl(&dev->icrxdtxd);
+       /* start SCLclk */
+       writel(~MSR_MDR, &dev->icmsr);
+
+       rcar_i2c_raw_rw_finish(dev);
+
+       return ret;
+}
+
+/*
+ * SCL  = iicck / (20 + SCGD * 8 + F[(ticf + tr + intd) * iicck])
+ * iicck  : I2C internal clock < 20 MHz
+ * ticf : I2C SCL falling time: 35 ns
+ * tr   : I2C SCL rising time:  200 ns
+ * intd : LSI internal delay:   I2C0: 50 ns I2C1-3: 5
+ * F[n] : n rounded up to an integer
+ */
+static u32 rcar_clock_gen(int i2c_no, u32 bus_speed)
+{
+       u32 iicck, f, scl, scgd;
+       u32 intd = 5;
+
+       int bit = 0, cdf_width = 3;
+       for (bit = 0; bit < (1 << cdf_width); bit++) {
+               iicck = CONFIG_HP_CLK_FREQ / (1 + bit);
+               if (iicck < 20000000)
+                       break;
+       }
+
+       if (bit > (1 << cdf_width)) {
+               puts("rcar-i2c: Can not get CDF\n");
+               return 0;
+       }
+
+       if (i2c_no == 0)
+               intd = 50;
+
+       f = (35 + 200 + intd) * (iicck / 1000000000);
+
+       for (scgd = 0; scgd < 0x40; scgd++) {
+               scl = iicck / (20 + (scgd * 8) + f);
+               if (scl <= bus_speed)
+                       break;
+       }
+
+       if (scgd > 0x40) {
+               puts("rcar-i2c: Can not get SDGB\n");
+               return 0;
+       }
+
+       debug("%s: scl: %d\n", __func__, scl);
+       debug("%s: bit %x\n", __func__, bit);
+       debug("%s: scgd %x\n", __func__, scgd);
+       debug("%s: iccr %x\n", __func__, (scgd << (cdf_width) | bit));
+
+       return scgd << (cdf_width) | bit;
+}
+
+static void
+rcar_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
+{
+       struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr];
+       u32 icccr = 0;
+
+       /* No i2c support prior to relocation */
+       if (!(gd->flags & GD_FLG_RELOC))
+               return;
+
+       /*
+        * reset slave mode.
+        * slave mode is not used on this driver
+        */
+       writel(0, &dev->icsier);
+       writel(0, &dev->icsar);
+       writel(0, &dev->icscr);
+       writel(0, &dev->icssr);
+
+       /* reset master mode */
+       writel(0, &dev->icmier);
+       writel(0, &dev->icmcr);
+       writel(0, &dev->icmsr);
+       writel(0, &dev->icmar);
+
+       icccr = rcar_clock_gen(adap->hwadapnr, adap->speed);
+       if (icccr == 0)
+               puts("I2C: Init failed\n");
+       else
+               writel(icccr, &dev->icccr);
+}
+
+static int rcar_i2c_read(struct i2c_adapter *adap, uint8_t chip,
+                       uint addr, int alen, u8 *data, int len)
+{
+       struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr];
+       int i;
+
+       for (i = 0; i < len; i++)
+               data[i] = rcar_i2c_raw_read(dev, chip, addr + i);
+
+       return 0;
+}
+
+static int rcar_i2c_write(struct i2c_adapter *adap, uint8_t chip, uint addr,
+                       int alen, u8 *data, int len)
+{
+       struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr];
+       return rcar_i2c_raw_write(dev, chip, addr, data, len);
+}
+
+static int
+rcar_i2c_probe(struct i2c_adapter *adap, u8 dev)
+{
+       return rcar_i2c_read(adap, dev, 0, 0, NULL, 0);
+}
+
+static unsigned int rcar_i2c_set_bus_speed(struct i2c_adapter *adap,
+                       unsigned int speed)
+{
+       struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr];
+       u32 icccr;
+       int ret = 0;
+
+       rcar_i2c_raw_rw_finish(dev);
+
+       icccr = rcar_clock_gen(adap->hwadapnr, speed);
+       if (icccr == 0) {
+               puts("I2C: Init failed\n");
+               ret = -1;
+       } else {
+               writel(icccr, &dev->icccr);
+       }
+       return ret;
+}
+
+/*
+ * Register RCAR i2c adapters
+ */
+U_BOOT_I2C_ADAP_COMPLETE(rcar_0, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read,
+                        rcar_i2c_write, rcar_i2c_set_bus_speed,
+                        CONFIG_SYS_RCAR_I2C0_SPEED, 0, 0)
+U_BOOT_I2C_ADAP_COMPLETE(rcar_1, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read,
+                        rcar_i2c_write, rcar_i2c_set_bus_speed,
+                        CONFIG_SYS_RCAR_I2C1_SPEED, 0, 1)
+U_BOOT_I2C_ADAP_COMPLETE(rcar_2, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read,
+                        rcar_i2c_write, rcar_i2c_set_bus_speed,
+                        CONFIG_SYS_RCAR_I2C2_SPEED, 0, 2)
+U_BOOT_I2C_ADAP_COMPLETE(rcar_3, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read,
+                        rcar_i2c_write, rcar_i2c_set_bus_speed,
+                        CONFIG_SYS_RCAR_I2C3_SPEED, 0, 3)