Index: head/sys/arm64/rockchip/rk_gpio.c
===================================================================
--- head/sys/arm64/rockchip/rk_gpio.c
+++ head/sys/arm64/rockchip/rk_gpio.c
@@ -51,6 +51,8 @@
 
 #include "gpio_if.h"
 
+#include "fdt_pinctrl_if.h"
+
 #define	RK_GPIO_SWPORTA_DR	0x00	/* Data register */
 #define	RK_GPIO_SWPORTA_DDR	0x04	/* Data direction register */
 
@@ -68,6 +70,9 @@
 
 #define	RK_GPIO_LS_SYNC		0x60	/* Level sensitive syncronization enable register */
 
+#define	RK_GPIO_DEFAULT_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |	\
+    GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)
+
 struct rk_gpio_softc {
 	device_t		sc_dev;
 	device_t		sc_busdev;
@@ -76,6 +81,7 @@
 	bus_space_tag_t		sc_bst;
 	bus_space_handle_t	sc_bsh;
 	clk_t			clk;
+	device_t		pinctrl;
 };
 
 static struct ofw_compat_data compat_data[] = {
@@ -123,6 +129,7 @@
 
 	sc = device_get_softc(dev);
 	sc->sc_dev = dev;
+	sc->pinctrl = device_get_parent(dev);
 
 	node = ofw_bus_get_node(sc->sc_dev);
 	if (!OF_hasprop(node, "gpio-controller"))
@@ -220,18 +227,30 @@
 {
 	struct rk_gpio_softc *sc;
 	uint32_t reg;
+	int rv;
+	bool is_gpio;
 
 	sc = device_get_softc(dev);
 
-	/* XXX Combine this with parent (pinctrl) */
+	rv = FDT_PINCTRL_IS_GPIO(sc->pinctrl, dev, pin, &is_gpio);
+	if (rv != 0)
+		return (rv);
+	if (!is_gpio)
+		return (EINVAL);
+
+	*flags = 0;
+	rv = FDT_PINCTRL_GET_FLAGS(sc->pinctrl, dev, pin, flags);
+	if (rv != 0)
+		return (rv);
+
 	RK_GPIO_LOCK(sc);
 	reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DDR);
 	RK_GPIO_UNLOCK(sc);
 
 	if (reg & (1 << pin))
-		*flags = GPIO_PIN_OUTPUT;
+		*flags |= GPIO_PIN_OUTPUT;
 	else
-		*flags = GPIO_PIN_INPUT;
+		*flags |= GPIO_PIN_INPUT;
 
 	return (0);
 }
@@ -240,9 +259,7 @@
 rk_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
 {
 
-	/* Caps are managed by the pinctrl device */
-	/* XXX Pass this to parent (pinctrl) */
-	*caps = 0;
+	*caps = RK_GPIO_DEFAULT_CAPS;
 	return (0);
 }
 
@@ -251,10 +268,21 @@
 {
 	struct rk_gpio_softc *sc;
 	uint32_t reg;
+	int rv;
+	bool is_gpio;
 
 	sc = device_get_softc(dev);
 
-	/* XXX Combine this with parent (pinctrl) */
+	rv = FDT_PINCTRL_IS_GPIO(sc->pinctrl, dev, pin, &is_gpio);
+	if (rv != 0)
+		return (rv);
+	if (!is_gpio)
+		return (EINVAL);
+
+	rv = FDT_PINCTRL_SET_FLAGS(sc->pinctrl, dev, pin, flags);
+	if (rv != 0)
+		return (rv);
+
 	RK_GPIO_LOCK(sc);
 
 	reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DDR);
Index: head/sys/arm64/rockchip/rk_pinctrl.c
===================================================================
--- head/sys/arm64/rockchip/rk_pinctrl.c
+++ head/sys/arm64/rockchip/rk_pinctrl.c
@@ -55,6 +55,7 @@
 
 #include "gpio_if.h"
 #include "syscon_if.h"
