| /* |
| * Copyright (c) 2018-2021 Nordic Semiconductor ASA. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/init.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/devicetree.h> |
| #include <zephyr/logging/log.h> |
| #include <soc.h> |
| #include <hal/nrf_gpio.h> |
| |
| LOG_MODULE_REGISTER(board_control, CONFIG_BOARD_NRF9160DK_LOG_LEVEL); |
| |
| #define GET_CTLR(name, prop, idx) \ |
| DT_GPIO_CTLR_BY_IDX(DT_NODELABEL(name), prop, idx) |
| #define GET_PIN(name, prop, idx) \ |
| DT_GPIO_PIN_BY_IDX(DT_NODELABEL(name), prop, idx) |
| #define GET_PORT(name, prop, idx) \ |
| DT_PROP_BY_PHANDLE_IDX(DT_NODELABEL(name), prop, idx, port) |
| #define GET_FLAGS(name, prop, idx) \ |
| DT_GPIO_FLAGS_BY_IDX(DT_NODELABEL(name), prop, idx) |
| #define GET_DEV(name, prop, idx) DEVICE_DT_GET(GET_CTLR(name, prop, idx)) |
| |
| /* If the GPIO pin selected to be the reset line is actually the pin that |
| * exposes the nRESET function (P0.18 in nRF52840), there is no need to |
| * provide any additional GPIO configuration for it. |
| */ |
| #define RESET_INPUT_IS_PINRESET (DT_PROP(DT_NODELABEL(uicr), gpio_as_nreset) && \ |
| GET_PORT(reset_input, gpios, 0) == 0 && \ |
| GET_PIN(reset_input, gpios, 0) == 18) |
| #define USE_RESET_GPIO \ |
| (DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(reset_input)) && \ |
| !RESET_INPUT_IS_PINRESET) |
| |
| struct switch_cfg { |
| const struct device *gpio; |
| gpio_pin_t pin; |
| gpio_dt_flags_t flags; |
| bool on; |
| #if defined(CONFIG_LOG) |
| uint8_t port; |
| bool info; |
| const char *name; |
| #endif |
| }; |
| |
| #define ROUTING_ENABLED(_name) DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(_name)) |
| #define SWITCH_CFG(_name, _idx) \ |
| { \ |
| .gpio = GET_DEV(_name, control_gpios, _idx), \ |
| .pin = GET_PIN(_name, control_gpios, _idx), \ |
| .flags = GET_FLAGS(_name, control_gpios, _idx), \ |
| .on = ROUTING_ENABLED(_name), \ |
| COND_CODE_1(CONFIG_LOG, \ |
| ( \ |
| .port = GET_PORT(_name, control_gpios, _idx), \ |
| .info = (_idx == 0), \ |
| .name = #_name, \ |
| ), ()) \ |
| } |
| #define HAS_TWO_PINS(_name) \ |
| DT_PHA_HAS_CELL_AT_IDX(DT_NODELABEL(_name), control_gpios, 1, pin) |
| |
| #define ROUTING_SWITCH(_name) \ |
| COND_CODE_1(DT_NODE_EXISTS(DT_NODELABEL(_name)), \ |
| ( \ |
| COND_CODE_1(HAS_TWO_PINS(_name), \ |
| ( \ |
| SWITCH_CFG(_name, 1), \ |
| ), ()) \ |
| SWITCH_CFG(_name, 0), \ |
| ), ()) |
| |
| static const struct switch_cfg routing_switches[] = { |
| ROUTING_SWITCH(vcom0_pins_routing) |
| ROUTING_SWITCH(vcom2_pins_routing) |
| ROUTING_SWITCH(led1_pin_routing) |
| ROUTING_SWITCH(led2_pin_routing) |
| ROUTING_SWITCH(led3_pin_routing) |
| ROUTING_SWITCH(led4_pin_routing) |
| ROUTING_SWITCH(switch1_pin_routing) |
| ROUTING_SWITCH(switch2_pin_routing) |
| ROUTING_SWITCH(button1_pin_routing) |
| ROUTING_SWITCH(button2_pin_routing) |
| ROUTING_SWITCH(nrf_interface_pins_0_2_routing) |
| ROUTING_SWITCH(nrf_interface_pins_3_5_routing) |
| ROUTING_SWITCH(nrf_interface_pins_6_8_routing) |
| ROUTING_SWITCH(nrf_interface_pin_9_routing) |
| ROUTING_SWITCH(io_expander_pins_routing) |
| ROUTING_SWITCH(external_flash_pins_routing) |
| }; |
| |
| #if USE_RESET_GPIO |
| static void chip_reset(const struct device *gpio, |
| struct gpio_callback *cb, uint32_t pins) |
| { |
| const uint32_t stamp = k_cycle_get_32(); |
| |
| printk("GPIO reset line asserted, device reset.\n"); |
| printk("Bye @ cycle32 %u\n", stamp); |
| |
| NVIC_SystemReset(); |
| } |
| |
| static void reset_pin_wait_inactive(const struct device *gpio, uint32_t pin) |
| { |
| int val; |
| |
| /* Wait until the pin becomes inactive. */ |
| do { |
| val = gpio_pin_get(gpio, pin); |
| } while (val > 0); |
| } |
| |
| static int reset_pin_configure(void) |
| { |
| int rc; |
| static struct gpio_callback gpio_ctx; |
| |
| const struct device *gpio = GET_DEV(reset_input, gpios, 0); |
| gpio_pin_t pin = GET_PIN(reset_input, gpios, 0); |
| gpio_dt_flags_t flags = GET_FLAGS(reset_input, gpios, 0); |
| |
| if (!device_is_ready(gpio)) { |
| LOG_ERR("%s is not ready", gpio->name); |
| return -ENODEV; |
| } |
| |
| rc = gpio_pin_configure(gpio, pin, flags | GPIO_INPUT); |
| if (rc) { |
| LOG_ERR("Error %d while configuring pin P%d.%02d", |
| rc, GET_PORT(reset_input, gpios, 0), pin); |
| return rc; |
| } |
| |
| gpio_init_callback(&gpio_ctx, chip_reset, BIT(pin)); |
| |
| rc = gpio_add_callback(gpio, &gpio_ctx); |
| if (rc) { |
| return rc; |
| } |
| |
| rc = gpio_pin_interrupt_configure(gpio, pin, GPIO_INT_EDGE_TO_ACTIVE); |
| if (rc) { |
| return rc; |
| } |
| |
| LOG_INF("GPIO reset line enabled on pin P%d.%02d, holding...", |
| GET_PORT(reset_input, gpios, 0), pin); |
| |
| /* Wait until the pin becomes inactive before continuing. |
| * This lets the other side ensure that they are ready. |
| */ |
| reset_pin_wait_inactive(gpio, pin); |
| |
| return 0; |
| } |
| #endif /* USE_RESET_GPIO */ |
| |
| static int init(void) |
| { |
| int rc; |
| |
| for (int i = 0; i < ARRAY_SIZE(routing_switches); ++i) { |
| const struct switch_cfg *cfg = &routing_switches[i]; |
| gpio_flags_t flags = cfg->flags; |
| |
| if (!device_is_ready(cfg->gpio)) { |
| LOG_ERR("%s is not ready", cfg->gpio->name); |
| return -ENODEV; |
| } |
| |
| flags |= (cfg->on ? GPIO_OUTPUT_ACTIVE |
| : GPIO_OUTPUT_INACTIVE); |
| rc = gpio_pin_configure(cfg->gpio, cfg->pin, flags); |
| #if defined(CONFIG_LOG) |
| LOG_DBG("Configuring P%d.%02d with flags: 0x%08x", |
| cfg->port, cfg->pin, flags); |
| if (rc) { |
| LOG_ERR("Error %d while configuring pin P%d.%02d (%s)", |
| rc, cfg->port, cfg->pin, cfg->name); |
| } else if (cfg->info) { |
| LOG_INF("%s is %s", |
| cfg->name, cfg->on ? "ENABLED" : "disabled"); |
| } |
| #endif |
| if (rc) { |
| return rc; |
| } |
| } |
| |
| /* Make sure to configure the switches before initializing |
| * the GPIO reset pin, so that we are connected to |
| * the nRF9160 before enabling our interrupt. |
| */ |
| |
| #if USE_RESET_GPIO |
| rc = reset_pin_configure(); |
| if (rc) { |
| LOG_ERR("Unable to configure reset pin, err %d", rc); |
| return -EIO; |
| } |
| #endif |
| |
| LOG_INF("Board configured."); |
| |
| return 0; |
| } |
| |
| SYS_INIT(init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); |
| |
| #define EXT_MEM_CTRL DT_NODELABEL(external_flash_pins_routing) |
| #if DT_NODE_EXISTS(EXT_MEM_CTRL) |
| |
| static int early_init(void) |
| { |
| /* As soon as possible after the system starts up, enable the analog |
| * switch that routes signals to the external flash. Otherwise, the |
| * HOLD line in the flash chip may not be properly pulled up internally |
| * and consequently the chip will not respond to any command. |
| * Later on, during the normal initialization performed above, this |
| * analog switch will get configured according to what is selected |
| * in devicetree. |
| */ |
| uint32_t psel = NRF_DT_GPIOS_TO_PSEL(EXT_MEM_CTRL, control_gpios); |
| gpio_dt_flags_t flags = DT_GPIO_FLAGS(EXT_MEM_CTRL, control_gpios); |
| |
| if (flags & GPIO_ACTIVE_LOW) { |
| nrf_gpio_pin_clear(psel); |
| } else { |
| nrf_gpio_pin_set(psel); |
| } |
| nrf_gpio_cfg_output(psel); |
| |
| return 0; |
| } |
| |
| SYS_INIT(early_init, PRE_KERNEL_1, 0); |
| #endif |