]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
142a20c3 MK |
2 | /* |
3 | * Qualcomm UART driver | |
4 | * | |
5 | * (C) Copyright 2015 Mateusz Kulikowski <[email protected]> | |
6 | * | |
7 | * UART will work in Data Mover mode. | |
8 | * Based on Linux driver. | |
142a20c3 MK |
9 | */ |
10 | ||
11 | #include <common.h> | |
12 | #include <clk.h> | |
13 | #include <dm.h> | |
14 | #include <errno.h> | |
15 | #include <serial.h> | |
16 | #include <watchdog.h> | |
17 | #include <asm/io.h> | |
18 | #include <linux/compiler.h> | |
19 | ||
20 | /* Serial registers - this driver works in uartdm mode*/ | |
21 | ||
22 | #define UARTDM_DMRX 0x34 /* Max RX transfer length */ | |
23 | #define UARTDM_NCF_TX 0x40 /* Number of chars to TX */ | |
24 | ||
25 | #define UARTDM_RXFS 0x50 /* RX channel status register */ | |
26 | #define UARTDM_RXFS_BUF_SHIFT 0x7 /* Number of bytes in the packing buffer */ | |
27 | #define UARTDM_RXFS_BUF_MASK 0x7 | |
28 | ||
29 | #define UARTDM_SR 0xA4 /* Status register */ | |
30 | #define UARTDM_SR_RX_READY (1 << 0) /* Word is the receiver FIFO */ | |
31 | #define UARTDM_SR_TX_EMPTY (1 << 3) /* Transmitter underrun */ | |
32 | #define UARTDM_SR_UART_OVERRUN (1 << 4) /* Receive overrun */ | |
33 | ||
34 | #define UARTDM_CR 0xA8 /* Command register */ | |
35 | #define UARTDM_CR_CMD_RESET_ERR (3 << 4) /* Clear overrun error */ | |
36 | #define UARTDM_CR_CMD_RESET_STALE_INT (8 << 4) /* Clears stale irq */ | |
37 | #define UARTDM_CR_CMD_RESET_TX_READY (3 << 8) /* Clears TX Ready irq*/ | |
38 | #define UARTDM_CR_CMD_FORCE_STALE (4 << 8) /* Causes stale event */ | |
39 | #define UARTDM_CR_CMD_STALE_EVENT_DISABLE (6 << 8) /* Disable stale event */ | |
40 | ||
41 | #define UARTDM_IMR 0xB0 /* Interrupt mask register */ | |
42 | #define UARTDM_ISR 0xB4 /* Interrupt status register */ | |
43 | #define UARTDM_ISR_TX_READY 0x80 /* TX FIFO empty */ | |
44 | ||
45 | #define UARTDM_TF 0x100 /* UART Transmit FIFO register */ | |
46 | #define UARTDM_RF 0x140 /* UART Receive FIFO register */ | |
47 | ||
48 | ||
49 | DECLARE_GLOBAL_DATA_PTR; | |
50 | ||
51 | struct msm_serial_data { | |
52 | phys_addr_t base; | |
53 | unsigned chars_cnt; /* number of buffered chars */ | |
54 | uint32_t chars_buf; /* buffered chars */ | |
55 | }; | |
56 | ||
57 | static int msm_serial_fetch(struct udevice *dev) | |
58 | { | |
59 | struct msm_serial_data *priv = dev_get_priv(dev); | |
60 | unsigned sr; | |
61 | ||
62 | if (priv->chars_cnt) | |
63 | return priv->chars_cnt; | |
64 | ||
65 | /* Clear error in case of buffer overrun */ | |
66 | if (readl(priv->base + UARTDM_SR) & UARTDM_SR_UART_OVERRUN) | |
67 | writel(UARTDM_CR_CMD_RESET_ERR, priv->base + UARTDM_CR); | |
68 | ||
69 | /* We need to fetch new character */ | |
70 | sr = readl(priv->base + UARTDM_SR); | |
71 | ||
72 | if (sr & UARTDM_SR_RX_READY) { | |
73 | /* There are at least 4 bytes in fifo */ | |
74 | priv->chars_buf = readl(priv->base + UARTDM_RF); | |
75 | priv->chars_cnt = 4; | |
76 | } else { | |
77 | /* Check if there is anything in fifo */ | |
78 | priv->chars_cnt = readl(priv->base + UARTDM_RXFS); | |
79 | /* Extract number of characters in UART packing buffer*/ | |
80 | priv->chars_cnt = (priv->chars_cnt >> | |
81 | UARTDM_RXFS_BUF_SHIFT) & | |
82 | UARTDM_RXFS_BUF_MASK; | |
83 | if (!priv->chars_cnt) | |
84 | return 0; | |
85 | ||
86 | /* There is at least one charcter, move it to fifo */ | |
87 | writel(UARTDM_CR_CMD_FORCE_STALE, | |
88 | priv->base + UARTDM_CR); | |
89 | ||
90 | priv->chars_buf = readl(priv->base + UARTDM_RF); | |
91 | writel(UARTDM_CR_CMD_RESET_STALE_INT, | |
92 | priv->base + UARTDM_CR); | |
93 | writel(0x7, priv->base + UARTDM_DMRX); | |
94 | } | |
95 | ||
96 | return priv->chars_cnt; | |
97 | } | |
98 | ||
99 | static int msm_serial_getc(struct udevice *dev) | |
100 | { | |
101 | struct msm_serial_data *priv = dev_get_priv(dev); | |
102 | char c; | |
103 | ||
104 | if (!msm_serial_fetch(dev)) | |
105 | return -EAGAIN; | |
106 | ||
107 | c = priv->chars_buf & 0xFF; | |
108 | priv->chars_buf >>= 8; | |
109 | priv->chars_cnt--; | |
110 | ||
111 | return c; | |
112 | } | |
113 | ||
114 | static int msm_serial_putc(struct udevice *dev, const char ch) | |
115 | { | |
116 | struct msm_serial_data *priv = dev_get_priv(dev); | |
117 | ||
118 | if (!(readl(priv->base + UARTDM_SR) & UARTDM_SR_TX_EMPTY) && | |
119 | !(readl(priv->base + UARTDM_ISR) & UARTDM_ISR_TX_READY)) | |
120 | return -EAGAIN; | |
121 | ||
122 | writel(UARTDM_CR_CMD_RESET_TX_READY, priv->base + UARTDM_CR); | |
123 | ||
124 | writel(1, priv->base + UARTDM_NCF_TX); | |
125 | writel(ch, priv->base + UARTDM_TF); | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
130 | static int msm_serial_pending(struct udevice *dev, bool input) | |
131 | { | |
132 | if (input) { | |
133 | if (msm_serial_fetch(dev)) | |
134 | return 1; | |
135 | } | |
136 | ||
137 | return 0; | |
138 | } | |
139 | ||
140 | static const struct dm_serial_ops msm_serial_ops = { | |
141 | .putc = msm_serial_putc, | |
142 | .pending = msm_serial_pending, | |
143 | .getc = msm_serial_getc, | |
144 | }; | |
145 | ||
146 | static int msm_uart_clk_init(struct udevice *dev) | |
147 | { | |
e160f7d4 | 148 | uint clk_rate = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), |
142a20c3 MK |
149 | "clock-frequency", 115200); |
150 | uint clkd[2]; /* clk_id and clk_no */ | |
151 | int clk_offset; | |
135aa950 SW |
152 | struct udevice *clk_dev; |
153 | struct clk clk; | |
142a20c3 MK |
154 | int ret; |
155 | ||
e160f7d4 SG |
156 | ret = fdtdec_get_int_array(gd->fdt_blob, dev_of_offset(dev), "clock", |
157 | clkd, 2); | |
142a20c3 MK |
158 | if (ret) |
159 | return ret; | |
160 | ||
161 | clk_offset = fdt_node_offset_by_phandle(gd->fdt_blob, clkd[0]); | |
162 | if (clk_offset < 0) | |
163 | return clk_offset; | |
164 | ||
135aa950 | 165 | ret = uclass_get_device_by_of_offset(UCLASS_CLK, clk_offset, &clk_dev); |
142a20c3 MK |
166 | if (ret) |
167 | return ret; | |
168 | ||
135aa950 SW |
169 | clk.id = clkd[1]; |
170 | ret = clk_request(clk_dev, &clk); | |
171 | if (ret < 0) | |
172 | return ret; | |
173 | ||
174 | ret = clk_set_rate(&clk, clk_rate); | |
175 | clk_free(&clk); | |
142a20c3 MK |
176 | if (ret < 0) |
177 | return ret; | |
178 | ||
179 | return 0; | |
180 | } | |
181 | ||
182 | static int msm_serial_probe(struct udevice *dev) | |
183 | { | |
11d59fe5 | 184 | int ret; |
142a20c3 MK |
185 | struct msm_serial_data *priv = dev_get_priv(dev); |
186 | ||
7e5ad796 RF |
187 | /* No need to reinitialize the UART after relocation */ |
188 | if (gd->flags & GD_FLG_RELOC) | |
189 | return 0; | |
190 | ||
11d59fe5 RF |
191 | ret = msm_uart_clk_init(dev); |
192 | if (ret) | |
193 | return ret; | |
142a20c3 MK |
194 | |
195 | if (readl(priv->base + UARTDM_SR) & UARTDM_SR_UART_OVERRUN) | |
196 | writel(UARTDM_CR_CMD_RESET_ERR, priv->base + UARTDM_CR); | |
197 | ||
198 | writel(0, priv->base + UARTDM_IMR); | |
199 | writel(UARTDM_CR_CMD_STALE_EVENT_DISABLE, priv->base + UARTDM_CR); | |
200 | msm_serial_fetch(dev); | |
201 | ||
202 | return 0; | |
203 | } | |
204 | ||
205 | static int msm_serial_ofdata_to_platdata(struct udevice *dev) | |
206 | { | |
207 | struct msm_serial_data *priv = dev_get_priv(dev); | |
208 | ||
a821c4af | 209 | priv->base = devfdt_get_addr(dev); |
142a20c3 MK |
210 | if (priv->base == FDT_ADDR_T_NONE) |
211 | return -EINVAL; | |
212 | ||
213 | return 0; | |
214 | } | |
215 | ||
216 | static const struct udevice_id msm_serial_ids[] = { | |
217 | { .compatible = "qcom,msm-uartdm-v1.4" }, | |
218 | { } | |
219 | }; | |
220 | ||
221 | U_BOOT_DRIVER(serial_msm) = { | |
222 | .name = "serial_msm", | |
223 | .id = UCLASS_SERIAL, | |
224 | .of_match = msm_serial_ids, | |
225 | .ofdata_to_platdata = msm_serial_ofdata_to_platdata, | |
226 | .priv_auto_alloc_size = sizeof(struct msm_serial_data), | |
227 | .probe = msm_serial_probe, | |
228 | .ops = &msm_serial_ops, | |
229 | }; |