summary refs log tree commit diff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/bus/Kconfig21
-rw-r--r--drivers/bus/Makefile8
-rw-r--r--drivers/bus/omap-ocp2scp.c88
-rw-r--r--drivers/bus/omap_l3_noc.c267
-rw-r--r--drivers/bus/omap_l3_noc.h176
-rw-r--r--drivers/bus/omap_l3_smx.c297
-rw-r--r--drivers/bus/omap_l3_smx.h338
-rw-r--r--drivers/char/nwflash.c34
-rw-r--r--drivers/gpio/gpio-samsung.c21
-rw-r--r--drivers/i2c/busses/i2c-tegra.c130
-rw-r--r--drivers/leds/Kconfig10
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/ledtrig-cpu.c163
-rw-r--r--drivers/mtd/nand/Kconfig40
-rw-r--r--drivers/pinctrl/Kconfig9
-rw-r--r--drivers/pinctrl/Makefile2
-rw-r--r--drivers/pinctrl/pinctrl-exynos.c579
-rw-r--r--drivers/pinctrl/pinctrl-exynos.h218
-rw-r--r--drivers/pinctrl/pinctrl-samsung.c888
-rw-r--r--drivers/pinctrl/pinctrl-samsung.h239
22 files changed, 3420 insertions, 112 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 36d3daa19a74..dbdefa3fe775 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -2,6 +2,8 @@ menu "Device Drivers"
 
 source "drivers/base/Kconfig"
 
+source "drivers/bus/Kconfig"
+
 source "drivers/connector/Kconfig"
 
 source "drivers/mtd/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 8c30e73cd94c..acb48fa4531c 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -6,6 +6,7 @@
 #
 
 obj-y				+= irqchip/
