Tom Rini | 10e4779 | 2018-05-06 17:58:06 -0400 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 2 | /* |
| 3 | * Qualcomm UART driver |
| 4 | * |
| 5 | * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> |
| 6 | * |
| 7 | * UART will work in Data Mover mode. |
| 8 | * Based on Linux driver. |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 9 | */ |
| 10 | |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 11 | #include <clk.h> |
| 12 | #include <dm.h> |
| 13 | #include <errno.h> |
Simon Glass | 9bc1564 | 2020-02-03 07:36:16 -0700 | [diff] [blame] | 14 | #include <malloc.h> |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 15 | #include <serial.h> |
| 16 | #include <watchdog.h> |
Simon Glass | 3ba929a | 2020-10-30 21:38:53 -0600 | [diff] [blame] | 17 | #include <asm/global_data.h> |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 18 | #include <asm/io.h> |
| 19 | #include <linux/compiler.h> |
Stephan Gerhold | 9a4569f | 2021-07-14 10:56:26 +0200 | [diff] [blame] | 20 | #include <linux/delay.h> |
Ramon Fried | 2292ed3 | 2018-05-16 12:13:42 +0300 | [diff] [blame] | 21 | #include <dm/pinctrl.h> |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 22 | |
| 23 | /* Serial registers - this driver works in uartdm mode*/ |
| 24 | |
| 25 | #define UARTDM_DMRX 0x34 /* Max RX transfer length */ |
Stephan Gerhold | 706d481 | 2021-06-28 10:40:09 +0200 | [diff] [blame] | 26 | #define UARTDM_DMEN 0x3C /* DMA/data-packing mode */ |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 27 | #define UARTDM_NCF_TX 0x40 /* Number of chars to TX */ |
| 28 | |
| 29 | #define UARTDM_RXFS 0x50 /* RX channel status register */ |
| 30 | #define UARTDM_RXFS_BUF_SHIFT 0x7 /* Number of bytes in the packing buffer */ |
| 31 | #define UARTDM_RXFS_BUF_MASK 0x7 |
Ramon Fried | 2292ed3 | 2018-05-16 12:13:42 +0300 | [diff] [blame] | 32 | #define UARTDM_MR1 0x00 |
| 33 | #define UARTDM_MR2 0x04 |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 34 | /* |
| 35 | * This is documented on page 1817 of the apq8016e technical reference manual. |
| 36 | * section 6.2.5.3.26 |
| 37 | * |
| 38 | * The upper nybble contains the bit clock divider for the RX pin, the lower |
| 39 | * nybble defines the TX pin. In almost all cases these should be the same value. |
| 40 | * |
| 41 | * The baud rate is the core clock frequency divided by the fixed divider value |
| 42 | * programmed into this register (defined in calc_csr_bitrate()). |
| 43 | */ |
Ramon Fried | 2292ed3 | 2018-05-16 12:13:42 +0300 | [diff] [blame] | 44 | #define UARTDM_CSR 0xA0 |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 45 | |
| 46 | #define UARTDM_SR 0xA4 /* Status register */ |
| 47 | #define UARTDM_SR_RX_READY (1 << 0) /* Word is the receiver FIFO */ |
| 48 | #define UARTDM_SR_TX_EMPTY (1 << 3) /* Transmitter underrun */ |
| 49 | #define UARTDM_SR_UART_OVERRUN (1 << 4) /* Receive overrun */ |
| 50 | |
| 51 | #define UARTDM_CR 0xA8 /* Command register */ |
| 52 | #define UARTDM_CR_CMD_RESET_ERR (3 << 4) /* Clear overrun error */ |
| 53 | #define UARTDM_CR_CMD_RESET_STALE_INT (8 << 4) /* Clears stale irq */ |
| 54 | #define UARTDM_CR_CMD_RESET_TX_READY (3 << 8) /* Clears TX Ready irq*/ |
| 55 | #define UARTDM_CR_CMD_FORCE_STALE (4 << 8) /* Causes stale event */ |
| 56 | #define UARTDM_CR_CMD_STALE_EVENT_DISABLE (6 << 8) /* Disable stale event */ |
| 57 | |
| 58 | #define UARTDM_IMR 0xB0 /* Interrupt mask register */ |
| 59 | #define UARTDM_ISR 0xB4 /* Interrupt status register */ |
| 60 | #define UARTDM_ISR_TX_READY 0x80 /* TX FIFO empty */ |
| 61 | |
| 62 | #define UARTDM_TF 0x100 /* UART Transmit FIFO register */ |
| 63 | #define UARTDM_RF 0x140 /* UART Receive FIFO register */ |
| 64 | |
Sumit Garg | 4f219db | 2024-04-12 15:24:34 +0530 | [diff] [blame] | 65 | #define MSM_BOOT_UART_DM_8_N_1_MODE 0x34 |
| 66 | #define MSM_BOOT_UART_DM_CMD_RESET_RX 0x10 |
| 67 | #define MSM_BOOT_UART_DM_CMD_RESET_TX 0x20 |
| 68 | #define MSM_UART_MR1_RX_RDY_CTL BIT(7) |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 69 | |
| 70 | DECLARE_GLOBAL_DATA_PTR; |
| 71 | |
| 72 | struct msm_serial_data { |
| 73 | phys_addr_t base; |
| 74 | unsigned chars_cnt; /* number of buffered chars */ |
| 75 | uint32_t chars_buf; /* buffered chars */ |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 76 | uint32_t clk_rate; /* core clock rate */ |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 77 | }; |
| 78 | |
| 79 | static int msm_serial_fetch(struct udevice *dev) |
| 80 | { |
| 81 | struct msm_serial_data *priv = dev_get_priv(dev); |
| 82 | unsigned sr; |
| 83 | |
| 84 | if (priv->chars_cnt) |
| 85 | return priv->chars_cnt; |
| 86 | |
| 87 | /* Clear error in case of buffer overrun */ |
| 88 | if (readl(priv->base + UARTDM_SR) & UARTDM_SR_UART_OVERRUN) |
| 89 | writel(UARTDM_CR_CMD_RESET_ERR, priv->base + UARTDM_CR); |
| 90 | |
| 91 | /* We need to fetch new character */ |
| 92 | sr = readl(priv->base + UARTDM_SR); |
| 93 | |
| 94 | if (sr & UARTDM_SR_RX_READY) { |
| 95 | /* There are at least 4 bytes in fifo */ |
| 96 | priv->chars_buf = readl(priv->base + UARTDM_RF); |
| 97 | priv->chars_cnt = 4; |
| 98 | } else { |
| 99 | /* Check if there is anything in fifo */ |
| 100 | priv->chars_cnt = readl(priv->base + UARTDM_RXFS); |
| 101 | /* Extract number of characters in UART packing buffer*/ |
| 102 | priv->chars_cnt = (priv->chars_cnt >> |
| 103 | UARTDM_RXFS_BUF_SHIFT) & |
| 104 | UARTDM_RXFS_BUF_MASK; |
| 105 | if (!priv->chars_cnt) |
| 106 | return 0; |
| 107 | |
| 108 | /* There is at least one charcter, move it to fifo */ |
| 109 | writel(UARTDM_CR_CMD_FORCE_STALE, |
| 110 | priv->base + UARTDM_CR); |
| 111 | |
| 112 | priv->chars_buf = readl(priv->base + UARTDM_RF); |
| 113 | writel(UARTDM_CR_CMD_RESET_STALE_INT, |
| 114 | priv->base + UARTDM_CR); |
| 115 | writel(0x7, priv->base + UARTDM_DMRX); |
| 116 | } |
| 117 | |
| 118 | return priv->chars_cnt; |
| 119 | } |
| 120 | |
| 121 | static int msm_serial_getc(struct udevice *dev) |
| 122 | { |
| 123 | struct msm_serial_data *priv = dev_get_priv(dev); |
| 124 | char c; |
| 125 | |
| 126 | if (!msm_serial_fetch(dev)) |
| 127 | return -EAGAIN; |
| 128 | |
| 129 | c = priv->chars_buf & 0xFF; |
| 130 | priv->chars_buf >>= 8; |
| 131 | priv->chars_cnt--; |
| 132 | |
| 133 | return c; |
| 134 | } |
| 135 | |
| 136 | static int msm_serial_putc(struct udevice *dev, const char ch) |
| 137 | { |
| 138 | struct msm_serial_data *priv = dev_get_priv(dev); |
| 139 | |
| 140 | if (!(readl(priv->base + UARTDM_SR) & UARTDM_SR_TX_EMPTY) && |
| 141 | !(readl(priv->base + UARTDM_ISR) & UARTDM_ISR_TX_READY)) |
| 142 | return -EAGAIN; |
| 143 | |
| 144 | writel(UARTDM_CR_CMD_RESET_TX_READY, priv->base + UARTDM_CR); |
| 145 | |
| 146 | writel(1, priv->base + UARTDM_NCF_TX); |
| 147 | writel(ch, priv->base + UARTDM_TF); |
| 148 | |
| 149 | return 0; |
| 150 | } |
| 151 | |
| 152 | static int msm_serial_pending(struct udevice *dev, bool input) |
| 153 | { |
| 154 | if (input) { |
| 155 | if (msm_serial_fetch(dev)) |
| 156 | return 1; |
| 157 | } |
| 158 | |
| 159 | return 0; |
| 160 | } |
| 161 | |
| 162 | static const struct dm_serial_ops msm_serial_ops = { |
| 163 | .putc = msm_serial_putc, |
| 164 | .pending = msm_serial_pending, |
| 165 | .getc = msm_serial_getc, |
| 166 | }; |
| 167 | |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 168 | static long msm_uart_clk_init(struct udevice *dev) |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 169 | { |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 170 | struct msm_serial_data *priv = dev_get_priv(dev); |
Stephen Warren | a962243 | 2016-06-17 09:44:00 -0600 | [diff] [blame] | 171 | struct clk clk; |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 172 | int ret; |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 173 | long rate; |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 174 | |
Caleb Connolly | 7a9c7b9 | 2024-02-26 17:26:12 +0000 | [diff] [blame] | 175 | ret = clk_get_by_name(dev, "core", &clk); |
| 176 | if (ret < 0) { |
| 177 | pr_warn("%s: Failed to get clock: %d\n", __func__, ret); |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 178 | return 0; |
Caleb Connolly | 7a9c7b9 | 2024-02-26 17:26:12 +0000 | [diff] [blame] | 179 | } |
Stephen Warren | a962243 | 2016-06-17 09:44:00 -0600 | [diff] [blame] | 180 | |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 181 | rate = clk_set_rate(&clk, priv->clk_rate); |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 182 | |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 183 | return rate; |
| 184 | } |
| 185 | |
| 186 | static int calc_csr_bitrate(struct msm_serial_data *priv) |
| 187 | { |
| 188 | /* This table is from the TRE. See the definition of UARTDM_CSR */ |
| 189 | unsigned int csr_div_table[] = {24576, 12288, 6144, 3072, 1536, 768, 512, 384, |
| 190 | 256, 192, 128, 96, 64, 48, 32, 16}; |
| 191 | int i = ARRAY_SIZE(csr_div_table) - 1; |
| 192 | /* Currently we only support one baudrate */ |
| 193 | int baud = 115200; |
| 194 | |
| 195 | for (; i >= 0; i--) { |
| 196 | int x = priv->clk_rate / csr_div_table[i]; |
| 197 | |
| 198 | if (x == baud) |
| 199 | /* Duplicate the configuration for RX |
| 200 | * as the lower nybble only configures TX |
| 201 | */ |
| 202 | return i + (i << 4); |
| 203 | } |
| 204 | |
| 205 | return -EINVAL; |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 206 | } |
| 207 | |
Ramon Fried | 2292ed3 | 2018-05-16 12:13:42 +0300 | [diff] [blame] | 208 | static void uart_dm_init(struct msm_serial_data *priv) |
| 209 | { |
Stephan Gerhold | 9a4569f | 2021-07-14 10:56:26 +0200 | [diff] [blame] | 210 | /* Delay initialization for a bit to let pins stabilize if necessary */ |
| 211 | mdelay(5); |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 212 | int bitrate = calc_csr_bitrate(priv); |
| 213 | if (bitrate < 0) { |
| 214 | log_warning("Couldn't calculate bit clock divider! Using default\n"); |
| 215 | /* This happens to be the value used on MSM8916 for the hardcoded clockrate |
| 216 | * in clock-apq8016. It's at least a better guess than a value we *know* |
| 217 | * is wrong... |
| 218 | */ |
| 219 | bitrate = 0xCC; |
| 220 | } |
Stephan Gerhold | 9a4569f | 2021-07-14 10:56:26 +0200 | [diff] [blame] | 221 | |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 222 | writel(bitrate, priv->base + UARTDM_CSR); |
Sumit Garg | 4f219db | 2024-04-12 15:24:34 +0530 | [diff] [blame] | 223 | /* Enable RS232 flow control to support RS232 db9 connector */ |
| 224 | writel(MSM_UART_MR1_RX_RDY_CTL, priv->base + UARTDM_MR1); |
Ramon Fried | 2292ed3 | 2018-05-16 12:13:42 +0300 | [diff] [blame] | 225 | writel(MSM_BOOT_UART_DM_8_N_1_MODE, priv->base + UARTDM_MR2); |
| 226 | writel(MSM_BOOT_UART_DM_CMD_RESET_RX, priv->base + UARTDM_CR); |
| 227 | writel(MSM_BOOT_UART_DM_CMD_RESET_TX, priv->base + UARTDM_CR); |
Stephan Gerhold | 706d481 | 2021-06-28 10:40:09 +0200 | [diff] [blame] | 228 | |
| 229 | /* Make sure BAM/single character mode is disabled */ |
| 230 | writel(0x0, priv->base + UARTDM_DMEN); |
Ramon Fried | 2292ed3 | 2018-05-16 12:13:42 +0300 | [diff] [blame] | 231 | } |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 232 | static int msm_serial_probe(struct udevice *dev) |
| 233 | { |
| 234 | struct msm_serial_data *priv = dev_get_priv(dev); |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 235 | long rate; |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 236 | |
Ramon Fried | 1f0f4a1 | 2018-05-16 12:13:38 +0300 | [diff] [blame] | 237 | /* No need to reinitialize the UART after relocation */ |
| 238 | if (gd->flags & GD_FLG_RELOC) |
| 239 | return 0; |
| 240 | |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 241 | rate = msm_uart_clk_init(dev); |
| 242 | if (rate < 0) |
| 243 | return rate; |
| 244 | if (!rate) { |
| 245 | log_err("Got core clock rate of 0... Please fix your clock driver\n"); |
| 246 | return -EINVAL; |
| 247 | } |
| 248 | |
| 249 | /* Update the clock rate to the actual programmed rate returned by the |
| 250 | * clock driver |
| 251 | */ |
| 252 | priv->clk_rate = rate; |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 253 | |
Ramon Fried | 2292ed3 | 2018-05-16 12:13:42 +0300 | [diff] [blame] | 254 | uart_dm_init(priv); |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 255 | |
| 256 | return 0; |
| 257 | } |
| 258 | |
Simon Glass | aad29ae | 2020-12-03 16:55:21 -0700 | [diff] [blame] | 259 | static int msm_serial_of_to_plat(struct udevice *dev) |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 260 | { |
| 261 | struct msm_serial_data *priv = dev_get_priv(dev); |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 262 | int ret; |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 263 | |
Masahiro Yamada | a89b4de | 2020-07-17 14:36:48 +0900 | [diff] [blame] | 264 | priv->base = dev_read_addr(dev); |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 265 | if (priv->base == FDT_ADDR_T_NONE) |
| 266 | return -EINVAL; |
| 267 | |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 268 | ret = dev_read_u32(dev, "clock-frequency", &priv->clk_rate); |
| 269 | if (ret < 0) { |
| 270 | log_debug("No clock frequency specified, using default rate\n"); |
| 271 | /* Default for APQ8016 */ |
| 272 | priv->clk_rate = 7372800; |
| 273 | } |
Robert Marko | 03a3602 | 2020-07-06 10:37:55 +0200 | [diff] [blame] | 274 | |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 275 | return 0; |
| 276 | } |
| 277 | |
| 278 | static const struct udevice_id msm_serial_ids[] = { |
| 279 | { .compatible = "qcom,msm-uartdm-v1.4" }, |
| 280 | { } |
| 281 | }; |
| 282 | |
| 283 | U_BOOT_DRIVER(serial_msm) = { |
| 284 | .name = "serial_msm", |
| 285 | .id = UCLASS_SERIAL, |
| 286 | .of_match = msm_serial_ids, |
Simon Glass | aad29ae | 2020-12-03 16:55:21 -0700 | [diff] [blame] | 287 | .of_to_plat = msm_serial_of_to_plat, |
Simon Glass | 8a2b47f | 2020-12-03 16:55:17 -0700 | [diff] [blame] | 288 | .priv_auto = sizeof(struct msm_serial_data), |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 289 | .probe = msm_serial_probe, |
| 290 | .ops = &msm_serial_ops, |
Caleb Connolly | 7a9c7b9 | 2024-02-26 17:26:12 +0000 | [diff] [blame] | 291 | .flags = DM_FLAG_PRE_RELOC, |
Mateusz Kulikowski | 8aac697 | 2016-03-31 23:12:14 +0200 | [diff] [blame] | 292 | }; |
Caleb Connolly | a86d23a | 2024-02-26 17:26:11 +0000 | [diff] [blame] | 293 | |
| 294 | #ifdef CONFIG_DEBUG_UART_MSM |
| 295 | |
| 296 | static struct msm_serial_data init_serial_data = { |
| 297 | .base = CONFIG_VAL(DEBUG_UART_BASE), |
Caleb Connolly | fb47e06 | 2024-04-15 16:03:40 +0100 | [diff] [blame] | 298 | .clk_rate = CONFIG_VAL(DEBUG_UART_CLOCK), |
Caleb Connolly | a86d23a | 2024-02-26 17:26:11 +0000 | [diff] [blame] | 299 | }; |
| 300 | |
| 301 | #include <debug_uart.h> |
| 302 | |
| 303 | /* Uncomment to turn on UART clocks when debugging U-Boot as aboot on MSM8916 */ |
Sumit Garg | bf06b69 | 2024-04-12 15:24:33 +0530 | [diff] [blame] | 304 | //int apq8016_clk_init_uart(phys_addr_t gcc_base, unsigned long id); |
Caleb Connolly | a86d23a | 2024-02-26 17:26:11 +0000 | [diff] [blame] | 305 | |
| 306 | static inline void _debug_uart_init(void) |
| 307 | { |
Sumit Garg | bf06b69 | 2024-04-12 15:24:33 +0530 | [diff] [blame] | 308 | /* |
| 309 | * Uncomment to turn on UART clocks when debugging U-Boot as aboot |
| 310 | * on MSM8916. Supported debug UART clock IDs: |
| 311 | * - db410c: GCC_BLSP1_UART2_APPS_CLK |
| 312 | * - HMIBSC: GCC_BLSP1_UART1_APPS_CLK |
| 313 | */ |
| 314 | //apq8016_clk_init_uart(0x1800000, <uart_clk_id>); |
Caleb Connolly | a86d23a | 2024-02-26 17:26:11 +0000 | [diff] [blame] | 315 | uart_dm_init(&init_serial_data); |
| 316 | } |
| 317 | |
| 318 | static inline void _debug_uart_putc(int ch) |
| 319 | { |
| 320 | struct msm_serial_data *priv = &init_serial_data; |
| 321 | |
| 322 | while (!(readl(priv->base + UARTDM_SR) & UARTDM_SR_TX_EMPTY) && |
| 323 | !(readl(priv->base + UARTDM_ISR) & UARTDM_ISR_TX_READY)) |
| 324 | ; |
| 325 | |
| 326 | writel(UARTDM_CR_CMD_RESET_TX_READY, priv->base + UARTDM_CR); |
| 327 | |
| 328 | writel(1, priv->base + UARTDM_NCF_TX); |
| 329 | writel(ch, priv->base + UARTDM_TF); |
| 330 | } |
| 331 | |
| 332 | DEBUG_UART_FUNCS |
| 333 | |
| 334 | #endif |