xref: /device/qemu/drivers/uart/uart_pl011.c (revision d6aed566)
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 "soc/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
31static 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    }
63end:
64    /* clear all interrupt */
65    OSAL_WRITEW(0xFFFF, port->physBase + UART_CLR);
66    return HDF_SUCCESS;
67}
68
69static 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
92static 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
112static 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
145static 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
158static 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
172static 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
218static 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
263static 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
303static 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
321static 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
346struct UartOps g_pl011Uops = {
347    .StartUp        = Pl011StartUp,
348    .ShutDown       = Pl011ShutDown,
349    .StartTx        = Pl011StartTx,
350    .Config         = Pl011Config,
351    .DmaStartUp     = NULL,
352    .DmaShutDown    = NULL,
353};
354
355int32_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
400static 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
410int32_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
456bool PL011UartRxBufEmpty(struct UartDriverData *udd)
457{
458    struct UartTransfer *transfer = udd->rxTransfer;
459    return (transfer->wp == transfer->rp);
460}
461
462struct UartOps *Pl011GetOps(void)
463{
464    return &g_pl011Uops;
465}
466