1 /*
2 * Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "hisoc/uart.h"
17 #include "los_magickey.h"
18 #include "los_task.h"
19 #include "hdf_log.h"
20 #include "osal_io.h"
21 #include "osal_irq.h"
22 #include "osal_time.h"
23 #include "uart_pl011.h"
24
25 #define HDF_LOG_TAG uart_pl011
26 #define FIFO_SIZE 128
27 #define UART_WAIT_MS 10
28 #define IBRD_COEFFICIENTS 16
29 #define FBRD_COEFFICIENTS 8
Pl011Irq(uint32_t irq, void *data)30 static uint32_t Pl011Irq(uint32_t irq, void *data)
31 {
32 uint32_t status;
33 uint32_t fr;
34 char buf[FIFO_SIZE];
35 uint32_t count = 0;
36 struct UartPl011Port *port = NULL;
37 struct UartDriverData *udd = (struct UartDriverData *)data;
38
39 UNUSED(irq);
40 if (udd == NULL || udd->private == NULL) {
41 HDF_LOGE("%s: invalid parame", __func__);
42 return HDF_FAILURE;
43 }
44 port = (struct UartPl011Port *)udd->private;
45 status = OSAL_READW(port->physBase + UART_MIS);
46 if (status & (UART_MIS_RX | UART_IMSC_TIMEOUT)) {
47 do {
48 fr = OSAL_READB(port->physBase + UART_FR);
49 if (fr & UART_FR_RXFE) {
50 break;
51 }
52 buf[count++] = OSAL_READB(port->physBase + UART_DR);
53 if (udd->num != CONSOLE_UART) {
54 continue;
55 }
56 if (CheckMagicKey(buf[count - 1], CONSOLE_SERIAL)) {
57 goto end;
58 }
59 } while (count < FIFO_SIZE);
60 udd->recv(udd, buf, count);
61 }
62 end:
63 /* clear all interrupt */
64 OSAL_WRITEW(0xFFFF, port->physBase + UART_CLR);
65 return HDF_SUCCESS;
66 }
67
Pl011ConfigBaudrate(const struct UartDriverData *udd, const struct UartPl011Port *port)68 static void Pl011ConfigBaudrate(const struct UartDriverData *udd, const struct UartPl011Port *port)
69 {
70 uint64_t tmp;
71 uint32_t value;
72 uint32_t divider;
73 uint32_t remainder;
74 uint32_t fraction;
75
76 tmp = (uint64_t)IBRD_COEFFICIENTS * (uint64_t)udd->baudrate;
77 if (tmp == 0 || tmp > UINT32_MAX) {
78 HDF_LOGE("%s: err, baudrate %u is invalid", __func__, udd->baudrate);
79 return;
80 }
81
82 value = IBRD_COEFFICIENTS * udd->baudrate;
83 divider = CONFIG_UART_CLK_INPUT / value;
84 remainder = CONFIG_UART_CLK_INPUT % value;
85 value = (FBRD_COEFFICIENTS * remainder) / udd->baudrate;
86 fraction = (value >> 1) + (value & 1);
87 OSAL_WRITEL(divider, port->physBase + UART_IBRD);
88 OSAL_WRITEL(fraction, port->physBase + UART_FBRD);
89 }
90
Pl011ConfigDataBits(const struct UartDriverData *udd, uint32_t *lcrh)91 static void Pl011ConfigDataBits(const struct UartDriverData *udd, uint32_t *lcrh)
92 {
93 *lcrh &= ~UART_LCR_H_8_BIT;
94 switch (udd->attr.dataBits) {
95 case UART_ATTR_DATABIT_5:
96 *lcrh |= UART_LCR_H_5_BIT;
97 break;
98 case UART_ATTR_DATABIT_6:
99 *lcrh |= UART_LCR_H_6_BIT;
100 break;
101 case UART_ATTR_DATABIT_7:
102 *lcrh |= UART_LCR_H_7_BIT;
103 break;
104 case UART_ATTR_DATABIT_8:
105 default:
106 *lcrh |= UART_LCR_H_8_BIT;
107 break;
108 }
109 }
110
Pl011ConfigParity(const struct UartDriverData *udd, uint32_t *lcrh)111 static void Pl011ConfigParity(const struct UartDriverData *udd, uint32_t *lcrh)
112 {
113 switch (udd->attr.parity) {
114 case UART_ATTR_PARITY_EVEN:
115 *lcrh |= UART_LCR_H_PEN;
116 *lcrh |= UART_LCR_H_EPS;
117 *lcrh |= UART_LCR_H_FIFO_EN;
118 break;
119 case UART_ATTR_PARITY_ODD:
120 *lcrh |= UART_LCR_H_PEN;
121 *lcrh &= ~UART_LCR_H_EPS;
122 *lcrh |= UART_LCR_H_FIFO_EN;
123 break;
124 case UART_ATTR_PARITY_MARK:
125 *lcrh |= UART_LCR_H_PEN;
126 *lcrh &= ~UART_LCR_H_EPS;
127 *lcrh |= UART_LCR_H_FIFO_EN;
128 *lcrh |= UART_LCR_H_SPS;
129 break;
130 case UART_ATTR_PARITY_SPACE:
131 *lcrh |= UART_LCR_H_PEN;
132 *lcrh |= UART_LCR_H_EPS;
133 *lcrh |= UART_LCR_H_FIFO_EN;
134 *lcrh |= UART_LCR_H_SPS;
135 break;
136 case UART_ATTR_PARITY_NONE:
137 default:
138 *lcrh &= ~UART_LCR_H_PEN;
139 *lcrh &= ~UART_LCR_H_SPS;
140 break;
141 }
142 }
143
Pl011ConfigStopBits(const struct UartDriverData *udd, uint32_t *lcrh)144 static void Pl011ConfigStopBits(const struct UartDriverData *udd, uint32_t *lcrh)
145 {
146 switch (udd->attr.stopBits) {
147 case UART_ATTR_STOPBIT_2:
148 *lcrh |= UART_LCR_H_STP2;
149 break;
150 case UART_ATTR_STOPBIT_1:
151 default:
152 *lcrh &= ~UART_LCR_H_STP2;
153 break;
154 }
155 }
156
Pl011ConfigLCRH(const struct UartDriverData *udd, const struct UartPl011Port *port, uint32_t lcrh)157 static void Pl011ConfigLCRH(const struct UartDriverData *udd, const struct UartPl011Port *port, uint32_t lcrh)
158 {
159 Pl011ConfigDataBits(udd, &lcrh);
160 lcrh &= ~UART_LCR_H_PEN;
161 lcrh &= ~UART_LCR_H_EPS;
162 lcrh &= ~UART_LCR_H_SPS;
163 Pl011ConfigParity(udd, &lcrh);
164 Pl011ConfigStopBits(udd, &lcrh);
165 if (udd->attr.fifoRxEn || udd->attr.fifoTxEn) {
166 lcrh |= UART_LCR_H_FIFO_EN;
167 }
168 OSAL_WRITEB(lcrh, port->physBase + UART_LCR_H);
169 }
170
Pl011ConfigIn(struct UartDriverData *udd)171 static int32_t Pl011ConfigIn(struct UartDriverData *udd)
172 {
173 uint32_t cr;
174 uint32_t lcrh;
175 struct UartPl011Port *port = NULL;
176
177 port = (struct UartPl011Port *)udd->private;
178 if (port == NULL) {
179 HDF_LOGE("%s: port is NULL", __func__);
180 return HDF_ERR_INVALID_PARAM;
181 }
182 /* get CR */
183 cr = OSAL_READW(port->physBase + UART_CR);
184 /* get LCR_H */
185 lcrh = OSAL_READW(port->physBase + UART_LCR_H);
186 /* uart disable */
187 OSAL_WRITEW(0, port->physBase + UART_CR);
188 /* config cts/rts */
189 if (UART_ATTR_CTS_EN == udd->attr.cts) {
190 cr |= UART_CR_CTS;
191 } else {
192 cr &= ~UART_CR_CTS;
193 }
194 if (UART_ATTR_RTS_EN == udd->attr.rts) {
195 cr |= UART_CR_RTS;
196 } else {
197 cr &= ~UART_CR_RTS;
198 }
199 lcrh &= ~UART_LCR_H_FIFO_EN;
200 OSAL_WRITEB(lcrh, port->physBase + UART_LCR_H);
201
202 cr &= ~UART_CR_EN;
203 OSAL_WRITEW(cr, port->physBase + UART_CR);
204
205 /* set baud rate */
206 Pl011ConfigBaudrate(udd, port);
207
208 /* config lcr_h */
209 Pl011ConfigLCRH(udd, port, lcrh);
210
211 cr |= UART_CR_EN;
212 /* resume CR */
213 OSAL_WRITEW(cr, port->physBase + UART_CR);
214 return HDF_SUCCESS;
215 }
216
Pl011StartUp(struct UartDriverData *udd)217 static int32_t Pl011StartUp(struct UartDriverData *udd)
218 {
219 int32_t ret;
220 uint32_t cr;
221 struct UartPl011Port *port = NULL;
222
223 if (udd == NULL) {
224 HDF_LOGE("%s: udd is null", __func__);
225 return HDF_ERR_INVALID_PARAM;
226 }
227 port = (struct UartPl011Port *)udd->private;
228 if (port == NULL) {
229 HDF_LOGE("%s: port is null", __func__);
230 return HDF_ERR_INVALID_PARAM;
231 }
232 /* enable the clock */
233 LOS_TaskLock();
234 uart_clk_cfg(udd->num, true);
235 LOS_TaskUnlock();
236 /* uart disable */
237 OSAL_WRITEW(0, port->physBase + UART_CR);
238 OSAL_WRITEW(0xFF, port->physBase + UART_RSR);
239 /* clear all interrupt,set mask */
240 OSAL_WRITEW(0xFFFF, port->physBase + UART_CLR);
241 /* mask all interrupt */
242 OSAL_WRITEW(0x0, port->physBase + UART_IMSC);
243 /* interrupt trigger line RX: 4/8, TX 7/8 */
244 OSAL_WRITEW(UART_IFLS_RX4_8 | UART_IFLS_TX7_8, port->physBase + UART_IFLS);
245 if (!(udd->flags & UART_FLG_DMA_RX)) {
246 if (!(port->flags & PL011_FLG_IRQ_REQUESTED)) {
247 ret = OsalRegisterIrq(port->irqNum, 0, Pl011Irq, "uart_pl011", udd);
248 if (ret == 0) {
249 port->flags |= PL011_FLG_IRQ_REQUESTED;
250 /* enable rx and timeout interrupt */
251 OSAL_WRITEW(UART_IMSC_RX | UART_IMSC_TIMEOUT, port->physBase + UART_IMSC);
252 }
253 }
254 }
255 cr = OSAL_READW(port->physBase + UART_CR);
256 cr |= UART_CR_EN | UART_CR_RX_EN | UART_CR_TX_EN;
257 OSAL_WRITEL(cr, port->physBase + UART_CR);
258 ret = Pl011ConfigIn(udd);
259 return ret;
260 }
261
Pl011ShutDown(struct UartDriverData *udd)262 static int32_t Pl011ShutDown(struct UartDriverData *udd)
263 {
264 uint32_t reg_tmp;
265 struct UartPl011Port *port = NULL;
266
267 if (udd == NULL) {
268 HDF_LOGE("%s: udd is null", __func__);
269 return HDF_ERR_INVALID_PARAM;
270 }
271 port = (struct UartPl011Port *)udd->private;
272 if (port == NULL) {
273 HDF_LOGE("%s: port is null", __func__);
274 return HDF_ERR_INVALID_PARAM;
275 }
276 OSAL_WRITEW(0, port->physBase + UART_IMSC);
277 OSAL_WRITEW(0xFFFF, port->physBase + UART_CLR);
278 if (port->flags & PL011_FLG_IRQ_REQUESTED) {
279 OsalUnregisterIrq(port->irqNum, udd);
280 port->flags &= ~PL011_FLG_IRQ_REQUESTED;
281 }
282
283 reg_tmp = OSAL_READW(port->physBase + UART_CR);
284 reg_tmp &= ~UART_CR_TX_EN;
285 reg_tmp &= ~UART_CR_RX_EN;
286 reg_tmp &= ~UART_CR_EN;
287 OSAL_WRITEW(reg_tmp, port->physBase + UART_CR);
288
289 /* disable break and fifo */
290 reg_tmp = OSAL_READW(port->physBase + UART_LCR_H);
291 reg_tmp &= ~(UART_LCR_H_BREAK);
292 reg_tmp &= ~(UART_LCR_H_FIFO_EN);
293 OSAL_WRITEW(reg_tmp, port->physBase + UART_LCR_H);
294
295 /* shut down the clock */
296 LOS_TaskLock();
297 uart_clk_cfg(udd->num, false);
298 LOS_TaskUnlock();
299 return HDF_SUCCESS;
300 }
301
Pl011StartTx(struct UartDriverData *udd, const char *buf, size_t count)302 static int32_t Pl011StartTx(struct UartDriverData *udd, const char *buf, size_t count)
303 {
304 struct UartPl011Port *port = NULL;
305
306 if (udd == NULL || buf == NULL || count == 0) {
307 HDF_LOGE("%s: invalid parame", __func__);
308 return HDF_ERR_INVALID_PARAM;
309 }
310 port = (struct UartPl011Port *)udd->private;
311 if (port == NULL) {
312 HDF_LOGE("%s: port is null", __func__);
313 return HDF_ERR_INVALID_PARAM;
314 }
315 /* UART_WITH_LOCK: there is a spinlock in the function to write reg in order. */
316 (void)UartPutsReg(port->physBase, buf, count, UART_WITH_LOCK);
317 return HDF_SUCCESS;
318 }
319
Pl011Config(struct UartDriverData *udd)320 static int32_t Pl011Config(struct UartDriverData *udd)
321 {
322 uint32_t fr;
323 struct UartPl011Port *port = NULL;
324
325 if (udd == NULL) {
326 HDF_LOGE("%s: udd is null", __func__);
327 return HDF_ERR_INVALID_PARAM;
328 }
329 port = (struct UartPl011Port *)udd->private;
330 if (port == NULL) {
331 HDF_LOGE("%s: port is null", __func__);
332 return HDF_ERR_INVALID_PARAM;
333 }
334 /* wait for send finish */
335 do {
336 fr = OSAL_READB(port->physBase + UART_FR);
337 if (!(fr & UART_FR_BUSY)) {
338 break;
339 }
340 OsalMSleep(UART_WAIT_MS);
341 } while (1);
342 return Pl011ConfigIn(udd);
343 }
344
345 struct UartOps g_pl011Uops = {
346 .StartUp = Pl011StartUp,
347 .ShutDown = Pl011ShutDown,
348 .StartTx = Pl011StartTx,
349 .Config = Pl011Config,
350 .DmaStartUp = NULL,
351 .DmaShutDown = NULL,
352 };
353
Pl011Read(struct UartDriverData *udd, char *buf, size_t count)354 int32_t Pl011Read(struct UartDriverData *udd, char *buf, size_t count)
355 {
356 uint32_t wp;
357 uint32_t rp;
358 uint32_t upperHalf;
359 uint32_t lowerHalf = 0;
360 unsigned long data;
361
362 if (udd == NULL || buf == NULL || count == 0 || udd->rxTransfer == NULL) {
363 HDF_LOGE("%s: invalid parameter", __func__);
364 return HDF_ERR_INVALID_PARAM;
365 }
366 wp = udd->rxTransfer->wp;
367 rp = udd->rxTransfer->rp;
368 data = (unsigned long)(udd->rxTransfer->data);
369
370 if (rp == wp) {
371 return 0; // buffer empty
372 }
373
374 if (rp < wp) { // rp behind
375 upperHalf = (count > (wp - rp)) ? (wp - rp) : count;
376 if (upperHalf > 0 && memcpy_s(buf, upperHalf, (void *)(data + rp), upperHalf) != EOK) {
377 return HDF_ERR_IO;
378 }
379 rp += upperHalf;
380 } else { // wp behind
381 count = (count > (BUF_SIZE - rp + wp)) ? (BUF_SIZE - rp + wp) : count;
382 upperHalf = (count > (BUF_SIZE - rp)) ? (BUF_SIZE - rp) : count;
383 lowerHalf = (count > (BUF_SIZE - rp)) ? (count - (BUF_SIZE - rp)) : 0;
384 if (upperHalf > 0 && memcpy_s(buf, upperHalf, (void *)(data + rp), upperHalf) != EOK) {
385 return HDF_ERR_IO;
386 }
387 if (lowerHalf > 0 && memcpy_s(buf + upperHalf, lowerHalf, (void *)(data), lowerHalf) != EOK) {
388 return HDF_ERR_IO;
389 }
390 rp += upperHalf;
391 if (rp >= BUF_SIZE) {
392 rp = lowerHalf;
393 }
394 }
395 udd->rxTransfer->rp = rp;
396 return (upperHalf + lowerHalf);
397 }
398
Pl011Notify(struct wait_queue_head *wait)399 static int32_t Pl011Notify(struct wait_queue_head *wait)
400 {
401 if (wait == NULL) {
402 return HDF_ERR_INVALID_PARAM;
403 }
404 LOS_EventWrite(&wait->stEvent, 0x1);
405 notify_poll(wait);
406 return HDF_SUCCESS;
407 }
408
PL011UartRecvNotify(struct UartDriverData *udd, const char *buf, size_t count)409 int32_t PL011UartRecvNotify(struct UartDriverData *udd, const char *buf, size_t count)
410 {
411 uint32_t wp;
412 uint32_t rp;
413 uint32_t upperHalf;
414 uint32_t lowerHalf = 0;
415 unsigned long data;
416
417 if (udd == NULL || buf == NULL || count == 0 || udd->rxTransfer == NULL) {
418 HDF_LOGE("%s: invalid parameter", __func__);
419 return HDF_ERR_INVALID_PARAM;
420 }
421 wp = udd->rxTransfer->wp;
422 rp = udd->rxTransfer->rp;
423 data = (unsigned long)(udd->rxTransfer->data);
424
425 if (wp < rp) { // wp behind
426 upperHalf = (count > (rp - wp - 1)) ? (rp - wp - 1) : count;
427 if (upperHalf > 0 && memcpy_s((void *)(data + wp), upperHalf, buf, upperHalf) != EOK) {
428 return HDF_ERR_IO;
429 }
430 wp += upperHalf;
431 } else { // rp behind
432 count = (count > ((BUF_SIZE - wp) + rp - 1)) ? (BUF_SIZE - wp) + rp - 1 : count;
433 upperHalf = (count > (BUF_SIZE - wp)) ? (BUF_SIZE - wp) : count;
434 lowerHalf = (count > (BUF_SIZE - wp)) ? (count - (BUF_SIZE - wp)) : 0;
435 if (upperHalf > 0 && memcpy_s((void *)(data + wp), upperHalf, buf, upperHalf) != EOK) {
436 return HDF_ERR_IO;
437 }
438 if (lowerHalf > 0 && memcpy_s((void *)(data), lowerHalf, buf + upperHalf, lowerHalf) != EOK) {
439 return HDF_ERR_IO;
440 }
441 wp += upperHalf;
442 if (wp >= BUF_SIZE) {
443 wp = lowerHalf;
444 }
445 }
446
447 if (Pl011Notify(&udd->wait) != HDF_SUCCESS) {
448 HDF_LOGE("%s: Pl011 notify err", __func__);
449 return HDF_FAILURE;
450 }
451 udd->rxTransfer->wp = wp;
452 return (upperHalf + lowerHalf);
453 }
454
PL011UartRxBufEmpty(struct UartDriverData *udd)455 bool PL011UartRxBufEmpty(struct UartDriverData *udd)
456 {
457 struct UartTransfer *transfer = udd->rxTransfer;
458 return (transfer->wp == transfer->rp);
459 }
460
Pl011GetOps(void)461 struct UartOps *Pl011GetOps(void)
462 {
463 return &g_pl011Uops;
464 }
465