blob: 7cf12f453a30d1c161bd47b3320203413a7085cf [file] [log] [blame]
Mark Kettenisd42f1072022-01-23 16:48:13 +01001// 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};