1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (c) 2021 Nuvoton Technology Corp. 4 */ 5 6#include <common.h> 7#include <clk.h> 8#include <dm.h> 9#include <serial.h> 10 11struct npcm_uart { 12 union { 13 u32 rbr; /* Receive Buffer Register */ 14 u32 thr; /* Transmit Holding Register */ 15 u32 dll; /* Divisor Latch (Low Byte) Register */ 16 }; 17 union { 18 u32 ier; /* Interrupt Enable Register */ 19 u32 dlm; /* Divisor Latch (Low Byte) Register */ 20 }; 21 union { 22 u32 iir; /* Interrupt Identification Register */ 23 u32 fcr; /* FIFO Control Register */ 24 }; 25 u32 lcr; /* Line Control Register */ 26 u32 mcr; /* Modem Control Register */ 27 u32 lsr; /* Line Status Control Register */ 28 u32 msr; /* Modem Status Register */ 29 u32 tor; /* Timeout Register */ 30}; 31 32#define LCR_WLS_8BITS 3 /* 8-bit word length select */ 33#define FCR_TFR BIT(2) /* TxFIFO reset */ 34#define FCR_RFR BIT(1) /* RxFIFO reset */ 35#define FCR_FME BIT(0) /* FIFO mode enable */ 36#define LSR_THRE BIT(5) /* Status of TxFIFO empty */ 37#define LSR_RFDR BIT(0) /* Status of RxFIFO data ready */ 38#define LCR_DLAB BIT(7) /* Divisor latch access bit */ 39 40struct npcm_serial_plat { 41 struct npcm_uart *reg; 42 u32 uart_clk; /* frequency of uart clock source */ 43}; 44 45static int npcm_serial_pending(struct udevice *dev, bool input) 46{ 47 struct npcm_serial_plat *plat = dev_get_plat(dev); 48 struct npcm_uart *uart = plat->reg; 49 50 if (input) 51 return readb(&uart->lsr) & LSR_RFDR ? 1 : 0; 52 else 53 return readb(&uart->lsr) & LSR_THRE ? 0 : 1; 54} 55 56static int npcm_serial_putc(struct udevice *dev, const char ch) 57{ 58 struct npcm_serial_plat *plat = dev_get_plat(dev); 59 struct npcm_uart *uart = plat->reg; 60 61 if (!(readb(&uart->lsr) & LSR_THRE)) 62 return -EAGAIN; 63 64 writeb(ch, &uart->thr); 65 66 return 0; 67} 68 69static int npcm_serial_getc(struct udevice *dev) 70{ 71 struct npcm_serial_plat *plat = dev_get_plat(dev); 72 struct npcm_uart *uart = plat->reg; 73 74 if (!(readb(&uart->lsr) & LSR_RFDR)) 75 return -EAGAIN; 76 77 return readb(&uart->rbr); 78} 79 80static int npcm_serial_setbrg(struct udevice *dev, int baudrate) 81{ 82 struct npcm_serial_plat *plat = dev_get_plat(dev); 83 struct npcm_uart *uart = plat->reg; 84 u16 divisor; 85 86 if (IS_ENABLED(CONFIG_SYS_SKIP_UART_INIT)) 87 return 0; 88 89 /* BaudOut = UART Clock / (16 * [Divisor + 2]) */ 90 divisor = DIV_ROUND_CLOSEST(plat->uart_clk, 16 * baudrate) - 2; 91 92 setbits_8(&uart->lcr, LCR_DLAB); 93 writeb(divisor & 0xff, &uart->dll); 94 writeb(divisor >> 8, &uart->dlm); 95 clrbits_8(&uart->lcr, LCR_DLAB); 96 97 return 0; 98} 99 100static int npcm_serial_probe(struct udevice *dev) 101{ 102 struct npcm_serial_plat *plat = dev_get_plat(dev); 103 struct npcm_uart *uart; 104 struct clk clk, parent; 105 u32 freq; 106 int ret; 107 108 plat->reg = dev_read_addr_ptr(dev); 109 uart = plat->reg; 110 111 if (!IS_ENABLED(CONFIG_SYS_SKIP_UART_INIT)) { 112 freq = dev_read_u32_default(dev, "clock-frequency", 24000000); 113 114 ret = clk_get_by_index(dev, 0, &clk); 115 if (ret < 0) 116 return ret; 117 118 ret = clk_get_by_index(dev, 1, &parent); 119 if (!ret) { 120 ret = clk_set_parent(&clk, &parent); 121 if (ret) 122 return ret; 123 } 124 125 if (freq) { 126 ret = clk_set_rate(&clk, freq); 127 if (ret < 0) 128 return ret; 129 } 130 plat->uart_clk = clk_get_rate(&clk); 131 } 132 133 /* Disable all interrupt */ 134 writeb(0, &uart->ier); 135 136 /* Set 8 bit, 1 stop, no parity */ 137 writeb(LCR_WLS_8BITS, &uart->lcr); 138 139 /* Reset RX/TX FIFO */ 140 writeb(FCR_FME | FCR_RFR | FCR_TFR, &uart->fcr); 141 142 return 0; 143} 144 145static const struct dm_serial_ops npcm_serial_ops = { 146 .getc = npcm_serial_getc, 147 .setbrg = npcm_serial_setbrg, 148 .putc = npcm_serial_putc, 149 .pending = npcm_serial_pending, 150}; 151 152static const struct udevice_id npcm_serial_ids[] = { 153 { .compatible = "nuvoton,npcm750-uart" }, 154 { .compatible = "nuvoton,npcm845-uart" }, 155 { } 156}; 157 158U_BOOT_DRIVER(serial_npcm) = { 159 .name = "serial_npcm", 160 .id = UCLASS_SERIAL, 161 .of_match = npcm_serial_ids, 162 .plat_auto = sizeof(struct npcm_serial_plat), 163 .probe = npcm_serial_probe, 164 .ops = &npcm_serial_ops, 165 .flags = DM_FLAG_PRE_RELOC, 166}; 167