1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright (c) 2022, HiSilicon Limited
4 */
5
6 #include <assert.h>
7 #include <drivers/lpc_uart.h>
8 #include <io.h>
9 #include <keep.h>
10 #include <mm/core_mmu.h>
11 #include <util.h>
12
chip_to_base(struct serial_chip * chip)13 static paddr_t chip_to_base(struct serial_chip *chip)
14 {
15 struct lpc_uart_data *pd =
16 container_of(chip, struct lpc_uart_data, chip);
17
18 return io_pa_or_va(&pd->base, LPC_SIZE);
19 }
20
lpc_byte_read(paddr_t addr,uint8_t * data)21 static void lpc_byte_read(paddr_t addr, uint8_t *data)
22 {
23 uint32_t status = 0;
24 uint32_t cnt = 0;
25
26 io_write32(LPC_CMD_REG_OFFSET + addr, LPC_SINGLE_READ);
27
28 io_write32(LPC_OP_LEN_REG_OFFSET + addr, 1);
29 io_write32(LPC_ADDR_REG_OFFSET + addr, UART_BASE + UART_LSR);
30
31 io_write32(LPC_START_REG_OFFSET + addr, 1);
32
33 status = io_read32(LPC_IRQ_ST_REG_OFFSET + addr);
34 while (!(status & LPC_IRQ_ST_ON)) {
35 if (cnt > UART_SEND_LOOP_MAX)
36 return;
37 cnt++;
38 status = io_read32(LPC_IRQ_ST_REG_OFFSET + addr);
39 }
40
41 io_write32(LPC_IRQ_ST_REG_OFFSET + addr, LPC_IRQ_ST_ON);
42
43 if (io_read32(LPC_OP_STATUS_REG_OFFSET + addr) & LPC_IRQ_ST_ON)
44 *data = io_read32(LPC_RDATA_REG_OFFSET + addr);
45 }
46
lpc_byte_write(paddr_t addr,uint8_t data)47 static void lpc_byte_write(paddr_t addr, uint8_t data)
48 {
49 uint32_t status = 0;
50 uint32_t cnt = 0;
51
52 io_write32(LPC_CMD_REG_OFFSET + addr, LPC_SINGLE_WRITE);
53 io_write32(LPC_OP_LEN_REG_OFFSET + addr, 1);
54 io_write32(LPC_WDATA_REG_OFFSET + addr, data);
55
56 io_write32(LPC_ADDR_REG_OFFSET + addr, UART_BASE + UART_THR);
57 io_write32(LPC_START_REG_OFFSET + addr, 1);
58
59 status = io_read32(LPC_IRQ_ST_REG_OFFSET + addr);
60 while (!(status & LPC_IRQ_ST_ON)) {
61 if (cnt > UART_SEND_LOOP_MAX)
62 return;
63 cnt++;
64 status = io_read32(LPC_IRQ_ST_REG_OFFSET + addr);
65 }
66
67 io_write32(LPC_IRQ_ST_REG_OFFSET + addr, LPC_IRQ_ST_ON);
68 }
69
lpc_uart_core_putc(paddr_t base,int ch)70 static void lpc_uart_core_putc(paddr_t base, int ch)
71 {
72 uint8_t var = '\0';
73 uint32_t i = 0;
74
75 for (i = 0; i < UART_SEND_LOOP_MAX; i++) {
76 lpc_byte_read(base, &var);
77 if ((var & LPC_RADTA_LEN) == LPC_RADTA_LEN)
78 break;
79 }
80
81 lpc_byte_write(base, ch);
82
83 for (i = 0; i < UART_SEND_LOOP_MAX; i++) {
84 lpc_byte_read(base, &var);
85 if ((var & LPC_RADTA_LEN) == LPC_RADTA_LEN)
86 break;
87 }
88 }
89
lpc_uart_putc(struct serial_chip * chip,int ch)90 static void lpc_uart_putc(struct serial_chip *chip, int ch)
91 {
92 paddr_t base = chip_to_base(chip);
93
94 lpc_uart_core_putc(base, ch);
95 }
96
97 static const struct serial_ops lpc_uart_ops = {
98 .putc = lpc_uart_putc,
99 };
100 DECLARE_KEEP_PAGER(lpc_uart_ops);
101
lpc_uart_init(struct lpc_uart_data * pd,paddr_t base,uint32_t uart_clk __unused,uint32_t baud_rate __unused)102 void lpc_uart_init(struct lpc_uart_data *pd, paddr_t base,
103 uint32_t uart_clk __unused, uint32_t baud_rate __unused)
104 {
105 pd->base.pa = base;
106 pd->chip.ops = &lpc_uart_ops;
107 }
108
109