xref: /rk3399_rockchip-uboot/drivers/i2c/imx_lpi2c.c (revision 90d0ce442b2814a193ffd3009d11f4f5aca4d325)
17ee3f149SPeng Fan /*
27ee3f149SPeng Fan  * Copyright 2016 Freescale Semiconductors, Inc.
37ee3f149SPeng Fan  *
47ee3f149SPeng Fan  * SPDX-License-Identifier:	GPL-2.0+
57ee3f149SPeng Fan  */
67ee3f149SPeng Fan 
77ee3f149SPeng Fan #include <common.h>
87ee3f149SPeng Fan #include <errno.h>
97ee3f149SPeng Fan #include <asm/io.h>
107ee3f149SPeng Fan #include <asm/arch/clock.h>
117ee3f149SPeng Fan #include <asm/arch/imx-regs.h>
127ee3f149SPeng Fan #include <asm/arch/imx_lpi2c.h>
137ee3f149SPeng Fan #include <asm/arch/sys_proto.h>
147ee3f149SPeng Fan #include <dm.h>
157ee3f149SPeng Fan #include <fdtdec.h>
167ee3f149SPeng Fan #include <i2c.h>
177ee3f149SPeng Fan 
187ee3f149SPeng Fan DECLARE_GLOBAL_DATA_PTR;
197ee3f149SPeng Fan #define LPI2C_FIFO_SIZE 4
207ee3f149SPeng Fan #define LPI2C_TIMEOUT_MS 100
217ee3f149SPeng Fan 
227ee3f149SPeng Fan /* Weak linked function for overridden by some SoC power function */
init_i2c_power(unsigned i2c_num)237ee3f149SPeng Fan int __weak init_i2c_power(unsigned i2c_num)
247ee3f149SPeng Fan {
257ee3f149SPeng Fan 	return 0;
267ee3f149SPeng Fan }
277ee3f149SPeng Fan 
imx_lpci2c_check_busy_bus(const struct imx_lpi2c_reg * regs)28a821c4afSSimon Glass static int imx_lpci2c_check_busy_bus(const struct imx_lpi2c_reg *regs)
297ee3f149SPeng Fan {
307ee3f149SPeng Fan 	lpi2c_status_t result = LPI2C_SUCESS;
317ee3f149SPeng Fan 	u32 status;
327ee3f149SPeng Fan 
337ee3f149SPeng Fan 	status = readl(&regs->msr);
347ee3f149SPeng Fan 
357ee3f149SPeng Fan 	if ((status & LPI2C_MSR_BBF_MASK) && !(status & LPI2C_MSR_MBF_MASK))
367ee3f149SPeng Fan 		result = LPI2C_BUSY;
377ee3f149SPeng Fan 
387ee3f149SPeng Fan 	return result;
397ee3f149SPeng Fan }
407ee3f149SPeng Fan 
imx_lpci2c_check_clear_error(struct imx_lpi2c_reg * regs)41a821c4afSSimon Glass static int imx_lpci2c_check_clear_error(struct imx_lpi2c_reg *regs)
427ee3f149SPeng Fan {
437ee3f149SPeng Fan 	lpi2c_status_t result = LPI2C_SUCESS;
447ee3f149SPeng Fan 	u32 val, status;
457ee3f149SPeng Fan 
467ee3f149SPeng Fan 	status = readl(&regs->msr);
477ee3f149SPeng Fan 	/* errors to check for */
487ee3f149SPeng Fan 	status &= LPI2C_MSR_NDF_MASK | LPI2C_MSR_ALF_MASK |
497ee3f149SPeng Fan 		LPI2C_MSR_FEF_MASK | LPI2C_MSR_PLTF_MASK;
507ee3f149SPeng Fan 
517ee3f149SPeng Fan 	if (status) {
527ee3f149SPeng Fan 		if (status & LPI2C_MSR_PLTF_MASK)
537ee3f149SPeng Fan 			result = LPI2C_PIN_LOW_TIMEOUT_ERR;
547ee3f149SPeng Fan 		else if (status & LPI2C_MSR_ALF_MASK)
557ee3f149SPeng Fan 			result = LPI2C_ARB_LOST_ERR;
567ee3f149SPeng Fan 		else if (status & LPI2C_MSR_NDF_MASK)
577ee3f149SPeng Fan 			result = LPI2C_NAK_ERR;
587ee3f149SPeng Fan 		else if (status & LPI2C_MSR_FEF_MASK)
597ee3f149SPeng Fan 			result = LPI2C_FIFO_ERR;
607ee3f149SPeng Fan 
617ee3f149SPeng Fan 		/* clear status flags */
627ee3f149SPeng Fan 		writel(0x7f00, &regs->msr);
637ee3f149SPeng Fan 		/* reset fifos */
647ee3f149SPeng Fan 		val = readl(&regs->mcr);
657ee3f149SPeng Fan 		val |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK;
667ee3f149SPeng Fan 		writel(val, &regs->mcr);
677ee3f149SPeng Fan 	}
687ee3f149SPeng Fan 
697ee3f149SPeng Fan 	return result;
707ee3f149SPeng Fan }
717ee3f149SPeng Fan 
bus_i2c_wait_for_tx_ready(struct imx_lpi2c_reg * regs)72a821c4afSSimon Glass static int bus_i2c_wait_for_tx_ready(struct imx_lpi2c_reg *regs)
737ee3f149SPeng Fan {
747ee3f149SPeng Fan 	lpi2c_status_t result = LPI2C_SUCESS;
757ee3f149SPeng Fan 	u32 txcount = 0;
767ee3f149SPeng Fan 	ulong start_time = get_timer(0);
777ee3f149SPeng Fan 
787ee3f149SPeng Fan 	do {
797ee3f149SPeng Fan 		txcount = LPI2C_MFSR_TXCOUNT(readl(&regs->mfsr));
807ee3f149SPeng Fan 		txcount = LPI2C_FIFO_SIZE - txcount;
81a821c4afSSimon Glass 		result = imx_lpci2c_check_clear_error(regs);
827ee3f149SPeng Fan 		if (result) {
837ee3f149SPeng Fan 			debug("i2c: wait for tx ready: result 0x%x\n", result);
847ee3f149SPeng Fan 			return result;
857ee3f149SPeng Fan 		}
867ee3f149SPeng Fan 		if (get_timer(start_time) > LPI2C_TIMEOUT_MS) {
877ee3f149SPeng Fan 			debug("i2c: wait for tx ready: timeout\n");
887ee3f149SPeng Fan 			return -1;
897ee3f149SPeng Fan 		}
907ee3f149SPeng Fan 	} while (!txcount);
917ee3f149SPeng Fan 
927ee3f149SPeng Fan 	return result;
937ee3f149SPeng Fan }
947ee3f149SPeng Fan 
bus_i2c_send(struct imx_lpi2c_reg * regs,u8 * txbuf,int len)95a821c4afSSimon Glass static int bus_i2c_send(struct imx_lpi2c_reg *regs, u8 *txbuf, int len)
967ee3f149SPeng Fan {
977ee3f149SPeng Fan 	lpi2c_status_t result = LPI2C_SUCESS;
987ee3f149SPeng Fan 
997ee3f149SPeng Fan 	/* empty tx */
1007ee3f149SPeng Fan 	if (!len)
1017ee3f149SPeng Fan 		return result;
1027ee3f149SPeng Fan 
1037ee3f149SPeng Fan 	while (len--) {
104a821c4afSSimon Glass 		result = bus_i2c_wait_for_tx_ready(regs);
1057ee3f149SPeng Fan 		if (result) {
1067ee3f149SPeng Fan 			debug("i2c: send wait fot tx ready: %d\n", result);
1077ee3f149SPeng Fan 			return result;
1087ee3f149SPeng Fan 		}
1097ee3f149SPeng Fan 		writel(*txbuf++, &regs->mtdr);
1107ee3f149SPeng Fan 	}
1117ee3f149SPeng Fan 
1127ee3f149SPeng Fan 	return result;
1137ee3f149SPeng Fan }
1147ee3f149SPeng Fan 
bus_i2c_receive(struct imx_lpi2c_reg * regs,u8 * rxbuf,int len)115a821c4afSSimon Glass static int bus_i2c_receive(struct imx_lpi2c_reg *regs, u8 *rxbuf, int len)
1167ee3f149SPeng Fan {
1177ee3f149SPeng Fan 	lpi2c_status_t result = LPI2C_SUCESS;
1187ee3f149SPeng Fan 	u32 val;
1197ee3f149SPeng Fan 	ulong start_time = get_timer(0);
1207ee3f149SPeng Fan 
1217ee3f149SPeng Fan 	/* empty read */
1227ee3f149SPeng Fan 	if (!len)
1237ee3f149SPeng Fan 		return result;
1247ee3f149SPeng Fan 
125a821c4afSSimon Glass 	result = bus_i2c_wait_for_tx_ready(regs);
1267ee3f149SPeng Fan 	if (result) {
1277ee3f149SPeng Fan 		debug("i2c: receive wait fot tx ready: %d\n", result);
1287ee3f149SPeng Fan 		return result;
1297ee3f149SPeng Fan 	}
1307ee3f149SPeng Fan 
1317ee3f149SPeng Fan 	/* clear all status flags */
1327ee3f149SPeng Fan 	writel(0x7f00, &regs->msr);
1337ee3f149SPeng Fan 	/* send receive command */
1347ee3f149SPeng Fan 	val = LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(len - 1);
1357ee3f149SPeng Fan 	writel(val, &regs->mtdr);
1367ee3f149SPeng Fan 
1377ee3f149SPeng Fan 	while (len--) {
1387ee3f149SPeng Fan 		do {
139a821c4afSSimon Glass 			result = imx_lpci2c_check_clear_error(regs);
1407ee3f149SPeng Fan 			if (result) {
141a821c4afSSimon Glass 				debug("i2c: receive check clear error: %d\n",
142a821c4afSSimon Glass 				      result);
1437ee3f149SPeng Fan 				return result;
1447ee3f149SPeng Fan 			}
1457ee3f149SPeng Fan 			if (get_timer(start_time) > LPI2C_TIMEOUT_MS) {
1467ee3f149SPeng Fan 				debug("i2c: receive mrdr: timeout\n");
1477ee3f149SPeng Fan 				return -1;
1487ee3f149SPeng Fan 			}
1497ee3f149SPeng Fan 			val = readl(&regs->mrdr);
1507ee3f149SPeng Fan 		} while (val & LPI2C_MRDR_RXEMPTY_MASK);
1517ee3f149SPeng Fan 		*rxbuf++ = LPI2C_MRDR_DATA(val);
1527ee3f149SPeng Fan 	}
1537ee3f149SPeng Fan 
1547ee3f149SPeng Fan 	return result;
1557ee3f149SPeng Fan }
1567ee3f149SPeng Fan 
bus_i2c_start(struct imx_lpi2c_reg * regs,u8 addr,u8 dir)157a821c4afSSimon Glass static int bus_i2c_start(struct imx_lpi2c_reg *regs, u8 addr, u8 dir)
1587ee3f149SPeng Fan {
1597ee3f149SPeng Fan 	lpi2c_status_t result = LPI2C_SUCESS;
1607ee3f149SPeng Fan 	u32 val;
1617ee3f149SPeng Fan 
162a821c4afSSimon Glass 	result = imx_lpci2c_check_busy_bus(regs);
1637ee3f149SPeng Fan 	if (result) {
1647ee3f149SPeng Fan 		debug("i2c: start check busy bus: 0x%x\n", result);
1657ee3f149SPeng Fan 		return result;
1667ee3f149SPeng Fan 	}
1677ee3f149SPeng Fan 	/* clear all status flags */
1687ee3f149SPeng Fan 	writel(0x7f00, &regs->msr);
1697ee3f149SPeng Fan 	/* turn off auto-stop condition */
1707ee3f149SPeng Fan 	val = readl(&regs->mcfgr1) & ~LPI2C_MCFGR1_AUTOSTOP_MASK;
1717ee3f149SPeng Fan 	writel(val, &regs->mcfgr1);
1727ee3f149SPeng Fan 	/* wait tx fifo ready */
173a821c4afSSimon Glass 	result = bus_i2c_wait_for_tx_ready(regs);
1747ee3f149SPeng Fan 	if (result) {
1757ee3f149SPeng Fan 		debug("i2c: start wait for tx ready: 0x%x\n", result);
1767ee3f149SPeng Fan 		return result;
1777ee3f149SPeng Fan 	}
1787ee3f149SPeng Fan 	/* issue start command */
1797ee3f149SPeng Fan 	val = LPI2C_MTDR_CMD(0x4) | (addr << 0x1) | dir;
1807ee3f149SPeng Fan 	writel(val, &regs->mtdr);
1817ee3f149SPeng Fan 
1827ee3f149SPeng Fan 	return result;
1837ee3f149SPeng Fan }
184a821c4afSSimon Glass 
bus_i2c_stop(struct imx_lpi2c_reg * regs)185a821c4afSSimon Glass static int bus_i2c_stop(struct imx_lpi2c_reg *regs)
1867ee3f149SPeng Fan {
1877ee3f149SPeng Fan 	lpi2c_status_t result = LPI2C_SUCESS;
1887ee3f149SPeng Fan 	u32 status;
1897ee3f149SPeng Fan 
190a821c4afSSimon Glass 	result = bus_i2c_wait_for_tx_ready(regs);
1917ee3f149SPeng Fan 	if (result) {
1927ee3f149SPeng Fan 		debug("i2c: stop wait for tx ready: 0x%x\n", result);
1937ee3f149SPeng Fan 		return result;
1947ee3f149SPeng Fan 	}
1957ee3f149SPeng Fan 
1967ee3f149SPeng Fan 	/* send stop command */
1977ee3f149SPeng Fan 	writel(LPI2C_MTDR_CMD(0x2), &regs->mtdr);
1987ee3f149SPeng Fan 
1997ee3f149SPeng Fan 	while (result == LPI2C_SUCESS) {
2007ee3f149SPeng Fan 		status = readl(&regs->msr);
201a821c4afSSimon Glass 		result = imx_lpci2c_check_clear_error(regs);
2027ee3f149SPeng Fan 		/* stop detect flag */
2037ee3f149SPeng Fan 		if (status & LPI2C_MSR_SDF_MASK) {
2047ee3f149SPeng Fan 			/* clear stop flag */
2057ee3f149SPeng Fan 			status &= LPI2C_MSR_SDF_MASK;
2067ee3f149SPeng Fan 			writel(status, &regs->msr);
2077ee3f149SPeng Fan 			break;
2087ee3f149SPeng Fan 		}
2097ee3f149SPeng Fan 	}
2107ee3f149SPeng Fan 
2117ee3f149SPeng Fan 	return result;
2127ee3f149SPeng Fan }
2137ee3f149SPeng Fan 
bus_i2c_read(struct imx_lpi2c_reg * regs,u32 chip,u8 * buf,int len)214a821c4afSSimon Glass static int bus_i2c_read(struct imx_lpi2c_reg *regs, u32 chip, u8 *buf, int len)
2157ee3f149SPeng Fan {
2167ee3f149SPeng Fan 	lpi2c_status_t result = LPI2C_SUCESS;
2177ee3f149SPeng Fan 
218a821c4afSSimon Glass 	result = bus_i2c_start(regs, chip, 1);
2197ee3f149SPeng Fan 	if (result)
2207ee3f149SPeng Fan 		return result;
221a821c4afSSimon Glass 	result = bus_i2c_receive(regs, buf, len);
2227ee3f149SPeng Fan 	if (result)
2237ee3f149SPeng Fan 		return result;
224a821c4afSSimon Glass 	result = bus_i2c_stop(regs);
2257ee3f149SPeng Fan 	if (result)
2267ee3f149SPeng Fan 		return result;
2277ee3f149SPeng Fan 
2287ee3f149SPeng Fan 	return result;
2297ee3f149SPeng Fan }
2307ee3f149SPeng Fan 
bus_i2c_write(struct imx_lpi2c_reg * regs,u32 chip,u8 * buf,int len)231a821c4afSSimon Glass static int bus_i2c_write(struct imx_lpi2c_reg *regs, u32 chip, u8 *buf, int len)
2327ee3f149SPeng Fan {
2337ee3f149SPeng Fan 	lpi2c_status_t result = LPI2C_SUCESS;
2347ee3f149SPeng Fan 
235a821c4afSSimon Glass 	result = bus_i2c_start(regs, chip, 0);
2367ee3f149SPeng Fan 	if (result)
2377ee3f149SPeng Fan 		return result;
238a821c4afSSimon Glass 	result = bus_i2c_send(regs, buf, len);
2397ee3f149SPeng Fan 	if (result)
2407ee3f149SPeng Fan 		return result;
241a821c4afSSimon Glass 	result = bus_i2c_stop(regs);
2427ee3f149SPeng Fan 	if (result)
2437ee3f149SPeng Fan 		return result;
2447ee3f149SPeng Fan 
2457ee3f149SPeng Fan 	return result;
2467ee3f149SPeng Fan }
2477ee3f149SPeng Fan 
2487ee3f149SPeng Fan 
bus_i2c_set_bus_speed(struct udevice * bus,int speed)2497ee3f149SPeng Fan static int bus_i2c_set_bus_speed(struct udevice *bus, int speed)
2507ee3f149SPeng Fan {
251a821c4afSSimon Glass 	struct imx_lpi2c_reg *regs;
2527ee3f149SPeng Fan 	u32 val;
2537ee3f149SPeng Fan 	u32 preescale = 0, best_pre = 0, clkhi = 0;
2547ee3f149SPeng Fan 	u32 best_clkhi = 0, abs_error = 0, rate;
2557ee3f149SPeng Fan 	u32 error = 0xffffffff;
2567ee3f149SPeng Fan 	u32 clock_rate;
2577ee3f149SPeng Fan 	bool mode;
2587ee3f149SPeng Fan 	int i;
2597ee3f149SPeng Fan 
260a821c4afSSimon Glass 	regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
2617ee3f149SPeng Fan 	clock_rate = imx_get_i2cclk(bus->seq + 4);
2627ee3f149SPeng Fan 	if (!clock_rate)
2637ee3f149SPeng Fan 		return -EPERM;
2647ee3f149SPeng Fan 
2657ee3f149SPeng Fan 	mode = (readl(&regs->mcr) & LPI2C_MCR_MEN_MASK) >> LPI2C_MCR_MEN_SHIFT;
2667ee3f149SPeng Fan 	/* disable master mode */
2677ee3f149SPeng Fan 	val = readl(&regs->mcr) & ~LPI2C_MCR_MEN_MASK;
2687ee3f149SPeng Fan 	writel(val | LPI2C_MCR_MEN(0), &regs->mcr);
2697ee3f149SPeng Fan 
2707ee3f149SPeng Fan 	for (preescale = 1; (preescale <= 128) &&
2717ee3f149SPeng Fan 		(error != 0); preescale = 2 * preescale) {
2727ee3f149SPeng Fan 		for (clkhi = 1; clkhi < 32; clkhi++) {
2737ee3f149SPeng Fan 			if (clkhi == 1)
2747ee3f149SPeng Fan 				rate = (clock_rate / preescale) / (1 + 3 + 2 + 2 / preescale);
2757ee3f149SPeng Fan 			else
2767ee3f149SPeng Fan 				rate = (clock_rate / preescale / (3 * clkhi + 2 + 2 / preescale));
2777ee3f149SPeng Fan 
2787ee3f149SPeng Fan 			abs_error = speed > rate ? speed - rate : rate - speed;
2797ee3f149SPeng Fan 
2807ee3f149SPeng Fan 			if (abs_error < error) {
2817ee3f149SPeng Fan 				best_pre = preescale;
2827ee3f149SPeng Fan 				best_clkhi = clkhi;
2837ee3f149SPeng Fan 				error = abs_error;
2847ee3f149SPeng Fan 				if (abs_error == 0)
2857ee3f149SPeng Fan 					break;
2867ee3f149SPeng Fan 			}
2877ee3f149SPeng Fan 		}
2887ee3f149SPeng Fan 	}
2897ee3f149SPeng Fan 
2907ee3f149SPeng Fan 	/* Standard, fast, fast mode plus and ultra-fast transfers. */
2917ee3f149SPeng Fan 	val = LPI2C_MCCR0_CLKHI(best_clkhi);
2927ee3f149SPeng Fan 	if (best_clkhi < 2)
2937ee3f149SPeng Fan 		val |= LPI2C_MCCR0_CLKLO(3) | LPI2C_MCCR0_SETHOLD(2) | LPI2C_MCCR0_DATAVD(1);
2947ee3f149SPeng Fan 	else
2957ee3f149SPeng Fan 		val |= LPI2C_MCCR0_CLKLO(2 * best_clkhi) | LPI2C_MCCR0_SETHOLD(best_clkhi) |
2967ee3f149SPeng Fan 			LPI2C_MCCR0_DATAVD(best_clkhi / 2);
2977ee3f149SPeng Fan 	writel(val, &regs->mccr0);
2987ee3f149SPeng Fan 
2997ee3f149SPeng Fan 	for (i = 0; i < 8; i++) {
3007ee3f149SPeng Fan 		if (best_pre == (1 << i)) {
3017ee3f149SPeng Fan 			best_pre = i;
3027ee3f149SPeng Fan 			break;
3037ee3f149SPeng Fan 		}
3047ee3f149SPeng Fan 	}
3057ee3f149SPeng Fan 
3067ee3f149SPeng Fan 	val = readl(&regs->mcfgr1) & ~LPI2C_MCFGR1_PRESCALE_MASK;
3077ee3f149SPeng Fan 	writel(val | LPI2C_MCFGR1_PRESCALE(best_pre), &regs->mcfgr1);
3087ee3f149SPeng Fan 
3097ee3f149SPeng Fan 	if (mode) {
3107ee3f149SPeng Fan 		val = readl(&regs->mcr) & ~LPI2C_MCR_MEN_MASK;
3117ee3f149SPeng Fan 		writel(val | LPI2C_MCR_MEN(1), &regs->mcr);
3127ee3f149SPeng Fan 	}
3137ee3f149SPeng Fan 
3147ee3f149SPeng Fan 	return 0;
3157ee3f149SPeng Fan }
3167ee3f149SPeng Fan 
bus_i2c_init(struct udevice * bus,int speed)3177ee3f149SPeng Fan static int bus_i2c_init(struct udevice *bus, int speed)
3187ee3f149SPeng Fan {
319a821c4afSSimon Glass 	struct imx_lpi2c_reg *regs;
3207ee3f149SPeng Fan 	u32 val;
3217ee3f149SPeng Fan 	int ret;
3227ee3f149SPeng Fan 
323a821c4afSSimon Glass 	regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
3247ee3f149SPeng Fan 	/* reset peripheral */
3257ee3f149SPeng Fan 	writel(LPI2C_MCR_RST_MASK, &regs->mcr);
3267ee3f149SPeng Fan 	writel(0x0, &regs->mcr);
3277ee3f149SPeng Fan 	/* Disable Dozen mode */
3287ee3f149SPeng Fan 	writel(LPI2C_MCR_DBGEN(0) | LPI2C_MCR_DOZEN(1), &regs->mcr);
3297ee3f149SPeng Fan 	/* host request disable, active high, external pin */
3307ee3f149SPeng Fan 	val = readl(&regs->mcfgr0);
3317ee3f149SPeng Fan 	val &= (~(LPI2C_MCFGR0_HREN_MASK | LPI2C_MCFGR0_HRPOL_MASK |
3327ee3f149SPeng Fan 				LPI2C_MCFGR0_HRSEL_MASK));
3337ee3f149SPeng Fan 	val |= LPI2C_MCFGR0_HRPOL(0x1);
3347ee3f149SPeng Fan 	writel(val, &regs->mcfgr0);
3357ee3f149SPeng Fan 	/* pincfg and ignore ack */
3367ee3f149SPeng Fan 	val = readl(&regs->mcfgr1);
3377ee3f149SPeng Fan 	val &= ~(LPI2C_MCFGR1_PINCFG_MASK | LPI2C_MCFGR1_IGNACK_MASK);
3387ee3f149SPeng Fan 	val |= LPI2C_MCFGR1_PINCFG(0x0); /* 2 pin open drain */
3397ee3f149SPeng Fan 	val |= LPI2C_MCFGR1_IGNACK(0x0); /* ignore nack */
3407ee3f149SPeng Fan 	writel(val, &regs->mcfgr1);
3417ee3f149SPeng Fan 
3427ee3f149SPeng Fan 	ret = bus_i2c_set_bus_speed(bus, speed);
3437ee3f149SPeng Fan 
3447ee3f149SPeng Fan 	/* enable lpi2c in master mode */
3457ee3f149SPeng Fan 	val = readl(&regs->mcr) & ~LPI2C_MCR_MEN_MASK;
3467ee3f149SPeng Fan 	writel(val | LPI2C_MCR_MEN(1), &regs->mcr);
3477ee3f149SPeng Fan 
3487ee3f149SPeng Fan 	debug("i2c : controller bus %d, speed %d:\n", bus->seq, speed);
3497ee3f149SPeng Fan 
3507ee3f149SPeng Fan 	return ret;
3517ee3f149SPeng Fan }
3527ee3f149SPeng Fan 
imx_lpi2c_probe_chip(struct udevice * bus,u32 chip,u32 chip_flags)3537ee3f149SPeng Fan static int imx_lpi2c_probe_chip(struct udevice *bus, u32 chip,
3547ee3f149SPeng Fan 				u32 chip_flags)
3557ee3f149SPeng Fan {
356a821c4afSSimon Glass 	struct imx_lpi2c_reg *regs;
3577ee3f149SPeng Fan 	lpi2c_status_t result = LPI2C_SUCESS;
3587ee3f149SPeng Fan 
359a821c4afSSimon Glass 	regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
360a821c4afSSimon Glass 	result = bus_i2c_start(regs, chip, 0);
3617ee3f149SPeng Fan 	if (result) {
362a821c4afSSimon Glass 		bus_i2c_stop(regs);
3637ee3f149SPeng Fan 		bus_i2c_init(bus, 100000);
3647ee3f149SPeng Fan 		return result;
3657ee3f149SPeng Fan 	}
3667ee3f149SPeng Fan 
367a821c4afSSimon Glass 	result = bus_i2c_stop(regs);
3687ee3f149SPeng Fan 	if (result) {
3697ee3f149SPeng Fan 		bus_i2c_init(bus, 100000);
3707ee3f149SPeng Fan 		return -result;
3717ee3f149SPeng Fan 	}
3727ee3f149SPeng Fan 
3737ee3f149SPeng Fan 	return result;
3747ee3f149SPeng Fan }
3757ee3f149SPeng Fan 
imx_lpi2c_xfer(struct udevice * bus,struct i2c_msg * msg,int nmsgs)3767ee3f149SPeng Fan static int imx_lpi2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
3777ee3f149SPeng Fan {
378a821c4afSSimon Glass 	struct imx_lpi2c_reg *regs;
3797ee3f149SPeng Fan 	int ret = 0;
3807ee3f149SPeng Fan 
381a821c4afSSimon Glass 	regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus);
3827ee3f149SPeng Fan 	for (; nmsgs > 0; nmsgs--, msg++) {
3837ee3f149SPeng Fan 		debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
3847ee3f149SPeng Fan 		if (msg->flags & I2C_M_RD)
385a821c4afSSimon Glass 			ret = bus_i2c_read(regs, msg->addr, msg->buf, msg->len);
3867ee3f149SPeng Fan 		else {
387a821c4afSSimon Glass 			ret = bus_i2c_write(regs, msg->addr, msg->buf,
3887ee3f149SPeng Fan 					    msg->len);
3897ee3f149SPeng Fan 			if (ret)
3907ee3f149SPeng Fan 				break;
3917ee3f149SPeng Fan 		}
3927ee3f149SPeng Fan 	}
3937ee3f149SPeng Fan 
3947ee3f149SPeng Fan 	if (ret)
3957ee3f149SPeng Fan 		debug("i2c_write: error sending\n");
3967ee3f149SPeng Fan 
3977ee3f149SPeng Fan 	return ret;
3987ee3f149SPeng Fan }
3997ee3f149SPeng Fan 
imx_lpi2c_set_bus_speed(struct udevice * bus,unsigned int speed)4007ee3f149SPeng Fan static int imx_lpi2c_set_bus_speed(struct udevice *bus, unsigned int speed)
4017ee3f149SPeng Fan {
4027ee3f149SPeng Fan 	return bus_i2c_set_bus_speed(bus, speed);
4037ee3f149SPeng Fan }
4047ee3f149SPeng Fan 
imx_lpi2c_probe(struct udevice * bus)4057ee3f149SPeng Fan static int imx_lpi2c_probe(struct udevice *bus)
4067ee3f149SPeng Fan {
4077ee3f149SPeng Fan 	struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
4087ee3f149SPeng Fan 	fdt_addr_t addr;
4097ee3f149SPeng Fan 	int ret;
4107ee3f149SPeng Fan 
4117ee3f149SPeng Fan 	i2c_bus->driver_data = dev_get_driver_data(bus);
4127ee3f149SPeng Fan 
413a821c4afSSimon Glass 	addr = devfdt_get_addr(bus);
4147ee3f149SPeng Fan 	if (addr == FDT_ADDR_T_NONE)
415*90d0ce44SSimon Glass 		return -EINVAL;
4167ee3f149SPeng Fan 
4177ee3f149SPeng Fan 	i2c_bus->base = addr;
4187ee3f149SPeng Fan 	i2c_bus->index = bus->seq;
4197ee3f149SPeng Fan 	i2c_bus->bus = bus;
4207ee3f149SPeng Fan 
4217ee3f149SPeng Fan 	/* power up i2c resource */
4227ee3f149SPeng Fan 	ret = init_i2c_power(bus->seq + 4);
4237ee3f149SPeng Fan 	if (ret) {
4247ee3f149SPeng Fan 		debug("init_i2c_power err = %d\n", ret);
4257ee3f149SPeng Fan 		return ret;
4267ee3f149SPeng Fan 	}
4277ee3f149SPeng Fan 
4287ee3f149SPeng Fan 	/* Enable clk, only i2c4-7 can be handled by A7 core */
4297ee3f149SPeng Fan 	ret = enable_i2c_clk(1, bus->seq + 4);
4307ee3f149SPeng Fan 	if (ret < 0)
4317ee3f149SPeng Fan 		return ret;
4327ee3f149SPeng Fan 
4337ee3f149SPeng Fan 	ret = bus_i2c_init(bus, 100000);
4347ee3f149SPeng Fan 	if (ret < 0)
4357ee3f149SPeng Fan 		return ret;
4367ee3f149SPeng Fan 
4377ee3f149SPeng Fan 	debug("i2c : controller bus %d at %lu , speed %d: ",
4387ee3f149SPeng Fan 	      bus->seq, i2c_bus->base,
4397ee3f149SPeng Fan 	      i2c_bus->speed);
4407ee3f149SPeng Fan 
4417ee3f149SPeng Fan 	return 0;
4427ee3f149SPeng Fan }
4437ee3f149SPeng Fan 
4447ee3f149SPeng Fan static const struct dm_i2c_ops imx_lpi2c_ops = {
4457ee3f149SPeng Fan 	.xfer		= imx_lpi2c_xfer,
4467ee3f149SPeng Fan 	.probe_chip	= imx_lpi2c_probe_chip,
4477ee3f149SPeng Fan 	.set_bus_speed	= imx_lpi2c_set_bus_speed,
4487ee3f149SPeng Fan };
4497ee3f149SPeng Fan 
4507ee3f149SPeng Fan static const struct udevice_id imx_lpi2c_ids[] = {
4517ee3f149SPeng Fan 	{ .compatible = "fsl,imx7ulp-lpi2c", },
4527ee3f149SPeng Fan 	{}
4537ee3f149SPeng Fan };
4547ee3f149SPeng Fan 
4557ee3f149SPeng Fan U_BOOT_DRIVER(imx_lpi2c) = {
4567ee3f149SPeng Fan 	.name = "imx_lpi2c",
4577ee3f149SPeng Fan 	.id = UCLASS_I2C,
4587ee3f149SPeng Fan 	.of_match = imx_lpi2c_ids,
4597ee3f149SPeng Fan 	.probe = imx_lpi2c_probe,
4607ee3f149SPeng Fan 	.priv_auto_alloc_size = sizeof(struct imx_lpi2c_bus),
4617ee3f149SPeng Fan 	.ops = &imx_lpi2c_ops,
4627ee3f149SPeng Fan };
463