diff options
Diffstat (limited to 'zephyr/drivers/cros_kb_raw/cros_kb_raw_npcx.c')
-rw-r--r-- | zephyr/drivers/cros_kb_raw/cros_kb_raw_npcx.c | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/zephyr/drivers/cros_kb_raw/cros_kb_raw_npcx.c b/zephyr/drivers/cros_kb_raw/cros_kb_raw_npcx.c new file mode 100644 index 0000000000..4858d5d251 --- /dev/null +++ b/zephyr/drivers/cros_kb_raw/cros_kb_raw_npcx.c @@ -0,0 +1,239 @@ +/* + * Copyright 2020 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nuvoton_npcx_cros_kb_raw + +#include <assert.h> +#include <dt-bindings/clock/npcx_clock.h> +#include <drivers/cros_kb_raw.h> +#include <drivers/clock_control.h> +#include <drivers/gpio.h> +#include <kernel.h> +#include <soc.h> +#include <soc/nuvoton_npcx/reg_def_cros.h> + +#include "ec_tasks.h" +#include "keyboard_raw.h" +#include "soc_miwu.h" +#include "task.h" + +#include <logging/log.h> +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); |