summaryrefslogtreecommitdiff
path: root/zephyr/drivers/cros_kb_raw/cros_kb_raw_npcx.c
diff options
context:
space:
mode:
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.c239
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);