+obj-y				+= bus/
 
 # GPIO must come after pinctrl as gpios may need to mux pins etc
 obj-y				+= pinctrl/
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
new file mode 100644
index 000000000000..bbec35d21fe5
--- /dev/null
+++ b/drivers/bus/Kconfig
@@ -0,0 +1,21 @@
+#
+# Bus Devices
+#
+
+menu "Bus devices"
+
+config OMAP_OCP2SCP
+	tristate "OMAP OCP2SCP DRIVER"
+	help
+	  Driver to enable ocp2scp module which transforms ocp interface
+	  protocol to scp protocol. In OMAP4, USB PHY is connected via
+	  OCP2SCP and in OMAP5, both USB PHY and SATA PHY is connected via
+	  OCP2SCP.
+
+config OMAP_INTERCONNECT
+	tristate "OMAP INTERCONNECT DRIVER"
+	depends on ARCH_OMAP2PLUS
+
+	help
+	  Driver to enable OMAP interconnect error handling driver.
+endmenu
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
new file mode 100644
index 000000000000..45d997c85453
--- /dev/null
+++ b/drivers/bus/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the bus drivers.
+#
+
+obj-$(CONFIG_OMAP_OCP2SCP)	+= omap-ocp2scp.o
+
+# Interconnect bus driver for OMAP SoCs.
+obj-$(CONFIG_OMAP_INTERCONNECT)	+= omap_l3_smx.o omap_l3_noc.o
diff --git a/drivers/bus/omap-ocp2scp.c b/drivers/bus/omap-ocp2scp.c
new file mode 100644
index 000000000000..ff63560b8467
--- /dev/null
+++ b/drivers/bus/omap-ocp2scp.c
@@ -0,0 +1,88 @@
+/*
+ * omap-ocp2scp.c - transform ocp interface protocol to scp protocol
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+static int ocp2scp_remove_devices(struct device *dev, void *c)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+
+	platform_device_unregister(pdev);
+
+	return 0;
+}
+
+static int __devinit omap_ocp2scp_probe(struct platform_device *pdev)
+{
+	int			ret;
+	struct device_node	*np = pdev->dev.of_node;
+
+	if (np) {
+		ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to add resources for ocp2scp child\n");
+			goto err0;
+		}
+	}
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+
+err0:
+	device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices);
+
+	return ret;
+}
+
+static int __devexit omap_ocp2scp_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+	device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id omap_ocp2scp_id_table[] = {
+	{ .compatible = "ti,omap-ocp2scp" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, omap_ocp2scp_id_table);
+#endif
+
+static struct platform_driver omap_ocp2scp_driver = {
+	.probe		= omap_ocp2scp_probe,
+	.remove		= __devexit_p(omap_ocp2scp_remove),
+	.driver		= {
+		.name	= "omap-ocp2scp",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(omap_ocp2scp_id_table),
+	},
+};
+
+module_platform_driver(omap_ocp2scp_driver);
+
+MODULE_ALIAS("platform: omap-ocp2scp");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("OMAP OCP2SCP driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c
new file mode 100644
index 000000000000..44b2b3e57882
--- /dev/null
+++ b/drivers/bus/omap_l3_noc.c
@@ -0,0 +1,267 @@
+/*
+ * OMAP4XXX L3 Interconnect error handling driver
+ *
+ * Copyright (C) 2011 Texas Corporation
+ *	Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *	Sricharan <r.sricharan@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "soc.h"
+#include "omap_l3_noc.h"
+
+/*
+ * Interrupt Handler for L3 error detection.
+ *	1) Identify the L3 clockdomain partition to which the error belongs to.
+ *	2) Identify the slave where the error information is logged
+ *	3) Print the logged information.
+ *	4) Add dump stack to provide kernel trace.
+ *
+ * Two Types of errors :
+ *	1) Custom errors in L3 :
+ *		Target like DMM/FW/EMIF generates SRESP=ERR error
+ *	2) Standard L3 error:
+ *		- Unsupported CMD.
+ *			L3 tries to access target while it is idle
+ *		- OCP disconnect.
+ *		- Address hole error:
+ *			If DSS/ISS/FDIF/USBHOSTFS access a target where they
+ *			do not have connectivity, the error is logged in
+ *			their default target which is DMM2.
+ *
+ *	On High Secure devices, firewall errors are possible and those
+ *	can be trapped as well. But the trapping is implemented as part
+ *	secure software and hence need not be implemented here.
+ */
+static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
+{
+
+	struct omap4_l3 *l3 = _l3;
+	int inttype, i, k;
+	int err_src = 0;
+	u32 std_err_main, err_reg, clear, masterid;
+	void __iomem *base, *l3_targ_base;
+	char *target_name, *master_name = "UN IDENTIFIED";
+
+	/* Get the Type of interrupt */
+	inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR;
+
+	for (i = 0; i < L3_MODULES; i++) {
+		/*
+		 * Read the regerr register of the clock domain
+		 * to determine the source
+		 */
+		base = l3->l3_base[i];
+		err_reg = __raw_readl(base + l3_flagmux[i] +
+					+ L3_FLAGMUX_REGERR0 + (inttype << 3));
+
+		/* Get the corresponding error and analyse */
+		if (err_reg) {
+			/* Identify the source from control status register */
+			err_src = __ffs(err_reg);
+
+			/* Read the stderrlog_main_source from clk domain */
+			l3_targ_base = base + *(l3_targ[i] + err_src);
+			std_err_main =  __raw_readl(l3_targ_base +
+					L3_TARG_STDERRLOG_MAIN);
+			masterid = __raw_readl(l3_targ_base +
+					L3_TARG_STDERRLOG_MSTADDR);
+
+			switch (std_err_main & CUSTOM_ERROR) {
+			case STANDARD_ERROR:
+				target_name =
+					l3_targ_inst_name[i][err_src];
+				WARN(true, "L3 standard error: TARGET:%s at address 0x%x\n",
+					target_name,
+					__raw_readl(l3_targ_base +
+						L3_TARG_STDERRLOG_SLVOFSLSB));
+				/* clear the std error log*/
+				clear = std_err_main | CLEAR_STDERR_LOG;
+				writel(clear, l3_targ_base +
+					L3_TARG_STDERRLOG_MAIN);
+				break;
+
+			case CUSTOM_ERROR:
+				target_name =
+					l3_targ_inst_name[i][err_src];
+				for (k = 0; k < NUM_OF_L3_MASTERS; k++) {
+					if (masterid == l3_masters[k].id)
+						master_name =
+							l3_masters[k].name;
+				}
+				WARN(true, "L3 custom error: MASTER:%s TARGET:%s\n",
+					master_name, target_name);
+				/* clear the std error log*/
+				clear = std_err_main | CLEAR_STDERR_LOG;
+				writel(clear, l3_targ_base +
+					L3_TARG_STDERRLOG_MAIN);
+				break;
+
+			default:
+				/* Nothing to be handled here as of now */
+				break;
+			}
+		/* Error found so break the for loop */
+		break;
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+static int __devinit omap4_l3_probe(struct platform_device *pdev)
+{
+	static struct omap4_l3 *l3;
+	struct resource	*res;
+	int ret;
+
+	l3 = kzalloc(sizeof(*l3), GFP_KERNEL);
+	if (!l3)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, l3);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "couldn't find resource 0\n");
+		ret = -ENODEV;
+		goto err0;
+	}
+
+	l3->l3_base[0] = ioremap(res->start, resource_size(res));
+	if (!l3->l3_base[0]) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto err0;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		dev_err(&pdev->dev, "couldn't find resource 1\n");
+		ret = -ENODEV;
+		goto err1;
+	}
+
+	l3->l3_base[1] = ioremap(res->start, resource_size(res));
+	if (!l3->l3_base[1]) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto err1;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	if (!res) {
+		dev_err(&pdev->dev, "couldn't find resource 2\n");
+		ret = -ENODEV;
+		goto err2;
+	}
+
+	l3->l3_base[2] = ioremap(res->start, resource_size(res));
+	if (!l3->l3_base[2]) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto err2;
+	}
+
+	/*
+	 * Setup interrupt Handlers
+	 */
+	l3->debug_irq = platform_get_irq(pdev, 0);
+	ret = request_irq(l3->debug_irq,
+			l3_interrupt_handler,
+			IRQF_DISABLED, "l3-dbg-irq", l3);
+	if (ret) {
+		pr_crit("L3: request_irq failed to register for 0x%x\n",
+						l3->debug_irq);
+		goto err3;
+	}
+
+	l3->app_irq = platform_get_irq(pdev, 1);
+	ret = request_irq(l3->app_irq,
+			l3_interrupt_handler,
+			IRQF_DISABLED, "l3-app-irq", l3);
+	if (ret) {
+		pr_crit("L3: request_irq failed to register for 0x%x\n",
+						l3->app_irq);
+		goto err4;
+	}
+
+	return 0;
+
+err4:
+	free_irq(l3->debug_irq, l3);
+err3:
+	iounmap(l3->l3_base[2]);
+err2:
+	iounmap(l3->l3_base[1]);
+err1:
+	iounmap(l3->l3_base[0]);
+err0:
+	kfree(l3);
+	return ret;
+}
+
+static int __devexit omap4_l3_remove(struct platform_device *pdev)
+{
+	struct omap4_l3 *l3 = platform_get_drvdata(pdev);
+
+	free_irq(l3->app_irq, l3);
+	free_irq(l3->debug_irq, l3);
+	iounmap(l3->l3_base[0]);
+	iounmap(l3->l3_base[1]);
+	iounmap(l3->l3_base[2]);
+	kfree(l3);
+
+	return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id l3_noc_match[] = {
+	{.compatible = "ti,omap4-l3-noc", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, l3_noc_match);
+#else
+#define l3_noc_match NULL
+#endif
+
+static struct platform_driver omap4_l3_driver = {
+	.probe		= omap4_l3_probe,
+	.remove		= __devexit_p(omap4_l3_remove),
+	.driver		= {
+		.name		= "omap_l3_noc",
+		.owner		= THIS_MODULE,
+		.of_match_table = l3_noc_match,
+	},
+};
+
+static int __init omap4_l3_init(void)
+{
+	return platform_driver_register(&omap4_l3_driver);
+}
+postcore_initcall_sync(omap4_l3_init);
+
+static void __exit omap4_l3_exit(void)
+{
+	platform_driver_unregister(&omap4_l3_driver);
+}
+module_exit(omap4_l3_exit);
diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h
new file mode 100644
index 000000000000..a6ce34dc4814
--- /dev/null
+++ b/drivers/bus/omap_l3_noc.h
@@ -0,0 +1,176 @@
+/*
+ * OMAP4XXX L3 Interconnect  error handling driver header
+ *
+ * Copyright (C) 2011 Texas Corporation
+ *	Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *	sricharan <r.sricharan@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef __ARCH_ARM_MACH_OMAP2_L3_INTERCONNECT_3XXX_H
+#define __ARCH_ARM_MACH_OMAP2_L3_INTERCONNECT_3XXX_H
+
+#define L3_MODULES			3
+#define CLEAR_STDERR_LOG		(1 << 31)
+#define CUSTOM_ERROR			0x2
+#define STANDARD_ERROR			0x0
+#define INBAND_ERROR			0x0
+#define L3_APPLICATION_ERROR		0x0
+#define L3_DEBUG_ERROR			0x1
+
+/* L3 TARG register offsets */
+#define L3_TARG_STDERRLOG_MAIN		0x48
+#define L3_TARG_STDERRLOG_SLVOFSLSB	0x5c
+#define L3_TARG_STDERRLOG_MSTADDR	0x68
+#define L3_FLAGMUX_REGERR0		0xc
+
+#define NUM_OF_L3_MASTERS	(sizeof(l3_masters)/sizeof(l3_masters[0]))
+
+static u32 l3_flagmux[L3_MODULES] = {
+	0x500,
+	0x1000,
+	0X0200
+};
+
+/* L3 Target standard Error register offsets */
+static u32 l3_targ_inst_clk1[] = {
+	0x100, /* DMM1 */
+	0x200, /* DMM2 */
+	0x300, /* ABE */
+	0x400, /* L4CFG */
+	0x600,  /* CLK2 PWR DISC */
+	0x0,	/* Host CLK1 */
+	0x900	/* L4 Wakeup */
+};
+
+static u32 l3_targ_inst_clk2[] = {
+	0x500, /* CORTEX M3 */
+	0x300, /* DSS */
+	0x100, /* GPMC */
+	0x400, /* ISS */
+	0x700, /* IVAHD */
+	0xD00, /* missing in TRM  corresponds to AES1*/
+	0x900, /* L4 PER0*/
+	0x200, /* OCMRAM */
+	0x100, /* missing in TRM corresponds to GPMC sERROR*/
+	0x600, /* SGX */
+	0x800, /* SL2 */
+	0x1600, /* C2C */
+	0x1100,	/* missing in TRM corresponds PWR DISC CLK1*/
+	0xF00, /* missing in TRM corrsponds to SHA1*/
+	0xE00, /* missing in TRM corresponds to AES2*/
+	0xC00, /* L4 PER3 */
+	0xA00, /* L4 PER1*/
+	0xB00, /* L4 PER2*/
+	0x0, /* HOST CLK2 */
+	0x1800, /* CAL */
+	0x1700 /* LLI */
+};
+
+static u32 l3_targ_inst_clk3[] = {
+	0x0100	/* EMUSS */,
+	0x0300, /* DEBUGSS_CT_TBR */
+	0x0 /* HOST CLK3 */
+};
+
+static struct l3_masters_data {
+	u32 id;
+	char name[10];
+} l3_masters[] = {
+	{ 0x0 , "MPU"},
+	{ 0x10, "CS_ADP"},
+	{ 0x14, "xxx"},
+	{ 0x20, "DSP"},
+	{ 0x30, "IVAHD"},
+	{ 0x40, "ISS"},
+	{ 0x44, "DucatiM3"},
+	{ 0x48, "FaceDetect"},
+	{ 0x50, "SDMA_Rd"},
+	{ 0x54, "SDMA_Wr"},
+	{ 0x58, "xxx"},
+	{ 0x5C, "xxx"},
+	{ 0x60, "SGX"},
+	{ 0x70, "DSS"},
+	{ 0x80, "C2C"},
+	{ 0x88, "xxx"},
+	{ 0x8C, "xxx"},
+	{ 0x90, "HSI"},
+	{ 0xA0, "MMC1"},
+	{ 0xA4, "MMC2"},
+	{ 0xA8, "MMC6"},
+	{ 0xB0, "UNIPRO1"},
+	{ 0xC0, "USBHOSTHS"},
+	{ 0xC4, "USBOTGHS"},
+	{ 0xC8, "USBHOSTFS"}
+};
+
+static char *l3_targ_inst_name[L3_MODULES][21] = {
+	{
+		"DMM1",
+		"DMM2",
+		"ABE",
+		"L4CFG",
+		"CLK2 PWR DISC",
+		"HOST CLK1",
+		"L4 WAKEUP"
+	},
+	{
+		"CORTEX M3" ,
+		"DSS ",
+		"GPMC ",
+		"ISS ",
+		"IVAHD ",
+		"AES1",
+		"L4 PER0",
+		"OCMRAM ",
+		"GPMC sERROR",
+		"SGX ",
+		"SL2 ",
+		"C2C ",
+		"PWR DISC CLK1",
+		"SHA1",
+		"AES2",
+		"L4 PER3",
+		"L4 PER1",
+		"L4 PER2",
+		"HOST CLK2",
+		"CAL",
+		"LLI"
+	},
+	{
+		"EMUSS",
+		"DEBUG SOURCE",
+		"HOST CLK3"
+	},
+};
+
+static u32 *l3_targ[L3_MODULES] = {
+	l3_targ_inst_clk1,
+	l3_targ_inst_clk2,
+	l3_targ_inst_clk3,
+};
+
+struct omap4_l3 {
+	struct device *dev;
+	struct clk *ick;
+
+	/* memory base */
+	void __iomem *l3_base[L3_MODULES];
+
+	int debug_irq;
+	int app_irq;
+};
+#endif
diff --git a/drivers/bus/omap_l3_smx.c b/drivers/bus/omap_l3_smx.c
new file mode 100644
index 000000000000..acc216491b8a
--- /dev/null
+++ b/drivers/bus/omap_l3_smx.c
@@ -0,0 +1,297 @@
+/*
+ * OMAP3XXX L3 Interconnect Driver
+ *
+ * Copyright (C) 2011 Texas Corporation
+ *	Felipe Balbi <balbi@ti.com>
+ *	Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *	Sricharan <r.sricharan@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include "omap_l3_smx.h"
+
+static inline u64 omap3_l3_readll(void __iomem *base, u16 reg)
+{
+	return __raw_readll(base + reg);
+}
+
+static inline void omap3_l3_writell(void __iomem *base, u16 reg, u64 value)
+{
+	__raw_writell(value, base + reg);
+}
+
+static inline enum omap3_l3_code omap3_l3_decode_error_code(u64 error)
+{
+	return (error & 0x0f000000) >> L3_ERROR_LOG_CODE;
+}
+
+static inline u32 omap3_l3_decode_addr(u64 error_addr)
+{
+	return error_addr & 0xffffffff;
+}
+
+static inline unsigned omap3_l3_decode_cmd(u64 error)
+{
+	return (error & 0x07) >> L3_ERROR_LOG_CMD;
+}
+
+static inline enum omap3_l3_initiator_id omap3_l3_decode_initid(u64 error)
+{
+	return (error & 0xff00) >> L3_ERROR_LOG_INITID;
+}
+
+static inline unsigned omap3_l3_decode_req_info(u64 error)
+{
+	return (error >> 32) & 0xffff;
+}
+
+static char *omap3_l3_code_string(u8 code)
+{
+	switch (code) {
+	case OMAP_L3_CODE_NOERROR:
+		return "No Error";
+	case OMAP_L3_CODE_UNSUP_CMD:
+		return "Unsupported Command";
+	case OMAP_L3_CODE_ADDR_HOLE:
+		return "Address Hole";
+	case OMAP_L3_CODE_PROTECT_VIOLATION:
+		return "Protection Violation";
+	case OMAP_L3_CODE_IN_BAND_ERR:
+		return "In-band Error";
+	case OMAP_L3_CODE_REQ_TOUT_NOT_ACCEPT:
+		return "Request Timeout Not Accepted";
+	case OMAP_L3_CODE_REQ_TOUT_NO_RESP:
+		return "Request Timeout, no response";
+	default:
+		return "UNKNOWN error";
+	}
+}
+
+static char *omap3_l3_initiator_string(u8 initid)
+{
+	switch (initid) {
+	case OMAP_L3_LCD:
+		return "LCD";
+	case OMAP_L3_SAD2D:
+		return "SAD2D";
+	case OMAP_L3_IA_MPU_SS_1:
+	case OMAP_L3_IA_MPU_SS_2:
+	case OMAP_L3_IA_MPU_SS_3:
+	case OMAP_L3_IA_MPU_SS_4:
+	case OMAP_L3_IA_MPU_SS_5:
+		return "MPU";
+	case OMAP_L3_IA_IVA_SS_1:
+	case OMAP_L3_IA_IVA_SS_2:
+	case OMAP_L3_IA_IVA_SS_3:
+		return "IVA_SS";
+	case OMAP_L3_IA_IVA_SS_DMA_1:
+	case OMAP_L3_IA_IVA_SS_DMA_2:
+	case OMAP_L3_IA_IVA_SS_DMA_3:
+	case OMAP_L3_IA_IVA_SS_DMA_4:
+	case OMAP_L3_IA_IVA_SS_DMA_5:
+	case OMAP_L3_IA_IVA_SS_DMA_6:
+		return "IVA_SS_DMA";
+	case OMAP_L3_IA_SGX:
+		return "SGX";
+	case OMAP_L3_IA_CAM_1:
+	case OMAP_L3_IA_CAM_2:
+	case OMAP_L3_IA_CAM_3:
+		return "CAM";
+	case OMAP_L3_IA_DAP:
+		return "DAP";
+	case OMAP_L3_SDMA_WR_1:
+	case OMAP_L3_SDMA_WR_2:
+		return "SDMA_WR";
+	case OMAP_L3_SDMA_RD_1:
+	case OMAP_L3_SDMA_RD_2:
+	case OMAP_L3_SDMA_RD_3:
+	case OMAP_L3_SDMA_RD_4:
+		return "SDMA_RD";
+	case OMAP_L3_USBOTG:
+		return "USB_OTG";
+	case OMAP_L3_USBHOST:
+		return "USB_HOST";
+	default:
+		return "UNKNOWN Initiator";
+	}
+}
+
+/*
+ * omap3_l3_block_irq - handles a register block's irq
+ * @l3: struct omap3_l3 *
+ * @base: register block base address
+ * @error: L3_ERROR_LOG register of our block
+ *
+ * Called in hard-irq context. Caller should take care of locking
+ *
+ * OMAP36xx TRM gives, on page 2001, Figure 9-10, the Typical Error
+ * Analysis Sequence, we are following that sequence here, please
+ * refer to that Figure for more information on the subject.
+ */
+static irqreturn_t omap3_l3_block_irq(struct omap3_l3 *l3,
+					u64 error, int error_addr)
+{
+	u8 code = omap3_l3_decode_error_code(error);
+	u8 initid = omap3_l3_decode_initid(error);
+	u8 multi = error & L3_ERROR_LOG_MULTI;
+	u32 address = omap3_l3_decode_addr(error_addr);
+
+	pr_err("%s seen by %s %s at address %x\n",
+			omap3_l3_code_string(code),
+			omap3_l3_initiator_string(initid),
+			multi ? "Multiple Errors" : "", address);
+	WARN_ON(1);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t omap3_l3_app_irq(int irq, void *_l3)
+{
+	struct omap3_l3 *l3 = _l3;
+	u64 status, clear;
+	u64 error;
+	u64 error_addr;
+	u64 err_source = 0;
+	void __iomem *base;
+	int int_type;
+	irqreturn_t ret = IRQ_NONE;
+
+	int_type = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR;
+	if (!int_type) {
+		status = omap3_l3_readll(l3->rt, L3_SI_FLAG_STATUS_0);
+		/*
+		 * if we have a timeout error, there's nothing we can
+		 * do besides rebooting the board. So let's BUG on any
+		 * of such errors and handle the others. timeout error
+		 * is severe and not expected to occur.
+		 */
+		BUG_ON(status & L3_STATUS_0_TIMEOUT_MASK);
+	} else {
+		status = omap3_l3_readll(l3->rt, L3_SI_FLAG_STATUS_1);
+		/* No timeout error for debug sources */
+	}
+
+	/* identify the error source */
+	err_source = __ffs(status);
+
+	base = l3->rt + omap3_l3_bases[int_type][err_source];
+	error = omap3_l3_readll(base, L3_ERROR_LOG);
+	if (error) {
+		error_addr = omap3_l3_readll(base, L3_ERROR_LOG_ADDR);
+		ret |= omap3_l3_block_irq(l3, error, error_addr);
+	}
+
+	/* Clear the status register */
+	clear = (L3_AGENT_STATUS_CLEAR_IA << int_type) |
+		L3_AGENT_STATUS_CLEAR_TA;
+	omap3_l3_writell(base, L3_AGENT_STATUS, clear);
+
+	/* clear the error log register */
+	omap3_l3_writell(base, L3_ERROR_LOG, error);
+
+	return ret;
+}
+
+static int __init omap3_l3_probe(struct platform_device *pdev)
+{
+	struct omap3_l3 *l3;
+	struct resource *res;
+	int ret;
+
+	l3 = kzalloc(sizeof(*l3), GFP_KERNEL);
+	if (!l3)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, l3);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "couldn't find resource\n");
+		ret = -ENODEV;
+		goto err0;
+	}
+	l3->rt = ioremap(res->start, resource_size(res));
+	if (!l3->rt) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto err0;
+	}
+
+	l3->debug_irq = platform_get_irq(pdev, 0);
+	ret = request_irq(l3->debug_irq, omap3_l3_app_irq,
+		IRQF_DISABLED | IRQF_TRIGGER_RISING,
+		"l3-debug-irq", l3);
+	if (ret) {
+		dev_err(&pdev->dev, "couldn't request debug irq\n");
+		goto err1;
+	}
+
+	l3->app_irq = platform_get_irq(pdev, 1);
+	ret = request_irq(l3->app_irq, omap3_l3_app_irq,
+		IRQF_DISABLED | IRQF_TRIGGER_RISING,
+		"l3-app-irq", l3);
+	if (ret) {
+		dev_err(&pdev->dev, "couldn't request app irq\n");
+		goto err2;
+	}
+
+	return 0;
+
+err2:
+	free_irq(l3->debug_irq, l3);
+err1:
+	iounmap(l3->rt);
+err0:
+	kfree(l3);
+	return ret;
+}
+
+static int __exit omap3_l3_remove(struct platform_device *pdev)
+{
+	struct omap3_l3         *l3 = platform_get_drvdata(pdev);
+
+	free_irq(l3->app_irq, l3);
+	free_irq(l3->debug_irq, l3);
+	iounmap(l3->rt);
+	kfree(l3);
+
+	return 0;
+}
+
+static struct platform_driver omap3_l3_driver = {
+	.remove         = __exit_p(omap3_l3_remove),
+	.driver         = {
+	.name   = "omap_l3_smx",
+	},
+};
+
+static int __init omap3_l3_init(void)
+{
+	return platform_driver_probe(&omap3_l3_driver, omap3_l3_probe);
+}
+postcore_initcall_sync(omap3_l3_init);
+
+static void __exit omap3_l3_exit(void)
+{
+	platform_driver_unregister(&omap3_l3_driver);
+}
+module_exit(omap3_l3_exit);
diff --git a/drivers/bus/omap_l3_smx.h b/drivers/bus/omap_l3_smx.h
new file mode 100644
index 000000000000..4f3cebca4179
--- /dev/null
+++ b/drivers/bus/omap_l3_smx.h
@@ -0,0 +1,338 @@
+/*
+ * OMAP3XXX L3 Interconnect Driver header
+ *
+ * Copyright (C) 2011 Texas Corporation
+ *	Felipe Balbi <balbi@ti.com>
+ *	Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *	sricharan <r.sricharan@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef __ARCH_ARM_MACH_OMAP2_L3_INTERCONNECT_3XXX_H
+#define __ARCH_ARM_MACH_OMAP2_L3_INTERCONNECT_3XXX_H
+
+/* Register definitions. All 64-bit wide */
+#define L3_COMPONENT			0x000
+#define L3_CORE				0x018
+#define L3_AGENT_CONTROL		0x020
+#define L3_AGENT_STATUS			0x028
+#define L3_ERROR_LOG			0x058
+
+#define L3_ERROR_LOG_MULTI		(1 << 31)
+#define L3_ERROR_LOG_SECONDARY		(1 << 30)
+
+#define L3_ERROR_LOG_ADDR		0x060
+
+/* Register definitions for Sideband Interconnect */
+#define L3_SI_CONTROL			0x020
+#define L3_SI_FLAG_STATUS_0		0x510
+
+static const u64 shift = 1;
+
+#define L3_STATUS_0_MPUIA_BRST		(shift << 0)
+#define L3_STATUS_0_MPUIA_RSP		(shift << 1)
+#define L3_STATUS_0_MPUIA_INBAND	(shift << 2)
+#define L3_STATUS_0_IVAIA_BRST		(shift << 6)
+#define L3_STATUS_0_IVAIA_RSP		(shift << 7)
+#define L3_STATUS_0_IVAIA_INBAND	(shift << 8)
+#define L3_STATUS_0_SGXIA_BRST		(shift << 9)
+#define L3_STATUS_0_SGXIA_RSP		(shift << 10)
+#define L3_STATUS_0_SGXIA_MERROR	(shift << 11)
+#define L3_STATUS_0_CAMIA_BRST		(shift << 12)
+#define L3_STATUS_0_CAMIA_RSP		(shift << 13)
+#define L3_STATUS_0_CAMIA_INBAND	(shift << 14)
+#define L3_STATUS_0_DISPIA_BRST		(shift << 15)
+#define L3_STATUS_0_DISPIA_RSP		(shift << 16)
+#define L3_STATUS_0_DMARDIA_BRST	(shift << 18)
+#define L3_STATUS_0_DMARDIA_RSP		(shift << 19)
+#define L3_STATUS_0_DMAWRIA_BRST	(shift << 21)
+#define L3_STATUS_0_DMAWRIA_RSP		(shift << 22)
+#define L3_STATUS_0_USBOTGIA_BRST	(shift << 24)
+#define L3_STATUS_0_USBOTGIA_RSP	(shift << 25)
+#define L3_STATUS_0_USBOTGIA_INBAND	(shift << 26)
+#define L3_STATUS_0_USBHOSTIA_BRST	(shift << 27)
+#define L3_STATUS_0_USBHOSTIA_INBAND	(shift << 28)
+#define L3_STATUS_0_SMSTA_REQ		(shift << 48)
+#define L3_STATUS_0_GPMCTA_REQ		(shift << 49)
+#define L3_STATUS_0_OCMRAMTA_REQ	(shift << 50)
+#define L3_STATUS_0_OCMROMTA_REQ	(shift << 51)
+#define L3_STATUS_0_IVATA_REQ		(shift << 54)
+#define L3_STATUS_0_SGXTA_REQ		(shift << 55)
+#define L3_STATUS_0_SGXTA_SERROR	(shift << 56)
+#define L3_STATUS_0_GPMCTA_SERROR	(shift << 57)
+#define L3_STATUS_0_L4CORETA_REQ	(shift << 58)
+#define L3_STATUS_0_L4PERTA_REQ		(shift << 59)
+#define L3_STATUS_0_L4EMUTA_REQ		(shift << 60)
+#define L3_STATUS_0_MAD2DTA_REQ		(shift << 61)
+
+#define L3_STATUS_0_TIMEOUT_MASK	(L3_STATUS_0_MPUIA_BRST		\
+					| L3_STATUS_0_MPUIA_RSP		\
+					| L3_STATUS_0_IVAIA_BRST	\
+					| L3_STATUS_0_IVAIA_RSP		\
+					| L3_STATUS_0_SGXIA_BRST	\
+					| L3_STATUS_0_SGXIA_RSP		\
+					| L3_STATUS_0_CAMIA_BRST	\
+					| L3_STATUS_0_CAMIA_RSP		\
+					| L3_STATUS_0_DISPIA_BRST	\
+					| L3_STATUS_0_DISPIA_RSP	\
+					| L3_STATUS_0_DMARDIA_BRST	\
+					| L3_STATUS_0_DMARDIA_RSP	\
+					| L3_STATUS_0_DMAWRIA_BRST	\
+					| L3_STATUS_0_DMAWRIA_RSP	\
+					| L3_STATUS_0_USBOTGIA_BRST	\
+					| L3_STATUS_0_USBOTGIA_RSP	\
+					| L3_STATUS_0_USBHOSTIA_BRST	\
+					| L3_STATUS_0_SMSTA_REQ		\
+					| L3_STATUS_0_GPMCTA_REQ	\
+					| L3_STATUS_0_OCMRAMTA_REQ	\
+					| L3_STATUS_0_OCMROMTA_REQ	\
+					| L3_STATUS_0_IVATA_REQ		\
+					| L3_STATUS_0_SGXTA_REQ		\
+					| L3_STATUS_0_L4CORETA_REQ	\
+					| L3_STATUS_0_L4PERTA_REQ	\
+					| L3_STATUS_0_L4EMUTA_REQ	\
+					| L3_STATUS_0_MAD2DTA_REQ)
+
+#define L3_SI_FLAG_STATUS_1		0x530
+
+#define L3_STATUS_1_MPU_DATAIA		(1 << 0)
+#define L3_STATUS_1_DAPIA0		(1 << 3)
+#define L3_STATUS_1_DAPIA1		(1 << 4)
+#define L3_STATUS_1_IVAIA		(1 << 6)
+
+#define L3_PM_ERROR_LOG			0x020
+#define L3_PM_CONTROL			0x028
+#define L3_PM_ERROR_CLEAR_SINGLE	0x030
+#define L3_PM_ERROR_CLEAR_MULTI		0x038
+#define L3_PM_REQ_INFO_PERMISSION(n)	(0x048 + (0x020 * n))
+#define L3_PM_READ_PERMISSION(n)	(0x050 + (0x020 * n))
+#define L3_PM_WRITE_PERMISSION(n)	(0x058 + (0x020 * n))
+#define L3_PM_ADDR_MATCH(n)		(0x060 + (0x020 * n))
+
+/* L3 error log bit fields. Common for IA and TA */
+#define L3_ERROR_LOG_CODE		24
+#define L3_ERROR_LOG_INITID		8
+#define L3_ERROR_LOG_CMD		0
+
+/* L3 agent status bit fields. */
+#define L3_AGENT_STATUS_CLEAR_IA	0x10000000
+#define L3_AGENT_STATUS_CLEAR_TA	0x01000000
+
+#define OMAP34xx_IRQ_L3_APP		10
+#define L3_APPLICATION_ERROR		0x0
+#define L3_DEBUG_ERROR			0x1
+
+enum omap3_l3_initiator_id {
+	/* LCD has 1 ID */
+	OMAP_L3_LCD = 29,
+	/* SAD2D has 1 ID */
+	OMAP_L3_SAD2D = 28,
+	/* MPU has 5 IDs */
+	OMAP_L3_IA_MPU_SS_1 = 27,
+	OMAP_L3_IA_MPU_SS_2 = 26,
+	OMAP_L3_IA_MPU_SS_3 = 25,
+	OMAP_L3_IA_MPU_SS_4 = 24,
+	OMAP_L3_IA_MPU_SS_5 = 23,
+	/* IVA2.2 SS has 3 IDs*/
+	OMAP_L3_IA_IVA_SS_1 = 22,
+	OMAP_L3_IA_IVA_SS_2 = 21,
+	OMAP_L3_IA_IVA_SS_3 = 20,
+	/* IVA 2.2 SS DMA has 6 IDS */
+	OMAP_L3_IA_IVA_SS_DMA_1 = 19,
+	OMAP_L3_IA_IVA_SS_DMA_2 = 18,
+	OMAP_L3_IA_IVA_SS_DMA_3 = 17,
+	OMAP_L3_IA_IVA_SS_DMA_4 = 16,
+	OMAP_L3_IA_IVA_SS_DMA_5 = 15,
+	OMAP_L3_IA_IVA_SS_DMA_6 = 14,
+	/* SGX has 1 ID */
+	OMAP_L3_IA_SGX = 13,
+	/* CAM has 3 ID */
+	OMAP_L3_IA_CAM_1 = 12,
+	OMAP_L3_IA_CAM_2 = 11,
+	OMAP_L3_IA_CAM_3 = 10,
+	/* DAP has 1 ID */
+	OMAP_L3_IA_DAP = 9,
+	/* SDMA WR has 2 IDs */
+	OMAP_L3_SDMA_WR_1 = 8,
+	OMAP_L3_SDMA_WR_2 = 7,
+	/* SDMA RD has 4 IDs */
+	OMAP_L3_SDMA_RD_1 = 6,
+	OMAP_L3_SDMA_RD_2 = 5,
+	OMAP_L3_SDMA_RD_3 = 4,
+	OMAP_L3_SDMA_RD_4 = 3,
+	/* HSUSB OTG has 1 ID */
+	OMAP_L3_USBOTG = 2,
+	/* HSUSB HOST has 1 ID */
+	OMAP_L3_USBHOST = 1,
+};
+
+enum omap3_l3_code {
+	OMAP_L3_CODE_NOERROR = 0,
+	OMAP_L3_CODE_UNSUP_CMD = 1,
+	OMAP_L3_CODE_ADDR_HOLE = 2,
+	OMAP_L3_CODE_PROTECT_VIOLATION = 3,
+	OMAP_L3_CODE_IN_BAND_ERR = 4,
+	/* codes 5 and 6 are reserved */
+	OMAP_L3_CODE_REQ_TOUT_NOT_ACCEPT = 7,
+	OMAP_L3_CODE_REQ_TOUT_NO_RESP = 8,
+	/* codes 9 - 15 are also reserved */
+};
+
+struct omap3_l3 {
+	struct device *dev;
+	struct clk *ick;
+
+	/* memory base*/
+	void __iomem *rt;
+
+	int debug_irq;
+	int app_irq;
+
+	/* true when and inband functional error occurs */
+	unsigned inband:1;
+};
+
+/* offsets for l3 agents in order with the Flag status register */
+static unsigned int omap3_l3_app_bases[] = {
+	/* MPU IA */
+	0x1400,
+	0x1400,
+	0x1400,
+	/* RESERVED */
+	0,
+	0,
+	0,
+	/* IVA 2.2 IA */
+	0x1800,
+	0x1800,
+	0x1800,
+	/* SGX IA */
+	0x1c00,
+	0x1c00,
+	/* RESERVED */
+	0,
+	/* CAMERA IA */
+	0x5800,
+	0x5800,
+	0x5800,
+	/* DISPLAY IA */
+	0x5400,
+	0x5400,
+	/* RESERVED */
+	0,
+	/*SDMA RD IA */
+	0x4c00,
+	0x4c00,
+	/* RESERVED */
+	0,
+	/* SDMA WR IA */
+	0x5000,
+	0x5000,
+	/* RESERVED */
+	0,
+	/* USB OTG IA */
+	0x4400,
+	0x4400,
+	0x4400,
+	/* USB HOST IA */
+	0x4000,
+	0x4000,
+	/* RESERVED */
+	0,
+	0,
+	0,
+	0,
+	/* SAD2D IA */
+	0x3000,
+	0x3000,
+	0x3000,
+	/* RESERVED */
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	/* SMA TA */
+	0x2000,
+	/* GPMC TA */
+	0x2400,
+	/* OCM RAM TA */
+	0x2800,
+	/* OCM ROM TA */
+	0x2C00,
+	/* L4 CORE TA */
+	0x6800,
+	/* L4 PER TA */
+	0x6c00,
+	/* IVA 2.2 TA */
+	0x6000,
+	/* SGX TA */
+	0x6400,
+	/* L4 EMU TA */
+	0x7000,
+	/* GPMC TA */
+	0x2400,
+	/* L4 CORE TA */
+	0x6800,
+	/* L4 PER TA */
+	0x6c00,
+	/* L4 EMU TA */
+	0x7000,
+	/* MAD2D TA */
+	0x3400,
+	/* RESERVED */
+	0,
+	0,
+};
+
+static unsigned int omap3_l3_debug_bases[] = {
+	/* MPU DATA IA */
+	0x1400,
+	/* RESERVED */
+	0,
+	0,
+	/* DAP IA */
+	0x5c00,
+	0x5c00,
+	/* RESERVED */
+	0,
+	/* IVA 2.2 IA */
+	0x1800,
+	/* REST RESERVED */
+};
+
+static u32 *omap3_l3_bases[] = {
+	omap3_l3_app_bases,
+	omap3_l3_debug_bases,
+};
+
+/*
+ * REVISIT define __raw_readll/__raw_writell here, but move them to
+ * <asm/io.h> at some point
+ */
+#define __raw_writell(v, a)	(__chk_io_ptr(a), \
+				*(volatile u64 __force *)(a) = (v))
+#define __raw_readll(a)		(__chk_io_ptr(a), \
+				*(volatile u64 __force *)(a))
+
+#endif
diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c
index d45c3345b4af..a0e2f7d70355 100644
--- a/drivers/char/nwflash.c
+++ b/drivers/char/nwflash.c
@@ -30,7 +30,6 @@
 
 #include <asm/hardware/dec21285.h>
 #include <asm/io.h>
