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 <common.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 /* 41 * These are the definitions for the Line Control Register 42 */ 43 #define UART_LCR_WLS_8 0x03 /* 8 bit character length */ 44 45 /* 46 * These are the definitions for the Line Status Register 47 */ 48 #define UART_LSR_DR 0x01 /* Data ready */ 49 #define UART_LSR_THRE 0x20 /* Xmit holding register empty */ 50 51 struct uniphier_serial_private_data { 52 struct uniphier_serial __iomem *membase; 53 }; 54 55 #define uniphier_serial_port(dev) \ 56 ((struct uniphier_serial_private_data *)dev_get_priv(dev))->membase 57 58 static int uniphier_serial_setbrg(struct udevice *dev, int baudrate) 59 { 60 struct uniphier_serial_platform_data *plat = dev_get_platdata(dev); 61 struct uniphier_serial __iomem *port = uniphier_serial_port(dev); 62 const unsigned int mode_x_div = 16; 63 unsigned int divisor; 64 65 writeb(UART_LCR_WLS_8, &port->lcr); 66 67 divisor = DIV_ROUND_CLOSEST(plat->uartclk, mode_x_div * baudrate); 68 69 writew(divisor, &port->dlr); 70 71 return 0; 72 } 73 74 static int uniphier_serial_getc(struct udevice *dev) 75 { 76 struct uniphier_serial __iomem *port = uniphier_serial_port(dev); 77 78 if (!(readb(&port->lsr) & UART_LSR_DR)) 79 return -EAGAIN; 80 81 return readb(&port->rbr); 82 } 83 84 static int uniphier_serial_putc(struct udevice *dev, const char c) 85 { 86 struct uniphier_serial __iomem *port = uniphier_serial_port(dev); 87 88 if (!(readb(&port->lsr) & UART_LSR_THRE)) 89 return -EAGAIN; 90 91 writeb(c, &port->thr); 92 93 return 0; 94 } 95 96 static int uniphier_serial_pending(struct udevice *dev, bool input) 97 { 98 struct uniphier_serial __iomem *port = uniphier_serial_port(dev); 99 100 if (input) 101 return readb(&port->lsr) & UART_LSR_DR; 102 else 103 return !(readb(&port->lsr) & UART_LSR_THRE); 104 } 105 106 static int uniphier_serial_probe(struct udevice *dev) 107 { 108 struct uniphier_serial_private_data *priv = dev_get_priv(dev); 109 struct uniphier_serial_platform_data *plat = dev_get_platdata(dev); 110 111 priv->membase = map_sysmem(plat->base, sizeof(struct uniphier_serial)); 112 113 if (!priv->membase) 114 return -ENOMEM; 115 116 return 0; 117 } 118 119 static int uniphier_serial_remove(struct udevice *dev) 120 { 121 unmap_sysmem(uniphier_serial_port(dev)); 122 123 return 0; 124 } 125 126 #ifdef CONFIG_OF_CONTROL 127 static const struct udevice_id uniphier_uart_of_match = { 128 { .compatible = "panasonic,uniphier-uart"}, 129 {}, 130 }; 131 132 static int uniphier_serial_ofdata_to_platdata(struct udevice *dev) 133 { 134 /* 135 * TODO: Masahiro Yamada (yamada.m@jp.panasonic.com) 136 * 137 * Implement conversion code from DTB to platform data 138 * when supporting CONFIG_OF_CONTROL on UniPhir platform. 139 */ 140 } 141 #endif 142 143 static const struct dm_serial_ops uniphier_serial_ops = { 144 .setbrg = uniphier_serial_setbrg, 145 .getc = uniphier_serial_getc, 146 .putc = uniphier_serial_putc, 147 .pending = uniphier_serial_pending, 148 }; 149 150 U_BOOT_DRIVER(uniphier_serial) = { 151 .name = DRIVER_NAME, 152 .id = UCLASS_SERIAL, 153 .of_match = of_match_ptr(uniphier_uart_of_match), 154 .ofdata_to_platdata = of_match_ptr(uniphier_serial_ofdata_to_platdata), 155 .probe = uniphier_serial_probe, 156 .remove = uniphier_serial_remove, 157 .priv_auto_alloc_size = sizeof(struct uniphier_serial_private_data), 158 .platdata_auto_alloc_size = 159 sizeof(struct uniphier_serial_platform_data), 160 .ops = &uniphier_serial_ops, 161 .flags = DM_FLAG_PRE_RELOC, 162 }; 163