/* Copyright 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. */ #include "common.h" #include "gpio.h" #include "hooks.h" #include "registers.h" #include "task.h" #include "util.h" /* * For each interrupt (INT0-INT3, PORT), record which GPIO entry uses it. */ static const struct gpio_info *gpio_ints[NRF51_GPIOTE_IN_COUNT]; static const struct gpio_info *gpio_int_port; volatile uint32_t * const nrf51_alt_funcs[] = { /* UART */ &NRF51_UART_PSELRTS, &NRF51_UART_PSELTXD, &NRF51_UART_PSELCTS, &NRF51_UART_PSELRXD, /* SPI1 (SPI Master) */ &NRF51_SPI0_PSELSCK, &NRF51_SPI0_PSELMOSI, &NRF51_SPI0_PSELMISO, /* TWI0 (I2C) */ &NRF51_TWI0_PSELSCL, &NRF51_TWI0_PSELSDA, /* SPI1 (SPI Master) */ &NRF51_SPI1_PSELSCK, &NRF51_SPI1_PSELMOSI, &NRF51_SPI1_PSELMISO, /* TWI1 (I2C) */ &NRF51_TWI1_PSELSCL, &NRF51_TWI1_PSELSDA, /* SPIS1 (SPI SLAVE) */ &NRF51_SPIS1_PSELSCK, &NRF51_SPIS1_PSELMISO, &NRF51_SPIS1_PSELMOSI, &NRF51_SPIS1_PSELCSN, /* QDEC (ROTARY DECODER) */ &NRF51_QDEC_PSELLED, &NRF51_QDEC_PSELA, &NRF51_QDEC_PSELB, /* LPCOMP (Low Power Comparator) */ &NRF51_LPCOMP_PSEL, }; const unsigned int nrf51_alt_func_count = ARRAY_SIZE(nrf51_alt_funcs); /* Make sure the function table and defines stay in sync */ BUILD_ASSERT(ARRAY_SIZE(nrf51_alt_funcs) == NRF51_MAX_ALT_FUNCS && NRF51_MAX_ALT_FUNCS <= GPIO_ALT_FUNC_MAX); void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags) { uint32_t val = 0; uint32_t bit = GPIO_MASK_TO_NUM(mask); if (flags & GPIO_OUTPUT) val |= NRF51_PIN_CNF_DIR_OUTPUT; else if (flags & GPIO_INPUT) val |= NRF51_PIN_CNF_DIR_INPUT; if (flags & GPIO_PULL_DOWN) val |= NRF51_PIN_CNF_PULLDOWN; else if (flags & GPIO_PULL_UP) val |= NRF51_PIN_CNF_PULLUP; /* TODO: Drive strength? H0D1? */ if (flags & GPIO_OPEN_DRAIN) val |= NRF51_PIN_CNF_DRIVE_S0D1; if (flags & GPIO_OUTPUT) { if (flags & GPIO_HIGH) NRF51_GPIO0_OUTSET = mask; else if (flags & GPIO_LOW) NRF51_GPIO0_OUTCLR = mask; } /* Interrupt levels */ if (flags & GPIO_INT_SHARED) { /* * There are no shared edge-triggered interrupts; * they're either high or low. */ ASSERT((flags & (GPIO_INT_F_RISING | GPIO_INT_F_FALLING)) == 0); ASSERT((flags & GPIO_INT_LEVEL) != GPIO_INT_LEVEL); if (flags & GPIO_INT_F_LOW) val |= NRF51_PIN_CNF_SENSE_LOW; else if (flags & GPIO_INT_F_HIGH) val |= NRF51_PIN_CNF_SENSE_HIGH; } NRF51_PIN_CNF(bit) = val; } static void gpio_init(void) { task_enable_irq(NRF51_PERID_GPIOTE); } DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT); test_mockable int gpio_get_level(enum gpio_signal signal) { return !!(NRF51_GPIO0_IN & gpio_list[signal].mask); } void gpio_set_level(enum gpio_signal signal, int value) { if (value) NRF51_GPIO0_OUTSET = gpio_list[signal].mask; else NRF51_GPIO0_OUTCLR = gpio_list[signal].mask; } void gpio_pre_init(void) { const struct gpio_info *g = gpio_list; int is_warm = 0; int i; if (NRF51_POWER_RESETREAS & (NRF51_POWER_RESETREAS_OFF | /* GPIO Wake */ NRF51_POWER_RESETREAS_LPCOMP)) { /* This is a warm reboot */ is_warm = 1; } /* Initialize Interrupt configuration */ for (i = 0; i < NRF51_GPIOTE_IN_COUNT; i++) gpio_ints[i] = NULL; gpio_int_port = NULL; /* Set all GPIOs to defaults */ for (i = 0; i < GPIO_COUNT; i++, g++) { int flags = g->flags; if (flags & GPIO_DEFAULT) continue; /* * If this is a warm reboot, don't set the output levels again. */ if (is_warm) flags &= ~(GPIO_LOW | GPIO_HIGH); /* Set up GPIO based on flags */ gpio_set_flags_by_mask(g->port, g->mask, flags); } } /* * NRF51 doesn't have an alternate function table. * Use the pin select registers in place of the function number. */ void gpio_set_alternate_function(uint32_t port, uint32_t mask, enum gpio_alternate_func func) { uint32_t bit = GPIO_MASK_TO_NUM(mask); ASSERT((~mask & BIT(bit)) == 0); /* Only one bit set. */ ASSERT(port == GPIO_0); ASSERT((func >= GPIO_ALT_FUNC_DEFAULT && func < nrf51_alt_func_count) || func == GPIO_ALT_FUNC_NONE); /* Remove the previous setting(s) */ if (func == GPIO_ALT_FUNC_NONE) { int i; for (i = 0; i < nrf51_alt_func_count; i++) { if (*(nrf51_alt_funcs[i]) == bit) *(nrf51_alt_funcs[i]) = 0xffffffff; } } else { *(nrf51_alt_funcs[func]) = bit; } } /* * Enable the interrupt associated with the "signal" * The architecture has one general (PORT) * and NRF51_GPIOTE_IN_COUNT single-pin (IN0, IN1, ...) interrupts. * */ int gpio_enable_interrupt(enum gpio_signal signal) { int pin; const struct gpio_info *g = gpio_list + signal; /* Fail if not implemented or no interrupt handler */ if (!g->mask || signal >= GPIO_IH_COUNT) return EC_ERROR_INVAL; /* If it's not shared, use INT0-INT3, otherwise use PORT. */ if (!(g->flags & GPIO_INT_SHARED)) { int int_num, free_slot = -1; uint32_t event_config = 0; for (int_num = 0; int_num < NRF51_GPIOTE_IN_COUNT; int_num++) { if (gpio_ints[int_num] == g) return EC_SUCCESS; /* This is already set up. */ if (gpio_ints[int_num] == NULL && free_slot == -1) free_slot = int_num; } ASSERT(free_slot != -1); gpio_ints[free_slot] = g; pin = GPIO_MASK_TO_NUM(g->mask); event_config = (pin << NRF51_GPIOTE_PSEL_POS) | NRF51_GPIOTE_MODE_EVENT; ASSERT(g->flags & (GPIO_INT_F_RISING | GPIO_INT_F_FALLING)); /* RISING | FALLING = TOGGLE */ if (g->flags & GPIO_INT_F_RISING) event_config |= NRF51_GPIOTE_POLARITY_LOTOHI; if (g->flags & GPIO_INT_F_FALLING) event_config |= NRF51_GPIOTE_POLARITY_HITOLO; NRF51_GPIOTE_CONFIG(free_slot) = event_config; /* Enable the IN[] interrupt. */ NRF51_GPIOTE_INTENSET = 1 << free_slot; } else { /* The first handler for the shared interrupt wins. */ if (gpio_int_port == NULL) { gpio_int_port = g; /* Enable the PORT interrupt. */ NRF51_GPIOTE_INTENSET = 1 << NRF51_GPIOTE_PORT_BIT; } } return EC_SUCCESS; } /* * Disable the interrupt associated with the "signal" * The architecture has one general (PORT) * and NRF51_GPIOTE_IN_COUNT single-pin (IN0, IN1, ...) interrupts. */ int gpio_disable_interrupt(enum gpio_signal signal) { const struct gpio_info *g = gpio_list + signal; int i; /* Fail if not implemented or no interrupt handler */ if (!g->mask || signal >= GPIO_IH_COUNT) return EC_ERROR_INVAL; /* If it's not shared, use INT0-INT3, otherwise use PORT. */ if (!(g->flags & GPIO_INT_SHARED)) { for (i = 0; i < NRF51_GPIOTE_IN_COUNT; i++) { /* Remove matching handler. */ if (gpio_ints[i] == g) { /* Disable the interrupt */ NRF51_GPIOTE_INTENCLR = 1 << NRF51_GPIOTE_IN_BIT(i); /* Zero the handler */ gpio_ints[i] = NULL; } } } else { /* Disable the interrupt */ NRF51_GPIOTE_INTENCLR = 1 << NRF51_GPIOTE_PORT_BIT; /* Zero the shared handler */ gpio_int_port = NULL; } return EC_SUCCESS; } /* * Clear interrupt and run handler. */ void gpio_interrupt(void) { const struct gpio_info *g; int i; int signal; for (i = 0; i < NRF51_GPIOTE_IN_COUNT; i++) { if (NRF51_GPIOTE_IN(i)) { NRF51_GPIOTE_IN(i) = 0; g = gpio_ints[i]; signal = g - gpio_list; if (g && signal < GPIO_IH_COUNT) gpio_irq_handlers[signal](signal); } } if (NRF51_GPIOTE_PORT) { NRF51_GPIOTE_PORT = 0; g = gpio_int_port; signal = g - gpio_list; if (g && signal < GPIO_IH_COUNT) gpio_irq_handlers[signal](signal); } } DECLARE_IRQ(NRF51_PERID_GPIOTE, gpio_interrupt, 1);