diff options
| author | Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> | 2013-09-27 16:58:30 +0900 | 
|---|---|---|
| committer | Heiko Schocher <hs@denx.de> | 2013-10-17 07:20:25 +0200 | 
| commit | 1086bfa9f49127e40a92b1225af1ed5e41f8fa1c (patch) | |
| tree | aa5b74f8eb12b1f2b8c037dc4857eb05eee80481 /drivers/i2c/rcar_i2c.c | |
| parent | e66587495d75ad82c34e9f04af03d3c5741644d3 (diff) | |
| download | olio-uboot-2014.01-1086bfa9f49127e40a92b1225af1ed5e41f8fa1c.tar.xz olio-uboot-2014.01-1086bfa9f49127e40a92b1225af1ed5e41f8fa1c.zip | |
i2c: Add support for Renesas rcar
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>
Diffstat (limited to 'drivers/i2c/rcar_i2c.c')
| -rw-r--r-- | drivers/i2c/rcar_i2c.c | 288 | 
1 files changed, 288 insertions, 0 deletions
| diff --git a/drivers/i2c/rcar_i2c.c b/drivers/i2c/rcar_i2c.c new file mode 100644 index 000000000..ba2cadb17 --- /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) |