summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCHLin <CHLIN56@nuvoton.com>2019-12-25 16:29:14 +0800
committerCommit Bot <commit-bot@chromium.org>2020-01-30 20:23:49 +0000
commitd79ee1cba05d36d568ed6f30ba5dadb45fd57681 (patch)
treeb2fcfdfba2da96fe0efc8daf9ba8b605d40f501b
parent85902285e20f4e6d8390d944b48ccb96ee252f40 (diff)
downloadchrome-ec-d79ee1cba05d36d568ed6f30ba5dadb45fd57681.tar.gz
npcx: Add driver support for PS/2 interface
Morphius connects the trackpoint device to EC via the PS/2 interface. To support it, we implemented the chip level PS/2 driver in this CL. The PS/2 driver can be used on all series of NPCX EC chips (NPCX5/7). BUG=b:145575366 BRANCH=none TEST=No error for "make buildall" TEST=Apply this and related CLs, connect npcx5/npcx7 EVBs to standard PS/2 keyboards and PS/2 device emulator with different channels. Verify that the PS/2 write/read transaction can keep working for several hours without issue. Change-Id: I5bae313db2d697999c2da5cf33478be2da754b8c Signed-off-by: CHLin <CHLIN56@nuvoton.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1982302 Tested-by: CH Lin <chlin56@nuvoton.com> Commit-Queue: Edward Hill <ecgh@chromium.org> Auto-Submit: CH Lin <chlin56@nuvoton.com> Reviewed-by: Edward Hill <ecgh@chromium.org>
-rw-r--r--chip/npcx/build.mk2
-rw-r--r--chip/npcx/gpio_chip-npcx5.h22
-rw-r--r--chip/npcx/gpio_chip-npcx7.h31
-rw-r--r--chip/npcx/ps2.c366
-rw-r--r--chip/npcx/ps2_chip.h24
-rw-r--r--chip/npcx/registers.h58
-rw-r--r--include/config.h4
-rw-r--r--include/console_channel.inc3
-rw-r--r--include/module_id.h1
-rw-r--r--include/task.h1
10 files changed, 506 insertions, 6 deletions
diff --git a/chip/npcx/build.mk b/chip/npcx/build.mk
index 6092503b06..81294edfbb 100644
--- a/chip/npcx/build.mk
+++ b/chip/npcx/build.mk
@@ -41,6 +41,8 @@ ifndef CONFIG_KEYBOARD_NOT_RAW
chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o
endif
+chip-$(CONFIG_PS2)+=ps2.o
+
# spi monitor program fw for openocd and UUT(UART Update Tool)
npcx-monitor-fw=chip/npcx/spiflashfw/npcx_monitor
npcx-monitor-fw-bin=${out}/$(npcx-monitor-fw).bin
diff --git a/chip/npcx/gpio_chip-npcx5.h b/chip/npcx/gpio_chip-npcx5.h
index 2a5f67e955..ee113aea44 100644
--- a/chip/npcx/gpio_chip-npcx5.h
+++ b/chip/npcx/gpio_chip-npcx5.h
@@ -206,7 +206,11 @@
/* MFT Module */
#define NPCX_ALT_GPIO_9_3 ALT(9, 3, NPCX_ALT(C, TA1_SL2)) /* TA1_SEL2 */
+#ifdef CONFIG_PS2
+#define NPCX_ALT_GPIO_A_6 ALT(A, 6, NPCX_ALT(C, PS2_3_SL2)) /* PS2_CLK3 */
+#else
#define NPCX_ALT_GPIO_A_6 ALT(A, 6, NPCX_ALT(C, TA2_SL2)) /* TA2_SEL2 */
+#endif
#define NPCX_ALT_GPIO_4_0 ALT(4, 0, NPCX_ALT(3, TA1_SL1)) /* TA1_SEL1 */
#define NPCX_ALT_GPIO_7_3 ALT(7, 3, NPCX_ALT(3, TA2_SL1)) /* TA2_SEL1 */
@@ -244,6 +248,15 @@
#define NPCX_ALT_GPIO_7_5 ALT(7, 5, NPCX_ALT(A, 32K_OUT_SL)) /* 32KHZ_OUT */
#define NPCX_ALT_GPIO_E_7 ALT(E, 7, NPCX_ALT(A, 32KCLKIN_SL)) /* 32KCLKIN */
+/* PS/2 module */
+#define NPCX_ALT_GPIO_6_7 ALT(6, 7, NPCX_ALT(3, PS2_0_SL)) /* PS2_CLK0 */
+#define NPCX_ALT_GPIO_7_0 ALT(7, 0, NPCX_ALT(3, PS2_0_SL)) /* PS2_DATA0 */
+#define NPCX_ALT_GPIO_6_2 ALT(6, 2, NPCX_ALT(3, PS2_1_SL)) /* PS2_CLK1 */
+#define NPCX_ALT_GPIO_6_3 ALT(6, 3, NPCX_ALT(3, PS2_1_SL)) /* PS2_DATA1 */
+#define NPCX_ALT_GPIO_3_7 ALT(3, 7, NPCX_ALT(3, PS2_2_SL)) /* PS2_CLK2 */
+#define NPCX_ALT_GPIO_3_4 ALT(3, 4, NPCX_ALT(3, PS2_2_SL)) /* PS2_DATA2 */
+#define NPCX_ALT_GPIO_A_7 ALT(A, 7, NPCX_ALT(C, PS2_3_SL2)) /* PS2_DAT3 */
+
#define NPCX_ALT_TABLE { \
NPCX_ALT_GPIO_0_3 /* KSO16 */ \
NPCX_ALT_GPIO_0_4 /* KSO13 */ \
@@ -268,6 +281,8 @@
NPCX_ALT_GPIO_2_7 /* KSI2 */ \
NPCX_ALT_GPIO_3_0 /* KSI1 */ \
NPCX_ALT_GPIO_3_1 /* KSI0 */ \
+ NPCX_ALT_GPIO_3_4 /* PS2_DAT2 */ \
+ NPCX_ALT_GPIO_3_7 /* PS2_CLK2 */ \
NPCX_ALT_GPIO_4_0 /* TA1_SEL1 */ \
NPCX_ALT_GPIO_4_1 /* ADC4 */ \
NPCX_ALT_GPIO_4_2 /* ADC3 */ \
@@ -275,8 +290,12 @@
NPCX_ALT_GPIO_4_5 /* ADC0 */ \
NPCX_ALT_GPIO_4_3 /* ADC2 */ \
NPCX_ALT_GPIO_6_0 /* PWM7 */ \
+ NPCX_ALT_GPIO_6_2 /* PS2_CLK1 */ \
+ NPCX_ALT_GPIO_6_3 /* PS2_DAT1 */ \
NPCX_ALT_GPIO_6_4 /* CR_SIN2 */ \
NPCX_ALT_GPIO_6_5 /* CR_SOUT2 */ \
+ NPCX_ALT_GPIO_6_7 /* PS2_CLK0 */ \
+ NPCX_ALT_GPIO_7_0 /* PS2_DAT0 */ \
NPCX_ALT_GPIO_7_3 /* TA2_SEL1 */ \
NPCX_ALT_GPIO_7_5 /* 32KHZ_OUT */ \
NPCX_ALT_GPIO_8_0 /* PWM3 */ \
@@ -291,7 +310,8 @@
NPCX_ALT_GPIO_A_1 /* SPIP_SCLK */ \
NPCX_ALT_GPIO_A_3 /* SPIP_MOSI */ \
NPCX_ALT_GPIO_A_5 /* SPIP_CS1 */ \
- NPCX_ALT_GPIO_A_6 /* TA2_SEL2 */ \
+ NPCX_ALT_GPIO_A_6 /* TA2_SEL2/PS2_CLK3 */ \
+ NPCX_ALT_GPIO_A_7 /* PS2_DAT3 */ \
NPCX_ALT_GPIO_B_1 /* KSO17 */ \
NPCX_ALT_GPIO_B_2 /* SMB0SDA1 */ \
NPCX_ALT_GPIO_B_3 /* SMB0SCL1 */ \
diff --git a/chip/npcx/gpio_chip-npcx7.h b/chip/npcx/gpio_chip-npcx7.h
index 44911dcf32..2f6337d896 100644
--- a/chip/npcx/gpio_chip-npcx7.h
+++ b/chip/npcx/gpio_chip-npcx7.h
@@ -230,12 +230,23 @@
#define NPCX_ALT_GPIO_4_3 ALT(4, 3, NPCX_ALT(6, ADC2_SL)) /* ADC2 */
#define NPCX_ALT_GPIO_4_2 ALT(4, 2, NPCX_ALT(6, ADC3_SL)) /* ADC3 */
#define NPCX_ALT_GPIO_4_1 ALT(4, 1, NPCX_ALT(6, ADC4_SL)) /* ADC4 */
+#ifdef CONFIG_PS2
+#define NPCX_ALT_GPIO_3_7 ALT(3, 7, NPCX_ALT(3, PS2_2_SL)) /* PS2_CLK2 */
+#define NPCX_ALT_GPIO_3_4 ALT(3, 4, NPCX_ALT(3, PS2_2_SL)) /* PS2_DATA2 */
+#else
#define NPCX_ALT_GPIO_3_7 ALT(3, 7, NPCX_ALT(F, ADC5_SL)) /* ADC5 */
#define NPCX_ALT_GPIO_3_4 ALT(3, 4, NPCX_ALT(F, ADC6_SL)) /* ADC6 */
+#endif
#define NPCX_ALT_GPIO_E_1 ALT(E, 1, NPCX_ALT(F, ADC7_SL)) /* ADC7 */
#define NPCX_ALT_GPIO_F_1 ALT(F, 1, NPCX_ALT(F, ADC8_SL)) /* ADC8 */
#define NPCX_ALT_GPIO_F_0 ALT(F, 0, NPCX_ALT(F, ADC9_SL)) /* ADC9 */
+/* PS/2 Module */
+#define NPCX_ALT_GPIO_6_2 ALT(6, 2, NPCX_ALT(3, PS2_1_SL)) /* PS2_CLK1 */
+#define NPCX_ALT_GPIO_6_3 ALT(6, 3, NPCX_ALT(3, PS2_1_SL)) /* PS2_DATA1 */
+#define NPCX_ALT_GPIO_6_7 ALT(6, 7, NPCX_ALT(3, PS2_0_SL)) /* PS2_CLK0 */
+#define NPCX_ALT_GPIO_7_0 ALT(7, 0, NPCX_ALT(3, PS2_0_SL)) /* PS2_DATA0 */
+
/* UART Module */
#define NPCX_ALT_GPIO_6_4 ALT(6, 4, NPCX_ALT(C, UART_SL2)) /* CR_SIN SEL2 */
#define NPCX_ALT_GPIO_6_5 ALT(6, 5, NPCX_ALT(C, UART_SL2)) /* CR_SOUT SEL2 */
@@ -253,7 +264,11 @@
#define NPCX_ALT_GPIO_4_0 ALT(4, 0, NPCX_ALT(3, TA1_SL1)) /* TA1_SEL1 */
#define NPCX_ALT_GPIO_7_3 ALT(7, 3, NPCX_ALT(3, TA2_SL1)) /* TA2_SEL1 */
#define NPCX_ALT_GPIO_9_3 ALT(9, 3, NPCX_ALT(C, TA1_SL2)) /* TA1_SEL2 */
+#ifdef CONFIG_PS2
+#define NPCX_ALT_GPIO_A_6 ALT(A, 6, NPCX_ALT(C, PS2_3_SL2)) /* PS2_CLK3 */
+#else
#define NPCX_ALT_GPIO_A_6 ALT(A, 6, NPCX_ALT(C, TA2_SL2)) /* TA2_SEL2 */
+#endif
/* Keyboard Scan Module */
#define NPCX_ALT_GPIO_3_1 ALT(3, 1, NPCX_ALT_INV(7, NO_KSI0_SL)) /* KSI0 */
@@ -331,7 +346,11 @@
#define NPCX_ALT_GPIO_A_3 ALT(A, 3, NPCX_ALT(0, SPIP_SL)) /* SPIP_MOSI */
#define NPCX_ALT_GPIO_A_1 ALT(A, 1, NPCX_ALT(0, SPIP_SL)) /* SPIP_SCLK */
+#ifdef CONFIG_PS2
+#define NPCX_ALT_GPIO_A_7 ALT(A, 7, NPCX_ALT(C, PS2_3_SL2)) /* PS2_DAT3 */
+#else
#define NPCX_ALT_GPIO_A_7
+#endif
#define NPCX_ALT_GPIO_B_0
#define NPCX_ALT_GPIO_9_4
#define NPCX_ALT_GPIO_9_7
@@ -365,9 +384,9 @@
NPCX_ALT_GPIO_3_0 /* KSI1 */ \
NPCX_ALT_GPIO_3_1 /* KSI0 */ \
NPCX_ALT_GPIO_3_3 /* SMB5SCL0 */ \
- NPCX_ALT_GPIO_3_4 /* ADC6 */ \
+ NPCX_ALT_GPIO_3_4 /* ADC6/PS2_DAT2 */ \
NPCX_ALT_GPIO_3_6 /* SMB5SDA0 */ \
- NPCX_ALT_GPIO_3_7 /* ADC5 */ \
+ NPCX_ALT_GPIO_3_7 /* ADC5/PS2_CLK2 */ \
NPCX_ALT_GPIO_4_0 /* TA1_SEL1 */ \
NPCX_ALT_GPIO_4_1 /* ADC4 */ \
NPCX_ALT_GPIO_4_2 /* ADC3 */ \
@@ -375,8 +394,12 @@
NPCX_ALT_GPIO_4_4 /* ADC1 */ \
NPCX_ALT_GPIO_4_5 /* ADC0 */ \
NPCX_ALT_GPIO_6_0 /* PWM7 */ \
+ NPCX_ALT_GPIO_6_2 /* PS2_CLK1 */ \
+ NPCX_ALT_GPIO_6_3 /* PS2_DAT1 */ \
NPCX_ALT_GPIO_6_4 /* CR_SIN1 SEL2 */ \
NPCX_ALT_GPIO_6_5 /* CR_SOUT1 SEL2 */ \
+ NPCX_ALT_GPIO_6_7 /* PS2_CLK0 */ \
+ NPCX_ALT_GPIO_7_0 /* PS2_DAT0 */ \
NPCX_ALT_GPIO_7_3 /* TA2_SEL1 */ \
NPCX_ALT_GPIO_7_5 /* CR_SIN2 & 32KHZ_OUT */ \
NPCX_ALT_GPIO_8_0 /* PWM3 */ \
@@ -395,8 +418,8 @@
NPCX_ALT_GPIO_A_1 /* SPIP_SCLK */ \
NPCX_ALT_GPIO_A_3 /* SPIP_MOSI */ \
NPCX_ALT_GPIO_A_5 /* SPIP_CS1 & I2S_SYNC */ \
- NPCX_ALT_GPIO_A_6 /* TA2_SEL2 */ \
- NPCX_ALT_GPIO_A_7 /* I2S_SCLK */ \
+ NPCX_ALT_GPIO_A_6 /* TA2_SEL2/PS2_CLK3 */ \
+ NPCX_ALT_GPIO_A_7 /* I2S_SCLK/PS2_DAT3 */ \
NPCX_ALT_GPIO_B_0 /* I2S_DATA */ \
NPCX_ALT_GPIO_B_1 /* KSO17 */ \
NPCX_ALT_GPIO_B_2 /* SMB7SDA0 */ \
diff --git a/chip/npcx/ps2.c b/chip/npcx/ps2.c
new file mode 100644
index 0000000000..c470bbe68d
--- /dev/null
+++ b/chip/npcx/ps2.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* PS/2 module for Chrome EC */
+#include "atomic.h"
+#include "clock.h"
+#include "console.h"
+#include "hooks.h"
+#include "gpio.h"
+#include "ps2_chip.h"
+#include "task.h"
+#include "registers.h"
+#include "timer.h"
+#include "util.h"
+
+#define CPRINTS(format, args...) cprints(CC_PS2, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_PS2, format, ## args)
+
+#if !(DEBUG_PS2)
+#define DEBUG_CPRINTS(...)
+#define DEBUG_CPRINTF(...)
+#else
+#define DEBUG_CPRINTS(format, args...) cprints(CC_PS2, format, ## args)
+#define DEBUG_CPRINTF(format, args...) cprintf(CC_PS2, format, ## args)
+#endif
+
+/*
+ * Set WDAT3-0 and clear CLK3-0 in the PSOSIG register to
+ * reset the shift mechanism.
+ */
+#define PS2_SHIFT_MECH_RESET 0x47
+
+#define PS2_TRANSACTION_TIMEOUT (20 * MSEC)
+#define PS2_BUSY_RETRY 10
+
+enum ps2_input_debounce_cycle {
+ PS2_IDB_1_CYCLE,
+ PS2_IDB_2_CYCLE,
+ PS2_IDB_4_CYCLE,
+ PS2_IDB_8_CYCLE,
+ PS2_IDB_16_CYCLE,
+ PS2_IDB_32_CYCLE,
+};
+
+enum ps2_opr_mode {
+ PS2_TX_MODE,
+ PS2_RX_MODE,
+};
+
+struct ps2_data {
+ /* PS/2 module operation mode */
+ uint8_t opr_mode;
+ /*
+ * The callback function to process data received from PS/2 device.
+ * Note: this is called in the PS/2 interrupt handler
+ */
+ void (*rx_handler_cb)(uint8_t data);
+};
+static struct ps2_data ps2_ch_data[NPCX_PS2_CH_COUNT] = {
+ [0 ... (NPCX_PS2_CH_COUNT - 1)] = { PS2_RX_MODE, NULL }
+};
+
+/*
+ * Bitmap to record the enabled PS/2 channel by upper layer.
+ * Only bit[7 and bit[5:3] are used
+ * (i.e. the bit position of CLK3-0 in the PS2_PSOSIG register)
+ */
+static uint32_t channel_enabled_mask;
+static struct mutex ps2_lock;
+static volatile task_id_t task_waiting = TASK_ID_INVALID;
+
+static void ps2_init(void)
+{
+ /* Disable the power down bit of PS/2 */
+ clock_enable_peripheral(CGC_OFFSET_PS2, CGC_PS2_MASK,
+ CGC_MODE_RUN | CGC_MODE_SLEEP);
+
+ /* Disable shift mechanism and configure PS/2 to received mode. */
+ NPCX_PS2_PSCON = 0x0;
+ /* Set WDAT3-0 and clear CLK3-0 before enabling shift mechanism */
+ NPCX_PS2_PSOSIG = PS2_SHIFT_MECH_RESET;
+
+ /*
+ * PS/2 interrupt enable register
+ * [0] - : SOTIE = 1: Start Of Transaction Interrupt Enable
+ * [1] - : EOTIE = 1: End Of Transaction Interrupt Enable
+ * [4] - : WUE = 1: Wake-Up Enable
+ * [7] - : CLK_SEL = 1: Select Free-Run clock as the basic clock
+ */
+ NPCX_PS2_PSIEN = BIT(NPCX_PS2_PSIEN_SOTIE) |
+ BIT(NPCX_PS2_PSIEN_EOTIE) |
+ BIT(NPCX_PS2_PSIEN_PS2_WUE) |
+ BIT(NPCX_PS2_PSIEN_PS2_CLK_SEL);
+
+ /* Enable weak internal pull-up */
+ SET_BIT(NPCX_PS2_PSCON, NPCX_PS2_PSCON_WPUED);
+ /* Enable shift mechanism */
+ SET_BIT(NPCX_PS2_PSCON, NPCX_PS2_PSCON_EN);
+
+ /* Configure pins from GPIOs to PS/2 interface */
+ gpio_config_module(MODULE_PS2, 1);
+ task_enable_irq(NPCX_IRQ_PS2);
+}
+DECLARE_HOOK(HOOK_INIT, ps2_init, HOOK_PRIO_DEFAULT);
+
+void ps2_enable_channel(int channel, int enable,
+ void (*callback)(uint8_t data))
+{
+ if (channel >= NPCX_PS2_CH_COUNT) {
+ CPRINTS("Err:PS/2 CH exceed %d", NPCX_PS2_CH_COUNT);
+ return;
+ }
+
+ /*
+ * Disable the interrupt during changing the enabled channel mask to
+ * prevent from preemption
+ */
+ interrupt_disable();
+ if (enable) {
+ ps2_ch_data[channel].rx_handler_cb = callback;
+ channel_enabled_mask |= BIT(NPCX_PS2_PSOSIG_CLK(channel));
+ /* Enable the relevant channel clock */
+ SET_BIT(NPCX_PS2_PSOSIG, NPCX_PS2_PSOSIG_CLK(channel));
+ } else {
+ channel_enabled_mask &= ~BIT(NPCX_PS2_PSOSIG_CLK(channel));
+ /* Disable the relevant channel clock */
+ CLEAR_BIT(NPCX_PS2_PSOSIG, NPCX_PS2_PSOSIG_CLK(channel));
+ ps2_ch_data[channel].rx_handler_cb = NULL;
+ }
+ interrupt_enable();
+}
+
+/* Check if the shift mechanism is busy */
+static int ps2_is_busy(void)
+{
+ /*
+ * The driver pulls the CLK for non-active channels to low when Start
+ * bit is detected and pull the CLK of the active channel low after
+ * Stop bit detected. The EOT bit is set when Stop bit is detected,
+ * but both SOT and EOT are cleared when all CLKs are pull low
+ * (due to Shift Mechanism is reset)
+ */
+ return (IS_BIT_SET(NPCX_PS2_PSTAT, NPCX_PS2_PSTAT_SOT) |
+ IS_BIT_SET(NPCX_PS2_PSTAT, NPCX_PS2_PSTAT_EOT)) ? 1 : 0;
+}
+
+int ps2_transmit_byte(int channel, uint8_t data)
+{
+ int event;
+
+ uint8_t busy_retry = PS2_BUSY_RETRY;
+
+ if (channel >= NPCX_PS2_CH_COUNT) {
+ CPRINTS("Err:PS/2 CH exceed %d", NPCX_PS2_CH_COUNT);
+ return EC_ERROR_INVAL;
+ }
+
+ if (!(BIT(NPCX_PS2_PSOSIG_CLK(channel)) & channel_enabled_mask)) {
+ CPRINTS("Err: PS/2 Tx w/o enabling CH");
+ return EC_ERROR_INVAL;
+ }
+
+ mutex_lock(&ps2_lock);
+ while (ps2_is_busy()) {
+ usleep(PS2_TRANSACTION_TIMEOUT);
+ if (busy_retry == 0) {
+ mutex_unlock(&ps2_lock);
+ return EC_ERROR_BUSY;
+ }
+ busy_retry--;
+ }
+
+ task_waiting = task_get_current();
+ ps2_ch_data[channel].opr_mode = PS2_TX_MODE;
+
+ /* Set PS/2 in transmit mode */
+ SET_BIT(NPCX_PS2_PSCON, NPCX_PS2_PSCON_XMT);
+ /* Enable Start Of Transaction interrupt */
+ SET_BIT(NPCX_PS2_PSIEN, NPCX_PS2_PSIEN_SOTIE);
+
+ /* Reset the shift mechanism */
+ NPCX_PS2_PSOSIG = PS2_SHIFT_MECH_RESET;
+ /* Inhibit communication should last at least 100 micro-seconds */
+ udelay(100);
+
+ /* Write the data to be transmitted */
+ NPCX_PS2_PSDAT = data;
+ /* Apply the Request-to-send */
+ CLEAR_BIT(NPCX_PS2_PSOSIG, NPCX_PS2_PSOSIG_WDAT(channel));
+ SET_BIT(NPCX_PS2_PSOSIG, NPCX_PS2_PSOSIG_CLK(channel));
+
+ /* Wait for interrupt */
+ event = task_wait_event_mask(TASK_EVENT_PS2_DONE,
+ PS2_TRANSACTION_TIMEOUT);
+ task_waiting = TASK_ID_INVALID;
+
+ if (event == TASK_EVENT_TIMER) {
+ task_disable_irq(NPCX_IRQ_PS2);
+ CPRINTS("PS/2 Tx timeout");
+ /* Reset the shift mechanism */
+ NPCX_PS2_PSOSIG = PS2_SHIFT_MECH_RESET;
+ /* Change the PS/2 module to receive mode */
+ CLEAR_BIT(NPCX_PS2_PSCON, NPCX_PS2_PSCON_XMT);
+ /* Restore the channel to Receive mode */
+ ps2_ch_data[channel].opr_mode = PS2_RX_MODE;
+ /*
+ * Restore the enabled channel according to channel_enabled_mask
+ */
+ NPCX_PS2_PSOSIG |= channel_enabled_mask;
+ task_enable_irq(NPCX_IRQ_PS2);
+ }
+ mutex_unlock(&ps2_lock);
+
+ DEBUG_CPRINTF("Evt:0x%08x\n", event);
+ return (event == TASK_EVENT_PS2_DONE) ? EC_SUCCESS : EC_ERROR_TIMEOUT;
+
+}
+
+static void ps2_stop_inactive_ch_clk(uint8_t active_ch)
+{
+ uint8_t mask;
+
+ mask = ~NPCX_PS2_PSOSIG_CLK_MASK_ALL |
+ BIT(NPCX_PS2_PSOSIG_CLK(active_ch));
+ NPCX_PS2_PSOSIG &= mask;
+
+}
+
+static int ps2_is_rx_error(uint8_t ch)
+{
+ uint8_t status;
+
+ status = NPCX_PS2_PSTAT &
+ (BIT(NPCX_PS2_PSTAT_PERR) |
+ BIT(NPCX_PS2_PSTAT_RFERR));
+ if (status) {
+
+ if (status & BIT(NPCX_PS2_PSTAT_PERR))
+ CPRINTF("PS2 CH %d RX parity error\n", ch);
+ if (status & BIT(NPCX_PS2_PSTAT_RFERR))
+ CPRINTF("PS2 CH %d RX Frame error\n", ch);
+ return 1;
+ } else
+ return 0;
+}
+
+void ps2_int_handler(void)
+{
+ uint8_t active_ch;
+
+ DEBUG_CPRINTS("PS2 INT");
+ /*
+ * ACH = 1 : CHannel 0
+ * ACH = 2 : CHannel 1
+ * ACH = 4 : CHannel 2
+ * ACH = 5 : CHannel 3
+ */
+ active_ch = GET_FIELD(NPCX_PS2_PSTAT, NPCX_PS2_PSTAT_ACH);
+ active_ch = active_ch > 2 ? (active_ch - 2) : (active_ch - 1);
+ DEBUG_CPRINTF("ACH:%0d-", active_ch);
+
+ /*
+ * Inhibit PS/2 transaction of the other non-active channels by
+ * pulling down the clock signal
+ */
+ ps2_stop_inactive_ch_clk(active_ch);
+
+ /* PS/2 Start of Transaction */
+ if (IS_BIT_SET(NPCX_PS2_PSTAT, NPCX_PS2_PSTAT_SOT) &&
+ IS_BIT_SET(NPCX_PS2_PSIEN, NPCX_PS2_PSIEN_SOTIE)) {
+ DEBUG_CPRINTF("SOT-");
+ /*
+ * Once set, SOT is not cleared until the shift mechanism
+ * is reset. Therefore, SOTIE should be cleared on the
+ * first occurrence of an SOT interrupt.
+ */
+ CLEAR_BIT(NPCX_PS2_PSIEN, NPCX_PS2_PSIEN_SOTIE);
+ /* PS/2 End of Transaction */
+ } else if (IS_BIT_SET(NPCX_PS2_PSTAT, NPCX_PS2_PSTAT_EOT)) {
+ DEBUG_CPRINTF("EOT-");
+ CLEAR_BIT(NPCX_PS2_PSIEN, NPCX_PS2_PSIEN_EOTIE);
+
+ /*
+ * Clear the CLK of active channel to reset
+ * the shift mechanism
+ */
+ CLEAR_BIT(NPCX_PS2_PSOSIG, NPCX_PS2_PSOSIG_CLK(active_ch));
+
+ if (ps2_ch_data[active_ch].opr_mode == PS2_TX_MODE) {
+ /* Change the PS/2 module to receive mode */
+ CLEAR_BIT(NPCX_PS2_PSCON, NPCX_PS2_PSCON_XMT);
+ ps2_ch_data[active_ch].opr_mode = PS2_RX_MODE;
+ task_set_event(task_waiting, TASK_EVENT_PS2_DONE, 0);
+ } else {
+ if (!ps2_is_rx_error(active_ch)) {
+ uint8_t data_read = NPCX_PS2_PSDAT;
+ struct ps2_data *ps2_ptr =
+ &ps2_ch_data[active_ch];
+
+ DEBUG_CPRINTF("Recv:0x%02x", data_read);
+ if (ps2_ptr->rx_handler_cb)
+ ps2_ptr->rx_handler_cb(data_read);
+ }
+ }
+
+ /* Restore the enabled channel */
+ NPCX_PS2_PSOSIG |= channel_enabled_mask;
+ /*
+ * Re-enable the Start Of Transaction interrupt when
+ * the shift mechanism is reset
+ */
+ SET_BIT(NPCX_PS2_PSIEN, NPCX_PS2_PSIEN_SOTIE);
+ SET_BIT(NPCX_PS2_PSIEN, NPCX_PS2_PSIEN_EOTIE);
+ }
+ DEBUG_CPRINTF("\n");
+
+}
+DECLARE_IRQ(NPCX_IRQ_PS2, ps2_int_handler, 5);
+
+#ifdef CONFIG_CMD_PS2
+static int command_ps2ench(int argc, char **argv)
+{
+ uint8_t ch;
+ uint8_t enable;
+ char *e;
+
+ ch = strtoi(argv[1], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM2;
+
+ enable = strtoi(argv[2], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM2;
+ if (enable)
+ ps2_enable_channel(ch, 1, NULL);
+ else
+ ps2_enable_channel(ch, 0, NULL);
+
+ return 0;
+}
+DECLARE_CONSOLE_COMMAND(ps2ench, command_ps2ench,
+ "ps2_ench channel 1|0",
+ "Enable/Disable PS/2 channel");
+
+static int command_ps2write(int argc, char **argv)
+{
+ uint8_t ch, data;
+ char *e;
+
+ ch = strtoi(argv[1], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM2;
+ data = strtoi(argv[2], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM2;
+
+ ps2_transmit_byte(ch, data);
+ return 0;
+}
+DECLARE_CONSOLE_COMMAND(ps2write, command_ps2write,
+ "ps2_write channel data",
+ "Write data byte to PS/2 channel ");
+#endif
diff --git a/chip/npcx/ps2_chip.h b/chip/npcx/ps2_chip.h
new file mode 100644
index 0000000000..d88e6791ad
--- /dev/null
+++ b/chip/npcx/ps2_chip.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_PS2_CHIP_H
+#define __CROS_EC_PS2_CHIP_H
+
+#include "common.h"
+
+enum npcx_ps2_channel {
+ NPCX_PS2_CH0,
+ NPCX_PS2_CH1,
+ NPCX_PS2_CH2,
+ NPCX_PS2_CH3,
+ NPCX_PS2_CH_COUNT
+};
+
+void ps2_enable_channel(int channel, int enable,
+ void (*callback)(uint8_t data));
+int ps2_transmit_byte(int channel, uint8_t data);
+
+#endif /* __CROS_EC_PS2_CHIP_H */
diff --git a/chip/npcx/registers.h b/chip/npcx/registers.h
index 101eb93cb0..5065fdf6a7 100644
--- a/chip/npcx/registers.h
+++ b/chip/npcx/registers.h
@@ -67,6 +67,7 @@
#define DEBUG_ESPI 0
#define DEBUG_CEC 0
#define DEBUG_SIB 0
+#define DEBUG_PS2 0
/* Modules Map */
#define NPCX_ESPI_BASE_ADDR 0x4000A000
@@ -82,6 +83,7 @@
#define NPCX_APM_BASE_ADDR 0x400A4800
#define NPCX_GLUE_REGS_BASE 0x400A5000
#define NPCX_BBRAM_BASE_ADDR 0x400AF000
+#define NPCX_PS2_BASE_ADDR 0x400B1000
#define NPCX_HFCG_BASE_ADDR 0x400B5000
#define NPCX_LFCG_BASE_ADDR 0x400B5100
#define NPCX_FMUL2_BASE_ADDR 0x400B5200
@@ -1001,6 +1003,7 @@ enum {
CGC_OFFSET_UART = 0,
CGC_OFFSET_FAN = 0,
CGC_OFFSET_FIU = 0,
+ CGC_OFFSET_PS2 = 0,
CGC_OFFSET_PWM = 1,
CGC_OFFSET_I2C = 2,
CGC_OFFSET_ADC = 3,
@@ -1039,6 +1042,7 @@ enum NPCX_PMC_PWDWN_CTL_T {
#define CGC_FAN_MASK (BIT(NPCX_PWDWN_CTL1_MFT1_PD) | \
BIT(NPCX_PWDWN_CTL1_MFT2_PD))
#define CGC_FIU_MASK BIT(NPCX_PWDWN_CTL1_FIU_PD)
+#define CGC_PS2_MASK BIT(NPCX_PWDWN_CTL1_PS2_PD)
#if defined(CHIP_FAMILY_NPCX5)
#define CGC_I2C_MASK (BIT(NPCX_PWDWN_CTL3_SMB0_PD) | \
BIT(NPCX_PWDWN_CTL3_SMB1_PD) | \
@@ -2191,7 +2195,59 @@ static inline int npcx_is_uart(void)
}
#endif
-/* Wake pin definitions, defined at board-level */
+/******************************************************************************/
+/* PS/2 registers */
+#define NPCX_PS2_PSDAT REG8(NPCX_PS2_BASE_ADDR + 0x000)
+#define NPCX_PS2_PSTAT REG8(NPCX_PS2_BASE_ADDR + 0x002)
+#define NPCX_PS2_PSCON REG8(NPCX_PS2_BASE_ADDR + 0x004)
+#define NPCX_PS2_PSOSIG REG8(NPCX_PS2_BASE_ADDR + 0x006)
+#define NPCX_PS2_PSISIG REG8(NPCX_PS2_BASE_ADDR + 0x008)
+#define NPCX_PS2_PSIEN REG8(NPCX_PS2_BASE_ADDR + 0x00A)
+
+/* PS/2 register field */
+#define NPCX_PS2_PSTAT_SOT 0
+#define NPCX_PS2_PSTAT_EOT 1
+#define NPCX_PS2_PSTAT_PERR 2
+#define NPCX_PS2_PSTAT_ACH FIELD(3, 3)
+#define NPCX_PS2_PSTAT_RFERR 6
+
+#define NPCX_PS2_PSCON_EN 0
+#define NPCX_PS2_PSCON_XMT 1
+#define NPCX_PS2_PSCON_HDRV FIELD(2, 2)
+#define NPCX_PS2_PSCON_IDB FIELD(4, 3)
+#define NPCX_PS2_PSCON_WPUED 7
+
+#define NPCX_PS2_PSOSIG_WDAT0 0
+#define NPCX_PS2_PSOSIG_WDAT1 1
+#define NPCX_PS2_PSOSIG_WDAT2 2
+#define NPCX_PS2_PSOSIG_CLK0 3
+#define NPCX_PS2_PSOSIG_CLK1 4
+#define NPCX_PS2_PSOSIG_CLK2 5
+#define NPCX_PS2_PSOSIG_WDAT3 6
+#define NPCX_PS2_PSOSIG_CLK3 7
+#define NPCX_PS2_PSOSIG_CLK(n) (((n) < NPCX_PS2_CH3) ? \
+ ((n) + 3) : 7)
+#define NPCX_PS2_PSOSIG_WDAT(n) (((n) < NPCX_PS2_CH3) ? \
+ ((n) + 0) : 6)
+#define NPCX_PS2_PSOSIG_CLK_MASK_ALL \
+ (BIT(NPCX_PS2_PSOSIG_CLK0) | \
+ BIT(NPCX_PS2_PSOSIG_CLK1) | \
+ BIT(NPCX_PS2_PSOSIG_CLK2) | \
+ BIT(NPCX_PS2_PSOSIG_CLK3))
+
+#define NPCX_PS2_PSISIG_RDAT0 0
+#define NPCX_PS2_PSISIG_RDAT1 1
+#define NPCX_PS2_PSISIG_RDAT2 2
+#define NPCX_PS2_PSISIG_RCLK0 3
+#define NPCX_PS2_PSISIG_RCLK1 4
+#define NPCX_PS2_PSISIG_RCLK2 5
+#define NPCX_PS2_PSISIG_RDAT3 6
+#define NPCX_PS2_PSISIG_RCLK3 7
+#define NPCX_PS2_PSIEN_SOTIE 0
+#define NPCX_PS2_PSIEN_EOTIE 1
+#define NPCX_PS2_PSIEN_PS2_WUE 4
+#define NPCX_PS2_PSIEN_PS2_CLK_SEL 7
+
extern const enum gpio_signal hibernate_wake_pins[];
extern const int hibernate_wake_pins_used;
diff --git a/include/config.h b/include/config.h
index 7741c807eb..ad23405eb1 100644
--- a/include/config.h
+++ b/include/config.h
@@ -1245,6 +1245,7 @@
#define CONFIG_CMD_PWR_AVG
#define CONFIG_CMD_POWER_AP
#undef CONFIG_CMD_PPC_DUMP
+#undef CONFIG_CMD_PS2
#undef CONFIG_CMD_RAND
#define CONFIG_CMD_REGULATOR
#undef CONFIG_CMD_RTC
@@ -3004,6 +3005,9 @@
*/
#undef CONFIG_CPU_PROCHOT_ACTIVE_LOW
+/* Support PS/2 interface */
+#undef CONFIG_PS2
+
/*
* Define this option to enable programmable voltage detector which will
* trigger an interrupt when the voltage drops below a threshold specified
diff --git a/include/console_channel.inc b/include/console_channel.inc
index d4b63040a5..f4558bef3c 100644
--- a/include/console_channel.inc
+++ b/include/console_channel.inc
@@ -78,6 +78,9 @@ CONSOLE_CHANNEL(CC_MOTION_SENSE, "motionsense")
CONSOLE_CHANNEL(CC_PD_HOST_CMD, "pdhostcm")
#endif
CONSOLE_CHANNEL(CC_PORT80, "port80")
+#ifdef CONFIG_PS2
+CONSOLE_CHANNEL(CC_PS2, "ps2")
+#endif
#if defined(CONFIG_PWM) || defined(CONFIG_FANS)
CONSOLE_CHANNEL(CC_PWM, "pwm")
#endif
diff --git a/include/module_id.h b/include/module_id.h
index 10c2ee3b8d..3f26802333 100644
--- a/include/module_id.h
+++ b/include/module_id.h
@@ -34,6 +34,7 @@ enum module_id {
MODULE_PMU,
MODULE_PORT80,
MODULE_POWER_LED,
+ MODULE_PS2,
MODULE_PWM,
MODULE_RDD,
MODULE_RBOX,
diff --git a/include/task.h b/include/task.h
index 897ba79f34..c0ce1e6838 100644
--- a/include/task.h
+++ b/include/task.h
@@ -40,6 +40,7 @@
#endif
#else
#define TASK_EVENT_I2C_IDLE BIT(20)
+#define TASK_EVENT_PS2_DONE BIT(21)
#endif
/* DMA transmit complete event */