ams_delta_serio.c (5161B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Amstrad E3 (Delta) keyboard port driver 4 * 5 * Copyright (c) 2006 Matt Callow 6 * Copyright (c) 2010 Janusz Krzysztofik 7 * 8 * Thanks to Cliff Lawson for his help 9 * 10 * The Amstrad Delta keyboard (aka mailboard) uses normal PC-AT style serial 11 * transmission. The keyboard port is formed of two GPIO lines, for clock 12 * and data. Due to strict timing requirements of the interface, 13 * the serial data stream is read and processed by a FIQ handler. 14 * The resulting words are fetched by this driver from a circular buffer. 15 * 16 * Standard AT keyboard driver (atkbd) is used for handling the keyboard data. 17 * However, when used with the E3 mailboard that producecs non-standard 18 * scancodes, a custom key table must be prepared and loaded from userspace. 19 */ 20#include <linux/irq.h> 21#include <linux/platform_data/ams-delta-fiq.h> 22#include <linux/platform_device.h> 23#include <linux/regulator/consumer.h> 24#include <linux/serio.h> 25#include <linux/slab.h> 26#include <linux/module.h> 27 28#define DRIVER_NAME "ams-delta-serio" 29 30MODULE_AUTHOR("Matt Callow"); 31MODULE_DESCRIPTION("AMS Delta (E3) keyboard port driver"); 32MODULE_LICENSE("GPL"); 33 34struct ams_delta_serio { 35 struct serio *serio; 36 struct regulator *vcc; 37 unsigned int *fiq_buffer; 38}; 39 40static int check_data(struct serio *serio, int data) 41{ 42 int i, parity = 0; 43 44 /* check valid stop bit */ 45 if (!(data & 0x400)) { 46 dev_warn(&serio->dev, "invalid stop bit, data=0x%X\n", data); 47 return SERIO_FRAME; 48 } 49 /* calculate the parity */ 50 for (i = 1; i < 10; i++) { 51 if (data & (1 << i)) 52 parity++; 53 } 54 /* it should be odd */ 55 if (!(parity & 0x01)) { 56 dev_warn(&serio->dev, 57 "parity check failed, data=0x%X parity=0x%X\n", data, 58 parity); 59 return SERIO_PARITY; 60 } 61 return 0; 62} 63 64static irqreturn_t ams_delta_serio_interrupt(int irq, void *dev_id) 65{ 66 struct ams_delta_serio *priv = dev_id; 67 int *circ_buff = &priv->fiq_buffer[FIQ_CIRC_BUFF]; 68 int data, dfl; 69 u8 scancode; 70 71 priv->fiq_buffer[FIQ_IRQ_PEND] = 0; 72 73 /* 74 * Read data from the circular buffer, check it 75 * and then pass it on the serio 76 */ 77 while (priv->fiq_buffer[FIQ_KEYS_CNT] > 0) { 78 79 data = circ_buff[priv->fiq_buffer[FIQ_HEAD_OFFSET]++]; 80 priv->fiq_buffer[FIQ_KEYS_CNT]--; 81 if (priv->fiq_buffer[FIQ_HEAD_OFFSET] == 82 priv->fiq_buffer[FIQ_BUF_LEN]) 83 priv->fiq_buffer[FIQ_HEAD_OFFSET] = 0; 84 85 dfl = check_data(priv->serio, data); 86 scancode = (u8) (data >> 1) & 0xFF; 87 serio_interrupt(priv->serio, scancode, dfl); 88 } 89 return IRQ_HANDLED; 90} 91 92static int ams_delta_serio_open(struct serio *serio) 93{ 94 struct ams_delta_serio *priv = serio->port_data; 95 96 /* enable keyboard */ 97 return regulator_enable(priv->vcc); 98} 99 100static void ams_delta_serio_close(struct serio *serio) 101{ 102 struct ams_delta_serio *priv = serio->port_data; 103 104 /* disable keyboard */ 105 regulator_disable(priv->vcc); 106} 107 108static int ams_delta_serio_init(struct platform_device *pdev) 109{ 110 struct ams_delta_serio *priv; 111 struct serio *serio; 112 int irq, err; 113 114 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 115 if (!priv) 116 return -ENOMEM; 117 118 priv->fiq_buffer = pdev->dev.platform_data; 119 if (!priv->fiq_buffer) 120 return -EINVAL; 121 122 priv->vcc = devm_regulator_get(&pdev->dev, "vcc"); 123 if (IS_ERR(priv->vcc)) { 124 err = PTR_ERR(priv->vcc); 125 dev_err(&pdev->dev, "regulator request failed (%d)\n", err); 126 /* 127 * When running on a non-dt platform and requested regulator 128 * is not available, devm_regulator_get() never returns 129 * -EPROBE_DEFER as it is not able to justify if the regulator 130 * may still appear later. On the other hand, the board can 131 * still set full constriants flag at late_initcall in order 132 * to instruct devm_regulator_get() to returnn a dummy one 133 * if sufficient. Hence, if we get -ENODEV here, let's convert 134 * it to -EPROBE_DEFER and wait for the board to decide or 135 * let Deferred Probe infrastructure handle this error. 136 */ 137 if (err == -ENODEV) 138 err = -EPROBE_DEFER; 139 return err; 140 } 141 142 irq = platform_get_irq(pdev, 0); 143 if (irq < 0) 144 return -ENXIO; 145 146 err = devm_request_irq(&pdev->dev, irq, ams_delta_serio_interrupt, 147 IRQ_TYPE_EDGE_RISING, DRIVER_NAME, priv); 148 if (err < 0) { 149 dev_err(&pdev->dev, "IRQ request failed (%d)\n", err); 150 return err; 151 } 152 153 serio = kzalloc(sizeof(*serio), GFP_KERNEL); 154 if (!serio) 155 return -ENOMEM; 156 157 priv->serio = serio; 158 159 serio->id.type = SERIO_8042; 160 serio->open = ams_delta_serio_open; 161 serio->close = ams_delta_serio_close; 162 strlcpy(serio->name, "AMS DELTA keyboard adapter", sizeof(serio->name)); 163 strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys)); 164 serio->dev.parent = &pdev->dev; 165 serio->port_data = priv; 166 167 serio_register_port(serio); 168 169 platform_set_drvdata(pdev, priv); 170 171 dev_info(&serio->dev, "%s\n", serio->name); 172 173 return 0; 174} 175 176static int ams_delta_serio_exit(struct platform_device *pdev) 177{ 178 struct ams_delta_serio *priv = platform_get_drvdata(pdev); 179 180 serio_unregister_port(priv->serio); 181 182 return 0; 183} 184 185static struct platform_driver ams_delta_serio_driver = { 186 .probe = ams_delta_serio_init, 187 .remove = ams_delta_serio_exit, 188 .driver = { 189 .name = DRIVER_NAME 190 }, 191}; 192module_platform_driver(ams_delta_serio_driver);