diff --git a/MAINTAINERS b/MAINTAINERS
index 754db5553d..1f2545191b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -100,6 +100,7 @@ F:	drivers/mmc/bcm2835_sdhci.c
 F:	drivers/serial/serial_bcm283x_mu.c
 F:	drivers/video/bcm2835.c
 F:	include/dm/platform_data/serial_bcm283x_mu.h
+F:	drivers/pinctrl/broadcom/
 
 ARM FREESCALE IMX
 M:	Stefano Babic <sbabic@denx.de>
diff --git a/arch/arm/mach-bcm283x/include/mach/gpio.h b/arch/arm/mach-bcm283x/include/mach/gpio.h
index daaee52f81..7b4ddc9246 100644
--- a/arch/arm/mach-bcm283x/include/mach/gpio.h
+++ b/arch/arm/mach-bcm283x/include/mach/gpio.h
@@ -61,6 +61,4 @@ struct bcm2835_gpio_platdata {
 	unsigned long base;
 };
 
-int bcm2835_gpio_get_func_id(struct udevice *dev, unsigned gpio);
-
 #endif /* _BCM2835_GPIO_H_ */
diff --git a/board/raspberrypi/rpi/rpi.c b/board/raspberrypi/rpi/rpi.c
index 3b7a54f519..c8924d4362 100644
--- a/board/raspberrypi/rpi/rpi.c
+++ b/board/raspberrypi/rpi/rpi.c
@@ -24,6 +24,7 @@
 #include <asm/armv8/mmu.h>
 #endif
 #include <watchdog.h>
+#include <dm/pinctrl.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -430,10 +431,10 @@ static bool rpi_is_serial_active(void)
 	 * out whether it is available is to check if the RX pin is muxed.
 	 */
 
-	if (uclass_first_device(UCLASS_GPIO, &dev) || !dev)
+	if (uclass_first_device(UCLASS_PINCTRL, &dev) || !dev)
 		return true;
 
-	if (bcm2835_gpio_get_func_id(dev, serial_gpio) != BCM2835_GPIO_ALT5)
+	if (pinctrl_get_gpio_mux(dev, 0, serial_gpio) != BCM2835_GPIO_ALT5)
 		return false;
 
 	return true;
diff --git a/configs/rpi_0_w_defconfig b/configs/rpi_0_w_defconfig
index 9a6d24be22..e8df255679 100644
--- a/configs/rpi_0_w_defconfig
+++ b/configs/rpi_0_w_defconfig
@@ -25,3 +25,7 @@ CONFIG_SYS_WHITE_ON_BLACK=y
 CONFIG_CONSOLE_SCROLL_LINES=10
 CONFIG_PHYS_TO_BUS=y
 CONFIG_OF_LIBFDT_OVERLAY=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_FULL=y
+# CONFIG_PINCTRL_GENERIC is not set
+CONFIG_PINCTRL_BCM283X=y
diff --git a/configs/rpi_2_defconfig b/configs/rpi_2_defconfig
index c45ffb65af..b30e6e144c 100644
--- a/configs/rpi_2_defconfig
+++ b/configs/rpi_2_defconfig
@@ -32,3 +32,7 @@ CONFIG_SYS_WHITE_ON_BLACK=y
 CONFIG_CONSOLE_SCROLL_LINES=10
 CONFIG_PHYS_TO_BUS=y
 CONFIG_OF_LIBFDT_OVERLAY=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_FULL=y
+# CONFIG_PINCTRL_GENERIC is not set
+CONFIG_PINCTRL_BCM283X=y
diff --git a/configs/rpi_3_32b_defconfig b/configs/rpi_3_32b_defconfig
index f7aed35797..bb40644064 100644
--- a/configs/rpi_3_32b_defconfig
+++ b/configs/rpi_3_32b_defconfig
@@ -34,3 +34,7 @@ CONFIG_SYS_WHITE_ON_BLACK=y
 CONFIG_CONSOLE_SCROLL_LINES=10
 CONFIG_PHYS_TO_BUS=y
 CONFIG_OF_LIBFDT_OVERLAY=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_FULL=y
