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

olpc_apsp.c (7118B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * OLPC serio driver for multiplexed input from Marvell MMP security processor
      4 *
      5 * Copyright (C) 2011-2013 One Laptop Per Child
      6 */
      7
      8#include <linux/module.h>
      9#include <linux/interrupt.h>
     10#include <linux/serio.h>
     11#include <linux/err.h>
     12#include <linux/platform_device.h>
     13#include <linux/io.h>
     14#include <linux/of.h>
     15#include <linux/slab.h>
     16#include <linux/delay.h>
     17
     18/*
     19 * The OLPC XO-1.75 and XO-4 laptops do not have a hardware PS/2 controller.
     20 * Instead, the OLPC firmware runs a bit-banging PS/2 implementation on an
     21 * otherwise-unused slow processor which is included in the Marvell MMP2/MMP3
     22 * SoC, known as the "Security Processor" (SP) or "Wireless Trusted Module"
     23 * (WTM). This firmware then reports its results via the WTM registers,
     24 * which we read from the Application Processor (AP, i.e. main CPU) in this
     25 * driver.
     26 *
     27 * On the hardware side we have a PS/2 mouse and an AT keyboard, the data
     28 * is multiplexed through this system. We create a serio port for each one,
     29 * and demultiplex the data accordingly.
     30 */
     31
     32/* WTM register offsets */
     33#define SECURE_PROCESSOR_COMMAND	0x40
     34#define COMMAND_RETURN_STATUS		0x80
     35#define COMMAND_FIFO_STATUS		0xc4
     36#define PJ_RST_INTERRUPT		0xc8
     37#define PJ_INTERRUPT_MASK		0xcc
     38
     39/*
     40 * The upper byte of SECURE_PROCESSOR_COMMAND and COMMAND_RETURN_STATUS is
     41 * used to identify which port (device) is being talked to. The lower byte
     42 * is the data being sent/received.
     43 */
     44#define PORT_MASK	0xff00
     45#define DATA_MASK	0x00ff
     46#define PORT_SHIFT	8
     47#define KEYBOARD_PORT	0
     48#define TOUCHPAD_PORT	1
     49
     50/* COMMAND_FIFO_STATUS */
     51#define CMD_CNTR_MASK		0x7 /* Number of pending/unprocessed commands */
     52#define MAX_PENDING_CMDS	4   /* from device specs */
     53
     54/* PJ_RST_INTERRUPT */
     55#define SP_COMMAND_COMPLETE_RESET	0x1
     56
     57/* PJ_INTERRUPT_MASK */
     58#define INT_0	(1 << 0)
     59
     60/* COMMAND_FIFO_STATUS */
     61#define CMD_STS_MASK	0x100
     62
     63struct olpc_apsp {
     64	struct device *dev;
     65	struct serio *kbio;
     66	struct serio *padio;
     67	void __iomem *base;
     68	int open_count;
     69	int irq;
     70};
     71
     72static int olpc_apsp_write(struct serio *port, unsigned char val)
     73{
     74	struct olpc_apsp *priv = port->port_data;
     75	unsigned int i;
     76	u32 which = 0;
     77
     78	if (port == priv->padio)
     79		which = TOUCHPAD_PORT << PORT_SHIFT;
     80	else
     81		which = KEYBOARD_PORT << PORT_SHIFT;
     82
     83	dev_dbg(priv->dev, "olpc_apsp_write which=%x val=%x\n", which, val);
     84	for (i = 0; i < 50; i++) {
     85		u32 sts = readl(priv->base + COMMAND_FIFO_STATUS);
     86		if ((sts & CMD_CNTR_MASK) < MAX_PENDING_CMDS) {
     87			writel(which | val,
     88			       priv->base + SECURE_PROCESSOR_COMMAND);
     89			return 0;
     90		}
     91		/* SP busy. This has not been seen in practice. */
     92		mdelay(1);
     93	}
     94
     95	dev_dbg(priv->dev, "olpc_apsp_write timeout, status=%x\n",
     96		readl(priv->base + COMMAND_FIFO_STATUS));
     97
     98	return -ETIMEDOUT;
     99}
    100
    101static irqreturn_t olpc_apsp_rx(int irq, void *dev_id)
    102{
    103	struct olpc_apsp *priv = dev_id;
    104	unsigned int w, tmp;
    105	struct serio *serio;
    106
    107	/*
    108	 * Write 1 to PJ_RST_INTERRUPT to acknowledge and clear the interrupt
    109	 * Write 0xff00 to SECURE_PROCESSOR_COMMAND.
    110	 */
    111	tmp = readl(priv->base + PJ_RST_INTERRUPT);
    112	if (!(tmp & SP_COMMAND_COMPLETE_RESET)) {
    113		dev_warn(priv->dev, "spurious interrupt?\n");
    114		return IRQ_NONE;
    115	}
    116
    117	w = readl(priv->base + COMMAND_RETURN_STATUS);
    118	dev_dbg(priv->dev, "olpc_apsp_rx %x\n", w);
    119
    120	if (w >> PORT_SHIFT == KEYBOARD_PORT)
    121		serio = priv->kbio;
    122	else
    123		serio = priv->padio;
    124
    125	serio_interrupt(serio, w & DATA_MASK, 0);
    126
    127	/* Ack and clear interrupt */
    128	writel(tmp | SP_COMMAND_COMPLETE_RESET, priv->base + PJ_RST_INTERRUPT);
    129	writel(PORT_MASK, priv->base + SECURE_PROCESSOR_COMMAND);
    130
    131	pm_wakeup_event(priv->dev, 1000);
    132	return IRQ_HANDLED;
    133}
    134
    135static int olpc_apsp_open(struct serio *port)
    136{
    137	struct olpc_apsp *priv = port->port_data;
    138	unsigned int tmp;
    139	unsigned long l;
    140
    141	if (priv->open_count++ == 0) {
    142		l = readl(priv->base + COMMAND_FIFO_STATUS);
    143		if (!(l & CMD_STS_MASK)) {
    144			dev_err(priv->dev, "SP cannot accept commands.\n");
    145			return -EIO;
    146		}
    147
    148		/* Enable interrupt 0 by clearing its bit */
    149		tmp = readl(priv->base + PJ_INTERRUPT_MASK);
    150		writel(tmp & ~INT_0, priv->base + PJ_INTERRUPT_MASK);
    151	}
    152
    153	return 0;
    154}
    155
    156static void olpc_apsp_close(struct serio *port)
    157{
    158	struct olpc_apsp *priv = port->port_data;
    159	unsigned int tmp;
    160
    161	if (--priv->open_count == 0) {
    162		/* Disable interrupt 0 */
    163		tmp = readl(priv->base + PJ_INTERRUPT_MASK);
    164		writel(tmp | INT_0, priv->base + PJ_INTERRUPT_MASK);
    165	}
    166}
    167
    168static int olpc_apsp_probe(struct platform_device *pdev)
    169{
    170	struct serio *kb_serio, *pad_serio;
    171	struct olpc_apsp *priv;
    172	struct resource *res;
    173	int error;
    174
    175	priv = devm_kzalloc(&pdev->dev, sizeof(struct olpc_apsp), GFP_KERNEL);
    176	if (!priv)
    177		return -ENOMEM;
    178
    179	priv->dev = &pdev->dev;
    180
    181	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    182	priv->base = devm_ioremap_resource(&pdev->dev, res);
    183	if (IS_ERR(priv->base)) {
    184		dev_err(&pdev->dev, "Failed to map WTM registers\n");
    185		return PTR_ERR(priv->base);
    186	}
    187
    188	priv->irq = platform_get_irq(pdev, 0);
    189	if (priv->irq < 0)
    190		return priv->irq;
    191
    192	/* KEYBOARD */
    193	kb_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
    194	if (!kb_serio)
    195		return -ENOMEM;
    196	kb_serio->id.type	= SERIO_8042_XL;
    197	kb_serio->write		= olpc_apsp_write;
    198	kb_serio->open		= olpc_apsp_open;
    199	kb_serio->close		= olpc_apsp_close;
    200	kb_serio->port_data	= priv;
    201	kb_serio->dev.parent	= &pdev->dev;
    202	strlcpy(kb_serio->name, "sp keyboard", sizeof(kb_serio->name));
    203	strlcpy(kb_serio->phys, "sp/serio0", sizeof(kb_serio->phys));
    204	priv->kbio		= kb_serio;
    205	serio_register_port(kb_serio);
    206
    207	/* TOUCHPAD */
    208	pad_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
    209	if (!pad_serio) {
    210		error = -ENOMEM;
    211		goto err_pad;
    212	}
    213	pad_serio->id.type	= SERIO_8042;
    214	pad_serio->write	= olpc_apsp_write;
    215	pad_serio->open		= olpc_apsp_open;
    216	pad_serio->close	= olpc_apsp_close;
    217	pad_serio->port_data	= priv;
    218	pad_serio->dev.parent	= &pdev->dev;
    219	strlcpy(pad_serio->name, "sp touchpad", sizeof(pad_serio->name));
    220	strlcpy(pad_serio->phys, "sp/serio1", sizeof(pad_serio->phys));
    221	priv->padio		= pad_serio;
    222	serio_register_port(pad_serio);
    223
    224	error = request_irq(priv->irq, olpc_apsp_rx, 0, "olpc-apsp", priv);
    225	if (error) {
    226		dev_err(&pdev->dev, "Failed to request IRQ\n");
    227		goto err_irq;
    228	}
    229
    230	device_init_wakeup(priv->dev, 1);
    231	platform_set_drvdata(pdev, priv);
    232
    233	dev_dbg(&pdev->dev, "probed successfully.\n");
    234	return 0;
    235
    236err_irq:
    237	serio_unregister_port(pad_serio);
    238err_pad:
    239	serio_unregister_port(kb_serio);
    240	return error;
    241}
    242
    243static int olpc_apsp_remove(struct platform_device *pdev)
    244{
    245	struct olpc_apsp *priv = platform_get_drvdata(pdev);
    246
    247	free_irq(priv->irq, priv);
    248
    249	serio_unregister_port(priv->kbio);
    250	serio_unregister_port(priv->padio);
    251
    252	return 0;
    253}
    254
    255static const struct of_device_id olpc_apsp_dt_ids[] = {
    256	{ .compatible = "olpc,ap-sp", },
    257	{}
    258};
    259MODULE_DEVICE_TABLE(of, olpc_apsp_dt_ids);
    260
    261static struct platform_driver olpc_apsp_driver = {
    262	.probe		= olpc_apsp_probe,
    263	.remove		= olpc_apsp_remove,
    264	.driver		= {
    265		.name	= "olpc-apsp",
    266		.of_match_table = olpc_apsp_dt_ids,
    267	},
    268};
    269
    270MODULE_DESCRIPTION("OLPC AP-SP serio driver");
    271MODULE_LICENSE("GPL");
    272module_platform_driver(olpc_apsp_driver);