diff --git a/drivers/Kconfig b/drivers/Kconfig
index 524d73e64c89634cfae995c5e9628c465c7bb9e5..63c92c594a7dfe2cd0defd13eb3eee219b649368 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -40,6 +40,8 @@ source "drivers/pci/Kconfig"
 
 source "drivers/pcmcia/Kconfig"
 
+source "drivers/pinctrl/Kconfig"
+
 source "drivers/power/Kconfig"
 
 source "drivers/ram/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index a721ec86dfefb580bf14eed7e6b9faab12306d52..9d0a5959a8ef0079a7d4268b89000be663385415 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -1,6 +1,7 @@
 obj-$(CONFIG_$(SPL_)DM)		+= core/
 obj-$(CONFIG_$(SPL_)CLK)	+= clk/
 obj-$(CONFIG_$(SPL_)LED)	+= led/
+obj-$(CONFIG_$(SPL_)PINCTRL)	+= pinctrl/
 obj-$(CONFIG_$(SPL_)RAM)	+= ram/
 
 ifdef CONFIG_SPL_BUILD
diff --git a/drivers/core/device.c b/drivers/core/device.c
index afa4b4fda97809e72f0e671fa6f9307a3a92e88d..a6cd93698f5df210fa5d1d3f5e11f0bef22d5e05 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -15,6 +15,7 @@
 #include <dm/device.h>
 #include <dm/device-internal.h>
 #include <dm/lists.h>
+#include <dm/pinctrl.h>
 #include <dm/platdata.h>
 #include <dm/uclass.h>
 #include <dm/uclass-internal.h>
@@ -286,6 +287,9 @@ int device_probe_child(struct udevice *dev, void *parent_priv)
 
 	dev->flags |= DM_FLAG_ACTIVATED;
 