-#include <asm/leds.h>
 #include <asm/mach-types.h>
 #include <asm/uaccess.h>
 
@@ -179,9 +178,6 @@ static ssize_t flash_write(struct file *file, const char __user *buf,
 
 	written = 0;
 
-	leds_event(led_claim);
-	leds_event(led_green_on);
-
 	nBlock = (int) p >> 16;	//block # of 64K bytes
 
 	/*
@@ -258,11 +254,6 @@ static ssize_t flash_write(struct file *file, const char __user *buf,
 			printk(KERN_DEBUG "flash_write: written 0x%X bytes OK.\n", written);
 	}
 
-	/*
-	 * restore reg on exit
-	 */
-	leds_event(led_release);
-
 	mutex_unlock(&nwflash_mutex);
 
 	return written;
@@ -334,11 +325,6 @@ static int erase_block(int nBlock)
 	int temp, temp1;
 
 	/*
-	 * orange LED == erase
-	 */
-	leds_event(led_amber_on);
-
-	/*
 	 * reset footbridge to the correct offset 0 (...0..3)
 	 */
 	*CSR_ROMWRITEREG = 0;
@@ -446,12 +432,6 @@ static int write_block(unsigned long p, const char __user *buf, int count)
 	unsigned long timeout;
 	unsigned long timeout1;
 
-	/*
-	 * red LED == write
-	 */
-	leds_event(led_amber_off);
-	leds_event(led_red_on);
-
 	pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p));
 
 	/*
@@ -558,17 +538,9 @@ static int write_block(unsigned long p, const char __user *buf, int count)
 					       pWritePtr - FLASH_BASE);
 
 				/*
-				 * no LED == waiting
-				 */
-				leds_event(led_amber_off);
-				/*
 				 * wait couple ms
 				 */
 				msleep(10);
-				/*
-				 * red LED == write
-				 */
-				leds_event(led_red_on);
 
 				goto WriteRetry;
 			} else {
@@ -583,12 +555,6 @@ static int write_block(unsigned long p, const char __user *buf, int count)
 		}
 	}
 
-	/*
-	 * green LED == read/verify
-	 */
-	leds_event(led_amber_off);
-	leds_event(led_green_on);
-
 	msleep(10);
 
 	pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p));
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
index 8af4b06e80f7..a006f0db15af 100644
--- a/drivers/gpio/gpio-samsung.c
+++ b/drivers/gpio/gpio-samsung.c
@@ -2797,6 +2797,27 @@ static __init void exynos4_gpiolib_init(void)
 	int group = 0;
 	void __iomem *gpx_base;
 
+#ifdef CONFIG_PINCTRL_SAMSUNG
+		/*
+		 * This gpio driver includes support for device tree support and
+		 * there are platforms using it. In order to maintain
+		 * compatibility with those platforms, and to allow non-dt
+		 * Exynos4210 platforms to use this gpiolib support, a check
+		 * is added to find out if there is a active pin-controller
+		 * driver support available. If it is available, this gpiolib
+		 * support is ignored and the gpiolib support available in
+		 * pin-controller driver is used. This is a temporary check and
+		 * will go away when all of the Exynos4210 platforms have
+		 * switched to using device tree and the pin-ctrl driver.
+		 */
+		struct device_node *pctrl_np;
+		const char *pctrl_compat = "samsung,pinctrl-exynos4210";
+		pctrl_np = of_find_compatible_node(NULL, NULL, pctrl_compat);
+		if (pctrl_np)
+			if (of_device_is_available(pctrl_np))
+				return;
+#endif
+
 	/* gpio part1 */
 	gpio_base1 = ioremap(EXYNOS4_PA_GPIO1, SZ_4K);
 	if (gpio_base1 == NULL) {
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 9a08c57bc936..f981ac4e6783 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -27,6 +27,7 @@
 #include <linux/slab.h>
 #include <linux/i2c-tegra.h>
 #include <linux/of_i2c.h>
+#include <linux/of_device.h>
 #include <linux/module.h>
 
 #include <asm/unaligned.h>
@@ -114,11 +115,21 @@ enum msg_end_type {
 };
 
 /**
+ * struct tegra_i2c_hw_feature : Different HW support on Tegra
+ * @has_continue_xfer_support: Continue transfer supports.
+ */
+
+struct tegra_i2c_hw_feature {
+	bool has_continue_xfer_support;
+};
+
+/**
  * struct tegra_i2c_dev	- per device i2c context
  * @dev: device reference for power management
+ * @hw: Tegra i2c hw feature.
  * @adapter: core i2c layer adapter information
- * @clk: clock reference for i2c controller
- * @i2c_clk: clock reference for i2c bus
+ * @div_clk: clock reference for div clock of i2c controller.
+ * @fast_clk: clock reference for fast clock of i2c controller.
  * @base: ioremapped registers cookie
  * @cont_id: i2c controller id, used for for packet header
  * @irq: irq number of transfer complete interrupt
@@ -133,9 +144,10 @@ enum msg_end_type {
  */
 struct tegra_i2c_dev {
 	struct device *dev;
+	const struct tegra_i2c_hw_feature *hw;
 	struct i2c_adapter adapter;
-	struct clk *clk;
-	struct clk *i2c_clk;
+	struct clk *div_clk;
+	struct clk *fast_clk;
 	void __iomem *base;
 	int cont_id;
 	int irq;
@@ -351,16 +363,40 @@ static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev)
 	dvc_writel(i2c_dev, val, DVC_CTRL_REG1);
 }
 
+static inline int tegra_i2c_clock_enable(struct tegra_i2c_dev *i2c_dev)
+{
+	int ret;
+	ret = clk_prepare_enable(i2c_dev->fast_clk);
+	if (ret < 0) {
+		dev_err(i2c_dev->dev,
+			"Enabling fast clk failed, err %d\n", ret);
+		return ret;
+	}
+	ret = clk_prepare_enable(i2c_dev->div_clk);
+	if (ret < 0) {
+		dev_err(i2c_dev->dev,
+			"Enabling div clk failed, err %d\n", ret);
+		clk_disable_unprepare(i2c_dev->fast_clk);
+	}
+	return ret;
+}
+
+static inline void tegra_i2c_clock_disable(struct tegra_i2c_dev *i2c_dev)
+{
+	clk_disable_unprepare(i2c_dev->div_clk);
+	clk_disable_unprepare(i2c_dev->fast_clk);
+}
+
 static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
 {
 	u32 val;
 	int err = 0;
 
-	clk_prepare_enable(i2c_dev->clk);
+	tegra_i2c_clock_enable(i2c_dev);
 
-	tegra_periph_reset_assert(i2c_dev->clk);
+	tegra_periph_reset_assert(i2c_dev->div_clk);
 	udelay(2);
-	tegra_periph_reset_deassert(i2c_dev->clk);
+	tegra_periph_reset_deassert(i2c_dev->div_clk);
 
 	if (i2c_dev->is_dvc)
 		tegra_dvc_init(i2c_dev);
@@ -369,7 +405,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
 		(0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
 	i2c_writel(i2c_dev, val, I2C_CNFG);
 	i2c_writel(i2c_dev, 0, I2C_INT_MASK);
-	clk_set_rate(i2c_dev->clk, i2c_dev->bus_clk_rate * 8);
+	clk_set_rate(i2c_dev->div_clk, i2c_dev->bus_clk_rate * 8);
 
 	if (!i2c_dev->is_dvc) {
 		u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG);
@@ -387,7 +423,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
 	if (tegra_i2c_flush_fifos(i2c_dev))
 		err = -ETIMEDOUT;
 
-	clk_disable_unprepare(i2c_dev->clk);
+	tegra_i2c_clock_disable(i2c_dev);
 
 	if (i2c_dev->irq_disabled) {
 		i2c_dev->irq_disabled = 0;
@@ -563,7 +599,7 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
 	if (i2c_dev->is_suspended)
 		return -EBUSY;
 
-	clk_prepare_enable(i2c_dev->clk);
+	tegra_i2c_clock_enable(i2c_dev);
 	for (i = 0; i < num; i++) {
 		enum msg_end_type end_type = MSG_END_STOP;
 		if (i < (num - 1)) {
@@ -576,14 +612,19 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
 		if (ret)
 			break;
 	}
-	clk_disable_unprepare(i2c_dev->clk);
+	tegra_i2c_clock_disable(i2c_dev);
 	return ret ?: i;
 }
 
 static u32 tegra_i2c_func(struct i2c_adapter *adap)
 {
-	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
-		I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART;
+	struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+	u32 ret = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
+				I2C_FUNC_PROTOCOL_MANGLING;
+
+	if (i2c_dev->hw->has_continue_xfer_support)
+		ret |= I2C_FUNC_NOSTART;
+	return ret;
 }
 
 static const struct i2c_algorithm tegra_i2c_algo = {
@@ -591,13 +632,32 @@ static const struct i2c_algorithm tegra_i2c_algo = {
 	.functionality	= tegra_i2c_func,
 };
 
+static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
+	.has_continue_xfer_support = false,
+};
+
+static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
+	.has_continue_xfer_support = true,
+};
+
+#if defined(CONFIG_OF)
+/* Match table for of_platform binding */
+static const struct of_device_id tegra_i2c_of_match[] __devinitconst = {
+	{ .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, },
+	{ .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_hw, },
+	{ .compatible = "nvidia,tegra20-i2c-dvc", .data = &tegra20_i2c_hw, },
+	{},
+};
+MODULE_DEVICE_TABLE(of, tegra_i2c_of_match);
+#endif
+
 static int __devinit tegra_i2c_probe(struct platform_device *pdev)
 {
 	struct tegra_i2c_dev *i2c_dev;
 	struct tegra_i2c_platform_data *pdata = pdev->dev.platform_data;
 	struct resource *res;
-	struct clk *clk;
-	struct clk *i2c_clk;
+	struct clk *div_clk;
+	struct clk *fast_clk;
 	const unsigned int *prop;
 	void __iomem *base;
 	int irq;
@@ -622,16 +682,16 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
 	}
 	irq = res->start;
 
-	clk = devm_clk_get(&pdev->dev, NULL);
-	if (IS_ERR(clk)) {
+	div_clk = devm_clk_get(&pdev->dev, "div-clk");
+	if (IS_ERR(div_clk)) {
 		dev_err(&pdev->dev, "missing controller clock");
-		return PTR_ERR(clk);
+		return PTR_ERR(div_clk);
 	}
 
-	i2c_clk = devm_clk_get(&pdev->dev, "i2c");
-	if (IS_ERR(i2c_clk)) {
+	fast_clk = devm_clk_get(&pdev->dev, "fast-clk");
+	if (IS_ERR(fast_clk)) {
 		dev_err(&pdev->dev, "missing bus clock");
-		return PTR_ERR(i2c_clk);
+		return PTR_ERR(fast_clk);
 	}
 
 	i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
@@ -641,8 +701,8 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
 	}
 
 	i2c_dev->base = base;
-	i2c_dev->clk = clk;
-	i2c_dev->i2c_clk = i2c_clk;
+	i2c_dev->div_clk = div_clk;
+	i2c_dev->fast_clk = fast_clk;
 	i2c_dev->adapter.algo = &tegra_i2c_algo;
 	i2c_dev->irq = irq;
 	i2c_dev->cont_id = pdev->id;
@@ -659,11 +719,18 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
 			i2c_dev->bus_clk_rate = be32_to_cpup(prop);
 	}
 
-	if (pdev->dev.of_node)
+	i2c_dev->hw = &tegra20_i2c_hw;
+
+	if (pdev->dev.of_node) {
+		const struct of_device_id *match;
+		match = of_match_device(of_match_ptr(tegra_i2c_of_match),
+						&pdev->dev);
+		i2c_dev->hw = match->data;
 		i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node,
 						"nvidia,tegra20-i2c-dvc");
-	else if (pdev->id == 3)
+	} else if (pdev->id == 3) {
 		i2c_dev->is_dvc = 1;
+	}
 	init_completion(&i2c_dev->msg_complete);
 
 	platform_set_drvdata(pdev, i2c_dev);
@@ -681,8 +748,6 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	clk_prepare_enable(i2c_dev->i2c_clk);
-
 	i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
 	i2c_dev->adapter.owner = THIS_MODULE;
 	i2c_dev->adapter.class = I2C_CLASS_HWMON;
@@ -696,7 +761,6 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
 	ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to add I2C adapter\n");
-		clk_disable_unprepare(i2c_dev->i2c_clk);
 		return ret;
 	}
 
