diff options
Diffstat (limited to 'driver/ioexpander/it8801.c')
-rw-r--r-- | driver/ioexpander/it8801.c | 683 |
1 files changed, 0 insertions, 683 deletions
diff --git a/driver/ioexpander/it8801.c b/driver/ioexpander/it8801.c deleted file mode 100644 index 96070074fb..0000000000 --- a/driver/ioexpander/it8801.c +++ /dev/null @@ -1,683 +0,0 @@ -/* 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 "gpio.h" -#include "hooks.h" -#include "i2c.h" -#include "ioexpander.h" -#include "it8801.h" -#include "keyboard_raw.h" -#include "keyboard_scan.h" -#include "registers.h" -#include "task.h" -#include "util.h" -#include "keyboard_backlight.h" - -#define CPRINTS(format, args...) cprints(CC_KEYSCAN, format, ## args) - -static int it8801_ioex_set_level(int ioex, int port, int mask, int value); -static void it8801_ioex_event_handler(void); -DECLARE_DEFERRED(it8801_ioex_event_handler); - -static int it8801_read(int reg, int *data) -{ - return i2c_read8(IT8801_KEYBOARD_PWM_I2C_PORT, - IT8801_KEYBOARD_PWM_I2C_ADDR_FLAGS, reg, data); -} - -__maybe_unused static int it8801_write(int reg, int data) -{ - return i2c_write8(IT8801_KEYBOARD_PWM_I2C_PORT, - IT8801_KEYBOARD_PWM_I2C_ADDR_FLAGS, 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; -} - -/* - * Keyboard and GPIO interrupts are muxed inside the IT8801 chip. - * Interrupt enable register controls the individual pins from - * triggering this global interrupt hence it is okay that this - * pin is enabled all the time. - */ -static void it8801_muxed_kbd_gpio_intr_enable(void) -{ - static bool intr_enabled; - - /* - * Allow enabling this pin either by Keyboard enable code or - * IOEX init code whichever gets called first. - */ - if (!intr_enabled) { - gpio_clear_pending_interrupt(GPIO_IT8801_SMB_INT); - gpio_enable_interrupt(GPIO_IT8801_SMB_INT); - intr_enabled = true; - } -} - -#ifdef CONFIG_KEYBOARD_NOT_RAW -void keyboard_raw_init(void) -{ - int ret; - - /* 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_ioex_set_level(0, 2, IT8801_REG_GPIO23SOV, 1); - } - - /* 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); -} - -__overridable const uint8_t it8801_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(it8801_kso_mapping) == KEYBOARD_COLS_MAX); - -test_mockable void keyboard_raw_drive_column(int col) -{ - int kso_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_ioex_set_level(0, 2, IT8801_REG_GPIO23SOV, 0); - } - } - /* 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_ioex_set_level(0, 2, IT8801_REG_GPIO23SOV, 1); - } - } 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 = it8801_kso_mapping[col]; - - if (IS_ENABLED(CONFIG_KEYBOARD_COL2_INVERTED)) { - /* GPIO23 is inverted. */ - if (col == IT8801_REG_MASK_SELKSO2) { - /* Output high(so selected). */ - it8801_ioex_set_level(0, 2, - IT8801_REG_GPIO23SOV, 1); - } else { - /* Output low(so not selected). */ - it8801_ioex_set_level(0, 2, - IT8801_REG_GPIO23SOV, 0); - } - } - } - - 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) { - /* Clear pending iterrupts */ - it8801_write(IT8801_REG_KSIEER, 0xff); - - /* Enable muxed Keyboard & GPIO interrupt */ - it8801_muxed_kbd_gpio_intr_enable(); - } - - it8801_write(IT8801_REG_KSIIER, enable ? 0xff : 0x00); -} -#endif /* CONFIG_KEYBOARD_NOT_RAW */ - -void io_expander_it8801_interrupt(enum gpio_signal signal) -{ - hook_call_deferred(&it8801_ioex_event_handler_data, 0); -} - -static int it8801_ioex_read(int ioex, int reg, int *data) -{ - struct ioexpander_config_t *ioex_p = &ioex_config[ioex]; - - return i2c_read8(ioex_p->i2c_host_port, ioex_p->i2c_addr_flags, - reg, data); -} - -static int it8801_ioex_write(int ioex, int reg, int data) -{ - struct ioexpander_config_t *ioex_p = &ioex_config[ioex]; - - return i2c_write8(ioex_p->i2c_host_port, ioex_p->i2c_addr_flags, - reg, data); -} - -static int it8801_ioex_update(int ioex, int reg, int data, - enum mask_update_action action) -{ - struct ioexpander_config_t *ioex_p = &ioex_config[ioex]; - - return i2c_update8(ioex_p->i2c_host_port, ioex_p->i2c_addr_flags, - reg, data, action); -} - -static const int it8801_valid_gpio_group[] = { - IT8801_VALID_GPIO_G0_MASK, - IT8801_VALID_GPIO_G1_MASK, - IT8801_VALID_GPIO_G2_MASK, -}; - -/* Mutexes */ -static struct mutex ioex_mutex; - -static uint8_t it8801_gpio_sov[ARRAY_SIZE(it8801_valid_gpio_group)]; - -/* - * Initialize the general purpose I/O port(GPIO) - */ -static int it8801_ioex_init(int ioex) -{ - int ret, port, val = 0; - - /* Verify Vendor ID registers. */ - ret = it8801_check_vendor_id(); - if (ret) { - CPRINTS("Failed to read IT8801 vendor id %x", ret); - return ret; - } - - /* - * We will read the value of SOVR and write it to the - * cache(it8801_gpio_sov[port]) to avoid causing cache - * to reset when EC is reset. - */ - for (port = 0; port < ARRAY_SIZE(it8801_valid_gpio_group); port++) { - it8801_ioex_read(ioex, IT8801_REG_GPIO_SOVR(port), &val); - it8801_gpio_sov[port] = val; - } - - /* Enable muxed Keyboard & GPIO interrupt */ - it8801_muxed_kbd_gpio_intr_enable(); - - return EC_SUCCESS; -} - -static int ioex_check_is_not_valid(int port, int mask) -{ - if (port >= ARRAY_SIZE(it8801_valid_gpio_group)) { - CPRINTS("Port%d is not support in IT8801", port); - return EC_ERROR_INVAL; - } - - if (mask & ~it8801_valid_gpio_group[port]) { - CPRINTS("GPIO%d-%d is not support in IT8801", port, - __fls(mask & ~it8801_valid_gpio_group[port])); - return EC_ERROR_INVAL; - } - - return EC_SUCCESS; -} - -static int it8801_ioex_get_level(int ioex, int port, int mask, int *val) -{ - int rv; - - if (ioex_check_is_not_valid(port, mask)) - return EC_ERROR_INVAL; - - rv = it8801_ioex_read(ioex, IT8801_REG_GPIO_IPSR(port), val); - - *val = !!(*val & mask); - - return rv; -} - -static int it8801_ioex_set_level(int ioex, int port, int mask, int value) -{ - int rv = EC_SUCCESS; - - if (ioex_check_is_not_valid(port, mask)) - return EC_ERROR_INVAL; - - mutex_lock(&ioex_mutex); - /* - * The bit of output value in SOV is different than - * the one we were about to set it to. - */ - if (!!(it8801_gpio_sov[port] & mask) ^ value) { - if (value) - it8801_gpio_sov[port] |= mask; - else - it8801_gpio_sov[port] &= ~mask; - - rv = it8801_ioex_write(ioex, IT8801_REG_GPIO_SOVR(port), - it8801_gpio_sov[port]); - } - mutex_unlock(&ioex_mutex); - - return rv; -} - -static int it8801_ioex_get_flags_by_mask(int ioex, int port, - int mask, int *flags) -{ - int rv, val; - - if (ioex_check_is_not_valid(port, mask)) - return EC_ERROR_INVAL; - - rv = it8801_ioex_read(ioex, IT8801_REG_GPIO_CR(port, mask), &val); - if (rv) - return rv; - - *flags = 0; - - /* Get GPIO direction */ - *flags |= (val & IT8801_GPIODIR) ? GPIO_OUTPUT : GPIO_INPUT; - - /* Get GPIO type, 0:push-pull 1:open-drain */ - if (val & IT8801_GPIOIOT) - *flags |= GPIO_OPEN_DRAIN; - - rv = it8801_ioex_read(ioex, IT8801_REG_GPIO_IPSR(port), &val); - if (rv) - return rv; - - /* Get GPIO output level */ - *flags |= (val & mask) ? GPIO_HIGH : GPIO_LOW; - - return EC_SUCCESS; -} - -static int it8801_ioex_set_flags_by_mask(int ioex, int port, - int mask, int flags) -{ - int rv, val; - - if (ioex_check_is_not_valid(port, mask)) - return EC_ERROR_INVAL; - - if (flags & ~IT8801_SUPPORT_GPIO_FLAGS) { - CPRINTS("Flag 0x%08x is not supported at port %d, mask %d", - flags, port, mask); - return EC_ERROR_INVAL; - } - - /* GPIO alternate function switching(GPIO[00, 12:15, 20:23]). */ - rv = it8801_ioex_write(ioex, IT8801_REG_GPIO_CR(port, mask), - IT8801_REG_MASK_GPIOAFS_FUNC1); - if (rv) - return rv; - - mutex_lock(&ioex_mutex); - rv = it8801_ioex_read(ioex, IT8801_REG_GPIO_CR(port, mask), &val); - if (rv) - goto unlock_mutex; - - /* Select open drain 0:push-pull 1:open-drain */ - if (flags & GPIO_OPEN_DRAIN) - val |= IT8801_GPIOIOT; - else - val &= ~IT8801_GPIOIOT; - - /* Select GPIO direction */ - if (flags & GPIO_OUTPUT) { - /* Configure the output level */ - if (flags & GPIO_HIGH) - it8801_gpio_sov[port] |= mask; - else - it8801_gpio_sov[port] &= ~mask; - - rv = it8801_ioex_write(ioex, IT8801_REG_GPIO_SOVR(port), - it8801_gpio_sov[port]); - if (rv) - goto unlock_mutex; - - val |= IT8801_GPIODIR; - } else { - val &= ~IT8801_GPIODIR; - } - - /* Set Interrupt Type */ - if (flags & GPIO_INT_RISING) - val |= IT8801_GPIOIOT_INT_RISING; - if (flags & GPIO_INT_FALLING) - val |= IT8801_GPIOIOT_INT_FALLING; - - rv = it8801_ioex_write(ioex, IT8801_REG_GPIO_CR(port, mask), val); - -unlock_mutex: - mutex_unlock(&ioex_mutex); - - return rv; -} - -/* Enable the individual GPIO interrupt pins based on the board requirement. */ -static int it8801_ioex_enable_interrupt(int ioex, int port, int mask, - int enable) -{ - int rv; - - if (ioex_check_is_not_valid(port, mask)) - return EC_ERROR_INVAL; - - /* Clear pending interrupt */ - rv = it8801_ioex_update(ioex, IT8801_REG_GPIO_ISR(port), - mask, MASK_SET); - if (rv) - return rv; - - return it8801_ioex_update(ioex, IT8801_REG_GPIO_IER(port), - mask, enable ? MASK_SET : MASK_CLR); -} - -static void it8801_ioex_irq(int ioex, int port) -{ - int rv, data, i; - const struct ioex_info *g; - - rv = it8801_ioex_read(ioex, IT8801_REG_GPIO_ISR(port), &data); - if (rv || !data) - return; - - /* Trigger the intended interrupt from the IOEX IRQ pins */ - for (i = 0, g = ioex_list; i < ioex_ih_count; i++, g++) { - if (ioex == g->ioex && port == g->port && data & g->mask) { - ioex_irq_handlers[i](i + IOEX_SIGNAL_START); - data &= ~g->mask; - - /* Clear pending interrupt */ - it8801_ioex_update(ioex, IT8801_REG_GPIO_ISR(port), - g->mask, MASK_SET); - - if (!data) - break; - } - } -} - -static void it8801_ioex_event_handler(void) -{ - int data, i; - - /* Gather KSI interrupt status register */ - if (it8801_read(IT8801_REG_GISR, &data)) - return; - - /* Wake the keyboard scan task if KSI interrupts are triggered */ - if (IS_ENABLED(CONFIG_KEYBOARD_NOT_RAW) && - data & IT8801_REG_MASK_GISR_GKSIIS) - task_wake(TASK_ID_KEYSCAN); - - /* - * Trigger the GPIO callback functions if the GPIO interrupts are - * triggered. - */ - if (data & (IT8801_REG_MASK_GISR_GGPIOGXIS)) { - for (i = 0; i < CONFIG_IO_EXPANDER_PORT_COUNT; i++) { - if (ioex_config[i].drv == &it8801_ioexpander_drv) { - /* Interrupt from GPIO port 0 is triggered */ - if (data & IT8801_REG_MASK_GISR_GGPIOG0IS) - it8801_ioex_irq(i, 0); - /* Interrupt from GPIO port 1 is triggered */ - if (data & IT8801_REG_MASK_GISR_GGPIOG1IS) - it8801_ioex_irq(i, 1); - /* Interrupt from GPIO port 2 is triggered */ - if (data & IT8801_REG_MASK_GISR_GGPIOG2IS) - it8801_ioex_irq(i, 2); - } - } - } -} - -const struct ioexpander_drv it8801_ioexpander_drv = { - .init = &it8801_ioex_init, - .get_level = &it8801_ioex_get_level, - .set_level = &it8801_ioex_set_level, - .get_flags_by_mask = &it8801_ioex_get_flags_by_mask, - .set_flags_by_mask = &it8801_ioex_set_flags_by_mask, - .enable_interrupt = &it8801_ioex_enable_interrupt, -}; - -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"); - -#ifdef CONFIG_IO_EXPANDER_IT8801_PWM - -struct it8801_pwm_gpio_map { - int port; - int mask; - int pushpull_en; -}; - -const static struct it8801_pwm_gpio_map it8801_pwm_gpio_map[] = { - [1] = {.port = 1, .mask = BIT(2), .pushpull_en = BIT(0)}, - [2] = {.port = 1, .mask = BIT(3), .pushpull_en = BIT(1)}, - [3] = {.port = 1, .mask = BIT(4), .pushpull_en = BIT(2)}, - [4] = {.port = 1, .mask = BIT(5), .pushpull_en = BIT(3)}, - [7] = {.port = 2, .mask = BIT(0), .pushpull_en = BIT(4)}, - [8] = {.port = 2, .mask = BIT(3), .pushpull_en = BIT(5)}, - [9] = {.port = 2, .mask = BIT(2), .pushpull_en = BIT(6)}, -}; - -void it8801_pwm_enable(enum pwm_channel ch, int enabled) -{ - int port, mask, val, index; - - index = it8801_pwm_channels[ch].index; - if (index < 0 || index >= ARRAY_SIZE(it8801_pwm_gpio_map)) - return; - port = it8801_pwm_gpio_map[index].port; - mask = it8801_pwm_gpio_map[index].mask; - if (port == 0 && mask == 0) - return; - - /* - * PWM1~4,7: alt func 1 - * PWM8,9: alt func 2 - */ - if (it8801_pwm_channels[ch].index <= 7) - it8801_write(IT8801_REG_GPIO_CR(port, mask), - 0x1 << IT8801_GPIOAFS_SHIFT); - else - it8801_write(IT8801_REG_GPIO_CR(port, mask), - 0x2 << IT8801_GPIOAFS_SHIFT); - - it8801_read(IT8801_REG_PWMMCR(it8801_pwm_channels[ch].index), &val); - val &= (~IT8801_PWMMCR_MCR_MASK); - if (enabled) - val |= IT8801_PWMMCR_MCR_BLINKING; - it8801_write(IT8801_REG_PWMMCR(it8801_pwm_channels[ch].index), val); - - /* - * 1: enable push pull function - */ - it8801_read(IT8801_REG_PWMODDSR, &val); - val &= ~it8801_pwm_gpio_map[index].pushpull_en; - if (enabled) - val |= it8801_pwm_gpio_map[index].pushpull_en; - it8801_write(IT8801_REG_PWMODDSR, val); - -} - -int it8801_pwm_get_enabled(enum pwm_channel ch) -{ - int val; - - if (it8801_read(IT8801_REG_PWMMCR(it8801_pwm_channels[ch].index), &val)) - return 0; - return (val & IT8801_PWMMCR_MCR_MASK) == IT8801_PWMMCR_MCR_BLINKING; -} - -void it8801_pwm_set_raw_duty(enum pwm_channel ch, uint16_t duty) -{ - duty = MIN(duty, 255); - duty = MAX(duty, 0); - it8801_write(IT8801_REG_PWMDCR(it8801_pwm_channels[ch].index), duty); -} - -uint16_t it8801_pwm_get_raw_duty(enum pwm_channel ch) -{ - int val; - - if (it8801_read(IT8801_REG_PWMDCR(it8801_pwm_channels[ch].index), &val)) - return 0; - return val; -} - -void it8801_pwm_set_duty(enum pwm_channel ch, int percent) -{ - return it8801_pwm_set_raw_duty(ch, percent * 255 / 100); -} - -int it8801_pwm_get_duty(enum pwm_channel ch) -{ - return it8801_pwm_get_raw_duty(ch) * 100 / 255; -} - -#ifdef CONFIG_KEYBOARD_BACKLIGHT -const enum pwm_channel it8801_kblight_pwm_ch = IT8801_PWM_CH_KBLIGHT; - -static int it8801_kblight_enable(int enable) -{ - it8801_pwm_enable(it8801_kblight_pwm_ch, enable); - return EC_SUCCESS; -} - -static int it8801_kblight_set_brightness(int percent) -{ - it8801_pwm_set_duty(it8801_kblight_pwm_ch, percent); - return EC_SUCCESS; -} - -static int it8801_kblight_get_brightness(void) -{ - return it8801_pwm_get_duty(it8801_kblight_pwm_ch); -} - -static int it8801_kblight_init(void) -{ - it8801_pwm_set_duty(it8801_kblight_pwm_ch, 0); - it8801_pwm_enable(it8801_kblight_pwm_ch, 1); - return EC_SUCCESS; -} - -const struct kblight_drv kblight_it8801 = { - .init = it8801_kblight_init, - .set = it8801_kblight_set_brightness, - .get = it8801_kblight_get_brightness, - .enable = it8801_kblight_enable, -}; -#endif -#endif /* CONFIG_IO_EXPANDER_IT8801_PWM */ |