| /* |
| * SPDX-License-Identifier: BSD-2-Clause |
| * |
| * Copyright (c) 2019 Western Digital Corporation or its affiliates. |
| * |
| * Authors: |
| * Anup Patel <anup.patel@wdc.com> |
| */ |
| |
| #include <sbi/riscv_asm.h> |
| #include <sbi/riscv_io.h> |
| #include <sbi/sbi_console.h> |
| #include <sbi/sbi_domain.h> |
| #include <sbi_utils/serial/uart8250.h> |
| |
| /* clang-format off */ |
| |
| #define UART_RBR_OFFSET 0 /* In: Recieve Buffer Register */ |
| #define UART_THR_OFFSET 0 /* Out: Transmitter Holding Register */ |
| #define UART_DLL_OFFSET 0 /* Out: Divisor Latch Low */ |
| #define UART_IER_OFFSET 1 /* I/O: Interrupt Enable Register */ |
| #define UART_DLM_OFFSET 1 /* Out: Divisor Latch High */ |
| #define UART_FCR_OFFSET 2 /* Out: FIFO Control Register */ |
| #define UART_IIR_OFFSET 2 /* I/O: Interrupt Identification Register */ |
| #define UART_LCR_OFFSET 3 /* Out: Line Control Register */ |
| #define UART_MCR_OFFSET 4 /* Out: Modem Control Register */ |
| #define UART_LSR_OFFSET 5 /* In: Line Status Register */ |
| #define UART_MSR_OFFSET 6 /* In: Modem Status Register */ |
| #define UART_SCR_OFFSET 7 /* I/O: Scratch Register */ |
| #define UART_MDR1_OFFSET 8 /* I/O: Mode Register */ |
| |
| #define UART_LSR_FIFOE 0x80 /* Fifo error */ |
| #define UART_LSR_TEMT 0x40 /* Transmitter empty */ |
| #define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ |
| #define UART_LSR_BI 0x10 /* Break interrupt indicator */ |
| #define UART_LSR_FE 0x08 /* Frame error indicator */ |
| #define UART_LSR_PE 0x04 /* Parity error indicator */ |
| #define UART_LSR_OE 0x02 /* Overrun error indicator */ |
| #define UART_LSR_DR 0x01 /* Receiver data ready */ |
| #define UART_LSR_BRK_ERROR_BITS 0x1E /* BI, FE, PE, OE bits */ |
| |
| /* clang-format on */ |
| |
| static volatile char *uart8250_base; |
| static u32 uart8250_in_freq; |
| static u32 uart8250_baudrate; |
| static u32 uart8250_reg_width; |
| static u32 uart8250_reg_shift; |
| |
| static u32 get_reg(u32 num) |
| { |
| u32 offset = num << uart8250_reg_shift; |
| |
| if (uart8250_reg_width == 1) |
| return readb(uart8250_base + offset); |
| else if (uart8250_reg_width == 2) |
| return readw(uart8250_base + offset); |
| else |
| return readl(uart8250_base + offset); |
| } |
| |
| static void set_reg(u32 num, u32 val) |
| { |
| u32 offset = num << uart8250_reg_shift; |
| |
| if (uart8250_reg_width == 1) |
| writeb(val, uart8250_base + offset); |
| else if (uart8250_reg_width == 2) |
| writew(val, uart8250_base + offset); |
| else |
| writel(val, uart8250_base + offset); |
| } |
| |
| static void uart8250_putc(char ch) |
| { |
| while ((get_reg(UART_LSR_OFFSET) & UART_LSR_THRE) == 0) |
| ; |
| |
| set_reg(UART_THR_OFFSET, ch); |
| } |
| |
| static int uart8250_getc(void) |
| { |
| if (get_reg(UART_LSR_OFFSET) & UART_LSR_DR) |
| return get_reg(UART_RBR_OFFSET); |
| return -1; |
| } |
| |
| static struct sbi_console_device uart8250_console = { |
| .name = "uart8250", |
| .console_putc = uart8250_putc, |
| .console_getc = uart8250_getc |
| }; |
| |
| int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift, |
| u32 reg_width, u32 reg_offset) |
| { |
| u16 bdiv = 0; |
| |
| uart8250_base = (volatile char *)base + reg_offset; |
| uart8250_reg_shift = reg_shift; |
| uart8250_reg_width = reg_width; |
| uart8250_in_freq = in_freq; |
| uart8250_baudrate = baudrate; |
| |
| if (uart8250_baudrate) { |
| bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) / |
| (16 * uart8250_baudrate); |
| } |
| |
| /* Disable all interrupts */ |
| set_reg(UART_IER_OFFSET, 0x00); |
| /* Enable DLAB */ |
| set_reg(UART_LCR_OFFSET, 0x80); |
| |
| if (bdiv) { |
| /* Set divisor low byte */ |
| set_reg(UART_DLL_OFFSET, bdiv & 0xff); |
| /* Set divisor high byte */ |
| set_reg(UART_DLM_OFFSET, (bdiv >> 8) & 0xff); |
| } |
| |
| /* 8 bits, no parity, one stop bit */ |
| set_reg(UART_LCR_OFFSET, 0x03); |
| /* Enable FIFO */ |
| set_reg(UART_FCR_OFFSET, 0x01); |
| /* No modem control DTR RTS */ |
| set_reg(UART_MCR_OFFSET, 0x00); |
| /* Clear line status */ |
| get_reg(UART_LSR_OFFSET); |
| /* Read receive buffer */ |
| get_reg(UART_RBR_OFFSET); |
| /* Set scratchpad */ |
| set_reg(UART_SCR_OFFSET, 0x00); |
| |
| sbi_console_set_device(&uart8250_console); |
| |
| return sbi_domain_root_add_memrange(base, PAGE_SIZE, PAGE_SIZE, |
| (SBI_DOMAIN_MEMREGION_MMIO | |
| SBI_DOMAIN_MEMREGION_SHARED_SURW_MRW)); |
| } |