| /* Copyright 2013 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. |
| */ |
| |
| /* GPIO module for Chrome EC */ |
| |
| #include "clock.h" |
| #include "common.h" |
| #include "gpio.h" |
| #include "hooks.h" |
| #include "intc.h" |
| #include "it83xx_pd.h" |
| #include "kmsc_chip.h" |
| #include "registers.h" |
| #include "switch.h" |
| #include "system.h" |
| #include "task.h" |
| #include "timer.h" |
| #include "util.h" |
| |
| /* Data structure to define KSI/KSO GPIO mode control registers. */ |
| struct kbs_gpio_ctrl_t { |
| /* GPIO mode control register. */ |
| volatile uint8_t *gpio_mode; |
| /* GPIO output enable register. */ |
| volatile uint8_t *gpio_out; |
| }; |
| |
| static const struct kbs_gpio_ctrl_t kbs_gpio_ctrl_regs[] = { |
| /* KSI pins 7:0 */ |
| {&IT83XX_KBS_KSIGCTRL, &IT83XX_KBS_KSIGOEN}, |
| /* KSO pins 15:8 */ |
| {&IT83XX_KBS_KSOHGCTRL, &IT83XX_KBS_KSOHGOEN}, |
| /* KSO pins 7:0 */ |
| {&IT83XX_KBS_KSOLGCTRL, &IT83XX_KBS_KSOLGOEN}, |
| }; |
| |
| /** |
| * Convert wake-up controller (WUC) group to the corresponding wake-up edge |
| * sense register (WUESR). Return pointer to the register. |
| * |
| * @param grp WUC group. |
| * |
| * @return Pointer to corresponding WUESR register. |
| */ |
| static volatile uint8_t *wuesr(uint8_t grp) |
| { |
| /* |
| * From WUESR1-WUESR4, the address increases by ones. From WUESR5 on |
| * the address increases by fours. |
| */ |
| return (grp <= 4) ? |
| (volatile uint8_t *)(IT83XX_WUC_WUESR1 + grp-1) : |
| (volatile uint8_t *)(IT83XX_WUC_WUESR5 + 4*(grp-5)); |
| } |
| |
| /** |
| * Convert wake-up controller (WUC) group to the corresponding wake-up edge |
| * mode register (WUEMR). Return pointer to the register. |
| * |
| * @param grp WUC group. |
| * |
| * @return Pointer to corresponding WUEMR register. |
| */ |
| static volatile uint8_t *wuemr(uint8_t grp) |
| { |
| /* |
| * From WUEMR1-WUEMR4, the address increases by ones. From WUEMR5 on |
| * the address increases by fours. |
| */ |
| return (grp <= 4) ? |
| (volatile uint8_t *)(IT83XX_WUC_WUEMR1 + grp-1) : |
| (volatile uint8_t *)(IT83XX_WUC_WUEMR5 + 4*(grp-5)); |
| } |
| |
| /** |
| * Convert wake-up controller (WUC) group to the corresponding wake-up both edge |
| * mode register (WUBEMR). Return pointer to the register. |
| * |
| * @param grp WUC group. |
| * |
| * @return Pointer to corresponding WUBEMR register. |
| */ |
| #ifdef IT83XX_GPIO_INT_FLEXIBLE |
| static volatile uint8_t *wubemr(uint8_t grp) |
| { |
| /* |
| * From WUBEMR1-WUBEMR4, the address increases by ones. From WUBEMR5 on |
| * the address increases by fours. |
| */ |
| return (grp <= 4) ? |
| (volatile uint8_t *)(IT83XX_WUC_WUBEMR1 + grp-1) : |
| (volatile uint8_t *)(IT83XX_WUC_WUBEMR5 + 4*(grp-5)); |
| } |
| #endif |
| |
| /* |
| * Array to store the corresponding GPIO port and mask, and WUC group and mask |
| * for each WKO interrupt. This allows GPIO interrupts coming in through WKO |
| * to easily identify which pin caused the interrupt. |
| * Note: Using designated initializers here in addition to using the array size |
| * assert because many rows are purposely skipped. Not all IRQs are WKO IRQs, |
| * so the IRQ index skips around. But, we still want the entire array to take |
| * up the size of the total number of IRQs because the index to the array could |
| * be any IRQ number. |
| */ |
| static const struct { |
| uint8_t gpio_port; |
| uint8_t gpio_mask; |
| uint8_t wuc_group; |
| uint8_t wuc_mask; |
| } gpio_irqs[] = { |
| /* irq gpio_port,gpio_mask,wuc_group,wuc_mask */ |
| [IT83XX_IRQ_WKO20] = {GPIO_D, BIT(0), 2, BIT(0)}, |
| [IT83XX_IRQ_WKO21] = {GPIO_D, BIT(1), 2, BIT(1)}, |
| [IT83XX_IRQ_WKO22] = {GPIO_C, BIT(4), 2, BIT(2)}, |
| [IT83XX_IRQ_WKO23] = {GPIO_C, BIT(6), 2, BIT(3)}, |
| [IT83XX_IRQ_WKO24] = {GPIO_D, BIT(2), 2, BIT(4)}, |
| #ifdef IT83XX_GPIO_INT_FLEXIBLE |
| [IT83XX_IRQ_WKO40] = {GPIO_E, BIT(5), 4, BIT(0)}, |
| [IT83XX_IRQ_WKO45] = {GPIO_E, BIT(6), 4, BIT(5)}, |
| [IT83XX_IRQ_WKO46] = {GPIO_E, BIT(7), 4, BIT(6)}, |
| #endif |
| [IT83XX_IRQ_WKO50] = {GPIO_K, BIT(0), 5, BIT(0)}, |
| [IT83XX_IRQ_WKO51] = {GPIO_K, BIT(1), 5, BIT(1)}, |
| [IT83XX_IRQ_WKO52] = {GPIO_K, BIT(2), 5, BIT(2)}, |
| [IT83XX_IRQ_WKO53] = {GPIO_K, BIT(3), 5, BIT(3)}, |
| [IT83XX_IRQ_WKO54] = {GPIO_K, BIT(4), 5, BIT(4)}, |
| [IT83XX_IRQ_WKO55] = {GPIO_K, BIT(5), 5, BIT(5)}, |
| [IT83XX_IRQ_WKO56] = {GPIO_K, BIT(6), 5, BIT(6)}, |
| [IT83XX_IRQ_WKO57] = {GPIO_K, BIT(7), 5, BIT(7)}, |
| [IT83XX_IRQ_WKO60] = {GPIO_H, BIT(0), 6, BIT(0)}, |
| [IT83XX_IRQ_WKO61] = {GPIO_H, BIT(1), 6, BIT(1)}, |
| [IT83XX_IRQ_WKO62] = {GPIO_H, BIT(2), 6, BIT(2)}, |
| [IT83XX_IRQ_WKO63] = {GPIO_H, BIT(3), 6, BIT(3)}, |
| [IT83XX_IRQ_WKO64] = {GPIO_F, BIT(4), 6, BIT(4)}, |
| [IT83XX_IRQ_WKO65] = {GPIO_F, BIT(5), 6, BIT(5)}, |
| [IT83XX_IRQ_WKO65] = {GPIO_F, BIT(6), 6, BIT(6)}, |
| [IT83XX_IRQ_WKO67] = {GPIO_F, BIT(7), 6, BIT(7)}, |
| [IT83XX_IRQ_WKO70] = {GPIO_E, BIT(0), 7, BIT(0)}, |
| [IT83XX_IRQ_WKO71] = {GPIO_E, BIT(1), 7, BIT(1)}, |
| [IT83XX_IRQ_WKO72] = {GPIO_E, BIT(2), 7, BIT(2)}, |
| [IT83XX_IRQ_WKO73] = {GPIO_E, BIT(3), 7, BIT(3)}, |
| [IT83XX_IRQ_WKO74] = {GPIO_I, BIT(4), 7, BIT(4)}, |
| [IT83XX_IRQ_WKO75] = {GPIO_I, BIT(5), 7, BIT(5)}, |
| [IT83XX_IRQ_WKO76] = {GPIO_I, BIT(6), 7, BIT(6)}, |
| [IT83XX_IRQ_WKO77] = {GPIO_I, BIT(7), 7, BIT(7)}, |
| [IT83XX_IRQ_WKO80] = {GPIO_A, BIT(3), 8, BIT(0)}, |
| [IT83XX_IRQ_WKO81] = {GPIO_A, BIT(4), 8, BIT(1)}, |
| [IT83XX_IRQ_WKO82] = {GPIO_A, BIT(5), 8, BIT(2)}, |
| [IT83XX_IRQ_WKO83] = {GPIO_A, BIT(6), 8, BIT(3)}, |
| [IT83XX_IRQ_WKO84] = {GPIO_B, BIT(2), 8, BIT(4)}, |
| [IT83XX_IRQ_WKO85] = {GPIO_C, BIT(0), 8, BIT(5)}, |
| [IT83XX_IRQ_WKO86] = {GPIO_C, BIT(7), 8, BIT(6)}, |
| [IT83XX_IRQ_WKO87] = {GPIO_D, BIT(7), 8, BIT(7)}, |
| [IT83XX_IRQ_WKO88] = {GPIO_H, BIT(4), 9, BIT(0)}, |
| [IT83XX_IRQ_WKO89] = {GPIO_H, BIT(5), 9, BIT(1)}, |
| [IT83XX_IRQ_WKO90] = {GPIO_H, BIT(6), 9, BIT(2)}, |
| [IT83XX_IRQ_WKO91] = {GPIO_A, BIT(0), 9, BIT(3)}, |
| [IT83XX_IRQ_WKO92] = {GPIO_A, BIT(1), 9, BIT(4)}, |
| [IT83XX_IRQ_WKO93] = {GPIO_A, BIT(2), 9, BIT(5)}, |
| [IT83XX_IRQ_WKO94] = {GPIO_B, BIT(4), 9, BIT(6)}, |
| [IT83XX_IRQ_WKO95] = {GPIO_C, BIT(2), 9, BIT(7)}, |
| [IT83XX_IRQ_WKO96] = {GPIO_F, BIT(0), 10, BIT(0)}, |
| [IT83XX_IRQ_WKO97] = {GPIO_F, BIT(1), 10, BIT(1)}, |
| [IT83XX_IRQ_WKO98] = {GPIO_F, BIT(2), 10, BIT(2)}, |
| [IT83XX_IRQ_WKO99] = {GPIO_F, BIT(3), 10, BIT(3)}, |
| [IT83XX_IRQ_WKO100] = {GPIO_A, BIT(7), 10, BIT(4)}, |
| [IT83XX_IRQ_WKO101] = {GPIO_B, BIT(0), 10, BIT(5)}, |
| [IT83XX_IRQ_WKO102] = {GPIO_B, BIT(1), 10, BIT(6)}, |
| [IT83XX_IRQ_WKO103] = {GPIO_B, BIT(3), 10, BIT(7)}, |
| [IT83XX_IRQ_WKO104] = {GPIO_B, BIT(5), 11, BIT(0)}, |
| [IT83XX_IRQ_WKO105] = {GPIO_B, BIT(6), 11, BIT(1)}, |
| [IT83XX_IRQ_WKO106] = {GPIO_B, BIT(7), 11, BIT(2)}, |
| [IT83XX_IRQ_WKO107] = {GPIO_C, BIT(1), 11, BIT(3)}, |
| [IT83XX_IRQ_WKO108] = {GPIO_C, BIT(3), 11, BIT(4)}, |
| [IT83XX_IRQ_WKO109] = {GPIO_C, BIT(5), 11, BIT(5)}, |
| [IT83XX_IRQ_WKO110] = {GPIO_D, BIT(3), 11, BIT(6)}, |
| [IT83XX_IRQ_WKO111] = {GPIO_D, BIT(4), 11, BIT(7)}, |
| [IT83XX_IRQ_WKO112] = {GPIO_D, BIT(5), 12, BIT(0)}, |
| [IT83XX_IRQ_WKO113] = {GPIO_D, BIT(6), 12, BIT(1)}, |
| [IT83XX_IRQ_WKO114] = {GPIO_E, BIT(4), 12, BIT(2)}, |
| [IT83XX_IRQ_WKO115] = {GPIO_G, BIT(0), 12, BIT(3)}, |
| [IT83XX_IRQ_WKO116] = {GPIO_G, BIT(1), 12, BIT(4)}, |
| [IT83XX_IRQ_WKO117] = {GPIO_G, BIT(2), 12, BIT(5)}, |
| [IT83XX_IRQ_WKO118] = {GPIO_G, BIT(6), 12, BIT(6)}, |
| [IT83XX_IRQ_WKO119] = {GPIO_I, BIT(0), 12, BIT(7)}, |
| [IT83XX_IRQ_WKO120] = {GPIO_I, BIT(1), 13, BIT(0)}, |
| [IT83XX_IRQ_WKO121] = {GPIO_I, BIT(2), 13, BIT(1)}, |
| [IT83XX_IRQ_WKO122] = {GPIO_I, BIT(3), 13, BIT(2)}, |
| #ifdef IT83XX_GPIO_INT_FLEXIBLE |
| [IT83XX_IRQ_WKO123] = {GPIO_G, BIT(3), 13, BIT(3)}, |
| [IT83XX_IRQ_WKO124] = {GPIO_G, BIT(4), 13, BIT(4)}, |
| [IT83XX_IRQ_WKO125] = {GPIO_G, BIT(5), 13, BIT(5)}, |
| [IT83XX_IRQ_WKO126] = {GPIO_G, BIT(7), 13, BIT(6)}, |
| #endif |
| [IT83XX_IRQ_WKO128] = {GPIO_J, BIT(0), 14, BIT(0)}, |
| [IT83XX_IRQ_WKO129] = {GPIO_J, BIT(1), 14, BIT(1)}, |
| [IT83XX_IRQ_WKO130] = {GPIO_J, BIT(2), 14, BIT(2)}, |
| [IT83XX_IRQ_WKO131] = {GPIO_J, BIT(3), 14, BIT(3)}, |
| [IT83XX_IRQ_WKO132] = {GPIO_J, BIT(4), 14, BIT(4)}, |
| [IT83XX_IRQ_WKO133] = {GPIO_J, BIT(5), 14, BIT(5)}, |
| [IT83XX_IRQ_WKO134] = {GPIO_J, BIT(6), 14, BIT(6)}, |
| [IT83XX_IRQ_WKO135] = {GPIO_J, BIT(7), 14, BIT(7)}, |
| [IT83XX_IRQ_WKO136] = {GPIO_L, BIT(0), 15, BIT(0)}, |
| [IT83XX_IRQ_WKO137] = {GPIO_L, BIT(1), 15, BIT(1)}, |
| [IT83XX_IRQ_WKO138] = {GPIO_L, BIT(2), 15, BIT(2)}, |
| [IT83XX_IRQ_WKO139] = {GPIO_L, BIT(3), 15, BIT(3)}, |
| [IT83XX_IRQ_WKO140] = {GPIO_L, BIT(4), 15, BIT(4)}, |
| [IT83XX_IRQ_WKO141] = {GPIO_L, BIT(5), 15, BIT(5)}, |
| [IT83XX_IRQ_WKO142] = {GPIO_L, BIT(6), 15, BIT(6)}, |
| [IT83XX_IRQ_WKO143] = {GPIO_L, BIT(7), 15, BIT(7)}, |
| #ifdef IT83XX_GPIO_INT_FLEXIBLE |
| [IT83XX_IRQ_WKO144] = {GPIO_M, BIT(0), 16, BIT(0)}, |
| [IT83XX_IRQ_WKO145] = {GPIO_M, BIT(1), 16, BIT(1)}, |
| [IT83XX_IRQ_WKO146] = {GPIO_M, BIT(2), 16, BIT(2)}, |
| [IT83XX_IRQ_WKO147] = {GPIO_M, BIT(3), 16, BIT(3)}, |
| [IT83XX_IRQ_WKO148] = {GPIO_M, BIT(4), 16, BIT(4)}, |
| [IT83XX_IRQ_WKO149] = {GPIO_M, BIT(5), 16, BIT(5)}, |
| [IT83XX_IRQ_WKO150] = {GPIO_M, BIT(6), 16, BIT(6)}, |
| #endif |
| #if defined(CHIP_FAMILY_IT8XXX1) || defined(CHIP_FAMILY_IT8XXX2) |
| [IT83XX_IRQ_GPO0] = {GPIO_O, BIT(0), 19, BIT(0)}, |
| [IT83XX_IRQ_GPO1] = {GPIO_O, BIT(1), 19, BIT(1)}, |
| [IT83XX_IRQ_GPO2] = {GPIO_O, BIT(2), 19, BIT(2)}, |
| [IT83XX_IRQ_GPO3] = {GPIO_O, BIT(3), 19, BIT(3)}, |
| [IT83XX_IRQ_GPP0] = {GPIO_P, BIT(0), 20, BIT(0)}, |
| [IT83XX_IRQ_GPP1] = {GPIO_P, BIT(1), 20, BIT(1)}, |
| [IT83XX_IRQ_GPP2] = {GPIO_P, BIT(2), 20, BIT(2)}, |
| [IT83XX_IRQ_GPP3] = {GPIO_P, BIT(3), 20, BIT(3)}, |
| [IT83XX_IRQ_GPP4] = {GPIO_P, BIT(4), 20, BIT(4)}, |
| [IT83XX_IRQ_GPP5] = {GPIO_P, BIT(5), 20, BIT(5)}, |
| [IT83XX_IRQ_GPP6] = {GPIO_P, BIT(6), 20, BIT(6)}, |
| [IT83XX_IRQ_GPQ0] = {GPIO_Q, BIT(0), 21, BIT(0)}, |
| [IT83XX_IRQ_GPQ1] = {GPIO_Q, BIT(1), 21, BIT(1)}, |
| [IT83XX_IRQ_GPQ2] = {GPIO_Q, BIT(2), 21, BIT(2)}, |
| [IT83XX_IRQ_GPQ3] = {GPIO_Q, BIT(3), 21, BIT(3)}, |
| [IT83XX_IRQ_GPQ4] = {GPIO_Q, BIT(4), 21, BIT(4)}, |
| [IT83XX_IRQ_GPQ5] = {GPIO_Q, BIT(5), 21, BIT(5)}, |
| [IT83XX_IRQ_GPR0] = {GPIO_R, BIT(0), 22, BIT(0)}, |
| [IT83XX_IRQ_GPR1] = {GPIO_R, BIT(1), 22, BIT(1)}, |
| [IT83XX_IRQ_GPR2] = {GPIO_R, BIT(2), 22, BIT(2)}, |
| [IT83XX_IRQ_GPR3] = {GPIO_R, BIT(3), 22, BIT(3)}, |
| [IT83XX_IRQ_GPR4] = {GPIO_R, BIT(4), 22, BIT(4)}, |
| [IT83XX_IRQ_GPR5] = {GPIO_R, BIT(5), 22, BIT(5)}, |
| #endif |
| [IT83XX_IRQ_COUNT] = { 0, 0, 0, 0}, |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(gpio_irqs) == IT83XX_IRQ_COUNT + 1); |
| |
| /** |
| * Given a GPIO port and mask, find the corresponding WKO interrupt number. |
| * |
| * @param port GPIO port |
| * @param mask GPIO mask |
| * |
| * @return IRQ for the WKO interrupt on the corresponding input pin. |
| */ |
| static int gpio_to_irq(uint8_t port, uint8_t mask) |
| { |
| int i; |
| |
| for (i = 0; i < IT83XX_IRQ_COUNT; i++) { |
| if (gpio_irqs[i].gpio_port == port && |
| gpio_irqs[i].gpio_mask == mask) |
| return i; |
| } |
| |
| return -1; |
| } |
| |
| struct gpio_1p8v_t { |
| volatile uint8_t *reg; |
| uint8_t sel; |
| }; |
| |
| static const struct gpio_1p8v_t gpio_1p8v_sel[GPIO_PORT_COUNT][8] = { |
| #ifdef IT83XX_GPIO_1P8V_PIN_EXTENDED |
| [GPIO_A] = { [4] = {&IT83XX_GPIO_GRC24, BIT(0)}, |
| [5] = {&IT83XX_GPIO_GRC24, BIT(1)}, |
| [6] = {&IT83XX_GPIO_GRC24, BIT(5)}, |
| [7] = {&IT83XX_GPIO_GRC24, BIT(6)} }, |
| [GPIO_B] = { [3] = {&IT83XX_GPIO_GRC22, BIT(1)}, |
| [4] = {&IT83XX_GPIO_GRC22, BIT(0)}, |
| [5] = {&IT83XX_GPIO_GRC19, BIT(7)}, |
| [6] = {&IT83XX_GPIO_GRC19, BIT(6)}, |
| [7] = {&IT83XX_GPIO_GRC24, BIT(4)} }, |
| [GPIO_C] = { [0] = {&IT83XX_GPIO_GRC22, BIT(7)}, |
| [1] = {&IT83XX_GPIO_GRC19, BIT(5)}, |
| [2] = {&IT83XX_GPIO_GRC19, BIT(4)}, |
| [4] = {&IT83XX_GPIO_GRC24, BIT(2)}, |
| [6] = {&IT83XX_GPIO_GRC24, BIT(3)}, |
| [7] = {&IT83XX_GPIO_GRC19, BIT(3)} }, |
| [GPIO_D] = { [0] = {&IT83XX_GPIO_GRC19, BIT(2)}, |
| [1] = {&IT83XX_GPIO_GRC19, BIT(1)}, |
| [2] = {&IT83XX_GPIO_GRC19, BIT(0)}, |
| [3] = {&IT83XX_GPIO_GRC20, BIT(7)}, |
| [4] = {&IT83XX_GPIO_GRC20, BIT(6)}, |
| [5] = {&IT83XX_GPIO_GRC22, BIT(4)}, |
| [6] = {&IT83XX_GPIO_GRC22, BIT(5)}, |
| [7] = {&IT83XX_GPIO_GRC22, BIT(6)} }, |
| [GPIO_E] = { [0] = {&IT83XX_GPIO_GRC20, BIT(5)}, |
| [1] = {&IT83XX_GPIO_GCR28, BIT(6)}, |
| [2] = {&IT83XX_GPIO_GCR28, BIT(7)}, |
| [4] = {&IT83XX_GPIO_GRC22, BIT(2)}, |
| [5] = {&IT83XX_GPIO_GRC22, BIT(3)}, |
| [6] = {&IT83XX_GPIO_GRC20, BIT(4)}, |
| [7] = {&IT83XX_GPIO_GRC20, BIT(3)} }, |
| [GPIO_F] = { [0] = {&IT83XX_GPIO_GCR28, BIT(4)}, |
| [1] = {&IT83XX_GPIO_GCR28, BIT(5)}, |
| [2] = {&IT83XX_GPIO_GRC20, BIT(2)}, |
| [3] = {&IT83XX_GPIO_GRC20, BIT(1)}, |
| [4] = {&IT83XX_GPIO_GRC20, BIT(0)}, |
| [5] = {&IT83XX_GPIO_GRC21, BIT(7)}, |
| [6] = {&IT83XX_GPIO_GRC21, BIT(6)}, |
| [7] = {&IT83XX_GPIO_GRC21, BIT(5)} }, |
| [GPIO_G] = { [0] = {&IT83XX_GPIO_GCR28, BIT(2)}, |
| [1] = {&IT83XX_GPIO_GRC21, BIT(4)}, |
| [2] = {&IT83XX_GPIO_GCR28, BIT(3)}, |
| [6] = {&IT83XX_GPIO_GRC21, BIT(3)} }, |
| [GPIO_H] = { [0] = {&IT83XX_GPIO_GRC21, BIT(2)}, |
| [1] = {&IT83XX_GPIO_GRC21, BIT(1)}, |
| [2] = {&IT83XX_GPIO_GRC21, BIT(0)}, |
| [5] = {&IT83XX_GPIO_GCR27, BIT(7)}, |
| [6] = {&IT83XX_GPIO_GCR28, BIT(0)} }, |
| [GPIO_I] = { [0] = {&IT83XX_GPIO_GCR27, BIT(3)}, |
| [1] = {&IT83XX_GPIO_GRC23, BIT(4)}, |
| [2] = {&IT83XX_GPIO_GRC23, BIT(5)}, |
| [3] = {&IT83XX_GPIO_GRC23, BIT(6)}, |
| [4] = {&IT83XX_GPIO_GRC23, BIT(7)}, |
| [5] = {&IT83XX_GPIO_GCR27, BIT(4)}, |
| [6] = {&IT83XX_GPIO_GCR27, BIT(5)}, |
| [7] = {&IT83XX_GPIO_GCR27, BIT(6)} }, |
| [GPIO_J] = { [0] = {&IT83XX_GPIO_GRC23, BIT(0)}, |
| [1] = {&IT83XX_GPIO_GRC23, BIT(1)}, |
| [2] = {&IT83XX_GPIO_GRC23, BIT(2)}, |
| [3] = {&IT83XX_GPIO_GRC23, BIT(3)}, |
| [4] = {&IT83XX_GPIO_GCR27, BIT(0)}, |
| [5] = {&IT83XX_GPIO_GCR27, BIT(1)}, |
| [6] = {&IT83XX_GPIO_GCR27, BIT(2)}, |
| [7] = {&IT83XX_GPIO_GCR33, BIT(2)} }, |
| [GPIO_K] = { [0] = {&IT83XX_GPIO_GCR26, BIT(0)}, |
| [1] = {&IT83XX_GPIO_GCR26, BIT(1)}, |
| [2] = {&IT83XX_GPIO_GCR26, BIT(2)}, |
| [3] = {&IT83XX_GPIO_GCR26, BIT(3)}, |
| [4] = {&IT83XX_GPIO_GCR26, BIT(4)}, |
| [5] = {&IT83XX_GPIO_GCR26, BIT(5)}, |
| [6] = {&IT83XX_GPIO_GCR26, BIT(6)}, |
| [7] = {&IT83XX_GPIO_GCR26, BIT(7)} }, |
| [GPIO_L] = { [0] = {&IT83XX_GPIO_GCR25, BIT(0)}, |
| [1] = {&IT83XX_GPIO_GCR25, BIT(1)}, |
| [2] = {&IT83XX_GPIO_GCR25, BIT(2)}, |
| [3] = {&IT83XX_GPIO_GCR25, BIT(3)}, |
| [4] = {&IT83XX_GPIO_GCR25, BIT(4)}, |
| [5] = {&IT83XX_GPIO_GCR25, BIT(5)}, |
| [6] = {&IT83XX_GPIO_GCR25, BIT(6)}, |
| [7] = {&IT83XX_GPIO_GCR25, BIT(7)} }, |
| #if defined(CHIP_FAMILY_IT8XXX1) || defined(CHIP_FAMILY_IT8XXX2) |
| [GPIO_O] = { [0] = {&IT83XX_GPIO_GCR31, BIT(0)}, |
| [1] = {&IT83XX_GPIO_GCR31, BIT(1)}, |
| [2] = {&IT83XX_GPIO_GCR31, BIT(2)}, |
| [3] = {&IT83XX_GPIO_GCR31, BIT(3)} }, |
| [GPIO_P] = { [0] = {&IT83XX_GPIO_GCR32, BIT(0)}, |
| [1] = {&IT83XX_GPIO_GCR32, BIT(1)}, |
| [2] = {&IT83XX_GPIO_GCR32, BIT(2)}, |
| [3] = {&IT83XX_GPIO_GCR32, BIT(3)}, |
| [4] = {&IT83XX_GPIO_GCR32, BIT(4)}, |
| [5] = {&IT83XX_GPIO_GCR32, BIT(5)}, |
| [6] = {&IT83XX_GPIO_GCR32, BIT(6)} }, |
| #endif |
| #else |
| [GPIO_A] = { [4] = {&IT83XX_GPIO_GRC24, BIT(0)}, |
| [5] = {&IT83XX_GPIO_GRC24, BIT(1)} }, |
| [GPIO_B] = { [3] = {&IT83XX_GPIO_GRC22, BIT(1)}, |
| [4] = {&IT83XX_GPIO_GRC22, BIT(0)}, |
| [5] = {&IT83XX_GPIO_GRC19, BIT(7)}, |
| [6] = {&IT83XX_GPIO_GRC19, BIT(6)} }, |
| [GPIO_C] = { [1] = {&IT83XX_GPIO_GRC19, BIT(5)}, |
| [2] = {&IT83XX_GPIO_GRC19, BIT(4)}, |
| [7] = {&IT83XX_GPIO_GRC19, BIT(3)} }, |
| [GPIO_D] = { [0] = {&IT83XX_GPIO_GRC19, BIT(2)}, |
| [1] = {&IT83XX_GPIO_GRC19, BIT(1)}, |
| [2] = {&IT83XX_GPIO_GRC19, BIT(0)}, |
| [3] = {&IT83XX_GPIO_GRC20, BIT(7)}, |
| [4] = {&IT83XX_GPIO_GRC20, BIT(6)} }, |
| [GPIO_E] = { [0] = {&IT83XX_GPIO_GRC20, BIT(5)}, |
| [6] = {&IT83XX_GPIO_GRC20, BIT(4)}, |
| [7] = {&IT83XX_GPIO_GRC20, BIT(3)} }, |
| [GPIO_F] = { [2] = {&IT83XX_GPIO_GRC20, BIT(2)}, |
| [3] = {&IT83XX_GPIO_GRC20, BIT(1)}, |
| [4] = {&IT83XX_GPIO_GRC20, BIT(0)}, |
| [5] = {&IT83XX_GPIO_GRC21, BIT(7)}, |
| [6] = {&IT83XX_GPIO_GRC21, BIT(6)}, |
| [7] = {&IT83XX_GPIO_GRC21, BIT(5)} }, |
| [GPIO_H] = { [0] = {&IT83XX_GPIO_GRC21, BIT(2)}, |
| [1] = {&IT83XX_GPIO_GRC21, BIT(1)}, |
| [2] = {&IT83XX_GPIO_GRC21, BIT(0)} }, |
| [GPIO_I] = { [1] = {&IT83XX_GPIO_GRC23, BIT(4)}, |
| [2] = {&IT83XX_GPIO_GRC23, BIT(5)}, |
| [3] = {&IT83XX_GPIO_GRC23, BIT(6)}, |
| [4] = {&IT83XX_GPIO_GRC23, BIT(7)} }, |
| [GPIO_J] = { [0] = {&IT83XX_GPIO_GRC23, BIT(0)}, |
| [1] = {&IT83XX_GPIO_GRC23, BIT(1)}, |
| [2] = {&IT83XX_GPIO_GRC23, BIT(2)}, |
| [3] = {&IT83XX_GPIO_GRC23, BIT(3)} }, |
| #endif |
| }; |
| |
| static void gpio_1p8v_3p3v_sel_by_pin(uint8_t port, uint8_t pin, int sel_1p8v) |
| { |
| volatile uint8_t *reg_1p8v = gpio_1p8v_sel[port][pin].reg; |
| uint8_t sel = gpio_1p8v_sel[port][pin].sel; |
| |
| if (reg_1p8v == NULL) |
| return; |
| |
| if (sel_1p8v) |
| *reg_1p8v |= sel; |
| else |
| *reg_1p8v &= ~sel; |
| } |
| |
| static inline void it83xx_set_alt_func(uint32_t port, uint32_t pin, |
| enum gpio_alternate_func func) |
| { |
| /* |
| * If func is not ALT_FUNC_NONE, set for alternate function. |
| * Otherwise, turn the pin into an input as it's default. |
| */ |
| if (func != GPIO_ALT_FUNC_NONE) |
| IT83XX_GPIO_CTRL(port, pin) &= ~(GPCR_PORT_PIN_MODE_OUTPUT | |
| GPCR_PORT_PIN_MODE_INPUT); |
| else |
| IT83XX_GPIO_CTRL(port, pin) = |
| (IT83XX_GPIO_CTRL(port, pin) | GPCR_PORT_PIN_MODE_INPUT) |
| & ~GPCR_PORT_PIN_MODE_OUTPUT; |
| } |
| |
| void gpio_set_alternate_function(uint32_t port, uint32_t mask, |
| enum gpio_alternate_func func) |
| { |
| uint32_t pin = 0; |
| |
| /* Alternate function configuration for KSI/KSO pins */ |
| if (port > GPIO_PORT_COUNT) { |
| port -= GPIO_KSI; |
| /* |
| * If func is non-negative, set for keyboard scan function. |
| * Otherwise, turn the pin into a GPIO input. |
| */ |
| if (func >= GPIO_ALT_FUNC_DEFAULT) { |
| /* KBS mode */ |
| *kbs_gpio_ctrl_regs[port].gpio_mode &= ~mask; |
| } else { |
| /* input */ |
| *kbs_gpio_ctrl_regs[port].gpio_out &= ~mask; |
| /* GPIO mode */ |
| *kbs_gpio_ctrl_regs[port].gpio_mode |= mask; |
| } |
| return; |
| } |
| |
| /* For each bit high in the mask, set that pin to use alt. func. */ |
| while (mask > 0) { |
| if (mask & 1) |
| it83xx_set_alt_func(port, pin, func); |
| pin++; |
| mask >>= 1; |
| } |
| } |
| |
| test_mockable int gpio_get_level(enum gpio_signal signal) |
| { |
| return (IT83XX_GPIO_DATA_MIRROR(gpio_list[signal].port) & |
| gpio_list[signal].mask) ? 1 : 0; |
| } |
| |
| void gpio_set_level(enum gpio_signal signal, int value) |
| { |
| /* critical section with interrupts off */ |
| uint32_t int_mask = read_clear_int_mask(); |
| |
| if (value) |
| IT83XX_GPIO_DATA(gpio_list[signal].port) |= |
| gpio_list[signal].mask; |
| else |
| IT83XX_GPIO_DATA(gpio_list[signal].port) &= |
| ~gpio_list[signal].mask; |
| /* restore interrupts */ |
| set_int_mask(int_mask); |
| } |
| |
| void gpio_kbs_pin_gpio_mode(uint32_t port, uint32_t mask, uint32_t flags) |
| { |
| uint32_t idx = port - GPIO_KSI; |
| |
| /* Set GPIO mode */ |
| *kbs_gpio_ctrl_regs[idx].gpio_mode |= mask; |
| |
| /* Set input or output */ |
| if (flags & GPIO_OUTPUT) { |
| /* |
| * Select open drain first, so that we don't glitch the signal |
| * when changing the line to an output. |
| */ |
| if (flags & GPIO_OPEN_DRAIN) |
| /* |
| * it83xx: need external pullup for output data high |
| * it8xxx2: this pin is always internal pullup |
| */ |
| IT83XX_GPIO_GPOT(port) |= mask; |
| else |
| /* |
| * it8xxx2: this pin is not internal pullup |
| */ |
| IT83XX_GPIO_GPOT(port) &= ~mask; |
| |
| /* Set level before change to output. */ |
| if (flags & GPIO_HIGH) |
| IT83XX_GPIO_DATA(port) |= mask; |
| else if (flags & GPIO_LOW) |
| IT83XX_GPIO_DATA(port) &= ~mask; |
| *kbs_gpio_ctrl_regs[idx].gpio_out |= mask; |
| } else { |
| *kbs_gpio_ctrl_regs[idx].gpio_out &= ~mask; |
| if (flags & GPIO_PULL_UP) |
| IT83XX_GPIO_GPOT(port) |= mask; |
| else |
| /* No internal pullup and pulldown */ |
| IT83XX_GPIO_GPOT(port) &= ~mask; |
| } |
| } |
| |
| #ifndef IT83XX_GPIO_INT_FLEXIBLE |
| /* Returns true when the falling trigger bit actually mean both trigger. */ |
| static int group_falling_is_both(const int group) |
| { |
| return group == 7 || group == 10 || group == 12; |
| } |
| |
| static const char *get_gpio_string(const int port, const int mask) |
| { |
| static char buffer[3]; |
| int i; |
| |
| buffer[0] = port - GPIO_A + 'A'; |
| buffer[1] = '!'; |
| |
| for (i = 0; i < 8; ++i) { |
| if (mask & BIT(i)) { |
| buffer[1] = i + '0'; |
| break; |
| } |
| } |
| return buffer; |
| } |
| #endif /* IT83XX_GPIO_INT_FLEXIBLE */ |
| |
| void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags) |
| { |
| uint32_t pin = 0; |
| uint32_t mask_copy = mask; |
| |
| /* Set GPIO mode for KSI/KSO pins */ |
| if (port > GPIO_PORT_COUNT) { |
| gpio_kbs_pin_gpio_mode(port, mask, flags); |
| return; |
| } |
| |
| /* |
| * Select open drain first, so that we don't glitch the signal |
| * when changing the line to an output. |
| */ |
| if (flags & GPIO_OPEN_DRAIN) |
| IT83XX_GPIO_GPOT(port) |= mask; |
| else |
| IT83XX_GPIO_GPOT(port) &= ~mask; |
| |
| /* If output, set level before changing type to an output. */ |
| if (flags & GPIO_OUTPUT) { |
| if (flags & GPIO_HIGH) |
| IT83XX_GPIO_DATA(port) |= mask; |
| else if (flags & GPIO_LOW) |
| IT83XX_GPIO_DATA(port) &= ~mask; |
| } |
| |
| /* For each bit high in the mask, set input/output and pullup/down. */ |
| while (mask_copy > 0) { |
| if (mask_copy & 1) { |
| /* Set input or output. */ |
| if (flags & GPIO_OUTPUT) |
| IT83XX_GPIO_CTRL(port, pin) = |
| (IT83XX_GPIO_CTRL(port, pin) | |
| GPCR_PORT_PIN_MODE_OUTPUT) & |
| ~GPCR_PORT_PIN_MODE_INPUT; |
| else |
| IT83XX_GPIO_CTRL(port, pin) = |
| (IT83XX_GPIO_CTRL(port, pin) | |
| GPCR_PORT_PIN_MODE_INPUT) & |
| ~GPCR_PORT_PIN_MODE_OUTPUT; |
| |
| /* Handle pullup / pulldown */ |
| if (flags & GPIO_PULL_UP) { |
| IT83XX_GPIO_CTRL(port, pin) = |
| (IT83XX_GPIO_CTRL(port, pin) | |
| GPCR_PORT_PIN_MODE_PULLUP) & |
| ~GPCR_PORT_PIN_MODE_PULLDOWN; |
| } else if (flags & GPIO_PULL_DOWN) { |
| IT83XX_GPIO_CTRL(port, pin) = |
| (IT83XX_GPIO_CTRL(port, pin) | |
| GPCR_PORT_PIN_MODE_PULLDOWN) & |
| ~GPCR_PORT_PIN_MODE_PULLUP; |
| } else { |
| /* No pull up/down */ |
| IT83XX_GPIO_CTRL(port, pin) &= |
| ~(GPCR_PORT_PIN_MODE_PULLUP | |
| GPCR_PORT_PIN_MODE_PULLDOWN); |
| } |
| |
| /* To select 1.8v or 3.3v support. */ |
| gpio_1p8v_3p3v_sel_by_pin(port, pin, |
| (flags & GPIO_SEL_1P8V)); |
| } |
| |
| pin++; |
| mask_copy >>= 1; |
| } |
| |
| if (flags & (GPIO_INT_F_RISING | GPIO_INT_F_FALLING)) { |
| int irq, wuc_group, wuc_mask; |
| irq = gpio_to_irq(port, mask); |
| wuc_group = gpio_irqs[irq].wuc_group; |
| wuc_mask = gpio_irqs[irq].wuc_mask; |
| |
| /* |
| * Set both edges interrupt. |
| * The WUBEMR register is valid on IT8320 DX version. |
| * And the setting (falling or rising edge) of WUEMR register is |
| * invalid if this mode is set. |
| */ |
| #ifdef IT83XX_GPIO_INT_FLEXIBLE |
| if ((flags & GPIO_INT_BOTH) == GPIO_INT_BOTH) |
| *(wubemr(wuc_group)) |= wuc_mask; |
| else |
| *(wubemr(wuc_group)) &= ~wuc_mask; |
| #endif |
| |
| if (flags & GPIO_INT_F_FALLING) { |
| #ifndef IT83XX_GPIO_INT_FLEXIBLE |
| if (!!(flags & GPIO_INT_F_RISING) != |
| group_falling_is_both(wuc_group)) { |
| ccprintf("!!Fix GPIO %s interrupt config!!\n", |
| get_gpio_string(port, mask)); |
| } |
| #endif |
| *(wuemr(wuc_group)) |= wuc_mask; |
| } else { |
| *(wuemr(wuc_group)) &= ~wuc_mask; |
| } |
| /* |
| * Always write 1 to clear the WUC status register after |
| * modifying edge mode selection register (WUBEMR and WUEMR). |
| */ |
| *(wuesr(wuc_group)) = wuc_mask; |
| } |
| } |
| |
| int gpio_enable_interrupt(enum gpio_signal signal) |
| { |
| int irq = gpio_to_irq(gpio_list[signal].port, gpio_list[signal].mask); |
| |
| if (irq == -1) |
| return EC_ERROR_UNKNOWN; |
| else |
| task_enable_irq(irq); |
| |
| return EC_SUCCESS; |
| } |
| |
| int gpio_disable_interrupt(enum gpio_signal signal) |
| { |
| int irq = gpio_to_irq(gpio_list[signal].port, gpio_list[signal].mask); |
| |
| if (irq == -1) |
| return EC_ERROR_UNKNOWN; |
| else |
| task_disable_irq(irq); |
| |
| return EC_SUCCESS; |
| } |
| |
| int gpio_clear_pending_interrupt(enum gpio_signal signal) |
| { |
| int irq = gpio_to_irq(gpio_list[signal].port, gpio_list[signal].mask); |
| |
| if (irq == -1) |
| return EC_ERROR_UNKNOWN; |
| |
| *(wuesr(gpio_irqs[irq].wuc_group)) = gpio_irqs[irq].wuc_mask; |
| task_clear_pending_irq(irq); |
| return EC_SUCCESS; |
| } |
| |
| /* To prevent cc pins leakage, disables integrated cc module. */ |
| void it83xx_disable_cc_module(int port) |
| { |
| /* Power down all CC, and disable CC voltage detector */ |
| IT83XX_USBPD_CCGCR(port) |= USBPD_REG_MASK_DISABLE_CC; |
| #if defined(CONFIG_USB_PD_TCPM_DRIVER_IT83XX) |
| IT83XX_USBPD_CCCSR(port) |= USBPD_REG_MASK_DISABLE_CC_VOL_DETECTOR; |
| #elif defined(CONFIG_USB_PD_TCPM_DRIVER_IT8XXX2) |
| IT83XX_USBPD_CCGCR(port) |= USBPD_REG_MASK_DISABLE_CC_VOL_DETECTOR; |
| #endif |
| /* |
| * Disconnect CC analog module (ex.UP/RD/DET/TX/RX), and |
| * disconnect CC 5.1K to GND |
| */ |
| IT83XX_USBPD_CCCSR(port) |= (USBPD_REG_MASK_CC2_DISCONNECT | |
| USBPD_REG_MASK_CC2_DISCONNECT_5_1K_TO_GND | |
| USBPD_REG_MASK_CC1_DISCONNECT | |
| USBPD_REG_MASK_CC1_DISCONNECT_5_1K_TO_GND); |
| /* Disconnect CC 5V tolerant */ |
| IT83XX_USBPD_CCPSR(port) |= (USBPD_REG_MASK_DISCONNECT_POWER_CC2 | |
| USBPD_REG_MASK_DISCONNECT_POWER_CC1); |
| } |
| |
| void gpio_pre_init(void) |
| { |
| const struct gpio_info *g = gpio_list; |
| int is_warm = system_is_reboot_warm(); |
| int flags; |
| int i; |
| |
| IT83XX_GPIO_GCR = 0x06; |
| |
| #if !defined(CONFIG_IT83XX_VCC_1P8V) && !defined(CONFIG_IT83XX_VCC_3P3V) |
| #error Please select voltage level of VCC for EC. |
| #endif |
| |
| #if defined(CONFIG_IT83XX_VCC_1P8V) && defined(CONFIG_IT83XX_VCC_3P3V) |
| #error Must select only one voltage level of VCC for EC. |
| #endif |
| /* The power level of GPM6 follows VCC */ |
| IT83XX_GPIO_GCR29 |= BIT(0); |
| |
| /* The power level (VCC) of GPM0~6 is 1.8V */ |
| if (IS_ENABLED(CONFIG_IT83XX_VCC_1P8V)) |
| IT83XX_GPIO_GCR30 |= BIT(4); |
| |
| /* The power level (VCC) of GPM0~6 is 3.3V */ |
| if (IS_ENABLED(CONFIG_IT83XX_VCC_3P3V)) |
| IT83XX_GPIO_GCR30 &= ~BIT(4); |
| |
| #if IT83XX_USBPD_PHY_PORT_COUNT < CONFIG_USB_PD_ITE_ACTIVE_PORT_COUNT |
| #error "ITE pd active port count should be less than physical port count !" |
| #endif |
| /* |
| * To prevent cc pins leakage and cc pins can be used as gpio, |
| * disable board not active ITE TCPC port cc modules. |
| */ |
| for (i = CONFIG_USB_PD_ITE_ACTIVE_PORT_COUNT; |
| i < IT83XX_USBPD_PHY_PORT_COUNT; i++) { |
| it83xx_disable_cc_module(i); |
| /* Dis-connect 5.1K dead battery resistor to CC */ |
| IT83XX_USBPD_CCPSR(i) |= |
| (USBPD_REG_MASK_DISCONNECT_5_1K_CC2_DB | |
| USBPD_REG_MASK_DISCONNECT_5_1K_CC1_DB); |
| } |
| |
| #ifndef CONFIG_USB |
| /* |
| * We need to enable USB's clock so we can config USB control register. |
| * This is important for a software reset as the hardware clock may |
| * already be disabled from the previous run. |
| * We will disable clock to USB module in clock_module_disable() later. |
| */ |
| clock_enable_peripheral(CGC_OFFSET_USB, 0, 0); |
| /* |
| * Disable default pull-down of USB controller (GPH5 and GPH6) if we |
| * don't use this module. |
| */ |
| IT83XX_USB_P0MCR &= ~USB_DP_DM_PULL_DOWN_EN; |
| #endif |
| |
| #if defined(CHIP_FAMILY_IT8XXX1) || defined(CHIP_FAMILY_IT8XXX2) |
| /* Q group pins are default GPI mode, clear alternate setting. */ |
| IT83XX_VBATPC_XLPIER = 0x0; |
| /* |
| * R group pins are default alternate output low, we clear alternate |
| * setting (sink power switch from VBAT to VSTBY) to become GPO output |
| * low. |
| * NOTE: GPR0~5 pins are output low by default. It should consider |
| * that if output low signal effect external circuit or not, |
| * until we reconfig these pins in gpio.inc. |
| */ |
| IT83XX_VBATPC_BGPOPSCR = 0x0; |
| #endif |
| |
| /* |
| * On IT81202 (128-pins package), the pins of GPIO group K and L aren't |
| * bonding with pad. So we configure these pins as internal pull-down |
| * at default to prevent leakage current due to floating. |
| */ |
| if (IS_ENABLED(IT83XX_GPIO_GROUP_K_L_DEFAULT_PULL_DOWN)) { |
| for (i = 0; i < 8; i++) { |
| IT83XX_GPIO_CTRL(GPIO_K, i) = (GPCR_PORT_PIN_MODE_INPUT |
| | GPCR_PORT_PIN_MODE_PULLDOWN); |
| IT83XX_GPIO_CTRL(GPIO_L, i) = (GPCR_PORT_PIN_MODE_INPUT |
| | GPCR_PORT_PIN_MODE_PULLDOWN); |
| } |
| } |
| |
| /* |
| * On IT81202/IT81302, the GPIOH7 isn't bonding with pad and is left |
| * floating internally. We need to enable internal pull-down for the pin |
| * to prevent leakage current, but IT81202/IT81302 doesn't have the |
| * capability to pull it down. We can only set it as output low, |
| * so we enable output low for it at initialization to prevent leakage. |
| */ |
| if (IS_ENABLED(IT83XX_GPIO_H7_DEFAULT_OUTPUT_LOW)) { |
| IT83XX_GPIO_CTRL(GPIO_H, 7) = GPCR_PORT_PIN_MODE_OUTPUT; |
| IT83XX_GPIO_DATA(GPIO_H) &= ~BIT(7); |
| } |
| |
| for (i = 0; i < GPIO_COUNT; i++, g++) { |
| flags = g->flags; |
| |
| if (flags & GPIO_DEFAULT) |
| continue; |
| |
| /* |
| * If this is a warm reboot, don't set the output levels or |
| * we'll shut off the AP. |
| */ |
| if (is_warm) |
| flags &= ~(GPIO_LOW | GPIO_HIGH); |
| |
| /* Set up GPIO based on flags */ |
| gpio_set_flags_by_mask(g->port, g->mask, flags); |
| } |
| } |
| |
| /** |
| * Handle a GPIO interrupt by calling the pins corresponding handler if |
| * one exists. |
| * |
| * @param port GPIO port (GPIO_*) |
| * @param mask GPIO mask |
| */ |
| static void gpio_interrupt(int port, uint8_t mask) |
| { |
| int i = 0; |
| const struct gpio_info *g = gpio_list; |
| |
| for (i = 0; i < GPIO_IH_COUNT; i++, g++) { |
| if (port == g->port && (mask & g->mask)) { |
| gpio_irq_handlers[i](i); |
| return; |
| } |
| } |
| } |
| |
| /** |
| * Define one IRQ function to handle all GPIO interrupts. The IRQ determines |
| * the interrupt number which was triggered, calls the master handler above, |
| * and clears status registers. |
| */ |
| static void __gpio_irq(void) |
| { |
| /* Determine interrupt number. */ |
| int irq = intc_get_ec_int(); |
| |
| #ifdef HAS_TASK_KEYSCAN |
| if (irq == IT83XX_IRQ_WKINTC) { |
| keyboard_raw_interrupt(); |
| return; |
| } |
| #endif |
| |
| #ifdef CONFIG_HOSTCMD_X86 |
| if (irq == IT83XX_IRQ_WKINTAD) |
| return; |
| #endif |
| |
| /* |
| * Clear the WUC status register. Note the external pin first goes |
| * to the WUC module and is always edge triggered. |
| */ |
| *(wuesr(gpio_irqs[irq].wuc_group)) = gpio_irqs[irq].wuc_mask; |
| |
| /* |
| * Clear the interrupt controller status register. Note the interrupt |
| * controller is level triggered from the WUC status. |
| */ |
| task_clear_pending_irq(irq); |
| |
| /* Run the GPIO master handler above with corresponding port/mask. */ |
| gpio_interrupt(gpio_irqs[irq].gpio_port, gpio_irqs[irq].gpio_mask); |
| } |
| |
| /* Route all WKO interrupts coming from INT#2 into __gpio_irq. */ |
| DECLARE_IRQ(CPU_INT_2_ALL_GPIOS, __gpio_irq, 1); |