183d290c5STom 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>
7*8f5b6299SPeng Fan #include <clk.h>
8fdbae099SBin Meng #include <dm.h>
9c40d612bSPeng Fan #include <fsl_lpuart.h>
10427eba70SAlison Wang #include <watchdog.h>
11427eba70SAlison Wang #include <asm/io.h>
12427eba70SAlison Wang #include <serial.h>
13427eba70SAlison Wang #include <linux/compiler.h>
14427eba70SAlison Wang #include <asm/arch/imx-regs.h>
15427eba70SAlison Wang #include <asm/arch/clock.h>
16427eba70SAlison Wang 
17427eba70SAlison Wang #define US1_TDRE	(1 << 7)
18427eba70SAlison Wang #define US1_RDRF	(1 << 5)
19a3db78d8SStefan Agner #define US1_OR		(1 << 3)
20427eba70SAlison Wang #define UC2_TE		(1 << 3)
21427eba70SAlison Wang #define UC2_RE		(1 << 2)
2289e69fd4SStefan Agner #define CFIFO_TXFLUSH	(1 << 7)
2389e69fd4SStefan Agner #define CFIFO_RXFLUSH	(1 << 6)
2489e69fd4SStefan Agner #define SFIFO_RXOF	(1 << 2)
2589e69fd4SStefan Agner #define SFIFO_RXUF	(1 << 0)
26427eba70SAlison Wang 
276209e14cSJingchang Lu #define STAT_LBKDIF	(1 << 31)
286209e14cSJingchang Lu #define STAT_RXEDGIF	(1 << 30)
296209e14cSJingchang Lu #define STAT_TDRE	(1 << 23)
306209e14cSJingchang Lu #define STAT_RDRF	(1 << 21)
316209e14cSJingchang Lu #define STAT_IDLE	(1 << 20)
326209e14cSJingchang Lu #define STAT_OR		(1 << 19)
336209e14cSJingchang Lu #define STAT_NF		(1 << 18)
346209e14cSJingchang Lu #define STAT_FE		(1 << 17)
356209e14cSJingchang Lu #define STAT_PF		(1 << 16)
366209e14cSJingchang Lu #define STAT_MA1F	(1 << 15)
376209e14cSJingchang Lu #define STAT_MA2F	(1 << 14)
386209e14cSJingchang Lu #define STAT_FLAGS	(STAT_LBKDIF | STAT_RXEDGIF | STAT_IDLE | STAT_OR | \
396209e14cSJingchang Lu 			 STAT_NF | STAT_FE | STAT_PF | STAT_MA1F | STAT_MA2F)
406209e14cSJingchang Lu 
416209e14cSJingchang Lu #define CTRL_TE		(1 << 19)
426209e14cSJingchang Lu #define CTRL_RE		(1 << 18)
436209e14cSJingchang Lu 
44cdc16f61SYe Li #define FIFO_RXFLUSH		BIT(14)
45cdc16f61SYe Li #define FIFO_TXFLUSH		BIT(15)
46cdc16f61SYe Li #define FIFO_TXSIZE_MASK	0x70
47cdc16f61SYe Li #define FIFO_TXSIZE_OFF	4
48cdc16f61SYe Li #define FIFO_RXSIZE_MASK	0x7
49cdc16f61SYe Li #define FIFO_RXSIZE_OFF	0
506209e14cSJingchang Lu #define FIFO_TXFE		0x80
51126f8849SPeng Fan #ifdef CONFIG_ARCH_IMX8
52126f8849SPeng Fan #define FIFO_RXFE		0x08
53126f8849SPeng Fan #else
546209e14cSJingchang Lu #define FIFO_RXFE		0x40
55126f8849SPeng Fan #endif
566209e14cSJingchang Lu 
57cdc16f61SYe Li #define WATER_TXWATER_OFF	0
586209e14cSJingchang Lu #define WATER_RXWATER_OFF	16
596209e14cSJingchang Lu 
60427eba70SAlison Wang DECLARE_GLOBAL_DATA_PTR;
61427eba70SAlison Wang 
62c40d612bSPeng Fan #define LPUART_FLAG_REGMAP_32BIT_REG	BIT(0)
63c40d612bSPeng Fan #define LPUART_FLAG_REGMAP_ENDIAN_BIG	BIT(1)
64c40d612bSPeng Fan 
657edf5c45SPeng Fan enum lpuart_devtype {
667edf5c45SPeng Fan 	DEV_VF610 = 1,
677edf5c45SPeng Fan 	DEV_LS1021A,
68126f8849SPeng Fan 	DEV_MX7ULP,
69126f8849SPeng Fan 	DEV_IMX8
707edf5c45SPeng Fan };
717edf5c45SPeng Fan 
72fdbae099SBin Meng struct lpuart_serial_platdata {
73c40d612bSPeng Fan 	void *reg;
747edf5c45SPeng Fan 	enum lpuart_devtype devtype;
75c40d612bSPeng Fan 	ulong flags;
76fdbae099SBin Meng };
77fdbae099SBin Meng 
lpuart_read32(u32 flags,u32 * addr,u32 * val)78c40d612bSPeng Fan static void lpuart_read32(u32 flags, u32 *addr, u32 *val)
79427eba70SAlison Wang {
80c40d612bSPeng Fan 	if (flags & LPUART_FLAG_REGMAP_32BIT_REG) {
81c40d612bSPeng Fan 		if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG)
82c40d612bSPeng Fan 			*(u32 *)val = in_be32(addr);
83c40d612bSPeng Fan 		else
84c40d612bSPeng Fan 			*(u32 *)val = in_le32(addr);
85c40d612bSPeng Fan 	}
86c40d612bSPeng Fan }
87c40d612bSPeng Fan 
lpuart_write32(u32 flags,u32 * addr,u32 val)88c40d612bSPeng Fan static void lpuart_write32(u32 flags, u32 *addr, u32 val)
89c40d612bSPeng Fan {
90c40d612bSPeng Fan 	if (flags & LPUART_FLAG_REGMAP_32BIT_REG) {
91c40d612bSPeng Fan 		if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG)
92c40d612bSPeng Fan 			out_be32(addr, val);
93c40d612bSPeng Fan 		else
94c40d612bSPeng Fan 			out_le32(addr, val);
95c40d612bSPeng Fan 	}
96c40d612bSPeng Fan }
97c40d612bSPeng Fan 
98c40d612bSPeng Fan 
99c40d612bSPeng Fan #ifndef CONFIG_SYS_CLK_FREQ
100c40d612bSPeng Fan #define CONFIG_SYS_CLK_FREQ	0
101c40d612bSPeng Fan #endif
102c40d612bSPeng Fan 
get_lpuart_clk(void)103c40d612bSPeng Fan u32 __weak get_lpuart_clk(void)
104c40d612bSPeng Fan {
105c40d612bSPeng Fan 	return CONFIG_SYS_CLK_FREQ;
106c40d612bSPeng Fan }
107c40d612bSPeng Fan 
108*8f5b6299SPeng Fan #if IS_ENABLED(CONFIG_CLK)
get_lpuart_clk_rate(struct udevice * dev,u32 * clk)109*8f5b6299SPeng Fan static int get_lpuart_clk_rate(struct udevice *dev, u32 *clk)
110*8f5b6299SPeng Fan {
111*8f5b6299SPeng Fan 	struct clk per_clk;
112*8f5b6299SPeng Fan 	ulong rate;
113*8f5b6299SPeng Fan 	int ret;
114*8f5b6299SPeng Fan 
115*8f5b6299SPeng Fan 	ret = clk_get_by_name(dev, "per", &per_clk);
116*8f5b6299SPeng Fan 	if (ret) {
117*8f5b6299SPeng Fan 		dev_err(dev, "Failed to get per clk: %d\n", ret);
118*8f5b6299SPeng Fan 		return ret;
119*8f5b6299SPeng Fan 	}
120*8f5b6299SPeng Fan 
121*8f5b6299SPeng Fan 	rate = clk_get_rate(&per_clk);
122*8f5b6299SPeng Fan 	if ((long)rate <= 0) {
123*8f5b6299SPeng Fan 		dev_err(dev, "Failed to get per clk rate: %ld\n", (long)rate);
124*8f5b6299SPeng Fan 		return ret;
125*8f5b6299SPeng Fan 	}
126*8f5b6299SPeng Fan 	*clk = rate;
127*8f5b6299SPeng Fan 	return 0;
128*8f5b6299SPeng Fan }
129*8f5b6299SPeng Fan #else
get_lpuart_clk_rate(struct udevice * dev,u32 * clk)130*8f5b6299SPeng Fan static inline int get_lpuart_clk_rate(struct udevice *dev, u32 *clk)
131*8f5b6299SPeng Fan { return -ENOSYS; }
132*8f5b6299SPeng Fan #endif
133*8f5b6299SPeng Fan 
is_lpuart32(struct udevice * dev)134c40d612bSPeng Fan static bool is_lpuart32(struct udevice *dev)
135c40d612bSPeng Fan {
136c40d612bSPeng Fan 	struct lpuart_serial_platdata *plat = dev->platdata;
137c40d612bSPeng Fan 
138c40d612bSPeng Fan 	return plat->flags & LPUART_FLAG_REGMAP_32BIT_REG;
139c40d612bSPeng Fan }
140c40d612bSPeng Fan 
_lpuart_serial_setbrg(struct udevice * dev,int baudrate)141*8f5b6299SPeng Fan static void _lpuart_serial_setbrg(struct udevice *dev,
142c40d612bSPeng Fan 				  int baudrate)
143c40d612bSPeng Fan {
144*8f5b6299SPeng Fan 	struct lpuart_serial_platdata *plat = dev_get_platdata(dev);
145c40d612bSPeng Fan 	struct lpuart_fsl *base = plat->reg;
146*8f5b6299SPeng Fan 	u32 clk;
147427eba70SAlison Wang 	u16 sbr;
148*8f5b6299SPeng Fan 	int ret;
149*8f5b6299SPeng Fan 
150*8f5b6299SPeng Fan 	if (IS_ENABLED(CONFIG_CLK)) {
151*8f5b6299SPeng Fan 		ret = get_lpuart_clk_rate(dev, &clk);
152*8f5b6299SPeng Fan 		if (ret)
153*8f5b6299SPeng Fan 			return;
154*8f5b6299SPeng Fan 	} else {
155*8f5b6299SPeng Fan 		clk = get_lpuart_clk();
156*8f5b6299SPeng Fan 	}
157427eba70SAlison Wang 
1586ca13b12SBin Meng 	sbr = (u16)(clk / (16 * baudrate));
159427eba70SAlison Wang 
16047f1bfcaSBin Meng 	/* place adjustment later - n/32 BRFA */
161427eba70SAlison Wang 	__raw_writeb(sbr >> 8, &base->ubdh);
162427eba70SAlison Wang 	__raw_writeb(sbr & 0xff, &base->ubdl);
163427eba70SAlison Wang }
164427eba70SAlison Wang 
_lpuart_serial_getc(struct lpuart_serial_platdata * plat)165c40d612bSPeng Fan static int _lpuart_serial_getc(struct lpuart_serial_platdata *plat)
166427eba70SAlison Wang {
167c40d612bSPeng Fan 	struct lpuart_fsl *base = plat->reg;
168a3db78d8SStefan Agner 	while (!(__raw_readb(&base->us1) & (US1_RDRF | US1_OR)))
169427eba70SAlison Wang 		WATCHDOG_RESET();
170427eba70SAlison Wang 
171a3db78d8SStefan Agner 	barrier();
172427eba70SAlison Wang 
173427eba70SAlison Wang 	return __raw_readb(&base->ud);
174427eba70SAlison Wang }
175427eba70SAlison Wang 
_lpuart_serial_putc(struct lpuart_serial_platdata * plat,const char c)176c40d612bSPeng Fan static void _lpuart_serial_putc(struct lpuart_serial_platdata *plat,
177c40d612bSPeng Fan 				const char c)
178427eba70SAlison Wang {
179c40d612bSPeng Fan 	struct lpuart_fsl *base = plat->reg;
180c40d612bSPeng Fan 
181427eba70SAlison Wang 	while (!(__raw_readb(&base->us1) & US1_TDRE))
182427eba70SAlison Wang 		WATCHDOG_RESET();
183427eba70SAlison Wang 
184427eba70SAlison Wang 	__raw_writeb(c, &base->ud);
185427eba70SAlison Wang }
186427eba70SAlison Wang 
18747f1bfcaSBin Meng /* Test whether a character is in the RX buffer */
_lpuart_serial_tstc(struct lpuart_serial_platdata * plat)188c40d612bSPeng Fan static int _lpuart_serial_tstc(struct lpuart_serial_platdata *plat)
189427eba70SAlison Wang {
190c40d612bSPeng Fan 	struct lpuart_fsl *base = plat->reg;
191c40d612bSPeng Fan 
192427eba70SAlison Wang 	if (__raw_readb(&base->urcfifo) == 0)
193427eba70SAlison Wang 		return 0;
194427eba70SAlison Wang 
195427eba70SAlison Wang 	return 1;
196427eba70SAlison Wang }
197427eba70SAlison Wang 
198427eba70SAlison Wang /*
199427eba70SAlison Wang  * Initialise the serial port with the given baudrate. The settings
200427eba70SAlison Wang  * are always 8 data bits, no parity, 1 stop bit, no start bits.
201427eba70SAlison Wang  */
_lpuart_serial_init(struct udevice * dev)202*8f5b6299SPeng Fan static int _lpuart_serial_init(struct udevice *dev)
203427eba70SAlison Wang {
204*8f5b6299SPeng Fan 	struct lpuart_serial_platdata *plat = dev_get_platdata(dev);
205c40d612bSPeng Fan 	struct lpuart_fsl *base = (struct lpuart_fsl *)plat->reg;
206427eba70SAlison Wang 	u8 ctrl;
207427eba70SAlison Wang 
208427eba70SAlison Wang 	ctrl = __raw_readb(&base->uc2);
209427eba70SAlison Wang 	ctrl &= ~UC2_RE;
210427eba70SAlison Wang 	ctrl &= ~UC2_TE;
211427eba70SAlison Wang 	__raw_writeb(ctrl, &base->uc2);
212427eba70SAlison Wang 
213427eba70SAlison Wang 	__raw_writeb(0, &base->umodem);
214427eba70SAlison Wang 	__raw_writeb(0, &base->uc1);
215427eba70SAlison Wang 
21689e69fd4SStefan Agner 	/* Disable FIFO and flush buffer */
21789e69fd4SStefan Agner 	__raw_writeb(0x0, &base->upfifo);
21889e69fd4SStefan Agner 	__raw_writeb(0x0, &base->utwfifo);
21989e69fd4SStefan Agner 	__raw_writeb(0x1, &base->urwfifo);
22089e69fd4SStefan Agner 	__raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo);
22189e69fd4SStefan Agner 
222427eba70SAlison Wang 	/* provide data bits, parity, stop bit, etc */
223*8f5b6299SPeng Fan 	_lpuart_serial_setbrg(dev, gd->baudrate);
224427eba70SAlison Wang 
225427eba70SAlison Wang 	__raw_writeb(UC2_RE | UC2_TE, &base->uc2);
226427eba70SAlison Wang 
227427eba70SAlison Wang 	return 0;
228427eba70SAlison Wang }
229427eba70SAlison Wang 
_lpuart32_serial_setbrg_7ulp(struct udevice * dev,int baudrate)230*8f5b6299SPeng Fan static void _lpuart32_serial_setbrg_7ulp(struct udevice *dev,
2317edf5c45SPeng Fan 					 int baudrate)
2327edf5c45SPeng Fan {
233*8f5b6299SPeng Fan 	struct lpuart_serial_platdata *plat = dev_get_platdata(dev);
2347edf5c45SPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
2357edf5c45SPeng Fan 	u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;
236*8f5b6299SPeng Fan 	u32 clk;
237*8f5b6299SPeng Fan 	int ret;
238*8f5b6299SPeng Fan 
239*8f5b6299SPeng Fan 	if (IS_ENABLED(CONFIG_CLK)) {
240*8f5b6299SPeng Fan 		ret = get_lpuart_clk_rate(dev, &clk);
241*8f5b6299SPeng Fan 		if (ret)
242*8f5b6299SPeng Fan 			return;
243*8f5b6299SPeng Fan 	} else {
244*8f5b6299SPeng Fan 		clk = get_lpuart_clk();
245*8f5b6299SPeng Fan 	}
2467edf5c45SPeng Fan 
2477edf5c45SPeng Fan 	baud_diff = baudrate;
2487edf5c45SPeng Fan 	osr = 0;
2497edf5c45SPeng Fan 	sbr = 0;
2507edf5c45SPeng Fan 
2517edf5c45SPeng Fan 	for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) {
2527edf5c45SPeng Fan 		tmp_sbr = (clk / (baudrate * tmp_osr));
2537edf5c45SPeng Fan 
2547edf5c45SPeng Fan 		if (tmp_sbr == 0)
2557edf5c45SPeng Fan 			tmp_sbr = 1;
2567edf5c45SPeng Fan 
2577edf5c45SPeng Fan 		/*calculate difference in actual buad w/ current values */
2587edf5c45SPeng Fan 		tmp_diff = (clk / (tmp_osr * tmp_sbr));
2597edf5c45SPeng Fan 		tmp_diff = tmp_diff - baudrate;
2607edf5c45SPeng Fan 
2617edf5c45SPeng Fan 		/* select best values between sbr and sbr+1 */
2627edf5c45SPeng Fan 		if (tmp_diff > (baudrate - (clk / (tmp_osr * (tmp_sbr + 1))))) {
2637edf5c45SPeng Fan 			tmp_diff = baudrate - (clk / (tmp_osr * (tmp_sbr + 1)));
2647edf5c45SPeng Fan 			tmp_sbr++;
2657edf5c45SPeng Fan 		}
2667edf5c45SPeng Fan 
2677edf5c45SPeng Fan 		if (tmp_diff <= baud_diff) {
2687edf5c45SPeng Fan 			baud_diff = tmp_diff;
2697edf5c45SPeng Fan 			osr = tmp_osr;
2707edf5c45SPeng Fan 			sbr = tmp_sbr;
2717edf5c45SPeng Fan 		}
2727edf5c45SPeng Fan 	}
2737edf5c45SPeng Fan 
2747edf5c45SPeng Fan 	/*
2757edf5c45SPeng Fan 	 * TODO: handle buadrate outside acceptable rate
2767edf5c45SPeng Fan 	 * if (baudDiff > ((config->baudRate_Bps / 100) * 3))
2777edf5c45SPeng Fan 	 * {
2787edf5c45SPeng Fan 	 *   Unacceptable baud rate difference of more than 3%
2797edf5c45SPeng Fan 	 *   return kStatus_LPUART_BaudrateNotSupport;
2807edf5c45SPeng Fan 	 * }
2817edf5c45SPeng Fan 	 */
2827edf5c45SPeng Fan 	tmp = in_le32(&base->baud);
2837edf5c45SPeng Fan 
2847edf5c45SPeng Fan 	if ((osr > 3) && (osr < 8))
2857edf5c45SPeng Fan 		tmp |= LPUART_BAUD_BOTHEDGE_MASK;
2867edf5c45SPeng Fan 
2877edf5c45SPeng Fan 	tmp &= ~LPUART_BAUD_OSR_MASK;
2887edf5c45SPeng Fan 	tmp |= LPUART_BAUD_OSR(osr-1);
2897edf5c45SPeng Fan 
2907edf5c45SPeng Fan 	tmp &= ~LPUART_BAUD_SBR_MASK;
2917edf5c45SPeng Fan 	tmp |= LPUART_BAUD_SBR(sbr);
2927edf5c45SPeng Fan 
2937edf5c45SPeng Fan 	/* explicitly disable 10 bit mode & set 1 stop bit */
2947edf5c45SPeng Fan 	tmp &= ~(LPUART_BAUD_M10_MASK | LPUART_BAUD_SBNS_MASK);
2957edf5c45SPeng Fan 
2967edf5c45SPeng Fan 	out_le32(&base->baud, tmp);
2977edf5c45SPeng Fan }
2987edf5c45SPeng Fan 
_lpuart32_serial_setbrg(struct udevice * dev,int baudrate)299*8f5b6299SPeng Fan static void _lpuart32_serial_setbrg(struct udevice *dev,
300c40d612bSPeng Fan 				    int baudrate)
301fdbae099SBin Meng {
302*8f5b6299SPeng Fan 	struct lpuart_serial_platdata *plat = dev_get_platdata(dev);
303c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
304*8f5b6299SPeng Fan 	u32 clk;
3056209e14cSJingchang Lu 	u32 sbr;
306*8f5b6299SPeng Fan 	int ret;
307*8f5b6299SPeng Fan 
308*8f5b6299SPeng Fan 	if (IS_ENABLED(CONFIG_CLK)) {
309*8f5b6299SPeng Fan 		ret = get_lpuart_clk_rate(dev, &clk);
310*8f5b6299SPeng Fan 		if (ret)
311*8f5b6299SPeng Fan 			return;
312*8f5b6299SPeng Fan 	} else {
313*8f5b6299SPeng Fan 		clk = get_lpuart_clk();
314*8f5b6299SPeng Fan 	}
3156209e14cSJingchang Lu 
3166ca13b12SBin Meng 	sbr = (clk / (16 * baudrate));
3176209e14cSJingchang Lu 
31847f1bfcaSBin Meng 	/* place adjustment later - n/32 BRFA */
319c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->baud, sbr);
3206209e14cSJingchang Lu }
3216209e14cSJingchang Lu 
_lpuart32_serial_getc(struct lpuart_serial_platdata * plat)322c40d612bSPeng Fan static int _lpuart32_serial_getc(struct lpuart_serial_platdata *plat)
3236209e14cSJingchang Lu {
324c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
3257edf5c45SPeng Fan 	u32 stat, val;
3266209e14cSJingchang Lu 
327c40d612bSPeng Fan 	lpuart_read32(plat->flags, &base->stat, &stat);
328c40d612bSPeng Fan 	while ((stat & STAT_RDRF) == 0) {
329c40d612bSPeng Fan 		lpuart_write32(plat->flags, &base->stat, STAT_FLAGS);
3306209e14cSJingchang Lu 		WATCHDOG_RESET();
331c40d612bSPeng Fan 		lpuart_read32(plat->flags, &base->stat, &stat);
3326209e14cSJingchang Lu 	}
3336209e14cSJingchang Lu 
3347edf5c45SPeng Fan 	lpuart_read32(plat->flags, &base->data, &val);
335c40d612bSPeng Fan 
3367edf5c45SPeng Fan 	lpuart_read32(plat->flags, &base->stat, &stat);
3377edf5c45SPeng Fan 	if (stat & STAT_OR)
3387edf5c45SPeng Fan 		lpuart_write32(plat->flags, &base->stat, STAT_OR);
3397edf5c45SPeng Fan 
3407edf5c45SPeng Fan 	return val & 0x3ff;
3416209e14cSJingchang Lu }
3426209e14cSJingchang Lu 
_lpuart32_serial_putc(struct lpuart_serial_platdata * plat,const char c)343c40d612bSPeng Fan static void _lpuart32_serial_putc(struct lpuart_serial_platdata *plat,
344c40d612bSPeng Fan 				  const char c)
3456209e14cSJingchang Lu {
346c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
347c40d612bSPeng Fan 	u32 stat;
3486209e14cSJingchang Lu 
3497edf5c45SPeng Fan 	if (c == '\n')
3507edf5c45SPeng Fan 		serial_putc('\r');
3517edf5c45SPeng Fan 
352c40d612bSPeng Fan 	while (true) {
353c40d612bSPeng Fan 		lpuart_read32(plat->flags, &base->stat, &stat);
354c40d612bSPeng Fan 
355c40d612bSPeng Fan 		if ((stat & STAT_TDRE))
356c40d612bSPeng Fan 			break;
357c40d612bSPeng Fan 
358c40d612bSPeng Fan 		WATCHDOG_RESET();
359c40d612bSPeng Fan 	}
360c40d612bSPeng Fan 
361c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->data, c);
3626209e14cSJingchang Lu }
3636209e14cSJingchang Lu 
36447f1bfcaSBin Meng /* Test whether a character is in the RX buffer */
_lpuart32_serial_tstc(struct lpuart_serial_platdata * plat)365c40d612bSPeng Fan static int _lpuart32_serial_tstc(struct lpuart_serial_platdata *plat)
3666209e14cSJingchang Lu {
367c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
368c40d612bSPeng Fan 	u32 water;
369c40d612bSPeng Fan 
370c40d612bSPeng Fan 	lpuart_read32(plat->flags, &base->water, &water);
371c40d612bSPeng Fan 
372c40d612bSPeng Fan 	if ((water >> 24) == 0)
3736209e14cSJingchang Lu 		return 0;
3746209e14cSJingchang Lu 
3756209e14cSJingchang Lu 	return 1;
3766209e14cSJingchang Lu }
3776209e14cSJingchang Lu 
3786209e14cSJingchang Lu /*
3796209e14cSJingchang Lu  * Initialise the serial port with the given baudrate. The settings
3806209e14cSJingchang Lu  * are always 8 data bits, no parity, 1 stop bit, no start bits.
3816209e14cSJingchang Lu  */
_lpuart32_serial_init(struct udevice * dev)382*8f5b6299SPeng Fan static int _lpuart32_serial_init(struct udevice *dev)
3836209e14cSJingchang Lu {
384*8f5b6299SPeng Fan 	struct lpuart_serial_platdata *plat = dev_get_platdata(dev);
385c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = (struct lpuart_fsl_reg32 *)plat->reg;
386cdc16f61SYe Li 	u32 val, tx_fifo_size;
3876209e14cSJingchang Lu 
388cdc16f61SYe Li 	lpuart_read32(plat->flags, &base->ctrl, &val);
389cdc16f61SYe Li 	val &= ~CTRL_RE;
390cdc16f61SYe Li 	val &= ~CTRL_TE;
391cdc16f61SYe Li 	lpuart_write32(plat->flags, &base->ctrl, val);
3926209e14cSJingchang Lu 
393c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->modir, 0);
394cdc16f61SYe Li 
395cdc16f61SYe Li 	lpuart_read32(plat->flags, &base->fifo, &val);
396cdc16f61SYe Li 	tx_fifo_size = (val & FIFO_TXSIZE_MASK) >> FIFO_TXSIZE_OFF;
397cdc16f61SYe Li 	/* Set the TX water to half of FIFO size */
398cdc16f61SYe Li 	if (tx_fifo_size > 1)
399cdc16f61SYe Li 		tx_fifo_size = tx_fifo_size >> 1;
400cdc16f61SYe Li 
401cdc16f61SYe Li 	/* Set RX water to 0, to be triggered by any receive data */
402cdc16f61SYe Li 	lpuart_write32(plat->flags, &base->water,
403cdc16f61SYe Li 		       (tx_fifo_size << WATER_TXWATER_OFF));
404cdc16f61SYe Li 
405cdc16f61SYe Li 	/* Enable TX and RX FIFO */
406cdc16f61SYe Li 	val |= (FIFO_TXFE | FIFO_RXFE | FIFO_TXFLUSH | FIFO_RXFLUSH);
407cdc16f61SYe Li 	lpuart_write32(plat->flags, &base->fifo, val);
4086209e14cSJingchang Lu 
409c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->match, 0);
4106209e14cSJingchang Lu 
411126f8849SPeng Fan 	if (plat->devtype == DEV_MX7ULP || plat->devtype == DEV_IMX8) {
412*8f5b6299SPeng Fan 		_lpuart32_serial_setbrg_7ulp(dev, gd->baudrate);
4137edf5c45SPeng Fan 	} else {
41447f1bfcaSBin Meng 		/* provide data bits, parity, stop bit, etc */
415*8f5b6299SPeng Fan 		_lpuart32_serial_setbrg(dev, gd->baudrate);
4167edf5c45SPeng Fan 	}
4176209e14cSJingchang Lu 
418c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->ctrl, CTRL_RE | CTRL_TE);
4196209e14cSJingchang Lu 
4206209e14cSJingchang Lu 	return 0;
4216209e14cSJingchang Lu }
4226209e14cSJingchang Lu 
lpuart_serial_setbrg(struct udevice * dev,int baudrate)423c40d612bSPeng Fan static int lpuart_serial_setbrg(struct udevice *dev, int baudrate)
424fdbae099SBin Meng {
425*8f5b6299SPeng Fan 	struct lpuart_serial_platdata *plat = dev_get_platdata(dev);
426fdbae099SBin Meng 
4277edf5c45SPeng Fan 	if (is_lpuart32(dev)) {
428126f8849SPeng Fan 		if (plat->devtype == DEV_MX7ULP || plat->devtype == DEV_IMX8)
429*8f5b6299SPeng Fan 			_lpuart32_serial_setbrg_7ulp(dev, baudrate);
430c40d612bSPeng Fan 		else
431*8f5b6299SPeng Fan 			_lpuart32_serial_setbrg(dev, baudrate);
4327edf5c45SPeng Fan 	} else {
433*8f5b6299SPeng Fan 		_lpuart_serial_setbrg(dev, baudrate);
4347edf5c45SPeng Fan 	}
435fdbae099SBin Meng 
436fdbae099SBin Meng 	return 0;
437fdbae099SBin Meng }
438fdbae099SBin Meng 
lpuart_serial_getc(struct udevice * dev)439c40d612bSPeng Fan static int lpuart_serial_getc(struct udevice *dev)
440fdbae099SBin Meng {
441fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
442fdbae099SBin Meng 
443c40d612bSPeng Fan 	if (is_lpuart32(dev))
444c40d612bSPeng Fan 		return _lpuart32_serial_getc(plat);
445c40d612bSPeng Fan 
446c40d612bSPeng Fan 	return _lpuart_serial_getc(plat);
447fdbae099SBin Meng }
448fdbae099SBin Meng 
lpuart_serial_putc(struct udevice * dev,const char c)449c40d612bSPeng Fan static int lpuart_serial_putc(struct udevice *dev, const char c)
450fdbae099SBin Meng {
451fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
452fdbae099SBin Meng 
453c40d612bSPeng Fan 	if (is_lpuart32(dev))
454c40d612bSPeng Fan 		_lpuart32_serial_putc(plat, c);
455c40d612bSPeng Fan 	else
456c40d612bSPeng Fan 		_lpuart_serial_putc(plat, c);
457fdbae099SBin Meng 
458fdbae099SBin Meng 	return 0;
459fdbae099SBin Meng }
460fdbae099SBin Meng 
lpuart_serial_pending(struct udevice * dev,bool input)461c40d612bSPeng Fan static int lpuart_serial_pending(struct udevice *dev, bool input)
462fdbae099SBin Meng {
463fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
464fdbae099SBin Meng 	struct lpuart_fsl *reg = plat->reg;
465c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *reg32 = plat->reg;
466c40d612bSPeng Fan 	u32 stat;
467c40d612bSPeng Fan 
468c40d612bSPeng Fan 	if (is_lpuart32(dev)) {
469c40d612bSPeng Fan 		if (input) {
470c40d612bSPeng Fan 			return _lpuart32_serial_tstc(plat);
471c40d612bSPeng Fan 		} else {
472c40d612bSPeng Fan 			lpuart_read32(plat->flags, &reg32->stat, &stat);
473c40d612bSPeng Fan 			return stat & STAT_TDRE ? 0 : 1;
474c40d612bSPeng Fan 		}
475c40d612bSPeng Fan 	}
476fdbae099SBin Meng 
477fdbae099SBin Meng 	if (input)
478c40d612bSPeng Fan 		return _lpuart_serial_tstc(plat);
479fdbae099SBin Meng 	else
480c40d612bSPeng Fan 		return __raw_readb(&reg->us1) & US1_TDRE ? 0 : 1;
481fdbae099SBin Meng }
482fdbae099SBin Meng 
lpuart_serial_probe(struct udevice * dev)483c40d612bSPeng Fan static int lpuart_serial_probe(struct udevice *dev)
484fdbae099SBin Meng {
485c40d612bSPeng Fan 	if (is_lpuart32(dev))
486*8f5b6299SPeng Fan 		return _lpuart32_serial_init(dev);
487c40d612bSPeng Fan 	else
488*8f5b6299SPeng Fan 		return _lpuart_serial_init(dev);
489fdbae099SBin Meng }
490427eba70SAlison Wang 
lpuart_serial_ofdata_to_platdata(struct udevice * dev)491fdbae099SBin Meng static int lpuart_serial_ofdata_to_platdata(struct udevice *dev)
492fdbae099SBin Meng {
493fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
4947edf5c45SPeng Fan 	const void *blob = gd->fdt_blob;
495da409cccSSimon Glass 	int node = dev_of_offset(dev);
496fdbae099SBin Meng 	fdt_addr_t addr;
497fdbae099SBin Meng 
498a821c4afSSimon Glass 	addr = devfdt_get_addr(dev);
499fdbae099SBin Meng 	if (addr == FDT_ADDR_T_NONE)
500fdbae099SBin Meng 		return -EINVAL;
501fdbae099SBin Meng 
502c40d612bSPeng Fan 	plat->reg = (void *)addr;
503c40d612bSPeng Fan 	plat->flags = dev_get_driver_data(dev);
504fdbae099SBin Meng 
5057edf5c45SPeng Fan 	if (!fdt_node_check_compatible(blob, node, "fsl,ls1021a-lpuart"))
5067edf5c45SPeng Fan 		plat->devtype = DEV_LS1021A;
5077edf5c45SPeng Fan 	else if (!fdt_node_check_compatible(blob, node, "fsl,imx7ulp-lpuart"))
5087edf5c45SPeng Fan 		plat->devtype = DEV_MX7ULP;
5097edf5c45SPeng Fan 	else if (!fdt_node_check_compatible(blob, node, "fsl,vf610-lpuart"))
5107edf5c45SPeng Fan 		plat->devtype = DEV_VF610;
511126f8849SPeng Fan 	else if (!fdt_node_check_compatible(blob, node, "fsl,imx8qm-lpuart"))
512126f8849SPeng Fan 		plat->devtype = DEV_IMX8;
5137edf5c45SPeng Fan 
514fdbae099SBin Meng 	return 0;
515fdbae099SBin Meng }
516fdbae099SBin Meng 
517fdbae099SBin Meng static const struct dm_serial_ops lpuart_serial_ops = {
518fdbae099SBin Meng 	.putc = lpuart_serial_putc,
519fdbae099SBin Meng 	.pending = lpuart_serial_pending,
520fdbae099SBin Meng 	.getc = lpuart_serial_getc,
521fdbae099SBin Meng 	.setbrg = lpuart_serial_setbrg,
522fdbae099SBin Meng };
523fdbae099SBin Meng 
524fdbae099SBin Meng static const struct udevice_id lpuart_serial_ids[] = {
525c40d612bSPeng Fan 	{ .compatible = "fsl,ls1021a-lpuart", .data =
526c40d612bSPeng Fan 		LPUART_FLAG_REGMAP_32BIT_REG | LPUART_FLAG_REGMAP_ENDIAN_BIG },
5277edf5c45SPeng Fan 	{ .compatible = "fsl,imx7ulp-lpuart",
5287edf5c45SPeng Fan 		.data = LPUART_FLAG_REGMAP_32BIT_REG },
529fdbae099SBin Meng 	{ .compatible = "fsl,vf610-lpuart"},
530126f8849SPeng Fan 	{ .compatible = "fsl,imx8qm-lpuart",
531126f8849SPeng Fan 		.data = LPUART_FLAG_REGMAP_32BIT_REG },
532fdbae099SBin Meng 	{ }
533fdbae099SBin Meng };
534fdbae099SBin Meng 
535fdbae099SBin Meng U_BOOT_DRIVER(serial_lpuart) = {
536fdbae099SBin Meng 	.name	= "serial_lpuart",
537fdbae099SBin Meng 	.id	= UCLASS_SERIAL,
538fdbae099SBin Meng 	.of_match = lpuart_serial_ids,
539fdbae099SBin Meng 	.ofdata_to_platdata = lpuart_serial_ofdata_to_platdata,
540fdbae099SBin Meng 	.platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata),
541fdbae099SBin Meng 	.probe = lpuart_serial_probe,
542fdbae099SBin Meng 	.ops	= &lpuart_serial_ops,
543fdbae099SBin Meng };
544