11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause
244bd24c5SJames Kung /*
344bd24c5SJames Kung * Copyright (c) 2015, Linaro Limited
444bd24c5SJames Kung */
544bd24c5SJames Kung
6d66fa083SJerome Forissier #include <compiler.h>
744bd24c5SJames Kung #include <console.h>
88d94060aSEtienne Carriere #include <drivers/serial8250_uart.h>
944bd24c5SJames Kung #include <io.h>
108d94060aSEtienne Carriere #include <keep.h>
11d276907cSYing-Chun Liu (PaulLiu) #include <kernel/dt.h>
12*9e3c57c8SEtienne Carriere #include <kernel/dt_driver.h>
13*9e3c57c8SEtienne Carriere #include <util.h>
1444bd24c5SJames Kung
1544bd24c5SJames Kung /* uart register defines */
1644bd24c5SJames Kung #define UART_RHR 0x0
1744bd24c5SJames Kung #define UART_THR 0x0
1844bd24c5SJames Kung #define UART_IER 0x4
1944bd24c5SJames Kung #define UART_ISR 0x8
2044bd24c5SJames Kung #define UART_FCR 0x8
2144bd24c5SJames Kung #define UART_LCR 0xc
2244bd24c5SJames Kung #define UART_MCR 0x10
2344bd24c5SJames Kung #define UART_LSR 0x14
2444bd24c5SJames Kung #define UART_MSR 0x18
2544bd24c5SJames Kung #define UART_SPR 0x1c
2644bd24c5SJames Kung
2744bd24c5SJames Kung /* uart status register bits */
2844bd24c5SJames Kung #define LSR_TEMT 0x40 /* Transmitter empty */
2944bd24c5SJames Kung #define LSR_THRE 0x20 /* Transmit-hold-register empty */
3044bd24c5SJames Kung #define LSR_EMPTY (LSR_TEMT | LSR_THRE)
3144bd24c5SJames Kung #define LSR_DR 0x01 /* DATA Ready */
3244bd24c5SJames Kung
chip_to_base(struct serial_chip * chip)33d66fa083SJerome Forissier static vaddr_t chip_to_base(struct serial_chip *chip)
3444bd24c5SJames Kung {
35d66fa083SJerome Forissier struct serial8250_uart_data *pd =
36d66fa083SJerome Forissier container_of(chip, struct serial8250_uart_data, chip);
37d66fa083SJerome Forissier
38c2e4eb43SAnton Rybakov return io_pa_or_va(&pd->base, SERIAL8250_UART_REG_SIZE);
3944bd24c5SJames Kung }
4044bd24c5SJames Kung
serial8250_uart_flush(struct serial_chip * chip)41d66fa083SJerome Forissier static void serial8250_uart_flush(struct serial_chip *chip)
4244bd24c5SJames Kung {
43d66fa083SJerome Forissier vaddr_t base = chip_to_base(chip);
44d66fa083SJerome Forissier
4544bd24c5SJames Kung while (1) {
46918bb3a5SEtienne Carriere uint32_t state = io_read32(base + UART_LSR);
4744bd24c5SJames Kung
48d66fa083SJerome Forissier /* Wait until transmit FIFO is empty */
4944bd24c5SJames Kung if ((state & LSR_EMPTY) == LSR_EMPTY)
5044bd24c5SJames Kung break;
5144bd24c5SJames Kung }
5244bd24c5SJames Kung }
5344bd24c5SJames Kung
serial8250_uart_have_rx_data(struct serial_chip * chip)54d66fa083SJerome Forissier static bool serial8250_uart_have_rx_data(struct serial_chip *chip)
5544bd24c5SJames Kung {
56d66fa083SJerome Forissier vaddr_t base = chip_to_base(chip);
57d66fa083SJerome Forissier
58918bb3a5SEtienne Carriere return (io_read32(base + UART_LSR) & LSR_DR);
5944bd24c5SJames Kung }
6044bd24c5SJames Kung
serial8250_uart_getchar(struct serial_chip * chip)61d66fa083SJerome Forissier static int serial8250_uart_getchar(struct serial_chip *chip)
6244bd24c5SJames Kung {
63d66fa083SJerome Forissier vaddr_t base = chip_to_base(chip);
6444bd24c5SJames Kung
65d66fa083SJerome Forissier while (!serial8250_uart_have_rx_data(chip)) {
66d66fa083SJerome Forissier /* Transmit FIFO is empty, waiting again */
6744bd24c5SJames Kung ;
6844bd24c5SJames Kung }
69918bb3a5SEtienne Carriere return io_read32(base + UART_RHR) & 0xff;
7044bd24c5SJames Kung }
7144bd24c5SJames Kung
serial8250_uart_putc(struct serial_chip * chip,int ch)72d66fa083SJerome Forissier static void serial8250_uart_putc(struct serial_chip *chip, int ch)
73d66fa083SJerome Forissier {
74d66fa083SJerome Forissier vaddr_t base = chip_to_base(chip);
75d66fa083SJerome Forissier
76d66fa083SJerome Forissier serial8250_uart_flush(chip);
77d66fa083SJerome Forissier
78d66fa083SJerome Forissier /* Write out character to transmit FIFO */
79918bb3a5SEtienne Carriere io_write32(base + UART_THR, ch);
80d66fa083SJerome Forissier }
81d66fa083SJerome Forissier
82d66fa083SJerome Forissier static const struct serial_ops serial8250_uart_ops = {
83d66fa083SJerome Forissier .flush = serial8250_uart_flush,
84d66fa083SJerome Forissier .getchar = serial8250_uart_getchar,
85d66fa083SJerome Forissier .have_rx_data = serial8250_uart_have_rx_data,
86d66fa083SJerome Forissier .putc = serial8250_uart_putc,
87d66fa083SJerome Forissier };
883639b55fSJerome Forissier DECLARE_KEEP_PAGER(serial8250_uart_ops);
89d66fa083SJerome Forissier
serial8250_uart_init(struct serial8250_uart_data * pd,paddr_t base,uint32_t __unused uart_clk,uint32_t __unused baud_rate)90d66fa083SJerome Forissier void serial8250_uart_init(struct serial8250_uart_data *pd, paddr_t base,
91d66fa083SJerome Forissier uint32_t __unused uart_clk,
92d66fa083SJerome Forissier uint32_t __unused baud_rate)
93d66fa083SJerome Forissier
94d66fa083SJerome Forissier {
95d66fa083SJerome Forissier pd->base.pa = base;
96d66fa083SJerome Forissier pd->chip.ops = &serial8250_uart_ops;
97d66fa083SJerome Forissier
98d66fa083SJerome Forissier /*
99d66fa083SJerome Forissier * do nothing, debug uart(uart0) share with normal world,
100d66fa083SJerome Forissier * everything for uart0 is ready now.
101d66fa083SJerome Forissier */
102d66fa083SJerome Forissier }
103d276907cSYing-Chun Liu (PaulLiu)
104d276907cSYing-Chun Liu (PaulLiu) #ifdef CFG_DT
105d276907cSYing-Chun Liu (PaulLiu)
serial8250_uart_dev_alloc(void)106d276907cSYing-Chun Liu (PaulLiu) static struct serial_chip *serial8250_uart_dev_alloc(void)
107d276907cSYing-Chun Liu (PaulLiu) {
108630d13fbSJens Wiklander struct serial8250_uart_data *pd = calloc(1, sizeof(*pd));
109d276907cSYing-Chun Liu (PaulLiu)
110d276907cSYing-Chun Liu (PaulLiu) if (!pd)
111d276907cSYing-Chun Liu (PaulLiu) return NULL;
112d276907cSYing-Chun Liu (PaulLiu) return &pd->chip;
113d276907cSYing-Chun Liu (PaulLiu) }
114d276907cSYing-Chun Liu (PaulLiu)
serial8250_uart_dev_init(struct serial_chip * chip,const void * fdt,int offs,const char * parms)115d276907cSYing-Chun Liu (PaulLiu) static int serial8250_uart_dev_init(struct serial_chip *chip,
116d276907cSYing-Chun Liu (PaulLiu) const void *fdt,
117d276907cSYing-Chun Liu (PaulLiu) int offs,
118d276907cSYing-Chun Liu (PaulLiu) const char *parms)
119d276907cSYing-Chun Liu (PaulLiu) {
120d276907cSYing-Chun Liu (PaulLiu) struct serial8250_uart_data *pd =
121d276907cSYing-Chun Liu (PaulLiu) container_of(chip, struct serial8250_uart_data, chip);
122d276907cSYing-Chun Liu (PaulLiu) vaddr_t vbase;
123d276907cSYing-Chun Liu (PaulLiu) paddr_t pbase;
124d276907cSYing-Chun Liu (PaulLiu) size_t size;
125d276907cSYing-Chun Liu (PaulLiu)
126d276907cSYing-Chun Liu (PaulLiu) if (parms && parms[0])
127d276907cSYing-Chun Liu (PaulLiu) IMSG("serial8250_uart: device parameters ignored (%s)", parms);
128d276907cSYing-Chun Liu (PaulLiu)
129a5d5bbc8SVesa Jääskeläinen if (dt_map_dev(fdt, offs, &vbase, &size, DT_MAP_AUTO) < 0)
130d276907cSYing-Chun Liu (PaulLiu) return -1;
131d276907cSYing-Chun Liu (PaulLiu)
132d276907cSYing-Chun Liu (PaulLiu) if (size < SERIAL8250_UART_REG_SIZE) {
133d276907cSYing-Chun Liu (PaulLiu) EMSG("serial8250_uart: register size too small: %zx", size);
134d276907cSYing-Chun Liu (PaulLiu) return -1;
135d276907cSYing-Chun Liu (PaulLiu) }
136d276907cSYing-Chun Liu (PaulLiu)
137d276907cSYing-Chun Liu (PaulLiu) pbase = virt_to_phys((void *)vbase);
138d276907cSYing-Chun Liu (PaulLiu) serial8250_uart_init(pd, pbase, 0, 0);
139d276907cSYing-Chun Liu (PaulLiu)
140d276907cSYing-Chun Liu (PaulLiu) return 0;
141d276907cSYing-Chun Liu (PaulLiu) }
142d276907cSYing-Chun Liu (PaulLiu)
serial8250_uart_dev_free(struct serial_chip * chip)143d276907cSYing-Chun Liu (PaulLiu) static void serial8250_uart_dev_free(struct serial_chip *chip)
144d276907cSYing-Chun Liu (PaulLiu) {
145d276907cSYing-Chun Liu (PaulLiu) struct serial8250_uart_data *pd =
146d276907cSYing-Chun Liu (PaulLiu) container_of(chip, struct serial8250_uart_data, chip);
147d276907cSYing-Chun Liu (PaulLiu)
148d276907cSYing-Chun Liu (PaulLiu) free(pd);
149d276907cSYing-Chun Liu (PaulLiu) }
150d276907cSYing-Chun Liu (PaulLiu)
151d276907cSYing-Chun Liu (PaulLiu) static const struct serial_driver serial8250_driver = {
152d276907cSYing-Chun Liu (PaulLiu) .dev_alloc = serial8250_uart_dev_alloc,
153d276907cSYing-Chun Liu (PaulLiu) .dev_init = serial8250_uart_dev_init,
154d276907cSYing-Chun Liu (PaulLiu) .dev_free = serial8250_uart_dev_free,
155d276907cSYing-Chun Liu (PaulLiu) };
156d276907cSYing-Chun Liu (PaulLiu)
157d276907cSYing-Chun Liu (PaulLiu) static const struct dt_device_match serial8250_match_table[] = {
158d276907cSYing-Chun Liu (PaulLiu) { .compatible = "snps,dw-apb-uart" },
159d276907cSYing-Chun Liu (PaulLiu) { 0 }
160d276907cSYing-Chun Liu (PaulLiu) };
161d276907cSYing-Chun Liu (PaulLiu)
16261bdedeaSJerome Forissier DEFINE_DT_DRIVER(serial8250_dt_driver) = {
163d276907cSYing-Chun Liu (PaulLiu) .name = "serial8250_uart",
1645e588771SClément Léger .type = DT_DRIVER_UART,
165d276907cSYing-Chun Liu (PaulLiu) .match_table = serial8250_match_table,
166d276907cSYing-Chun Liu (PaulLiu) .driver = &serial8250_driver,
167d276907cSYing-Chun Liu (PaulLiu) };
168d276907cSYing-Chun Liu (PaulLiu)
169d276907cSYing-Chun Liu (PaulLiu) #endif /* CFG_DT */
170