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> 11d66fa083SJerome Forissier #include <util.h> 12d276907cSYing-Chun Liu (PaulLiu) #include <kernel/dt.h> 1344bd24c5SJames Kung 1444bd24c5SJames Kung /* uart register defines */ 1544bd24c5SJames Kung #define UART_RHR 0x0 1644bd24c5SJames Kung #define UART_THR 0x0 1744bd24c5SJames Kung #define UART_IER 0x4 1844bd24c5SJames Kung #define UART_ISR 0x8 1944bd24c5SJames Kung #define UART_FCR 0x8 2044bd24c5SJames Kung #define UART_LCR 0xc 2144bd24c5SJames Kung #define UART_MCR 0x10 2244bd24c5SJames Kung #define UART_LSR 0x14 2344bd24c5SJames Kung #define UART_MSR 0x18 2444bd24c5SJames Kung #define UART_SPR 0x1c 2544bd24c5SJames Kung 2644bd24c5SJames Kung /* uart status register bits */ 2744bd24c5SJames Kung #define LSR_TEMT 0x40 /* Transmitter empty */ 2844bd24c5SJames Kung #define LSR_THRE 0x20 /* Transmit-hold-register empty */ 2944bd24c5SJames Kung #define LSR_EMPTY (LSR_TEMT | LSR_THRE) 3044bd24c5SJames Kung #define LSR_DR 0x01 /* DATA Ready */ 3144bd24c5SJames Kung 32d66fa083SJerome Forissier static vaddr_t chip_to_base(struct serial_chip *chip) 3344bd24c5SJames Kung { 34d66fa083SJerome Forissier struct serial8250_uart_data *pd = 35d66fa083SJerome Forissier container_of(chip, struct serial8250_uart_data, chip); 36d66fa083SJerome Forissier 37c2e4eb43SAnton Rybakov return io_pa_or_va(&pd->base, SERIAL8250_UART_REG_SIZE); 3844bd24c5SJames Kung } 3944bd24c5SJames Kung 40d66fa083SJerome Forissier static void serial8250_uart_flush(struct serial_chip *chip) 4144bd24c5SJames Kung { 42d66fa083SJerome Forissier vaddr_t base = chip_to_base(chip); 43d66fa083SJerome Forissier 4444bd24c5SJames Kung while (1) { 45918bb3a5SEtienne Carriere uint32_t state = io_read32(base + UART_LSR); 4644bd24c5SJames Kung 47d66fa083SJerome Forissier /* Wait until transmit FIFO is empty */ 4844bd24c5SJames Kung if ((state & LSR_EMPTY) == LSR_EMPTY) 4944bd24c5SJames Kung break; 5044bd24c5SJames Kung } 5144bd24c5SJames Kung } 5244bd24c5SJames Kung 53d66fa083SJerome Forissier static bool serial8250_uart_have_rx_data(struct serial_chip *chip) 5444bd24c5SJames Kung { 55d66fa083SJerome Forissier vaddr_t base = chip_to_base(chip); 56d66fa083SJerome Forissier 57918bb3a5SEtienne Carriere return (io_read32(base + UART_LSR) & LSR_DR); 5844bd24c5SJames Kung } 5944bd24c5SJames Kung 60d66fa083SJerome Forissier static int serial8250_uart_getchar(struct serial_chip *chip) 6144bd24c5SJames Kung { 62d66fa083SJerome Forissier vaddr_t base = chip_to_base(chip); 6344bd24c5SJames Kung 64d66fa083SJerome Forissier while (!serial8250_uart_have_rx_data(chip)) { 65d66fa083SJerome Forissier /* Transmit FIFO is empty, waiting again */ 6644bd24c5SJames Kung ; 6744bd24c5SJames Kung } 68918bb3a5SEtienne Carriere return io_read32(base + UART_RHR) & 0xff; 6944bd24c5SJames Kung } 7044bd24c5SJames Kung 71d66fa083SJerome Forissier static void serial8250_uart_putc(struct serial_chip *chip, int ch) 72d66fa083SJerome Forissier { 73d66fa083SJerome Forissier vaddr_t base = chip_to_base(chip); 74d66fa083SJerome Forissier 75d66fa083SJerome Forissier serial8250_uart_flush(chip); 76d66fa083SJerome Forissier 77d66fa083SJerome Forissier /* Write out character to transmit FIFO */ 78918bb3a5SEtienne Carriere io_write32(base + UART_THR, ch); 79d66fa083SJerome Forissier } 80d66fa083SJerome Forissier 81d66fa083SJerome Forissier static const struct serial_ops serial8250_uart_ops = { 82d66fa083SJerome Forissier .flush = serial8250_uart_flush, 83d66fa083SJerome Forissier .getchar = serial8250_uart_getchar, 84d66fa083SJerome Forissier .have_rx_data = serial8250_uart_have_rx_data, 85d66fa083SJerome Forissier .putc = serial8250_uart_putc, 86d66fa083SJerome Forissier }; 873639b55fSJerome Forissier DECLARE_KEEP_PAGER(serial8250_uart_ops); 88d66fa083SJerome Forissier 89d66fa083SJerome Forissier void serial8250_uart_init(struct serial8250_uart_data *pd, paddr_t base, 90d66fa083SJerome Forissier uint32_t __unused uart_clk, 91d66fa083SJerome Forissier uint32_t __unused baud_rate) 92d66fa083SJerome Forissier 93d66fa083SJerome Forissier { 94d66fa083SJerome Forissier pd->base.pa = base; 95d66fa083SJerome Forissier pd->chip.ops = &serial8250_uart_ops; 96d66fa083SJerome Forissier 97d66fa083SJerome Forissier /* 98d66fa083SJerome Forissier * do nothing, debug uart(uart0) share with normal world, 99d66fa083SJerome Forissier * everything for uart0 is ready now. 100d66fa083SJerome Forissier */ 101d66fa083SJerome Forissier } 102d276907cSYing-Chun Liu (PaulLiu) 103d276907cSYing-Chun Liu (PaulLiu) #ifdef CFG_DT 104d276907cSYing-Chun Liu (PaulLiu) 105d276907cSYing-Chun Liu (PaulLiu) static struct serial_chip *serial8250_uart_dev_alloc(void) 106d276907cSYing-Chun Liu (PaulLiu) { 107630d13fbSJens Wiklander struct serial8250_uart_data *pd = calloc(1, sizeof(*pd)); 108d276907cSYing-Chun Liu (PaulLiu) 109d276907cSYing-Chun Liu (PaulLiu) if (!pd) 110d276907cSYing-Chun Liu (PaulLiu) return NULL; 111d276907cSYing-Chun Liu (PaulLiu) return &pd->chip; 112d276907cSYing-Chun Liu (PaulLiu) } 113d276907cSYing-Chun Liu (PaulLiu) 114d276907cSYing-Chun Liu (PaulLiu) static int serial8250_uart_dev_init(struct serial_chip *chip, 115d276907cSYing-Chun Liu (PaulLiu) const void *fdt, 116d276907cSYing-Chun Liu (PaulLiu) int offs, 117d276907cSYing-Chun Liu (PaulLiu) const char *parms) 118d276907cSYing-Chun Liu (PaulLiu) { 119d276907cSYing-Chun Liu (PaulLiu) struct serial8250_uart_data *pd = 120d276907cSYing-Chun Liu (PaulLiu) container_of(chip, struct serial8250_uart_data, chip); 121d276907cSYing-Chun Liu (PaulLiu) vaddr_t vbase; 122d276907cSYing-Chun Liu (PaulLiu) paddr_t pbase; 123d276907cSYing-Chun Liu (PaulLiu) size_t size; 124d276907cSYing-Chun Liu (PaulLiu) 125d276907cSYing-Chun Liu (PaulLiu) if (parms && parms[0]) 126d276907cSYing-Chun Liu (PaulLiu) IMSG("serial8250_uart: device parameters ignored (%s)", parms); 127d276907cSYing-Chun Liu (PaulLiu) 128*a5d5bbc8SVesa Jääskeläinen if (dt_map_dev(fdt, offs, &vbase, &size, DT_MAP_AUTO) < 0) 129d276907cSYing-Chun Liu (PaulLiu) return -1; 130d276907cSYing-Chun Liu (PaulLiu) 131d276907cSYing-Chun Liu (PaulLiu) if (size < SERIAL8250_UART_REG_SIZE) { 132d276907cSYing-Chun Liu (PaulLiu) EMSG("serial8250_uart: register size too small: %zx", size); 133d276907cSYing-Chun Liu (PaulLiu) return -1; 134d276907cSYing-Chun Liu (PaulLiu) } 135d276907cSYing-Chun Liu (PaulLiu) 136d276907cSYing-Chun Liu (PaulLiu) pbase = virt_to_phys((void *)vbase); 137d276907cSYing-Chun Liu (PaulLiu) serial8250_uart_init(pd, pbase, 0, 0); 138d276907cSYing-Chun Liu (PaulLiu) 139d276907cSYing-Chun Liu (PaulLiu) return 0; 140d276907cSYing-Chun Liu (PaulLiu) } 141d276907cSYing-Chun Liu (PaulLiu) 142d276907cSYing-Chun Liu (PaulLiu) static void serial8250_uart_dev_free(struct serial_chip *chip) 143d276907cSYing-Chun Liu (PaulLiu) { 144d276907cSYing-Chun Liu (PaulLiu) struct serial8250_uart_data *pd = 145d276907cSYing-Chun Liu (PaulLiu) container_of(chip, struct serial8250_uart_data, chip); 146d276907cSYing-Chun Liu (PaulLiu) 147d276907cSYing-Chun Liu (PaulLiu) free(pd); 148d276907cSYing-Chun Liu (PaulLiu) } 149d276907cSYing-Chun Liu (PaulLiu) 150d276907cSYing-Chun Liu (PaulLiu) static const struct serial_driver serial8250_driver = { 151d276907cSYing-Chun Liu (PaulLiu) .dev_alloc = serial8250_uart_dev_alloc, 152d276907cSYing-Chun Liu (PaulLiu) .dev_init = serial8250_uart_dev_init, 153d276907cSYing-Chun Liu (PaulLiu) .dev_free = serial8250_uart_dev_free, 154d276907cSYing-Chun Liu (PaulLiu) }; 155d276907cSYing-Chun Liu (PaulLiu) 156d276907cSYing-Chun Liu (PaulLiu) static const struct dt_device_match serial8250_match_table[] = { 157d276907cSYing-Chun Liu (PaulLiu) { .compatible = "snps,dw-apb-uart" }, 158d276907cSYing-Chun Liu (PaulLiu) { 0 } 159d276907cSYing-Chun Liu (PaulLiu) }; 160d276907cSYing-Chun Liu (PaulLiu) 16161bdedeaSJerome Forissier DEFINE_DT_DRIVER(serial8250_dt_driver) = { 162d276907cSYing-Chun Liu (PaulLiu) .name = "serial8250_uart", 1635e588771SClément Léger .type = DT_DRIVER_UART, 164d276907cSYing-Chun Liu (PaulLiu) .match_table = serial8250_match_table, 165d276907cSYing-Chun Liu (PaulLiu) .driver = &serial8250_driver, 166d276907cSYing-Chun Liu (PaulLiu) }; 167d276907cSYing-Chun Liu (PaulLiu) 168d276907cSYing-Chun Liu (PaulLiu) #endif /* CFG_DT */ 169