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