+#include "fdt_pinctrl_if.h"
 
 struct rk_pinctrl_pin_drive {
 	uint32_t	bank;
@@ -101,6 +102,8 @@
 	uint32_t	(*get_pd_offset)(struct rk_pinctrl_softc *, uint32_t);
 	struct syscon	*(*get_syscon)(struct rk_pinctrl_softc *, uint32_t);
 	int		(*parse_bias)(phandle_t, int);
+	int		(*resolv_bias_value)(int, int);
+	int		(*get_bias_value)(int, int);
 };
 
 struct rk_pinctrl_softc {
@@ -109,8 +112,13 @@
 	struct syscon		*grf;
 	struct syscon		*pmu;
 	struct rk_pinctrl_conf	*conf;
+	struct mtx		mtx;
 };
 
+#define	RK_PINCTRL_LOCK(_sc)		mtx_lock_spin(&(_sc)->mtx)
+#define	RK_PINCTRL_UNLOCK(_sc)		mtx_unlock_spin(&(_sc)->mtx)
+#define	RK_PINCTRL_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->mtx, MA_OWNED)
+
 #define	RK_IOMUX(_bank, _subbank, _offset, _nbits)			\
 {									\
 	.bank = _bank,							\
@@ -384,6 +392,32 @@
 	return (-1);
 }
 
+static int
+rk3288_resolv_bias_value(int bank, int bias)
+{
+	int rv = 0;
+
+	if (bias == 1)
+		rv = GPIO_PIN_PULLUP;
+	else if (bias == 2)
+		rv = GPIO_PIN_PULLDOWN;
+
+	return (rv);
+}
+
+static int
+rk3288_get_bias_value(int bank, int bias)
+{
+	int rv = 0;
+
+	if (bias & GPIO_PIN_PULLUP)
+		rv = 1;
+	else if (bias & GPIO_PIN_PULLDOWN)
+		rv = 2;
+
+	return (rv);
+}
+
 struct rk_pinctrl_conf rk3288_conf = {
 	.iomux_conf = rk3288_iomux_bank,
 	.iomux_nbanks = nitems(rk3288_iomux_bank),
@@ -396,6 +430,8 @@
 	.get_pd_offset = rk3288_get_pd_offset,
 	.get_syscon = rk3288_get_syscon,
 	.parse_bias = rk3288_parse_bias,
+	.resolv_bias_value = rk3288_resolv_bias_value,
+	.get_bias_value = rk3288_get_bias_value,
 };
 
 static struct rk_pinctrl_gpio rk3328_gpio_bank[] = {
@@ -540,6 +576,8 @@
 	.get_pd_offset = rk3328_get_pd_offset,
 	.get_syscon = rk3328_get_syscon,
 	.parse_bias = rk3288_parse_bias,
+	.resolv_bias_value = rk3288_resolv_bias_value,
+	.get_bias_value = rk3288_get_bias_value,
 };
 
 static struct rk_pinctrl_gpio rk3399_gpio_bank[] = {
@@ -663,6 +701,58 @@
 	return (-1);
 }
 
+static int
+rk3399_resolv_bias_value(int bank, int bias)
+{
+	int rv = 0;
+
+	switch (bank) {
+	case 0:
+	case 2:
+		if (bias == 3)
+			rv = GPIO_PIN_PULLUP;
+		else if (bias == 1)
+			rv = GPIO_PIN_PULLDOWN;
+		break;
+	case 1:
+	case 3:
+	case 4:
+		if (bias == 1)
+			rv = GPIO_PIN_PULLUP;
+		else if (bias == 2)
+			rv = GPIO_PIN_PULLDOWN;
+		break;
+	}
+
+	return (rv);
+}
+
+static int
+rk3399_get_bias_value(int bank, int bias)
+{
+	int rv = 0;
+
+	switch (bank) {
+	case 0:
+	case 2:
+		if (bias & GPIO_PIN_PULLUP)
+			rv = 3;
+		else if (bias & GPIO_PIN_PULLDOWN)
+			rv = 1;
+		break;
+	case 1:
+	case 3:
+	case 4:
+		if (bias & GPIO_PIN_PULLUP)
+			rv = 1;
+		else if (bias & GPIO_PIN_PULLDOWN)
+			rv = 2;
+		break;
+	}
+
+	return (rv);
+}
+
 struct rk_pinctrl_conf rk3399_conf = {
 	.iomux_conf = rk3399_iomux_bank,
 	.iomux_nbanks = nitems(rk3399_iomux_bank),
@@ -675,6 +765,8 @@
 	.get_pd_offset = rk3399_get_pd_offset,
 	.get_syscon = rk3399_get_syscon,
 	.parse_bias = rk3399_parse_bias,
+	.resolv_bias_value = rk3399_resolv_bias_value,
+	.get_bias_value = rk3399_get_bias_value,
 };
 
 static struct ofw_compat_data compat_data[] = {
@@ -920,8 +1012,189 @@
 	return (0);
 }
 
