18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// ChromeOS EC keyboard driver 38c2ecf20Sopenharmony_ci// 48c2ecf20Sopenharmony_ci// Copyright (C) 2012 Google, Inc. 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// This driver uses the ChromeOS EC byte-level message-based protocol for 78c2ecf20Sopenharmony_ci// communicating the keyboard state (which keys are pressed) from a keyboard EC 88c2ecf20Sopenharmony_ci// to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, 98c2ecf20Sopenharmony_ci// but everything else (including deghosting) is done here. The main 108c2ecf20Sopenharmony_ci// motivation for this is to keep the EC firmware as simple as possible, since 118c2ecf20Sopenharmony_ci// it cannot be easily upgraded and EC flash/IRAM space is relatively 128c2ecf20Sopenharmony_ci// expensive. 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/bitops.h> 168c2ecf20Sopenharmony_ci#include <linux/i2c.h> 178c2ecf20Sopenharmony_ci#include <linux/input.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/notifier.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/sysrq.h> 248c2ecf20Sopenharmony_ci#include <linux/input/matrix_keypad.h> 258c2ecf20Sopenharmony_ci#include <linux/platform_data/cros_ec_commands.h> 268c2ecf20Sopenharmony_ci#include <linux/platform_data/cros_ec_proto.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * @rows: Number of rows in the keypad 328c2ecf20Sopenharmony_ci * @cols: Number of columns in the keypad 338c2ecf20Sopenharmony_ci * @row_shift: log2 or number of rows, rounded up 348c2ecf20Sopenharmony_ci * @keymap_data: Matrix keymap data used to convert to keyscan values 358c2ecf20Sopenharmony_ci * @ghost_filter: true to enable the matrix key-ghosting filter 368c2ecf20Sopenharmony_ci * @valid_keys: bitmap of existing keys for each matrix column 378c2ecf20Sopenharmony_ci * @old_kb_state: bitmap of keys pressed last scan 388c2ecf20Sopenharmony_ci * @dev: Device pointer 398c2ecf20Sopenharmony_ci * @ec: Top level ChromeOS device to use to talk to EC 408c2ecf20Sopenharmony_ci * @idev: The input device for the matrix keys. 418c2ecf20Sopenharmony_ci * @bs_idev: The input device for non-matrix buttons and switches (or NULL). 428c2ecf20Sopenharmony_ci * @notifier: interrupt event notifier for transport devices 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_cistruct cros_ec_keyb { 458c2ecf20Sopenharmony_ci unsigned int rows; 468c2ecf20Sopenharmony_ci unsigned int cols; 478c2ecf20Sopenharmony_ci int row_shift; 488c2ecf20Sopenharmony_ci const struct matrix_keymap_data *keymap_data; 498c2ecf20Sopenharmony_ci bool ghost_filter; 508c2ecf20Sopenharmony_ci uint8_t *valid_keys; 518c2ecf20Sopenharmony_ci uint8_t *old_kb_state; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci struct device *dev; 548c2ecf20Sopenharmony_ci struct cros_ec_device *ec; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci struct input_dev *idev; 578c2ecf20Sopenharmony_ci struct input_dev *bs_idev; 588c2ecf20Sopenharmony_ci struct notifier_block notifier; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/** 638c2ecf20Sopenharmony_ci * cros_ec_bs_map - Struct mapping Linux keycodes to EC button/switch bitmap 648c2ecf20Sopenharmony_ci * #defines 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci * @ev_type: The type of the input event to generate (e.g., EV_KEY). 678c2ecf20Sopenharmony_ci * @code: A linux keycode 688c2ecf20Sopenharmony_ci * @bit: A #define like EC_MKBP_POWER_BUTTON or EC_MKBP_LID_OPEN 698c2ecf20Sopenharmony_ci * @inverted: If the #define and EV_SW have opposite meanings, this is true. 708c2ecf20Sopenharmony_ci * Only applicable to switches. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_cistruct cros_ec_bs_map { 738c2ecf20Sopenharmony_ci unsigned int ev_type; 748c2ecf20Sopenharmony_ci unsigned int code; 758c2ecf20Sopenharmony_ci u8 bit; 768c2ecf20Sopenharmony_ci bool inverted; 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* cros_ec_keyb_bs - Map EC button/switch #defines into kernel ones */ 808c2ecf20Sopenharmony_cistatic const struct cros_ec_bs_map cros_ec_keyb_bs[] = { 818c2ecf20Sopenharmony_ci /* Buttons */ 828c2ecf20Sopenharmony_ci { 838c2ecf20Sopenharmony_ci .ev_type = EV_KEY, 848c2ecf20Sopenharmony_ci .code = KEY_POWER, 858c2ecf20Sopenharmony_ci .bit = EC_MKBP_POWER_BUTTON, 868c2ecf20Sopenharmony_ci }, 878c2ecf20Sopenharmony_ci { 888c2ecf20Sopenharmony_ci .ev_type = EV_KEY, 898c2ecf20Sopenharmony_ci .code = KEY_VOLUMEUP, 908c2ecf20Sopenharmony_ci .bit = EC_MKBP_VOL_UP, 918c2ecf20Sopenharmony_ci }, 928c2ecf20Sopenharmony_ci { 938c2ecf20Sopenharmony_ci .ev_type = EV_KEY, 948c2ecf20Sopenharmony_ci .code = KEY_VOLUMEDOWN, 958c2ecf20Sopenharmony_ci .bit = EC_MKBP_VOL_DOWN, 968c2ecf20Sopenharmony_ci }, 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* Switches */ 998c2ecf20Sopenharmony_ci { 1008c2ecf20Sopenharmony_ci .ev_type = EV_SW, 1018c2ecf20Sopenharmony_ci .code = SW_LID, 1028c2ecf20Sopenharmony_ci .bit = EC_MKBP_LID_OPEN, 1038c2ecf20Sopenharmony_ci .inverted = true, 1048c2ecf20Sopenharmony_ci }, 1058c2ecf20Sopenharmony_ci { 1068c2ecf20Sopenharmony_ci .ev_type = EV_SW, 1078c2ecf20Sopenharmony_ci .code = SW_TABLET_MODE, 1088c2ecf20Sopenharmony_ci .bit = EC_MKBP_TABLET_MODE, 1098c2ecf20Sopenharmony_ci }, 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* 1138c2ecf20Sopenharmony_ci * Returns true when there is at least one combination of pressed keys that 1148c2ecf20Sopenharmony_ci * results in ghosting. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_cistatic bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci int col1, col2, buf1, buf2; 1198c2ecf20Sopenharmony_ci struct device *dev = ckdev->dev; 1208c2ecf20Sopenharmony_ci uint8_t *valid_keys = ckdev->valid_keys; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* 1238c2ecf20Sopenharmony_ci * Ghosting happens if for any pressed key X there are other keys 1248c2ecf20Sopenharmony_ci * pressed both in the same row and column of X as, for instance, 1258c2ecf20Sopenharmony_ci * in the following diagram: 1268c2ecf20Sopenharmony_ci * 1278c2ecf20Sopenharmony_ci * . . Y . g . 1288c2ecf20Sopenharmony_ci * . . . . . . 1298c2ecf20Sopenharmony_ci * . . . . . . 1308c2ecf20Sopenharmony_ci * . . X . Z . 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * In this case only X, Y, and Z are pressed, but g appears to be 1338c2ecf20Sopenharmony_ci * pressed too (see Wikipedia). 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci for (col1 = 0; col1 < ckdev->cols; col1++) { 1368c2ecf20Sopenharmony_ci buf1 = buf[col1] & valid_keys[col1]; 1378c2ecf20Sopenharmony_ci for (col2 = col1 + 1; col2 < ckdev->cols; col2++) { 1388c2ecf20Sopenharmony_ci buf2 = buf[col2] & valid_keys[col2]; 1398c2ecf20Sopenharmony_ci if (hweight8(buf1 & buf2) > 1) { 1408c2ecf20Sopenharmony_ci dev_dbg(dev, "ghost found at: B[%02d]:0x%02x & B[%02d]:0x%02x", 1418c2ecf20Sopenharmony_ci col1, buf1, col2, buf2); 1428c2ecf20Sopenharmony_ci return true; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return false; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* 1528c2ecf20Sopenharmony_ci * Compares the new keyboard state to the old one and produces key 1538c2ecf20Sopenharmony_ci * press/release events accordingly. The keyboard state is 13 bytes (one byte 1548c2ecf20Sopenharmony_ci * per column) 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_cistatic void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, 1578c2ecf20Sopenharmony_ci uint8_t *kb_state, int len) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct input_dev *idev = ckdev->idev; 1608c2ecf20Sopenharmony_ci int col, row; 1618c2ecf20Sopenharmony_ci int new_state; 1628c2ecf20Sopenharmony_ci int old_state; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (ckdev->ghost_filter && cros_ec_keyb_has_ghosting(ckdev, kb_state)) { 1658c2ecf20Sopenharmony_ci /* 1668c2ecf20Sopenharmony_ci * Simple-minded solution: ignore this state. The obvious 1678c2ecf20Sopenharmony_ci * improvement is to only ignore changes to keys involved in 1688c2ecf20Sopenharmony_ci * the ghosting, but process the other changes. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci dev_dbg(ckdev->dev, "ghosting found\n"); 1718c2ecf20Sopenharmony_ci return; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci for (col = 0; col < ckdev->cols; col++) { 1758c2ecf20Sopenharmony_ci for (row = 0; row < ckdev->rows; row++) { 1768c2ecf20Sopenharmony_ci int pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); 1778c2ecf20Sopenharmony_ci const unsigned short *keycodes = idev->keycode; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci new_state = kb_state[col] & (1 << row); 1808c2ecf20Sopenharmony_ci old_state = ckdev->old_kb_state[col] & (1 << row); 1818c2ecf20Sopenharmony_ci if (new_state != old_state) { 1828c2ecf20Sopenharmony_ci dev_dbg(ckdev->dev, 1838c2ecf20Sopenharmony_ci "changed: [r%d c%d]: byte %02x\n", 1848c2ecf20Sopenharmony_ci row, col, new_state); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci input_event(idev, EV_MSC, MSC_SCAN, pos); 1878c2ecf20Sopenharmony_ci input_report_key(idev, keycodes[pos], 1888c2ecf20Sopenharmony_ci new_state); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci ckdev->old_kb_state[col] = kb_state[col]; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci input_sync(ckdev->idev); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/** 1978c2ecf20Sopenharmony_ci * cros_ec_keyb_report_bs - Report non-matrixed buttons or switches 1988c2ecf20Sopenharmony_ci * 1998c2ecf20Sopenharmony_ci * This takes a bitmap of buttons or switches from the EC and reports events, 2008c2ecf20Sopenharmony_ci * syncing at the end. 2018c2ecf20Sopenharmony_ci * 2028c2ecf20Sopenharmony_ci * @ckdev: The keyboard device. 2038c2ecf20Sopenharmony_ci * @ev_type: The input event type (e.g., EV_KEY). 2048c2ecf20Sopenharmony_ci * @mask: A bitmap of buttons from the EC. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_cistatic void cros_ec_keyb_report_bs(struct cros_ec_keyb *ckdev, 2078c2ecf20Sopenharmony_ci unsigned int ev_type, u32 mask) 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct input_dev *idev = ckdev->bs_idev; 2118c2ecf20Sopenharmony_ci int i; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cros_ec_keyb_bs); i++) { 2148c2ecf20Sopenharmony_ci const struct cros_ec_bs_map *map = &cros_ec_keyb_bs[i]; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (map->ev_type != ev_type) 2178c2ecf20Sopenharmony_ci continue; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci input_event(idev, ev_type, map->code, 2208c2ecf20Sopenharmony_ci !!(mask & BIT(map->bit)) ^ map->inverted); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci input_sync(idev); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int cros_ec_keyb_work(struct notifier_block *nb, 2268c2ecf20Sopenharmony_ci unsigned long queued_during_suspend, void *_notify) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb, 2298c2ecf20Sopenharmony_ci notifier); 2308c2ecf20Sopenharmony_ci u32 val; 2318c2ecf20Sopenharmony_ci unsigned int ev_type; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* 2348c2ecf20Sopenharmony_ci * If not wake enabled, discard key state changes during 2358c2ecf20Sopenharmony_ci * suspend. Switches will be re-checked in 2368c2ecf20Sopenharmony_ci * cros_ec_keyb_resume() to be sure nothing is lost. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci if (queued_during_suspend && !device_may_wakeup(ckdev->dev)) 2398c2ecf20Sopenharmony_ci return NOTIFY_OK; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci switch (ckdev->ec->event_data.event_type) { 2428c2ecf20Sopenharmony_ci case EC_MKBP_EVENT_KEY_MATRIX: 2438c2ecf20Sopenharmony_ci pm_wakeup_event(ckdev->dev, 0); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (ckdev->ec->event_size != ckdev->cols) { 2468c2ecf20Sopenharmony_ci dev_err(ckdev->dev, 2478c2ecf20Sopenharmony_ci "Discarded incomplete key matrix event.\n"); 2488c2ecf20Sopenharmony_ci return NOTIFY_OK; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci cros_ec_keyb_process(ckdev, 2528c2ecf20Sopenharmony_ci ckdev->ec->event_data.data.key_matrix, 2538c2ecf20Sopenharmony_ci ckdev->ec->event_size); 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci case EC_MKBP_EVENT_SYSRQ: 2578c2ecf20Sopenharmony_ci pm_wakeup_event(ckdev->dev, 0); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci val = get_unaligned_le32(&ckdev->ec->event_data.data.sysrq); 2608c2ecf20Sopenharmony_ci dev_dbg(ckdev->dev, "sysrq code from EC: %#x\n", val); 2618c2ecf20Sopenharmony_ci handle_sysrq(val); 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci case EC_MKBP_EVENT_BUTTON: 2658c2ecf20Sopenharmony_ci case EC_MKBP_EVENT_SWITCH: 2668c2ecf20Sopenharmony_ci pm_wakeup_event(ckdev->dev, 0); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) { 2698c2ecf20Sopenharmony_ci val = get_unaligned_le32( 2708c2ecf20Sopenharmony_ci &ckdev->ec->event_data.data.buttons); 2718c2ecf20Sopenharmony_ci ev_type = EV_KEY; 2728c2ecf20Sopenharmony_ci } else { 2738c2ecf20Sopenharmony_ci val = get_unaligned_le32( 2748c2ecf20Sopenharmony_ci &ckdev->ec->event_data.data.switches); 2758c2ecf20Sopenharmony_ci ev_type = EV_SW; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci cros_ec_keyb_report_bs(ckdev, ev_type, val); 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci default: 2818c2ecf20Sopenharmony_ci return NOTIFY_DONE; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return NOTIFY_OK; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* 2888c2ecf20Sopenharmony_ci * Walks keycodes flipping bit in buffer COLUMNS deep where bit is ROW. Used by 2898c2ecf20Sopenharmony_ci * ghosting logic to ignore NULL or virtual keys. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_cistatic void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci int row, col; 2948c2ecf20Sopenharmony_ci int row_shift = ckdev->row_shift; 2958c2ecf20Sopenharmony_ci unsigned short *keymap = ckdev->idev->keycode; 2968c2ecf20Sopenharmony_ci unsigned short code; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci BUG_ON(ckdev->idev->keycodesize != sizeof(*keymap)); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci for (col = 0; col < ckdev->cols; col++) { 3018c2ecf20Sopenharmony_ci for (row = 0; row < ckdev->rows; row++) { 3028c2ecf20Sopenharmony_ci code = keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; 3038c2ecf20Sopenharmony_ci if (code && (code != KEY_BATTERY)) 3048c2ecf20Sopenharmony_ci ckdev->valid_keys[col] |= 1 << row; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci dev_dbg(ckdev->dev, "valid_keys[%02d] = 0x%02x\n", 3078c2ecf20Sopenharmony_ci col, ckdev->valid_keys[col]); 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/** 3128c2ecf20Sopenharmony_ci * cros_ec_keyb_info - Wrap the EC command EC_CMD_MKBP_INFO 3138c2ecf20Sopenharmony_ci * 3148c2ecf20Sopenharmony_ci * This wraps the EC_CMD_MKBP_INFO, abstracting out all of the marshalling and 3158c2ecf20Sopenharmony_ci * unmarshalling and different version nonsense into something simple. 3168c2ecf20Sopenharmony_ci * 3178c2ecf20Sopenharmony_ci * @ec_dev: The EC device 3188c2ecf20Sopenharmony_ci * @info_type: Either EC_MKBP_INFO_SUPPORTED or EC_MKBP_INFO_CURRENT. 3198c2ecf20Sopenharmony_ci * @event_type: Either EC_MKBP_EVENT_BUTTON or EC_MKBP_EVENT_SWITCH. Actually 3208c2ecf20Sopenharmony_ci * in some cases this could be EC_MKBP_EVENT_KEY_MATRIX or 3218c2ecf20Sopenharmony_ci * EC_MKBP_EVENT_HOST_EVENT too but we don't use in this driver. 3228c2ecf20Sopenharmony_ci * @result: Where we'll store the result; a union 3238c2ecf20Sopenharmony_ci * @result_size: The size of the result. Expected to be the size of one of 3248c2ecf20Sopenharmony_ci * the elements in the union. 3258c2ecf20Sopenharmony_ci * 3268c2ecf20Sopenharmony_ci * Returns 0 if no error or -error upon error. 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_cistatic int cros_ec_keyb_info(struct cros_ec_device *ec_dev, 3298c2ecf20Sopenharmony_ci enum ec_mkbp_info_type info_type, 3308c2ecf20Sopenharmony_ci enum ec_mkbp_event event_type, 3318c2ecf20Sopenharmony_ci union ec_response_get_next_data *result, 3328c2ecf20Sopenharmony_ci size_t result_size) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct ec_params_mkbp_info *params; 3358c2ecf20Sopenharmony_ci struct cros_ec_command *msg; 3368c2ecf20Sopenharmony_ci int ret; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci msg = kzalloc(sizeof(*msg) + max_t(size_t, result_size, 3398c2ecf20Sopenharmony_ci sizeof(*params)), GFP_KERNEL); 3408c2ecf20Sopenharmony_ci if (!msg) 3418c2ecf20Sopenharmony_ci return -ENOMEM; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci msg->command = EC_CMD_MKBP_INFO; 3448c2ecf20Sopenharmony_ci msg->version = 1; 3458c2ecf20Sopenharmony_ci msg->outsize = sizeof(*params); 3468c2ecf20Sopenharmony_ci msg->insize = result_size; 3478c2ecf20Sopenharmony_ci params = (struct ec_params_mkbp_info *)msg->data; 3488c2ecf20Sopenharmony_ci params->info_type = info_type; 3498c2ecf20Sopenharmony_ci params->event_type = event_type; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci ret = cros_ec_cmd_xfer_status(ec_dev, msg); 3528c2ecf20Sopenharmony_ci if (ret == -ENOPROTOOPT) { 3538c2ecf20Sopenharmony_ci /* With older ECs we just return 0 for everything */ 3548c2ecf20Sopenharmony_ci memset(result, 0, result_size); 3558c2ecf20Sopenharmony_ci ret = 0; 3568c2ecf20Sopenharmony_ci } else if (ret < 0) { 3578c2ecf20Sopenharmony_ci dev_warn(ec_dev->dev, "Transfer error %d/%d: %d\n", 3588c2ecf20Sopenharmony_ci (int)info_type, (int)event_type, ret); 3598c2ecf20Sopenharmony_ci } else if (ret != result_size) { 3608c2ecf20Sopenharmony_ci dev_warn(ec_dev->dev, "Wrong size %d/%d: %d != %zu\n", 3618c2ecf20Sopenharmony_ci (int)info_type, (int)event_type, 3628c2ecf20Sopenharmony_ci ret, result_size); 3638c2ecf20Sopenharmony_ci ret = -EPROTO; 3648c2ecf20Sopenharmony_ci } else { 3658c2ecf20Sopenharmony_ci memcpy(result, msg->data, result_size); 3668c2ecf20Sopenharmony_ci ret = 0; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci kfree(msg); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return ret; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci/** 3758c2ecf20Sopenharmony_ci * cros_ec_keyb_query_switches - Query the state of switches and report 3768c2ecf20Sopenharmony_ci * 3778c2ecf20Sopenharmony_ci * This will ask the EC about the current state of switches and report to the 3788c2ecf20Sopenharmony_ci * kernel. Note that we don't query for buttons because they are more 3798c2ecf20Sopenharmony_ci * transitory and we'll get an update on the next release / press. 3808c2ecf20Sopenharmony_ci * 3818c2ecf20Sopenharmony_ci * @ckdev: The keyboard device 3828c2ecf20Sopenharmony_ci * 3838c2ecf20Sopenharmony_ci * Returns 0 if no error or -error upon error. 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_cistatic int cros_ec_keyb_query_switches(struct cros_ec_keyb *ckdev) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct cros_ec_device *ec_dev = ckdev->ec; 3888c2ecf20Sopenharmony_ci union ec_response_get_next_data event_data = {}; 3898c2ecf20Sopenharmony_ci int ret; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci ret = cros_ec_keyb_info(ec_dev, EC_MKBP_INFO_CURRENT, 3928c2ecf20Sopenharmony_ci EC_MKBP_EVENT_SWITCH, &event_data, 3938c2ecf20Sopenharmony_ci sizeof(event_data.switches)); 3948c2ecf20Sopenharmony_ci if (ret) 3958c2ecf20Sopenharmony_ci return ret; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci cros_ec_keyb_report_bs(ckdev, EV_SW, 3988c2ecf20Sopenharmony_ci get_unaligned_le32(&event_data.switches)); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci return 0; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/** 4048c2ecf20Sopenharmony_ci * cros_ec_keyb_resume - Resume the keyboard 4058c2ecf20Sopenharmony_ci * 4068c2ecf20Sopenharmony_ci * We use the resume notification as a chance to query the EC for switches. 4078c2ecf20Sopenharmony_ci * 4088c2ecf20Sopenharmony_ci * @dev: The keyboard device 4098c2ecf20Sopenharmony_ci * 4108c2ecf20Sopenharmony_ci * Returns 0 if no error or -error upon error. 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_cistatic __maybe_unused int cros_ec_keyb_resume(struct device *dev) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (ckdev->bs_idev) 4178c2ecf20Sopenharmony_ci return cros_ec_keyb_query_switches(ckdev); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci/** 4238c2ecf20Sopenharmony_ci * cros_ec_keyb_register_bs - Register non-matrix buttons/switches 4248c2ecf20Sopenharmony_ci * 4258c2ecf20Sopenharmony_ci * Handles all the bits of the keyboard driver related to non-matrix buttons 4268c2ecf20Sopenharmony_ci * and switches, including asking the EC about which are present and telling 4278c2ecf20Sopenharmony_ci * the kernel to expect them. 4288c2ecf20Sopenharmony_ci * 4298c2ecf20Sopenharmony_ci * If this device has no support for buttons and switches we'll return no error 4308c2ecf20Sopenharmony_ci * but the ckdev->bs_idev will remain NULL when this function exits. 4318c2ecf20Sopenharmony_ci * 4328c2ecf20Sopenharmony_ci * @ckdev: The keyboard device 4338c2ecf20Sopenharmony_ci * 4348c2ecf20Sopenharmony_ci * Returns 0 if no error or -error upon error. 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_cistatic int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct cros_ec_device *ec_dev = ckdev->ec; 4398c2ecf20Sopenharmony_ci struct device *dev = ckdev->dev; 4408c2ecf20Sopenharmony_ci struct input_dev *idev; 4418c2ecf20Sopenharmony_ci union ec_response_get_next_data event_data = {}; 4428c2ecf20Sopenharmony_ci const char *phys; 4438c2ecf20Sopenharmony_ci u32 buttons; 4448c2ecf20Sopenharmony_ci u32 switches; 4458c2ecf20Sopenharmony_ci int ret; 4468c2ecf20Sopenharmony_ci int i; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ret = cros_ec_keyb_info(ec_dev, EC_MKBP_INFO_SUPPORTED, 4498c2ecf20Sopenharmony_ci EC_MKBP_EVENT_BUTTON, &event_data, 4508c2ecf20Sopenharmony_ci sizeof(event_data.buttons)); 4518c2ecf20Sopenharmony_ci if (ret) 4528c2ecf20Sopenharmony_ci return ret; 4538c2ecf20Sopenharmony_ci buttons = get_unaligned_le32(&event_data.buttons); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci ret = cros_ec_keyb_info(ec_dev, EC_MKBP_INFO_SUPPORTED, 4568c2ecf20Sopenharmony_ci EC_MKBP_EVENT_SWITCH, &event_data, 4578c2ecf20Sopenharmony_ci sizeof(event_data.switches)); 4588c2ecf20Sopenharmony_ci if (ret) 4598c2ecf20Sopenharmony_ci return ret; 4608c2ecf20Sopenharmony_ci switches = get_unaligned_le32(&event_data.switches); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (!buttons && !switches) 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* 4668c2ecf20Sopenharmony_ci * We call the non-matrix buttons/switches 'input1', if present. 4678c2ecf20Sopenharmony_ci * Allocate phys before input dev, to ensure correct tear-down 4688c2ecf20Sopenharmony_ci * ordering. 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci phys = devm_kasprintf(dev, GFP_KERNEL, "%s/input1", ec_dev->phys_name); 4718c2ecf20Sopenharmony_ci if (!phys) 4728c2ecf20Sopenharmony_ci return -ENOMEM; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci idev = devm_input_allocate_device(dev); 4758c2ecf20Sopenharmony_ci if (!idev) 4768c2ecf20Sopenharmony_ci return -ENOMEM; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci idev->name = "cros_ec_buttons"; 4798c2ecf20Sopenharmony_ci idev->phys = phys; 4808c2ecf20Sopenharmony_ci __set_bit(EV_REP, idev->evbit); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci idev->id.bustype = BUS_VIRTUAL; 4838c2ecf20Sopenharmony_ci idev->id.version = 1; 4848c2ecf20Sopenharmony_ci idev->id.product = 0; 4858c2ecf20Sopenharmony_ci idev->dev.parent = dev; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci input_set_drvdata(idev, ckdev); 4888c2ecf20Sopenharmony_ci ckdev->bs_idev = idev; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cros_ec_keyb_bs); i++) { 4918c2ecf20Sopenharmony_ci const struct cros_ec_bs_map *map = &cros_ec_keyb_bs[i]; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if ((map->ev_type == EV_KEY && (buttons & BIT(map->bit))) || 4948c2ecf20Sopenharmony_ci (map->ev_type == EV_SW && (switches & BIT(map->bit)))) 4958c2ecf20Sopenharmony_ci input_set_capability(idev, map->ev_type, map->code); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci ret = cros_ec_keyb_query_switches(ckdev); 4998c2ecf20Sopenharmony_ci if (ret) { 5008c2ecf20Sopenharmony_ci dev_err(dev, "cannot query switches\n"); 5018c2ecf20Sopenharmony_ci return ret; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci ret = input_register_device(ckdev->bs_idev); 5058c2ecf20Sopenharmony_ci if (ret) { 5068c2ecf20Sopenharmony_ci dev_err(dev, "cannot register input device\n"); 5078c2ecf20Sopenharmony_ci return ret; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci/** 5148c2ecf20Sopenharmony_ci * cros_ec_keyb_register_bs - Register matrix keys 5158c2ecf20Sopenharmony_ci * 5168c2ecf20Sopenharmony_ci * Handles all the bits of the keyboard driver related to matrix keys. 5178c2ecf20Sopenharmony_ci * 5188c2ecf20Sopenharmony_ci * @ckdev: The keyboard device 5198c2ecf20Sopenharmony_ci * 5208c2ecf20Sopenharmony_ci * Returns 0 if no error or -error upon error. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_cistatic int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct cros_ec_device *ec_dev = ckdev->ec; 5258c2ecf20Sopenharmony_ci struct device *dev = ckdev->dev; 5268c2ecf20Sopenharmony_ci struct input_dev *idev; 5278c2ecf20Sopenharmony_ci const char *phys; 5288c2ecf20Sopenharmony_ci int err; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols); 5318c2ecf20Sopenharmony_ci if (err) 5328c2ecf20Sopenharmony_ci return err; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci ckdev->valid_keys = devm_kzalloc(dev, ckdev->cols, GFP_KERNEL); 5358c2ecf20Sopenharmony_ci if (!ckdev->valid_keys) 5368c2ecf20Sopenharmony_ci return -ENOMEM; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci ckdev->old_kb_state = devm_kzalloc(dev, ckdev->cols, GFP_KERNEL); 5398c2ecf20Sopenharmony_ci if (!ckdev->old_kb_state) 5408c2ecf20Sopenharmony_ci return -ENOMEM; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* 5438c2ecf20Sopenharmony_ci * We call the keyboard matrix 'input0'. Allocate phys before input 5448c2ecf20Sopenharmony_ci * dev, to ensure correct tear-down ordering. 5458c2ecf20Sopenharmony_ci */ 5468c2ecf20Sopenharmony_ci phys = devm_kasprintf(dev, GFP_KERNEL, "%s/input0", ec_dev->phys_name); 5478c2ecf20Sopenharmony_ci if (!phys) 5488c2ecf20Sopenharmony_ci return -ENOMEM; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci idev = devm_input_allocate_device(dev); 5518c2ecf20Sopenharmony_ci if (!idev) 5528c2ecf20Sopenharmony_ci return -ENOMEM; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci idev->name = CROS_EC_DEV_NAME; 5558c2ecf20Sopenharmony_ci idev->phys = phys; 5568c2ecf20Sopenharmony_ci __set_bit(EV_REP, idev->evbit); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci idev->id.bustype = BUS_VIRTUAL; 5598c2ecf20Sopenharmony_ci idev->id.version = 1; 5608c2ecf20Sopenharmony_ci idev->id.product = 0; 5618c2ecf20Sopenharmony_ci idev->dev.parent = dev; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci ckdev->ghost_filter = of_property_read_bool(dev->of_node, 5648c2ecf20Sopenharmony_ci "google,needs-ghost-filter"); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols, 5678c2ecf20Sopenharmony_ci NULL, idev); 5688c2ecf20Sopenharmony_ci if (err) { 5698c2ecf20Sopenharmony_ci dev_err(dev, "cannot build key matrix\n"); 5708c2ecf20Sopenharmony_ci return err; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci ckdev->row_shift = get_count_order(ckdev->cols); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci input_set_capability(idev, EV_MSC, MSC_SCAN); 5768c2ecf20Sopenharmony_ci input_set_drvdata(idev, ckdev); 5778c2ecf20Sopenharmony_ci ckdev->idev = idev; 5788c2ecf20Sopenharmony_ci cros_ec_keyb_compute_valid_keys(ckdev); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci err = input_register_device(ckdev->idev); 5818c2ecf20Sopenharmony_ci if (err) { 5828c2ecf20Sopenharmony_ci dev_err(dev, "cannot register input device\n"); 5838c2ecf20Sopenharmony_ci return err; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci return 0; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic int cros_ec_keyb_probe(struct platform_device *pdev) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); 5928c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 5938c2ecf20Sopenharmony_ci struct cros_ec_keyb *ckdev; 5948c2ecf20Sopenharmony_ci int err; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (!dev->of_node) 5978c2ecf20Sopenharmony_ci return -ENODEV; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci ckdev = devm_kzalloc(dev, sizeof(*ckdev), GFP_KERNEL); 6008c2ecf20Sopenharmony_ci if (!ckdev) 6018c2ecf20Sopenharmony_ci return -ENOMEM; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci ckdev->ec = ec; 6048c2ecf20Sopenharmony_ci ckdev->dev = dev; 6058c2ecf20Sopenharmony_ci dev_set_drvdata(dev, ckdev); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci err = cros_ec_keyb_register_matrix(ckdev); 6088c2ecf20Sopenharmony_ci if (err) { 6098c2ecf20Sopenharmony_ci dev_err(dev, "cannot register matrix inputs: %d\n", err); 6108c2ecf20Sopenharmony_ci return err; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci err = cros_ec_keyb_register_bs(ckdev); 6148c2ecf20Sopenharmony_ci if (err) { 6158c2ecf20Sopenharmony_ci dev_err(dev, "cannot register non-matrix inputs: %d\n", err); 6168c2ecf20Sopenharmony_ci return err; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci ckdev->notifier.notifier_call = cros_ec_keyb_work; 6208c2ecf20Sopenharmony_ci err = blocking_notifier_chain_register(&ckdev->ec->event_notifier, 6218c2ecf20Sopenharmony_ci &ckdev->notifier); 6228c2ecf20Sopenharmony_ci if (err) { 6238c2ecf20Sopenharmony_ci dev_err(dev, "cannot register notifier: %d\n", err); 6248c2ecf20Sopenharmony_ci return err; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci device_init_wakeup(ckdev->dev, true); 6288c2ecf20Sopenharmony_ci return 0; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic int cros_ec_keyb_remove(struct platform_device *pdev) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci struct cros_ec_keyb *ckdev = dev_get_drvdata(&pdev->dev); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci blocking_notifier_chain_unregister(&ckdev->ec->event_notifier, 6368c2ecf20Sopenharmony_ci &ckdev->notifier); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci return 0; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 6428c2ecf20Sopenharmony_cistatic const struct of_device_id cros_ec_keyb_of_match[] = { 6438c2ecf20Sopenharmony_ci { .compatible = "google,cros-ec-keyb" }, 6448c2ecf20Sopenharmony_ci {}, 6458c2ecf20Sopenharmony_ci}; 6468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, cros_ec_keyb_of_match); 6478c2ecf20Sopenharmony_ci#endif 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cros_ec_keyb_pm_ops, NULL, cros_ec_keyb_resume); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic struct platform_driver cros_ec_keyb_driver = { 6528c2ecf20Sopenharmony_ci .probe = cros_ec_keyb_probe, 6538c2ecf20Sopenharmony_ci .remove = cros_ec_keyb_remove, 6548c2ecf20Sopenharmony_ci .driver = { 6558c2ecf20Sopenharmony_ci .name = "cros-ec-keyb", 6568c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(cros_ec_keyb_of_match), 6578c2ecf20Sopenharmony_ci .pm = &cros_ec_keyb_pm_ops, 6588c2ecf20Sopenharmony_ci }, 6598c2ecf20Sopenharmony_ci}; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cimodule_platform_driver(cros_ec_keyb_driver); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 6648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ChromeOS EC keyboard driver"); 6658c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:cros-ec-keyb"); 666