1427eba70SAlison Wang /*
2427eba70SAlison Wang  * Copyright 2013 Freescale Semiconductor, Inc.
3427eba70SAlison Wang  *
41a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
5427eba70SAlison Wang  */
6427eba70SAlison Wang 
7427eba70SAlison Wang #include <common.h>
8fdbae099SBin Meng #include <dm.h>
9*c40d612bSPeng 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 
446209e14cSJingchang Lu #define FIFO_TXFE		0x80
456209e14cSJingchang Lu #define FIFO_RXFE		0x40
466209e14cSJingchang Lu 
476209e14cSJingchang Lu #define WATER_TXWATER_OFF	1
486209e14cSJingchang Lu #define WATER_RXWATER_OFF	16
496209e14cSJingchang Lu 
50427eba70SAlison Wang DECLARE_GLOBAL_DATA_PTR;
51427eba70SAlison Wang 
52*c40d612bSPeng Fan #define LPUART_FLAG_REGMAP_32BIT_REG	BIT(0)
53*c40d612bSPeng Fan #define LPUART_FLAG_REGMAP_ENDIAN_BIG	BIT(1)
54*c40d612bSPeng Fan 
55fdbae099SBin Meng struct lpuart_serial_platdata {
56*c40d612bSPeng Fan 	void *reg;
57*c40d612bSPeng Fan 	ulong flags;
58fdbae099SBin Meng };
59fdbae099SBin Meng 
60*c40d612bSPeng Fan static void lpuart_read32(u32 flags, u32 *addr, u32 *val)
61427eba70SAlison Wang {
62*c40d612bSPeng Fan 	if (flags & LPUART_FLAG_REGMAP_32BIT_REG) {
63*c40d612bSPeng Fan 		if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG)
64*c40d612bSPeng Fan 			*(u32 *)val = in_be32(addr);
65*c40d612bSPeng Fan 		else
66*c40d612bSPeng Fan 			*(u32 *)val = in_le32(addr);
67*c40d612bSPeng Fan 	}
68*c40d612bSPeng Fan }
69*c40d612bSPeng Fan 
70*c40d612bSPeng Fan static void lpuart_write32(u32 flags, u32 *addr, u32 val)
71*c40d612bSPeng Fan {
72*c40d612bSPeng Fan 	if (flags & LPUART_FLAG_REGMAP_32BIT_REG) {
73*c40d612bSPeng Fan 		if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG)
74*c40d612bSPeng Fan 			out_be32(addr, val);
75*c40d612bSPeng Fan 		else
76*c40d612bSPeng Fan 			out_le32(addr, val);
77*c40d612bSPeng Fan 	}
78*c40d612bSPeng Fan }
79*c40d612bSPeng Fan 
80*c40d612bSPeng Fan 
81*c40d612bSPeng Fan #ifndef CONFIG_SYS_CLK_FREQ
82*c40d612bSPeng Fan #define CONFIG_SYS_CLK_FREQ	0
83*c40d612bSPeng Fan #endif
84*c40d612bSPeng Fan 
85*c40d612bSPeng Fan u32 __weak get_lpuart_clk(void)
86*c40d612bSPeng Fan {
87*c40d612bSPeng Fan 	return CONFIG_SYS_CLK_FREQ;
88*c40d612bSPeng Fan }
89*c40d612bSPeng Fan 
90*c40d612bSPeng Fan static bool is_lpuart32(struct udevice *dev)
91*c40d612bSPeng Fan {
92*c40d612bSPeng Fan 	struct lpuart_serial_platdata *plat = dev->platdata;
93*c40d612bSPeng Fan 
94*c40d612bSPeng Fan 	return plat->flags & LPUART_FLAG_REGMAP_32BIT_REG;
95*c40d612bSPeng Fan }
96*c40d612bSPeng Fan 
97*c40d612bSPeng Fan static void _lpuart_serial_setbrg(struct lpuart_serial_platdata *plat,
98*c40d612bSPeng Fan 				  int baudrate)
99*c40d612bSPeng Fan {
100*c40d612bSPeng Fan 	struct lpuart_fsl *base = plat->reg;
101*c40d612bSPeng Fan 	u32 clk = get_lpuart_clk();
102427eba70SAlison Wang 	u16 sbr;
103427eba70SAlison Wang 
1046ca13b12SBin Meng 	sbr = (u16)(clk / (16 * baudrate));
105427eba70SAlison Wang 
10647f1bfcaSBin Meng 	/* place adjustment later - n/32 BRFA */
107427eba70SAlison Wang 	__raw_writeb(sbr >> 8, &base->ubdh);
108427eba70SAlison Wang 	__raw_writeb(sbr & 0xff, &base->ubdl);
109427eba70SAlison Wang }
110427eba70SAlison Wang 
111*c40d612bSPeng Fan static int _lpuart_serial_getc(struct lpuart_serial_platdata *plat)
112427eba70SAlison Wang {
113*c40d612bSPeng Fan 	struct lpuart_fsl *base = plat->reg;
114a3db78d8SStefan Agner 	while (!(__raw_readb(&base->us1) & (US1_RDRF | US1_OR)))
115427eba70SAlison Wang 		WATCHDOG_RESET();
116427eba70SAlison Wang 
117a3db78d8SStefan Agner 	barrier();
118427eba70SAlison Wang 
119427eba70SAlison Wang 	return __raw_readb(&base->ud);
120427eba70SAlison Wang }
121427eba70SAlison Wang 
122*c40d612bSPeng Fan static void _lpuart_serial_putc(struct lpuart_serial_platdata *plat,
123*c40d612bSPeng Fan 				const char c)
124427eba70SAlison Wang {
125*c40d612bSPeng Fan 	struct lpuart_fsl *base = plat->reg;
126*c40d612bSPeng Fan 
127427eba70SAlison Wang 	while (!(__raw_readb(&base->us1) & US1_TDRE))
128427eba70SAlison Wang 		WATCHDOG_RESET();
129427eba70SAlison Wang 
130427eba70SAlison Wang 	__raw_writeb(c, &base->ud);
131427eba70SAlison Wang }
132427eba70SAlison Wang 
13347f1bfcaSBin Meng /* Test whether a character is in the RX buffer */
134*c40d612bSPeng Fan static int _lpuart_serial_tstc(struct lpuart_serial_platdata *plat)
135427eba70SAlison Wang {
136*c40d612bSPeng Fan 	struct lpuart_fsl *base = plat->reg;
137*c40d612bSPeng Fan 
138427eba70SAlison Wang 	if (__raw_readb(&base->urcfifo) == 0)
139427eba70SAlison Wang 		return 0;
140427eba70SAlison Wang 
141427eba70SAlison Wang 	return 1;
142427eba70SAlison Wang }
143427eba70SAlison Wang 
144427eba70SAlison Wang /*
145427eba70SAlison Wang  * Initialise the serial port with the given baudrate. The settings
146427eba70SAlison Wang  * are always 8 data bits, no parity, 1 stop bit, no start bits.
147427eba70SAlison Wang  */
148*c40d612bSPeng Fan static int _lpuart_serial_init(struct lpuart_serial_platdata *plat)
149427eba70SAlison Wang {
150*c40d612bSPeng Fan 	struct lpuart_fsl *base = (struct lpuart_fsl *)plat->reg;
151427eba70SAlison Wang 	u8 ctrl;
152427eba70SAlison Wang 
153427eba70SAlison Wang 	ctrl = __raw_readb(&base->uc2);
154427eba70SAlison Wang 	ctrl &= ~UC2_RE;
155427eba70SAlison Wang 	ctrl &= ~UC2_TE;
156427eba70SAlison Wang 	__raw_writeb(ctrl, &base->uc2);
157427eba70SAlison Wang 
158427eba70SAlison Wang 	__raw_writeb(0, &base->umodem);
159427eba70SAlison Wang 	__raw_writeb(0, &base->uc1);
160427eba70SAlison Wang 
16189e69fd4SStefan Agner 	/* Disable FIFO and flush buffer */
16289e69fd4SStefan Agner 	__raw_writeb(0x0, &base->upfifo);
16389e69fd4SStefan Agner 	__raw_writeb(0x0, &base->utwfifo);
16489e69fd4SStefan Agner 	__raw_writeb(0x1, &base->urwfifo);
16589e69fd4SStefan Agner 	__raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo);
16689e69fd4SStefan Agner 
167427eba70SAlison Wang 	/* provide data bits, parity, stop bit, etc */
168*c40d612bSPeng Fan 	_lpuart_serial_setbrg(plat, gd->baudrate);
169427eba70SAlison Wang 
170427eba70SAlison Wang 	__raw_writeb(UC2_RE | UC2_TE, &base->uc2);
171427eba70SAlison Wang 
172427eba70SAlison Wang 	return 0;
173427eba70SAlison Wang }
174427eba70SAlison Wang 
175*c40d612bSPeng Fan static void _lpuart32_serial_setbrg(struct lpuart_serial_platdata *plat,
176*c40d612bSPeng Fan 				    int baudrate)
177fdbae099SBin Meng {
178*c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
1799b46213bSShaohui Xie 	u32 clk = get_lpuart_clk();
1806209e14cSJingchang Lu 	u32 sbr;
1816209e14cSJingchang Lu 
1826ca13b12SBin Meng 	sbr = (clk / (16 * baudrate));
1836209e14cSJingchang Lu 
18447f1bfcaSBin Meng 	/* place adjustment later - n/32 BRFA */
185*c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->baud, sbr);
1866209e14cSJingchang Lu }
1876209e14cSJingchang Lu 
188*c40d612bSPeng Fan static int _lpuart32_serial_getc(struct lpuart_serial_platdata *plat)
1896209e14cSJingchang Lu {
190*c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
1916209e14cSJingchang Lu 	u32 stat;
1926209e14cSJingchang Lu 
193*c40d612bSPeng Fan 	lpuart_read32(plat->flags, &base->stat, &stat);
194*c40d612bSPeng Fan 	while ((stat & STAT_RDRF) == 0) {
195*c40d612bSPeng Fan 		lpuart_write32(plat->flags, &base->stat, STAT_FLAGS);
1966209e14cSJingchang Lu 		WATCHDOG_RESET();
197*c40d612bSPeng Fan 		lpuart_read32(plat->flags, &base->stat, &stat);
1986209e14cSJingchang Lu 	}
1996209e14cSJingchang Lu 
200*c40d612bSPeng Fan 	/* Reuse stat */
201*c40d612bSPeng Fan 	lpuart_read32(plat->flags, &base->data, &stat);
202*c40d612bSPeng Fan 
203*c40d612bSPeng Fan 	return stat & 0x3ff;
2046209e14cSJingchang Lu }
2056209e14cSJingchang Lu 
206*c40d612bSPeng Fan static void _lpuart32_serial_putc(struct lpuart_serial_platdata *plat,
207*c40d612bSPeng Fan 				  const char c)
2086209e14cSJingchang Lu {
209*c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
210*c40d612bSPeng Fan 	u32 stat;
2116209e14cSJingchang Lu 
212*c40d612bSPeng Fan 	while (true) {
213*c40d612bSPeng Fan 		lpuart_read32(plat->flags, &base->stat, &stat);
214*c40d612bSPeng Fan 
215*c40d612bSPeng Fan 		if ((stat & STAT_TDRE))
216*c40d612bSPeng Fan 			break;
217*c40d612bSPeng Fan 
218*c40d612bSPeng Fan 		WATCHDOG_RESET();
219*c40d612bSPeng Fan 	}
220*c40d612bSPeng Fan 
221*c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->data, c);
2226209e14cSJingchang Lu }
2236209e14cSJingchang Lu 
22447f1bfcaSBin Meng /* Test whether a character is in the RX buffer */
225*c40d612bSPeng Fan static int _lpuart32_serial_tstc(struct lpuart_serial_platdata *plat)
2266209e14cSJingchang Lu {
227*c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = plat->reg;
228*c40d612bSPeng Fan 	u32 water;
229*c40d612bSPeng Fan 
230*c40d612bSPeng Fan 	lpuart_read32(plat->flags, &base->water, &water);
231*c40d612bSPeng Fan 
232*c40d612bSPeng Fan 	if ((water >> 24) == 0)
2336209e14cSJingchang Lu 		return 0;
2346209e14cSJingchang Lu 
2356209e14cSJingchang Lu 	return 1;
2366209e14cSJingchang Lu }
2376209e14cSJingchang Lu 
2386209e14cSJingchang Lu /*
2396209e14cSJingchang Lu  * Initialise the serial port with the given baudrate. The settings
2406209e14cSJingchang Lu  * are always 8 data bits, no parity, 1 stop bit, no start bits.
2416209e14cSJingchang Lu  */
242*c40d612bSPeng Fan static int _lpuart32_serial_init(struct lpuart_serial_platdata *plat)
2436209e14cSJingchang Lu {
244*c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *base = (struct lpuart_fsl_reg32 *)plat->reg;
245*c40d612bSPeng Fan 	u32 ctrl;
2466209e14cSJingchang Lu 
247*c40d612bSPeng Fan 	lpuart_read32(plat->flags, &base->ctrl, &ctrl);
2486209e14cSJingchang Lu 	ctrl &= ~CTRL_RE;
2496209e14cSJingchang Lu 	ctrl &= ~CTRL_TE;
250*c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->ctrl, ctrl);
2516209e14cSJingchang Lu 
252*c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->modir, 0);
253*c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->fifo, ~(FIFO_TXFE | FIFO_RXFE));
2546209e14cSJingchang Lu 
255*c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->match, 0);
2566209e14cSJingchang Lu 
25747f1bfcaSBin Meng 	/* provide data bits, parity, stop bit, etc */
258*c40d612bSPeng Fan 	_lpuart32_serial_setbrg(plat, gd->baudrate);
2596209e14cSJingchang Lu 
260*c40d612bSPeng Fan 	lpuart_write32(plat->flags, &base->ctrl, CTRL_RE | CTRL_TE);
2616209e14cSJingchang Lu 
2626209e14cSJingchang Lu 	return 0;
2636209e14cSJingchang Lu }
2646209e14cSJingchang Lu 
265*c40d612bSPeng Fan static int lpuart_serial_setbrg(struct udevice *dev, int baudrate)
266fdbae099SBin Meng {
267fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
268fdbae099SBin Meng 
269*c40d612bSPeng Fan 	if (is_lpuart32(dev))
270*c40d612bSPeng Fan 		_lpuart32_serial_setbrg(plat, baudrate);
271*c40d612bSPeng Fan 	else
272*c40d612bSPeng Fan 		_lpuart_serial_setbrg(plat, baudrate);
273fdbae099SBin Meng 
274fdbae099SBin Meng 	return 0;
275fdbae099SBin Meng }
276fdbae099SBin Meng 
277*c40d612bSPeng Fan static int lpuart_serial_getc(struct udevice *dev)
278fdbae099SBin Meng {
279fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
280fdbae099SBin Meng 
281*c40d612bSPeng Fan 	if (is_lpuart32(dev))
282*c40d612bSPeng Fan 		return _lpuart32_serial_getc(plat);
283*c40d612bSPeng Fan 
284*c40d612bSPeng Fan 	return _lpuart_serial_getc(plat);
285fdbae099SBin Meng }
286fdbae099SBin Meng 
287*c40d612bSPeng Fan static int lpuart_serial_putc(struct udevice *dev, const char c)
288fdbae099SBin Meng {
289fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
290fdbae099SBin Meng 
291*c40d612bSPeng Fan 	if (is_lpuart32(dev))
292*c40d612bSPeng Fan 		_lpuart32_serial_putc(plat, c);
293*c40d612bSPeng Fan 	else
294*c40d612bSPeng Fan 		_lpuart_serial_putc(plat, c);
295fdbae099SBin Meng 
296fdbae099SBin Meng 	return 0;
297fdbae099SBin Meng }
298fdbae099SBin Meng 
299*c40d612bSPeng Fan static int lpuart_serial_pending(struct udevice *dev, bool input)
300fdbae099SBin Meng {
301fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
302fdbae099SBin Meng 	struct lpuart_fsl *reg = plat->reg;
303*c40d612bSPeng Fan 	struct lpuart_fsl_reg32 *reg32 = plat->reg;
304*c40d612bSPeng Fan 	u32 stat;
305*c40d612bSPeng Fan 
306*c40d612bSPeng Fan 	if (is_lpuart32(dev)) {
307*c40d612bSPeng Fan 		if (input) {
308*c40d612bSPeng Fan 			return _lpuart32_serial_tstc(plat);
309*c40d612bSPeng Fan 		} else {
310*c40d612bSPeng Fan 			lpuart_read32(plat->flags, &reg32->stat, &stat);
311*c40d612bSPeng Fan 			return stat & STAT_TDRE ? 0 : 1;
312*c40d612bSPeng Fan 		}
313*c40d612bSPeng Fan 	}
314fdbae099SBin Meng 
315fdbae099SBin Meng 	if (input)
316*c40d612bSPeng Fan 		return _lpuart_serial_tstc(plat);
317fdbae099SBin Meng 	else
318*c40d612bSPeng Fan 		return __raw_readb(&reg->us1) & US1_TDRE ? 0 : 1;
319fdbae099SBin Meng }
320fdbae099SBin Meng 
321*c40d612bSPeng Fan static int lpuart_serial_probe(struct udevice *dev)
322fdbae099SBin Meng {
323fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
324fdbae099SBin Meng 
325*c40d612bSPeng Fan 	if (is_lpuart32(dev))
326*c40d612bSPeng Fan 		return _lpuart32_serial_init(plat);
327*c40d612bSPeng Fan 	else
328*c40d612bSPeng Fan 		return _lpuart_serial_init(plat);
329fdbae099SBin Meng }
330427eba70SAlison Wang 
331fdbae099SBin Meng static int lpuart_serial_ofdata_to_platdata(struct udevice *dev)
332fdbae099SBin Meng {
333fdbae099SBin Meng 	struct lpuart_serial_platdata *plat = dev->platdata;
334fdbae099SBin Meng 	fdt_addr_t addr;
335fdbae099SBin Meng 
336fdbae099SBin Meng 	addr = dev_get_addr(dev);
337fdbae099SBin Meng 	if (addr == FDT_ADDR_T_NONE)
338fdbae099SBin Meng 		return -EINVAL;
339fdbae099SBin Meng 
340*c40d612bSPeng Fan 	plat->reg = (void *)addr;
341*c40d612bSPeng Fan 	plat->flags = dev_get_driver_data(dev);
342fdbae099SBin Meng 
343fdbae099SBin Meng 	return 0;
344fdbae099SBin Meng }
345fdbae099SBin Meng 
346fdbae099SBin Meng static const struct dm_serial_ops lpuart_serial_ops = {
347fdbae099SBin Meng 	.putc = lpuart_serial_putc,
348fdbae099SBin Meng 	.pending = lpuart_serial_pending,
349fdbae099SBin Meng 	.getc = lpuart_serial_getc,
350fdbae099SBin Meng 	.setbrg = lpuart_serial_setbrg,
351fdbae099SBin Meng };
352fdbae099SBin Meng 
353fdbae099SBin Meng static const struct udevice_id lpuart_serial_ids[] = {
354*c40d612bSPeng Fan 	{ .compatible = "fsl,ls1021a-lpuart", .data =
355*c40d612bSPeng Fan 		LPUART_FLAG_REGMAP_32BIT_REG | LPUART_FLAG_REGMAP_ENDIAN_BIG },
356fdbae099SBin Meng 	{ .compatible = "fsl,vf610-lpuart"},
357fdbae099SBin Meng 	{ }
358fdbae099SBin Meng };
359fdbae099SBin Meng 
360fdbae099SBin Meng U_BOOT_DRIVER(serial_lpuart) = {
361fdbae099SBin Meng 	.name	= "serial_lpuart",
362fdbae099SBin Meng 	.id	= UCLASS_SERIAL,
363fdbae099SBin Meng 	.of_match = lpuart_serial_ids,
364fdbae099SBin Meng 	.ofdata_to_platdata = lpuart_serial_ofdata_to_platdata,
365fdbae099SBin Meng 	.platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata),
366fdbae099SBin Meng 	.probe = lpuart_serial_probe,
367fdbae099SBin Meng 	.ops	= &lpuart_serial_ops,
368fdbae099SBin Meng 	.flags = DM_FLAG_PRE_RELOC,
369fdbae099SBin Meng };
370