| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org>
- */
- #include <common.h>
- #include <dm.h>
- #include <keyboard.h>
- #include <spi.h>
- #include <stdio_dev.h>
- #include <asm-generic/gpio.h>
- #include <linux/delay.h>
- #include <linux/input.h>
- /*
- * The Apple SPI keyboard controller implements a protocol that
- * closely resembles HID Keyboard Boot protocol. The key codes are
- * mapped according to the HID Keyboard/Keypad Usage Table.
- */
- /* Modifier key bits */
- #define HID_MOD_LEFTCTRL BIT(0)
- #define HID_MOD_LEFTSHIFT BIT(1)
- #define HID_MOD_LEFTALT BIT(2)
- #define HID_MOD_LEFTGUI BIT(3)
- #define HID_MOD_RIGHTCTRL BIT(4)
- #define HID_MOD_RIGHTSHIFT BIT(5)
- #define HID_MOD_RIGHTALT BIT(6)
- #define HID_MOD_RIGHTGUI BIT(7)
- static const u8 hid_kbd_keymap[] = {
- KEY_RESERVED, 0xff, 0xff, 0xff,
- KEY_A, KEY_B, KEY_C, KEY_D,
- KEY_E, KEY_F, KEY_G, KEY_H,
- KEY_I, KEY_J, KEY_K, KEY_L,
- KEY_M, KEY_N, KEY_O, KEY_P,
- KEY_Q, KEY_R, KEY_S, KEY_T,
- KEY_U, KEY_V, KEY_W, KEY_X,
- KEY_Y, KEY_Z, KEY_1, KEY_2,
- KEY_3, KEY_4, KEY_5, KEY_6,
- KEY_7, KEY_8, KEY_9, KEY_0,
- KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB,
- KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE,
- KEY_RIGHTBRACE, KEY_BACKSLASH, 0xff, KEY_SEMICOLON,
- KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, KEY_DOT,
- KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2,
- KEY_F3, KEY_F4, KEY_F5, KEY_F6,
- KEY_F7, KEY_F8, KEY_F9, KEY_F10,
- KEY_F11, KEY_F12, KEY_SYSRQ, KEY_SCROLLLOCK,
- KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP,
- KEY_DELETE, KEY_END, KEY_PAGEDOWN, KEY_RIGHT,
- KEY_LEFT, KEY_DOWN, KEY_UP, KEY_NUMLOCK,
- KEY_KPSLASH, KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS,
- KEY_KPENTER, KEY_KP1, KEY_KP2, KEY_KP3,
- KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7,
- KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT,
- KEY_BACKSLASH, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL,
- };
- /* Report ID used for keyboard input reports. */
- #define KBD_REPORTID 0x01
- struct apple_spi_kbd_report {
- u8 reportid;
- u8 modifiers;
- u8 reserved;
- u8 keycode[6];
- u8 fn;
- };
- struct apple_spi_kbd_priv {
- struct gpio_desc enable;
- struct apple_spi_kbd_report old; /* previous keyboard input report */
- struct apple_spi_kbd_report new; /* current keyboard input report */
- };
- /* Keyboard device. */
- #define KBD_DEVICE 0x01
- /* The controller sends us fixed-size packets of 256 bytes. */
- struct apple_spi_kbd_packet {
- u8 flags;
- #define PACKET_READ 0x20
- u8 device;
- u16 offset;
- u16 remaining;
- u16 len;
- u8 data[246];
- u16 crc;
- };
- /* Packets contain a single variable-sized message. */
- struct apple_spi_kbd_msg {
- u8 type;
- #define MSG_REPORT 0x10
- u8 device;
- u8 unknown;
- u8 msgid;
- u16 rsplen;
- u16 cmdlen;
- u8 data[0];
- };
- static void apple_spi_kbd_service_modifiers(struct input_config *input)
- {
- struct apple_spi_kbd_priv *priv = dev_get_priv(input->dev);
- u8 new = priv->new.modifiers;
- u8 old = priv->old.modifiers;
- if ((new ^ old) & HID_MOD_LEFTCTRL)
- input_add_keycode(input, KEY_LEFTCTRL,
- old & HID_MOD_LEFTCTRL);
- if ((new ^ old) & HID_MOD_RIGHTCTRL)
- input_add_keycode(input, KEY_RIGHTCTRL,
- old & HID_MOD_RIGHTCTRL);
- if ((new ^ old) & HID_MOD_LEFTSHIFT)
- input_add_keycode(input, KEY_LEFTSHIFT,
- old & HID_MOD_LEFTSHIFT);
- if ((new ^ old) & HID_MOD_RIGHTSHIFT)
- input_add_keycode(input, KEY_RIGHTSHIFT,
- old & HID_MOD_RIGHTSHIFT);
- if ((new ^ old) & HID_MOD_LEFTALT)
- input_add_keycode(input, KEY_LEFTALT,
- old & HID_MOD_LEFTALT);
- if ((new ^ old) & HID_MOD_RIGHTALT)
- input_add_keycode(input, KEY_RIGHTALT,
- old & HID_MOD_RIGHTALT);
- if ((new ^ old) & HID_MOD_LEFTGUI)
- input_add_keycode(input, KEY_LEFTMETA,
- old & HID_MOD_LEFTGUI);
- if ((new ^ old) & HID_MOD_RIGHTGUI)
- input_add_keycode(input, KEY_RIGHTMETA,
- old & HID_MOD_RIGHTGUI);
- }
- static void apple_spi_kbd_service_key(struct input_config *input, int i,
- int released)
- {
- struct apple_spi_kbd_priv *priv = dev_get_priv(input->dev);
- u8 *new;
- u8 *old;
- if (released) {
- new = priv->new.keycode;
- old = priv->old.keycode;
- } else {
- new = priv->old.keycode;
- old = priv->new.keycode;
- }
- if (memscan(new, old[i], sizeof(priv->new.keycode)) ==
- new + sizeof(priv->new.keycode) &&
- old[i] < ARRAY_SIZE(hid_kbd_keymap))
- input_add_keycode(input, hid_kbd_keymap[old[i]], released);
- }
- static int apple_spi_kbd_check(struct input_config *input)
- {
- struct udevice *dev = input->dev;
- struct apple_spi_kbd_priv *priv = dev_get_priv(dev);
- struct apple_spi_kbd_packet packet;
- struct apple_spi_kbd_msg *msg;
- struct apple_spi_kbd_report *report;
- int i, ret;
- memset(&packet, 0, sizeof(packet));
- ret = dm_spi_claim_bus(dev);
- if (ret < 0)
- return ret;
- /*
- * The keyboard controller needs delays after asserting CS#
- * and before deasserting CS#.
- */
- ret = dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_BEGIN);
- if (ret < 0)
- goto fail;
- udelay(100);
- ret = dm_spi_xfer(dev, sizeof(packet) * 8, NULL, &packet, 0);
- if (ret < 0)
- goto fail;
- udelay(100);
- ret = dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_END);
- if (ret < 0)
- goto fail;
- dm_spi_release_bus(dev);
- /*
- * The keyboard controller needs a delay between subsequent
- * SPI transfers.
- */
- udelay(250);
- msg = (struct apple_spi_kbd_msg *)packet.data;
- report = (struct apple_spi_kbd_report *)msg->data;
- if (packet.flags == PACKET_READ && packet.device == KBD_DEVICE &&
- msg->type == MSG_REPORT && msg->device == KBD_DEVICE &&
- msg->cmdlen == sizeof(struct apple_spi_kbd_report) &&
- report->reportid == KBD_REPORTID) {
- memcpy(&priv->new, report,
- sizeof(struct apple_spi_kbd_report));
- apple_spi_kbd_service_modifiers(input);
- for (i = 0; i < sizeof(priv->new.keycode); i++) {
- apple_spi_kbd_service_key(input, i, 1);
- apple_spi_kbd_service_key(input, i, 0);
- }
- memcpy(&priv->old, &priv->new,
- sizeof(struct apple_spi_kbd_report));
- return 1;
- }
- return 0;
- fail:
- /*
- * Make sure CS# is deasserted. If this fails there is nothing
- * we can do, so ignore any errors.
- */
- dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_END);
- dm_spi_release_bus(dev);
- return ret;
- }
- static int apple_spi_kbd_probe(struct udevice *dev)
- {
- struct apple_spi_kbd_priv *priv = dev_get_priv(dev);
- struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
- struct stdio_dev *sdev = &uc_priv->sdev;
- struct input_config *input = &uc_priv->input;
- int ret;
- ret = gpio_request_by_name(dev, "spien-gpios", 0, &priv->enable,
- GPIOD_IS_OUT);
- if (ret < 0)
- return ret;
- /* Reset the keyboard controller. */
- dm_gpio_set_value(&priv->enable, 1);
- udelay(5000);
- dm_gpio_set_value(&priv->enable, 0);
- udelay(5000);
- /* Enable the keyboard controller. */
- dm_gpio_set_value(&priv->enable, 1);
- input->dev = dev;
- input->read_keys = apple_spi_kbd_check;
- input_add_tables(input, false);
- strcpy(sdev->name, "spikbd");
- return input_stdio_register(sdev);
- }
- static const struct keyboard_ops apple_spi_kbd_ops = {
- };
- static const struct udevice_id apple_spi_kbd_of_match[] = {
- { .compatible = "apple,spi-hid-transport" },
- { /* sentinel */ }
- };
- U_BOOT_DRIVER(apple_spi_kbd) = {
- .name = "apple_spi_kbd",
- .id = UCLASS_KEYBOARD,
- .of_match = apple_spi_kbd_of_match,
- .probe = apple_spi_kbd_probe,
- .priv_auto = sizeof(struct apple_spi_kbd_priv),
- .ops = &apple_spi_kbd_ops,
- };
|