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 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 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 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 /* 125 * TODO: Masahiro Yamada (yamada.m@jp.panasonic.com) 126 * 127 * Implement conversion code from DTB to platform data 128 * when supporting CONFIG_OF_CONTROL on UniPhir platform. 129 */ 130 } 131 #endif 132 133 static const struct dm_serial_ops uniphier_serial_ops = { 134 .setbrg = uniphier_serial_setbrg, 135 .getc = uniphier_serial_getc, 136 .putc = uniphier_serial_putc, 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