mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-10-31 00:11:51 +01:00 
			
		
		
		
	Maintainers need to be notified more directly of the need to convert these drivers. Add a note to the top each affected file. Signed-off-by: Simon Glass <sjg@chromium.org> Acked-by: Heiko Schocher <hs@denx.de>
		
			
				
	
	
		
			294 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			294 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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
 | |
|  *
 | |
|  * NOTE: This driver should be converted to driver model before June 2017.
 | |
|  * Please see doc/driver-model/i2c-howto.txt for instructions.
 | |
|  */
 | |
| 
 | |
| #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);
 | |
| 	/* clear status */
 | |
| 	writel(0, &dev->icmsr);
 | |
| 
 | |
| 	while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDR))
 | |
| 		!= (MSR_MAT | MSR_MDR))
 | |
| 		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)
 |