1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org>
4  */
5 
6 #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 
30 static 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 
62 struct apple_spi_kbd_report {
63 	u8 reportid;
64 	u8 modifiers;
65 	u8 reserved;
66 	u8 keycode[6];
67 	u8 fn;
68 };
69 
70 struct 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. */
80 struct 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. */
92 struct 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 
apple_spi_kbd_service_modifiers(struct input_config * input)103 static 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 
apple_spi_kbd_service_key(struct input_config * input,int i,int released)135 static 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 
apple_spi_kbd_check(struct input_config * input)156 static 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 
215 fail:
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 
apple_spi_kbd_probe(struct udevice * dev)225 static 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 
255 static const struct keyboard_ops apple_spi_kbd_ops = {
256 };
257 
258 static const struct udevice_id apple_spi_kbd_of_match[] = {
259 	{ .compatible = "apple,spi-hid-transport" },
260 	{ /* sentinel */ }
261 };
262 
263 U_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 };
271