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