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

arc_ps2.c (6351B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
      4 *
      5 * Driver is originally developed by Pavel Sokolov <psokolov@synopsys.com>
      6 */
      7
      8#include <linux/err.h>
      9#include <linux/module.h>
     10#include <linux/interrupt.h>
     11#include <linux/input.h>
     12#include <linux/serio.h>
     13#include <linux/platform_device.h>
     14#include <linux/of.h>
     15#include <linux/io.h>
     16#include <linux/kernel.h>
     17#include <linux/slab.h>
     18
     19#define ARC_PS2_PORTS                   2
     20
     21#define ARC_ARC_PS2_ID                  0x0001f609
     22
     23#define STAT_TIMEOUT                    128
     24
     25#define PS2_STAT_RX_FRM_ERR             (1)
     26#define PS2_STAT_RX_BUF_OVER            (1 << 1)
     27#define PS2_STAT_RX_INT_EN              (1 << 2)
     28#define PS2_STAT_RX_VAL                 (1 << 3)
     29#define PS2_STAT_TX_ISNOT_FUL           (1 << 4)
     30#define PS2_STAT_TX_INT_EN              (1 << 5)
     31
     32struct arc_ps2_port {
     33	void __iomem *data_addr;
     34	void __iomem *status_addr;
     35	struct serio *io;
     36};
     37
     38struct arc_ps2_data {
     39	struct arc_ps2_port port[ARC_PS2_PORTS];
     40	void __iomem *addr;
     41	unsigned int frame_error;
     42	unsigned int buf_overflow;
     43	unsigned int total_int;
     44};
     45
     46static void arc_ps2_check_rx(struct arc_ps2_data *arc_ps2,
     47			     struct arc_ps2_port *port)
     48{
     49	unsigned int timeout = 1000;
     50	unsigned int flag, status;
     51	unsigned char data;
     52
     53	do {
     54		status = ioread32(port->status_addr);
     55		if (!(status & PS2_STAT_RX_VAL))
     56			return;
     57
     58		data = ioread32(port->data_addr) & 0xff;
     59
     60		flag = 0;
     61		arc_ps2->total_int++;
     62		if (status & PS2_STAT_RX_FRM_ERR) {
     63			arc_ps2->frame_error++;
     64			flag |= SERIO_PARITY;
     65		} else if (status & PS2_STAT_RX_BUF_OVER) {
     66			arc_ps2->buf_overflow++;
     67			flag |= SERIO_FRAME;
     68		}
     69
     70		serio_interrupt(port->io, data, flag);
     71	} while (--timeout);
     72
     73	dev_err(&port->io->dev, "PS/2 hardware stuck\n");
     74}
     75
     76static irqreturn_t arc_ps2_interrupt(int irq, void *dev)
     77{
     78	struct arc_ps2_data *arc_ps2 = dev;
     79	int i;
     80
     81	for (i = 0; i < ARC_PS2_PORTS; i++)
     82		arc_ps2_check_rx(arc_ps2, &arc_ps2->port[i]);
     83
     84	return IRQ_HANDLED;
     85}
     86
     87static int arc_ps2_write(struct serio *io, unsigned char val)
     88{
     89	unsigned status;
     90	struct arc_ps2_port *port = io->port_data;
     91	int timeout = STAT_TIMEOUT;
     92
     93	do {
     94		status = ioread32(port->status_addr);
     95		cpu_relax();
     96
     97		if (status & PS2_STAT_TX_ISNOT_FUL) {
     98			iowrite32(val & 0xff, port->data_addr);
     99			return 0;
    100		}
    101
    102	} while (--timeout);
    103
    104	dev_err(&io->dev, "write timeout\n");
    105	return -ETIMEDOUT;
    106}
    107
    108static int arc_ps2_open(struct serio *io)
    109{
    110	struct arc_ps2_port *port = io->port_data;
    111
    112	iowrite32(PS2_STAT_RX_INT_EN, port->status_addr);
    113
    114	return 0;
    115}
    116
    117static void arc_ps2_close(struct serio *io)
    118{
    119	struct arc_ps2_port *port = io->port_data;
    120
    121	iowrite32(ioread32(port->status_addr) & ~PS2_STAT_RX_INT_EN,
    122		  port->status_addr);
    123}
    124
    125static void __iomem *arc_ps2_calc_addr(struct arc_ps2_data *arc_ps2,
    126						  int index, bool status)
    127{
    128	void __iomem *addr;
    129
    130	addr = arc_ps2->addr + 4 + 4 * index;
    131	if (status)
    132		addr += ARC_PS2_PORTS * 4;
    133
    134	return addr;
    135}
    136
    137static void arc_ps2_inhibit_ports(struct arc_ps2_data *arc_ps2)
    138{
    139	void __iomem *addr;
    140	u32 val;
    141	int i;
    142
    143	for (i = 0; i < ARC_PS2_PORTS; i++) {
    144		addr = arc_ps2_calc_addr(arc_ps2, i, true);
    145		val = ioread32(addr);
    146		val &= ~(PS2_STAT_RX_INT_EN | PS2_STAT_TX_INT_EN);
    147		iowrite32(val, addr);
    148	}
    149}
    150
    151static int arc_ps2_create_port(struct platform_device *pdev,
    152					 struct arc_ps2_data *arc_ps2,
    153					 int index)
    154{
    155	struct arc_ps2_port *port = &arc_ps2->port[index];
    156	struct serio *io;
    157
    158	io = kzalloc(sizeof(struct serio), GFP_KERNEL);
    159	if (!io)
    160		return -ENOMEM;
    161
    162	io->id.type = SERIO_8042;
    163	io->write = arc_ps2_write;
    164	io->open = arc_ps2_open;
    165	io->close = arc_ps2_close;
    166	snprintf(io->name, sizeof(io->name), "ARC PS/2 port%d", index);
    167	snprintf(io->phys, sizeof(io->phys), "arc/serio%d", index);
    168	io->port_data = port;
    169
    170	port->io = io;
    171
    172	port->data_addr = arc_ps2_calc_addr(arc_ps2, index, false);
    173	port->status_addr = arc_ps2_calc_addr(arc_ps2, index, true);
    174
    175	dev_dbg(&pdev->dev, "port%d is allocated (data = 0x%p, status = 0x%p)\n",
    176		index, port->data_addr, port->status_addr);
    177
    178	serio_register_port(port->io);
    179	return 0;
    180}
    181
    182static int arc_ps2_probe(struct platform_device *pdev)
    183{
    184	struct arc_ps2_data *arc_ps2;
    185	struct resource *res;
    186	int irq;
    187	int error, id, i;
    188
    189	irq = platform_get_irq_byname(pdev, "arc_ps2_irq");
    190	if (irq < 0)
    191		return -EINVAL;
    192
    193	arc_ps2 = devm_kzalloc(&pdev->dev, sizeof(struct arc_ps2_data),
    194				GFP_KERNEL);
    195	if (!arc_ps2) {
    196		dev_err(&pdev->dev, "out of memory\n");
    197		return -ENOMEM;
    198	}
    199
    200	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    201	arc_ps2->addr = devm_ioremap_resource(&pdev->dev, res);
    202	if (IS_ERR(arc_ps2->addr))
    203		return PTR_ERR(arc_ps2->addr);
    204
    205	dev_info(&pdev->dev, "irq = %d, address = 0x%p, ports = %i\n",
    206		 irq, arc_ps2->addr, ARC_PS2_PORTS);
    207
    208	id = ioread32(arc_ps2->addr);
    209	if (id != ARC_ARC_PS2_ID) {
    210		dev_err(&pdev->dev, "device id does not match\n");
    211		return -ENXIO;
    212	}
    213
    214	arc_ps2_inhibit_ports(arc_ps2);
    215
    216	error = devm_request_irq(&pdev->dev, irq, arc_ps2_interrupt,
    217				 0, "arc_ps2", arc_ps2);
    218	if (error) {
    219		dev_err(&pdev->dev, "Could not allocate IRQ\n");
    220		return error;
    221	}
    222
    223	for (i = 0; i < ARC_PS2_PORTS; i++) {
    224		error = arc_ps2_create_port(pdev, arc_ps2, i);
    225		if (error) {
    226			while (--i >= 0)
    227				serio_unregister_port(arc_ps2->port[i].io);
    228			return error;
    229		}
    230	}
    231
    232	platform_set_drvdata(pdev, arc_ps2);
    233
    234	return 0;
    235}
    236
    237static int arc_ps2_remove(struct platform_device *pdev)
    238{
    239	struct arc_ps2_data *arc_ps2 = platform_get_drvdata(pdev);
    240	int i;
    241
    242	for (i = 0; i < ARC_PS2_PORTS; i++)
    243		serio_unregister_port(arc_ps2->port[i].io);
    244
    245	dev_dbg(&pdev->dev, "interrupt count = %i\n", arc_ps2->total_int);
    246	dev_dbg(&pdev->dev, "frame error count = %i\n", arc_ps2->frame_error);
    247	dev_dbg(&pdev->dev, "buffer overflow count = %i\n",
    248		arc_ps2->buf_overflow);
    249
    250	return 0;
    251}
    252
    253#ifdef CONFIG_OF
    254static const struct of_device_id arc_ps2_match[] = {
    255	{ .compatible = "snps,arc_ps2" },
    256	{ },
    257};
    258MODULE_DEVICE_TABLE(of, arc_ps2_match);
    259#endif
    260
    261static struct platform_driver arc_ps2_driver = {
    262	.driver	= {
    263		.name		= "arc_ps2",
    264		.of_match_table	= of_match_ptr(arc_ps2_match),
    265	},
    266	.probe	= arc_ps2_probe,
    267	.remove	= arc_ps2_remove,
    268};
    269
    270module_platform_driver(arc_ps2_driver);
    271
    272MODULE_LICENSE("GPL");
    273MODULE_AUTHOR("Pavel Sokolov <psokolov@synopsys.com>");
    274MODULE_DESCRIPTION("ARC PS/2 Driver");