/* 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 MEC1322 */ #include "common.h" #include "gpio.h" #include "hooks.h" #include "registers.h" #include "system.h" #include "task.h" #include "timer.h" #include "util.h" struct gpio_int_mapping { int8_t girq_id; int8_t port_offset; }; /* Mapping from GPIO port to GIRQ info */ static const struct gpio_int_mapping int_map[22] = { {11, 0}, {11, 0}, {11, 0}, {11, 0}, {10, 4}, {10, 4}, {10, 4}, {-1, -1}, {-1, -1}, {-1, -1}, {9, 10}, {9, 10}, {9, 10}, {9, 10}, {8, 14}, {8, 14}, {8, 14}, {-1, -1}, {-1, -1}, {-1, -1}, {20, 20}, {20, 20} }; void gpio_set_alternate_function(uint32_t port, uint32_t mask, enum gpio_alternate_func func) { int i; uint32_t val; while (mask) { i = __builtin_ffs(mask) - 1; val = MEC1322_GPIO_CTL(port, i); val &= ~(BIT(12) | BIT(13)); /* mux_control = DEFAULT, indicates GPIO */ if (func > GPIO_ALT_FUNC_DEFAULT) val |= (func & 0x3) << 12; MEC1322_GPIO_CTL(port, i) = val; mask &= ~BIT(i); } } test_mockable int gpio_get_level(enum gpio_signal signal) { uint32_t mask = gpio_list[signal].mask; int i; uint32_t val; if (mask == 0) return 0; i = GPIO_MASK_TO_NUM(mask); val = MEC1322_GPIO_CTL(gpio_list[signal].port, i); return (val & BIT(24)) ? 1 : 0; } void gpio_set_level(enum gpio_signal signal, int value) { uint32_t mask = gpio_list[signal].mask; int i; if (mask == 0) return; i = GPIO_MASK_TO_NUM(mask); if (value) MEC1322_GPIO_CTL(gpio_list[signal].port, i) |= BIT(16); else MEC1322_GPIO_CTL(gpio_list[signal].port, i) &= ~BIT(16); } void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags) { int i; uint32_t val; while (mask) { i = GPIO_MASK_TO_NUM(mask); mask &= ~BIT(i); val = MEC1322_GPIO_CTL(port, i); /* * Select open drain first, so that we don't glitch the signal * when changing the line to an output. */ if (flags & GPIO_OPEN_DRAIN) val |= BIT(8); else val &= ~BIT(8); if (flags & GPIO_OUTPUT) { val |= BIT(9); val &= ~BIT(10); } else { val &= ~BIT(9); val |= BIT(10); } /* Handle pullup / pulldown */ if (flags & GPIO_PULL_UP) val = (val & ~0x3) | 0x1; else if (flags & GPIO_PULL_DOWN) val = (val & ~0x3) | 0x2; else val &= ~0x3; /* Set up interrupt */ if (flags & (GPIO_INT_F_RISING | GPIO_INT_F_FALLING)) val |= BIT(7); else val &= ~BIT(7); val &= ~(0x7 << 4); if ((flags & GPIO_INT_F_RISING) && (flags & GPIO_INT_F_FALLING)) val |= 0x7 << 4; else if (flags & GPIO_INT_F_RISING) val |= 0x5 << 4; else if (flags & GPIO_INT_F_FALLING) val |= 0x6 << 4; else if (flags & GPIO_INT_F_HIGH) val |= 0x1 << 4; else if (!(flags & GPIO_INT_F_LOW)) /* No interrupt flag set */ val |= 0x4 << 4; /* Set up level */ if (flags & GPIO_HIGH) val |= BIT(16); else if (flags & GPIO_LOW) val &= ~BIT(16); MEC1322_GPIO_CTL(port, i) = val; } } int gpio_enable_interrupt(enum gpio_signal signal) { int i, port, girq_id, bit_id; if (gpio_list[signal].mask == 0) return EC_SUCCESS; i = GPIO_MASK_TO_NUM(gpio_list[signal].mask); port = gpio_list[signal].port; girq_id = int_map[port].girq_id; bit_id = (port - int_map[port].port_offset) * 8 + i; MEC1322_INT_ENABLE(girq_id) |= BIT(bit_id); MEC1322_INT_BLK_EN |= BIT(girq_id); return EC_SUCCESS; } int gpio_disable_interrupt(enum gpio_signal signal) { int i, port, girq_id, bit_id; if (gpio_list[signal].mask == 0) return EC_SUCCESS; i = GPIO_MASK_TO_NUM(gpio_list[signal].mask); port = gpio_list[signal].port; girq_id = int_map[port].girq_id; bit_id = (port - int_map[port].port_offset) * 8 + i; MEC1322_INT_DISABLE(girq_id) = BIT(bit_id); return EC_SUCCESS; } int gpio_clear_pending_interrupt(enum gpio_signal signal) { int i, port, girq_id, bit_id; if (gpio_list[signal].mask == 0) return EC_SUCCESS; i = GPIO_MASK_TO_NUM(gpio_list[signal].mask); port = gpio_list[signal].port; girq_id = int_map[port].girq_id; bit_id = (port - int_map[port].port_offset) * 8 + i; /* Clear interrupt source sticky status bit even if not enabled */ MEC1322_INT_SOURCE(girq_id) |= 1 << bit_id; return EC_SUCCESS; } void gpio_pre_init(void) { int i; int flags; int is_warm = system_is_reboot_warm(); const struct gpio_info *g = 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); gpio_set_flags_by_mask(g->port, g->mask, flags); /* Use as GPIO, not alternate function */ gpio_set_alternate_function(g->port, g->mask, GPIO_ALT_FUNC_NONE); } } /* Clear any interrupt flags before enabling GPIO interrupt */ #define ENABLE_GPIO_GIRQ(x) \ do { \ MEC1322_INT_SOURCE(x) |= MEC1322_INT_RESULT(x); \ task_enable_irq(MEC1322_IRQ_GIRQ ## x); \ } while (0) static void gpio_init(void) { ENABLE_GPIO_GIRQ(8); ENABLE_GPIO_GIRQ(9); ENABLE_GPIO_GIRQ(10); ENABLE_GPIO_GIRQ(11); ENABLE_GPIO_GIRQ(20); } DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT); /*****************************************************************************/ /* Interrupt handlers */ /** * Handler for each GIRQ interrupt. This reads and clears the interrupt bits for * the GIRQ interrupt, then finds and calls the corresponding GPIO interrupt * handlers. * * @param girq GIRQ index * @param port_offset GPIO port offset for the given GIRQ */ static void gpio_interrupt(int girq, int port_offset) { int i, bit; const struct gpio_info *g = gpio_list; uint32_t sts = MEC1322_INT_RESULT(girq); MEC1322_INT_SOURCE(girq) |= sts; for (i = 0; i < GPIO_IH_COUNT && sts; ++i, ++g) { bit = (g->port - port_offset) * 8 + __builtin_ffs(g->mask) - 1; if (sts & BIT(bit)) gpio_irq_handlers[i](i); sts &= ~BIT(bit); } } #define GPIO_IRQ_FUNC(irqfunc, girq, port_offset) \ void irqfunc(void) \ { \ gpio_interrupt(girq, port_offset); \ } GPIO_IRQ_FUNC(__girq_8_interrupt, 8, 14); GPIO_IRQ_FUNC(__girq_9_interrupt, 9, 10); GPIO_IRQ_FUNC(__girq_10_interrupt, 10, 4); GPIO_IRQ_FUNC(__girq_11_interrupt, 11, 0); GPIO_IRQ_FUNC(__girq_20_interrupt, 20, 20); #undef GPIO_IRQ_FUNC /* * Declare IRQs. Nesting this macro inside the GPIO_IRQ_FUNC macro works * poorly because DECLARE_IRQ() stringizes its inputs. */ DECLARE_IRQ(MEC1322_IRQ_GIRQ8, __girq_8_interrupt, 1); DECLARE_IRQ(MEC1322_IRQ_GIRQ9, __girq_9_interrupt, 1); DECLARE_IRQ(MEC1322_IRQ_GIRQ10, __girq_10_interrupt, 1); DECLARE_IRQ(MEC1322_IRQ_GIRQ11, __girq_11_interrupt, 1); DECLARE_IRQ(MEC1322_IRQ_GIRQ20, __girq_20_interrupt, 1);