+static int
+rk_pinctrl_is_gpio_locked(struct rk_pinctrl_softc *sc, struct syscon *syscon,
+  int bank, uint32_t pin, bool *is_gpio)
+{
+	uint32_t subbank, bit, mask, reg;
+	uint32_t pinfunc;
+	int i;
 
+	RK_PINCTRL_LOCK_ASSERT(sc);
+
+	subbank = pin / 8;
+	*is_gpio = false;
+
+	for (i = 0; i < sc->conf->iomux_nbanks; i++)
+		if (sc->conf->iomux_conf[i].bank == bank &&
+		    sc->conf->iomux_conf[i].subbank == subbank)
+			break;
+
+	if (i == sc->conf->iomux_nbanks) {
+		device_printf(sc->dev, "Unknown pin %d in bank %d\n", pin,
+		    bank);
+		return (EINVAL);
+	}
+
+	syscon = sc->conf->get_syscon(sc, bank);
+
+	/* Parse pin function */
+	reg = sc->conf->iomux_conf[i].offset;
+	switch (sc->conf->iomux_conf[i].nbits) {
+	case 4:
+		if ((pin % 8) >= 4)
+			reg += 0x4;
+		bit = (pin % 4) * 4;
+		mask = (0xF << bit);
+		break;
+	case 3:
+		if ((pin % 8) >= 5)
+			reg += 4;
+		bit = (pin % 8 % 5) * 3;
+		mask = (0x7 << bit);
+		break;
+	case 2:
+		bit = (pin % 8) * 2;
+		mask = (0x3 << bit);
+		break;
+	default:
+		device_printf(sc->dev,
+		    "Unknown pin stride width %d in bank %d\n",
+		    sc->conf->iomux_conf[i].nbits, bank);
+		return (EINVAL);
+	}
+	rk_pinctrl_get_fixup(sc, bank, pin, &reg, &mask, &bit);
+
+	reg = SYSCON_READ_4(syscon, reg);
+	pinfunc = (reg & mask) >> bit;
+
+	/* Test if the pin is in gpio mode */
+	if (pinfunc == 0)
+		*is_gpio = true;
+
+	return (0);
+}
+
 static int
