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