diff options
author | CHLin <CHLIN56@nuvoton.com> | 2019-12-25 16:29:14 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-01-30 20:23:49 +0000 |
commit | d79ee1cba05d36d568ed6f30ba5dadb45fd57681 (patch) | |
tree | b2fcfdfba2da96fe0efc8daf9ba8b605d40f501b | |
parent | 85902285e20f4e6d8390d944b48ccb96ee252f40 (diff) | |
download | chrome-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.mk | 2 | ||||
-rw-r--r-- | chip/npcx/gpio_chip-npcx5.h | 22 | ||||
-rw-r--r-- | chip/npcx/gpio_chip-npcx7.h | 31 | ||||
-rw-r--r-- | chip/npcx/ps2.c | 366 | ||||
-rw-r--r-- | chip/npcx/ps2_chip.h | 24 | ||||
-rw-r--r-- | chip/npcx/registers.h | 58 | ||||
-rw-r--r-- | include/config.h | 4 | ||||
-rw-r--r-- | include/console_channel.inc | 3 | ||||
-rw-r--r-- | include/module_id.h | 1 | ||||
-rw-r--r-- | include/task.h | 1 |
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 */ |