1 /* 2 * (C) Copyright 2000 3 * Rob Taylor, Flying Pig Systems. robt@flyingpig.com. 4 * 5 * (C) Copyright 2004 6 * ARM Ltd. 7 * Philippe Robin, <philippe.robin@arm.com> 8 * 9 * SPDX-License-Identifier: GPL-2.0+ 10 */ 11 12 /* Simple U-Boot driver for the PrimeCell PL010/PL011 UARTs */ 13 14 #include <common.h> 15 #include <dm.h> 16 #include <errno.h> 17 #include <watchdog.h> 18 #include <asm/io.h> 19 #include <serial.h> 20 #include <dm/platform_data/serial_pl01x.h> 21 #include <linux/compiler.h> 22 #include "serial_pl01x_internal.h" 23 24 #ifndef CONFIG_DM_SERIAL 25 26 static volatile unsigned char *const port[] = CONFIG_PL01x_PORTS; 27 static enum pl01x_type pl01x_type __attribute__ ((section(".data"))); 28 static struct pl01x_regs *base_regs __attribute__ ((section(".data"))); 29 #define NUM_PORTS (sizeof(port)/sizeof(port[0])) 30 31 DECLARE_GLOBAL_DATA_PTR; 32 #endif 33 34 static int pl01x_putc(struct pl01x_regs *regs, char c) 35 { 36 /* Wait until there is space in the FIFO */ 37 if (readl(®s->fr) & UART_PL01x_FR_TXFF) 38 return -EAGAIN; 39 40 /* Send the character */ 41 writel(c, ®s->dr); 42 43 return 0; 44 } 45 46 static int pl01x_getc(struct pl01x_regs *regs) 47 { 48 unsigned int data; 49 50 /* Wait until there is data in the FIFO */ 51 if (readl(®s->fr) & UART_PL01x_FR_RXFE) 52 return -EAGAIN; 53 54 data = readl(®s->dr); 55 56 /* Check for an error flag */ 57 if (data & 0xFFFFFF00) { 58 /* Clear the error */ 59 writel(0xFFFFFFFF, ®s->ecr); 60 return -1; 61 } 62 63 return (int) data; 64 } 65 66 static int pl01x_tstc(struct pl01x_regs *regs) 67 { 68 WATCHDOG_RESET(); 69 return !(readl(®s->fr) & UART_PL01x_FR_RXFE); 70 } 71 72 static int pl01x_generic_serial_init(struct pl01x_regs *regs, 73 enum pl01x_type type) 74 { 75 #ifdef CONFIG_PL011_SERIAL_FLUSH_ON_INIT 76 if (type == TYPE_PL011) { 77 /* Empty RX fifo if necessary */ 78 if (readl(®s->pl011_cr) & UART_PL011_CR_UARTEN) { 79 while (!(readl(®s->fr) & UART_PL01x_FR_RXFE)) 80 readl(®s->dr); 81 } 82 } 83 #endif 84 85 /* First, disable everything */ 86 writel(0, ®s->pl010_cr); 87 88 switch (type) { 89 case TYPE_PL010: 90 break; 91 case TYPE_PL011: 92 break; 93 default: 94 return -EINVAL; 95 } 96 97 return 0; 98 } 99 100 static int set_line_control(struct pl01x_regs *regs) 101 { 102 unsigned int lcr; 103 /* 104 * Internal update of baud rate register require line 105 * control register write 106 */ 107 lcr = UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN; 108 #ifdef CONFIG_PL011_SERIAL_RLCR 109 { 110 int i; 111 112 /* 113 * Program receive line control register after waiting 114 * 10 bus cycles. Delay be writing to readonly register 115 * 10 times 116 */ 117 for (i = 0; i < 10; i++) 118 writel(lcr, ®s->fr); 119 120 writel(lcr, ®s->pl011_rlcr); 121 } 122 #endif 123 writel(lcr, ®s->pl011_lcrh); 124 return 0; 125 } 126 127 static int pl01x_generic_setbrg(struct pl01x_regs *regs, enum pl01x_type type, 128 int clock, int baudrate) 129 { 130 switch (type) { 131 case TYPE_PL010: { 132 unsigned int divisor; 133 134 switch (baudrate) { 135 case 9600: 136 divisor = UART_PL010_BAUD_9600; 137 break; 138 case 19200: 139 divisor = UART_PL010_BAUD_9600; 140 break; 141 case 38400: 142 divisor = UART_PL010_BAUD_38400; 143 break; 144 case 57600: 145 divisor = UART_PL010_BAUD_57600; 146 break; 147 case 115200: 148 divisor = UART_PL010_BAUD_115200; 149 break; 150 default: 151 divisor = UART_PL010_BAUD_38400; 152 } 153 154 writel((divisor & 0xf00) >> 8, ®s->pl010_lcrm); 155 writel(divisor & 0xff, ®s->pl010_lcrl); 156 157 /* Finally, enable the UART */ 158 writel(UART_PL010_CR_UARTEN, ®s->pl010_cr); 159 break; 160 } 161 case TYPE_PL011: { 162 unsigned int temp; 163 unsigned int divider; 164 unsigned int remainder; 165 unsigned int fraction; 166 167 /* 168 * Set baud rate 169 * 170 * IBRD = UART_CLK / (16 * BAUD_RATE) 171 * FBRD = RND((64 * MOD(UART_CLK,(16 * BAUD_RATE))) 172 * / (16 * BAUD_RATE)) 173 */ 174 temp = 16 * baudrate; 175 divider = clock / temp; 176 remainder = clock % temp; 177 temp = (8 * remainder) / baudrate; 178 fraction = (temp >> 1) + (temp & 1); 179 180 writel(divider, ®s->pl011_ibrd); 181 writel(fraction, ®s->pl011_fbrd); 182 183 set_line_control(regs); 184 /* Finally, enable the UART */ 185 writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE | 186 UART_PL011_CR_RXE | UART_PL011_CR_RTS, ®s->pl011_cr); 187 break; 188 } 189 default: 190 return -EINVAL; 191 } 192 193 return 0; 194 } 195 196 #ifndef CONFIG_DM_SERIAL 197 static void pl01x_serial_init_baud(int baudrate) 198 { 199 int clock = 0; 200 201 #if defined(CONFIG_PL010_SERIAL) 202 pl01x_type = TYPE_PL010; 203 #elif defined(CONFIG_PL011_SERIAL) 204 pl01x_type = TYPE_PL011; 205 clock = CONFIG_PL011_CLOCK; 206 #endif 207 base_regs = (struct pl01x_regs *)port[CONFIG_CONS_INDEX]; 208 209 pl01x_generic_serial_init(base_regs, pl01x_type); 210 pl01x_generic_setbrg(base_regs, pl01x_type, clock, baudrate); 211 } 212 213 /* 214 * Integrator AP has two UARTs, we use the first one, at 38400-8-N-1 215 * Integrator CP has two UARTs, use the first one, at 38400-8-N-1 216 * Versatile PB has four UARTs. 217 */ 218 int pl01x_serial_init(void) 219 { 220 pl01x_serial_init_baud(CONFIG_BAUDRATE); 221 222 return 0; 223 } 224 225 static void pl01x_serial_putc(const char c) 226 { 227 if (c == '\n') 228 while (pl01x_putc(base_regs, '\r') == -EAGAIN); 229 230 while (pl01x_putc(base_regs, c) == -EAGAIN); 231 } 232 233 static int pl01x_serial_getc(void) 234 { 235 while (1) { 236 int ch = pl01x_getc(base_regs); 237 238 if (ch == -EAGAIN) { 239 WATCHDOG_RESET(); 240 continue; 241 } 242 243 return ch; 244 } 245 } 246 247 static int pl01x_serial_tstc(void) 248 { 249 return pl01x_tstc(base_regs); 250 } 251 252 static void pl01x_serial_setbrg(void) 253 { 254 /* 255 * Flush FIFO and wait for non-busy before changing baudrate to avoid 256 * crap in console 257 */ 258 while (!(readl(&base_regs->fr) & UART_PL01x_FR_TXFE)) 259 WATCHDOG_RESET(); 260 while (readl(&base_regs->fr) & UART_PL01x_FR_BUSY) 261 WATCHDOG_RESET(); 262 pl01x_serial_init_baud(gd->baudrate); 263 } 264 265 static struct serial_device pl01x_serial_drv = { 266 .name = "pl01x_serial", 267 .start = pl01x_serial_init, 268 .stop = NULL, 269 .setbrg = pl01x_serial_setbrg, 270 .putc = pl01x_serial_putc, 271 .puts = default_serial_puts, 272 .getc = pl01x_serial_getc, 273 .tstc = pl01x_serial_tstc, 274 }; 275 276 void pl01x_serial_initialize(void) 277 { 278 serial_register(&pl01x_serial_drv); 279 } 280 281 __weak struct serial_device *default_serial_console(void) 282 { 283 return &pl01x_serial_drv; 284 } 285 286 #endif /* nCONFIG_DM_SERIAL */ 287 288 #ifdef CONFIG_DM_SERIAL 289 290 struct pl01x_priv { 291 struct pl01x_regs *regs; 292 enum pl01x_type type; 293 }; 294 295 static int pl01x_serial_setbrg(struct udevice *dev, int baudrate) 296 { 297 struct pl01x_serial_platdata *plat = dev_get_platdata(dev); 298 struct pl01x_priv *priv = dev_get_priv(dev); 299 300 pl01x_generic_setbrg(priv->regs, priv->type, plat->clock, baudrate); 301 302 return 0; 303 } 304 305 static int pl01x_serial_probe(struct udevice *dev) 306 { 307 struct pl01x_serial_platdata *plat = dev_get_platdata(dev); 308 struct pl01x_priv *priv = dev_get_priv(dev); 309 310 priv->regs = (struct pl01x_regs *)plat->base; 311 priv->type = plat->type; 312 return pl01x_generic_serial_init(priv->regs, priv->type); 313 } 314 315 static int pl01x_serial_getc(struct udevice *dev) 316 { 317 struct pl01x_priv *priv = dev_get_priv(dev); 318 319 return pl01x_getc(priv->regs); 320 } 321 322 static int pl01x_serial_putc(struct udevice *dev, const char ch) 323 { 324 struct pl01x_priv *priv = dev_get_priv(dev); 325 326 return pl01x_putc(priv->regs, ch); 327 } 328 329 static int pl01x_serial_pending(struct udevice *dev, bool input) 330 { 331 struct pl01x_priv *priv = dev_get_priv(dev); 332 unsigned int fr = readl(&priv->regs->fr); 333 334 if (input) 335 return pl01x_tstc(priv->regs); 336 else 337 return fr & UART_PL01x_FR_TXFF ? 0 : 1; 338 } 339 340 static const struct dm_serial_ops pl01x_serial_ops = { 341 .putc = pl01x_serial_putc, 342 .pending = pl01x_serial_pending, 343 .getc = pl01x_serial_getc, 344 .setbrg = pl01x_serial_setbrg, 345 }; 346 347 U_BOOT_DRIVER(serial_pl01x) = { 348 .name = "serial_pl01x", 349 .id = UCLASS_SERIAL, 350 .probe = pl01x_serial_probe, 351 .ops = &pl01x_serial_ops, 352 .flags = DM_FLAG_PRE_RELOC, 353 }; 354 355 #endif 356