1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2427eba70SAlison Wang /*
3427eba70SAlison Wang  * Copyright 2013 Freescale Semiconductor, Inc.
4427eba70SAlison Wang  */
5427eba70SAlison Wang 
6427eba70SAlison Wang #include <common.h>
7fdbae099SBin Meng #include <dm.h>
8c40d612bSPeng Fan #include <fsl_lpuart.h>
9427eba70SAlison Wang #include <watchdog.h>
10427eba70SAlison Wang #include <asm/io.h>
11427eba70SAlison Wang #include <serial.h>
12427eba70SAlison Wang #include <linux/compiler.h>
13427eba70SAlison Wang #include <asm/arch/imx-regs.h>
14427eba70SAlison Wang #include <asm/arch/clock.h>
15427eba70SAlison Wang 
16427eba70SAlison Wang #define US1_TDRE	(1 << 7)
17427eba70SAlison Wang #define US1_RDRF	(1 << 5)
18a3db78d8SStefan Agner #define US1_OR		(1 << 3)
19427eba70SAlison Wang #define UC2_TE		(1 << 3)
20427eba70SAlison Wang #define UC2_RE		(1 << 2)
2189e69fd4SStefan Agner #define CFIFO_TXFLUSH	(1 << 7)
2289e69fd4SStefan Agner #define CFIFO_RXFLUSH	(1 << 6)
2389e69fd4SStefan Agner #define SFIFO_RXOF	(1 << 2)
2489e69fd4SStefan Agner #define SFIFO_RXUF	(1 << 0)
25427eba70SAlison Wang 
266209e14cSJingchang Lu #define STAT_LBKDIF	(1 << 31)
276209e14cSJingchang Lu #define STAT_RXEDGIF	(1 << 30)
286209e14cSJingchang Lu #define STAT_TDRE	(1 << 23)
296209e14cSJingchang Lu #define STAT_RDRF	(1 << 21)
306209e14cSJingchang Lu #define STAT_IDLE	(1 << 20)
316209e14cSJingchang Lu #define STAT_OR		(1 << 19)
326209e14cSJingchang Lu #define STAT_NF		(1 << 18)
336209e14cSJingchang Lu #define STAT_FE		(1 << 17)
346209e14cSJingchang Lu #define STAT_PF		(1 << 16)
356209e14cSJingchang Lu #define STAT_MA1F	(1 << 15)
366209e14cSJingchang Lu #define STAT_MA2F	(1 << 14)
376209e14cSJingchang Lu #define STAT_FLAGS	(STAT_LBKDIF | STAT_RXEDGIF | STAT_IDLE | STAT_OR | \
386209e14cSJingchang Lu 			 STAT_NF | STAT_FE | STAT_PF | STAT_MA1F | STAT_MA2F)
396209e14cSJingchang Lu 
406209e14cSJingchang Lu #define CTRL_TE		(1 << 19)
416209e14cSJingchang Lu #define CTRL_RE		(1 << 18)
426209e14cSJingchang Lu 
436209e14cSJingchang Lu #define FIFO_TXFE		0x80
446209e14cSJingchang Lu #define FIFO_RXFE		0x40
456209e14cSJingchang Lu 
466209e14cSJingchang Lu #define WATER_TXWATER_OFF	1
476209e14cSJingchang Lu #define WATER_RXWATER_OFF	16
486209e14cSJingchang Lu 
49427eba70SAlison Wang DECLARE_GLOBAL_DATA_PTR;
50427eba70SAlison Wang 
51c40d612bSPeng Fan #define LPUART_FLAG_REGMAP_32BIT_REG	BIT(0)
52c40d612bSPeng Fan #define LPUART_FLAG_REGMAP_ENDIAN_BIG	BIT(1)
53c40d612bSPeng Fan 
547edf5c45SPeng Fan enum lpuart_devtype {
557edf5c45SPeng Fan 	DEV_VF610 = 1,
567edf5c45SPeng Fan 	DEV_LS1021A,
577edf5c45SPeng Fan 	DEV_MX7ULP
587edf5c45SPeng Fan };
597edf5c45SPeng Fan 
60fdbae099SBin Meng struct lpuart_serial_platdata {
61c40d612bSPeng Fan 	void *reg;
627edf5c45SPeng Fan 	enum lpuart_devtype devtype;
63c40d612bSPeng Fan 	ulong flags;
64fdbae099SBin Meng };
65fdbae099SBin Meng 
66c40d612bSPeng Fan static void lpuart_read32(u32 flags, u32 *addr, u32 *val)
67427eba70SAlison Wang {
68c40d612bSPeng Fan 	if (flags & LPUART_FLAG_REGMAP_32BIT_REG) {
69c40d612bSPeng Fan 		if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG)
70c40d612bSPeng Fan 			*(u32 *)val = in_be32(addr);
71c40d612bSPeng Fan 		else
72c40d612bSPeng Fan 			*(u32 *)val = in_le32(addr);
73c40d612bSPeng Fan 	}
74c40d612bSPeng Fan }
75c40d612bSPeng Fan 
76c40d612bSPeng Fan static void lpuart_write32(u32 flags, u32 *addr, u32 val)
77c40d612bSPeng Fan {
78c40d612bSPeng Fan 	if (flags & LPUART_FLAG_REGMAP_32BIT_REG) {
79c40d612bSPeng Fan 		if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG)
80c40d612bSPeng Fan 			out_be32(addr, val);
81c40d612bSPeng Fan 		else
82c40d612bSPeng Fan 			out_le32(addr, val);
83c40d612bSPeng Fan 	}
84c40d612bSPeng Fan }
85c40d612bSPeng Fan 
86c40d612bSPeng Fan 
87c40d612bSPeng Fan #ifndef CONFIG_SYS_CLK_FREQ
88c40d612bSPeng Fan #define CONFIG_SYS_CLK_FREQ	0
89c40d612bSPeng Fan #endif
90c40d612bSPeng Fan 
91c40d612bSPeng Fan u32 __weak get_lpuart_clk(void)
92c40d612bSPeng Fan {
93c40d612bSPeng Fan 	return CONFIG_SYS_CLK_FREQ;
94c40d612bSPeng Fan }
95c40d612bSPeng Fan 
96c40d612bSPeng Fan static bool is_lpuart32(struct udevice *dev)
97c40d612bSPeng Fan {
98c40d612bSPeng Fan 	struct lpuart_serial_platdata *plat = dev->platdata;
99c40d612bSPeng Fan 
100c40d612bSPeng Fan 	return plat->flags & LPUART_FLAG_REGMAP_32BIT_REG;
101c40d612bSPeng Fan }
102c40d612bSPeng Fan 
103c40d612bSPeng Fan static void _lpuart_serial_setbrg(struct lpuart_serial_platdata *plat,
104c40d612bSPeng Fan 				  int baudrate)
105c40d612bSPeng Fan {
106c40d612bSPeng Fan 	struct lpuart_fsl *base = plat->reg;
107c40d612bSPeng Fan 	u32 clk = get_lpuart_clk();
108427eba70SAlison Wang 	u16 sbr;
109427eba70SAlison Wang 
1106ca13b12SBin Meng 	sbr = (u16)(clk / (16 * baudrate));
111427eba70SAlison Wang 
11247f1bfcaSBin Meng 	/* place adjustment later - n/32 BRFA */
113427eba70SAlison Wang 	__raw_writeb(sbr >> 8, &base->ubdh);
114427eba70SAlison Wang 	__raw_writeb(sbr & 0xff, &base->ubdl);
115427eba70SAlison Wang }
116427eba70SAlison Wang 
117c40d612bSPeng Fan static int _lpuart_serial_getc(struct lpuart_serial_platdata *plat)
118427eba70SAlison Wang {
119c40d612bSPeng Fan 	struct lpuart_fsl *base = plat->reg;
120a3db78d8SStefan Agner 	while (!(__raw_readb(&base->us1) & (US1_RDRF | US1_OR)))
121427eba70SAlison Wang 		WATCHDOG_RESET();
122427eba70SAlison Wang 
123a3db78d8SStefan Agner 	barrier();
124427eba70SAlison Wang 
125427eba70SAlison Wang 	return __raw_readb(&base->ud);
126427eba70SAlison Wang }
127427eba70SAlison Wang 
128c40d612bSPeng Fan static void _lpuart_serial_putc(struct lpuart_serial_platdata *plat,
129c40d612bSPeng Fan 				const char c)
130427eba70SAlison Wang {
131c40d612bSPeng Fan 	struct lpuart_fsl *base = plat->reg;
132c40d612bSPeng Fan 
133427eba70SAlison Wang 	while (!(__raw_readb(&base->us1) & US1_TDRE))
134427eba70SAlison Wang 		WATCHDOG_RESET();
135427eba70SAlison Wang 
136427eba70SAlison Wang 	__raw_writeb(c, &base->ud);
137427eba70SAlison Wang }
138427eba70SAlison Wang 
13947f1bfcaSBin Meng /* Test whether a character is in the RX buffer */
140c40d612bSPeng Fan static int _lpuart_serial_tstc(struct lpuart_serial_platdata *plat)
141427eba70SAlison Wang {
142c40d612bSPeng Fan 	struct lpuart_fsl *base = plat->reg;
143c40d612bSPeng Fan 
144427eba70SAlison Wang 	if (__raw_readb(&base->urcfifo) == 0)
145427eba70SAlison Wang 		return 0;
146427eba70SAlison Wang 
147427eba70SAlison Wang 	return 1;
148427eba70SAlison Wang }
149427eba70SAlison Wang 
150427eba70SAlison Wang /*
151427eba70SAlison Wang  * Initialise the serial port with the given baudrate. The settings
152427eba70SAlison Wang  * are always 8 data bits, no parity, 1 stop bit, no start bits.
153427eba70SAlison Wang  */
154c40d612bSPeng Fan static int _lpuart_serial_init(struct lpuart_serial_platdata *plat)
155427eba70SAlison Wang {
156c40d612bSPeng Fan 	struct lpuart_fsl *base = (struct lpuart_fsl *)plat->reg;
157427eba70SAlison Wang 	u8 ctrl;
158427eba70SAlison Wang 
159427eba70SAlison Wang 	ctrl = __raw_readb(&base->uc2);
160427eba70SAlison Wang 	ctrl &= ~UC2_RE;
161427eba70SAlison Wang 	ctrl &= ~UC2_TE;
162427eba70SAlison Wang 	__raw_writeb(ctrl, &base->uc2);
163427eba70SAlison Wang 
164427eba70SAlison Wang 	__raw_writeb(0, &base->umodem);
165427eba70SAlison Wang 	__raw_writeb(0, &base->uc1);
166427eba70SAlison Wang 
16789e69fd4SStefan Agner 	/* Disable FIFO and flush buffer */
16889e69fd4SStefan Agner 	__raw_writeb(0x0, &base->upfifo);
16989e69fd4SStefan Agner 	__raw_writeb(0x0, &base->utwfifo);
17089e69fd4SStefan Agner 	__raw_writeb(0x1, &base->urwfifo);
17189e69fd4SStefan Agner 	__raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo);
17289e69fd4SStefan Agner 
173427eba70SAlison Wang 	/* provide data bits, parity, stop bit, etc */
174c40d612bSPeng Fan 	_lpuart_serial_setbrg(plat, gd->baudrate);
175427eba70SAlison Wang 
176427eba70SAlison Wang 	__raw_writeb(UC2_RE | UC2_TE, &base->uc2);
177427eba70SAlison Wang 
178427eba70SAlison Wang 	return 0;
179427eba70SAlison Wang }
180427eba70SAlison Wang 
1817edf5c45SPeng Fan static void _lpuart32_serial_setbrg_7ulp(struct lpuart_serial_platdata *plat,
1827edf5c45SPeng Fan 					 int baudrate)
1837edf5c45SPeng Fan {
1847edf5c45SPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
1857edf5c45SPeng Fan 	u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;
1867edf5c45SPeng Fan 	u32 clk = get_lpuart_clk();
1877edf5c45SPeng Fan 
1887edf5c45SPeng Fan 	baud_diff = baudrate;
1897edf5c45SPeng Fan 	osr = 0;
1907edf5c45SPeng Fan 	sbr = 0;
1917edf5c45SPeng Fan 
1927edf5c45SPeng Fan 	for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) {
1937edf5c45SPeng Fan 		tmp_sbr = (clk / (baudrate * tmp_osr));
1947edf5c45SPeng Fan 
1957edf5c45SPeng Fan 		if (tmp_sbr == 0)
1967edf5c45SPeng Fan 			tmp_sbr = 1;
1977edf5c45SPeng Fan 
1987edf5c45SPeng Fan 		/*calculate difference in actual buad w/ current values */
1997edf5c45SPeng Fan 		tmp_diff = (clk / (tmp_osr * tmp_sbr));
2007edf5c45SPeng Fan 		tmp_diff = tmp_diff - baudrate;
2017edf5c45SPeng Fan 
2027edf5c45SPeng Fan 		/* select best values between sbr and sbr+1 */
2037edf5c45SPeng Fan 		if (tmp_diff > (baudrate - (clk / (tmp_osr * (tmp_sbr + 1))))) {
2047edf5c45SPeng Fan 			tmp_diff = baudrate - (clk / (tmp_osr * (tmp_sbr + 1)));
2057edf5c45SPeng Fan 			tmp_sbr++;
2067edf5c45SPeng Fan 		}
2077edf5c45SPeng Fan 
2087edf5c45SPeng Fan 		if (tmp_diff <= baud_diff) {
2097edf5c45SPeng Fan 			baud_diff = tmp_diff;
2107edf5c45SPeng Fan 			osr = tmp_osr;
2117edf5c45SPeng Fan 			sbr = tmp_sbr;
2127edf5c45SPeng Fan 		}
2137edf5c45SPeng Fan 	}
2147edf5c45SPeng Fan 
2157edf5c45SPeng Fan 	/*
2167edf5c45SPeng Fan 	 * TODO: handle buadrate outside acceptable rate
2177edf5c45SPeng Fan 	 * if (baudDiff > ((config->baudRate_Bps / 100) * 3))
2187edf5c45SPeng Fan 	 * {
2197edf5c45SPeng Fan 	 *   Unacceptable baud rate difference of more than 3%
2207edf5c45SPeng Fan 	 *   return kStatus_LPUART_BaudrateNotSupport;
2217edf5c45SPeng Fan 	 * }
2227edf5c45SPeng Fan 	 */
2237edf5c45SPeng Fan 	tmp = in_le32(&base->baud);
2247edf5c45SPeng Fan 
2257edf5c45SPeng Fan 	if ((osr > 3) && (osr < 8))
2267edf5c45SPeng Fan 		tmp |= LPUART_BAUD_BOTHEDGE_MASK;
2277edf5c45SPeng Fan 
2287edf5c45SPeng Fan 	tmp &= ~LPUART_BAUD_OSR_MASK;
2297edf5c45SPeng Fan 	tmp |= LPUART_BAUD_OSR(osr-1);
2307edf5c45SPeng Fan 
2317edf5c45SPeng Fan 	tmp &= ~LPUART_BAUD_SBR_MASK;
2327edf5c45SPeng Fan 	tmp |= LPUART_BAUD_SBR(sbr);
2337edf5c45SPeng Fan 
2347edf5c45SPeng Fan 	/* explicitly disable 10 bit mode & set 1 stop bit */
2357edf5c45SPeng Fan 	tmp &= ~(LPUART_BAUD_M10_MASK | LPUART_BAUD_SBNS_MASK);
2367edf5c45SPeng Fan 
2377edf5c45SPeng Fan 	out_le32(&base->baud, tmp);
2387edf5c45SPeng Fan }
2397edf5c45SPeng Fan 
240c40d612bSPeng Fan static void _lpuart32_serial_setbrg(struct lpuart_serial_platdata *plat,
241c40d612bSPeng Fan 				    int baudrate)
242fdbae099SBin Meng {
243c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
2449b46213bSShaohui Xie 	u32 clk = get_lpuart_clk();
2456209e14cSJingchang Lu 	u32 sbr;
2466209e14cSJingchang Lu 
2476ca13b12SBin Meng 	sbr = (clk / (16 * baudrate));
2486209e14cSJingchang Lu 
24947f1bfcaSBin Meng 	/* place adjustment later - n/32 BRFA */
250c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->baud, sbr);
2516209e14cSJingchang Lu }
2526209e14cSJingchang Lu 
253c40d612bSPeng Fan static int _lpuart32_serial_getc(struct lpuart_serial_platdata *plat)
2546209e14cSJingchang Lu {
255c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
2567edf5c45SPeng Fan 	u32 stat, val;
2576209e14cSJingchang Lu 
258c40d612bSPeng Fan 	lpuart_read32(plat->flags, &base->stat, &stat);
259c40d612bSPeng Fan 	while ((stat & STAT_RDRF) == 0) {
260c40d612bSPeng Fan 		lpuart_write32(plat->flags, &base->stat, STAT_FLAGS);
2616209e14cSJingchang Lu 		WATCHDOG_RESET();
262c40d612bSPeng Fan 		lpuart_read32(plat->flags, &base->stat, &stat);
2636209e14cSJingchang Lu 	}
2646209e14cSJingchang Lu 
2657edf5c45SPeng Fan 	lpuart_read32(plat->flags, &base->data, &val);
266c40d612bSPeng Fan 
2677edf5c45SPeng Fan 	lpuart_read32(plat->flags, &base->stat, &stat);
2687edf5c45SPeng Fan 	if (stat & STAT_OR)
2697edf5c45SPeng Fan 		lpuart_write32(plat->flags, &base->stat, STAT_OR);
2707edf5c45SPeng Fan 
2717edf5c45SPeng Fan 	return val & 0x3ff;
2726209e14cSJingchang Lu }
2736209e14cSJingchang Lu 
274c40d612bSPeng Fan static void _lpuart32_serial_putc(struct lpuart_serial_platdata *plat,
275c40d612bSPeng Fan 				  const char c)
2766209e14cSJingchang Lu {
277c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
278c40d612bSPeng Fan 	u32 stat;
2796209e14cSJingchang Lu 
2807edf5c45SPeng Fan 	if (c == '\n')
2817edf5c45SPeng Fan 		serial_putc('\r');
2827edf5c45SPeng Fan 
283c40d612bSPeng Fan 	while (true) {
284c40d612bSPeng Fan 		lpuart_read32(plat->flags, &base->stat, &stat);
285c40d612bSPeng Fan 
286c40d612bSPeng Fan 		if ((stat & STAT_TDRE))
287c40d612bSPeng Fan 			break;
288c40d612bSPeng Fan 
289c40d612bSPeng Fan 		WATCHDOG_RESET();
290c40d612bSPeng Fan 	}
291c40d612bSPeng Fan 
292c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->data, c);
2936209e14cSJingchang Lu }
2946209e14cSJingchang Lu 
29547f1bfcaSBin Meng /* Test whether a character is in the RX buffer */
296c40d612bSPeng Fan static int _lpuart32_serial_tstc(struct lpuart_serial_platdata *plat)
2976209e14cSJingchang Lu {
298c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
299c40d612bSPeng Fan 	u32 water;
300c40d612bSPeng Fan 
301c40d612bSPeng Fan 	lpuart_read32(plat->flags, &base->water, &water);
302c40d612bSPeng Fan 
303c40d612bSPeng Fan 	if ((water >> 24) == 0)
3046209e14cSJingchang Lu 		return 0;
3056209e14cSJingchang Lu 
3066209e14cSJingchang Lu 	return 1;
3076209e14cSJingchang Lu }
3086209e14cSJingchang Lu 
3096209e14cSJingchang Lu /*
3106209e14cSJingchang Lu  * Initialise the serial port with the given baudrate. The settings
3116209e14cSJingchang Lu  * are always 8 data bits, no parity, 1 stop bit, no start bits.
3126209e14cSJingchang Lu  */
313c40d612bSPeng Fan static int _lpuart32_serial_init(struct lpuart_serial_platdata *plat)
3146209e14cSJingchang Lu {
315c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = (struct lpuart_fsl_reg32 *)plat->reg;
316c40d612bSPeng Fan 	u32 ctrl;
3176209e14cSJingchang Lu 
318c40d612bSPeng Fan 	lpuart_read32(plat->flags, &base->ctrl, &ctrl);
3196209e14cSJingchang Lu 	ctrl &= ~CTRL_RE;
3206209e14cSJingchang Lu 	ctrl &= ~CTRL_TE;
321c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->ctrl, ctrl);
3226209e14cSJingchang Lu 
323c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->modir, 0);
324c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->fifo, ~(FIFO_TXFE | FIFO_RXFE));
3256209e14cSJingchang Lu 
326c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->match, 0);
3276209e14cSJingchang Lu 
328a2bbfc54SSriram Dash 	if (plat->devtype == DEV_MX7ULP) {
3297edf5c45SPeng Fan 		_lpuart32_serial_setbrg_7ulp(plat, gd->baudrate);
3307edf5c45SPeng Fan 	} else {
33147f1bfcaSBin Meng 		/* provide data bits, parity, stop bit, etc */
332c40d612bSPeng Fan 		_lpuart32_serial_setbrg(plat, gd->baudrate);
3337edf5c45SPeng Fan 	}
3346209e14cSJingchang Lu 
335c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->ctrl, CTRL_RE | CTRL_TE);
3366209e14cSJingchang Lu 
3376209e14cSJingchang Lu 	return 0;
3386209e14cSJingchang Lu }
3396209e14cSJingchang Lu 
340c40d612bSPeng Fan static int lpuart_serial_setbrg(struct udevice *dev, int baudrate)
341fdbae099SBin Meng {
342fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
343fdbae099SBin Meng 
3447edf5c45SPeng Fan 	if (is_lpuart32(dev)) {
345a2bbfc54SSriram Dash 		if (plat->devtype == DEV_MX7ULP)
3467edf5c45SPeng Fan 			_lpuart32_serial_setbrg_7ulp(plat, baudrate);
347c40d612bSPeng Fan 		else
3487edf5c45SPeng Fan 			_lpuart32_serial_setbrg(plat, baudrate);
3497edf5c45SPeng Fan 	} else {
350c40d612bSPeng Fan 		_lpuart_serial_setbrg(plat, baudrate);
3517edf5c45SPeng Fan 	}
352fdbae099SBin Meng 
353fdbae099SBin Meng 	return 0;
354fdbae099SBin Meng }
355fdbae099SBin Meng 
356c40d612bSPeng Fan static int lpuart_serial_getc(struct udevice *dev)
357fdbae099SBin Meng {
358fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
359fdbae099SBin Meng 
360c40d612bSPeng Fan 	if (is_lpuart32(dev))
361c40d612bSPeng Fan 		return _lpuart32_serial_getc(plat);
362c40d612bSPeng Fan 
363c40d612bSPeng Fan 	return _lpuart_serial_getc(plat);
364fdbae099SBin Meng }
365fdbae099SBin Meng 
366c40d612bSPeng Fan static int lpuart_serial_putc(struct udevice *dev, const char c)
367fdbae099SBin Meng {
368fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
369fdbae099SBin Meng 
370c40d612bSPeng Fan 	if (is_lpuart32(dev))
371c40d612bSPeng Fan 		_lpuart32_serial_putc(plat, c);
372c40d612bSPeng Fan 	else
373c40d612bSPeng Fan 		_lpuart_serial_putc(plat, c);
374fdbae099SBin Meng 
375fdbae099SBin Meng 	return 0;
376fdbae099SBin Meng }
377fdbae099SBin Meng 
378c40d612bSPeng Fan static int lpuart_serial_pending(struct udevice *dev, bool input)
379fdbae099SBin Meng {
380fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
381fdbae099SBin Meng 	struct lpuart_fsl *reg = plat->reg;
382c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *reg32 = plat->reg;
383c40d612bSPeng Fan 	u32 stat;
384c40d612bSPeng Fan 
385c40d612bSPeng Fan 	if (is_lpuart32(dev)) {
386c40d612bSPeng Fan 		if (input) {
387c40d612bSPeng Fan 			return _lpuart32_serial_tstc(plat);
388c40d612bSPeng Fan 		} else {
389c40d612bSPeng Fan 			lpuart_read32(plat->flags, &reg32->stat, &stat);
390c40d612bSPeng Fan 			return stat & STAT_TDRE ? 0 : 1;
391c40d612bSPeng Fan 		}
392c40d612bSPeng Fan 	}
393fdbae099SBin Meng 
394fdbae099SBin Meng 	if (input)
395c40d612bSPeng Fan 		return _lpuart_serial_tstc(plat);
396fdbae099SBin Meng 	else
397c40d612bSPeng Fan 		return __raw_readb(&reg->us1) & US1_TDRE ? 0 : 1;
398fdbae099SBin Meng }
399fdbae099SBin Meng 
400c40d612bSPeng Fan static int lpuart_serial_probe(struct udevice *dev)
401fdbae099SBin Meng {
402fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
403fdbae099SBin Meng 
404c40d612bSPeng Fan 	if (is_lpuart32(dev))
405c40d612bSPeng Fan 		return _lpuart32_serial_init(plat);
406c40d612bSPeng Fan 	else
407c40d612bSPeng Fan 		return _lpuart_serial_init(plat);
408fdbae099SBin Meng }
409427eba70SAlison Wang 
410fdbae099SBin Meng static int lpuart_serial_ofdata_to_platdata(struct udevice *dev)
411fdbae099SBin Meng {
412fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
4137edf5c45SPeng Fan 	const void *blob = gd->fdt_blob;
414da409cccSSimon Glass 	int node = dev_of_offset(dev);
415fdbae099SBin Meng 	fdt_addr_t addr;
416fdbae099SBin Meng 
417a821c4afSSimon Glass 	addr = devfdt_get_addr(dev);
418fdbae099SBin Meng 	if (addr == FDT_ADDR_T_NONE)
419fdbae099SBin Meng 		return -EINVAL;
420fdbae099SBin Meng 
421c40d612bSPeng Fan 	plat->reg = (void *)addr;
422c40d612bSPeng Fan 	plat->flags = dev_get_driver_data(dev);
423fdbae099SBin Meng 
4247edf5c45SPeng Fan 	if (!fdt_node_check_compatible(blob, node, "fsl,ls1021a-lpuart"))
4257edf5c45SPeng Fan 		plat->devtype = DEV_LS1021A;
4267edf5c45SPeng Fan 	else if (!fdt_node_check_compatible(blob, node, "fsl,imx7ulp-lpuart"))
4277edf5c45SPeng Fan 		plat->devtype = DEV_MX7ULP;
4287edf5c45SPeng Fan 	else if (!fdt_node_check_compatible(blob, node, "fsl,vf610-lpuart"))
4297edf5c45SPeng Fan 		plat->devtype = DEV_VF610;
4307edf5c45SPeng Fan 
431fdbae099SBin Meng 	return 0;
432fdbae099SBin Meng }
433fdbae099SBin Meng 
434fdbae099SBin Meng static const struct dm_serial_ops lpuart_serial_ops = {
435fdbae099SBin Meng 	.putc = lpuart_serial_putc,
436fdbae099SBin Meng 	.pending = lpuart_serial_pending,
437fdbae099SBin Meng 	.getc = lpuart_serial_getc,
438fdbae099SBin Meng 	.setbrg = lpuart_serial_setbrg,
439fdbae099SBin Meng };
440fdbae099SBin Meng 
441fdbae099SBin Meng static const struct udevice_id lpuart_serial_ids[] = {
442c40d612bSPeng Fan 	{ .compatible = "fsl,ls1021a-lpuart", .data =
443c40d612bSPeng Fan 		LPUART_FLAG_REGMAP_32BIT_REG | LPUART_FLAG_REGMAP_ENDIAN_BIG },
4447edf5c45SPeng Fan 	{ .compatible = "fsl,imx7ulp-lpuart",
4457edf5c45SPeng Fan 		.data = LPUART_FLAG_REGMAP_32BIT_REG },
446fdbae099SBin Meng 	{ .compatible = "fsl,vf610-lpuart"},
447fdbae099SBin Meng 	{ }
448fdbae099SBin Meng };
449fdbae099SBin Meng 
450fdbae099SBin Meng U_BOOT_DRIVER(serial_lpuart) = {
451fdbae099SBin Meng 	.name	= "serial_lpuart",
452fdbae099SBin Meng 	.id	= UCLASS_SERIAL,
453fdbae099SBin Meng 	.of_match = lpuart_serial_ids,
454fdbae099SBin Meng 	.ofdata_to_platdata = lpuart_serial_ofdata_to_platdata,
455fdbae099SBin Meng 	.platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata),
456fdbae099SBin Meng 	.probe = lpuart_serial_probe,
457fdbae099SBin Meng 	.ops	= &lpuart_serial_ops,
458fdbae099SBin Meng 	.flags = DM_FLAG_PRE_RELOC,
459fdbae099SBin Meng };
460