+rk_pinctrl_get_bank(struct rk_pinctrl_softc *sc, device_t gpio, int *bank)
+{
+	int i;
+
+	for (i = 0; i < sc->conf->ngpio_bank; i++) {
+		if (sc->conf->gpio_bank[i].gpio_dev == gpio)
+			break;
+	}
+	if (i == sc->conf->ngpio_bank)
+		return (EINVAL);
+
+	*bank = i;
+	return (0);
+}
+
+static int
+rk_pinctrl_is_gpio(device_t pinctrl, device_t gpio, uint32_t pin, bool *is_gpio)
+{
+	struct rk_pinctrl_softc *sc;
+	struct syscon *syscon;
+	int bank;
+	int rv;
+
+	sc = device_get_softc(pinctrl);
+	RK_PINCTRL_LOCK(sc);
+
+	rv = rk_pinctrl_get_bank(sc, gpio, &bank);
+	if (rv != 0)
+		goto done;
+	syscon = sc->conf->get_syscon(sc, bank);
+	rv = rk_pinctrl_is_gpio_locked(sc, syscon, bank, pin, is_gpio);
+
+done:
+	RK_PINCTRL_UNLOCK(sc);
+
+	return (rv);
+}
+
+static int
+rk_pinctrl_get_flags(device_t pinctrl, device_t gpio, uint32_t pin,
+    uint32_t *flags)
+{
+	struct rk_pinctrl_softc *sc;
+	struct syscon *syscon;
+	uint32_t reg, mask, bit;
+	uint32_t bias;
+	int bank;
+	int rv = 0;
+	bool is_gpio;
+
+	sc = device_get_softc(pinctrl);
+	RK_PINCTRL_LOCK(sc);
+
+	rv = rk_pinctrl_get_bank(sc, gpio, &bank);
+	if (rv != 0)
+		goto done;
+	syscon = sc->conf->get_syscon(sc, bank);
+	rv = rk_pinctrl_is_gpio_locked(sc, syscon, bank, pin, &is_gpio);
+	if (rv != 0)
+		goto done;
+	if (!is_gpio) {
+		rv = EINVAL;
+		goto done;
+	}
+	/* Get the pullup/pulldown configuration */
+	reg = sc->conf->get_pd_offset(sc, bank);
+	reg += bank * 0x10 + ((pin / 8) * 0x4);
+	bit = (pin % 8) * 2;
+	mask = (0x3 << bit) << 16;
+	reg = SYSCON_READ_4(syscon, reg);
+	reg = (reg >> bit) & 0x3;
+	bias = sc->conf->resolv_bias_value(bank, reg);
+	*flags = bias;
+
+done:
+	RK_PINCTRL_UNLOCK(sc);
+	return (rv);
+}
+
+static int
+rk_pinctrl_set_flags(device_t pinctrl, device_t gpio, uint32_t pin,
+    uint32_t flags)
+{
+	struct rk_pinctrl_softc *sc;
+	struct syscon *syscon;
+	uint32_t bit, mask, reg;
+	uint32_t bias;
+	int bank;
+	int rv = 0;
+	bool is_gpio;
+
+	sc = device_get_softc(pinctrl);
+	RK_PINCTRL_LOCK(sc);
+
+	rv = rk_pinctrl_get_bank(sc, gpio, &bank);
+	if (rv != 0)
+		goto done;
+	syscon = sc->conf->get_syscon(sc, bank);
+	rv = rk_pinctrl_is_gpio_locked(sc, syscon, bank, pin, &is_gpio);
+	if (rv != 0)
+		goto done;
+	if (!is_gpio) {
+		rv = EINVAL;
+		goto done;
+	}
+	/* Get the pullup/pulldown configuration */
+	reg = sc->conf->get_pd_offset(sc, bank);
+	reg += bank * 0x10 + ((pin / 8) * 0x4);
+	bit = (pin % 8) * 2;
+	mask = (0x3 << bit);
+	bias = sc->conf->get_bias_value(bank, flags);
+	SYSCON_MODIFY_4(syscon, reg, mask, bias << bit | (mask << 16));
+
+done:
+	RK_PINCTRL_UNLOCK(sc);
+	return (rv);
+}
+
+static int
 rk_pinctrl_register_gpio(struct rk_pinctrl_softc *sc, char *gpio_name,
     device_t gpio_dev)
 {
@@ -982,6 +1255,8 @@
 		}
 	}
 
+	mtx_init(&sc->mtx, "rk pinctrl", "pinctrl", MTX_SPIN);
+
 	sc->conf = (struct rk_pinctrl_conf *)ofw_bus_search_compatible(dev,
 	    compat_data)->ocd_data;
 
@@ -1059,6 +1334,9 @@
 
         /* fdt_pinctrl interface */
 	DEVMETHOD(fdt_pinctrl_configure,	rk_pinctrl_configure_pins),
+	DEVMETHOD(fdt_pinctrl_is_gpio,		rk_pinctrl_is_gpio),
+	DEVMETHOD(fdt_pinctrl_get_flags,	rk_pinctrl_get_flags),
+	DEVMETHOD(fdt_pinctrl_set_flags,	rk_pinctrl_set_flags),
 
 	DEVMETHOD_END
 };