@@ -751,16 +815,6 @@ static SIMPLE_DEV_PM_OPS(tegra_i2c_pm, tegra_i2c_suspend, tegra_i2c_resume);
 #define TEGRA_I2C_PM	NULL
 #endif
 
-#if defined(CONFIG_OF)
-/* Match table for of_platform binding */
-static const struct of_device_id tegra_i2c_of_match[] __devinitconst = {
-	{ .compatible = "nvidia,tegra20-i2c", },
-	{ .compatible = "nvidia,tegra20-i2c-dvc", },
-	{},
-};
-MODULE_DEVICE_TABLE(of, tegra_i2c_of_match);
-#endif
-
 static struct platform_driver tegra_i2c_driver = {
 	.probe   = tegra_i2c_probe,
 	.remove  = __devexit_p(tegra_i2c_remove),
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index c96bbaadeebd..16578d3b52bb 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -506,6 +506,16 @@ config LEDS_TRIGGER_BACKLIGHT
 
 	  If unsure, say N.
 
+config LEDS_TRIGGER_CPU
+	bool "LED CPU Trigger"
+	depends on LEDS_TRIGGERS
+	help
+	  This allows LEDs to be controlled by active CPUs. This shows
+	  the active CPUs across an array of LEDs so you can see which
+	  CPUs are active on the system at any given moment.
+
+	  If unsure, say N.
+
 config LEDS_TRIGGER_GPIO
 	tristate "LED GPIO Trigger"
 	depends on LEDS_TRIGGERS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index a4429a9217bc..a9b627c4f8ba 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -61,5 +61,6 @@ obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK)	+= ledtrig-ide-disk.o
 obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)	+= ledtrig-heartbeat.o
 obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)	+= ledtrig-backlight.o
 obj-$(CONFIG_LEDS_TRIGGER_GPIO)		+= ledtrig-gpio.o
+obj-$(CONFIG_LEDS_TRIGGER_CPU)		+= ledtrig-cpu.o
 obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)	+= ledtrig-default-on.o
 obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)	+= ledtrig-transient.o