+	/* continue regardless of the result of pinctrl */
+	pinctrl_select_state(dev, "default");
+
 	ret = uclass_pre_probe_device(dev);
 	if (ret)
 		goto fail;
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..6ac56ebe7d0940ffc61d58d893011f9a705786a3
--- /dev/null
+++ b/drivers/pinctrl/Kconfig
@@ -0,0 +1,101 @@
+#
+# PINCTRL infrastructure and drivers
+#
+
+menu "Pin controllers"
+
+config PINCTRL
+	bool "Support pin controllers"
+	depends on DM
+	help
+	  This enables the basic support for pinctrl framework.  You may want
+	  to enable some more options depending on what you want to do.
+
+config PINCTRL_FULL
+	bool "Support full pin controllers"
+	depends on PINCTRL && OF_CONTROL
+	default y
+	help
+	  This provides Linux-compatible device tree interface for the pinctrl
+	  subsystem.  This feature depends on device tree configuration because
+	  it parses a device tree to look for the pinctrl device which the
+	  peripheral device is associated with.
+
+	  If this option is disabled (it is the only possible choice for non-DT
+	  boards), the pinctrl core provides no systematic mechanism for
+	  identifying peripheral devices, applying needed pinctrl settings.
+	  It is totally up to the implementation of each low-level driver.
+	  You can save memory footprint in return for some limitations.
+
+config PINCTRL_GENERIC
+	bool "Support generic pin controllers"
+	depends on PINCTRL_FULL
+	default y
+	help
+	  Say Y here if you want to use the pinctrl subsystem through the
+	  generic DT interface.  If enabled, some functions become available
+	  to parse common properties such as "pins", "groups", "functions" and
+	  some pin configuration parameters.  It would be easier if you only
+	  need the generic DT interface for pin muxing and pin configuration.
+	  If you need to handle vendor-specific DT properties, you can disable
+	  this option and implement your own set_state callback in the pinctrl
+	  operations.
+
+config PINMUX
+	bool "Support pin multiplexing controllers"
+	depends on PINCTRL_GENERIC
+	default y
+	help
+	  This option enables pin multiplexing through the generic pinctrl
+	  framework.
+
+config PINCONF
+	bool "Support pin configuration controllers"
+	depends on PINCTRL_GENERIC
+	help
+	  This option enables pin configuration through the generic pinctrl
+	  framework.
+
+config SPL_PINCTRL
+	bool "Support pin controlloers in SPL"
+	depends on SPL && SPL_DM
+	help
+	  This option is an SPL-variant of the PINCTRL option.
+	  See the help of PINCTRL for details.
+
+config SPL_PINCTRL_FULL
+	bool "Support full pin controllers in SPL"
+	depends on SPL_PINCTRL && SPL_OF_CONTROL
+	default y
+	help
+	  This option is an SPL-variant of the PINCTRL_FULL option.
+	  See the help of PINCTRL_FULL for details.
+
+config SPL_PINCTRL_GENERIC
+	bool "Support generic pin controllers in SPL"
+	depends on SPL_PINCTRL_FULL
+	default y
+	help
+	  This option is an SPL-variant of the PINCTRL_GENERIC option.
+	  See the help of PINCTRL_GENERIC for details.
+
+config SPL_PINMUX
+	bool "Support pin multiplexing controllers in SPL"
+	depends on SPL_PINCTRL_GENERIC
+	default y
+	help
+	  This option is an SPL-variant of the PINMUX option.
+	  See the help of PINMUX for details.
+
+config SPL_PINCONF
+	bool "Support pin configuration controllers in SPL"
+	depends on SPL_PINCTRL_GENERIC
+	help
+	  This option is an SPL-variant of the PINCONF option.
+	  See the help of PINCONF for details.
+
+if PINCTRL || SPL_PINCTRL
+
+endif
+
+endmenu
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..a713c7db905e0fac680bd5f47c6c5c4a696d650e
--- /dev/null
+++ b/drivers/pinctrl/Makefile
@@ -0,0 +1,2 @@
+obj-y					+= pinctrl-uclass.o
+obj-$(CONFIG_$(SPL_)PINCTRL_GENERIC)	+= pinctrl-generic.o
diff --git a/drivers/pinctrl/pinctrl-generic.c b/drivers/pinctrl/pinctrl-generic.c
new file mode 100644
index 0000000000000000000000000000000000000000..e86b72a8deeac36faa457b048a6fa5a1165a61d9
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-generic.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2015  Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/compat.h>
+#include <dm/device.h>
+#include <dm/pinctrl.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * pinctrl_pin_name_to_selector() - return the pin selector for a pin
+ *
+ * @dev: pin controller device
+ * @pin: the pin name to look up
+ * @return: pin selector, or negative error code on failure
+ */
+static int pinctrl_pin_name_to_selector(struct udevice *dev, const char *pin)
+{
+	const struct pinctrl_ops *ops = pinctrl_get_ops(dev);
+	unsigned npins, selector;
+
+	if (!ops->get_pins_count || !ops->get_pin_name) {
+		dev_dbg(dev, "get_pins_count or get_pin_name missing\n");
+		return -ENOSYS;
+	}
+
+	npins = ops->get_pins_count(dev);
+
+	/* See if this pctldev has this pin */
+	for (selector = 0; selector < npins; selector++) {
+		const char *pname = ops->get_pin_name(dev, selector);
+
+		if (!strcmp(pin, pname))
+			return selector;
+	}
+
+	return -ENOSYS;
+}
+
+/**
+ * pinctrl_group_name_to_selector() - return the group selector for a group
+ *
+ * @dev: pin controller device
+ * @group: the pin group name to look up
+ * @return: pin group selector, or negative error code on failure
+ */
+static int pinctrl_group_name_to_selector(struct udevice *dev,
+					  const char *group)
+{
+	const struct pinctrl_ops *ops = pinctrl_get_ops(dev);
+	unsigned ngroups, selector;
+
+	if (!ops->get_groups_count || !ops->get_group_name) {
+		dev_dbg(dev, "get_groups_count or get_group_name missing\n");
+		return -ENOSYS;
+	}
+
+	ngroups = ops->get_groups_count(dev);
+
+	/* See if this pctldev has this group */
+	for (selector = 0; selector < ngroups; selector++) {
+		const char *gname = ops->get_group_name(dev, selector);
+
+		if (!strcmp(group, gname))
+			return selector;
+	}
+
+	return -ENOSYS;
+}
+
+#if CONFIG_IS_ENABLED(PINMUX)
+/**
+ * pinmux_func_name_to_selector() - return the function selector for a function
+ *
+ * @dev: pin controller device
+ * @function: the function name to look up
+ * @return: function selector, or negative error code on failure
+ */
+static int pinmux_func_name_to_selector(struct udevice *dev,
+					const char *function)
+{
+	const struct pinctrl_ops *ops = pinctrl_get_ops(dev);
+	unsigned nfuncs, selector = 0;
+
+	if (!ops->get_functions_count || !ops->get_function_name) {
+		dev_dbg(dev,
+			"get_functions_count or get_function_name missing\n");
+		return -ENOSYS;
+	}
+
+	nfuncs = ops->get_functions_count(dev);
+
+	/* See if this pctldev has this function */
+	for (selector = 0; selector < nfuncs; selector++) {
+		const char *fname = ops->get_function_name(dev, selector);
+
+		if (!strcmp(function, fname))
+			return selector;
+	}
+
+	return -ENOSYS;
+}
+
+/**
+ * pinmux_enable_setting() - enable pin-mux setting for a certain pin/group
+ *
+ * @dev: pin controller device
+ * @is_group: target of operation (true: pin group, false: pin)
+ * @selector: pin selector or group selector, depending on @is_group
+ * @func_selector: function selector
+ * @return: 0 on success, or negative error code on failure
+ */
+static int pinmux_enable_setting(struct udevice *dev, bool is_group,
+				 unsigned selector, unsigned func_selector)
+{
+	const struct pinctrl_ops *ops = pinctrl_get_ops(dev);
+
+	if (is_group) {
+		if (!ops->pinmux_group_set) {
+			dev_dbg(dev, "pinmux_group_set op missing\n");
+			return -ENOSYS;
+		}
+
+		return ops->pinmux_group_set(dev, selector, func_selector);
+	} else {
+		if (!ops->pinmux_set) {
+			dev_dbg(dev, "pinmux_set op missing\n");
+			return -ENOSYS;
+		}
+		return ops->pinmux_set(dev, selector, func_selector);
+	}
+}
+#else
+static int pinmux_func_name_to_selector(struct udevice *dev,
+					const char *function)
+{
+	return 0;
+}
+
+static int pinmux_enable_setting(struct udevice *dev, bool is_group,
+				 unsigned selector, unsigned func_selector)
+{
+	return 0;
+}
+#endif
+
+#if CONFIG_IS_ENABLED(PINCONF)
+/**
+ * pinconf_prop_name_to_param() - return parameter ID for a property name
+ *
+ * @dev: pin controller device
+ * @property: property name in DTS, such as "bias-pull-up", "slew-rate", etc.
+ * @default_value: return default value in case no value is specified in DTS
+ * @return: return pamater ID, or negative error code on failure
+ */
+static int pinconf_prop_name_to_param(struct udevice *dev,
+				      const char *property, u32 *default_value)
+{
+	const struct pinctrl_ops *ops = pinctrl_get_ops(dev);
+	const struct pinconf_param *p, *end;
+
+	if (!ops->pinconf_num_params || !ops->pinconf_params) {
+		dev_dbg(dev, "pinconf_num_params or pinconf_params missing\n");
+		return -ENOSYS;
+	}
+
+	p = ops->pinconf_params;
+	end = p + ops->pinconf_num_params;
+
+	/* See if this pctldev supports this parameter */
+	for (; p < end; p++) {
+		if (!strcmp(property, p->property)) {
+			*default_value = p->default_value;
+			return p->param;
+		}
+	}
+
+	return -ENOSYS;
+}
+
+/**
+ * pinconf_enable_setting() - apply pin configuration for a certain pin/group
+ *
+ * @dev: pin controller device
+ * @is_group: target of operation (true: pin group, false: pin)
+ * @selector: pin selector or group selector, depending on @is_group
+ * @param: configuration paramter
+ * @argument: argument taken by some configuration parameters
+ * @return: 0 on success, or negative error code on failure
+ */
+static int pinconf_enable_setting(struct udevice *dev, bool is_group,
+				  unsigned selector, unsigned param,
+				  u32 argument)
+{
+	const struct pinctrl_ops *ops = pinctrl_get_ops(dev);
+
+	if (is_group) {
+		if (!ops->pinconf_group_set) {
+			dev_dbg(dev, "pinconf_group_set op missing\n");
+			return -ENOSYS;
+		}
+
+		return ops->pinconf_group_set(dev, selector, param,
+					      argument);
+	} else {
+		if (!ops->pinconf_set) {
+			dev_dbg(dev, "pinconf_set op missing\n");
+			return -ENOSYS;
+		}
+		return ops->pinconf_set(dev, selector, param, argument);
+	}
+}
+#else
+static int pinconf_prop_name_to_param(struct udevice *dev,
+				      const char *property, u32 *default_value)
+{
+	return -ENOSYS;
+}
+
+static int pinconf_enable_setting(struct udevice *dev, bool is_group,
+				  unsigned selector, unsigned param,
+				  u32 argument)
+{
+	return 0;
+}
+#endif
+
+/**
+ * pinctrl_generic_set_state_one() - set state for a certain pin/group
+ * Apply all pin multiplexing and pin configurations specified by @config
+ * for a given pin or pin group.
+ *
+ * @dev: pin controller device
+ * @config: pseudo device pointing to config node
+ * @is_group: target of operation (true: pin group, false: pin)
+ * @selector: pin selector or group selector, depending on @is_group
+ * @return: 0 on success, or negative error code on failure
+ */
+static int pinctrl_generic_set_state_one(struct udevice *dev,
+					 struct udevice *config,
+					 bool is_group, unsigned selector)
+{
+	const void *fdt = gd->fdt_blob;
+	int node_offset = config->of_offset;
+	const char *propname;
+	const void *value;
+	int prop_offset, len, func_selector, param, ret;
+	u32 arg, default_val;
+
+	for (prop_offset = fdt_first_property_offset(fdt, node_offset);
+	     prop_offset > 0;
+	     prop_offset = fdt_next_property_offset(fdt, prop_offset)) {
+		value = fdt_getprop_by_offset(fdt, prop_offset,
+					      &propname, &len);
+		if (!value)
+			return -EINVAL;
+
+		if (!strcmp(propname, "function")) {
+			func_selector = pinmux_func_name_to_selector(dev,
+								     value);
+			if (func_selector < 0)
+				return func_selector;
+			ret = pinmux_enable_setting(dev, is_group,
+						    selector,
+						    func_selector);
+		} else {
+			param = pinconf_prop_name_to_param(dev, propname,
+							   &default_val);
+			if (param < 0)
+				continue; /* just skip unknown properties */
+
+			if (len >= sizeof(fdt32_t))
+				arg = fdt32_to_cpu(*(fdt32_t *)value);
+			else
+				arg = default_val;
+
+			ret = pinconf_enable_setting(dev, is_group,
+						     selector, param, arg);
+		}
+
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * pinctrl_generic_set_state_subnode() - apply all settings in config node
+ *
+ * @dev: pin controller device
+ * @config: pseudo device pointing to config node
+ * @return: 0 on success, or negative error code on failure
+ */
+static int pinctrl_generic_set_state_subnode(struct udevice *dev,
+					     struct udevice *config)
+{
+	const void *fdt = gd->fdt_blob;
+	int node = config->of_offset;
+	const char *subnode_target_type = "pins";
+	bool is_group = false;
+	const char *name;
+	int strings_count, selector, i, ret;
+
+	strings_count = fdt_count_strings(fdt, node, subnode_target_type);
+	if (strings_count < 0) {
+		subnode_target_type = "groups";
+		is_group = true;
+		strings_count = fdt_count_strings(fdt, node,
+						  subnode_target_type);
+		if (strings_count < 0)
+			return -EINVAL;
+	}
+
+	for (i = 0; i < strings_count; i++) {
+		ret = fdt_get_string_index(fdt, node, subnode_target_type,
+					   i, &name);
+		if (ret < 0)
+			return -EINVAL;
+
+		if (is_group)
+			selector = pinctrl_group_name_to_selector(dev, name);
+		else
+			selector = pinctrl_pin_name_to_selector(dev, name);
+		if (selector < 0)
+			return selector;
+
+		ret = pinctrl_generic_set_state_one(dev, config,
+						    is_group, selector);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+int pinctrl_generic_set_state(struct udevice *dev, struct udevice *config)
+{
+	struct udevice *child;
+	int ret;
+
+	ret = pinctrl_generic_set_state_subnode(dev, config);
+	if (ret)
+		return ret;
+
+	for (device_find_first_child(config, &child);
+	     child;
+	     device_find_next_child(&child)) {
+		ret = pinctrl_generic_set_state_subnode(dev, child);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c
new file mode 100644
index 0000000000000000000000000000000000000000..d96c201e8316b7ae8e950eba0d807aa5fe1403f9
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-uclass.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2015  Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <libfdt.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <dm/device.h>
+#include <dm/lists.h>
+#include <dm/pinctrl.h>
+#include <dm/uclass.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if CONFIG_IS_ENABLED(PINCTRL_FULL)
+/**
+ * pinctrl_config_one() - apply pinctrl settings for a single node
+ *
+ * @config: pin configuration node
+ * @return: 0 on success, or negative error code on failure
+ */
+static int pinctrl_config_one(struct udevice *config)
+{
+	struct udevice *pctldev;
+	const struct pinctrl_ops *ops;
+
+	pctldev = config;
+	for (;;) {
+		pctldev = dev_get_parent(pctldev);
+		if (!pctldev) {
+			dev_err(config, "could not find pctldev\n");
+			return -EINVAL;
+		}
+		if (pctldev->uclass->uc_drv->id == UCLASS_PINCTRL)
+			break;
+	}
+
+	ops = pinctrl_get_ops(pctldev);
+	return ops->set_state(pctldev, config);
+}
+
+/**
+ * pinctrl_select_state_full() - full implementation of pinctrl_select_state
+ *
+ * @dev: peripheral device
+ * @statename: state name, like "default"
+ * @return: 0 on success, or negative error code on failure
+ */
+static int pinctrl_select_state_full(struct udevice *dev, const char *statename)
+{
+	const void *fdt = gd->fdt_blob;
+	int node = dev->of_offset;
+	char propname[32]; /* long enough */
+	const fdt32_t *list;
+	uint32_t phandle;
+	int config_node;
+	struct udevice *config;
+	int state, size, i, ret;
+
+	state = fdt_find_string(fdt, node, "pinctrl-names", statename);
+	if (state < 0) {
+		char *end;
+		/*
+		 * If statename is not found in "pinctrl-names",
+		 * assume statename is just the integer state ID.
+		 */
+		state = simple_strtoul(statename, &end, 10);
+		if (*end)
+			return -EINVAL;
+	}
+
+	snprintf(propname, sizeof(propname), "pinctrl-%d", state);
+	list = fdt_getprop(fdt, node, propname, &size);
+	if (!list)
+		return -EINVAL;
+
+	size /= sizeof(*list);
+	for (i = 0; i < size; i++) {
+		phandle = fdt32_to_cpu(*list++);
+
+		config_node = fdt_node_offset_by_phandle(fdt, phandle);
+		if (config_node < 0) {
+			dev_err(dev, "prop %s index %d invalid phandle\n",
+				propname, i);
+			return -EINVAL;
+		}
+		ret = uclass_get_device_by_of_offset(UCLASS_PINCONFIG,
+						     config_node, &config);
+		if (ret)
+			return ret;
+
+		ret = pinctrl_config_one(config);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * pinconfig_post-bind() - post binding for PINCONFIG uclass
+ * Recursively bind its children as pinconfig devices.
+ *
+ * @dev: pinconfig device
+ * @return: 0 on success, or negative error code on failure
+ */
+static int pinconfig_post_bind(struct udevice *dev)
+{
+	const void *fdt = gd->fdt_blob;
+	int offset = dev->of_offset;
+	const char *name;
+	int ret;
+
+	for (offset = fdt_first_subnode(fdt, offset);
+	     offset > 0;
+	     offset = fdt_next_subnode(fdt, offset)) {
+		/*
+		 * If this node has "compatible" property, this is not
+		 * a pin configuration node, but a normal device. skip.
+		 */
+		fdt_get_property(fdt, offset, "compatible", &ret);
+		if (ret >= 0)
+			continue;
+
+		if (ret != -FDT_ERR_NOTFOUND)
+			return ret;
+
+		name = fdt_get_name(fdt, offset, NULL);
+		if (!name)
+			return -EINVAL;
+		ret = device_bind_driver_to_node(dev, "pinconfig", name,
+						 offset, NULL);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+UCLASS_DRIVER(pinconfig) = {
+	.id = UCLASS_PINCONFIG,
+	.post_bind = pinconfig_post_bind,
+	.name = "pinconfig",
+};
+
+U_BOOT_DRIVER(pinconfig_generic) = {
+	.name = "pinconfig",
+	.id = UCLASS_PINCONFIG,
+};
+
+#else
+static int pinctrl_select_state_full(struct udevice *dev, const char *statename)
+{
+	return -ENODEV;
+}
+
+static int pinconfig_post_bind(struct udevice *dev)
+{
+	return 0;
+}
+#endif
+
+/**
+ * pinctrl_select_state_simple() - simple implementation of pinctrl_select_state
+ *
+ * @dev: peripheral device
+ * @return: 0 on success, or negative error code on failure
+ */
+static int pinctrl_select_state_simple(struct udevice *dev)
+{
+	struct udevice *pctldev;
+	struct pinctrl_ops *ops;
+	int ret;
+
+	/*
+	 * For simplicity, assume the first device of PINCTRL uclass
+	 * is the correct one.  This is most likely OK as there is
+	 * usually only one pinctrl device on the system.
+	 */
+	ret = uclass_get_device(UCLASS_PINCTRL, 0, &pctldev);
+	if (ret)
+		return ret;
+
+	ops = pinctrl_get_ops(pctldev);
+	if (!ops->set_state_simple) {
+		dev_dbg(dev, "set_state_simple op missing\n");
+		return -ENOSYS;
+	}
+
+	return ops->set_state_simple(pctldev, dev);
+}
+
+int pinctrl_select_state(struct udevice *dev, const char *statename)
+{
+	/*
+	 * Try full-implemented pinctrl first.
+	 * If it fails or is not implemented, try simple one.
+	 */
+	if (pinctrl_select_state_full(dev, statename))
+		return pinctrl_select_state_simple(dev);
+
+	return 0;
+}
+
+/**
+ * pinconfig_post-bind() - post binding for PINCTRL uclass
+ * Recursively bind child nodes as pinconfig devices in case of full pinctrl.
+ *
+ * @dev: pinctrl device
+ * @return: 0 on success, or negative error code on failure
+ */
+static int pinctrl_post_bind(struct udevice *dev)
+{
+	const struct pinctrl_ops *ops = pinctrl_get_ops(dev);
+
+	if (!ops) {
+		dev_dbg(dev, "ops is not set.  Do not bind.\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * If set_state callback is set, we assume this pinctrl driver is the
+	 * full implementation.  In this case, its child nodes should be bound
+	 * so that peripheral devices can easily search in parent devices
+	 * during later DT-parsing.
+	 */
+	if (ops->set_state)
+		return pinconfig_post_bind(dev);
+
+	return 0;
+}
+
+UCLASS_DRIVER(pinctrl) = {
+	.id = UCLASS_PINCTRL,
+	.post_bind = pinctrl_post_bind,
+	.name = "pinctrl",
+};
diff --git a/include/dm/pinctrl.h b/include/dm/pinctrl.h
new file mode 100644
index 0000000000000000000000000000000000000000..bc6fdb4a1f176c6c385424c1a027b628456e896a
--- /dev/null
+++ b/include/dm/pinctrl.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2015  Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __PINCTRL_H
+#define __PINCTRL_H
+
+/**
+ * struct pinconf_param - pin config parameters
+ *
+ * @property: property name in DT nodes
+ * @param: ID for this config parameter
+ * @default_value: default value for this config parameter used in case
+ *	no value is specified in DT nodes
+ */
+struct pinconf_param {
+	const char * const property;
+	unsigned int param;
+	u32 default_value;
+};
+
+/**
+ * struct pinctrl_ops - pin control operations, to be implemented by
+ * pin controller drivers.
+ *
+ * The @set_state is the only mandatory operation.  You can implement your
+ * pinctrl driver with its own @set_state.  In this case, the other callbacks
+ * are not required.  Otherwise, generic pinctrl framework is also available;
+ * use pinctrl_generic_set_state for @set_state, and implement other operations
+ * depending on your necessity.
+ *
+ * @get_pins_count: return number of selectable named pins available
+ *	in this driver.  (necessary to parse "pins" property in DTS)
+ * @get_pin_name: return the pin name of the pin selector,
+ *	called by the core to figure out which pin it shall do
+ *	operations to.  (necessary to parse "pins" property in DTS)
+ * @get_groups_count: return number of selectable named groups available
+ *	in this driver.  (necessary to parse "groups" property in DTS)
+ * @get_group_name: return the group name of the group selector,
+ *	called by the core to figure out which pin group it shall do
+ *	operations to.  (necessary to parse "groups" property in DTS)
+ * @get_functions_count: return number of selectable named functions available
+ *	in this driver.  (necessary for pin-muxing)
+ * @get_function_name: return the function name of the muxing selector,
+ *	called by the core to figure out which mux setting it shall map a
+ *	certain device to.  (necessary for pin-muxing)
+ * @pinmux_set: enable a certain muxing function with a certain pin.
+ *	The @func_selector selects a certain function whereas @pin_selector
+ *	selects a certain pin to be used. On simple controllers one of them
+ *	may be ignored.  (necessary for pin-muxing against a single pin)
+ * @pinmux_group_set: enable a certain muxing function with a certain pin
+ *	group.  The @func_selector selects a certain function whereas
+ *	@group_selector selects a certain set of pins to be used. On simple
+ *	controllers one of them may be ignored.
+ *	(necessary for pin-muxing against a pin group)
+ * @pinconf_num_params: number of driver-specific parameters to be parsed
+ *	from device trees  (necessary for pin-configuration)
+ * @pinconf_params: list of driver_specific parameters to be parsed from
+ *	device trees  (necessary for pin-configuration)
+ * @pinconf_set: configure an individual pin with a given parameter.
+ *	(necessary for pin-configuration against a single pin)
+ * @pinconf_group_set: configure all pins in a group with a given parameter.
+ *	(necessary for pin-configuration against a pin group)
+ * @set_state: do pinctrl operations specified by @config, a pseudo device
+ *	pointing a config node. (necessary for pinctrl_full)
+ * @set_state_simple: do needed pinctrl operations for a peripherl @periph.
+ *	(necessary for pinctrl_simple)
+ */
+struct pinctrl_ops {
+	int (*get_pins_count)(struct udevice *dev);
+	const char *(*get_pin_name)(struct udevice *dev, unsigned selector);
+	int (*get_groups_count)(struct udevice *dev);
+	const char *(*get_group_name)(struct udevice *dev, unsigned selector);
+	int (*get_functions_count)(struct udevice *dev);
+	const char *(*get_function_name)(struct udevice *dev,
+					 unsigned selector);
+	int (*pinmux_set)(struct udevice *dev, unsigned pin_selector,
+			  unsigned func_selector);
+	int (*pinmux_group_set)(struct udevice *dev, unsigned group_selector,
+				unsigned func_selector);
+	unsigned int pinconf_num_params;
+	const struct pinconf_param *pinconf_params;
+	int (*pinconf_set)(struct udevice *dev, unsigned pin_selector,
+			   unsigned param, unsigned argument);
+	int (*pinconf_group_set)(struct udevice *dev, unsigned group_selector,
+				 unsigned param, unsigned argument);
+	int (*set_state)(struct udevice *dev, struct udevice *config);
+	int (*set_state_simple)(struct udevice *dev, struct udevice *periph);
+};
+
+#define pinctrl_get_ops(dev)	((struct pinctrl_ops *)(dev)->driver->ops)
+
+/**
+ * Generic pin configuration paramters
+ *
+ * @PIN_CONFIG_BIAS_DISABLE: disable any pin bias on the pin, a
+ *	transition from say pull-up to pull-down implies that you disable
+ *	pull-up in the process, this setting disables all biasing.
+ * @PIN_CONFIG_BIAS_HIGH_IMPEDANCE: the pin will be set to a high impedance
+ *	mode, also know as "third-state" (tristate) or "high-Z" or "floating".
+ *	On output pins this effectively disconnects the pin, which is useful
+ *	if for example some other pin is going to drive the signal connected
+ *	to it for a while. Pins used for input are usually always high
+ *	impedance.
+ * @PIN_CONFIG_BIAS_BUS_HOLD: the pin will be set to weakly latch so that it
+ *	weakly drives the last value on a tristate bus, also known as a "bus
+ *	holder", "bus keeper" or "repeater". This allows another device on the
+ *	bus to change the value by driving the bus high or low and switching to
+ *	tristate. The argument is ignored.
+ * @PIN_CONFIG_BIAS_PULL_UP: the pin will be pulled up (usually with high
+ *	impedance to VDD). If the argument is != 0 pull-up is enabled,
+ *	if it is 0, pull-up is total, i.e. the pin is connected to VDD.
+ * @PIN_CONFIG_BIAS_PULL_DOWN: the pin will be pulled down (usually with high
+ *	impedance to GROUND). If the argument is != 0 pull-down is enabled,
+ *	if it is 0, pull-down is total, i.e. the pin is connected to GROUND.
+ * @PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: the pin will be pulled up or down based
+ *	on embedded knowledge of the controller hardware, like current mux
+ *	function. The pull direction and possibly strength too will normally
+ *	be decided completely inside the hardware block and not be readable
+ *	from the kernel side.
+ *	If the argument is != 0 pull up/down is enabled, if it is 0, the
+ *	configuration is ignored. The proper way to disable it is to use
+ *	@PIN_CONFIG_BIAS_DISABLE.
+ * @PIN_CONFIG_DRIVE_PUSH_PULL: the pin will be driven actively high and
+ *	low, this is the most typical case and is typically achieved with two
+ *	active transistors on the output. Setting this config will enable
+ *	push-pull mode, the argument is ignored.
+ * @PIN_CONFIG_DRIVE_OPEN_DRAIN: the pin will be driven with open drain (open
+ *	collector) which means it is usually wired with other output ports
+ *	which are then pulled up with an external resistor. Setting this
+ *	config will enable open drain mode, the argument is ignored.
+ * @PIN_CONFIG_DRIVE_OPEN_SOURCE: the pin will be driven with open source
+ *	(open emitter). Setting this config will enable open source mode, the
+ *	argument is ignored.
+ * @PIN_CONFIG_DRIVE_STRENGTH: the pin will sink or source at most the current
+ *	passed as argument. The argument is in mA.
+ * @PIN_CONFIG_INPUT_ENABLE: enable the pin's input.  Note that this does not
+ *	affect the pin's ability to drive output.  1 enables input, 0 disables
+ *	input.
+ * @PIN_CONFIG_INPUT_SCHMITT_ENABLE: control schmitt-trigger mode on the pin.
+ *      If the argument != 0, schmitt-trigger mode is enabled. If it's 0,
+ *      schmitt-trigger mode is disabled.
+ * @PIN_CONFIG_INPUT_SCHMITT: this will configure an input pin to run in
+ *	schmitt-trigger mode. If the schmitt-trigger has adjustable hysteresis,
+ *	the threshold value is given on a custom format as argument when
+ *	setting pins to this mode.
+ * @PIN_CONFIG_INPUT_DEBOUNCE: this will configure the pin to debounce mode,
+ *	which means it will wait for signals to settle when reading inputs. The
+ *	argument gives the debounce time in usecs. Setting the
+ *	argument to zero turns debouncing off.
+ * @PIN_CONFIG_POWER_SOURCE: if the pin can select between different power
+ *	supplies, the argument to this parameter (on a custom format) tells
+ *	the driver which alternative power source to use.
+ * @PIN_CONFIG_SLEW_RATE: if the pin can select slew rate, the argument to
+ *	this parameter (on a custom format) tells the driver which alternative
+ *	slew rate to use.
+ * @PIN_CONFIG_LOW_POWER_MODE: this will configure the pin for low power
+ *	operation, if several modes of operation are supported these can be
+ *	passed in the argument on a custom form, else just use argument 1
+ *	to indicate low power mode, argument 0 turns low power mode off.
+ * @PIN_CONFIG_OUTPUT: this will configure the pin as an output. Use argument
+ *	1 to indicate high level, argument 0 to indicate low level. (Please
+ *	see Documentation/pinctrl.txt, section "GPIO mode pitfalls" for a
+ *	discussion around this parameter.)
+ * @PIN_CONFIG_END: this is the last enumerator for pin configurations, if
+ *	you need to pass in custom configurations to the pin controller, use
+ *	PIN_CONFIG_END+1 as the base offset.
+ */
+#define PIN_CONFIG_BIAS_DISABLE			0
+#define PIN_CONFIG_BIAS_HIGH_IMPEDANCE		1
+#define PIN_CONFIG_BIAS_BUS_HOLD		2
+#define PIN_CONFIG_BIAS_PULL_UP			3
+#define PIN_CONFIG_BIAS_PULL_DOWN		4
+#define PIN_CONFIG_BIAS_PULL_PIN_DEFAULT	5
+#define PIN_CONFIG_DRIVE_PUSH_PULL		6
+#define PIN_CONFIG_DRIVE_OPEN_DRAIN		7
+#define PIN_CONFIG_DRIVE_OPEN_SOURCE		8
+#define PIN_CONFIG_DRIVE_STRENGTH		9
+#define PIN_CONFIG_INPUT_ENABLE			10
+#define PIN_CONFIG_INPUT_SCHMITT_ENABLE		11
+#define PIN_CONFIG_INPUT_SCHMITT		12
+#define PIN_CONFIG_INPUT_DEBOUNCE		13
+#define PIN_CONFIG_POWER_SOURCE			14
+#define PIN_CONFIG_SLEW_RATE			15
+#define PIN_CONFIG_LOW_POWER_MODE		16
+#define PIN_CONFIG_OUTPUT			17
+#define PIN_CONFIG_END				0x7FFF
+
+#if CONFIG_IS_ENABLED(PINCTRL_GENERIC)
+/**
+ * pinctrl_generic_set_state() - generic set_state operation
+ * Parse the DT node of @config and its children and handle generic properties
+ * such as "pins", "groups", "functions", and pin configuration parameters.
+ *
+ * @pctldev: pinctrl device
+ * @config: config device (pseudo device), pointing a config node in DTS
+ * @return: 0 on success, or negative error code on failure
+ */
+int pinctrl_generic_set_state(struct udevice *pctldev, struct udevice *config);
+#else
+static inline int pinctrl_generic_set_state(struct udevice *pctldev,
+					    struct udevice *config)
+{
+	return -EINVAL;
+}
+#endif
+
+#if CONFIG_IS_ENABLED(PINCTRL)
+/**
+ * pinctrl_select_state() - set a device to a given state
+ *
+ * @dev: peripheral device
+ * @statename: state name, like "default"
+ * @return: 0 on success, or negative error code on failure
+ */
+int pinctrl_select_state(struct udevice *dev, const char *statename);
+#else
+static inline int pinctrl_select_state(struct udevice *dev,
+				       const char *statename)
+{
+	return -EINVAL;
+}
+#endif
+
+#endif /* __PINCTRL_H */
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 3eff895ab7abb4ceab65dd0105b085b3e7790798..1eeec74964356fe9a4bbce9195d8fcc4693bf649 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -44,6 +44,8 @@ enum uclass_id {
 	UCLASS_PCH,		/* x86 platform controller hub */
 	UCLASS_PCI,		/* PCI bus */
 	UCLASS_PCI_GENERIC,	/* Generic PCI bus device */
+	UCLASS_PINCTRL,		/* Pinctrl (pin muxing/configuration) device */
+	UCLASS_PINCONFIG,	/* Pin configuration node device */
 	UCLASS_PMIC,		/* PMIC I/O device */
 	UCLASS_REGULATOR,	/* Regulator device */
 	UCLASS_RESET,		/* Reset device */