1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright 2017-2019 NXP
4 */
5
6 #include <assert.h>
7 #include <drivers/imx_uart.h>
8 #include <io.h>
9 #include <keep.h>
10 #include <kernel/dt.h>
11 #include <kernel/dt_driver.h>
12 #include <util.h>
13
14 #define STAT 0x14
15 #define DATA 0x1C
16 #define UART_SIZE 0x20
17 #define STAT_TDRE BIT(23)
18 #define STAT_RDRF BIT(21)
19 #define STAT_OR BIT(19)
20
chip_to_base(struct serial_chip * chip)21 static vaddr_t chip_to_base(struct serial_chip *chip)
22 {
23 struct imx_uart_data *pd =
24 container_of(chip, struct imx_uart_data, chip);
25
26 return io_pa_or_va(&pd->base, UART_SIZE);
27 }
28
imx_lpuart_getchar(struct serial_chip * chip)29 static int imx_lpuart_getchar(struct serial_chip *chip)
30 {
31 int ch = 0;
32 vaddr_t base = chip_to_base(chip);
33
34 while (io_read32(base + STAT) & STAT_RDRF)
35 ;
36
37 ch = io_read32(base + DATA) & 0x3ff;
38
39 if (io_read32(base + STAT) & STAT_OR)
40 io_write32(base + STAT, STAT_OR);
41
42 return ch;
43 }
44
imx_lpuart_putc(struct serial_chip * chip,int ch)45 static void imx_lpuart_putc(struct serial_chip *chip, int ch)
46 {
47 vaddr_t base = chip_to_base(chip);
48
49 while (!(io_read32(base + STAT) & STAT_TDRE))
50 ;
51
52 io_write32(base + DATA, ch);
53 }
54
55 static const struct serial_ops imx_lpuart_ops = {
56 .getchar = imx_lpuart_getchar,
57 .putc = imx_lpuart_putc,
58 };
59 DECLARE_KEEP_PAGER(imx_lpuart_ops);
60
imx_uart_init(struct imx_uart_data * pd,paddr_t base)61 void imx_uart_init(struct imx_uart_data *pd, paddr_t base)
62 {
63 pd->base.pa = base;
64 pd->chip.ops = &imx_lpuart_ops;
65
66 /*
67 * Do nothing, debug uart(sc lpuart) shared with normal world,
68 * everything for uart initialization is done in bootloader.
69 */
70 }
71
72 #ifdef CFG_DT
imx_lpuart_dev_alloc(void)73 static struct serial_chip *imx_lpuart_dev_alloc(void)
74 {
75 struct imx_uart_data *pd = calloc(1, sizeof(*pd));
76
77 if (!pd)
78 return NULL;
79
80 return &pd->chip;
81 }
82
imx_lpuart_dev_init(struct serial_chip * chip,const void * fdt,int offs,const char * parms)83 static int imx_lpuart_dev_init(struct serial_chip *chip, const void *fdt,
84 int offs, const char *parms)
85 {
86 struct imx_uart_data *pd =
87 container_of(chip, struct imx_uart_data, chip);
88 vaddr_t vbase = 0;
89 paddr_t pbase = 0;
90 size_t size = 0;
91
92 if (parms && parms[0])
93 IMSG("imx_lpuart: device parameters ignored (%s)", parms);
94
95 if (dt_map_dev(fdt, offs, &vbase, &size, DT_MAP_AUTO) < 0)
96 return -1;
97
98 pbase = virt_to_phys((void *)vbase);
99 imx_uart_init(pd, pbase);
100
101 return 0;
102 }
103
imx_lpuart_dev_free(struct serial_chip * chip)104 static void imx_lpuart_dev_free(struct serial_chip *chip)
105 {
106 struct imx_uart_data *pd =
107 container_of(chip, struct imx_uart_data, chip);
108
109 free(pd);
110 }
111
112 static const struct serial_driver imx_lpuart_driver = {
113 .dev_alloc = imx_lpuart_dev_alloc,
114 .dev_init = imx_lpuart_dev_init,
115 .dev_free = imx_lpuart_dev_free,
116 };
117
118 static const struct dt_device_match imx_match_table[] = {
119 { .compatible = "fsl,imx7ulp-lpuart" },
120 { .compatible = "fsl,imx8qm-lpuart" },
121 { 0 }
122 };
123
124 DEFINE_DT_DRIVER(imx_dt_driver) = {
125 .name = "imx_lpuart",
126 .type = DT_DRIVER_UART,
127 .match_table = imx_match_table,
128 .driver = &imx_lpuart_driver,
129 };
130
131 #endif /* CFG_DT */
132