1 /* 2 * Copyright (C) 2012-2014 Panasonic Corporation 3 * Author: Masahiro Yamada <yamada.m@jp.panasonic.com> 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 8 #include <linux/serial_reg.h> 9 #include <asm/io.h> 10 #include <asm/errno.h> 11 #include <dm/device.h> 12 #include <dm/platform_data/serial-uniphier.h> 13 #include <serial.h> 14 #include <fdtdec.h> 15 16 #define UART_REG(x) \ 17 u8 x; \ 18 u8 postpad_##x[3]; 19 20 /* 21 * Note: Register map is slightly different from that of 16550. 22 */ 23 struct uniphier_serial { 24 UART_REG(rbr); /* 0x00 */ 25 UART_REG(ier); /* 0x04 */ 26 UART_REG(iir); /* 0x08 */ 27 UART_REG(fcr); /* 0x0c */ 28 u8 mcr; /* 0x10 */ 29 u8 lcr; 30 u16 __postpad; 31 UART_REG(lsr); /* 0x14 */ 32 UART_REG(msr); /* 0x18 */ 33 u32 __none1; 34 u32 __none2; 35 u16 dlr; 36 u16 __postpad2; 37 }; 38 39 #define thr rbr 40 41 struct uniphier_serial_private_data { 42 struct uniphier_serial __iomem *membase; 43 }; 44 45 #define uniphier_serial_port(dev) \ 46 ((struct uniphier_serial_private_data *)dev_get_priv(dev))->membase 47 48 static int uniphier_serial_setbrg(struct udevice *dev, int baudrate) 49 { 50 struct uniphier_serial_platform_data *plat = dev_get_platdata(dev); 51 struct uniphier_serial __iomem *port = uniphier_serial_port(dev); 52 const unsigned int mode_x_div = 16; 53 unsigned int divisor; 54 55 writeb(UART_LCR_WLEN8, &port->lcr); 56 57 divisor = DIV_ROUND_CLOSEST(plat->uartclk, mode_x_div * baudrate); 58 59 writew(divisor, &port->dlr); 60 61 return 0; 62 } 63 64 static int uniphier_serial_getc(struct udevice *dev) 65 { 66 struct uniphier_serial __iomem *port = uniphier_serial_port(dev); 67 68 if (!(readb(&port->lsr) & UART_LSR_DR)) 69 return -EAGAIN; 70 71 return readb(&port->rbr); 72 } 73 74 static int uniphier_serial_putc(struct udevice *dev, const char c) 75 { 76 struct uniphier_serial __iomem *port = uniphier_serial_port(dev); 77 78 if (!(readb(&port->lsr) & UART_LSR_THRE)) 79 return -EAGAIN; 80 81 writeb(c, &port->thr); 82 83 return 0; 84 } 85 86 static int uniphier_serial_pending(struct udevice *dev, bool input) 87 { 88 struct uniphier_serial __iomem *port = uniphier_serial_port(dev); 89 90 if (input) 91 return readb(&port->lsr) & UART_LSR_DR; 92 else 93 return !(readb(&port->lsr) & UART_LSR_THRE); 94 } 95 96 static int uniphier_serial_probe(struct udevice *dev) 97 { 98 struct uniphier_serial_private_data *priv = dev_get_priv(dev); 99 struct uniphier_serial_platform_data *plat = dev_get_platdata(dev); 100 101 priv->membase = map_sysmem(plat->base, sizeof(struct uniphier_serial)); 102 103 if (!priv->membase) 104 return -ENOMEM; 105 106 return 0; 107 } 108 109 static int uniphier_serial_remove(struct udevice *dev) 110 { 111 unmap_sysmem(uniphier_serial_port(dev)); 112 113 return 0; 114 } 115 116 #ifdef CONFIG_OF_CONTROL 117 static const struct udevice_id uniphier_uart_of_match[] = { 118 { .compatible = "panasonic,uniphier-uart" }, 119 {}, 120 }; 121 122 static int uniphier_serial_ofdata_to_platdata(struct udevice *dev) 123 { 124 struct uniphier_serial_platform_data *plat = dev_get_platdata(dev); 125 DECLARE_GLOBAL_DATA_PTR; 126 127 plat->base = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); 128 plat->uartclk = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 129 "clock-frequency", 0); 130 131 return 0; 132 } 133 #endif 134 135 static const struct dm_serial_ops uniphier_serial_ops = { 136 .setbrg = uniphier_serial_setbrg, 137 .getc = uniphier_serial_getc, 138 .putc = uniphier_serial_putc, 139 .pending = uniphier_serial_pending, 140 }; 141 142 U_BOOT_DRIVER(uniphier_serial) = { 143 .name = DRIVER_NAME, 144 .id = UCLASS_SERIAL, 145 .of_match = of_match_ptr(uniphier_uart_of_match), 146 .ofdata_to_platdata = of_match_ptr(uniphier_serial_ofdata_to_platdata), 147 .probe = uniphier_serial_probe, 148 .remove = uniphier_serial_remove, 149 .priv_auto_alloc_size = sizeof(struct uniphier_serial_private_data), 150 .platdata_auto_alloc_size = 151 sizeof(struct uniphier_serial_platform_data), 152 .ops = &uniphier_serial_ops, 153 .flags = DM_FLAG_PRE_RELOC, 154 }; 155