+# CONFIG_PINCTRL_GENERIC is not set
+CONFIG_PINCTRL_BCM283X=y
diff --git a/configs/rpi_3_defconfig b/configs/rpi_3_defconfig
index 9416e3b8fe..8306bc251d 100644
--- a/configs/rpi_3_defconfig
+++ b/configs/rpi_3_defconfig
@@ -34,3 +34,7 @@ CONFIG_SYS_WHITE_ON_BLACK=y
 CONFIG_CONSOLE_SCROLL_LINES=10
 CONFIG_PHYS_TO_BUS=y
 CONFIG_OF_LIBFDT_OVERLAY=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_FULL=y
+# CONFIG_PINCTRL_GENERIC is not set
+CONFIG_PINCTRL_BCM283X=y
diff --git a/configs/rpi_defconfig b/configs/rpi_defconfig
index 3bfa745c2e..a7a079ddab 100644
--- a/configs/rpi_defconfig
+++ b/configs/rpi_defconfig
@@ -32,3 +32,7 @@ CONFIG_SYS_WHITE_ON_BLACK=y
 CONFIG_CONSOLE_SCROLL_LINES=10
 CONFIG_PHYS_TO_BUS=y
 CONFIG_OF_LIBFDT_OVERLAY=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_FULL=y
+# CONFIG_PINCTRL_GENERIC is not set
+CONFIG_PINCTRL_BCM283X=y
diff --git a/drivers/gpio/bcm2835_gpio.c b/drivers/gpio/bcm2835_gpio.c
index beaa21853a..d68f8df32d 100644
--- a/drivers/gpio/bcm2835_gpio.c
+++ b/drivers/gpio/bcm2835_gpio.c
@@ -7,6 +7,7 @@
 
 #include <common.h>
 #include <dm.h>
+#include <dm/pinctrl.h>
 #include <errno.h>
 #include <asm/gpio.h>
 #include <asm/io.h>
@@ -14,6 +15,7 @@
 
 struct bcm2835_gpios {
 	struct bcm2835_gpio_regs *reg;
+	struct udevice *pinctrl;
 };
 
 static int bcm2835_gpio_direction_input(struct udevice *dev, unsigned gpio)
@@ -29,7 +31,7 @@ static int bcm2835_gpio_direction_input(struct udevice *dev, unsigned gpio)
 	return 0;
 }
 
