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