diff --git a/drivers/leds/ledtrig-cpu.c b/drivers/leds/ledtrig-cpu.c
new file mode 100644
index 000000000000..b312056da14d
--- /dev/null
+++ b/drivers/leds/ledtrig-cpu.c
@@ -0,0 +1,163 @@
+/*
+ * ledtrig-cpu.c - LED trigger based on CPU activity
+ *
+ * This LED trigger will be registered for each possible CPU and named as
+ * cpu0, cpu1, cpu2, cpu3, etc.
+ *
+ * It can be bound to any LED just like other triggers using either a
+ * board file or via sysfs interface.
+ *
+ * An API named ledtrig_cpu is exported for any user, who want to add CPU
+ * activity indication in their code
+ *
+ * Copyright 2011 Linus Walleij <linus.walleij@linaro.org>
+ * Copyright 2011 - 2012 Bryan Wu <bryan.wu@canonical.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/percpu.h>
+#include <linux/syscore_ops.h>
+#include <linux/rwsem.h>
+#include "leds.h"
+
+#define MAX_NAME_LEN	8
+
+struct led_trigger_cpu {
+	char name[MAX_NAME_LEN];
+	struct led_trigger *_trig;
+	struct mutex lock;
+	int lock_is_inited;
+};
+
+static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig);
+
+/**
+ * ledtrig_cpu - emit a CPU event as a trigger
+ * @evt: CPU event to be emitted
+ *
+ * Emit a CPU event on a CPU core, which will trigger a
+ * binded LED to turn on or turn off.
+ */
+void ledtrig_cpu(enum cpu_led_event ledevt)
+{
+	struct led_trigger_cpu *trig = &__get_cpu_var(cpu_trig);
+
+	/* mutex lock should be initialized before calling mutex_call() */
+	if (!trig->lock_is_inited)
+		return;
+
+	mutex_lock(&trig->lock);
+
+	/* Locate the correct CPU LED */
+	switch (ledevt) {
+	case CPU_LED_IDLE_END:
+	case CPU_LED_START:
+		/* Will turn the LED on, max brightness */
+		led_trigger_event(trig->_trig, LED_FULL);
+		break;
+
+	case CPU_LED_IDLE_START:
+	case CPU_LED_STOP:
+	case CPU_LED_HALTED:
+		/* Will turn the LED off */
+		led_trigger_event(trig->_trig, LED_OFF);
+		break;
+
+	default:
+		/* Will leave the LED as it is */
+		break;
+	}
+
+	mutex_unlock(&trig->lock);
+}
+EXPORT_SYMBOL(ledtrig_cpu);
+
+static int ledtrig_cpu_syscore_suspend(void)
+{
+	ledtrig_cpu(CPU_LED_STOP);
+	return 0;
+}
+
+static void ledtrig_cpu_syscore_resume(void)
+{
+	ledtrig_cpu(CPU_LED_START);
+}
+
+static void ledtrig_cpu_syscore_shutdown(void)
+{
+	ledtrig_cpu(CPU_LED_HALTED);
+}
+
+static struct syscore_ops ledtrig_cpu_syscore_ops = {
+	.shutdown	= ledtrig_cpu_syscore_shutdown,
+	.suspend	= ledtrig_cpu_syscore_suspend,
+	.resume		= ledtrig_cpu_syscore_resume,
+};
+
+static int __init ledtrig_cpu_init(void)
+{
+	int cpu;
+
+	/* Supports up to 9999 cpu cores */
+	BUILD_BUG_ON(CONFIG_NR_CPUS > 9999);
+
+	/*
+	 * Registering CPU led trigger for each CPU core here
+	 * ignores CPU hotplug, but after this CPU hotplug works
+	 * fine with this trigger.
+	 */
+	for_each_possible_cpu(cpu) {
+		struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu);
+
+		mutex_init(&trig->lock);
+
+		snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu);
+
+		mutex_lock(&trig->lock);
+		led_trigger_register_simple(trig->name, &trig->_trig);
+		trig->lock_is_inited = 1;
+		mutex_unlock(&trig->lock);
+	}
+
+	register_syscore_ops(&ledtrig_cpu_syscore_ops);
+
+	pr_info("ledtrig-cpu: registered to indicate activity on CPUs\n");
+
+	return 0;
+}
+module_init(ledtrig_cpu_init);
+
+static void __exit ledtrig_cpu_exit(void)
+{
+	int cpu;
+
+	for_each_possible_cpu(cpu) {
+		struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu);
+
+		mutex_lock(&trig->lock);
+
+		led_trigger_unregister_simple(trig->_trig);
+		trig->_trig = NULL;
+		memset(trig->name, 0, MAX_NAME_LEN);
+		trig->lock_is_inited = 0;
+
+		mutex_unlock(&trig->lock);
+		mutex_destroy(&trig->lock);
+	}
+
+	unregister_syscore_ops(&ledtrig_cpu_syscore_ops);
+}
+module_exit(ledtrig_cpu_exit);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_AUTHOR("Bryan Wu <bryan.wu@canonical.com>");
+MODULE_DESCRIPTION("CPU LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 8ca417614c57..598cd0a3adee 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -406,46 +406,6 @@ config MTD_NAND_ATMEL
 	help
 	  Enables support for NAND Flash / Smart Media Card interface
 	  on Atmel AT91 and AVR32 processors.
-choice
-	prompt "ECC management for NAND Flash / SmartMedia on AT91 / AVR32"
-	depends on MTD_NAND_ATMEL
-
-config MTD_NAND_ATMEL_ECC_HW
-	bool "Hardware ECC"
-	depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9260 || AVR32
-	help
-	  Use hardware ECC instead of software ECC when the chip
-	  supports it.
-
-	  The hardware ECC controller is capable of single bit error
-	  correction and 2-bit random detection per page.
-
-	  NB : hardware and software ECC schemes are incompatible.
-	  If you switch from one to another, you'll have to erase your
-	  mtd partition.
-
-	  If unsure, say Y
-
-config MTD_NAND_ATMEL_ECC_SOFT
-	bool "Software ECC"
-	help
-	  Use software ECC.
-
-	  NB : hardware and software ECC schemes are incompatible.
-	  If you switch from one to another, you'll have to erase your
-	  mtd partition.
-
-config MTD_NAND_ATMEL_ECC_NONE
-	bool "No ECC (testing only, DANGEROUS)"
-	depends on DEBUG_KERNEL
-	help
-	  No ECC will be used.
-	  It's not a good idea and it should be reserved for testing
-	  purpose only.
-
-	  If unsure, say N
-
-endchoice
 
 config MTD_NAND_PXA3xx
 	tristate "Support for NAND flash devices on PXA3xx"
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 54e3588bef62..34e94c7f68ca 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -145,6 +145,15 @@ config PINCTRL_COH901
 	  COH 901 335 and COH 901 571/3. They contain 3, 5 or 7
 	  ports of 8 GPIO pins each.
 
+config PINCTRL_SAMSUNG
+	bool "Samsung pinctrl driver"
+	select PINMUX
+	select PINCONF
+
+config PINCTRL_EXYNOS4
+	bool "Pinctrl driver data for Exynos4 SoC"
+	select PINCTRL_SAMSUNG
+
 source "drivers/pinctrl/spear/Kconfig"
 
 endmenu
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f40b1f81ff2c..6a88113e11d9 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -29,5 +29,7 @@ obj-$(CONFIG_PINCTRL_TEGRA20)	+= pinctrl-tegra20.o
 obj-$(CONFIG_PINCTRL_TEGRA30)	+= pinctrl-tegra30.o
 obj-$(CONFIG_PINCTRL_U300)	+= pinctrl-u300.o
 obj-$(CONFIG_PINCTRL_COH901)	+= pinctrl-coh901.o
+obj-$(CONFIG_PINCTRL_SAMSUNG)	+= pinctrl-samsung.o
+obj-$(CONFIG_PINCTRL_EXYNOS4)	+= pinctrl-exynos.o
 
 obj-$(CONFIG_PLAT_SPEAR)	+= spear/
diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c
new file mode 100644
index 000000000000..21362f48d370
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-exynos.c
@@ -0,0 +1,579 @@
+/*
+ * Exynos specific support for Samsung pinctrl/gpiolib driver with eint support.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ * Copyright (c) 2012 Linaro Ltd
+ *		http://www.linaro.org
+ *
+ * Author: Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This file contains the Samsung Exynos specific information required by the
+ * the Samsung pinctrl/gpiolib driver. It also includes the implementation of
+ * external gpio and wakeup interrupt support.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <asm/mach/irq.h>
+
+#include "pinctrl-samsung.h"
+#include "pinctrl-exynos.h"
+
+/* list of external wakeup controllers supported */
+static const struct of_device_id exynos_wkup_irq_ids[] = {
+	{ .compatible = "samsung,exynos4210-wakeup-eint", },
+};
+
+static void exynos_gpio_irq_unmask(struct irq_data *irqd)
+{
+	struct samsung_pinctrl_drv_data *d = irqd->domain->host_data;
+	struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd);
+	unsigned long reg_mask = d->ctrl->geint_mask + edata->eint_offset;
+	unsigned long mask;
+
+	mask = readl(d->virt_base + reg_mask);
+	mask &= ~(1 << edata->pin);
+	writel(mask, d->virt_base + reg_mask);
+}
+
+static void exynos_gpio_irq_mask(struct irq_data *irqd)
+{
+	struct samsung_pinctrl_drv_data *d = irqd->domain->host_data;
+	struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd);
+	unsigned long reg_mask = d->ctrl->geint_mask + edata->eint_offset;
+	unsigned long mask;
+
+	mask = readl(d->virt_base + reg_mask);
+	mask |= 1 << edata->pin;
+	writel(mask, d->virt_base + reg_mask);
+}
+
+static void exynos_gpio_irq_ack(struct irq_data *irqd)
+{
+	struct samsung_pinctrl_drv_data *d = irqd->domain->host_data;
+	struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd);
+	unsigned long reg_pend = d->ctrl->geint_pend + edata->eint_offset;
+
+	writel(1 << edata->pin, d->virt_base + reg_pend);
+}
+
+static int exynos_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
+{
+	struct samsung_pinctrl_drv_data *d = irqd->domain->host_data;
+	struct samsung_pin_ctrl *ctrl = d->ctrl;
+	struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd);
+	struct samsung_pin_bank *bank = edata->bank;
+	unsigned int shift = EXYNOS_EINT_CON_LEN * edata->pin;
+	unsigned int con, trig_type;
+	unsigned long reg_con = ctrl->geint_con + edata->eint_offset;
+	unsigned int mask;
+
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		trig_type = EXYNOS_EINT_EDGE_RISING;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		trig_type = EXYNOS_EINT_EDGE_FALLING;
+		break;
+	case IRQ_TYPE_EDGE_BOTH:
+		trig_type = EXYNOS_EINT_EDGE_BOTH;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		trig_type = EXYNOS_EINT_LEVEL_HIGH;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		trig_type = EXYNOS_EINT_LEVEL_LOW;
+		break;
+	default:
+		pr_err("unsupported external interrupt type\n");
+		return -EINVAL;
+	}
+
+	if (type & IRQ_TYPE_EDGE_BOTH)
+		__irq_set_handler_locked(irqd->irq, handle_edge_irq);
+	else
+		__irq_set_handler_locked(irqd->irq, handle_level_irq);
+
+	con = readl(d->virt_base + reg_con);
+	con &= ~(EXYNOS_EINT_CON_MASK << shift);
+	con |= trig_type << shift;
+	writel(con, d->virt_base + reg_con);
+
+	reg_con = bank->pctl_offset;
+	shift = edata->pin * bank->func_width;
+	mask = (1 << bank->func_width) - 1;
+
+	con = readl(d->virt_base + reg_con);
+	con &= ~(mask << shift);
+	con |= EXYNOS_EINT_FUNC << shift;
+	writel(con, d->virt_base + reg_con);
+
+	return 0;
+}
+
+/*
+ * irq_chip for gpio interrupts.
+ */
+static struct irq_chip exynos_gpio_irq_chip = {
+	.name		= "exynos_gpio_irq_chip",
+	.irq_unmask	= exynos_gpio_irq_unmask,
+	.irq_mask	= exynos_gpio_irq_mask,
+	.irq_ack		= exynos_gpio_irq_ack,
+	.irq_set_type	= exynos_gpio_irq_set_type,
+};
+
+/*
+ * given a controller-local external gpio interrupt number, prepare the handler
+ * data for it.
+ */
+static struct exynos_geint_data *exynos_get_eint_data(irq_hw_number_t hw,
+				struct samsung_pinctrl_drv_data *d)
+{
+	struct samsung_pin_bank *bank = d->ctrl->pin_banks;
+	struct exynos_geint_data *eint_data;
+	unsigned int nr_banks = d->ctrl->nr_banks, idx;
+	unsigned int irq_base = 0, eint_offset = 0;
+
+	if (hw >= d->ctrl->nr_gint) {
+		dev_err(d->dev, "unsupported ext-gpio interrupt\n");
+		return NULL;
+	}
+
+	for (idx = 0; idx < nr_banks; idx++, bank++) {
+		if (bank->eint_type != EINT_TYPE_GPIO)
+			continue;
+		if ((hw >= irq_base) && (hw < (irq_base + bank->nr_pins)))
+			break;
+		irq_base += bank->nr_pins;
+		eint_offset += 4;
+	}
+
+	if (idx == nr_banks) {
+		dev_err(d->dev, "pin bank not found for ext-gpio interrupt\n");
+		return NULL;
+	}
+
+	eint_data = devm_kzalloc(d->dev, sizeof(*eint_data), GFP_KERNEL);
+	if (!eint_data) {
+		dev_err(d->dev, "no memory for eint-gpio data\n");
+		return NULL;
+	}
+
+	eint_data->bank	= bank;
+	eint_data->pin = hw - irq_base;
+	eint_data->eint_offset = eint_offset;
+	return eint_data;
+}
+
+static int exynos_gpio_irq_map(struct irq_domain *h, unsigned int virq,
+					irq_hw_number_t hw)
+{
+	struct samsung_pinctrl_drv_data *d = h->host_data;
+	struct exynos_geint_data *eint_data;
+
+	eint_data = exynos_get_eint_data(hw, d);
+	if (!eint_data)
+		return -EINVAL;
+
+	irq_set_handler_data(virq, eint_data);
+	irq_set_chip_data(virq, h->host_data);
+	irq_set_chip_and_handler(virq, &exynos_gpio_irq_chip,
+					handle_level_irq);
+	set_irq_flags(virq, IRQF_VALID);
+	return 0;
+}
+
+static void exynos_gpio_irq_unmap(struct irq_domain *h, unsigned int virq)
+{
+	struct samsung_pinctrl_drv_data *d = h->host_data;
+	struct exynos_geint_data *eint_data;
+
+	eint_data = irq_get_handler_data(virq);
+	devm_kfree(d->dev, eint_data);
+}
+
+/*
+ * irq domain callbacks for external gpio interrupt controller.
+ */
+static const struct irq_domain_ops exynos_gpio_irqd_ops = {
+	.map	= exynos_gpio_irq_map,
+	.unmap	= exynos_gpio_irq_unmap,
+	.xlate	= irq_domain_xlate_twocell,
+};
+
+static irqreturn_t exynos_eint_gpio_irq(int irq, void *data)
+{
+	struct samsung_pinctrl_drv_data *d = data;
+	struct samsung_pin_ctrl *ctrl = d->ctrl;
+	struct samsung_pin_bank *bank = ctrl->pin_banks;
+	unsigned int svc, group, pin, virq;
+
+	svc = readl(d->virt_base + ctrl->svc);
+	group = EXYNOS_SVC_GROUP(svc);
+	pin = svc & EXYNOS_SVC_NUM_MASK;
+
+	if (!group)
+		return IRQ_HANDLED;
+	bank += (group - 1);
+
+	virq = irq_linear_revmap(d->gpio_irqd, bank->irq_base + pin);
+	if (!virq)
+		return IRQ_NONE;
+	generic_handle_irq(virq);
+	return IRQ_HANDLED;
+}
+
+/*
+ * exynos_eint_gpio_init() - setup handling of external gpio interrupts.
+ * @d: driver data of samsung pinctrl driver.
+ */
+static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d)
+{
+	struct device *dev = d->dev;
+	unsigned int ret;
+
+	if (!d->irq) {
+		dev_err(dev, "irq number not available\n");
+		return -EINVAL;
+	}
+
+	ret = devm_request_irq(dev, d->irq, exynos_eint_gpio_irq,
+					0, dev_name(dev), d);
+	if (ret) {
+		dev_err(dev, "irq request failed\n");
+		return -ENXIO;
+	}
+
+	d->gpio_irqd = irq_domain_add_linear(dev->of_node, d->ctrl->nr_gint,
+				&exynos_gpio_irqd_ops, d);
+	if (!d->gpio_irqd) {
+		dev_err(dev, "gpio irq domain allocation failed\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void exynos_wkup_irq_unmask(struct irq_data *irqd)
+{
+	struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd);
+	unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK;
+	unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1);
+	unsigned long reg_mask = d->ctrl->weint_mask + (bank << 2);
+	unsigned long mask;
+
+	mask = readl(d->virt_base + reg_mask);
+	mask &= ~(1 << pin);
+	writel(mask, d->virt_base + reg_mask);
+}
+
+static void exynos_wkup_irq_mask(struct irq_data *irqd)
+{
+	struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd);
+	unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK;
+	unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1);
+	unsigned long reg_mask = d->ctrl->weint_mask + (bank << 2);
+	unsigned long mask;
+
+	mask = readl(d->virt_base + reg_mask);
+	mask |= 1 << pin;
+	writel(mask, d->virt_base + reg_mask);
+}
+
+static void exynos_wkup_irq_ack(struct irq_data *irqd)
+{
+	struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd);
+	unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK;
+	unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1);
+	unsigned long pend = d->ctrl->weint_pend + (bank << 2);
+
+	writel(1 << pin, d->virt_base + pend);
+}
+
+static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type)
+{
+	struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd);
+	unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK;
+	unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1);
+	unsigned long reg_con = d->ctrl->weint_con + (bank << 2);
+	unsigned long shift = EXYNOS_EINT_CON_LEN * pin;
+	unsigned long con, trig_type;
+
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		trig_type = EXYNOS_EINT_EDGE_RISING;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		trig_type = EXYNOS_EINT_EDGE_FALLING;
+		break;
+	case IRQ_TYPE_EDGE_BOTH:
+		trig_type = EXYNOS_EINT_EDGE_BOTH;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		trig_type = EXYNOS_EINT_LEVEL_HIGH;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		trig_type = EXYNOS_EINT_LEVEL_LOW;
+		break;
+	default:
+		pr_err("unsupported external interrupt type\n");
+		return -EINVAL;
+	}
+
+	if (type & IRQ_TYPE_EDGE_BOTH)
+		__irq_set_handler_locked(irqd->irq, handle_edge_irq);
+	else
+		__irq_set_handler_locked(irqd->irq, handle_level_irq);
+
+	con = readl(d->virt_base + reg_con);
+	con &= ~(EXYNOS_EINT_CON_MASK << shift);
+	con |= trig_type << shift;
+	writel(con, d->virt_base + reg_con);
+	return 0;
+}
+
+/*
+ * irq_chip for wakeup interrupts
+ */
+static struct irq_chip exynos_wkup_irq_chip = {
+	.name	= "exynos_wkup_irq_chip",
+	.irq_unmask	= exynos_wkup_irq_unmask,
+	.irq_mask	= exynos_wkup_irq_mask,
+	.irq_ack	= exynos_wkup_irq_ack,
+	.irq_set_type	= exynos_wkup_irq_set_type,
+};
+
+/* interrupt handler for wakeup interrupts 0..15 */
+static void exynos_irq_eint0_15(unsigned int irq, struct irq_desc *desc)
+{
+	struct exynos_weint_data *eintd = irq_get_handler_data(irq);
+	struct irq_chip *chip = irq_get_chip(irq);
+	int eint_irq;
+
+	chained_irq_enter(chip, desc);
+	chip->irq_mask(&desc->irq_data);
+
+	if (chip->irq_ack)
+		chip->irq_ack(&desc->irq_data);
+
+	eint_irq = irq_linear_revmap(eintd->domain, eintd->irq);
+	generic_handle_irq(eint_irq);
+	chip->irq_unmask(&desc->irq_data);
+	chained_irq_exit(chip, desc);
+}
+
+static inline void exynos_irq_demux_eint(int irq_base, unsigned long pend,
+					struct irq_domain *domain)
+{
+	unsigned int irq;
+
+	while (pend) {
+		irq = fls(pend) - 1;
+		generic_handle_irq(irq_find_mapping(domain, irq_base + irq));
+		pend &= ~(1 << irq);
+	}
+}
+
+/* interrupt handler for wakeup interrupt 16 */
+static void exynos_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_get_chip(irq);
+	struct exynos_weint_data *eintd = irq_get_handler_data(irq);
+	struct samsung_pinctrl_drv_data *d = eintd->domain->host_data;
+	unsigned long pend;
+	unsigned long mask;
+
+	chained_irq_enter(chip, desc);
+	pend = readl(d->virt_base + d->ctrl->weint_pend + 0x8);
+	mask = readl(d->virt_base + d->ctrl->weint_mask + 0x8);
+	exynos_irq_demux_eint(16, pend & ~mask, eintd->domain);
+	pend = readl(d->virt_base + d->ctrl->weint_pend + 0xC);
+	mask = readl(d->virt_base + d->ctrl->weint_mask + 0xC);
+	exynos_irq_demux_eint(24, pend & ~mask, eintd->domain);
+	chained_irq_exit(chip, desc);
+}
+
+static int exynos_wkup_irq_map(struct irq_domain *h, unsigned int virq,
+					irq_hw_number_t hw)
+{
+	irq_set_chip_and_handler(virq, &exynos_wkup_irq_chip, handle_level_irq);
+	irq_set_chip_data(virq, h->host_data);
+	set_irq_flags(virq, IRQF_VALID);
+	return 0;
+}
+
+/*
+ * irq domain callbacks for external wakeup interrupt controller.
+ */
+static const struct irq_domain_ops exynos_wkup_irqd_ops = {
+	.map	= exynos_wkup_irq_map,
+	.xlate	= irq_domain_xlate_twocell,
+};
+
+/*
+ * exynos_eint_wkup_init() - setup handling of external wakeup interrupts.
+ * @d: driver data of samsung pinctrl driver.
+ */
+static int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
+{
+	struct device *dev = d->dev;
+	struct device_node *wkup_np = NULL;
+	struct device_node *np;
+	struct exynos_weint_data *weint_data;
+	int idx, irq;
+
+	for_each_child_of_node(dev->of_node, np) {
+		if (of_match_node(exynos_wkup_irq_ids, np)) {
+			wkup_np = np;
+			break;
+		}
+	}
+	if (!wkup_np)
+		return -ENODEV;
+
+	d->wkup_irqd = irq_domain_add_linear(wkup_np, d->ctrl->nr_wint,
+				&exynos_wkup_irqd_ops, d);
+	if (!d->wkup_irqd) {
+		dev_err(dev, "wakeup irq domain allocation failed\n");
+		return -ENXIO;
+	}
+
+	weint_data = devm_kzalloc(dev, sizeof(*weint_data) * 17, GFP_KERNEL);
+	if (!weint_data) {
+		dev_err(dev, "could not allocate memory for weint_data\n");
+		return -ENOMEM;
+	}
+
+	irq = irq_of_parse_and_map(wkup_np, 16);
+	if (irq) {
+		weint_data[16].domain = d->wkup_irqd;
+		irq_set_chained_handler(irq, exynos_irq_demux_eint16_31);
+		irq_set_handler_data(irq, &weint_data[16]);
+	} else {
+		dev_err(dev, "irq number for EINT16-32 not found\n");
+	}
+
+	for (idx = 0; idx < 16; idx++) {
+		weint_data[idx].domain = d->wkup_irqd;
+		weint_data[idx].irq = idx;
+
+		irq = irq_of_parse_and_map(wkup_np, idx);
+		if (irq) {
+			irq_set_handler_data(irq, &weint_data[idx]);
+			irq_set_chained_handler(irq, exynos_irq_eint0_15);
+		} else {
+			dev_err(dev, "irq number for eint-%x not found\n", idx);
+		}
+	}
+	return 0;
+}
+
+/* pin banks of exynos4210 pin-controller 0 */
+static struct samsung_pin_bank exynos4210_pin_banks0[] = {
+	EXYNOS_PIN_BANK_EINTG(0x000, EXYNOS4210_GPIO_A0, "gpa0"),
+	EXYNOS_PIN_BANK_EINTG(0x020, EXYNOS4210_GPIO_A1, "gpa1"),
+	EXYNOS_PIN_BANK_EINTG(0x040, EXYNOS4210_GPIO_B, "gpb"),
+	EXYNOS_PIN_BANK_EINTG(0x060, EXYNOS4210_GPIO_C0, "gpc0"),
+	EXYNOS_PIN_BANK_EINTG(0x080, EXYNOS4210_GPIO_C1, "gpc1"),
+	EXYNOS_PIN_BANK_EINTG(0x0A0, EXYNOS4210_GPIO_D0, "gpd0"),
+	EXYNOS_PIN_BANK_EINTG(0x0C0, EXYNOS4210_GPIO_D1, "gpd1"),
+	EXYNOS_PIN_BANK_EINTG(0x0E0, EXYNOS4210_GPIO_E0, "gpe0"),
+	EXYNOS_PIN_BANK_EINTG(0x100, EXYNOS4210_GPIO_E1, "gpe1"),
+	EXYNOS_PIN_BANK_EINTG(0x120, EXYNOS4210_GPIO_E2, "gpe2"),
+	EXYNOS_PIN_BANK_EINTG(0x140, EXYNOS4210_GPIO_E3, "gpe3"),
+	EXYNOS_PIN_BANK_EINTG(0x160, EXYNOS4210_GPIO_E4, "gpe4"),
+	EXYNOS_PIN_BANK_EINTG(0x180, EXYNOS4210_GPIO_F0, "gpf0"),
+	EXYNOS_PIN_BANK_EINTG(0x1A0, EXYNOS4210_GPIO_F1, "gpf1"),
+	EXYNOS_PIN_BANK_EINTG(0x1C0, EXYNOS4210_GPIO_F2, "gpf2"),
+	EXYNOS_PIN_BANK_EINTG(0x1E0, EXYNOS4210_GPIO_F3, "gpf3"),
+};
+
+/* pin banks of exynos4210 pin-controller 1 */
+static struct samsung_pin_bank exynos4210_pin_banks1[] = {
+	EXYNOS_PIN_BANK_EINTG(0x000, EXYNOS4210_GPIO_J0, "gpj0"),
+	EXYNOS_PIN_BANK_EINTG(0x020, EXYNOS4210_GPIO_J1, "gpj1"),
+	EXYNOS_PIN_BANK_EINTG(0x040, EXYNOS4210_GPIO_K0, "gpk0"),
+	EXYNOS_PIN_BANK_EINTG(0x060, EXYNOS4210_GPIO_K1, "gpk1"),
+	EXYNOS_PIN_BANK_EINTG(0x080, EXYNOS4210_GPIO_K2, "gpk2"),
+	EXYNOS_PIN_BANK_EINTG(0x0A0, EXYNOS4210_GPIO_K3, "gpk3"),
+	EXYNOS_PIN_BANK_EINTG(0x0C0, EXYNOS4210_GPIO_L0, "gpl0"),
+	EXYNOS_PIN_BANK_EINTG(0x0E0, EXYNOS4210_GPIO_L1, "gpl1"),
+	EXYNOS_PIN_BANK_EINTG(0x100, EXYNOS4210_GPIO_L2, "gpl2"),
+	EXYNOS_PIN_BANK_EINTN(0x120, EXYNOS4210_GPIO_Y0, "gpy0"),
+	EXYNOS_PIN_BANK_EINTN(0x140, EXYNOS4210_GPIO_Y1, "gpy1"),
+	EXYNOS_PIN_BANK_EINTN(0x160, EXYNOS4210_GPIO_Y2, "gpy2"),
+	EXYNOS_PIN_BANK_EINTN(0x180, EXYNOS4210_GPIO_Y3, "gpy3"),
+	EXYNOS_PIN_BANK_EINTN(0x1A0, EXYNOS4210_GPIO_Y4, "gpy4"),
+	EXYNOS_PIN_BANK_EINTN(0x1C0, EXYNOS4210_GPIO_Y5, "gpy5"),
+	EXYNOS_PIN_BANK_EINTN(0x1E0, EXYNOS4210_GPIO_Y6, "gpy6"),
+	EXYNOS_PIN_BANK_EINTN(0xC00, EXYNOS4210_GPIO_X0, "gpx0"),
+	EXYNOS_PIN_BANK_EINTN(0xC20, EXYNOS4210_GPIO_X1, "gpx1"),
+	EXYNOS_PIN_BANK_EINTN(0xC40, EXYNOS4210_GPIO_X2, "gpx2"),
+	EXYNOS_PIN_BANK_EINTN(0xC60, EXYNOS4210_GPIO_X3, "gpx3"),
+};
+
+/* pin banks of exynos4210 pin-controller 2 */
+static struct samsung_pin_bank exynos4210_pin_banks2[] = {
+	EXYNOS_PIN_BANK_EINTN(0x000, EXYNOS4210_GPIO_Z, "gpz"),
+};
+
+/*
+ * Samsung pinctrl driver data for Exynos4210 SoC. Exynos4210 SoC includes
+ * three gpio/pin-mux/pinconfig controllers.
+ */
+struct samsung_pin_ctrl exynos4210_pin_ctrl[] = {
+	{
+		/* pin-controller instance 0 data */
+		.pin_banks	= exynos4210_pin_banks0,
+		.nr_banks	= ARRAY_SIZE(exynos4210_pin_banks0),
+		.base		= EXYNOS4210_GPIO_A0_START,
+		.nr_pins	= EXYNOS4210_GPIOA_NR_PINS,
+		.nr_gint	= EXYNOS4210_GPIOA_NR_GINT,
+		.geint_con	= EXYNOS_GPIO_ECON_OFFSET,
+		.geint_mask	= EXYNOS_GPIO_EMASK_OFFSET,
+		.geint_pend	= EXYNOS_GPIO_EPEND_OFFSET,
+		.svc		= EXYNOS_SVC_OFFSET,
+		.eint_gpio_init = exynos_eint_gpio_init,
+		.label		= "exynos4210-gpio-ctrl0",
+	}, {
+		/* pin-controller instance 1 data */
+		.pin_banks	= exynos4210_pin_banks1,
+		.nr_banks	= ARRAY_SIZE(exynos4210_pin_banks1),
+		.base		= EXYNOS4210_GPIOA_NR_PINS,
+		.nr_pins	= EXYNOS4210_GPIOB_NR_PINS,
+		.nr_gint	= EXYNOS4210_GPIOB_NR_GINT,
+		.nr_wint	= 32,
+		.geint_con	= EXYNOS_GPIO_ECON_OFFSET,
+		.geint_mask	= EXYNOS_GPIO_EMASK_OFFSET,
+		.geint_pend	= EXYNOS_GPIO_EPEND_OFFSET,
+		.weint_con	= EXYNOS_WKUP_ECON_OFFSET,
+		.weint_mask	= EXYNOS_WKUP_EMASK_OFFSET,
+		.weint_pend	= EXYNOS_WKUP_EPEND_OFFSET,
+		.svc		= EXYNOS_SVC_OFFSET,
+		.eint_gpio_init = exynos_eint_gpio_init,
+		.eint_wkup_init = exynos_eint_wkup_init,
+		.label		= "exynos4210-gpio-ctrl1",
+	}, {
+		/* pin-controller instance 2 data */
+		.pin_banks	= exynos4210_pin_banks2,
+		.nr_banks	= ARRAY_SIZE(exynos4210_pin_banks2),
+		.base		= EXYNOS4210_GPIOA_NR_PINS +
+					EXYNOS4210_GPIOB_NR_PINS,
+		.nr_pins	= EXYNOS4210_GPIOC_NR_PINS,
+		.label		= "exynos4210-gpio-ctrl2",
+	},
+};
diff --git a/drivers/pinctrl/pinctrl-exynos.h b/drivers/pinctrl/pinctrl-exynos.h
new file mode 100644
index 000000000000..31d0a06174e4
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-exynos.h
@@ -0,0 +1,218 @@
+/*
+ * Exynos specific definitions for Samsung pinctrl and gpiolib driver.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ * Copyright (c) 2012 Linaro Ltd
+ *		http://www.linaro.org
+ *
+ * This file contains the Exynos specific definitions for the Samsung
+ * pinctrl/gpiolib interface drivers.
+ *
+ * Author: Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#define EXYNOS_GPIO_START(__gpio)	((__gpio##_START) + (__gpio##_NR))
+
+#define EXYNOS4210_GPIO_A0_NR	(8)
+#define EXYNOS4210_GPIO_A1_NR	(6)
+#define EXYNOS4210_GPIO_B_NR	(8)
+#define EXYNOS4210_GPIO_C0_NR	(5)
+#define EXYNOS4210_GPIO_C1_NR	(5)
+#define EXYNOS4210_GPIO_D0_NR	(4)
+#define EXYNOS4210_GPIO_D1_NR	(4)
+#define EXYNOS4210_GPIO_E0_NR	(5)
+#define EXYNOS4210_GPIO_E1_NR	(8)
+#define EXYNOS4210_GPIO_E2_NR	(6)
+#define EXYNOS4210_GPIO_E3_NR	(8)
+#define EXYNOS4210_GPIO_E4_NR	(8)
+#define EXYNOS4210_GPIO_F0_NR	(8)
+#define EXYNOS4210_GPIO_F1_NR	(8)
+#define EXYNOS4210_GPIO_F2_NR	(8)
+#define EXYNOS4210_GPIO_F3_NR	(6)
+#define EXYNOS4210_GPIO_J0_NR	(8)
+#define EXYNOS4210_GPIO_J1_NR	(5)
+#define EXYNOS4210_GPIO_K0_NR	(7)
+#define EXYNOS4210_GPIO_K1_NR	(7)
+#define EXYNOS4210_GPIO_K2_NR	(7)
+#define EXYNOS4210_GPIO_K3_NR	(7)
+#define EXYNOS4210_GPIO_L0_NR	(8)
+#define EXYNOS4210_GPIO_L1_NR	(3)
+#define EXYNOS4210_GPIO_L2_NR	(8)
+#define EXYNOS4210_GPIO_Y0_NR	(6)
+#define EXYNOS4210_GPIO_Y1_NR	(4)
+#define EXYNOS4210_GPIO_Y2_NR	(6)
+#define EXYNOS4210_GPIO_Y3_NR	(8)
+#define EXYNOS4210_GPIO_Y4_NR	(8)
+#define EXYNOS4210_GPIO_Y5_NR	(8)
+#define EXYNOS4210_GPIO_Y6_NR	(8)
+#define EXYNOS4210_GPIO_X0_NR	(8)
+#define EXYNOS4210_GPIO_X1_NR	(8)
+#define EXYNOS4210_GPIO_X2_NR	(8)
+#define EXYNOS4210_GPIO_X3_NR	(8)
+#define EXYNOS4210_GPIO_Z_NR	(7)
+
+enum exynos4210_gpio_xa_start {
+	EXYNOS4210_GPIO_A0_START	= 0,
+	EXYNOS4210_GPIO_A1_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_A0),
+	EXYNOS4210_GPIO_B_START		= EXYNOS_GPIO_START(EXYNOS4210_GPIO_A1),
+	EXYNOS4210_GPIO_C0_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_B),
+	EXYNOS4210_GPIO_C1_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_C0),
+	EXYNOS4210_GPIO_D0_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_C1),
+	EXYNOS4210_GPIO_D1_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_D0),
+	EXYNOS4210_GPIO_E0_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_D1),
+	EXYNOS4210_GPIO_E1_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_E0),
+	EXYNOS4210_GPIO_E2_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_E1),
+	EXYNOS4210_GPIO_E3_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_E2),
+	EXYNOS4210_GPIO_E4_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_E3),
+	EXYNOS4210_GPIO_F0_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_E4),
+	EXYNOS4210_GPIO_F1_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_F0),
+	EXYNOS4210_GPIO_F2_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_F1),
+	EXYNOS4210_GPIO_F3_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_F2),
+};
+
+enum exynos4210_gpio_xb_start {
+	EXYNOS4210_GPIO_J0_START	= 0,
+	EXYNOS4210_GPIO_J1_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_J0),
+	EXYNOS4210_GPIO_K0_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_J1),
+	EXYNOS4210_GPIO_K1_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_K0),
+	EXYNOS4210_GPIO_K2_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_K1),
+	EXYNOS4210_GPIO_K3_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_K2),
+	EXYNOS4210_GPIO_L0_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_K3),
+	EXYNOS4210_GPIO_L1_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_L0),
+	EXYNOS4210_GPIO_L2_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_L1),
+	EXYNOS4210_GPIO_Y0_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_L2),
+	EXYNOS4210_GPIO_Y1_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y0),
+	EXYNOS4210_GPIO_Y2_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y1),
+	EXYNOS4210_GPIO_Y3_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y2),
+	EXYNOS4210_GPIO_Y4_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y3),
+	EXYNOS4210_GPIO_Y5_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y4),
+	EXYNOS4210_GPIO_Y6_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y5),
+	EXYNOS4210_GPIO_X0_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y6),
+	EXYNOS4210_GPIO_X1_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_X0),
+	EXYNOS4210_GPIO_X2_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_X1),
+	EXYNOS4210_GPIO_X3_START	= EXYNOS_GPIO_START(EXYNOS4210_GPIO_X2),
+};
+
+enum exynos4210_gpio_xc_start {
+	EXYNOS4210_GPIO_Z_START		= 0,
+};
+
+#define	EXYNOS4210_GPIO_A0_IRQ		EXYNOS4210_GPIO_A0_START
+#define	EXYNOS4210_GPIO_A1_IRQ		EXYNOS4210_GPIO_A1_START
+#define	EXYNOS4210_GPIO_B_IRQ		EXYNOS4210_GPIO_B_START
+#define	EXYNOS4210_GPIO_C0_IRQ		EXYNOS4210_GPIO_C0_START
+#define	EXYNOS4210_GPIO_C1_IRQ		EXYNOS4210_GPIO_C1_START
+#define	EXYNOS4210_GPIO_D0_IRQ		EXYNOS4210_GPIO_D0_START
+#define	EXYNOS4210_GPIO_D1_IRQ		EXYNOS4210_GPIO_D1_START
+#define	EXYNOS4210_GPIO_E0_IRQ		EXYNOS4210_GPIO_E0_START
+#define	EXYNOS4210_GPIO_E1_IRQ		EXYNOS4210_GPIO_E1_START
+#define	EXYNOS4210_GPIO_E2_IRQ		EXYNOS4210_GPIO_E2_START
+#define	EXYNOS4210_GPIO_E3_IRQ		EXYNOS4210_GPIO_E3_START
+#define	EXYNOS4210_GPIO_E4_IRQ		EXYNOS4210_GPIO_E4_START
+#define	EXYNOS4210_GPIO_F0_IRQ		EXYNOS4210_GPIO_F0_START
+#define	EXYNOS4210_GPIO_F1_IRQ		EXYNOS4210_GPIO_F1_START
+#define	EXYNOS4210_GPIO_F2_IRQ		EXYNOS4210_GPIO_F2_START
+#define	EXYNOS4210_GPIO_F3_IRQ		EXYNOS4210_GPIO_F3_START
+#define	EXYNOS4210_GPIO_J0_IRQ		EXYNOS4210_GPIO_J0_START
+#define	EXYNOS4210_GPIO_J1_IRQ		EXYNOS4210_GPIO_J1_START
+#define	EXYNOS4210_GPIO_K0_IRQ		EXYNOS4210_GPIO_K0_START
+#define	EXYNOS4210_GPIO_K1_IRQ		EXYNOS4210_GPIO_K1_START
+#define	EXYNOS4210_GPIO_K2_IRQ		EXYNOS4210_GPIO_K2_START
+#define	EXYNOS4210_GPIO_K3_IRQ		EXYNOS4210_GPIO_K3_START
+#define	EXYNOS4210_GPIO_L0_IRQ		EXYNOS4210_GPIO_L0_START
+#define	EXYNOS4210_GPIO_L1_IRQ		EXYNOS4210_GPIO_L1_START
+#define	EXYNOS4210_GPIO_L2_IRQ		EXYNOS4210_GPIO_L2_START
+#define	EXYNOS4210_GPIO_Z_IRQ		EXYNOS4210_GPIO_Z_START
+
+#define EXYNOS4210_GPIOA_NR_PINS	EXYNOS_GPIO_START(EXYNOS4210_GPIO_F3)
+#define EXYNOS4210_GPIOA_NR_GINT	EXYNOS_GPIO_START(EXYNOS4210_GPIO_F3)
+#define EXYNOS4210_GPIOB_NR_PINS	EXYNOS_GPIO_START(EXYNOS4210_GPIO_X3)
+#define EXYNOS4210_GPIOB_NR_GINT	EXYNOS_GPIO_START(EXYNOS4210_GPIO_L2)
+#define EXYNOS4210_GPIOC_NR_PINS	EXYNOS_GPIO_START(EXYNOS4210_GPIO_Z)
+
+/* External GPIO and wakeup interrupt related definitions */
+#define EXYNOS_GPIO_ECON_OFFSET		0x700
+#define EXYNOS_GPIO_EMASK_OFFSET	0x900
+#define EXYNOS_GPIO_EPEND_OFFSET	0xA00
+#define EXYNOS_WKUP_ECON_OFFSET		0xE00
+#define EXYNOS_WKUP_EMASK_OFFSET	0xF00
+#define EXYNOS_WKUP_EPEND_OFFSET	0xF40
+#define EXYNOS_SVC_OFFSET		0xB08
+#define EXYNOS_EINT_FUNC		0xF
+
+/* helpers to access interrupt service register */
+#define EXYNOS_SVC_GROUP_SHIFT		3
+#define EXYNOS_SVC_GROUP_MASK		0x1f
+#define EXYNOS_SVC_NUM_MASK		7
+#define EXYNOS_SVC_GROUP(x)		((x >> EXYNOS_SVC_GROUP_SHIFT) & \
+						EXYNOS_SVC_GROUP_MASK)
+
+/* Exynos specific external interrupt trigger types */
+#define EXYNOS_EINT_LEVEL_LOW		0
+#define EXYNOS_EINT_LEVEL_HIGH		1
+#define EXYNOS_EINT_EDGE_FALLING	2
+#define EXYNOS_EINT_EDGE_RISING		3
+#define EXYNOS_EINT_EDGE_BOTH		4
+#define EXYNOS_EINT_CON_MASK		0xF
+#define EXYNOS_EINT_CON_LEN		4
+
+#define EXYNOS_EINT_MAX_PER_BANK	8
+#define EXYNOS_EINT_NR_WKUP_EINT
+
+#define EXYNOS_PIN_BANK_EINTN(reg, __gpio, id)		\
+	{						\
+		.pctl_offset	= reg,			\
+		.pin_base	= (__gpio##_START),	\
+		.nr_pins	= (__gpio##_NR),	\
+		.func_width	= 4,			\
+		.pud_width	= 2,			\
+		.drv_width	= 2,			\
+		.conpdn_width	= 2,			\
+		.pudpdn_width	= 2,			\
+		.eint_type	= EINT_TYPE_NONE,	\
+		.name		= id			\
+	}
+
+#define EXYNOS_PIN_BANK_EINTG(reg, __gpio, id)		\
+	{						\
+		.pctl_offset	= reg,			\
+		.pin_base	= (__gpio##_START),	\
+		.nr_pins	= (__gpio##_NR),	\
+		.func_width	= 4,			\
+		.pud_width	= 2,			\
+		.drv_width	= 2,			\
+		.conpdn_width	= 2,			\
+		.pudpdn_width	= 2,			\
+		.eint_type	= EINT_TYPE_GPIO,	\
+		.irq_base	= (__gpio##_IRQ),	\
+		.name		= id			\
+	}
+
+/**
+ * struct exynos_geint_data: gpio eint specific data for irq_chip callbacks.
+ * @bank: pin bank from which this gpio interrupt originates.
+ * @pin: pin number within the bank.
+ * @eint_offset: offset to be added to the con/pend/mask register bank base.
+ */
+struct exynos_geint_data {
+	struct samsung_pin_bank	*bank;
+	u32			pin;
+	u32			eint_offset;
+};
+
+/**
+ * struct exynos_weint_data: irq specific data for all the wakeup interrupts
+ * generated by the external wakeup interrupt controller.
+ * @domain: irq domain representing the external wakeup interrupts
+ * @irq: interrupt number within the domain.
+ */
+struct exynos_weint_data {
+	struct irq_domain	*domain;
+	u32			irq;
+};
diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c
new file mode 100644
index 000000000000..dd108a94acf9
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-samsung.c
@@ -0,0 +1,888 @@
+/*
+ * pin-controller/pin-mux/pin-config/gpio-driver for Samsung's SoC's.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ * Copyright (c) 2012 Linaro Ltd
+ *		http://www.linaro.org
+ *
+ * Author: Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver implements the Samsung pinctrl driver. It supports setting up of
+ * pinmux and pinconf configurations. The gpiolib interface is also included.
+ * External interrupt (gpio and wakeup) support are not included in this driver
+ * but provides extensions to which platform specific implementation of the gpio
+ * and wakeup interrupts can be hooked to.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+
+#include "core.h"
+#include "pinctrl-samsung.h"
+
+#define GROUP_SUFFIX		"-grp"
+#define GSUFFIX_LEN		sizeof(GROUP_SUFFIX)
+#define FUNCTION_SUFFIX		"-mux"
+#define FSUFFIX_LEN		sizeof(FUNCTION_SUFFIX)
+
+/* list of all possible config options supported */
+struct pin_config {
+	char		*prop_cfg;
+	unsigned int	cfg_type;
+} pcfgs[] = {
+	{ "samsung,pin-pud", PINCFG_TYPE_PUD },
+	{ "samsung,pin-drv", PINCFG_TYPE_DRV },
+	{ "samsung,pin-con-pdn", PINCFG_TYPE_CON_PDN },
+	{ "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN },
+};
+
+/* check if the selector is a valid pin group selector */
+static int samsung_get_group_count(struct pinctrl_dev *pctldev)
+{
+	struct samsung_pinctrl_drv_data *drvdata;
+
+	drvdata = pinctrl_dev_get_drvdata(pctldev);
+	return drvdata->nr_groups;
+}
+
+/* return the name of the group selected by the group selector */
+static const char *samsung_get_group_name(struct pinctrl_dev *pctldev,
+						unsigned selector)
+{
+	struct samsung_pinctrl_drv_data *drvdata;
+
+	drvdata = pinctrl_dev_get_drvdata(pctldev);
+	return drvdata->pin_groups[selector].name;
+}
+
+/* return the pin numbers associated with the specified group */
+static int samsung_get_group_pins(struct pinctrl_dev *pctldev,
+		unsigned selector, const unsigned **pins, unsigned *num_pins)
+{
+	struct samsung_pinctrl_drv_data *drvdata;
+
+	drvdata = pinctrl_dev_get_drvdata(pctldev);
+	*pins = drvdata->pin_groups[selector].pins;
+	*num_pins = drvdata->pin_groups[selector].num_pins;
+	return 0;
+}
+
+/* create pinctrl_map entries by parsing device tree nodes */
+static int samsung_dt_node_to_map(struct pinctrl_dev *pctldev,
+			struct device_node *np, struct pinctrl_map **maps,
+			unsigned *nmaps)
+{
+	struct device *dev = pctldev->dev;
+	struct pinctrl_map *map;
+	unsigned long *cfg = NULL;
+	char *gname, *fname;
+	int cfg_cnt = 0, map_cnt = 0, idx = 0;
+
+	/* count the number of config options specfied in the node */
+	for (idx = 0; idx < ARRAY_SIZE(pcfgs); idx++) {
+		if (of_find_property(np, pcfgs[idx].prop_cfg, NULL))
+			cfg_cnt++;
+	}
+
+	/*
+	 * Find out the number of map entries to create. All the config options
+	 * can be accomadated into a single config map entry.
+	 */
+	if (cfg_cnt)
+		map_cnt = 1;
+	if (of_find_property(np, "samsung,pin-function", NULL))
+		map_cnt++;
+	if (!map_cnt) {
+		dev_err(dev, "node %s does not have either config or function "
+				"configurations\n", np->name);
+		return -EINVAL;
+	}
+
+	/* Allocate memory for pin-map entries */
+	map = kzalloc(sizeof(*map) * map_cnt, GFP_KERNEL);
+	if (!map) {
+		dev_err(dev, "could not alloc memory for pin-maps\n");
+		return -ENOMEM;
+	}
+	*nmaps = 0;
+
+	/*
+	 * Allocate memory for pin group name. The pin group name is derived
+	 * from the node name from which these map entries are be created.
+	 */
+	gname = kzalloc(strlen(np->name) + GSUFFIX_LEN, GFP_KERNEL);
+	if (!gname) {
+		dev_err(dev, "failed to alloc memory for group name\n");
+		goto free_map;
+	}
+	sprintf(gname, "%s%s", np->name, GROUP_SUFFIX);
+
+	/*
+	 * don't have config options? then skip over to creating function
+	 * map entries.
+	 */
+	if (!cfg_cnt)
+		goto skip_cfgs;
+
+	/* Allocate memory for config entries */
+	cfg = kzalloc(sizeof(*cfg) * cfg_cnt, GFP_KERNEL);
+	if (!cfg) {
+		dev_err(dev, "failed to alloc memory for configs\n");
+		goto free_gname;
+	}
+
+	/* Prepare a list of config settings */
+	for (idx = 0, cfg_cnt = 0; idx < ARRAY_SIZE(pcfgs); idx++) {
+		u32 value;
+		if (!of_property_read_u32(np, pcfgs[idx].prop_cfg, &value))
+			cfg[cfg_cnt++] =
+				PINCFG_PACK(pcfgs[idx].cfg_type, value);
+	}
+
+	/* create the config map entry */
+	map[*nmaps].data.configs.group_or_pin = gname;
+	map[*nmaps].data.configs.configs = cfg;
+	map[*nmaps].data.configs.num_configs = cfg_cnt;
+	map[*nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
+	*nmaps += 1;
+
+skip_cfgs:
+	/* create the function map entry */
+	if (of_find_property(np, "samsung,pin-function", NULL)) {
+		fname = kzalloc(strlen(np->name) + FSUFFIX_LEN,	GFP_KERNEL);
+		if (!fname) {
+			dev_err(dev, "failed to alloc memory for func name\n");
+			goto free_cfg;
+		}
+		sprintf(fname, "%s%s", np->name, FUNCTION_SUFFIX);
+
+		map[*nmaps].data.mux.group = gname;
+		map[*nmaps].data.mux.function = fname;
+		map[*nmaps].type = PIN_MAP_TYPE_MUX_GROUP;
+		*nmaps += 1;
+	}
+
+	*maps = map;
+	return 0;
+
+free_cfg:
+	kfree(cfg);
+free_gname:
+	kfree(gname);
+free_map:
+	kfree(map);
+	return -ENOMEM;
+}
+
+/* free the memory allocated to hold the pin-map table */
+static void samsung_dt_free_map(struct pinctrl_dev *pctldev,
+			     struct pinctrl_map *map, unsigned num_maps)
+{
+	int idx;
+
+	for (idx = 0; idx < num_maps; idx++) {
+		if (map[idx].type == PIN_MAP_TYPE_MUX_GROUP) {
+			kfree(map[idx].data.mux.function);
+			if (!idx)
+				kfree(map[idx].data.mux.group);
+		} else if (map->type == PIN_MAP_TYPE_CONFIGS_GROUP) {
+			kfree(map[idx].data.configs.configs);
+			if (!idx)
+				kfree(map[idx].data.configs.group_or_pin);
+		}
+	};
+
+	kfree(map);
+}
+
+/* list of pinctrl callbacks for the pinctrl core */
+static struct pinctrl_ops samsung_pctrl_ops = {
+	.get_groups_count	= samsung_get_group_count,
+	.get_group_name		= samsung_get_group_name,
+	.get_group_pins		= samsung_get_group_pins,
+	.dt_node_to_map		= samsung_dt_node_to_map,
+	.dt_free_map		= samsung_dt_free_map,
+};
+
+/* check if the selector is a valid pin function selector */
+static int samsung_get_functions_count(struct pinctrl_dev *pctldev)
+{
+	struct samsung_pinctrl_drv_data *drvdata;
+
+	drvdata = pinctrl_dev_get_drvdata(pctldev);
+	return drvdata->nr_functions;
+}
+
+/* return the name of the pin function specified */
+static const char *samsung_pinmux_get_fname(struct pinctrl_dev *pctldev,
+						unsigned selector)
+{
+	struct samsung_pinctrl_drv_data *drvdata;
+
+	drvdata = pinctrl_dev_get_drvdata(pctldev);
+	return drvdata->pmx_functions[selector].name;
+}
+
+/* return the groups associated for the specified function selector */
+static int samsung_pinmux_get_groups(struct pinctrl_dev *pctldev,
+		unsigned selector, const char * const **groups,
+		unsigned * const num_groups)
+{
+	struct samsung_pinctrl_drv_data *drvdata;
+
+	drvdata = pinctrl_dev_get_drvdata(pctldev);
+	*groups = drvdata->pmx_functions[selector].groups;
+	*num_groups = drvdata->pmx_functions[selector].num_groups;
+	return 0;
+}
+
+/*
+ * given a pin number that is local to a pin controller, find out the pin bank
+ * and the register base of the pin bank.
+ */
+static void pin_to_reg_bank(struct gpio_chip *gc, unsigned pin,
+			void __iomem **reg, u32 *offset,
+			struct samsung_pin_bank **bank)
+{
+	struct samsung_pinctrl_drv_data *drvdata;
+	struct samsung_pin_bank *b;
+
+	drvdata = dev_get_drvdata(gc->dev);
+	b = drvdata->ctrl->pin_banks;
+
+	while ((pin >= b->pin_base) &&
+			((b->pin_base + b->nr_pins - 1) < pin))
+		b++;
+
+	*reg = drvdata->virt_base + b->pctl_offset;
+	*offset = pin - b->pin_base;
+	if (bank)
+		*bank = b;
+
+	/* some banks have two config registers in a single bank */
+	if (*offset * b->func_width > BITS_PER_LONG)
+		*reg += 4;
+}
+
+/* enable or disable a pinmux function */
+static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
+					unsigned group, bool enable)
+{
+	struct samsung_pinctrl_drv_data *drvdata;
+	const unsigned int *pins;
+	struct samsung_pin_bank *bank;
+	void __iomem *reg;
+	u32 mask, shift, data, pin_offset, cnt;
+
+	drvdata = pinctrl_dev_get_drvdata(pctldev);
+	pins = drvdata->pin_groups[group].pins;
+
+	/*
+	 * for each pin in the pin group selected, program the correspoding pin
+	 * pin function number in the config register.
+	 */
+	for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) {
+		pin_to_reg_bank(drvdata->gc, pins[cnt] - drvdata->ctrl->base,
+				&reg, &pin_offset, &bank);
+		mask = (1 << bank->func_width) - 1;
+		shift = pin_offset * bank->func_width;
+
+		data = readl(reg);
+		data &= ~(mask << shift);
+		if (enable)
+			data |= drvdata->pin_groups[group].func << shift;
+		writel(data, reg);
+	}
+}
+
+/* enable a specified pinmux by writing to registers */
+static int samsung_pinmux_enable(struct pinctrl_dev *pctldev, unsigned selector,
+					unsigned group)
+{
+	samsung_pinmux_setup(pctldev, selector, group, true);
+	return 0;
+}
+
+/* disable a specified pinmux by writing to registers */
+static void samsung_pinmux_disable(struct pinctrl_dev *pctldev,
+					unsigned selector, unsigned group)
+{
+	samsung_pinmux_setup(pctldev, selector, group, false);
+}
+
+/*
+ * The calls to gpio_direction_output() and gpio_direction_input()
+ * leads to this function call (via the pinctrl_gpio_direction_{input|output}()
+ * function called from the gpiolib interface).
+ */
+static int samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev,
+		struct pinctrl_gpio_range *range, unsigned offset, bool input)
+{
+	struct samsung_pin_bank *bank;
+	void __iomem *reg;
+	u32 data, pin_offset, mask, shift;
+
+	pin_to_reg_bank(range->gc, offset, &reg, &pin_offset, &bank);
+	mask = (1 << bank->func_width) - 1;
+	shift = pin_offset * bank->func_width;
+
+	data = readl(reg);
+	data &= ~(mask << shift);
+	if (!input)
+		data |= FUNC_OUTPUT << shift;
+	writel(data, reg);
+	return 0;
+}
+
+/* list of pinmux callbacks for the pinmux vertical in pinctrl core */
+static struct pinmux_ops samsung_pinmux_ops = {
+	.get_functions_count	= samsung_get_functions_count,
+	.get_function_name	= samsung_pinmux_get_fname,
+	.get_function_groups	= samsung_pinmux_get_groups,
+	.enable			= samsung_pinmux_enable,
+	.disable		= samsung_pinmux_disable,
+	.gpio_set_direction	= samsung_pinmux_gpio_set_direction,
+};
+
+/* set or get the pin config settings for a specified pin */
+static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin,
+				unsigned long *config, bool set)
+{
+	struct samsung_pinctrl_drv_data *drvdata;
+	struct samsung_pin_bank *bank;
+	void __iomem *reg_base;
+	enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE(*config);
+	u32 data, width, pin_offset, mask, shift;
+	u32 cfg_value, cfg_reg;
+
+	drvdata = pinctrl_dev_get_drvdata(pctldev);
+	pin_to_reg_bank(drvdata->gc, pin - drvdata->ctrl->base, &reg_base,
+					&pin_offset, &bank);
+
+	switch (cfg_type) {
+	case PINCFG_TYPE_PUD:
+		width = bank->pud_width;
+		cfg_reg = PUD_REG;
+		break;
+	case PINCFG_TYPE_DRV:
+		width = bank->drv_width;
+		cfg_reg = DRV_REG;
+		break;
+	case PINCFG_TYPE_CON_PDN:
+		width = bank->conpdn_width;
+		cfg_reg = CONPDN_REG;
+		break;
+	case PINCFG_TYPE_PUD_PDN:
+		width = bank->pudpdn_width;
+		cfg_reg = PUDPDN_REG;
+		break;
+	default:
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	mask = (1 << width) - 1;
+	shift = pin_offset * width;
+	data = readl(reg_base + cfg_reg);
+
+	if (set) {
+		cfg_value = PINCFG_UNPACK_VALUE(*config);
+		data &= ~(mask << shift);
+		data |= (cfg_value << shift);
+		writel(data, reg_base + cfg_reg);
+	} else {
+		data >>= shift;
+		data &= mask;
+		*config = PINCFG_PACK(cfg_type, data);
+	}
+	return 0;
+}
+
+/* set the pin config settings for a specified pin */
+static int samsung_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
+				unsigned long config)
+{
+	return samsung_pinconf_rw(pctldev, pin, &config, true);
+}
+
+/* get the pin config settings for a specified pin */
+static int samsung_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
+					unsigned long *config)
+{
+	return samsung_pinconf_rw(pctldev, pin, config, false);
+}
+
+/* set the pin config settings for a specified pin group */
+static int samsung_pinconf_group_set(struct pinctrl_dev *pctldev,
+			unsigned group, unsigned long config)
+{
+	struct samsung_pinctrl_drv_data *drvdata;
+	const unsigned int *pins;
+	unsigned int cnt;
+
+	drvdata = pinctrl_dev_get_drvdata(pctldev);
+	pins = drvdata->pin_groups[group].pins;
+
+	for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++)
+		samsung_pinconf_set(pctldev, pins[cnt], config);
+
+	return 0;
+}
+
+/* get the pin config settings for a specified pin group */
+static int samsung_pinconf_group_get(struct pinctrl_dev *pctldev,
+				unsigned int group, unsigned long *config)
+{
+	struct samsung_pinctrl_drv_data *drvdata;
+	const unsigned int *pins;
+
+	drvdata = pinctrl_dev_get_drvdata(pctldev);
+	pins = drvdata->pin_groups[group].pins;
+	samsung_pinconf_get(pctldev, pins[0], config);
+	return 0;
+}
+
+/* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */
+static struct pinconf_ops samsung_pinconf_ops = {
+	.pin_config_get		= samsung_pinconf_get,
+	.pin_config_set		= samsung_pinconf_set,
+	.pin_config_group_get	= samsung_pinconf_group_get,
+	.pin_config_group_set	= samsung_pinconf_group_set,
+};
+
+/* gpiolib gpio_set callback function */
+static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+	void __iomem *reg;
+	u32 pin_offset, data;
+
+	pin_to_reg_bank(gc, offset, &reg, &pin_offset, NULL);
+	data = readl(reg + DAT_REG);
+	data &= ~(1 << pin_offset);
+	if (value)
+		data |= 1 << pin_offset;
+	writel(data, reg + DAT_REG);
+}
+
+/* gpiolib gpio_get callback function */
+static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+	void __iomem *reg;
+	u32 pin_offset, data;
+
+	pin_to_reg_bank(gc, offset, &reg, &pin_offset, NULL);
+	data = readl(reg + DAT_REG);
+	data >>= pin_offset;
+	data &= 1;
+	return data;
+}
+
+/*
+ * gpiolib gpio_direction_input callback function. The setting of the pin
+ * mux function as 'gpio input' will be handled by the pinctrl susbsystem
+ * interface.
+ */
+static int samsung_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+	return pinctrl_gpio_direction_input(gc->base + offset);
+}
+
+/*
+ * gpiolib gpio_direction_output callback function. The setting of the pin
+ * mux function as 'gpio output' will be handled by the pinctrl susbsystem
+ * interface.
+ */
+static int samsung_gpio_direction_output(struct gpio_chip *gc, unsigned offset,
+							int value)
+{
+	samsung_gpio_set(gc, offset, value);
+	return pinctrl_gpio_direction_output(gc->base + offset);
+}
+
+/*
+ * Parse the pin names listed in the 'samsung,pins' property and convert it
+ * into a list of gpio numbers are create a pin group from it.
+ */
+static int __init samsung_pinctrl_parse_dt_pins(struct platform_device *pdev,
+			struct device_node *cfg_np, struct pinctrl_desc *pctl,
+			unsigned int **pin_list, unsigned int *npins)
+{
+	struct device *dev = &pdev->dev;
+	struct property *prop;
+	struct pinctrl_pin_desc const *pdesc = pctl->pins;
+	unsigned int idx = 0, cnt;
+	const char *pin_name;
+
+	*npins = of_property_count_strings(cfg_np, "samsung,pins");
+	if (*npins < 0) {
+		dev_err(dev, "invalid pin list in %s node", cfg_np->name);
+		return -EINVAL;
+	}
+
+	*pin_list = devm_kzalloc(dev, *npins * sizeof(**pin_list), GFP_KERNEL);
+	if (!*pin_list) {
+		dev_err(dev, "failed to allocate memory for pin list\n");
+		return -ENOMEM;
+	}
+
+	of_property_for_each_string(cfg_np, "samsung,pins", prop, pin_name) {
+		for (cnt = 0; cnt < pctl->npins; cnt++) {
+			if (pdesc[cnt].name) {
+				if (!strcmp(pin_name, pdesc[cnt].name)) {
+					(*pin_list)[idx++] = pdesc[cnt].number;
+					break;
+				}
+			}
+		}
+		if (cnt == pctl->npins) {
+			dev_err(dev, "pin %s not valid in %s node\n",
+					pin_name, cfg_np->name);
+			devm_kfree(dev, *pin_list);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Parse the information about all the available pin groups and pin functions
+ * from device node of the pin-controller. A pin group is formed with all
+ * the pins listed in the "samsung,pins" property.
+ */
+static int __init samsung_pinctrl_parse_dt(struct platform_device *pdev,
+				struct samsung_pinctrl_drv_data *drvdata)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *dev_np = dev->of_node;
+	struct device_node *cfg_np;
+	struct samsung_pin_group *groups, *grp;
+	struct samsung_pmx_func *functions, *func;
+	unsigned *pin_list;
+	unsigned int npins, grp_cnt, func_idx = 0;
+	char *gname, *fname;
+	int ret;
+
+	grp_cnt = of_get_child_count(dev_np);
+	if (!grp_cnt)
+		return -EINVAL;
+
+	groups = devm_kzalloc(dev, grp_cnt * sizeof(*groups), GFP_KERNEL);
+	if (!groups) {
+		dev_err(dev, "failed allocate memory for ping group list\n");
+		return -EINVAL;
+	}
+	grp = groups;
+
+	functions = devm_kzalloc(dev, grp_cnt * sizeof(*functions), GFP_KERNEL);
+	if (!functions) {
+		dev_err(dev, "failed to allocate memory for function list\n");
+		return -EINVAL;
+	}
+	func = functions;
+
+	/*
+	 * Iterate over all the child nodes of the pin controller node
+	 * and create pin groups and pin function lists.
+	 */
+	for_each_child_of_node(dev_np, cfg_np) {
+		u32 function;
+		if (of_find_property(cfg_np, "interrupt-controller", NULL))
+			continue;
+
+		ret = samsung_pinctrl_parse_dt_pins(pdev, cfg_np,
+					&drvdata->pctl,	&pin_list, &npins);
+		if (ret)
+			return ret;
+
+		/* derive pin group name from the node name */
+		gname = devm_kzalloc(dev, strlen(cfg_np->name) + GSUFFIX_LEN,
+					GFP_KERNEL);
+		if (!gname) {
+			dev_err(dev, "failed to alloc memory for group name\n");
+			return -ENOMEM;
+		}
+		sprintf(gname, "%s%s", cfg_np->name, GROUP_SUFFIX);
+
+		grp->name = gname;
+		grp->pins = pin_list;
+		grp->num_pins = npins;
+		of_property_read_u32(cfg_np, "samsung,pin-function", &function);
+		grp->func = function;
+		grp++;
+
+		if (!of_find_property(cfg_np, "samsung,pin-function", NULL))
+			continue;
+
+		/* derive function name from the node name */
+		fname = devm_kzalloc(dev, strlen(cfg_np->name) + FSUFFIX_LEN,
+					GFP_KERNEL);
+		if (!fname) {
+			dev_err(dev, "failed to alloc memory for func name\n");
+			return -ENOMEM;
+		}
+		sprintf(fname, "%s%s", cfg_np->name, FUNCTION_SUFFIX);
+
+		func->name = fname;
+		func->groups = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL);
+		if (!func->groups) {
+			dev_err(dev, "failed to alloc memory for group list "
+					"in pin function");
+			return -ENOMEM;
+		}
+		func->groups[0] = gname;
+		func->num_groups = 1;
+		func++;
+		func_idx++;
+	}
+
+	drvdata->pin_groups = groups;
+	drvdata->nr_groups = grp_cnt;
+	drvdata->pmx_functions = functions;
+	drvdata->nr_functions = func_idx;
+
+	return 0;
+}
+
+/* register the pinctrl interface with the pinctrl subsystem */
+static int __init samsung_pinctrl_register(struct platform_device *pdev,
+				struct samsung_pinctrl_drv_data *drvdata)
+{
+	struct pinctrl_desc *ctrldesc = &drvdata->pctl;
+	struct pinctrl_pin_desc *pindesc, *pdesc;
+	struct samsung_pin_bank *pin_bank;
+	char *pin_names;
+	int pin, bank, ret;
+
+	ctrldesc->name = "samsung-pinctrl";
+	ctrldesc->owner = THIS_MODULE;
+	ctrldesc->pctlops = &samsung_pctrl_ops;
+	ctrldesc->pmxops = &samsung_pinmux_ops;
+	ctrldesc->confops = &samsung_pinconf_ops;
+
+	pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) *
+			drvdata->ctrl->nr_pins, GFP_KERNEL);
+	if (!pindesc) {
+		dev_err(&pdev->dev, "mem alloc for pin descriptors failed\n");
+		return -ENOMEM;
+	}
+	ctrldesc->pins = pindesc;
+	ctrldesc->npins = drvdata->ctrl->nr_pins;
+	ctrldesc->npins = drvdata->ctrl->nr_pins;
+
+	/* dynamically populate the pin number and pin name for pindesc */
+	for (pin = 0, pdesc = pindesc; pin < ctrldesc->npins; pin++, pdesc++)
+		pdesc->number = pin + drvdata->ctrl->base;
+
+	/*
+	 * allocate space for storing the dynamically generated names for all
+	 * the pins which belong to this pin-controller.
+	 */
+	pin_names = devm_kzalloc(&pdev->dev, sizeof(char) * PIN_NAME_LENGTH *
+					drvdata->ctrl->nr_pins, GFP_KERNEL);
+	if (!pin_names) {
+		dev_err(&pdev->dev, "mem alloc for pin names failed\n");
+		return -ENOMEM;
+	}
+
+	/* for each pin, the name of the pin is pin-bank name + pin number */
+	for (bank = 0; bank < drvdata->ctrl->nr_banks; bank++) {
+		pin_bank = &drvdata->ctrl->pin_banks[bank];
+		for (pin = 0; pin < pin_bank->nr_pins; pin++) {
+			sprintf(pin_names, "%s-%d", pin_bank->name, pin);
+			pdesc = pindesc + pin_bank->pin_base + pin;
+			pdesc->name = pin_names;
+			pin_names += PIN_NAME_LENGTH;
+		}
+	}
+
+	drvdata->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, drvdata);
+	if (!drvdata->pctl_dev) {
+		dev_err(&pdev->dev, "could not register pinctrl driver\n");
+		return -EINVAL;
+	}
+
+	drvdata->grange.name = "samsung-pctrl-gpio-range";
+	drvdata->grange.id = 0;
+	drvdata->grange.base = drvdata->ctrl->base;
+	drvdata->grange.npins = drvdata->ctrl->nr_pins;
+	drvdata->grange.gc = drvdata->gc;
+	pinctrl_add_gpio_range(drvdata->pctl_dev, &drvdata->grange);
+
+	ret = samsung_pinctrl_parse_dt(pdev, drvdata);
+	if (ret) {
+		pinctrl_unregister(drvdata->pctl_dev);
+		return ret;
+	}
+
+	return 0;
+}
+
+/* register the gpiolib interface with the gpiolib subsystem */
+static int __init samsung_gpiolib_register(struct platform_device *pdev,
+				struct samsung_pinctrl_drv_data *drvdata)
+{
+	struct gpio_chip *gc;
+	int ret;
+
+	gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
+	if (!gc) {
+		dev_err(&pdev->dev, "mem alloc for gpio_chip failed\n");
+		return -ENOMEM;
+	}
+
+	drvdata->gc = gc;
+	gc->base = drvdata->ctrl->base;
+	gc->ngpio = drvdata->ctrl->nr_pins;
+	gc->dev = &pdev->dev;
+	gc->set = samsung_gpio_set;
+	gc->get = samsung_gpio_get;
+	gc->direction_input = samsung_gpio_direction_input;
+	gc->direction_output = samsung_gpio_direction_output;
+	gc->label = drvdata->ctrl->label;
+	gc->owner = THIS_MODULE;
+	ret = gpiochip_add(gc);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register gpio_chip %s, error "
+					"code: %d\n", gc->label, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/* unregister the gpiolib interface with the gpiolib subsystem */
+static int __init samsung_gpiolib_unregister(struct platform_device *pdev,
+				struct samsung_pinctrl_drv_data *drvdata)
+{
+	int ret = gpiochip_remove(drvdata->gc);
+	if (ret) {
+		dev_err(&pdev->dev, "gpio chip remove failed\n");
+		return ret;
+	}
+	return 0;
+}
+
+static const struct of_device_id samsung_pinctrl_dt_match[];
+
+/* retrieve the soc specific data */
+static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data(
+				struct platform_device *pdev)
+{
+	int id;
+	const struct of_device_id *match;
+	const struct device_node *node = pdev->dev.of_node;
+
+	id = of_alias_get_id(pdev->dev.of_node, "pinctrl");
+	if (id < 0) {
+		dev_err(&pdev->dev, "failed to get alias id\n");
+		return NULL;
+	}
+	match = of_match_node(samsung_pinctrl_dt_match, node);
+	return (struct samsung_pin_ctrl *)match->data + id;
+}
+
+static int __devinit samsung_pinctrl_probe(struct platform_device *pdev)
+{
+	struct samsung_pinctrl_drv_data *drvdata;
+	struct device *dev = &pdev->dev;
+	struct samsung_pin_ctrl *ctrl;
+	struct resource *res;
+	int ret;
+
+	if (!dev->of_node) {
+		dev_err(dev, "device tree node not found\n");
+		return -ENODEV;
+	}
+
+	ctrl = samsung_pinctrl_get_soc_data(pdev);
+	if (!ctrl) {
+		dev_err(&pdev->dev, "driver data not available\n");
+		return -EINVAL;
+	}
+
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata) {
+		dev_err(dev, "failed to allocate memory for driver's "
+				"private data\n");
+		return -ENOMEM;
+	}
+	drvdata->ctrl = ctrl;
+	drvdata->dev = dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "cannot find IO resource\n");
+		return -ENOENT;
+	}
+
+	drvdata->virt_base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!drvdata->virt_base) {
+		dev_err(dev, "ioremap failed\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res)
+		drvdata->irq = res->start;
+
+	ret = samsung_gpiolib_register(pdev, drvdata);
+	if (ret)
+		return ret;
+
+	ret = samsung_pinctrl_register(pdev, drvdata);
+	if (ret) {
+		samsung_gpiolib_unregister(pdev, drvdata);
+		return ret;
+	}
+
+	if (ctrl->eint_gpio_init)
+		ctrl->eint_gpio_init(drvdata);
+	if (ctrl->eint_wkup_init)
+		ctrl->eint_wkup_init(drvdata);
+
+	platform_set_drvdata(pdev, drvdata);
+	return 0;
+}
+
+static const struct of_device_id samsung_pinctrl_dt_match[] = {
+	{ .compatible = "samsung,pinctrl-exynos4210",
+		.data = (void *)exynos4210_pin_ctrl },
+	{},
+};
+MODULE_DEVICE_TABLE(of, samsung_pinctrl_dt_match);
+
+static struct platform_driver samsung_pinctrl_driver = {
+	.probe		= samsung_pinctrl_probe,
+	.driver = {
+		.name	= "samsung-pinctrl",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(samsung_pinctrl_dt_match),
+	},
+};
+
+static int __init samsung_pinctrl_drv_register(void)
+{
+	return platform_driver_register(&samsung_pinctrl_driver);
+}
+postcore_initcall(samsung_pinctrl_drv_register);
+
+static void __exit samsung_pinctrl_drv_unregister(void)
+{
+	platform_driver_unregister(&samsung_pinctrl_driver);
+}
+module_exit(samsung_pinctrl_drv_unregister);
+
+MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>");
+MODULE_DESCRIPTION("Samsung pinctrl driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h
new file mode 100644
index 000000000000..b8956934cda6
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-samsung.h
@@ -0,0 +1,239 @@
+/*
+ * pin-controller/pin-mux/pin-config/gpio-driver for Samsung's SoC's.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ * Copyright (c) 2012 Linaro Ltd
+ *		http://www.linaro.org
+ *
+ * Author: Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __PINCTRL_SAMSUNG_H
+#define __PINCTRL_SAMSUNG_H
+
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/machine.h>
+
+/* register offsets within a pin bank */
+#define DAT_REG		0x4
+#define PUD_REG		0x8
+#define DRV_REG		0xC
+#define CONPDN_REG	0x10
+#define PUDPDN_REG	0x14
+
+/* pinmux function number for pin as gpio output line */
+#define FUNC_OUTPUT	0x1
+
+/**
+ * enum pincfg_type - possible pin configuration types supported.
+ * @PINCFG_TYPE_PUD: Pull up/down configuration.
+ * @PINCFG_TYPE_DRV: Drive strength configuration.
+ * @PINCFG_TYPE_CON_PDN: Pin function in power down mode.
+ * @PINCFG_TYPE_PUD_PDN: Pull up/down configuration in power down mode.
+ */
+enum pincfg_type {
+	PINCFG_TYPE_PUD,
+	PINCFG_TYPE_DRV,
+	PINCFG_TYPE_CON_PDN,
+	PINCFG_TYPE_PUD_PDN,
+};
+
+/*
+ * pin configuration (pull up/down and drive strength) type and its value are
+ * packed together into a 16-bits. The upper 8-bits represent the configuration
+ * type and the lower 8-bits hold the value of the configuration type.
+ */
+#define PINCFG_TYPE_MASK		0xFF
+#define PINCFG_VALUE_SHIFT		8
+#define PINCFG_VALUE_MASK		(0xFF << PINCFG_VALUE_SHIFT)
+#define PINCFG_PACK(type, value)	(((value) << PINCFG_VALUE_SHIFT) | type)
+#define PINCFG_UNPACK_TYPE(cfg)		((cfg) & PINCFG_TYPE_MASK)
+#define PINCFG_UNPACK_VALUE(cfg)	(((cfg) & PINCFG_VALUE_MASK) >> \
+						PINCFG_VALUE_SHIFT)
+/**
+ * enum eint_type - possible external interrupt types.
+ * @EINT_TYPE_NONE: bank does not support external interrupts
+ * @EINT_TYPE_GPIO: bank supportes external gpio interrupts
+ * @EINT_TYPE_WKUP: bank supportes external wakeup interrupts
+ *
+ * Samsung GPIO controller groups all the available pins into banks. The pins
+ * in a pin bank can support external gpio interrupts or external wakeup
+ * interrupts or no interrupts at all. From a software perspective, the only
+ * difference between external gpio and external wakeup interrupts is that
+ * the wakeup interrupts can additionally wakeup the system if it is in
+ * suspended state.
+ */
+enum eint_type {
+	EINT_TYPE_NONE,
+	EINT_TYPE_GPIO,
+	EINT_TYPE_WKUP,
+};
+
+/* maximum length of a pin in pin descriptor (example: "gpa0-0") */
+#define PIN_NAME_LENGTH	10
+
+#define PIN_GROUP(n, p, f)				\
+	{						\
+		.name		= n,			\
+		.pins		= p,			\
+		.num_pins	= ARRAY_SIZE(p),	\
+		.func		= f			\
+	}
+
+#define PMX_FUNC(n, g)					\
+	{						\
+		.name		= n,			\
+		.groups		= g,			\
+		.num_groups	= ARRAY_SIZE(g),	\
+	}
+
+struct samsung_pinctrl_drv_data;
+
+/**
+ * struct samsung_pin_bank: represent a controller pin-bank.
+ * @reg_offset: starting offset of the pin-bank registers.
+ * @pin_base: starting pin number of the bank.
+ * @nr_pins: number of pins included in this bank.
+ * @func_width: width of the function selector bit field.
+ * @pud_width: width of the pin pull up/down selector bit field.
+ * @drv_width: width of the pin driver strength selector bit field.
+ * @conpdn_width: width of the sleep mode function selector bin field.
+ * @pudpdn_width: width of the sleep mode pull up/down selector bit field.
+ * @eint_type: type of the external interrupt supported by the bank.
+ * @irq_base: starting controller local irq number of the bank.
+ * @name: name to be prefixed for each pin in this pin bank.
+ */
+struct samsung_pin_bank {
+	u32		pctl_offset;
+	u32		pin_base;
+	u8		nr_pins;
+	u8		func_width;
+	u8		pud_width;
+	u8		drv_width;
+	u8		conpdn_width;
+	u8		pudpdn_width;
+	enum eint_type	eint_type;
+	u32		irq_base;
+	char		*name;
+};
+
+/**
+ * struct samsung_pin_ctrl: represent a pin controller.
+ * @pin_banks: list of pin banks included in this controller.
+ * @nr_banks: number of pin banks.
+ * @base: starting system wide pin number.
+ * @nr_pins: number of pins supported by the controller.
+ * @nr_gint: number of external gpio interrupts supported.
+ * @nr_wint: number of external wakeup interrupts supported.
+ * @geint_con: offset of the ext-gpio controller registers.
+ * @geint_mask: offset of the ext-gpio interrupt mask registers.
+ * @geint_pend: offset of the ext-gpio interrupt pending registers.
+ * @weint_con: offset of the ext-wakeup controller registers.
+ * @weint_mask: offset of the ext-wakeup interrupt mask registers.
+ * @weint_pend: offset of the ext-wakeup interrupt pending registers.
+ * @svc: offset of the interrupt service register.
+ * @eint_gpio_init: platform specific callback to setup the external gpio
+ *	interrupts for the controller.
+ * @eint_wkup_init: platform specific callback to setup the external wakeup
+ *	interrupts for the controller.
+ * @label: for debug information.
+ */
+struct samsung_pin_ctrl {
+	struct samsung_pin_bank	*pin_banks;
+	u32		nr_banks;
+
+	u32		base;
+	u32		nr_pins;
+	u32		nr_gint;
+	u32		nr_wint;
+
+	u32		geint_con;
+	u32		geint_mask;
+	u32		geint_pend;
+
+	u32		weint_con;
+	u32		weint_mask;
+	u32		weint_pend;
+
+	u32		svc;
+
+	int		(*eint_gpio_init)(struct samsung_pinctrl_drv_data *);
+	int		(*eint_wkup_init)(struct samsung_pinctrl_drv_data *);
+	char		*label;
+};
+
+/**
+ * struct samsung_pinctrl_drv_data: wrapper for holding driver data together.
+ * @virt_base: register base address of the controller.
+ * @dev: device instance representing the controller.
+ * @irq: interrpt number used by the controller to notify gpio interrupts.
+ * @ctrl: pin controller instance managed by the driver.
+ * @pctl: pin controller descriptor registered with the pinctrl subsystem.
+ * @pctl_dev: cookie representing pinctrl device instance.
+ * @pin_groups: list of pin groups available to the driver.
+ * @nr_groups: number of such pin groups.
+ * @pmx_functions: list of pin functions available to the driver.
+ * @nr_function: number of such pin functions.
+ * @gc: gpio_chip instance registered with gpiolib.
+ * @grange: linux gpio pin range supported by this controller.
+ */
+struct samsung_pinctrl_drv_data {
+	void __iomem			*virt_base;
+	struct device			*dev;
+	int				irq;
+
+	struct samsung_pin_ctrl		*ctrl;
+	struct pinctrl_desc		pctl;
+	struct pinctrl_dev		*pctl_dev;
+
+	const struct samsung_pin_group	*pin_groups;
+	unsigned int			nr_groups;
+	const struct samsung_pmx_func	*pmx_functions;
+	unsigned int			nr_functions;
+
+	struct irq_domain		*gpio_irqd;
+	struct irq_domain		*wkup_irqd;
+
+	struct gpio_chip		*gc;
+	struct pinctrl_gpio_range	grange;
+};
+
+/**
+ * struct samsung_pin_group: represent group of pins of a pinmux function.
+ * @name: name of the pin group, used to lookup the group.
+ * @pins: the pins included in this group.
+ * @num_pins: number of pins included in this group.
+ * @func: the function number to be programmed when selected.
+ */
+struct samsung_pin_group {
+	const char		*name;
+	const unsigned int	*pins;
+	u8			num_pins;
+	u8			func;
+};
+
+/**
+ * struct samsung_pmx_func: represent a pin function.
+ * @name: name of the pin function, used to lookup the function.
+ * @groups: one or more names of pin groups that provide this function.
+ * @num_groups: number of groups included in @groups.
+ */
+struct samsung_pmx_func {
+	const char		*name;
+	const char		**groups;
+	u8			num_groups;
+};
+
+/* list of all exported SoC specific data */
+extern struct samsung_pin_ctrl exynos4210_pin_ctrl[];
+
+#endif /* __PINCTRL_SAMSUNG_H */