Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: add OLPC AP-SP driver

The OLPC XO-1.75 and XO-4 laptops include a PS/2 touchpad and an AT
keyboard, yet they do not have a hardware PS/2 controller. Instead, a
firmware runs on a dedicated core ("Security Processor", part of the SoC)
that acts as a PS/2 controller through bit-banging.

Communication between the main cpu (Application Processor) and the
Security Processor happens via a standard command mechanism implemented
by the SoC. Add a driver for this interface to enable keyboard/mouse
input on this platform.

Original author: Saadia Baloch
Signed-off-by: Daniel Drake <dsd@laptop.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Daniel Drake and committed by
Dmitry Torokhov
b56ece9a 20c3da9f

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