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(®s->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(®s->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, ®s->msr);
637ee3f149SPeng Fan /* reset fifos */
647ee3f149SPeng Fan val = readl(®s->mcr);
657ee3f149SPeng Fan val |= LPI2C_MCR_RRF_MASK | LPI2C_MCR_RTF_MASK;
667ee3f149SPeng Fan writel(val, ®s->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(®s->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++, ®s->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, ®s->msr);
1337ee3f149SPeng Fan /* send receive command */
1347ee3f149SPeng Fan val = LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(len - 1);
1357ee3f149SPeng Fan writel(val, ®s->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(®s->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, ®s->msr);
1697ee3f149SPeng Fan /* turn off auto-stop condition */
1707ee3f149SPeng Fan val = readl(®s->mcfgr1) & ~LPI2C_MCFGR1_AUTOSTOP_MASK;
1717ee3f149SPeng Fan writel(val, ®s->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, ®s->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), ®s->mtdr);
1987ee3f149SPeng Fan
1997ee3f149SPeng Fan while (result == LPI2C_SUCESS) {
2007ee3f149SPeng Fan status = readl(®s->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, ®s->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(®s->mcr) & LPI2C_MCR_MEN_MASK) >> LPI2C_MCR_MEN_SHIFT;
2667ee3f149SPeng Fan /* disable master mode */
2677ee3f149SPeng Fan val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK;
2687ee3f149SPeng Fan writel(val | LPI2C_MCR_MEN(0), ®s->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, ®s->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(®s->mcfgr1) & ~LPI2C_MCFGR1_PRESCALE_MASK;
3077ee3f149SPeng Fan writel(val | LPI2C_MCFGR1_PRESCALE(best_pre), ®s->mcfgr1);
3087ee3f149SPeng Fan
3097ee3f149SPeng Fan if (mode) {
3107ee3f149SPeng Fan val = readl(®s->mcr) & ~LPI2C_MCR_MEN_MASK;
3117ee3f149SPeng Fan writel(val | LPI2C_MCR_MEN(1), ®s->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, ®s->mcr);
3267ee3f149SPeng Fan writel(0x0, ®s->mcr);
3277ee3f149SPeng Fan /* Disable Dozen mode */
3287ee3f149SPeng Fan writel(LPI2C_MCR_DBGEN(0) | LPI2C_MCR_DOZEN(1), ®s->mcr);
3297ee3f149SPeng Fan /* host request disable, active high, external pin */
3307ee3f149SPeng Fan val = readl(®s->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, ®s->mcfgr0);
3357ee3f149SPeng Fan /* pincfg and ignore ack */
3367ee3f149SPeng Fan val = readl(®s->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, ®s->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(®s->mcr) & ~LPI2C_MCR_MEN_MASK;
3467ee3f149SPeng Fan writel(val | LPI2C_MCR_MEN(1), ®s->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