cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

8250_uniphier.c (7734B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
      4 */
      5
      6#include <linux/clk.h>
      7#include <linux/console.h>
      8#include <linux/io.h>
      9#include <linux/module.h>
     10#include <linux/of.h>
     11#include <linux/platform_device.h>
     12
     13#include "8250.h"
     14
     15/*
     16 * This hardware is similar to 8250, but its register map is a bit different:
     17 *   - MMIO32 (regshift = 2)
     18 *   - FCR is not at 2, but 3
     19 *   - LCR and MCR are not at 3 and 4, they share 4
     20 *   - No SCR (Instead, CHAR can be used as a scratch register)
     21 *   - Divisor latch at 9, no divisor latch access bit
     22 */
     23
     24#define UNIPHIER_UART_REGSHIFT		2
     25
     26/* bit[15:8] = CHAR, bit[7:0] = FCR */
     27#define UNIPHIER_UART_CHAR_FCR		(3 << (UNIPHIER_UART_REGSHIFT))
     28/* bit[15:8] = LCR, bit[7:0] = MCR */
     29#define UNIPHIER_UART_LCR_MCR		(4 << (UNIPHIER_UART_REGSHIFT))
     30/* Divisor Latch Register */
     31#define UNIPHIER_UART_DLR		(9 << (UNIPHIER_UART_REGSHIFT))
     32
     33struct uniphier8250_priv {
     34	int line;
     35	struct clk *clk;
     36	spinlock_t atomic_write_lock;
     37};
     38
     39#ifdef CONFIG_SERIAL_8250_CONSOLE
     40static int __init uniphier_early_console_setup(struct earlycon_device *device,
     41					       const char *options)
     42{
     43	if (!device->port.membase)
     44		return -ENODEV;
     45
     46	/* This hardware always expects MMIO32 register interface. */
     47	device->port.iotype = UPIO_MEM32;
     48	device->port.regshift = UNIPHIER_UART_REGSHIFT;
     49
     50	/*
     51	 * Do not touch the divisor register in early_serial8250_setup();
     52	 * we assume it has been initialized by a boot loader.
     53	 */
     54	device->baud = 0;
     55
     56	return early_serial8250_setup(device, options);
     57}
     58OF_EARLYCON_DECLARE(uniphier, "socionext,uniphier-uart",
     59		    uniphier_early_console_setup);
     60#endif
     61
     62/*
     63 * The register map is slightly different from that of 8250.
     64 * IO callbacks must be overridden for correct access to FCR, LCR, MCR and SCR.
     65 */
     66static unsigned int uniphier_serial_in(struct uart_port *p, int offset)
     67{
     68	unsigned int valshift = 0;
     69
     70	switch (offset) {
     71	case UART_SCR:
     72		/* No SCR for this hardware.  Use CHAR as a scratch register */
     73		valshift = 8;
     74		offset = UNIPHIER_UART_CHAR_FCR;
     75		break;
     76	case UART_LCR:
     77		valshift = 8;
     78		fallthrough;
     79	case UART_MCR:
     80		offset = UNIPHIER_UART_LCR_MCR;
     81		break;
     82	default:
     83		offset <<= UNIPHIER_UART_REGSHIFT;
     84		break;
     85	}
     86
     87	/*
     88	 * The return value must be masked with 0xff because some registers
     89	 * share the same offset that must be accessed by 32-bit write/read.
     90	 * 8 or 16 bit access to this hardware result in unexpected behavior.
     91	 */
     92	return (readl(p->membase + offset) >> valshift) & 0xff;
     93}
     94
     95static void uniphier_serial_out(struct uart_port *p, int offset, int value)
     96{
     97	unsigned int valshift = 0;
     98	bool normal = false;
     99
    100	switch (offset) {
    101	case UART_SCR:
    102		/* No SCR for this hardware.  Use CHAR as a scratch register */
    103		valshift = 8;
    104		fallthrough;
    105	case UART_FCR:
    106		offset = UNIPHIER_UART_CHAR_FCR;
    107		break;
    108	case UART_LCR:
    109		valshift = 8;
    110		/* Divisor latch access bit does not exist. */
    111		value &= ~UART_LCR_DLAB;
    112		fallthrough;
    113	case UART_MCR:
    114		offset = UNIPHIER_UART_LCR_MCR;
    115		break;
    116	default:
    117		offset <<= UNIPHIER_UART_REGSHIFT;
    118		normal = true;
    119		break;
    120	}
    121
    122	if (normal) {
    123		writel(value, p->membase + offset);
    124	} else {
    125		/*
    126		 * Special case: two registers share the same address that
    127		 * must be 32-bit accessed.  As this is not longer atomic safe,
    128		 * take a lock just in case.
    129		 */
    130		struct uniphier8250_priv *priv = p->private_data;
    131		unsigned long flags;
    132		u32 tmp;
    133
    134		spin_lock_irqsave(&priv->atomic_write_lock, flags);
    135		tmp = readl(p->membase + offset);
    136		tmp &= ~(0xff << valshift);
    137		tmp |= value << valshift;
    138		writel(tmp, p->membase + offset);
    139		spin_unlock_irqrestore(&priv->atomic_write_lock, flags);
    140	}
    141}
    142
    143/*
    144 * This hardware does not have the divisor latch access bit.
    145 * The divisor latch register exists at different address.
    146 * Override dl_read/write callbacks.
    147 */
    148static int uniphier_serial_dl_read(struct uart_8250_port *up)
    149{
    150	return readl(up->port.membase + UNIPHIER_UART_DLR);
    151}
    152
    153static void uniphier_serial_dl_write(struct uart_8250_port *up, int value)
    154{
    155	writel(value, up->port.membase + UNIPHIER_UART_DLR);
    156}
    157
    158static int uniphier_uart_probe(struct platform_device *pdev)
    159{
    160	struct device *dev = &pdev->dev;
    161	struct uart_8250_port up;
    162	struct uniphier8250_priv *priv;
    163	struct resource *regs;
    164	void __iomem *membase;
    165	int irq;
    166	int ret;
    167
    168	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    169	if (!regs) {
    170		dev_err(dev, "failed to get memory resource\n");
    171		return -EINVAL;
    172	}
    173
    174	membase = devm_ioremap(dev, regs->start, resource_size(regs));
    175	if (!membase)
    176		return -ENOMEM;
    177
    178	irq = platform_get_irq(pdev, 0);
    179	if (irq < 0)
    180		return irq;
    181
    182	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    183	if (!priv)
    184		return -ENOMEM;
    185
    186	memset(&up, 0, sizeof(up));
    187
    188	ret = of_alias_get_id(dev->of_node, "serial");
    189	if (ret < 0) {
    190		dev_err(dev, "failed to get alias id\n");
    191		return ret;
    192	}
    193	up.port.line = ret;
    194
    195	priv->clk = devm_clk_get(dev, NULL);
    196	if (IS_ERR(priv->clk)) {
    197		dev_err(dev, "failed to get clock\n");
    198		return PTR_ERR(priv->clk);
    199	}
    200
    201	ret = clk_prepare_enable(priv->clk);
    202	if (ret)
    203		return ret;
    204
    205	up.port.uartclk = clk_get_rate(priv->clk);
    206
    207	spin_lock_init(&priv->atomic_write_lock);
    208
    209	up.port.dev = dev;
    210	up.port.private_data = priv;
    211	up.port.mapbase = regs->start;
    212	up.port.mapsize = resource_size(regs);
    213	up.port.membase = membase;
    214	up.port.irq = irq;
    215
    216	up.port.type = PORT_16550A;
    217	up.port.iotype = UPIO_MEM32;
    218	up.port.fifosize = 64;
    219	up.port.regshift = UNIPHIER_UART_REGSHIFT;
    220	up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE;
    221	up.capabilities = UART_CAP_FIFO;
    222
    223	if (of_property_read_bool(dev->of_node, "auto-flow-control"))
    224		up.capabilities |= UART_CAP_AFE;
    225
    226	up.port.serial_in = uniphier_serial_in;
    227	up.port.serial_out = uniphier_serial_out;
    228	up.dl_read = uniphier_serial_dl_read;
    229	up.dl_write = uniphier_serial_dl_write;
    230
    231	ret = serial8250_register_8250_port(&up);
    232	if (ret < 0) {
    233		dev_err(dev, "failed to register 8250 port\n");
    234		clk_disable_unprepare(priv->clk);
    235		return ret;
    236	}
    237	priv->line = ret;
    238
    239	platform_set_drvdata(pdev, priv);
    240
    241	return 0;
    242}
    243
    244static int uniphier_uart_remove(struct platform_device *pdev)
    245{
    246	struct uniphier8250_priv *priv = platform_get_drvdata(pdev);
    247
    248	serial8250_unregister_port(priv->line);
    249	clk_disable_unprepare(priv->clk);
    250
    251	return 0;
    252}
    253
    254static int __maybe_unused uniphier_uart_suspend(struct device *dev)
    255{
    256	struct uniphier8250_priv *priv = dev_get_drvdata(dev);
    257	struct uart_8250_port *up = serial8250_get_port(priv->line);
    258
    259	serial8250_suspend_port(priv->line);
    260
    261	if (!uart_console(&up->port) || console_suspend_enabled)
    262		clk_disable_unprepare(priv->clk);
    263
    264	return 0;
    265}
    266
    267static int __maybe_unused uniphier_uart_resume(struct device *dev)
    268{
    269	struct uniphier8250_priv *priv = dev_get_drvdata(dev);
    270	struct uart_8250_port *up = serial8250_get_port(priv->line);
    271	int ret;
    272
    273	if (!uart_console(&up->port) || console_suspend_enabled) {
    274		ret = clk_prepare_enable(priv->clk);
    275		if (ret)
    276			return ret;
    277	}
    278
    279	serial8250_resume_port(priv->line);
    280
    281	return 0;
    282}
    283
    284static const struct dev_pm_ops uniphier_uart_pm_ops = {
    285	SET_SYSTEM_SLEEP_PM_OPS(uniphier_uart_suspend, uniphier_uart_resume)
    286};
    287
    288static const struct of_device_id uniphier_uart_match[] = {
    289	{ .compatible = "socionext,uniphier-uart" },
    290	{ /* sentinel */ }
    291};
    292MODULE_DEVICE_TABLE(of, uniphier_uart_match);
    293
    294static struct platform_driver uniphier_uart_platform_driver = {
    295	.probe		= uniphier_uart_probe,
    296	.remove		= uniphier_uart_remove,
    297	.driver = {
    298		.name	= "uniphier-uart",
    299		.of_match_table = uniphier_uart_match,
    300		.pm = &uniphier_uart_pm_ops,
    301	},
    302};
    303module_platform_driver(uniphier_uart_platform_driver);
    304
    305MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
    306MODULE_DESCRIPTION("UniPhier UART driver");
    307MODULE_LICENSE("GPL");