| /* Copyright 2018 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 */ |
| |
| #include "gpio.h" |
| #include "hooks.h" |
| #include "registers.h" |
| #include "system.h" |
| #include "task.h" |
| #include "util.h" |
| |
| void gpio_set_alternate_function(uint32_t port, uint32_t mask, |
| enum gpio_alternate_func func) |
| { |
| int bit, mode_reg_index, shift; |
| uint32_t mode_bits, mode_mask; |
| |
| /* Up to 8 alt functions per port */ |
| if (func > GPIO_ALT_FUNC_7) |
| return; |
| |
| if (func == GPIO_ALT_FUNC_NONE) |
| func = GPIO_ALT_FUNC_DEFAULT; |
| |
| while (mask) { |
| /* 32 gpio per port */ |
| bit = get_next_bit(&mask); |
| /* 8 gpio per mode reg */ |
| mode_reg_index = (port << 2) | (bit >> 3); |
| /* |
| * b[3] - write enable(?) |
| * b[2:0] - mode |
| */ |
| shift = (bit & 7) << 2; |
| mode_bits = func << shift; |
| mode_mask = ~(0xf << shift); |
| AP_GPIO_MODE(mode_reg_index) = (AP_GPIO_MODE(mode_reg_index) & |
| mode_mask) | mode_bits; |
| } |
| } |
| |
| test_mockable int gpio_get_level(enum gpio_signal signal) |
| { |
| return !!(AP_GPIO_DIN(gpio_list[signal].port) & |
| gpio_list[signal].mask); |
| } |
| |
| |
| void gpio_set_level(enum gpio_signal signal, int value) |
| { |
| if (value) |
| AP_GPIO_DOUT(gpio_list[signal].port) |= gpio_list[signal].mask; |
| else |
| AP_GPIO_DOUT(gpio_list[signal].port) &= ~gpio_list[signal].mask; |
| } |
| |
| void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags) |
| { |
| /* Set input/output mode */ |
| if (flags & GPIO_OUTPUT) { |
| /* Set level before changing to output mode */ |
| if (flags & GPIO_HIGH) |
| AP_GPIO_DOUT(port) |= mask; |
| if (flags & GPIO_LOW) |
| AP_GPIO_DOUT(port) &= ~mask; |
| AP_GPIO_DIR(port) |= mask; |
| } else { |
| AP_GPIO_DIR(port) &= ~mask; |
| } |
| |
| if (flags & (GPIO_INT_F_RISING | GPIO_INT_F_HIGH)) |
| SCP_EINT_POLARITY_SET[port] = mask; |
| |
| if (flags & (GPIO_INT_F_FALLING | GPIO_INT_F_LOW)) |
| SCP_EINT_POLARITY_CLR[port] = mask; |
| else |
| SCP_EINT_POLARITY_SET[port] = mask; |
| |
| /* Set sensitivity register on edge trigger */ |
| if (flags & (GPIO_INT_F_RISING | GPIO_INT_F_FALLING)) |
| SCP_EINT_SENS_SET[port] = mask; |
| else |
| SCP_EINT_SENS_CLR[port] = mask; |
| } |
| |
| int gpio_get_flags_by_mask(uint32_t port, uint32_t mask) |
| { |
| /* TODO(b/120167145): implement get flags */ |
| return 0; |
| } |
| |
| int gpio_enable_interrupt(enum gpio_signal signal) |
| { |
| const struct gpio_info *g = gpio_list + signal; |
| |
| if (signal >= GPIO_IH_COUNT || !g->mask) |
| return EC_ERROR_INVAL; |
| |
| SCP_EINT_MASK_CLR[g->port] = g->mask; |
| |
| return EC_SUCCESS; |
| } |
| |
| int gpio_disable_interrupt(enum gpio_signal signal) |
| { |
| const struct gpio_info *g = gpio_list + signal; |
| |
| if (signal >= GPIO_IH_COUNT || !g->mask) |
| return EC_ERROR_INVAL; |
| |
| SCP_EINT_MASK_SET[g->port] = g->mask; |
| |
| return EC_SUCCESS; |
| } |
| |
| int gpio_clear_pending_interrupt(enum gpio_signal signal) |
| { |
| const struct gpio_info *g = gpio_list + signal; |
| |
| if (signal >= GPIO_IH_COUNT || !g->mask) |
| return EC_ERROR_INVAL; |
| |
| SCP_EINT_ACK[g->port] = g->mask; |
| |
| return EC_SUCCESS; |
| } |
| |
| void gpio_pre_init(void) |
| { |
| const struct gpio_info *g = gpio_list; |
| int i; |
| int is_warm = system_is_reboot_warm(); |
| |
| for (i = 0; i < GPIO_COUNT; i++, g++) { |
| int flags = g->flags; |
| |
| if (flags & GPIO_DEFAULT) |
| continue; |
| |
| if (is_warm) |
| flags &= ~(GPIO_LOW | GPIO_HIGH); |
| |
| gpio_set_flags_by_mask(g->port, g->mask, flags); |
| } |
| } |
| |
| void gpio_init(void) |
| { |
| /* Enable EINT IRQ */ |
| task_enable_irq(SCP_IRQ_EINT); |
| } |
| DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT); |
| |
| /* Interrupt handler */ |
| void __keep gpio_interrupt(void) |
| { |
| int bit, port; |
| uint32_t pending; |
| enum gpio_signal signal; |
| |
| for (port = 0; port <= MAX_EINT_PORT; port++) { |
| pending = SCP_EINT_STATUS[port]; |
| |
| while (pending) { |
| bit = get_next_bit(&pending); |
| SCP_EINT_ACK[port] = BIT(bit); |
| /* Skip masked gpio */ |
| if (SCP_EINT_MASK_GET[port] & BIT(bit)) |
| continue; |
| /* Call handler */ |
| signal = port * 32 + bit; |
| if (signal < GPIO_IH_COUNT) |
| gpio_irq_handlers[signal](signal); |
| } |
| } |
| } |
| DECLARE_IRQ(SCP_IRQ_EINT, gpio_interrupt, 1); |