/* * Copyright 2020 Google LLC * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nuvoton_npcx_cros_kb_raw #include #include #include #include #include #include #include #include #include "ec_tasks.h" #include "keyboard_raw.h" #include "soc_miwu.h" #include "task.h" #include LOG_MODULE_REGISTER(cros_kb_raw, LOG_LEVEL_ERR); #define NPCX_MAX_KEY_COLS 18 /* Maximum rows of keyboard matrix */ #define NPCX_MAX_KEY_ROWS 8 /* Maximum columns of keyboard matrix */ #define NPCX_KB_ROW_MASK (BIT(NPCX_MAX_KEY_ROWS) - 1) /* Device config */ struct cros_kb_raw_npcx_config { /* keyboard scan controller base address */ uintptr_t base; /* clock configuration */ struct npcx_clk_cfg clk_cfg; /* pinmux configuration */ const uint8_t alts_size; const struct npcx_alt *alts_list; /* Keyboard scan input (KSI) wake-up irq */ int irq; /* Size of keyboard inputs-wui mapping array */ int wui_size; /* Mapping table between keyboard inputs and wui */ struct npcx_wui wui_maps[]; }; /* Driver convenience defines */ #define DRV_CONFIG(dev) ((const struct cros_kb_raw_npcx_config *)(dev)->config) #define HAL_INSTANCE(dev) (struct kbs_reg *)(DRV_CONFIG(dev)->base) /* Keyboard Scan local functions */ static struct miwu_dev_callback ksi_callback[NPCX_MAX_KEY_ROWS]; static void kb_raw_npcx_init_ksi_wui_callback( const struct device *dev, struct miwu_dev_callback *callback, const struct npcx_wui *wui, miwu_dev_callback_handler_t handler) { /* KSI signal which has no wake-up input source */ if (wui->table == NPCX_MIWU_TABLE_NONE) return; /* Install callback function */ npcx_miwu_init_dev_callback(callback, wui, handler, dev); npcx_miwu_manage_dev_callback(callback, 1); /* Configure MIWU setting and enable its interrupt */ npcx_miwu_interrupt_configure(wui, NPCX_MIWU_MODE_EDGE, NPCX_MIWU_TRIG_BOTH); npcx_miwu_irq_enable(wui); } static int kb_raw_npcx_init(const struct device *dev) { const struct cros_kb_raw_npcx_config *const config = DRV_CONFIG(dev); const struct device *const clk_dev = device_get_binding(NPCX_CLK_CTRL_NAME); int ret; /* Turn on device clock first and get source clock freq. */ ret = clock_control_on(clk_dev, (clock_control_subsys_t *)&config->clk_cfg); if (ret < 0) { LOG_ERR("Turn on KSCAN clock fail %d", ret); return ret; } return 0; } /* Cros ec keyboard raw api functions */ static void cros_kb_raw_npcx_enable_interrupt(const struct device *dev, int enable) { const struct cros_kb_raw_npcx_config *const config = DRV_CONFIG(dev); if (enable) irq_enable(config->irq); else irq_disable(config->irq); } static int cros_kb_raw_npcx_read_row(const struct device *dev) { struct kbs_reg *const inst = HAL_INSTANCE(dev); int val; val = inst->KBSIN; LOG_DBG("rows raw %02x", val); /* 1 means key pressed, otherwise means key released. */ return (~val & NPCX_KB_ROW_MASK); } static int cros_kb_raw_npcx_drive_column(const struct device *dev, int col) { struct kbs_reg *const inst = HAL_INSTANCE(dev); /* * Nuvoton 'Keyboard Scan' module supports 18x8 matrix * It also support automatic scan functionality. */ uint32_t mask, col_out; /* Add support for CONFIG_KEYBOARD_KSO_BASE shifting */ col_out = col + CONFIG_KEYBOARD_KSO_BASE; /* Drive all lines to high. ie. Key detection is disabled. */ if (col == KEYBOARD_COLUMN_NONE) { mask = ~0; if (IS_ENABLED(CONFIG_PLATFORM_EC_KEYBOARD_COL2_INVERTED)) { gpio_set_level(GPIO_KBD_KSO2, 0); } } /* Drive all lines to low for detection any key press */ else if (col == KEYBOARD_COLUMN_ALL) { mask = ~(BIT(keyboard_cols) - 1); if (IS_ENABLED(CONFIG_PLATFORM_EC_KEYBOARD_COL2_INVERTED)) { gpio_set_level(GPIO_KBD_KSO2, 1); } } /* Drive one line to low for determining which key's state changed. */ else { if (IS_ENABLED(CONFIG_PLATFORM_EC_KEYBOARD_COL2_INVERTED)) { if (col == 2) gpio_set_level(GPIO_KBD_KSO2, 1); else gpio_set_level(GPIO_KBD_KSO2, 0); } mask = ~BIT(col_out); } /* Set KBSOUT */ inst->KBSOUT0 = (mask & 0xFFFF); inst->KBSOUT1 = ((mask >> 16) & 0x03); return 0; } static void cros_kb_raw_npcx_ksi_isr(const struct device *dev, struct npcx_wui *wui) { ARG_UNUSED(dev); ARG_UNUSED(wui); LOG_DBG("%s: KSI%d is changed", __func__, wui->bit); /* Wake-up keyboard scan task */ task_wake(TASK_ID_KEYSCAN); } static int cros_kb_raw_npcx_init(const struct device *dev) { const struct cros_kb_raw_npcx_config *const config = DRV_CONFIG(dev); struct kbs_reg *const inst = HAL_INSTANCE(dev); /* Pull-up KBSIN0-7 internally */ inst->KBSINPU = 0xFF; /* * Keyboard Scan Control Register * * [6:7] - KBHDRV KBSOUTn signals output buffers are open-drain. * [3] - KBSINC Auto-increment of Buffer Data register is disabled * [2] - KBSIEN Interrupt of Auto-Scan is disabled * [1] - KBSMODE Key detection mechanism is implemented by firmware * [0] - START Write 0 to this field is not affected */ inst->KBSCTL = 0x00; /* * Select quasi-bidirectional buffers for KSO pins. It reduces the * low-to-high transition time. This feature only supports in npcx7. */ if (IS_ENABLED(CONFIG_KEYBOARD_KSO_HIGH_DRIVE)) { SET_FIELD(inst->KBSCTL, NPCX_KBSCTL_KBHDRV_FIELD, 0x01); } /* Configure pin-mux for kscan device */ npcx_pinctrl_mux_configure(config->alts_list, config->alts_size, 1); /* Drive all column lines to low for detection any key press */ cros_kb_raw_npcx_drive_column(dev, KEYBOARD_COLUMN_ALL); /* Configure wake-up input and callback for keyboard input signal */ for (int i = 0; i < ARRAY_SIZE(ksi_callback); i++) kb_raw_npcx_init_ksi_wui_callback(dev, &ksi_callback[i], &config->wui_maps[i], cros_kb_raw_npcx_ksi_isr); return 0; } static const struct cros_kb_raw_driver_api cros_kb_raw_npcx_driver_api = { .init = cros_kb_raw_npcx_init, .drive_colum = cros_kb_raw_npcx_drive_column, .read_rows = cros_kb_raw_npcx_read_row, }; static const struct npcx_alt cros_kb_raw_alts[] = DT_NPCX_ALT_ITEMS_LIST(0); static const struct cros_kb_raw_npcx_config cros_kb_raw_cfg = { .base = DT_INST_REG_ADDR(0), .alts_size = ARRAY_SIZE(cros_kb_raw_alts), .alts_list = cros_kb_raw_alts, .clk_cfg = DT_NPCX_CLK_CFG_ITEM(0), .irq = DT_INST_IRQN(0), .wui_size = DT_NPCX_WUI_ITEMS_LEN(0), .wui_maps = DT_NPCX_WUI_ITEMS_LIST(0), }; DEVICE_AND_API_INIT(cros_kb_raw_npcx_0, DT_INST_LABEL(0), kb_raw_npcx_init, NULL, &cros_kb_raw_cfg, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &cros_kb_raw_npcx_driver_api); /* KBS register structure check */ NPCX_REG_SIZE_CHECK(kbs_reg, 0x010); NPCX_REG_OFFSET_CHECK(kbs_reg, KBSIN, 0x004); NPCX_REG_OFFSET_CHECK(kbs_reg, KBSOUT0, 0x006); NPCX_REG_OFFSET_CHECK(kbs_reg, KBS_BUF_INDX, 0x00a);