diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index f5b685f7fe..36736f374d 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -2,6 +2,7 @@
 
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/gpio/sandbox-gpio.h>
+#include <dt-bindings/pinctrl/sandbox-pinmux.h>
 
 / {
 	model = "sandbox";
@@ -967,30 +968,60 @@
 	pinctrl {
 		compatible = "sandbox,pinctrl";
 
-		pinctrl-names = "default";
-		pinctrl-0 = <&gpios>;
+		pinctrl-names = "default", "alternate";
+		pinctrl-0 = <&pinctrl_gpios>, <&pinctrl_i2s>;
+		pinctrl-1 = <&pinctrl_spi>, <&pinctrl_i2c>;
 
-		gpios: gpios {
+		pinctrl_gpios: gpios {
 			gpio0 {
-				pins = "GPIO0";
+				pins = "P5";
+				function = "GPIO";
 				bias-pull-up;
 				input-disable;
 			};
 			gpio1 {
-				pins = "GPIO1";
+				pins = "P6";
+				function = "GPIO";
 				output-high;
 				drive-open-drain;
 			};
 			gpio2 {
-				pins = "GPIO2";
+				pinmux = <SANDBOX_PINMUX(7, SANDBOX_PINMUX_GPIO)>;
 				bias-pull-down;
 				input-enable;
 			};
 			gpio3 {
-				pins = "GPIO3";
+				pinmux = <SANDBOX_PINMUX(8, SANDBOX_PINMUX_GPIO)>;
 				bias-disable;
 			};
 		};
+
+		pinctrl_i2c: i2c {
+			groups {
+				groups = "I2C_UART";
+				function = "I2C";
+			};
+
+			pins {
+				pins = "P0", "P1";
+				drive-open-drain;
+			};
+		};
+
+		pinctrl_i2s: i2s {
+			groups = "SPI_I2S";
+			function = "I2S";
+		};
+
+		pinctrl_spi: spi {
+			groups = "SPI_I2S";
+			function = "SPI";
+
+			cs {
+				pinmux = <SANDBOX_PINMUX(5, SANDBOX_PINMUX_CS)>,
+					 <SANDBOX_PINMUX(6, SANDBOX_PINMUX_CS)>;
+			};
+		};
 	};
 
 	hwspinlock at 0 {
diff --git a/drivers/pinctrl/pinctrl-sandbox.c b/drivers/pinctrl/pinctrl-sandbox.c
index ac0119d198..9aa13fbd55 100644
--- a/drivers/pinctrl/pinctrl-sandbox.c
+++ b/drivers/pinctrl/pinctrl-sandbox.c
@@ -3,55 +3,67 @@
  * Copyright (C) 2015  Masahiro Yamada <yamada.masahiro at socionext.com>
  */
 
-/* #define DEBUG */
-
 #include <common.h>
 #include <dm.h>
-#include <log.h>
 #include <dm/pinctrl.h>
+#include <dt-bindings/pinctrl/sandbox-pinmux.h>
+#include <log.h>
 #include <linux/bitops.h>
 
+/*
+ * This driver emulates a pin controller with the following rules:
+ * - The pinctrl config for each pin must be set individually
+ * - The first three pins (P0-P2) must be muxed as a group
+ * - The next two pins (P3-P4) must be muxed as a group
+ * - The last four pins (P5-P8) must be muxed individually
+ */
+
 static const char * const sandbox_pins[] = {
-	"SCL",
-	"SDA",
-	"TX",
-	"RX",
-	"W1",
-	"GPIO0",
-	"GPIO1",
-	"GPIO2",
-	"GPIO3",
+#define PIN(x) \
+	[x] = "P" #x
+	PIN(0),
+	PIN(1),
+	PIN(2),
+	PIN(3),
+	PIN(4),
+	PIN(5),
+	PIN(6),
+	PIN(7),
+	PIN(8),
+#undef PIN
 };
 
-static const char * const sandbox_pins_muxing[] = {
-	"I2C SCL",
-	"I2C SDA",
-	"Uart TX",
-	"Uart RX",
-	"1-wire gpio",
-	"gpio",
-	"gpio",
-	"gpio",
-	"gpio",
+static const char * const sandbox_pins_muxing[][2] = {
+	{ "UART TX", "I2C SCL" },
+	{ "UART RX", "I2C SDA" },
+	{ "SPI SCLK", "I2S SCK" },
+	{ "SPI MOSI", "I2S SD" },
+	{ "SPI MISO", "I2S WS" },
+	{ "GPIO0", "SPI CS0" },
+	{ "GPIO1", "SPI CS1" },
+	{ "GPIO2", "PWM0" },
+	{ "GPIO3", "PWM1" },
 };
 
+#define SANDBOX_GROUP_I2C_UART 0
+#define SANDBOX_GROUP_SPI_I2S 1
+
 static const char * const sandbox_groups[] = {
-	"i2c",
-	"serial_a",
-	"serial_b",
-	"spi",
-	"w1",
+	[SANDBOX_GROUP_I2C_UART] = "I2C_UART",
+	[SANDBOX_GROUP_SPI_I2S] = "SPI_I2S",
 };
 
 static const char * const sandbox_functions[] = {
-	"i2c",
-	"serial",
-	"spi",
-	"w1",
-	"gpio",
-	"gpio",
-	"gpio",
-	"gpio",
+#define FUNC(id) \
+	[SANDBOX_PINMUX_##id] = #id
+	FUNC(UART),
+	FUNC(I2C),
+	FUNC(SPI),
+	FUNC(I2S),
+	FUNC(GPIO),
+	FUNC(CS),
+	FUNC(PWM),
+#undef FUNC
 };
 
 static const struct pinconf_param sandbox_conf_params[] = {
@@ -69,6 +81,7 @@ static const struct pinconf_param sandbox_conf_params[] = {
 };
 
 /* bitfield used to save param and value of each pin/selector */
+static unsigned int sandbox_mux;
 static unsigned int sandbox_pins_param[ARRAY_SIZE(sandbox_pins)];
 static unsigned int sandbox_pins_value[ARRAY_SIZE(sandbox_pins)];
 
@@ -89,7 +102,8 @@ static int sandbox_get_pin_muxing(struct udevice *dev,
 	const struct pinconf_param *p;
 	int i;
 
-	snprintf(buf, size, "%s", sandbox_pins_muxing[selector]);
+	snprintf(buf, size, "%s",
+		 sandbox_pins_muxing[selector][!!(priv->mux & BIT(selector))]);
 
 	if (sandbox_pins_param[selector]) {
 		for (i = 0, p = sandbox_conf_params;
@@ -133,10 +147,30 @@ static const char *sandbox_get_function_name(struct udevice *dev,
 static int sandbox_pinmux_set(struct udevice *dev, unsigned pin_selector,
 			      unsigned func_selector)
 {
+	int mux;
+	struct sandbox_pinctrl_priv *priv = dev_get_priv(dev);
+
 	debug("sandbox pinmux: pin = %d (%s), function = %d (%s)\n",
 	      pin_selector, sandbox_get_pin_name(dev, pin_selector),
 	      func_selector, sandbox_get_function_name(dev, func_selector));
 
+	if (pin_selector < 5)
+		return -EINVAL;
+
+	switch (func_selector) {
+	case SANDBOX_PINMUX_GPIO:
+		mux = 0;
+		break;
+	case SANDBOX_PINMUX_CS:
+	case SANDBOX_PINMUX_PWM:
+		mux = BIT(pin_selector);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	sandbox_mux &= ~BIT(pin_selector);
+	sandbox_mux |= mux;
 	sandbox_pins_param[pin_selector] = 0;
 	sandbox_pins_value[pin_selector] = 0;
 
@@ -147,13 +181,61 @@ static int sandbox_pinmux_group_set(struct udevice *dev,
 				    unsigned group_selector,
 				    unsigned func_selector)
 {
+	bool mux;
+	int i, group_start, group_end;
+	struct sandbox_pinctrl_priv *priv = dev_get_priv(dev);
+	unsigned int mask;
+
 	debug("sandbox pinmux: group = %d (%s), function = %d (%s)\n",
 	      group_selector, sandbox_get_group_name(dev, group_selector),
 	      func_selector, sandbox_get_function_name(dev, func_selector));
 
+	if (group_selector == SANDBOX_GROUP_I2C_UART) {
+		group_start = 0;
+		group_end = 1;
+
+		if (func_selector == SANDBOX_PINMUX_UART)
+			mux = false;
+		else if (func_selector == SANDBOX_PINMUX_I2C)
+			mux = true;
+		else
+			return -EINVAL;
+	} else if (group_selector == SANDBOX_GROUP_SPI_I2S) {
+		group_start = 2;
+		group_end = 4;
+
+		if (func_selector == SANDBOX_PINMUX_SPI)
+			mux = false;
+		else if (func_selector == SANDBOX_PINMUX_I2S)
+			mux = true;
+		else
+			return -EINVAL;
+	} else {
+		return -EINVAL;
+	}
+
+	mask = GENMASK(group_end, group_start);
+	priv->mux &= ~mask;
+	priv->mux |= mux ? mask : 0;
+
+	for (i = group_start; i < group_end; i++) {
+		priv->param[i] = 0;
+		priv->value[i] = 0;
+	}
+
 	return 0;
 }
 
+static int sandbox_pinmux_property_set(struct udevice *dev, u32 pinmux_group)
+{
+	int ret;
+	unsigned pin_selector = pinmux_group & 0xFFFF;
+	unsigned func_selector = pinmux_group >> 16;
+
+	ret = sandbox_pinmux_set(dev, pin_selector, func_selector);
+	return ret ? ret : pin_selector;
+}
+
 static int sandbox_pinconf_set(struct udevice *dev, unsigned pin_selector,
 			       unsigned param, unsigned argument)
 {
@@ -191,6 +273,7 @@ const struct pinctrl_ops sandbox_pinctrl_ops = {
 	.get_function_name = sandbox_get_function_name,
 	.pinmux_set = sandbox_pinmux_set,
 	.pinmux_group_set = sandbox_pinmux_group_set,
+	.pinmux_property_set = sandbox_pinmux_property_set,
 	.pinconf_num_params = ARRAY_SIZE(sandbox_conf_params),
 	.pinconf_params = sandbox_conf_params,
 	.pinconf_set = sandbox_pinconf_set,
diff --git a/include/dt-bindings/pinctrl/sandbox-pinmux.h b/include/dt-bindings/pinctrl/sandbox-pinmux.h
new file mode 100644
index 0000000000..891af072e5
--- /dev/null
+++ b/include/dt-bindings/pinctrl/sandbox-pinmux.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2 at gmail.com>
+ */
+
+#ifndef SANDBOX_PINMUX_H
+#define SANDBOX_PINMUX_H
+
+#define SANDBOX_PINMUX_UART 0
+#define SANDBOX_PINMUX_I2C  1
+#define SANDBOX_PINMUX_SPI  2
+#define SANDBOX_PINMUX_I2S  3
+#define SANDBOX_PINMUX_GPIO 4
+#define SANDBOX_PINMUX_CS   5
+#define SANDBOX_PINMUX_PWM  6
+
+#define SANDBOX_PINMUX(pin, func) ((func) << 16 | (pin))
+
+#endif /* SANDBOX_PINMUX_H */
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 0d1c66fa1e..9e273ee02d 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -76,4 +76,7 @@ obj-$(CONFIG_DM_RNG) += rng.o
 obj-$(CONFIG_CLK_K210_SET_RATE) += k210_pll.o
 obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
 obj-$(CONFIG_RESET_SYSCON) += syscon-reset.o
+ifneq ($(CONFIG_PINMUX),)
+obj-$(CONFIG_PINCONF) += pinmux.o
+endif
 endif
diff --git a/test/py/tests/test_pinmux.py b/test/py/tests/test_pinmux.py
index 4e6df992a4..0cbbae000c 100644
--- a/test/py/tests/test_pinmux.py
+++ b/test/py/tests/test_pinmux.py
@@ -28,15 +28,15 @@ def test_pinmux_status_all(u_boot_console):
     assert ('a6        : gpio output .' in output)
 
     assert ('pinctrl:' in output)
-    assert ('SCL       : I2C SCL.' in output)
-    assert ('SDA       : I2C SDA.' in output)
-    assert ('TX        : Uart TX.' in output)
-    assert ('RX        : Uart RX.' in output)
-    assert ('W1        : 1-wire gpio.' in output)
-    assert ('GPIO0     : gpio bias-pull-up input-disable.' in output)
-    assert ('GPIO1     : gpio drive-open-drain.' in output)
-    assert ('GPIO2     : gpio bias-pull-down input-enable.' in output)
-    assert ('GPIO3     : gpio bias-disable.' in output)
+    assert ('P0        : UART TX.' in output)
+    assert ('P1        : UART RX.' in output)
+    assert ('P2        : I2S SCK.' in output)
+    assert ('P3        : I2S SD.' in output)
+    assert ('P4        : I2S WS.' in output)
+    assert ('P5        : GPIO0 bias-pull-up input-disable.' in output)
+    assert ('P6        : GPIO1 drive-open-drain.' in output)
+    assert ('P7        : GPIO2 bias-pull-down input-enable.' in output)
+    assert ('P8        : GPIO3 bias-disable.' in output)
 
 @pytest.mark.buildconfigspec('cmd_pinmux')
 @pytest.mark.boardspec('sandbox')
@@ -73,12 +73,12 @@ def test_pinmux_status(u_boot_console):
     assert (not 'pinctrl-gpio:' in output)
     assert (not 'pinctrl:' in output)
 
-    assert ('SCL       : I2C SCL.' in output)
-    assert ('SDA       : I2C SDA.' in output)
-    assert ('TX        : Uart TX.' in output)
-    assert ('RX        : Uart RX.' in output)
-    assert ('W1        : 1-wire gpio.' in output)
-    assert ('GPIO0     : gpio bias-pull-up input-disable.' in output)
-    assert ('GPIO1     : gpio drive-open-drain.' in output)
-    assert ('GPIO2     : gpio bias-pull-down input-enable.' in output)
-    assert ('GPIO3     : gpio bias-disable.' in output)
+    assert ('P0        : UART TX.' in output)
+    assert ('P1        : UART RX.' in output)
+    assert ('P2        : I2S SCK.' in output)
+    assert ('P3        : I2S SD.' in output)
+    assert ('P4        : I2S WS.' in output)
+    assert ('P5        : GPIO0 bias-pull-up input-disable.' in output)
+    assert ('P6        : GPIO1 drive-open-drain.' in output)
+    assert ('P7        : GPIO2 bias-pull-down input-enable.' in output)
+    assert ('P8        : GPIO3 bias-disable.' in output)
