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, ®32->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(®->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