1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org> 4 */ 5 6#include <common.h> 7#include <dm.h> 8#include <keyboard.h> 9#include <spi.h> 10#include <stdio_dev.h> 11#include <asm-generic/gpio.h> 12#include <linux/delay.h> 13#include <linux/input.h> 14 15/* 16 * The Apple SPI keyboard controller implements a protocol that 17 * closely resembles HID Keyboard Boot protocol. The key codes are 18 * mapped according to the HID Keyboard/Keypad Usage Table. 19 */ 20 21/* Modifier key bits */ 22#define HID_MOD_LEFTCTRL BIT(0) 23#define HID_MOD_LEFTSHIFT BIT(1) 24#define HID_MOD_LEFTALT BIT(2) 25#define HID_MOD_LEFTGUI BIT(3) 26#define HID_MOD_RIGHTCTRL BIT(4) 27#define HID_MOD_RIGHTSHIFT BIT(5) 28#define HID_MOD_RIGHTALT BIT(6) 29#define HID_MOD_RIGHTGUI BIT(7) 30 31static const u8 hid_kbd_keymap[] = { 32 KEY_RESERVED, 0xff, 0xff, 0xff, 33 KEY_A, KEY_B, KEY_C, KEY_D, 34 KEY_E, KEY_F, KEY_G, KEY_H, 35 KEY_I, KEY_J, KEY_K, KEY_L, 36 KEY_M, KEY_N, KEY_O, KEY_P, 37 KEY_Q, KEY_R, KEY_S, KEY_T, 38 KEY_U, KEY_V, KEY_W, KEY_X, 39 KEY_Y, KEY_Z, KEY_1, KEY_2, 40 KEY_3, KEY_4, KEY_5, KEY_6, 41 KEY_7, KEY_8, KEY_9, KEY_0, 42 KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, 43 KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE, 44 KEY_RIGHTBRACE, KEY_BACKSLASH, 0xff, KEY_SEMICOLON, 45 KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, KEY_DOT, 46 KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2, 47 KEY_F3, KEY_F4, KEY_F5, KEY_F6, 48 KEY_F7, KEY_F8, KEY_F9, KEY_F10, 49 KEY_F11, KEY_F12, KEY_SYSRQ, KEY_SCROLLLOCK, 50 KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP, 51 KEY_DELETE, KEY_END, KEY_PAGEDOWN, KEY_RIGHT, 52 KEY_LEFT, KEY_DOWN, KEY_UP, KEY_NUMLOCK, 53 KEY_KPSLASH, KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS, 54 KEY_KPENTER, KEY_KP1, KEY_KP2, KEY_KP3, 55 KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7, 56 KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT, 57 KEY_BACKSLASH, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, 58}; 59 60/* Report ID used for keyboard input reports. */ 61#define KBD_REPORTID 0x01 62 63struct apple_spi_kbd_report { 64 u8 reportid; 65 u8 modifiers; 66 u8 reserved; 67 u8 keycode[6]; 68 u8 fn; 69}; 70 71struct apple_spi_kbd_priv { 72 struct gpio_desc enable; 73 struct apple_spi_kbd_report old; /* previous keyboard input report */ 74 struct apple_spi_kbd_report new; /* current keyboard input report */ 75}; 76 77/* Keyboard device. */ 78#define KBD_DEVICE 0x01 79 80/* The controller sends us fixed-size packets of 256 bytes. */ 81struct apple_spi_kbd_packet { 82 u8 flags; 83#define PACKET_READ 0x20 84 u8 device; 85 u16 offset; 86 u16 remaining; 87 u16 len; 88 u8 data[246]; 89 u16 crc; 90}; 91 92/* Packets contain a single variable-sized message. */ 93struct apple_spi_kbd_msg { 94 u8 type; 95#define MSG_REPORT 0x10 96 u8 device; 97 u8 unknown; 98 u8 msgid; 99 u16 rsplen; 100 u16 cmdlen; 101 u8 data[0]; 102}; 103 104static void apple_spi_kbd_service_modifiers(struct input_config *input) 105{ 106 struct apple_spi_kbd_priv *priv = dev_get_priv(input->dev); 107 u8 new = priv->new.modifiers; 108 u8 old = priv->old.modifiers; 109 110 if ((new ^ old) & HID_MOD_LEFTCTRL) 111 input_add_keycode(input, KEY_LEFTCTRL, 112 old & HID_MOD_LEFTCTRL); 113 if ((new ^ old) & HID_MOD_RIGHTCTRL) 114 input_add_keycode(input, KEY_RIGHTCTRL, 115 old & HID_MOD_RIGHTCTRL); 116 if ((new ^ old) & HID_MOD_LEFTSHIFT) 117 input_add_keycode(input, KEY_LEFTSHIFT, 118 old & HID_MOD_LEFTSHIFT); 119 if ((new ^ old) & HID_MOD_RIGHTSHIFT) 120 input_add_keycode(input, KEY_RIGHTSHIFT, 121 old & HID_MOD_RIGHTSHIFT); 122 if ((new ^ old) & HID_MOD_LEFTALT) 123 input_add_keycode(input, KEY_LEFTALT, 124 old & HID_MOD_LEFTALT); 125 if ((new ^ old) & HID_MOD_RIGHTALT) 126 input_add_keycode(input, KEY_RIGHTALT, 127 old & HID_MOD_RIGHTALT); 128 if ((new ^ old) & HID_MOD_LEFTGUI) 129 input_add_keycode(input, KEY_LEFTMETA, 130 old & HID_MOD_LEFTGUI); 131 if ((new ^ old) & HID_MOD_RIGHTGUI) 132 input_add_keycode(input, KEY_RIGHTMETA, 133 old & HID_MOD_RIGHTGUI); 134} 135 136static void apple_spi_kbd_service_key(struct input_config *input, int i, 137 int released) 138{ 139 struct apple_spi_kbd_priv *priv = dev_get_priv(input->dev); 140 u8 *new; 141 u8 *old; 142 143 if (released) { 144 new = priv->new.keycode; 145 old = priv->old.keycode; 146 } else { 147 new = priv->old.keycode; 148 old = priv->new.keycode; 149 } 150 151 if (memscan(new, old[i], sizeof(priv->new.keycode)) == 152 new + sizeof(priv->new.keycode) && 153 old[i] < ARRAY_SIZE(hid_kbd_keymap)) 154 input_add_keycode(input, hid_kbd_keymap[old[i]], released); 155} 156 157static int apple_spi_kbd_check(struct input_config *input) 158{ 159 struct udevice *dev = input->dev; 160 struct apple_spi_kbd_priv *priv = dev_get_priv(dev); 161 struct apple_spi_kbd_packet packet; 162 struct apple_spi_kbd_msg *msg; 163 struct apple_spi_kbd_report *report; 164 int i, ret; 165 166 memset(&packet, 0, sizeof(packet)); 167 168 ret = dm_spi_claim_bus(dev); 169 if (ret < 0) 170 return ret; 171 172 /* 173 * The keyboard controller needs delays after asserting CS# 174 * and before deasserting CS#. 175 */ 176 ret = dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_BEGIN); 177 if (ret < 0) 178 goto fail; 179 udelay(100); 180 ret = dm_spi_xfer(dev, sizeof(packet) * 8, NULL, &packet, 0); 181 if (ret < 0) 182 goto fail; 183 udelay(100); 184 ret = dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_END); 185 if (ret < 0) 186 goto fail; 187 188 dm_spi_release_bus(dev); 189 190 /* 191 * The keyboard controller needs a delay between subsequent 192 * SPI transfers. 193 */ 194 udelay(250); 195 196 msg = (struct apple_spi_kbd_msg *)packet.data; 197 report = (struct apple_spi_kbd_report *)msg->data; 198 if (packet.flags == PACKET_READ && packet.device == KBD_DEVICE && 199 msg->type == MSG_REPORT && msg->device == KBD_DEVICE && 200 msg->cmdlen == sizeof(struct apple_spi_kbd_report) && 201 report->reportid == KBD_REPORTID) { 202 memcpy(&priv->new, report, 203 sizeof(struct apple_spi_kbd_report)); 204 apple_spi_kbd_service_modifiers(input); 205 for (i = 0; i < sizeof(priv->new.keycode); i++) { 206 apple_spi_kbd_service_key(input, i, 1); 207 apple_spi_kbd_service_key(input, i, 0); 208 } 209 memcpy(&priv->old, &priv->new, 210 sizeof(struct apple_spi_kbd_report)); 211 return 1; 212 } 213 214 return 0; 215 216fail: 217 /* 218 * Make sure CS# is deasserted. If this fails there is nothing 219 * we can do, so ignore any errors. 220 */ 221 dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_END); 222 dm_spi_release_bus(dev); 223 return ret; 224} 225 226static int apple_spi_kbd_probe(struct udevice *dev) 227{ 228 struct apple_spi_kbd_priv *priv = dev_get_priv(dev); 229 struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); 230 struct stdio_dev *sdev = &uc_priv->sdev; 231 struct input_config *input = &uc_priv->input; 232 int ret; 233 234 ret = gpio_request_by_name(dev, "spien-gpios", 0, &priv->enable, 235 GPIOD_IS_OUT); 236 if (ret < 0) 237 return ret; 238 239 /* Reset the keyboard controller. */ 240 dm_gpio_set_value(&priv->enable, 1); 241 udelay(5000); 242 dm_gpio_set_value(&priv->enable, 0); 243 udelay(5000); 244 245 /* Enable the keyboard controller. */ 246 dm_gpio_set_value(&priv->enable, 1); 247 248 input->dev = dev; 249 input->read_keys = apple_spi_kbd_check; 250 input_add_tables(input, false); 251 strcpy(sdev->name, "spikbd"); 252 253 return input_stdio_register(sdev); 254} 255 256static const struct keyboard_ops apple_spi_kbd_ops = { 257}; 258 259static const struct udevice_id apple_spi_kbd_of_match[] = { 260 { .compatible = "apple,spi-hid-transport" }, 261 { /* sentinel */ } 262}; 263 264U_BOOT_DRIVER(apple_spi_kbd) = { 265 .name = "apple_spi_kbd", 266 .id = UCLASS_KEYBOARD, 267 .of_match = apple_spi_kbd_of_match, 268 .probe = apple_spi_kbd_probe, 269 .priv_auto = sizeof(struct apple_spi_kbd_priv), 270 .ops = &apple_spi_kbd_ops, 271}; 272