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_em.c (4150B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Renesas Emma Mobile 8250 driver
      4 *
      5 *  Copyright (C) 2012 Magnus Damm
      6 */
      7
      8#include <linux/device.h>
      9#include <linux/io.h>
     10#include <linux/module.h>
     11#include <linux/mod_devicetable.h>
     12#include <linux/serial_8250.h>
     13#include <linux/serial_reg.h>
     14#include <linux/platform_device.h>
     15#include <linux/clk.h>
     16#include <linux/slab.h>
     17
     18#include "8250.h"
     19
     20#define UART_DLL_EM 9
     21#define UART_DLM_EM 10
     22
     23struct serial8250_em_priv {
     24	struct clk *sclk;
     25	int line;
     26};
     27
     28static void serial8250_em_serial_out(struct uart_port *p, int offset, int value)
     29{
     30	switch (offset) {
     31	case UART_TX: /* TX @ 0x00 */
     32		writeb(value, p->membase);
     33		break;
     34	case UART_FCR: /* FCR @ 0x0c (+1) */
     35	case UART_LCR: /* LCR @ 0x10 (+1) */
     36	case UART_MCR: /* MCR @ 0x14 (+1) */
     37	case UART_SCR: /* SCR @ 0x20 (+1) */
     38		writel(value, p->membase + ((offset + 1) << 2));
     39		break;
     40	case UART_IER: /* IER @ 0x04 */
     41		value &= 0x0f; /* only 4 valid bits - not Xscale */
     42		fallthrough;
     43	case UART_DLL_EM: /* DLL @ 0x24 (+9) */
     44	case UART_DLM_EM: /* DLM @ 0x28 (+9) */
     45		writel(value, p->membase + (offset << 2));
     46	}
     47}
     48
     49static unsigned int serial8250_em_serial_in(struct uart_port *p, int offset)
     50{
     51	switch (offset) {
     52	case UART_RX: /* RX @ 0x00 */
     53		return readb(p->membase);
     54	case UART_MCR: /* MCR @ 0x14 (+1) */
     55	case UART_LSR: /* LSR @ 0x18 (+1) */
     56	case UART_MSR: /* MSR @ 0x1c (+1) */
     57	case UART_SCR: /* SCR @ 0x20 (+1) */
     58		return readl(p->membase + ((offset + 1) << 2));
     59	case UART_IER: /* IER @ 0x04 */
     60	case UART_IIR: /* IIR @ 0x08 */
     61	case UART_DLL_EM: /* DLL @ 0x24 (+9) */
     62	case UART_DLM_EM: /* DLM @ 0x28 (+9) */
     63		return readl(p->membase + (offset << 2));
     64	}
     65	return 0;
     66}
     67
     68static int serial8250_em_serial_dl_read(struct uart_8250_port *up)
     69{
     70	return serial_in(up, UART_DLL_EM) | serial_in(up, UART_DLM_EM) << 8;
     71}
     72
     73static void serial8250_em_serial_dl_write(struct uart_8250_port *up, int value)
     74{
     75	serial_out(up, UART_DLL_EM, value & 0xff);
     76	serial_out(up, UART_DLM_EM, value >> 8 & 0xff);
     77}
     78
     79static int serial8250_em_probe(struct platform_device *pdev)
     80{
     81	struct serial8250_em_priv *priv;
     82	struct uart_8250_port up;
     83	struct resource *regs;
     84	int irq, ret;
     85
     86	irq = platform_get_irq(pdev, 0);
     87	if (irq < 0)
     88		return irq;
     89
     90	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
     91	if (!regs) {
     92		dev_err(&pdev->dev, "missing registers\n");
     93		return -EINVAL;
     94	}
     95
     96	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
     97	if (!priv)
     98		return -ENOMEM;
     99
    100	priv->sclk = devm_clk_get(&pdev->dev, "sclk");
    101	if (IS_ERR(priv->sclk)) {
    102		dev_err(&pdev->dev, "unable to get clock\n");
    103		return PTR_ERR(priv->sclk);
    104	}
    105
    106	memset(&up, 0, sizeof(up));
    107	up.port.mapbase = regs->start;
    108	up.port.irq = irq;
    109	up.port.type = PORT_UNKNOWN;
    110	up.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_IOREMAP;
    111	up.port.dev = &pdev->dev;
    112	up.port.private_data = priv;
    113
    114	clk_prepare_enable(priv->sclk);
    115	up.port.uartclk = clk_get_rate(priv->sclk);
    116
    117	up.port.iotype = UPIO_MEM32;
    118	up.port.serial_in = serial8250_em_serial_in;
    119	up.port.serial_out = serial8250_em_serial_out;
    120	up.dl_read = serial8250_em_serial_dl_read;
    121	up.dl_write = serial8250_em_serial_dl_write;
    122
    123	ret = serial8250_register_8250_port(&up);
    124	if (ret < 0) {
    125		dev_err(&pdev->dev, "unable to register 8250 port\n");
    126		clk_disable_unprepare(priv->sclk);
    127		return ret;
    128	}
    129
    130	priv->line = ret;
    131	platform_set_drvdata(pdev, priv);
    132	return 0;
    133}
    134
    135static int serial8250_em_remove(struct platform_device *pdev)
    136{
    137	struct serial8250_em_priv *priv = platform_get_drvdata(pdev);
    138
    139	serial8250_unregister_port(priv->line);
    140	clk_disable_unprepare(priv->sclk);
    141	return 0;
    142}
    143
    144static const struct of_device_id serial8250_em_dt_ids[] = {
    145	{ .compatible = "renesas,em-uart", },
    146	{},
    147};
    148MODULE_DEVICE_TABLE(of, serial8250_em_dt_ids);
    149
    150static struct platform_driver serial8250_em_platform_driver = {
    151	.driver = {
    152		.name		= "serial8250-em",
    153		.of_match_table = serial8250_em_dt_ids,
    154	},
    155	.probe			= serial8250_em_probe,
    156	.remove			= serial8250_em_remove,
    157};
    158
    159module_platform_driver(serial8250_em_platform_driver);
    160
    161MODULE_AUTHOR("Magnus Damm");
    162MODULE_DESCRIPTION("Renesas Emma Mobile 8250 Driver");
    163MODULE_LICENSE("GPL v2");