samples: boards: nrf: dynamic_pinctrl: initial version

Add a sample that shows how dynamic pin control can be useful to remap a
peripheral to a different set of pins.

NOTE: default pin configuration is provided for the nRF52840-DK board.
Such configuration will be removed once all nRF-based boards are ported
to pinctrl.

Thanks to Francesco for documentation suggestions.

Co-authored-by: Francesco D. Servidio <francesco.servidio@nordicsemi.no>
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
diff --git a/samples/boards/nrf/dynamic_pinctrl/CMakeLists.txt b/samples/boards/nrf/dynamic_pinctrl/CMakeLists.txt
new file mode 100644
index 0000000..6afa54d
--- /dev/null
+++ b/samples/boards/nrf/dynamic_pinctrl/CMakeLists.txt
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: Apache-2.0
+
+cmake_minimum_required(VERSION 3.20.0)
+
+find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
+project(dynamic_pinctrl)
+
+target_sources(app PRIVATE src/main.c src/remap.c)
diff --git a/samples/boards/nrf/dynamic_pinctrl/Kconfig b/samples/boards/nrf/dynamic_pinctrl/Kconfig
new file mode 100644
index 0000000..9431a67
--- /dev/null
+++ b/samples/boards/nrf/dynamic_pinctrl/Kconfig
@@ -0,0 +1,14 @@
+# Copyright (c) 2021 Nordic Semiconductor ASA
+# SPDX-License-Identifier: Apache-2.0
+
+mainmenu "Dynamic pin control sample"
+
+config REMAP_INIT_PRIORITY
+	int "Remap routine initialization priority"
+	default 50
+	help
+	  Initialization priority of the remap routine within the PRE_KERNEL1 level.
+	  This priority must be greater than GPIO_INIT_PRIORITY and lower than
+	  UART_INIT_PRIORITY.
+
+source "Kconfig.zephyr"
diff --git a/samples/boards/nrf/dynamic_pinctrl/README.rst b/samples/boards/nrf/dynamic_pinctrl/README.rst
new file mode 100644
index 0000000..f44a58c
--- /dev/null
+++ b/samples/boards/nrf/dynamic_pinctrl/README.rst
@@ -0,0 +1,93 @@
+.. _samples_boards_nrf_dynamic_pinctrl:
+
+Dynamic Pin Control (nRF)
+#########################
+
+The Dynamic Pin Control (nRF) sample demonstrates how to change ``uart0`` at
+early boot stages, depending on the input level on a pin connected to a
+push-button.
+
+Overview
+********
+
+Slightly different board revisions that implement just small changes (like
+improving the PCB layout or changing components to new equivalents) do not
+necessarily require changes to the firmware. As such, one firmware image can be
+able to boot onto multiple board revisions.
+
+However, if a certain peripheral is routed to different sets of pins between
+revisions, the firmware needs to select the appropriate routing when the
+system is initialized.
+
+The Dynamic Pin Control (nRF) sample allows you to select the appropriate routing.
+
+If the push button is not pressed, the system does nothing and continues with
+the default configuration. If the button is pressed, the alternative
+configuration is applied.
+
+Alternative configurations can only be applied if the device driver using the
+associated pins has not been initialized yet. Therefore, pay attention to the
+initialization priorities set in the :file:`prj.conf` file.
+
+nRF52840 DK
+===========
+
+The diagram below shows the pins assigned to the default and alternative
+configurations.
+
+.. figure:: images/nrf52840dk-dynamic-pinctrl.png
+
+    Configuration for nRF52840 DK
+
+If you power on the board, the ``uart0`` peripheral is routed to the default
+set of pins. The default set of pins is also exposed through the left USB
+connector as a virtual COM port.
+
+If you power on the board while holding Button 1, the ``uart0`` peripheral is
+routed to the alternative set of pins.
+
+Building and Running
+********************
+
+You can build this application for the nRF52840 DK as follows:
+
+.. zephyr-app-commands::
+   :zephyr-app: samples/boards/nrf/dynamic_pinctrl
+   :board: nrf52840dk_nrf52840
+   :goals: build
+   :compact:
+
+The sample can also support other nRF based boards if you provide a Devicetree
+overlay file with an alternative configuration for ``uart0``. Select ``uart0``
+for ``zephyr,console`` to make the sample work as expected.
+
+Sample Output
+=============
+
+Follow these steps to test the two configurations :
+
+1. Connect a USB-to-UART adapter to both sets of pins. If the board routes the
+   default configuration to a virtual COM port (as in the nRF52840 DK), you can
+   directly use that port.
+
+#. Open two serial terminals, one connected to the default set of pins and the
+   other connected to the alternative set of pins.
+
+   .. figure:: images/terminals-empty.png
+
+       Two serial terminals (left: default, right: alternative).
+
+#. Turn on the board. You should see a ``Hello World!`` message printed on
+   the first terminal.
+
+   .. figure:: images/terminals-default.png
+
+       ``Hello World!`` printed on the default set of pins.
+
+#. Press and hold the configuration-selection push-button (button 1 on the
+   nRF52840 DK) and press the board reset button. You now should see a
+   ``Hello World!`` message on the second terminal.
+
+   .. figure:: images/terminals-alt.png
+
+       ``Hello World!`` printed on the alternative set of pins.
diff --git a/samples/boards/nrf/dynamic_pinctrl/boards/nrf52840dk_nrf52840.overlay b/samples/boards/nrf/dynamic_pinctrl/boards/nrf52840dk_nrf52840.overlay
new file mode 100644
index 0000000..b6d1f6d
--- /dev/null
+++ b/samples/boards/nrf/dynamic_pinctrl/boards/nrf52840dk_nrf52840.overlay
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2021 Nordic Semiconductor ASA
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/ {
+	zephyr,user {
+		uart0_alt_default = <&uart0_alt_default>;
+		uart0_alt_sleep = <&uart0_alt_sleep>;
+	};
+};
+
+/* TODO: move to board dts once all boards enable pinctrl by default */
+&uart0 {
+	pinctrl-0 = <&uart0_default>;
+	pinctrl-1 = <&uart0_sleep>;
+	pinctrl-names = "default", "sleep";
+};
+
+&pinctrl {
+	/* pin configuration for UART0 */
+	/* TODO: move to board dts once all boards enable pinctrl by default */
+	uart0_default: uart0_default {
+		group1 {
+			psels = <NRF_PSEL(UART_TX, 0, 6)>,
+				<NRF_PSEL(UART_RTS, 0, 5)>;
+		};
+		group2 {
+			psels = <NRF_PSEL(UART_RX, 0, 8)>,
+				<NRF_PSEL(UART_CTS, 0, 7)>;
+			bias-pull-up;
+		};
+	};
+
+	uart0_sleep: uart0_sleep {
+		group1 {
+			psels = <NRF_PSEL(UART_TX, 0, 6)>,
+				<NRF_PSEL(UART_RX, 0, 8)>,
+				<NRF_PSEL(UART_RTS, 0, 5)>,
+				<NRF_PSEL(UART_CTS, 0, 7)>;
+			low-power-enable;
+		};
+	};
+
+	/* Alternative pin configuration for UART0 */
+	uart0_alt_default: uart0_alt_default {
+		group1 {
+			psels = <NRF_PSEL(UART_TX, 1, 5)>,
+				<NRF_PSEL(UART_RTS, 1, 4)>;
+		};
+		group2 {
+			psels = <NRF_PSEL(UART_RX, 1, 7)>,
+				<NRF_PSEL(UART_CTS, 1, 6)>;
+			bias-pull-up;
+		};
+	};
+
+	uart0_alt_sleep: uart0_alt_sleep {
+		group1 {
+			psels = <NRF_PSEL(UART_TX, 1, 5)>,
+				<NRF_PSEL(UART_RX, 1, 7)>,
+				<NRF_PSEL(UART_RTS, 1, 4)>,
+				<NRF_PSEL(UART_CTS, 1, 6)>;
+			low-power-enable;
+		};
+	};
+};
diff --git a/samples/boards/nrf/dynamic_pinctrl/images/nrf52840dk-dynamic-pinctrl.odg b/samples/boards/nrf/dynamic_pinctrl/images/nrf52840dk-dynamic-pinctrl.odg
new file mode 100644
index 0000000..0bf320a
--- /dev/null
+++ b/samples/boards/nrf/dynamic_pinctrl/images/nrf52840dk-dynamic-pinctrl.odg
Binary files differ
diff --git a/samples/boards/nrf/dynamic_pinctrl/images/nrf52840dk-dynamic-pinctrl.png b/samples/boards/nrf/dynamic_pinctrl/images/nrf52840dk-dynamic-pinctrl.png
new file mode 100644
index 0000000..c4a2c04
--- /dev/null
+++ b/samples/boards/nrf/dynamic_pinctrl/images/nrf52840dk-dynamic-pinctrl.png
Binary files differ
diff --git a/samples/boards/nrf/dynamic_pinctrl/images/terminals-alt.png b/samples/boards/nrf/dynamic_pinctrl/images/terminals-alt.png
new file mode 100644
index 0000000..8172324
--- /dev/null
+++ b/samples/boards/nrf/dynamic_pinctrl/images/terminals-alt.png
Binary files differ
diff --git a/samples/boards/nrf/dynamic_pinctrl/images/terminals-default.png b/samples/boards/nrf/dynamic_pinctrl/images/terminals-default.png
new file mode 100644
index 0000000..f20da0e
--- /dev/null
+++ b/samples/boards/nrf/dynamic_pinctrl/images/terminals-default.png
Binary files differ
diff --git a/samples/boards/nrf/dynamic_pinctrl/images/terminals-empty.png b/samples/boards/nrf/dynamic_pinctrl/images/terminals-empty.png
new file mode 100644
index 0000000..f2e312c
--- /dev/null
+++ b/samples/boards/nrf/dynamic_pinctrl/images/terminals-empty.png
Binary files differ
diff --git a/samples/boards/nrf/dynamic_pinctrl/prj.conf b/samples/boards/nrf/dynamic_pinctrl/prj.conf
new file mode 100644
index 0000000..7aff295
--- /dev/null
+++ b/samples/boards/nrf/dynamic_pinctrl/prj.conf
@@ -0,0 +1,5 @@
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_DYNAMIC=y
+# configure serial and console to come after remap hook
+CONFIG_SERIAL_INIT_PRIORITY=60
+CONFIG_CONSOLE_INIT_PRIORITY=70
diff --git a/samples/boards/nrf/dynamic_pinctrl/sample.yaml b/samples/boards/nrf/dynamic_pinctrl/sample.yaml
new file mode 100644
index 0000000..b8f38be
--- /dev/null
+++ b/samples/boards/nrf/dynamic_pinctrl/sample.yaml
@@ -0,0 +1,9 @@
+sample:
+  description: A dynamic pin control sample for nRF board/s.
+  name: dynamic_pinctrl
+tests:
+  sample.boards.nrf.dynamic_pinctrl:
+    build_only: true
+    platform_allow: nrf52840dk_nrf52840
+    integration_platforms:
+      - nrf52840dk_nrf52840
diff --git a/samples/boards/nrf/dynamic_pinctrl/src/main.c b/samples/boards/nrf/dynamic_pinctrl/src/main.c
new file mode 100644
index 0000000..cea1c36
--- /dev/null
+++ b/samples/boards/nrf/dynamic_pinctrl/src/main.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2021 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <sys/printk.h>
+
+void main(void)
+{
+	printk("Hello World!\n");
+}
diff --git a/samples/boards/nrf/dynamic_pinctrl/src/remap.c b/samples/boards/nrf/dynamic_pinctrl/src/remap.c
new file mode 100644
index 0000000..6d90ddb
--- /dev/null
+++ b/samples/boards/nrf/dynamic_pinctrl/src/remap.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2021 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <device.h>
+#include <drivers/gpio.h>
+#include <drivers/pinctrl.h>
+
+/* make sure devices and remap hook are initialized in the correct order */
+BUILD_ASSERT((CONFIG_GPIO_INIT_PRIORITY < CONFIG_REMAP_INIT_PRIORITY) &&
+	     (CONFIG_REMAP_INIT_PRIORITY < CONFIG_SERIAL_INIT_PRIORITY),
+	     "Device driver priorities are not set correctly");
+
+PINCTRL_DT_DEV_CONFIG_DECLARE(DT_NODELABEL(uart0));
+
+/* UART0 alternative configurations (default and sleep states) */
+PINCTRL_DT_STATE_PINS_DEFINE(DT_PATH(zephyr_user), uart0_alt_default);
+#ifdef CONFIG_PM_DEVICE
+PINCTRL_DT_STATE_PINS_DEFINE(DT_PATH(zephyr_user), uart0_alt_sleep);
+#endif
+
+static const struct pinctrl_state uart0_alt[] = {
+	PINCTRL_DT_STATE_INIT(uart0_alt_default, PINCTRL_STATE_DEFAULT),
+#ifdef CONFIG_PM_DEVICE
+	PINCTRL_DT_STATE_INIT(uart0_alt_sleep, PINCTRL_STATE_SLEEP),
+#endif
+};
+
+static int remap_pins(const struct device *dev)
+{
+	int ret;
+	const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(DT_ALIAS(sw0),
+							       gpios, {0});
+
+	ARG_UNUSED(dev);
+
+	if (!device_is_ready(button.port)) {
+		return -ENODEV;
+	}
+
+	ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
+	if (ret < 0) {
+		return ret;
+	}
+
+	/* remap UART0 pins if button is pressed */
+	if (gpio_pin_get_dt(&button)) {
+		struct pinctrl_dev_config *uart0_config =
+			PINCTRL_DT_DEV_CONFIG_GET(DT_NODELABEL(uart0));
+
+		return pinctrl_update_states(uart0_config, uart0_alt,
+					     ARRAY_SIZE(uart0_alt));
+
+	}
+
+	return 0;
+}
+
+SYS_INIT(remap_pins, PRE_KERNEL_1, CONFIG_REMAP_INIT_PRIORITY);