diff mbox

[U-Boot,v2] i2c: Add support for Renesas rcar

Message ID 1380268710-31709-1-git-send-email-nobuhiro.iwamatsu.yj@renesas.com
State Awaiting Upstream
Delegated to: Heiko Schocher
Headers show

Commit Message

Nobuhiro Iwamatsu Sept. 27, 2013, 7:58 a.m. UTC
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>
---
 v2:
   - Add infomation about rcar_i2c to README.
   - Remove empty line.
   - Fix space.
   - Keep list sorted in Makefile

 README                 |  14 +++
 drivers/i2c/Makefile   |   1 +
 drivers/i2c/rcar_i2c.c | 288 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 303 insertions(+)
 create mode 100644 drivers/i2c/rcar_i2c.c

Comments

Heiko Schocher Oct. 17, 2013, 6:24 a.m. UTC | #1
Hello Nobuhiro,

Am 27.09.2013 09:58, schrieb Nobuhiro Iwamatsu:
> 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>
> ---
>   v2:
>     - Add infomation about rcar_i2c to README.
>     - Remove empty line.
>     - Fix space.
>     - Keep list sorted in Makefile
>
>   README                 |  14 +++
>   drivers/i2c/Makefile   |   1 +
>   drivers/i2c/rcar_i2c.c | 288 +++++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 303 insertions(+)
>   create mode 100644 drivers/i2c/rcar_i2c.c

Applied to u-boot-i2c.git

Thanks!

bye,
Heiko
diff mbox

Patch

diff --git a/README b/README
index ccd47fa..98c385a 100644
--- a/README
+++ b/README
@@ -1995,6 +1995,20 @@  CBFS (Coreboot Filesystem) support
 		  - CONFIG_SYS_I2C_PPC4XX_CH0 activate hardware channel 0
 		  - CONFIG_SYS_I2C_PPC4XX_CH1 activate hardware channel 1
 
+		- 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 nummber of i2c buses
+
 		additional defines:
 
 		CONFIG_SYS_NUM_I2C_BUSES
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 37ccbd1..e028534 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -30,6 +30,7 @@  COBJS-$(CONFIG_SYS_I2C) += i2c_core.o
 COBJS-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o
 COBJS-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.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
index 0000000..ba2cadb
--- /dev/null
+++ b/drivers/i2c/rcar_i2c.c
@@ -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)