diff --git a/boards/arm/nrf52840_pca10090/Kconfig b/boards/arm/nrf52840_pca10090/Kconfig index fa4f0b42d1b22b..3fe793b8bbb0f5 100644 --- a/boards/arm/nrf52840_pca10090/Kconfig +++ b/boards/arm/nrf52840_pca10090/Kconfig @@ -192,6 +192,65 @@ config BOARD_PCA10090_INTERFACE2_MCU endchoice +config BOARD_PCA10090_NRF52840_RESET + bool "Enable GPIO reset line" + help + Let the nRF52840 be reset from the nRF9160 via a GPIO line. + The GPIO line may only be one of the first 6 MCU interface pins. + The line is active high. + +if BOARD_PCA10090_NRF52840_RESET + +choice + prompt "Pin used for reset" + +comment "nRF52840 pins" + +config BOARD_PCA10090_NRF52840_RESET_P0_17 + bool "P0.17" + depends on BOARD_PCA10090_INTERFACE0_MCU + help + Pin P0.17 on nRF52840, + connected to P0.17 on the nRF9160. + +config BOARD_PCA10090_NRF52840_RESET_P0_20 + bool "P0.20" + depends on BOARD_PCA10090_INTERFACE0_MCU + help + Pin P0.20 on nRF52840, + connected to P0.18 on the nRF9160. + +config BOARD_PCA10090_NRF52840_RESET_P0_15 + bool "P0.15" + depends on BOARD_PCA10090_INTERFACE0_MCU + help + Pin P0.15 on nRF52840, + connected to P0.19 on the nRF9160. + +config BOARD_PCA10090_NRF52840_RESET_P0_22 + bool "P0.22" + depends on BOARD_PCA10090_INTERFACE1_MCU + help + Pin P0.22 on nRF52840, + connected to P0.21 on the nRF9160. + +config BOARD_PCA10090_NRF52840_RESET_P1_04 + bool "P1.04" + depends on BOARD_PCA10090_INTERFACE1_MCU + help + Pin P1.04 on nRF52840, + connected to P0.22 on the nRF9160. + +config BOARD_PCA10090_NRF52840_RESET_P1_02 + bool "P1.02" + depends on BOARD_PCA10090_INTERFACE1_MCU + help + Pin P1.02 on nRF52840, + connected to P0.23 on the nRF9160. + +endchoice + +endif # BOARD_PCA10090_NRF52840_RESET module = BOARD_PCA10090 module-str = Board Control diff --git a/boards/arm/nrf52840_pca10090/board.c b/boards/arm/nrf52840_pca10090/board.c index 2df892f666ad6f..3ac80150c31527 100644 --- a/boards/arm/nrf52840_pca10090/board.c +++ b/boards/arm/nrf52840_pca10090/board.c @@ -21,6 +21,7 @@ LOG_MODULE_REGISTER(board_control, CONFIG_BOARD_PCA10090_LOG_LEVEL); */ /* GPIO pins on Port 0 */ + #define INTERFACE0_U5 13 /* MCU interface pins 0 - 2 */ #define INTERFACE1_U6 24 /* MCU interface pins 3 - 5 */ #define UART1_VCOM_U7 12 /* Route nRF9160 UART1 to VCOM2 */ @@ -29,6 +30,7 @@ LOG_MODULE_REGISTER(board_control, CONFIG_BOARD_PCA10090_LOG_LEVEL); #define SWITCH2_U9 8 /* GPIO pins on Port 1 */ + #define INTERFACE2_U21 10 /* COEX interface pins 6 - 8 */ #define UART0_VCOM_U14 14 /* Route nRF9160 UART0 to VCOM0 */ #define UART1_VCOM_U7 12 /* Route nRF9160 UART1 to VCOM2 */ @@ -53,12 +55,18 @@ LOG_MODULE_REGISTER(board_control, CONFIG_BOARD_PCA10090_LOG_LEVEL); * | COEX2 | -- MCU Interface Pin 8 -- | P1.15 | COEX2_PH | */ -/* The following tables specify the -default- values for each pin. - * Thus, when configuring the pins if there is a one in this table, - * pull the pin to zero instead. +__packed struct pin_config { + u8_t pin; + u8_t val; +}; + +/* The following tables specify the configuration of each pin based on the + * Kconfig options that drive it. + * The switches have active-low logic, so when writing to the port we will + * need to invert the value to match the IS_ENABLED() logic. */ -static const u8_t pins_on_p0[][2] = { +static const struct pin_config pins_on_p0[] = { { INTERFACE0_U5, IS_ENABLED(CONFIG_BOARD_PCA10090_INTERFACE0_ARDUINO) }, { INTERFACE1_U6, IS_ENABLED(CONFIG_BOARD_PCA10090_INTERFACE1_TRACE) }, { UART1_VCOM_U7, IS_ENABLED(CONFIG_BOARD_PCA10090_UART1_ARDUINO) }, @@ -67,7 +75,7 @@ static const u8_t pins_on_p0[][2] = { { SWITCH2_U9, IS_ENABLED(CONFIG_BOARD_PCA10090_SWITCH1_PHY) }, }; -static const u8_t pins_on_p1[][2] = { +static const struct pin_config pins_on_p1[] = { { INTERFACE2_U21, IS_ENABLED(CONFIG_BOARD_PCA10090_INTERFACE2_COEX) }, { UART0_VCOM_U14, IS_ENABLED(CONFIG_BOARD_PCA10090_UART0_VCOM) }, { UART1_VCOM_U7, IS_ENABLED(CONFIG_BOARD_PCA10090_UART1_ARDUINO) }, @@ -163,40 +171,171 @@ static void config_print(void) IS_ENABLED(CONFIG_BOARD_PCA10090_SWITCH1_ARDUINO)); } -static void configure_pins(struct device *port, const u8_t pins[][2], - size_t size) +static int pins_configure(struct device *port, const struct pin_config cfg[], + size_t pins) { int err; - for (size_t i = 0; i < size; i++) { - err = gpio_pin_configure(port, pins[i][0], GPIO_DIR_OUT); - __ASSERT(err == 0, "Unable to configure pin %u", pins[i][0]); - - /* The pin tables contain the default values for each pin. - * Thus, if there is a one in the table, pull the pin to zero. + /* Write to the pins before configuring them as output, + * to make sure we are driving them to the correct level + * right after they are configured. + */ + for (size_t i = 0; i < pins; i++) { + /* The swiches on the board are active low, so we need + * to negate the IS_ENABLED() value from the tables. */ - err = gpio_pin_write(port, pins[i][0], !pins[i][1]); - __ASSERT(err == 0, "Unable to set pin %u to %u", pins[i][0], - !pins[i][1]); + err = gpio_pin_write(port, cfg[i].pin, !cfg[i].val); + if (err) { + return cfg[i].pin; + } + + err = gpio_pin_configure(port, cfg[i].pin, GPIO_DIR_OUT); + if (err) { + return cfg[i].pin; + } + + LOG_DBG("port %p, pin %u -> %u", + port, cfg[i].pin, !cfg[i].val); + } + + return 0; +} + +static void chip_reset(struct device *gpio, + struct gpio_callback *cb, u32_t pins) +{ + const u32_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_low(struct device *port, u32_t pin) +{ + int err; + u32_t val; + + /* Wait until the pin is pulled low */ + do { + err = gpio_pin_read(port, pin, &val); + } while (err == 0 && val != 0); +} + +static int reset_pin_configure(struct device *p0, struct device *p1) +{ + int err; + u32_t pin; + struct device *port = NULL; + + static struct gpio_callback gpio_ctx; + + /* MCU interface pins 0-2 */ + if (IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET_P0_17)) { + port = p0; + pin = 17; } + if (IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET_P0_20)) { + port = p0; + pin = 20; + } + if (IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET_P0_15)) { + port = p0; + pin = 15; + } + /* MCU interface pins 3-6 */ + if (IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET_P0_22)) { + port = p0; + pin = 22; + } + if (IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET_P1_04)) { + port = p1; + pin = 4; + } + if (IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET_P1_02)) { + port = p1; + pin = 2; + } + + if (port == NULL) { + return -EINVAL; + } + + err = gpio_pin_configure(port, pin, + GPIO_DIR_IN | GPIO_INT | GPIO_PUD_PULL_DOWN | + GPIO_INT_ACTIVE_HIGH | GPIO_INT_EDGE); + if (err) { + return err; + } + + gpio_init_callback(&gpio_ctx, chip_reset, BIT(pin)); + + err = gpio_add_callback(port, &gpio_ctx); + if (err) { + return err; + } + + err = gpio_pin_enable_callback(port, pin); + if (err) { + return err; + } + + /* Wait until the pin is pulled low before continuing. + * This lets the other side ensure that they are ready. + */ + LOG_INF("GPIO reset line enabled on pin %s.%02u, holding..", + port == p0 ? "P0" : "P1", pin); + + reset_pin_wait_low(port, pin); + + return 0; } static int init(struct device *dev) { + int rc; struct device *p0; struct device *p1; p0 = device_get_binding(DT_GPIO_P0_DEV_NAME); - __ASSERT(p0, "Unable to find GPIO %s", DT_GPIO_P0_DEV_NAME); + if (!p0) { + LOG_ERR("GPIO device " DT_GPIO_P0_DEV_NAME "not found!"); + return -EIO; + } p1 = device_get_binding(DT_GPIO_P1_DEV_NAME); - __ASSERT(p1, "Unable to find GPIO %s", DT_GPIO_P1_DEV_NAME); + if (!p1) { + LOG_ERR("GPIO device " DT_GPIO_P1_DEV_NAME " not found!"); + return -EIO; + } - configure_pins(p0, pins_on_p0, ARRAY_SIZE(pins_on_p0)); - configure_pins(p1, pins_on_p1, ARRAY_SIZE(pins_on_p1)); + /* Configure pins on each port */ + rc = pins_configure(p0, pins_on_p0, ARRAY_SIZE(pins_on_p0)); + if (rc) { + LOG_ERR("Error while configuring pin P0.%02d", rc); + return -EIO; + } + rc = pins_configure(p1, pins_on_p1, ARRAY_SIZE(pins_on_p1)); + if (rc) { + LOG_ERR("Error while configuring pin P1.%02d", rc); + return -EIO; + } config_print(); + /* 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 (IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET)) { + rc = reset_pin_configure(p0, p1); + if (rc) { + LOG_ERR("Unable to configure reset pin, err %d", rc); + return -EIO; + } + } + LOG_INF("Board configured."); return 0; diff --git a/boards/arm/nrf52840_pca10090/integrity.c b/boards/arm/nrf52840_pca10090/integrity.c index c8f699e2dbd676..867fcfddb5202e 100644 --- a/boards/arm/nrf52840_pca10090/integrity.c +++ b/boards/arm/nrf52840_pca10090/integrity.c @@ -49,3 +49,12 @@ BUILD_ASSERT_MSG(IS_ENABLED(CONFIG_BOARD_PCA10090_SWITCH0_PHY) || BUILD_ASSERT_MSG(IS_ENABLED(CONFIG_BOARD_PCA10090_SWITCH1_PHY) || IS_ENABLED(CONFIG_BOARD_PCA10090_SWITCH1_ARDUINO), "Invalid switch 2 routing"); + +BUILD_ASSERT_MSG(!IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET) || + IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET_P0_17) || + IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET_P0_20) || + IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET_P0_15) || + IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET_P0_22) || + IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET_P1_04) || + IS_ENABLED(CONFIG_BOARD_PCA10090_NRF52840_RESET_P1_02), + "No reset line selected, please check Kconfig macros");