1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2012-2015 Panasonic Corporation 4 * Copyright (C) 2015-2016 Socionext Inc. 5 * Author: Masahiro Yamada <yamada.masahiro@socionext.com> 6 */ 7 8#include <common.h> 9#include <dm.h> 10#include <linux/bitfield.h> 11#include <linux/bitops.h> 12#include <linux/bug.h> 13#include <linux/io.h> 14#include <linux/serial_reg.h> 15#include <linux/sizes.h> 16#include <linux/errno.h> 17#include <serial.h> 18#include <fdtdec.h> 19 20#define UNIPHIER_UART_REGSHIFT 2 21 22#define UNIPHIER_UART_RX (0 << (UNIPHIER_UART_REGSHIFT)) 23#define UNIPHIER_UART_TX UNIPHIER_UART_RX 24/* bit[15:8] = CHAR, bit[7:0] = FCR */ 25#define UNIPHIER_UART_CHAR_FCR (3 << (UNIPHIER_UART_REGSHIFT)) 26#define UNIPHIER_UART_FCR_MASK GENMASK(7, 0) 27/* bit[15:8] = LCR, bit[7:0] = MCR */ 28#define UNIPHIER_UART_LCR_MCR (4 << (UNIPHIER_UART_REGSHIFT)) 29#define UNIPHIER_UART_LCR_MASK GENMASK(15, 8) 30#define UNIPHIER_UART_LSR (5 << (UNIPHIER_UART_REGSHIFT)) 31/* Divisor Latch Register */ 32#define UNIPHIER_UART_DLR (9 << (UNIPHIER_UART_REGSHIFT)) 33 34struct uniphier_serial_priv { 35 void __iomem *membase; 36 unsigned int uartclk; 37}; 38 39static int uniphier_serial_setbrg(struct udevice *dev, int baudrate) 40{ 41 struct uniphier_serial_priv *priv = dev_get_priv(dev); 42 static const unsigned int mode_x_div = 16; 43 unsigned int divisor; 44 45 divisor = DIV_ROUND_CLOSEST(priv->uartclk, mode_x_div * baudrate); 46 47 /* flush the trasmitter before changing hw setting */ 48 while (!(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_TEMT)) 49 ; 50 51 writel(divisor, priv->membase + UNIPHIER_UART_DLR); 52 53 return 0; 54} 55 56static int uniphier_serial_getc(struct udevice *dev) 57{ 58 struct uniphier_serial_priv *priv = dev_get_priv(dev); 59 60 if (!(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_DR)) 61 return -EAGAIN; 62 63 return readl(priv->membase + UNIPHIER_UART_RX); 64} 65 66static int uniphier_serial_putc(struct udevice *dev, const char c) 67{ 68 struct uniphier_serial_priv *priv = dev_get_priv(dev); 69 70 if (!(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_THRE)) 71 return -EAGAIN; 72 73 writel(c, priv->membase + UNIPHIER_UART_TX); 74 75 return 0; 76} 77 78static int uniphier_serial_pending(struct udevice *dev, bool input) 79{ 80 struct uniphier_serial_priv *priv = dev_get_priv(dev); 81 82 if (input) 83 return readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_DR; 84 else 85 return !(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_THRE); 86} 87 88/* 89 * SPL does not have enough memory footprint for the clock driver. 90 * Hardcode clock frequency for each SoC. 91 */ 92struct uniphier_serial_clk_data { 93 const char *compatible; 94 unsigned int clk_rate; 95}; 96 97static const struct uniphier_serial_clk_data uniphier_serial_clk_data[] = { 98 { .compatible = "socionext,uniphier-ld4", .clk_rate = 36864000 }, 99 { .compatible = "socionext,uniphier-pro4", .clk_rate = 73728000 }, 100 { .compatible = "socionext,uniphier-sld8", .clk_rate = 80000000 }, 101 { .compatible = "socionext,uniphier-pro5", .clk_rate = 73728000 }, 102 { .compatible = "socionext,uniphier-pxs2", .clk_rate = 88888888 }, 103 { .compatible = "socionext,uniphier-ld6b", .clk_rate = 88888888 }, 104 { .compatible = "socionext,uniphier-ld11", .clk_rate = 58823529 }, 105 { .compatible = "socionext,uniphier-ld20", .clk_rate = 58823529 }, 106 { .compatible = "socionext,uniphier-pxs3", .clk_rate = 58823529 }, 107 { /* sentinel */ }, 108}; 109 110static int uniphier_serial_probe(struct udevice *dev) 111{ 112 struct uniphier_serial_priv *priv = dev_get_priv(dev); 113 const struct uniphier_serial_clk_data *clk_data; 114 ofnode root_node; 115 fdt_addr_t base; 116 u32 tmp; 117 118 base = dev_read_addr(dev); 119 if (base == FDT_ADDR_T_NONE) 120 return -EINVAL; 121 122 priv->membase = devm_ioremap(dev, base, SZ_64); 123 if (!priv->membase) 124 return -ENOMEM; 125 126 root_node = ofnode_path("/"); 127 clk_data = uniphier_serial_clk_data; 128 while (clk_data->compatible) { 129 if (ofnode_device_is_compatible(root_node, 130 clk_data->compatible)) 131 break; 132 clk_data++; 133 } 134 135 if (WARN_ON(!clk_data->compatible)) 136 return -ENOTSUPP; 137 138 priv->uartclk = clk_data->clk_rate; 139 140 /* flush the trasmitter before changing hw setting */ 141 while (!(readl(priv->membase + UNIPHIER_UART_LSR) & UART_LSR_TEMT)) 142 ; 143 144 /* enable FIFO */ 145 tmp = readl(priv->membase + UNIPHIER_UART_CHAR_FCR); 146 tmp &= ~UNIPHIER_UART_FCR_MASK; 147 tmp |= FIELD_PREP(UNIPHIER_UART_FCR_MASK, UART_FCR_ENABLE_FIFO); 148 writel(tmp, priv->membase + UNIPHIER_UART_CHAR_FCR); 149 150 tmp = readl(priv->membase + UNIPHIER_UART_LCR_MCR); 151 tmp &= ~UNIPHIER_UART_LCR_MASK; 152 tmp |= FIELD_PREP(UNIPHIER_UART_LCR_MASK, UART_LCR_WLEN8); 153 writel(tmp, priv->membase + UNIPHIER_UART_LCR_MCR); 154 155 return 0; 156} 157 158static const struct udevice_id uniphier_uart_of_match[] = { 159 { .compatible = "socionext,uniphier-uart" }, 160 { /* sentinel */ } 161}; 162 163static const struct dm_serial_ops uniphier_serial_ops = { 164 .setbrg = uniphier_serial_setbrg, 165 .getc = uniphier_serial_getc, 166 .putc = uniphier_serial_putc, 167 .pending = uniphier_serial_pending, 168}; 169 170U_BOOT_DRIVER(uniphier_serial) = { 171 .name = "uniphier-uart", 172 .id = UCLASS_SERIAL, 173 .of_match = uniphier_uart_of_match, 174 .probe = uniphier_serial_probe, 175 .priv_auto = sizeof(struct uniphier_serial_priv), 176 .ops = &uniphier_serial_ops, 177}; 178