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