243 lines
6.4 KiB
C
243 lines
6.4 KiB
C
/*
|
|
* 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
|