diff options
author | tim <tim2.lin@ite.corp-partner.google.com> | 2019-08-22 18:20:55 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-11-19 07:22:38 +0000 |
commit | 7abc7ca4abbc2f4154e177389924c258ea37ff5d (patch) | |
tree | 480aee7a32804c94be0ab58f124241243f3a46aa /driver | |
parent | 460cc37c5be4cc2d986a89fd7237fc31fc30bf7a (diff) | |
download | chrome-ec-7abc7ca4abbc2f4154e177389924c258ea37ff5d.tar.gz |
driver/ioexpander_it8801: add I/O expander driver for keyboard
IT8801 is an I/O expander with a keyboard matrix controller.
BUG=b:133200075
BRANCH=none
TEST=Press any key on keyboard and the console command
"ksstate on" can print keyboard scan state.
Change-Id: I6903aad1eacfdfb6a7c6c04e6954a14e8c8caaac
Signed-off-by: tim <tim2.lin@ite.corp-partner.google.com>
Signed-off-by: Alexandru M Stan <amstan@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1677976
Diffstat (limited to 'driver')
-rw-r--r-- | driver/build.mk | 1 | ||||
-rw-r--r-- | driver/ioexpander_it8801.c | 227 | ||||
-rw-r--r-- | driver/ioexpander_it8801.h | 46 |
3 files changed, 274 insertions, 0 deletions
diff --git a/driver/build.mk b/driver/build.mk index 7bc59eb331..27daf6bb3f 100644 --- a/driver/build.mk +++ b/driver/build.mk @@ -79,6 +79,7 @@ driver-$(CONFIG_CHARGER_SY21612)+=charger/sy21612.o include $(_driver_cur_dir)fingerprint/build.mk # I/O expander +driver-$(CONFIG_IO_EXPANDER_IT8801)+=ioexpander_it8801.o driver-$(CONFIG_IO_EXPANDER_PCA9534)+=ioexpander_pca9534.o driver-$(CONFIG_IO_EXPANDER_NCT38XX)+=ioexpander_nct38xx.o diff --git a/driver/ioexpander_it8801.c b/driver/ioexpander_it8801.c new file mode 100644 index 0000000000..64ef3628ed --- /dev/null +++ b/driver/ioexpander_it8801.c @@ -0,0 +1,227 @@ +/* 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. + */ + +#include "common.h" +#include "console.h" +#include "driver/ioexpander_it8801.h" +#include "gpio.h" +#include "hooks.h" +#include "i2c.h" +#include "keyboard_raw.h" +#include "keyboard_scan.h" +#include "registers.h" +#include "task.h" + +#define CPRINTS(format, args...) cprints(CC_KEYSCAN, format, ## args) + +static int it8801_read(int reg, int *data) +{ + return i2c_read8(I2C_PORT_IO_EXPANDER_IT8801, IT8801_I2C_ADDR, + reg, data); +} + +static int it8801_write(int reg, int data) +{ + return i2c_write8(I2C_PORT_IO_EXPANDER_IT8801, IT8801_I2C_ADDR, + reg, data); +} + +struct it8801_vendor_id_t { + uint8_t chip_id; + uint8_t reg; +}; + +static const struct it8801_vendor_id_t it8801_vendor_id_verify[] = { + { 0x12, IT8801_REG_HBVIDR}, + { 0x83, IT8801_REG_LBVIDR}, +}; + +static int it8801_check_vendor_id(void) +{ + int i, ret, val; + + /* Verify vendor ID registers(16-bits). */ + for (i = 0; i < ARRAY_SIZE(it8801_vendor_id_verify); i++) { + ret = it8801_read(it8801_vendor_id_verify[i].reg, &val); + + if (ret != EC_SUCCESS) + return ret; + + if (val != it8801_vendor_id_verify[i].chip_id) + return EC_ERROR_UNKNOWN; + } + + return EC_SUCCESS; +} + +void keyboard_raw_init(void) +{ + int ret, val; + + /* Verify Vendor ID registers. */ + ret = it8801_check_vendor_id(); + if (ret) { + CPRINTS("Failed to read IT8801 vendor id %x", ret); + return; + } + + /* KSO alternate function switching(KSO[21:20, 18]). */ + it8801_write(IT8801_REG_GPIO01_KSO18, IT8801_REG_MASK_GPIOAFS_FUNC2); + it8801_write(IT8801_REG_GPIO22_KSO21, IT8801_REG_MASK_GPIOAFS_FUNC2); + it8801_write(IT8801_REG_GPIO23_KSO20, IT8801_REG_MASK_GPIOAFS_FUNC2); + + /* Start with KEYBOARD_COLUMN_ALL, KSO[22:11, 6:0] output low. */ + it8801_write(IT8801_REG_KSOMCR, IT8801_REG_MASK_AKSOSC); + + if (IS_ENABLED(CONFIG_KEYBOARD_COL2_INVERTED)) { + /* + * Since most of the KSO pins can't drive up, we'll must use + * a pin capable of being a GPIO instead and use the GPIO + * feature to do the required inverted push pull. + */ + it8801_write(IT8801_REG_GPIO23_KSO20, IT8801_REG_MASK_GPIODIR); + + /* Start with KEYBOARD_COLUMN_ALL, output high(so selected). */ + it8801_read(IT8801_REG_GPIOG2SOVR, &val); + it8801_write(IT8801_REG_GPIOG2SOVR, val | IT8801_REG_GPIO23SOV); + } + + /* Keyboard scan in interrupt enable register */ + it8801_write(IT8801_REG_KSIIER, 0xff); + /* Gather KSI interrupt enable */ + it8801_write(IT8801_REG_GIECR, IT8801_REG_MASK_GKSIIE); + /* Alert response enable */ + it8801_write(IT8801_REG_SMBCR, IT8801_REG_MASK_ARE); + + keyboard_raw_enable_interrupt(0); +} + +void keyboard_raw_task_start(void) +{ + keyboard_raw_enable_interrupt(1); +} + +static const uint8_t kso_mapping[] = { + 0, 1, 20, 3, 4, 5, 6, 17, 18, 16, 15, 11, 12, +#ifdef CONFIG_KEYBOARD_KEYPAD + 13, 14 +#endif +}; +BUILD_ASSERT(ARRAY_SIZE(kso_mapping) == KEYBOARD_COLS_MAX); + +test_mockable void keyboard_raw_drive_column(int col) +{ + int kso_val, val; + + /* Tri-state all outputs */ + if (col == KEYBOARD_COLUMN_NONE) { + /* KSO[22:11, 6:0] output high */ + kso_val = IT8801_REG_MASK_KSOSDIC | IT8801_REG_MASK_AKSOSC; + + if (IS_ENABLED(CONFIG_KEYBOARD_COL2_INVERTED)) { + /* Output low(so not selected). */ + it8801_read(IT8801_REG_GPIOG2SOVR, &val); + it8801_write(IT8801_REG_GPIOG2SOVR, val & + ~IT8801_REG_GPIO23SOV); + } + } + /* Assert all outputs */ + else if (col == KEYBOARD_COLUMN_ALL) { + /* KSO[22:11, 6:0] output low */ + kso_val = IT8801_REG_MASK_AKSOSC; + + if (IS_ENABLED(CONFIG_KEYBOARD_COL2_INVERTED)) { + /* Output high(so selected). */ + it8801_read(IT8801_REG_GPIOG2SOVR, &val); + it8801_write(IT8801_REG_GPIOG2SOVR, val | + IT8801_REG_GPIO23SOV); + } + } else { + /* To check if column is valid or not. */ + if (col >= KEYBOARD_COLS_MAX) + return; + /* + * Selected KSO[20, 18:11, 6:3, 1:0] output low, + * all others KSO output high. + */ + kso_val = kso_mapping[col]; + + if (IS_ENABLED(CONFIG_KEYBOARD_COL2_INVERTED)) { + /* GPIO23 is inverted. */ + if (col == IT8801_REG_MASK_SELKSO2) { + /* Output high(so selected). */ + it8801_read(IT8801_REG_GPIOG2SOVR, &val); + it8801_write(IT8801_REG_GPIOG2SOVR, val | + IT8801_REG_GPIO23SOV); + } else { + /* Output low(so not selected). */ + it8801_read(IT8801_REG_GPIOG2SOVR, &val); + it8801_write(IT8801_REG_GPIOG2SOVR, val & + ~IT8801_REG_GPIO23SOV); + } + } + } + + it8801_write(IT8801_REG_KSOMCR, kso_val); +} + +test_mockable int keyboard_raw_read_rows(void) +{ + int data = 0; + int ksieer = 0; + + it8801_read(IT8801_REG_KSIDR, &data); + + /* This register needs to write clear after reading data */ + it8801_read(IT8801_REG_KSIEER, &ksieer); + it8801_write(IT8801_REG_KSIEER, ksieer); + + /* Bits are active-low, so invert returned levels */ + return (~data) & 0xff; +} + +void keyboard_raw_enable_interrupt(int enable) +{ + if (enable) { + it8801_write(IT8801_REG_KSIEER, 0xff); + gpio_clear_pending_interrupt(GPIO_IT8801_SMB_INT); + gpio_enable_interrupt(GPIO_IT8801_SMB_INT); + } else { + gpio_disable_interrupt(GPIO_IT8801_SMB_INT); + } +} + +void io_expander_it8801_interrupt(enum gpio_signal signal) +{ + /* Wake the scan task */ + task_wake(TASK_ID_KEYSCAN); +} + +static void dump_register(int reg) +{ + int rv; + int data; + + ccprintf("[%Xh] = ", reg); + + rv = it8801_read(reg, &data); + + if (!rv) + ccprintf("0x%02x\n", data); + else + ccprintf("ERR (%d)\n", rv); +} + +static int it8801_dump(int argc, char **argv) +{ + dump_register(IT8801_REG_KSIIER); + dump_register(IT8801_REG_KSIEER); + dump_register(IT8801_REG_KSIDR); + dump_register(IT8801_REG_KSOMCR); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(it8801_dump, it8801_dump, "NULL", + "Dumps IT8801 registers"); diff --git a/driver/ioexpander_it8801.h b/driver/ioexpander_it8801.h new file mode 100644 index 0000000000..9b84adf764 --- /dev/null +++ b/driver/ioexpander_it8801.h @@ -0,0 +1,46 @@ +/* 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. + * + * IT8801 is an I/O expander with the keyboard matrix controller. + * + */ + +#ifndef __CROS_EC_IO_EXPANDER_IT8801_H +#define __CROS_EC_IO_EXPANDER_IT8801_H + +/* I2C slave address(7-bit without R/W) */ +#define IT8801_I2C_ADDR 0x38 + +/* Keyboard Matrix Scan control (KBS) */ +#define IT8801_REG_KSOMCR 0x40 +#define IT8801_REG_MASK_KSOSDIC BIT(7) +#define IT8801_REG_MASK_KSE BIT(6) +#define IT8801_REG_MASK_AKSOSC BIT(5) +#define IT8801_REG_KSIDR 0x41 +#define IT8801_REG_KSIEER 0x42 +#define IT8801_REG_KSIIER 0x43 +#define IT8801_REG_SMBCR 0xfa +#define IT8801_REG_MASK_ARE BIT(4) +#define IT8801_REG_GIECR 0xfb +#define IT8801_REG_MASK_GKSIIE BIT(3) +#define IT8801_REG_GPIO10 0x12 +#define IT8801_REG_GPIO00_KSO19 0x0a +#define IT8801_REG_GPIO01_KSO18 0x0b +#define IT8801_REG_GPIO22_KSO21 0x1c +#define IT8801_REG_GPIO23_KSO20 0x1d +#define IT8801_REG_MASK_GPIOAFS_PULLUP BIT(7) +#define IT8801_REG_MASK_GPIOAFS_FUNC2 BIT(6) +#define IT8801_REG_MASK_GPIODIR BIT(5) +#define IT8801_REG_MASK_GPIOPUE BIT(0) +#define IT8801_REG_GPIOG2SOVR 0x07 +#define IT8801_REG_GPIO23SOV BIT(3) +#define IT8801_REG_MASK_SELKSO2 0x02 +#define IT8801_REG_LBVIDR 0xFE +#define IT8801_REG_HBVIDR 0xFF +#define IT8801_KSO_COUNT 18 + +/* ISR for IT8801's SMB_INT# */ +void io_expander_it8801_interrupt(enum gpio_signal signal); + +#endif /* __CROS_EC_KBEXPANDER_IT8801_H */ |