-static int bcm2835_gpio_direction_output(struct udevice *dev, unsigned gpio,
+static int bcm2835_gpio_direction_output(struct udevice *dev, unsigned int gpio,
 					 int value)
 {
 	struct bcm2835_gpios *gpios = dev_get_priv(dev);
@@ -73,19 +75,12 @@ static int bcm2835_gpio_set_value(struct udevice *dev, unsigned gpio,
 	return 0;
 }
 
-int bcm2835_gpio_get_func_id(struct udevice *dev, unsigned gpio)
-{
-	struct bcm2835_gpios *gpios = dev_get_priv(dev);
-	u32 val;
-
-	val = readl(&gpios->reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);
-
-	return (val >> BCM2835_GPIO_FSEL_SHIFT(gpio) & BCM2835_GPIO_FSEL_MASK);
-}
-
 static int bcm2835_gpio_get_function(struct udevice *dev, unsigned offset)
 {
-	int funcid = bcm2835_gpio_get_func_id(dev, offset);
+	struct bcm2835_gpios *priv = dev_get_priv(dev);
+	int funcid;
+
+	funcid = pinctrl_get_gpio_mux(priv->pinctrl, 0, offset);
 
 	switch (funcid) {
 	case BCM2835_GPIO_OUTPUT:
@@ -97,7 +92,6 @@ static int bcm2835_gpio_get_function(struct udevice *dev, unsigned offset)
 	}
 }
 
-
 static const struct dm_gpio_ops gpio_bcm2835_ops = {
 	.direction_input	= bcm2835_gpio_direction_input,
 	.direction_output	= bcm2835_gpio_direction_output,
@@ -116,15 +110,13 @@ static int bcm2835_gpio_probe(struct udevice *dev)
 	uc_priv->gpio_count = BCM2835_GPIO_COUNT;
 	gpios->reg = (struct bcm2835_gpio_regs *)plat->base;
 
+	/* We know we're spawned by the pinctrl driver */
+	gpios->pinctrl = dev->parent;
+
 	return 0;
 }
 
 #if CONFIG_IS_ENABLED(OF_CONTROL)
-static const struct udevice_id bcm2835_gpio_id[] = {
-	{.compatible = "brcm,bcm2835-gpio"},
-	{}
-};
-
 static int bcm2835_gpio_ofdata_to_platdata(struct udevice *dev)
 {
 	struct bcm2835_gpio_platdata *plat = dev_get_platdata(dev);
@@ -142,7 +134,6 @@ static int bcm2835_gpio_ofdata_to_platdata(struct udevice *dev)
 U_BOOT_DRIVER(gpio_bcm2835) = {
 	.name	= "gpio_bcm2835",
 	.id	= UCLASS_GPIO,
-	.of_match = of_match_ptr(bcm2835_gpio_id),
 	.ofdata_to_platdata = of_match_ptr(bcm2835_gpio_ofdata_to_platdata),
 	.platdata_auto_alloc_size = sizeof(struct bcm2835_gpio_platdata),
 	.ops	= &gpio_bcm2835_ops,
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 7e8e4b0b27..0a4dd3c0cf 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -306,5 +306,6 @@ source "drivers/pinctrl/renesas/Kconfig"
 source "drivers/pinctrl/uniphier/Kconfig"
 source "drivers/pinctrl/exynos/Kconfig"
 source "drivers/pinctrl/mvebu/Kconfig"
+source "drivers/pinctrl/broadcom/Kconfig"
 
 endmenu
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 8c04028dfb..c7135d29f8 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_ARCH_MVEBU)	+= mvebu/
 obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
 obj-$(CONFIG_PINCTRL_STI)	+= pinctrl-sti.o
 obj-$(CONFIG_PINCTRL_STM32)	+= pinctrl_stm32.o
+obj-y				+= broadcom/
diff --git a/drivers/pinctrl/broadcom/Kconfig b/drivers/pinctrl/broadcom/Kconfig
new file mode 100644
index 0000000000..4056782213
--- /dev/null
+++ b/drivers/pinctrl/broadcom/Kconfig
@@ -0,0 +1,7 @@
+config PINCTRL_BCM283X
+	depends on ARCH_BCM283X && PINCTRL_FULL && OF_CONTROL
+	default y
+	bool "Broadcom 283x family pin control driver"
+	help
+	   Support pin multiplexing and pin configuration control on
+	   Broadcom's 283x family of SoCs.
diff --git a/drivers/pinctrl/broadcom/Makefile b/drivers/pinctrl/broadcom/Makefile
new file mode 100644
index 0000000000..2a1e550f88
--- /dev/null
+++ b/drivers/pinctrl/broadcom/Makefile
@@ -0,0 +1,7 @@
+#
+# Copyright (C) 2018 Alexander Graf <agraf@suse.de>
+#
+# SPDX-License-Identifier:	GPL-2.0
+# https://spdx.org/licenses
+
+obj-$(CONFIG_PINCTRL_BCM283X) += pinctrl-bcm283x.o
diff --git a/drivers/pinctrl/broadcom/pinctrl-bcm283x.c b/drivers/pinctrl/broadcom/pinctrl-bcm283x.c
new file mode 100644
index 0000000000..62c35698f5
--- /dev/null
+++ b/drivers/pinctrl/broadcom/pinctrl-bcm283x.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2018 Alexander Graf <agraf@suse.de>
+ *
+ * Based on drivers/pinctrl/mvebu/pinctrl-mvebu.c and
+ *          drivers/gpio/bcm2835_gpio.c
+ *
+ * This driver gets instantiated by the GPIO driver, because both devices
+ * share the same device node.
+ *
+ * SPDX-License-Identifier:	GPL-2.0
+ * https://spdx.org/licenses
+ */
+
+#include <common.h>
+#include <config.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <dm/root.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct bcm283x_pinctrl_priv {
+	u32 *base_reg;
+};
+
+#define MAX_PINS_PER_BANK 16
+
+static void bcm2835_gpio_set_func_id(struct udevice *dev, unsigned int gpio,
+				     int func)
+{
+	struct bcm283x_pinctrl_priv *priv = dev_get_priv(dev);
+	int reg_offset;
+	int field_offset;
+
+	reg_offset = BCM2835_GPIO_FSEL_BANK(gpio);
+	field_offset = BCM2835_GPIO_FSEL_SHIFT(gpio);
+
+	clrsetbits_le32(&priv->base_reg[reg_offset],
+			BCM2835_GPIO_FSEL_MASK << field_offset,
+			(func & BCM2835_GPIO_FSEL_MASK) << field_offset);
+}
+
+static int bcm2835_gpio_get_func_id(struct udevice *dev, unsigned int gpio)
+{
+	struct bcm283x_pinctrl_priv *priv = dev_get_priv(dev);
+	u32 val;
+
+	val = readl(&priv->base_reg[BCM2835_GPIO_FSEL_BANK(gpio)]);
+
+	return (val >> BCM2835_GPIO_FSEL_SHIFT(gpio) & BCM2835_GPIO_FSEL_MASK);
+}
+
+/*
+ * bcm283x_pinctrl_set_state: configure pin functions.
+ * @dev: the pinctrl device to be configured.
+ * @config: the state to be configured.
+ * @return: 0 in success
+ */
+int bcm283x_pinctrl_set_state(struct udevice *dev, struct udevice *config)
+{
+	const void *blob = gd->fdt_blob;
+	int node = dev_of_offset(config);
+	u32 pin_arr[MAX_PINS_PER_BANK];
+	u32 function;
+	int i, pin_count;
+
+	pin_count = fdtdec_get_int_array_count(blob, node,
+					       "brcm,pins",
+					       pin_arr,
+					       ARRAY_SIZE(pin_arr));
+	if (pin_count <= 0) {
+		debug("Failed reading pins array for pinconfig %s (%d)\n",
+		      config->name, pin_count);
+		return -EINVAL;
+	}
+
+	function = fdtdec_get_int(blob, node, "brcm,function", -1);
+	if (function < 0) {
+		debug("Failed reading function for pinconfig %s (%d)\n",
+		      config->name, function);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < pin_count; i++)
+		bcm2835_gpio_set_func_id(dev, pin_arr[i], function);
+
+	return 0;
+}
+
+static int bcm283x_pinctrl_get_gpio_mux(struct udevice *dev, int banknum,
+					int index)
+{
+	if (banknum != 0)
+		return -EINVAL;
+
+	return bcm2835_gpio_get_func_id(dev, index);
+}
+
+static const struct udevice_id bcm2835_pinctrl_id[] = {
+	{.compatible = "brcm,bcm2835-gpio"},
+	{}
+};
+
+int bcm283x_pinctl_probe(struct udevice *dev)
+{
+	struct bcm283x_pinctrl_priv *priv;
+	int ret;
+	struct udevice *pdev;
+
+	priv = dev_get_priv(dev);
+	if (!priv) {
+		debug("%s: Failed to get private\n", __func__);
+		return -EINVAL;
+	}
+
+	priv->base_reg = devfdt_get_addr_ptr(dev);
+	if (priv->base_reg == (void *)FDT_ADDR_T_NONE) {
+		debug("%s: Failed to get base address\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Create GPIO device as well */
+	ret = device_bind(dev, lists_driver_lookup_name("gpio_bcm2835"),
+			  "gpio_bcm2835", NULL, dev_of_offset(dev), &pdev);
+	if (ret)
+		printf("Failed to bind GPIO driver\n");
+
+	return 0;
+}
+
+static struct pinctrl_ops bcm283x_pinctrl_ops = {
+	.set_state	= bcm283x_pinctrl_set_state,
+	.get_gpio_mux	= bcm283x_pinctrl_get_gpio_mux,
+};
+
+U_BOOT_DRIVER(pinctrl_bcm283x) = {
+	.name		= "bcm283x_pinctrl",
+	.id		= UCLASS_PINCTRL,
+	.of_match	= of_match_ptr(bcm2835_pinctrl_id),
+	.priv_auto_alloc_size = sizeof(struct bcm283x_pinctrl_priv),
+	.ops		= &bcm283x_pinctrl_ops,
+	.probe		= bcm283x_pinctl_probe
+};
