| /* Copyright (c) 2014 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 "keyboard_config.h" |
| #include "hooks.h" |
| #include "registers.h" |
| #include "switch.h" |
| #include "task.h" |
| #include "timer.h" |
| #include "util.h" |
| #include "system.h" |
| #include "system_chip.h" |
| |
| /* Marco functions for GPIO WUI/ALT table */ |
| #define NPCX_GPIO(grp, pin) \ |
| GPIO_PORT_##grp, MASK_PIN##pin |
| #define NPCX_GPIO_NONE \ |
| GPIO_PORT_COUNT, 0xFF |
| #define NPCX_WUI(tbl, grp, pin) \ |
| MIWU_TABLE_##tbl, MIWU_GROUP_##grp, MASK_PIN##pin |
| |
| #define ALT_MASK(pin) \ |
| CONCAT2(MASK_PIN, pin) |
| #define ALT_PIN(grp, pin) \ |
| ALT_MASK(NPCX_DEVALT##grp##_##pin) |
| #define NPCX_ALT(grp, pin) \ |
| ALT_GROUP_##grp, ALT_PIN(grp, pin) |
| |
| /* Flags for PWM IO type */ |
| #define PWM_IO_FUNC (1 << 1) /* PWM optional func bit */ |
| #define PWM_IO_OD (1 << 2) /* PWM IO open-drain bit */ |
| |
| struct gpio_wui_map { |
| uint8_t gpio_port; |
| uint8_t gpio_mask; |
| uint8_t wui_table; |
| uint8_t wui_group; |
| uint8_t wui_mask; |
| }; |
| |
| struct gpio_wui_item { |
| struct gpio_wui_map wui_map[8]; |
| uint8_t irq; |
| }; |
| |
| const struct gpio_wui_item gpio_wui_table[] = { |
| /* MIWU0 Group A */ |
| { { { NPCX_GPIO(8, 0), NPCX_WUI(0, 1, 0) }, |
| { NPCX_GPIO(8, 1), NPCX_WUI(0, 1, 1) }, |
| { NPCX_GPIO(8, 2), NPCX_WUI(0, 1, 2) }, |
| { NPCX_GPIO(8, 3), NPCX_WUI(0, 1, 3) }, |
| { NPCX_GPIO(8, 4), NPCX_WUI(0, 1, 4) }, |
| { NPCX_GPIO(8, 5), NPCX_WUI(0, 1, 5) }, |
| { NPCX_GPIO(8, 6), NPCX_WUI(0, 1, 6) }, |
| { NPCX_GPIO(8, 7), NPCX_WUI(0, 1, 7) }, |
| }, NPCX_IRQ_MTC_WKINTAD_0 }, |
| /* MIWU0 Group B */ |
| { { { NPCX_GPIO(9, 0), NPCX_WUI(0, 2, 0) }, |
| { NPCX_GPIO(9, 1), NPCX_WUI(0, 2, 1) }, |
| { NPCX_GPIO(9, 2), NPCX_WUI(0, 2, 2) }, |
| { NPCX_GPIO(9, 3), NPCX_WUI(0, 2, 3) }, |
| { NPCX_GPIO(9, 4), NPCX_WUI(0, 2, 4) }, |
| { NPCX_GPIO(9, 5), NPCX_WUI(0, 2, 5) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 2, 6) }, /* MSWC Wake-Up */ |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 2, 7) }, /* T0OUT Wake-Up */ |
| }, NPCX_IRQ_TWD_WKINTB_0 }, |
| /* MIWU0 Group C */ |
| { { { NPCX_GPIO(9, 6), NPCX_WUI(0, 3, 0) }, |
| { NPCX_GPIO(9, 7), NPCX_WUI(0, 3, 1) }, |
| { NPCX_GPIO(A, 0), NPCX_WUI(0, 3, 2) }, |
| { NPCX_GPIO(A, 1), NPCX_WUI(0, 3, 3) }, |
| { NPCX_GPIO(A, 2), NPCX_WUI(0, 3, 4) }, |
| { NPCX_GPIO(A, 3), NPCX_WUI(0, 3, 5) }, |
| { NPCX_GPIO(A, 4), NPCX_WUI(0, 3, 6) }, |
| { NPCX_GPIO(A, 5), NPCX_WUI(0, 3, 7) }, |
| }, NPCX_IRQ_WKINTC_0 }, |
| /* MIWU0 Group D */ |
| { { { NPCX_GPIO(A, 6), NPCX_WUI(0, 4, 0) }, |
| { NPCX_GPIO(A, 7), NPCX_WUI(0, 4, 1) }, |
| { NPCX_GPIO(B, 0), NPCX_WUI(0, 4, 2) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 4, 3) }, /* SMB0 Wake-Up */ |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 4, 4) }, /* SMB1 Wake-Up */ |
| { NPCX_GPIO(B, 1), NPCX_WUI(0, 4, 5) }, |
| { NPCX_GPIO(B, 2), NPCX_WUI(0, 4, 6) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 4, 7) }, /* MTC Wake-Up */ |
| }, NPCX_IRQ_MTC_WKINTAD_0 }, |
| |
| /* MIWU0 Group E */ |
| { { { NPCX_GPIO(B, 3), NPCX_WUI(0, 5 , 0) }, |
| { NPCX_GPIO(B, 4), NPCX_WUI(0, 5 , 1) }, |
| { NPCX_GPIO(B, 5), NPCX_WUI(0, 5 , 2) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 5 , 3) }, |
| { NPCX_GPIO(B, 7), NPCX_WUI(0, 5 , 4) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 5 , 5) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 5 , 6) }, /* Host Wake-Up */ |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 5 , 7) }, /* LRESET Wake-Up */ |
| }, NPCX_IRQ_WKINTEFGH_0 }, |
| |
| /* MIWU0 Group F */ |
| { { { NPCX_GPIO(C, 0), NPCX_WUI(0, 6, 0) }, |
| { NPCX_GPIO(C, 1), NPCX_WUI(0, 6, 1) }, |
| { NPCX_GPIO(C, 2), NPCX_WUI(0, 6, 2) }, |
| { NPCX_GPIO(C, 3), NPCX_WUI(0, 6, 3) }, |
| { NPCX_GPIO(C, 4), NPCX_WUI(0, 6, 4) }, |
| { NPCX_GPIO(C, 5), NPCX_WUI(0, 6, 5) }, |
| { NPCX_GPIO(C, 6), NPCX_WUI(0, 6, 6) }, |
| { NPCX_GPIO(C, 7), NPCX_WUI(0, 6, 7) }, |
| }, NPCX_IRQ_WKINTEFGH_0 }, |
| /* MIWU0 Group G */ |
| { { { NPCX_GPIO(D, 0), NPCX_WUI(0, 7, 0) }, |
| { NPCX_GPIO(D, 1), NPCX_WUI(0, 7, 1) }, |
| { NPCX_GPIO(D, 2), NPCX_WUI(0, 7, 2) }, |
| { NPCX_GPIO(D, 3), NPCX_WUI(0, 7, 3) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 7, 4) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 7, 5) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 7, 6) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 7, 7) }, |
| }, NPCX_IRQ_WKINTEFGH_0 }, |
| /* MIWU0 Group H */ |
| { { { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 0) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 1) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 2) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 3) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 4) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 5) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(0, 8, 6) }, |
| { NPCX_GPIO(E, 7), NPCX_WUI(0, 8, 7) }, |
| }, NPCX_IRQ_WKINTEFGH_0 }, |
| |
| /* MIWU1 Group A */ |
| { { { NPCX_GPIO(0, 0), NPCX_WUI(1, 1, 0) }, |
| { NPCX_GPIO(0, 1), NPCX_WUI(1, 1, 1) }, |
| { NPCX_GPIO(0, 2), NPCX_WUI(1, 1, 2) }, |
| { NPCX_GPIO(0, 3), NPCX_WUI(1, 1, 3) }, |
| { NPCX_GPIO(0, 4), NPCX_WUI(1, 1, 4) }, |
| { NPCX_GPIO(0, 5), NPCX_WUI(1, 1, 5) }, |
| { NPCX_GPIO(0, 6), NPCX_WUI(1, 1, 6) }, |
| { NPCX_GPIO(0, 7), NPCX_WUI(1, 1, 7) }, |
| }, NPCX_IRQ_WKINTA_1 }, |
| /* MIWU1 Group B */ |
| { { { NPCX_GPIO(1, 0), NPCX_WUI(1, 2, 0) }, |
| { NPCX_GPIO(1, 1), NPCX_WUI(1, 2, 1) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(1, 2, 2) }, |
| { NPCX_GPIO(1, 3), NPCX_WUI(1, 2, 3) }, |
| { NPCX_GPIO(1, 4), NPCX_WUI(1, 2, 4) }, |
| { NPCX_GPIO(1, 5), NPCX_WUI(1, 2, 5) }, |
| { NPCX_GPIO(1, 6), NPCX_WUI(1, 2, 6) }, |
| { NPCX_GPIO(1, 7), NPCX_WUI(1, 2, 7) }, |
| }, NPCX_IRQ_WKINTB_1 }, |
| /* MIWU1 Group C -- Skipping */ |
| /* MIWU1 Group D */ |
| { { { NPCX_GPIO(2, 0), NPCX_WUI(1, 4, 0) }, |
| { NPCX_GPIO(2, 1), NPCX_WUI(1, 4, 1) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(1, 4, 2) }, |
| { NPCX_GPIO(3, 3), NPCX_WUI(1, 4, 3) }, |
| { NPCX_GPIO(3, 4), NPCX_WUI(1, 4, 4) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(1, 4, 5) }, |
| { NPCX_GPIO(3, 6), NPCX_WUI(1, 4, 6) }, |
| { NPCX_GPIO(3, 7), NPCX_WUI(1, 4, 7) }, |
| }, NPCX_IRQ_WKINTD_1 }, |
| |
| /* MIWU1 Group E */ |
| { { { NPCX_GPIO(4, 0), NPCX_WUI(1, 5, 0) }, |
| { NPCX_GPIO(4, 1), NPCX_WUI(1, 5, 1) }, |
| { NPCX_GPIO(4, 2), NPCX_WUI(1, 5, 2) }, |
| { NPCX_GPIO(4, 3), NPCX_WUI(1, 5, 3) }, |
| { NPCX_GPIO(4, 4), NPCX_WUI(1, 5, 4) }, |
| { NPCX_GPIO(4, 5), NPCX_WUI(1, 5, 5) }, |
| { NPCX_GPIO(4, 6), NPCX_WUI(1, 5, 6) }, |
| { NPCX_GPIO(4, 7), NPCX_WUI(1, 5, 7) }, |
| }, NPCX_IRQ_WKINTE_1 }, |
| |
| /* MIWU1 Group F */ |
| { { { NPCX_GPIO(5, 0), NPCX_WUI(1, 6, 0) }, |
| { NPCX_GPIO(5, 1), NPCX_WUI(1, 6, 1) }, |
| { NPCX_GPIO(5, 2), NPCX_WUI(1, 6, 2) }, |
| { NPCX_GPIO(5, 3), NPCX_WUI(1, 6, 3) }, |
| { NPCX_GPIO(5, 4), NPCX_WUI(1, 6, 4) }, |
| { NPCX_GPIO(5, 5), NPCX_WUI(1, 6, 5) }, |
| { NPCX_GPIO(5, 6), NPCX_WUI(1, 6, 6) }, |
| { NPCX_GPIO(5, 7), NPCX_WUI(1, 6, 7) }, |
| }, NPCX_IRQ_WKINTF_1 }, |
| /* MIWU1 Group G */ |
| { { { NPCX_GPIO(6, 0), NPCX_WUI(1, 7, 0) }, |
| { NPCX_GPIO(6, 1), NPCX_WUI(1, 7, 1) }, |
| { NPCX_GPIO(6, 2), NPCX_WUI(1, 7, 2) }, |
| { NPCX_GPIO(6, 3), NPCX_WUI(1, 7, 3) }, |
| { NPCX_GPIO(6, 4), NPCX_WUI(1, 7, 4) }, |
| { NPCX_GPIO(6, 5), NPCX_WUI(1, 7, 5) }, |
| { NPCX_GPIO(6, 6), NPCX_WUI(1, 7, 6) }, |
| { NPCX_GPIO(7, 1), NPCX_WUI(1, 7, 7) }, |
| }, NPCX_IRQ_WKINTG_1 }, |
| /* MIWU1 Group H */ |
| { { { NPCX_GPIO(7, 0), NPCX_WUI(1, 8, 0) }, |
| { NPCX_GPIO(6, 7), NPCX_WUI(1, 8, 1) }, |
| { NPCX_GPIO(7, 2), NPCX_WUI(1, 8, 2) }, |
| { NPCX_GPIO(7, 3), NPCX_WUI(1, 8, 3) }, |
| { NPCX_GPIO(7, 4), NPCX_WUI(1, 8, 4) }, |
| { NPCX_GPIO(7, 5), NPCX_WUI(1, 8, 5) }, |
| { NPCX_GPIO(7, 6), NPCX_WUI(1, 8, 6) }, |
| { NPCX_GPIO_NONE, NPCX_WUI(1, 8, 7) }, |
| }, NPCX_IRQ_WKINTH_1 }, |
| }; |
| |
| struct gpio_alt_map { |
| uint8_t gpio_port; |
| uint8_t gpio_mask; |
| uint8_t alt_group; |
| uint8_t alt_mask; |
| }; |
| |
| const struct gpio_alt_map gpio_alt_table[] = { |
| /* I2C Module */ |
| #if I2C0_BUS0 |
| { NPCX_GPIO(B, 4), NPCX_ALT(2, I2C0_0_SL)}, /* SMB0SDA */ |
| { NPCX_GPIO(B, 5), NPCX_ALT(2, I2C0_0_SL)}, /* SMB0SCL */ |
| #else |
| { NPCX_GPIO(B, 2), NPCX_ALT(2, I2C0_1_SL)}, /* SMB0SDA */ |
| { NPCX_GPIO(B, 3), NPCX_ALT(2, I2C0_1_SL)}, /* SMB0SCL */ |
| #endif |
| { NPCX_GPIO(8, 7), NPCX_ALT(2, I2C1_0_SL)}, /* SMB1SDA */ |
| { NPCX_GPIO(9, 0), NPCX_ALT(2, I2C1_0_SL)}, /* SMB1SCL */ |
| { NPCX_GPIO(9, 1), NPCX_ALT(2, I2C2_0_SL)}, /* SMB2SDA */ |
| { NPCX_GPIO(9, 2), NPCX_ALT(2, I2C2_0_SL)}, /* SMB2SCL */ |
| { NPCX_GPIO(D, 0), NPCX_ALT(2, I2C3_0_SL)}, /* SMB3SDA */ |
| { NPCX_GPIO(D, 1), NPCX_ALT(2, I2C3_0_SL)}, /* SMB3SCL */ |
| /* ADC Module */ |
| { NPCX_GPIO(4, 5), NPCX_ALT(6, ADC0_SL)}, /* ADC0 */ |
| { NPCX_GPIO(4, 4), NPCX_ALT(6, ADC1_SL)}, /* ADC1 */ |
| { NPCX_GPIO(4, 3), NPCX_ALT(6, ADC2_SL)}, /* ADC2 */ |
| { NPCX_GPIO(4, 2), NPCX_ALT(6, ADC3_SL)}, /* ADC3 */ |
| { NPCX_GPIO(4, 1), NPCX_ALT(6, ADC4_SL)}, /* ADC4 */ |
| /* UART Module */ |
| { NPCX_GPIO(1, 0), NPCX_ALT(9, NO_KSO08_SL)}, /* CR_SIN/KSO09/GPIO10*/ |
| { NPCX_GPIO(1, 1), NPCX_ALT(9, NO_KSO09_SL)}, /* CR_SOUT/KSO10/GPIO11*/ |
| /* SPI Module */ |
| { NPCX_GPIO(9, 5), NPCX_ALT(0, SPIP_SL)}, /* SPIP_MISO */ |
| { NPCX_GPIO(A, 5), NPCX_ALT(0, SPIP_SL)}, /* SPIP_CS1 */ |
| { NPCX_GPIO(A, 3), NPCX_ALT(0, SPIP_SL)}, /* SPIP_MOSI */ |
| { NPCX_GPIO(A, 1), NPCX_ALT(0, SPIP_SL)}, /* SPIP_SCLK */ |
| /* PWM Module */ |
| { NPCX_GPIO(C, 3), NPCX_ALT(4, PWM0_SL)}, /* PWM0 */ |
| { NPCX_GPIO(C, 2), NPCX_ALT(4, PWM1_SL)}, /* PWM1 */ |
| { NPCX_GPIO(C, 4), NPCX_ALT(4, PWM2_SL)}, /* PWM2 */ |
| { NPCX_GPIO(8, 0), NPCX_ALT(4, PWM3_SL)}, /* PWM3 */ |
| { NPCX_GPIO(B, 6), NPCX_ALT(4, PWM4_SL)}, /* PWM4 */ |
| { NPCX_GPIO(B, 7), NPCX_ALT(4, PWM5_SL)}, /* PWM5 */ |
| { NPCX_GPIO(C, 0), NPCX_ALT(4, PWM6_SL)}, /* PWM6 */ |
| { NPCX_GPIO(6, 0), NPCX_ALT(4, PWM7_SL)}, /* PWM7 */ |
| /* MFT Module */ |
| #if TACH_SEL1 |
| { NPCX_GPIO(4, 0), NPCX_ALT(3, TA1_TACH1_SL1)},/* TA1_TACH1 */ |
| { NPCX_GPIO(A, 4), NPCX_ALT(3, TB1_TACH2_SL1)},/* TB1_TACH2 */ |
| #else |
| { NPCX_GPIO(9, 3), NPCX_ALT(C, TA1_TACH1_SL2)},/* TA1_TACH1 */ |
| { NPCX_GPIO(D, 3), NPCX_ALT(C, TB1_TACH2_SL2)},/* TB1_TACH2 */ |
| #endif |
| /* JTAG Module */ |
| #if !(JTAG1) |
| { NPCX_GPIO(2, 1), NPCX_ALT(5, NJEN0_EN) }, /* TCLK */ |
| { NPCX_GPIO(1, 7), NPCX_ALT(5, NJEN0_EN) }, /* TDI */ |
| { NPCX_GPIO(1, 6), NPCX_ALT(5, NJEN0_EN) }, /* TDO */ |
| { NPCX_GPIO(2, 0), NPCX_ALT(5, NJEN0_EN) }, /* TMS */ |
| #else |
| { NPCX_GPIO(D, 5), NPCX_ALT(5, NJEN1_EN) }, /* TCLK */ |
| { NPCX_GPIO(E, 2), NPCX_ALT(5, NJEN1_EN) }, /* TDI */ |
| { NPCX_GPIO(D, 4), NPCX_ALT(5, NJEN1_EN) }, /* TDO */ |
| { NPCX_GPIO(E, 5), NPCX_ALT(5, NJEN1_EN) }, /* TMS */ |
| #endif |
| /* 01 for PWRGD_OUT*/ |
| }; |
| |
| /*****************************************************************************/ |
| /* Internal functions */ |
| const struct gpio_wui_map *gpio_find_wui_from_io(uint8_t port, uint8_t mask) |
| { |
| int i, j; |
| for (i = 0; i < ARRAY_SIZE(gpio_wui_table); i++) { |
| const struct gpio_wui_map *map = gpio_wui_table[i].wui_map; |
| for (j = 0; j < 8; j++, map++) { |
| if (map->gpio_port == port && map->gpio_mask == mask) |
| return map; |
| } |
| } |
| return NULL; |
| } |
| |
| int gpio_find_irq_from_io(uint8_t port, uint8_t mask) |
| { |
| int i, j; |
| for (i = 0; i < ARRAY_SIZE(gpio_wui_table); i++) { |
| const struct gpio_wui_map *map = gpio_wui_table[i].wui_map; |
| for (j = 0; j < 8; j++, map++) { |
| if (map->gpio_port == port && map->gpio_mask == mask) |
| return gpio_wui_table[i].irq; |
| } |
| } |
| return -1; |
| } |
| |
| void gpio_pwm_io_type_sel(uint8_t alt_mask, uint8_t func) |
| { |
| uint8_t chan = 0; |
| do { |
| alt_mask = (alt_mask >> 1); |
| if (alt_mask == 0) |
| break; |
| chan++; |
| } while (1); |
| |
| /* Set PWM open drain output is open drain type*/ |
| if (func & PWM_IO_OD) |
| SET_BIT(NPCX_PWMCTLEX(chan), NPCX_PWMCTLEX_OD_OUT); |
| else /* Set PWM open drain output is push-pull type*/ |
| CLEAR_BIT(NPCX_PWMCTLEX(chan), NPCX_PWMCTLEX_OD_OUT); |
| } |
| |
| int gpio_alt_sel(uint8_t port, uint8_t mask, uint8_t func) |
| { |
| int i; |
| const struct gpio_alt_map *map = gpio_alt_table; |
| for (i = 0; i < ARRAY_SIZE(gpio_alt_table); i++, map++) { |
| if (map->gpio_port == port && |
| (map->gpio_mask == mask)) { |
| /* Enable alternative function if func >=0 */ |
| if (func <= 0) /* GPIO functionality */ |
| NPCX_DEVALT(map->alt_group) &= ~(map->alt_mask); |
| else { |
| NPCX_DEVALT(map->alt_group) |= (map->alt_mask); |
| /* PWM optional functionality */ |
| if (func & PWM_IO_FUNC) |
| gpio_pwm_io_type_sel(map->alt_mask, |
| func); |
| } |
| return 1; |
| } |
| } |
| return -1; |
| } |
| |
| void gpio_execute_isr(uint8_t port, uint8_t mask) |
| { |
| int i; |
| const struct gpio_info *g = gpio_list; |
| /* Find GPIOs and execute interrupt service routine */ |
| for (i = 0; i < GPIO_IH_COUNT; i++, g++) { |
| if (port == g->port && mask == g->mask) { |
| gpio_irq_handlers[i](i); |
| return; |
| } |
| } |
| } |
| |
| /* Set interrupt type for GPIO input */ |
| void gpio_interrupt_type_sel(uint8_t port, uint8_t mask, uint32_t flags) |
| { |
| const struct gpio_wui_map *map = gpio_find_wui_from_io(port, mask); |
| uint8_t table, group, pmask; |
| |
| if (map == NULL) |
| return; |
| |
| table = map->wui_table; |
| group = map->wui_group; |
| pmask = map->wui_mask; |
| |
| /* Handle interrupt for level trigger */ |
| if ((flags & GPIO_INT_F_HIGH) || |
| (flags & GPIO_INT_F_LOW)) { |
| /* Set detection mode to level */ |
| NPCX_WKMOD(table, group) |= pmask; |
| /* Handle interrupting on level high */ |
| if (flags & GPIO_INT_F_HIGH) |
| NPCX_WKEDG(table, group) &= ~pmask; |
| /* Handle interrupting on level low */ |
| else if (flags & GPIO_INT_F_LOW) |
| NPCX_WKEDG(table, group) |= pmask; |
| |
| /* Enable wake-up input sources */ |
| NPCX_WKEN(table, group) |= pmask; |
| } |
| /* Handle interrupt for edge trigger */ |
| else if ((flags & GPIO_INT_F_RISING) || |
| (flags & GPIO_INT_F_FALLING)) { |
| /* Set detection mode to edge */ |
| NPCX_WKMOD(table, group) &= ~pmask; |
| /* Handle interrupting on both edges */ |
| if ((flags & GPIO_INT_F_RISING) && |
| (flags & GPIO_INT_F_FALLING)) { |
| /* Enable any edge */ |
| NPCX_WKAEDG(table, group) |= pmask; |
| } |
| /* Handle interrupting on rising edge */ |
| else if (flags & GPIO_INT_F_RISING) { |
| /* Disable any edge */ |
| NPCX_WKAEDG(table, group) &= ~pmask; |
| NPCX_WKEDG(table, group) &= ~pmask; |
| } |
| /* Handle interrupting on falling edge */ |
| else if (flags & GPIO_INT_F_FALLING) { |
| /* Disable any edge */ |
| NPCX_WKAEDG(table, group) &= ~pmask; |
| NPCX_WKEDG(table, group) |= pmask; |
| } |
| /* Enable wake-up input sources */ |
| NPCX_WKEN(table, group) |= pmask; |
| } else{ |
| /* Disable wake-up input sources */ |
| NPCX_WKEN(table, group) &= ~pmask; |
| } |
| |
| /* No support analog mode */ |
| } |
| |
| /*****************************************************************************/ |
| /* IC specific low-level driver */ |
| |
| void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func) |
| { |
| /* Enable alternative pins by func*/ |
| int pin; |
| uint8_t pmask; |
| /* check each bit from mask */ |
| for (pin = 0; pin < 8; pin++) { |
| pmask = (mask & (1 << pin)); |
| if (pmask) |
| gpio_alt_sel(port, pmask, func); |
| } |
| } |
| |
| test_mockable int gpio_get_level(enum gpio_signal signal) |
| { |
| return (NPCX_PDIN(gpio_list[signal].port) & |
| gpio_list[signal].mask) ? 1 : 0; |
| } |
| |
| void gpio_set_level(enum gpio_signal signal, int value) |
| { |
| if (value) |
| NPCX_PDOUT(gpio_list[signal].port) |= |
| gpio_list[signal].mask; |
| else |
| NPCX_PDOUT(gpio_list[signal].port) &= |
| ~gpio_list[signal].mask; |
| } |
| |
| void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags) |
| { |
| /* |
| * Select open drain first, so that we don't glitch the signal |
| * when changing the line to an output. 0:push-pull 1:open-drain |
| */ |
| if (flags & GPIO_OPEN_DRAIN) |
| NPCX_PTYPE(port) |= mask; |
| else |
| NPCX_PTYPE(port) &= ~mask; |
| |
| /* Select direction of GPIO 0:input 1:output */ |
| if (flags & GPIO_OUTPUT) |
| NPCX_PDIR(port) |= mask; |
| else |
| NPCX_PDIR(port) &= ~mask; |
| |
| /* Select pull-up/down of GPIO 0:pull-up 1:pull-down */ |
| if (flags & GPIO_PULL_UP) { |
| NPCX_PPUD(port) &= ~mask; |
| NPCX_PPULL(port) |= mask; /* enable pull down/up */ |
| } else if (flags & GPIO_PULL_DOWN) { |
| NPCX_PPUD(port) |= mask; |
| NPCX_PPULL(port) |= mask; /* enable pull down/up */ |
| } else { |
| /* No pull up/down */ |
| NPCX_PPULL(port) &= ~mask; /* disable pull down/up */ |
| } |
| |
| /* Set up interrupt type */ |
| if (flags & GPIO_INPUT) |
| gpio_interrupt_type_sel(port, mask, flags); |
| |
| /* Set level 0:low 1:high*/ |
| if (flags & GPIO_HIGH) |
| NPCX_PDOUT(port) |= mask; |
| else if (flags & GPIO_LOW) |
| NPCX_PDOUT(port) &= ~mask; |
| |
| } |
| |
| int gpio_enable_interrupt(enum gpio_signal signal) |
| { |
| const struct gpio_info *g = gpio_list + signal; |
| int irq = gpio_find_irq_from_io(g->port, g->mask); |
| |
| /* Fail if no interrupt handler */ |
| if (irq < 0) |
| return EC_ERROR_UNKNOWN; |
| |
| task_enable_irq(irq); |
| return EC_SUCCESS; |
| } |
| |
| int gpio_disable_interrupt(enum gpio_signal signal) |
| { |
| const struct gpio_info *g = gpio_list + signal; |
| int irq = gpio_find_irq_from_io(g->port, g->mask); |
| |
| /* Fail if no interrupt handler */ |
| if (irq < 0) |
| return EC_ERROR_UNKNOWN; |
| |
| task_disable_irq(irq); |
| return EC_SUCCESS; |
| } |
| |
| int gpio_is_reboot_warm(void) |
| { |
| uint32_t reset_flags; |
| /* |
| * Check reset cause here, |
| * gpio_pre_init is executed faster than system_pre_init |
| */ |
| system_check_reset_cause(); |
| reset_flags = system_get_reset_flags(); |
| |
| if ((reset_flags & RESET_FLAG_RESET_PIN) || |
| (reset_flags & RESET_FLAG_POWER_ON) || |
| (reset_flags & RESET_FLAG_WATCHDOG) || |
| (reset_flags & RESET_FLAG_HARD) || |
| (reset_flags & RESET_FLAG_SOFT)) |
| return 0; |
| else |
| return 1; |
| } |
| |
| void gpio_pre_init(void) |
| { |
| const struct gpio_info *g = gpio_list; |
| const struct gpio_wui_map *map; |
| int is_warm = gpio_is_reboot_warm(); |
| int flags; |
| int i, j; |
| |
| uint32_t ksi_mask = (~((1<<KEYBOARD_ROWS)-1)) & KB_ROW_MASK; |
| uint32_t ks0_mask = (~((1<<KEYBOARD_COLS)-1)) & KB_COL_MASK; |
| |
| /* Set necessary pin mux first */ |
| /* Pin_Mux for KSO0-17 & KSI0-7 */ |
| NPCX_DEVALT(ALT_GROUP_7) = (uint8_t)(ksi_mask); |
| NPCX_DEVALT(ALT_GROUP_8) = (uint8_t)(ks0_mask); |
| NPCX_DEVALT(ALT_GROUP_9) = (uint8_t)(ks0_mask >> 8); |
| NPCX_DEVALT(ALT_GROUP_A) |= (uint8_t)(ks0_mask >> 16); |
| |
| /* Pin_Mux for FIU/SPI (set to GPIO) */ |
| SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_GPIO_NO_SPIP); |
| SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_NO_F_SPI); |
| |
| /* Clear all pending bits of GPIOS*/ |
| for (i = 0; i < 2; i++) |
| for (j = 0; j < 8; j++) |
| NPCX_WKPCL(i, j) = 0xFF; |
| |
| /* No support enable clock for the GPIO port in run and sleep. */ |
| /* Set flag for each GPIO pin in gpio_list */ |
| 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); |
| } |
| |
| /* Put power button information in bbram */ |
| g = gpio_list + GPIO_POWER_BUTTON_L; |
| map = gpio_find_wui_from_io(g->port, g->mask); |
| NPCX_BBRAM(BBRM_DATA_INDEX_PBUTTON) = map->wui_table; |
| NPCX_BBRAM(BBRM_DATA_INDEX_PBUTTON + 1) = map->wui_group; |
| NPCX_BBRAM(BBRM_DATA_INDEX_PBUTTON + 2) = map->wui_mask; |
| } |
| |
| /* List of GPIO IRQs to enable. Don't automatically enable interrupts for |
| * the keyboard input GPIO bank - that's handled separately. Of course the |
| * bank is different for different systems. */ |
| static void gpio_init(void) |
| { |
| int i; |
| /* Enable IRQs now that pins are set up */ |
| for (i = 0; i < ARRAY_SIZE(gpio_wui_table); i++) |
| task_enable_irq(gpio_wui_table[i].irq); |
| |
| } |
| DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT); |
| |
| /*****************************************************************************/ |
| /* Interrupt handlers */ |
| |
| /** |
| * Handle a GPIO interrupt. |
| * |
| * @param int_no Interrupt number for GPIO |
| */ |
| |
| static void gpio_interrupt(int int_no) |
| { |
| #if DEBUG_GPIO |
| static uint8_t i, pin, wui_mask; |
| #else |
| uint8_t i, pin, wui_mask; |
| #endif |
| for (i = 0; i < ARRAY_SIZE(gpio_wui_table); i++) { |
| /* If interrupt number is the same */ |
| if (gpio_wui_table[i].irq == int_no) { |
| /* Mapping relationship between WUI and GPIO */ |
| const struct gpio_wui_map *map = |
| gpio_wui_table[i].wui_map; |
| /* Get pending mask */ |
| wui_mask = NPCX_WKPND(map->wui_table , map->wui_group); |
| |
| /* If pending bits is not zero */ |
| if (wui_mask) { |
| /* Clear pending bits of WUI */ |
| NPCX_WKPCL(map->wui_table , map->wui_group) |
| = wui_mask; |
| |
| for (pin = 0; pin < 8; pin++, map++) { |
| /* If pending bit is high, execute ISR*/ |
| if (wui_mask & (1<<pin)) |
| gpio_execute_isr(map->gpio_port, |
| map->gpio_mask); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Handlers for each GPIO port. These read and clear the interrupt bits for |
| * the port, then call the master handler above. |
| */ |
| |
| #define GPIO_IRQ_FUNC(_irq_func, int_no) \ |
| void _irq_func(void) \ |
| { \ |
| gpio_interrupt(int_no); \ |
| } |
| |
| GPIO_IRQ_FUNC(__gpio_wk0ad_interrupt , NPCX_IRQ_MTC_WKINTAD_0); |
| GPIO_IRQ_FUNC(__gpio_wk0b_interrupt , NPCX_IRQ_TWD_WKINTB_0); |
| GPIO_IRQ_FUNC(__gpio_wk0c_interrupt , NPCX_IRQ_WKINTC_0); |
| GPIO_IRQ_FUNC(__gpio_wk0efgh_interrupt, NPCX_IRQ_WKINTEFGH_0); |
| GPIO_IRQ_FUNC(__gpio_wk1a_interrupt , NPCX_IRQ_WKINTA_1); |
| GPIO_IRQ_FUNC(__gpio_wk1b_interrupt , NPCX_IRQ_WKINTB_1); |
| GPIO_IRQ_FUNC(__gpio_wk1d_interrupt , NPCX_IRQ_WKINTD_1); |
| GPIO_IRQ_FUNC(__gpio_wk1e_interrupt , NPCX_IRQ_WKINTE_1); |
| GPIO_IRQ_FUNC(__gpio_wk1f_interrupt , NPCX_IRQ_WKINTF_1); |
| GPIO_IRQ_FUNC(__gpio_wk1g_interrupt , NPCX_IRQ_WKINTG_1); |
| GPIO_IRQ_FUNC(__gpio_wk1h_interrupt , NPCX_IRQ_WKINTH_1); |
| |
| DECLARE_IRQ(NPCX_IRQ_MTC_WKINTAD_0, __gpio_wk0ad_interrupt, 1); |
| DECLARE_IRQ(NPCX_IRQ_TWD_WKINTB_0, __gpio_wk0b_interrupt, 1); |
| DECLARE_IRQ(NPCX_IRQ_WKINTC_0, __gpio_wk0c_interrupt, 1); |
| DECLARE_IRQ(NPCX_IRQ_WKINTEFGH_0, __gpio_wk0efgh_interrupt, 1); |
| DECLARE_IRQ(NPCX_IRQ_WKINTA_1, __gpio_wk1a_interrupt, 1); |
| DECLARE_IRQ(NPCX_IRQ_WKINTB_1, __gpio_wk1b_interrupt, 1); |
| DECLARE_IRQ(NPCX_IRQ_WKINTD_1, __gpio_wk1d_interrupt, 1); |
| DECLARE_IRQ(NPCX_IRQ_WKINTE_1, __gpio_wk1e_interrupt, 1); |
| DECLARE_IRQ(NPCX_IRQ_WKINTF_1, __gpio_wk1f_interrupt, 1); |
| DECLARE_IRQ(NPCX_IRQ_WKINTG_1, __gpio_wk1g_interrupt, 1); |
| DECLARE_IRQ(NPCX_IRQ_WKINTH_1, __gpio_wk1h_interrupt, 1); |
| |
| |
| #undef GPIO_IRQ_FUNC |