lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <1371851554-4492-1-git-send-email-hanumant@codeaurora.org>
Date:	Fri, 21 Jun 2013 14:52:34 -0700
From:	Hanumant Singh <hanumant@...eaurora.org>
To:	linus.walleij@...aro.org
Cc:	linux-kernel@...r.kernel.org,
	Hanumant Singh <hanumant@...eaurora.org>
Subject: [PATCH] pinctrl: msm: Add support for MSM TLMM pinmux

Add a new device tree enabled pinctrl driver for
Qualcomm MSM SoC's. This driver provides an extensible
framework to interface all MSM's that use a TLMM pinmux,
with the pinctrl subsytem.

This driver is split into two parts: the pinctrl interface
and the TLMM version specific implementation. The pinctrl
interface parses the device tree and registers with the pinctrl
subsytem. The TLMM version specific implementation supports
pin configuration/register programming for the different
pin types present on a given TLMM pinmux version.

Add support only for TLMM version 3 pinmux right now,
as well as, only two of the different pin types present on the
TLMM v3 pinmux.
Pintype 1: General purpose pins.
Pintype 2: SDC pins.

Signed-off-by: Hanumant Singh <hanumant@...eaurora.org>
---
 .../devicetree/bindings/pinctrl/msm-pinctrl.txt    | 187 +++++
 drivers/pinctrl/Kconfig                            |  10 +
 drivers/pinctrl/Makefile                           |   2 +
 drivers/pinctrl/pinctrl-msm-tlmm-v3.c              | 345 +++++++++
 drivers/pinctrl/pinctrl-msm.c                      | 774 +++++++++++++++++++++
 drivers/pinctrl/pinctrl-msm.h                      | 112 +++
 6 files changed, 1430 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt
 create mode 100644 drivers/pinctrl/pinctrl-msm-tlmm-v3.c
 create mode 100644 drivers/pinctrl/pinctrl-msm.c
 create mode 100644 drivers/pinctrl/pinctrl-msm.h

diff --git a/Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt
new file mode 100644
index 0000000..c76777b
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt
@@ -0,0 +1,187 @@
+MSM TLMM pinmux controller
+
+Qualcomm MSM integrates a GPIO and Pin mux/config hardware, (TOP Level Mode
+Multiplexper in short TLMM). It controls the input/output settings on the
+available pads/pins and also provides ability to multiplex and configure the
+output of various on-chip controllers onto these pads. The pins are also of
+different types, encapsulating different functions and having differing register
+semantics.
+
+Required Properties:
+- compatible: should be one of the following.
+  - "qcom,msm-tlmm-v3": for MSM with TLMM version 3.
+
+- reg: Base address of the pin controller hardware module and length of
+  the address space it occupies.
+
+- Pin types as child nodes: Pin types supported by a particular controller
+  instance are represented as child nodes of the controller node. Each
+  pin type node must contain following properties:
+
+Required Properties
+  - qcom,pin-type: identifies the pin type.
+  - qcom,pin-cells: number of cells in the pin type specifier.
+  - qcom,num-pins: number of pins of given type present on the MSM.
+
+- Pin groups as child nodes: The pin mux (selecting pin function
+  mode) and pin config (pull up/down, driver strength, direction) settings are
+  represented as child nodes of the pin-controller node. There is no limit on
+  the count of these child nodes.
+
+  Required Properties
+    -qcom,pins: phandle specifying pin type and a pin number.
+
+  Optional Properties
+    -qcom,pin-func: function setting for the pin group.
+
+  The child node should contain a list of pin(s) on which a particular pin
+  function selection or pin configuration (or both) have to applied. This
+  list of pins is specified using the property name "qcom,pins". There
+  should be atleast one pin specfied for this property and there is no upper
+  limit on the count of pins that can be specified. The pins are specified
+  using the pintype phandle and the pin number within that pintype.
+
+  The pin function selection that should be applied on the pins listed in the
+  child node is specified using the "qcom,pin-func" property. The value
+  of this property that should be applied to each of the pins listed in the
+  "qcom,pins" property, should be picked from the hardware manual of the SoC.
+  This property is optional in the child node if no specific function
+  selection is desired for the pins listed in the child node or if the pin is
+  to be used for bit bang. The value of this property is used as-is to program
+  the pin-controller
+
+  The pin group node must additionally have a pin configuration node as its own
+  child node. There can be more then one such configuration node for a pin group
+  node. There can be one or more configurations within the configuration
+  node. These configurations are applied to all pins mentoned above using the
+  "qcom,pins" property. These configurations are specific to the pintype of the
+  pins. The following pin configuration properties are supported by general
+  purpose pins.
+
+  - qcom,gp-pull: Pull up/down configuration.
+  - qcom,gp-drv: Drive strength configuration.
+  - qcom,gp-dir: Pull up/down configuration in power down mode.
+
+  The following pin configurations are properties are supported by SDC pins
+  - qcom,sdc1-clk-pull: Pull up/down configuration SDC1 clock pin.
+  - qcom,sdc1-clk-drv: Drive strength configuration for SDC1 clock pin.
+  - qcom,sdc1-cmd-pull: Pull up/down configuration for SDC1 command pin.
+  - qcom,sdc1-cmd-drv: Drive strength configuration for SDC1 command pin.
+  - qcom,sdc1-data-pull: Pull up/down configuration for SDC1 data pin.
+  - qcom,sdc1-data-drv: Drive strength configuration for SDC1 data pin.
+  - qcom,sdc2-clk-pull: Pull up/down configuration SDC2 clock pin.
+  - qcom,sdc2-clk-drv: Drive strength configuration for SDC2 clock pin.
+  - qcom,sdc2-cmd-pull: Pull up/down configuration for SDC2 command pin.
+  - qcom,sdc2-cmd-drv: Drive strength configuration for SDC2 command pin.
+  - qcom,sdc2-data-pull: Pull up/down configuration for SDC2 data pin.
+  - qcom,sdc2-data-drv: Drive strength configuration for SDC2 data pin.
+
+
+  The values specified by these config properties should be derived from the
+  hardware manual and these values are programmed as-is into the pin
+  pull up/down and driver strength register of the pin-controller.
+
+  NOTE: A pin group node should be formed for all pins that are going to have
+  the same function and configuration settings. If a subset of pins to be used
+  by a client require different function or configuration settings or both
+  then they should be modelled as a separate pin group node to be used by
+  the client.
+
+  The client nodes that require a particular pin function selection and/or
+  pin configuration should use the bindings listed in the "pinctrl-bindings txt"
+  file.
+
+Example 1: A pin-controller node with pin types
+
+	pinctrl@...110000 {
+		compatible = "qcom,msm-tlmm-v3";
+		reg = <0x11400000 0x4000>;
+
+		/* General purpose pin type */
+		gp: gp {
+			qcom,pin-type = "gp";
+			qcom,num-pins = <117>;
+			#qcom,pin-cells = <1>;
+		};
+	};
+
+Example 2: Spi pin entries within the pincontroller node
+	pinctrl@...11000 {
+		....
+		..
+		spi-bus {
+			/*
+			 * MOSI, MISO and CLK lines
+			 * all sharing same function and config
+			 * settings for each configuration node.
+			 */
+			qcom,pins = <&gp 0>, <&gp 1>, <&gp 3>;
+			qcom,pin-func = <1>;
+
+			/* Active configuration of bus pins */
+			spi-bus-active: spi-bus-active {
+				qcom,gp-drv = <3>; /* 8 MA */
+				qcom,gp-pull = <0>; /* No PULL */
+			};
+			/* Sleep configuration of bus pins */
+			spi-bus-sleep: spi-bus-sleep {
+				qcom,gp-drv = <0>; /* 2 MA */
+				qcom,gp-pull = <0>; /* No PULL */
+			};
+		};
+
+		spi-cs {
+			/*
+			 * Chip select for SPI
+			 * different config
+			 * settings as compared to bus pins.
+			 */
+			qcom,pins = <&Gp 2>;
+			qcom,num-grp-pins = <1>;
+			qcom,pin-func = <1>;
+
+			/* Active configuration of cs pin */
+			spi-cs-active: spi-cs-active {
+				qcom,gp-drv = <2>; /* 8 MA */
+				qcom,gp-pull = <0>; /* No PULL */
+			};
+			/* Sleep configuration of cs pin */
+			spi-bus-sleep: spi-bus-sleep {
+				qcom,gp-drv = <0>; /* 2 MA */
+				qcom,gp-pull = <0>; /* No PULL */
+			};
+		};
+	};
+
+Example 3: A SPI client node that supports 'active' and 'sleep' states.
+
+	spi_0: spi@...23000 { /* BLSP1 QUP1 */
+			compatible = "qcom,spi-qup-v2";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg-names = "spi_physical", "spi_bam_physical";
+			reg = <0xf9923000 0x1000>,
+			<0xf9904000 0xF000>;
+			interrupt-names = "spi_irq", "spi_bam_irq";
+			interrupts = <0 95 0>, <0 238 0>;
+			spi-max-frequency = <19200000>;
+
+			/* pins used by spi controllers */
+			pinctrl-names = "default", "sleep";
+			pinctrl-0 = <&spi-bus-active &spi-cs-active>;
+			pinctrl-1 = <&spi-bus-sleep &spi-cs-sleep>;
+
+			qcom,infinite-mode = <0>;
+			qcom,use-bam;
+			qcom,ver-reg-exists;
+		qcom,bam-consumer-pipe-index = <12>;
+		qcom,bam-producer-pipe-index = <13>;
+		 };
+
+Example 4: Set up the default pin state for spi controller.
+
+	static inline int msm_spi_request_pins{struct msm_spi *dd)
+	{
+		/* ... */
+		dd->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+	}
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 8f66924..7cde411 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -132,6 +132,16 @@ config PINCTRL_IMX28
 	bool
 	select PINCTRL_MXS
 
+config PINCTRL_MSM
+	depends on OF
+	bool
+	select PINMUX
+	select PINCONF
+
+config PINCTRL_MSM_TLMM_V3
+	bool
+	select PINCTRL_MSM
+
 config PINCTRL_NOMADIK
 	bool "Nomadik pin controller driver"
 	depends on ARCH_U8500 || ARCH_NOMADIK
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 9bdaeb8..80469e4 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -26,6 +26,8 @@ obj-$(CONFIG_PINCTRL_FALCON)	+= pinctrl-falcon.o
 obj-$(CONFIG_PINCTRL_MXS)	+= pinctrl-mxs.o
 obj-$(CONFIG_PINCTRL_IMX23)	+= pinctrl-imx23.o
 obj-$(CONFIG_PINCTRL_IMX28)	+= pinctrl-imx28.o
+obj-$(CONFIG_PINCTRL_MSM)	+= pinctrl-msm.o
+obj-$(CONFIG_PINCTRL_MSM_TLMM_V3)	+= pinctrl-msm-tlmm-v3.o
 obj-$(CONFIG_PINCTRL_NOMADIK)	+= pinctrl-nomadik.o
 obj-$(CONFIG_PINCTRL_STN8815)	+= pinctrl-nomadik-stn8815.o
 obj-$(CONFIG_PINCTRL_DB8500)	+= pinctrl-nomadik-db8500.o
diff --git a/drivers/pinctrl/pinctrl-msm-tlmm-v3.c b/drivers/pinctrl/pinctrl-msm-tlmm-v3.c
new file mode 100644
index 0000000..8b979cf
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-msm-tlmm-v3.c
@@ -0,0 +1,345 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/bitops.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include "pinctrl-msm.h"
+
+/* GP PIN TYPE REG MASKS */
+#define TLMMV3_GP_DRV_SHFT		6
+#define TLMMV3_GP_DRV_MASK		0x7
+#define TLMMV3_GP_PULL_SHFT		0
+#define TLMMV3_GP_PULL_MASK		0x3
+#define TLMMV3_GP_DIR_SHFT		9
+#define TLMMV3_GP_DIR_MASK		1
+#define TLMMV3_GP_FUNC_SHFT		2
+#define TLMMV3_GP_FUNC_MASK		0xF
+/* SDC1 PIN TYPE REG MASKS */
+#define TLMMV3_SDC1_CLK_DRV_SHFT	6
+#define TLMMV3_SDC1_CLK_DRV_MASK	0x7
+#define TLMMV3_SDC1_DATA_DRV_SHFT	0
+#define TLMMV3_SDC1_DATA_DRV_MASK	0x7
+#define TLMMV3_SDC1_CMD_DRV_SHFT	3
+#define TLMMV3_SDC1_CMD_DRV_MASK	0x7
+#define TLMMV3_SDC1_CLK_PULL_SHFT	13
+#define TLMMV3_SDC1_CLK_PULL_MASK	0x3
+#define TLMMV3_SDC1_DATA_PULL_SHFT	9
+#define TLMMV3_SDC1_DATA_PULL_MASK	0x3
+#define TLMMV3_SDC1_CMD_PULL_SHFT	11
+#define TLMMV3_SDC1_CMD_PULL_MASK	0x3
+/* SDC2 PIN TYPE REG MASKS */
+#define TLMMV3_SDC2_CLK_DRV_SHFT	6
+#define TLMMV3_SDC2_CLK_DRV_MASK	0x7
+#define TLMMV3_SDC2_DATA_DRV_SHFT	0
+#define TLMMV3_SDC2_DATA_DRV_MASK	0x7
+#define TLMMV3_SDC2_CMD_DRV_SHFT	3
+#define TLMMV3_SDC2_CMD_DRV_MASK	0x7
+#define TLMMV3_SDC2_CLK_PULL_SHFT	14
+#define TLMMV3_SDC2_CLK_PULL_MASK	0x3
+#define TLMMV3_SDC2_DATA_PULL_SHFT	9
+#define TLMMV3_SDC2_DATA_PULL_MASK	0x3
+#define TLMMV3_SDC2_CMD_PULL_SHFT	11
+#define TLMMV3_SDC2_CMD_PULL_MASK	0x3
+
+#define TLMMV3_GP_INOUT_BIT		1
+#define TLMMV3_GP_OUT			BIT(TLMMV3_GP_INOUT_BIT)
+#define TLMMV3_GP_IN			0
+
+#define MSM_TLMM_PACK_CFG(val, arg)	((val << 16) | arg)
+#define MSM_TLMM_UNPACK_CFG(conf)	(conf >> 16)
+#define MSM_TLMM_UNPACK_ARG(conf)	(conf & 0xffff)
+
+/* SDC Pin type register offsets */
+#define TLMMV3_SDC1_CFG(base)		(base + 0x2044)
+#define TLMMV3_SDC2_CFG(base)		(TLMMV3_SDC1_CFG(base) + 0x4)
+
+/* GP pin type register offsets */
+#define TLMMV3_GP_CFG(base, pin)	(base + 0x1000 + 0x10 * (pin))
+#define TLMMV3_GP_INOUT(base, pin)	(base + 0x1004 + 0x10 * (pin))
+
+enum msm_tlmm_gp_pincfg_param {
+	/* GP PIN TYPE CFG PARAMS */
+	MSM_TLMM_GP_DIR,
+	MSM_TLMM_GP_DRV,
+	MSM_TLMM_GP_PULL,
+	/* SDC1 PIN TYPE CFG PARAMS */
+	MSM_TLMM_SDC1_CLK_DRV,
+	MSM_TLMM_SDC1_CMD_DRV,
+	MSM_TLMM_SDC1_DATA_DRV,
+	MSM_TLMM_SDC1_CLK_PULL,
+	MSM_TLMM_SDC1_CMD_PULL,
+	MSM_TLMM_SDC1_DATA_PULL,
+	/* SDC2 PIN TYPE CFG PARAMS */
+	MSM_TLMM_SDC2_CLK_DRV,
+	MSM_TLMM_SDC2_CMD_DRV,
+	MSM_TLMM_SDC2_DATA_DRV,
+	MSM_TLMM_SDC2_CLK_PULL,
+	MSM_TLMM_SDC2_CMD_PULL,
+	MSM_TLMM_SDC2_DATA_PULL,
+};
+
+static struct msm_tlmm_cfg_params msm_tlmm_v3_gp_cfg_param[] = {
+	{
+		.name	= "qcom,gp-dir",
+		.id	= MSM_TLMM_GP_DIR,
+	},
+	{
+		.name	= "qcom,gp-drv",
+		.id	= MSM_TLMM_GP_DRV,
+	},
+	{
+		.name	= "qcom,gp-pull",
+		.id	= MSM_TLMM_GP_PULL,
+	},
+
+};
+
+static struct msm_tlmm_cfg_params msm_tlmm_v3_sdc_cfg_param[] = {
+	{
+		.name	= "qcom,sdc1-clk-drv",
+		.id	= MSM_TLMM_SDC1_CLK_DRV,
+	},
+	{
+		.name	= "qcom,sdc1-clk-pull",
+		.id	= MSM_TLMM_SDC1_CLK_PULL,
+	},
+	{
+		.name	= "qcom,sdc1-cmd-drv",
+		.id	= MSM_TLMM_SDC1_CMD_PULL,
+	},
+	{
+		.name	= "qcom,sdc1-cmd-pull",
+		.id	= MSM_TLMM_SDC1_CMD_PULL,
+	},
+	{
+		.name	= "qcom,sdc1-data-pull",
+		.id	= MSM_TLMM_SDC1_DATA_PULL,
+	},
+	{
+		.name	= "qcom,sdc1-data-pull",
+		.id	= MSM_TLMM_SDC1_DATA_PULL,
+	},
+		{
+		.name	= "qcom,sdc2-clk-drv",
+		.id	= MSM_TLMM_SDC2_CLK_DRV,
+	},
+	{
+		.name	= "qcom,sdc2-clk-pull",
+		.id	= MSM_TLMM_SDC2_CLK_PULL,
+	},
+	{
+		.name	= "qcom,sdc2-cmd-drv",
+		.id	= MSM_TLMM_SDC2_CMD_PULL,
+	},
+	{
+		.name	= "qcom,sdc2-cmd-pull",
+		.id	= MSM_TLMM_SDC2_CMD_PULL,
+	},
+	{
+		.name	= "qcom,sdc2-data-pull",
+		.id	= MSM_TLMM_SDC2_DATA_PULL,
+	},
+	{
+		.name	= "qcom,sdc2-data-pull",
+		.id	= MSM_TLMM_SDC2_DATA_PULL,
+	},
+};
+
+static int msm_tlmm_v3_sdc_cfg(uint pin_no, unsigned long *config,
+						void __iomem *cfg_reg,
+						bool write)
+{
+	unsigned int val, id, data;
+	u32 mask, shft;
+
+	id = MSM_TLMM_UNPACK_ARG(*config);
+	/* Get mask and shft values for this config type */
+	switch (id) {
+	case MSM_TLMM_SDC1_CLK_DRV:
+		mask = TLMMV3_SDC1_CLK_DRV_MASK;
+		shft = TLMMV3_SDC1_CLK_DRV_SHFT;
+		break;
+	case MSM_TLMM_SDC1_CLK_PULL:
+		mask = TLMMV3_SDC1_CLK_PULL_MASK;
+		shft = TLMMV3_SDC1_CLK_PULL_SHFT;
+		break;
+	case MSM_TLMM_SDC1_DATA_DRV:
+		mask = TLMMV3_SDC1_DATA_DRV_MASK;
+		shft = TLMMV3_SDC1_DATA_DRV_SHFT;
+		break;
+	case MSM_TLMM_SDC1_DATA_PULL:
+		mask = TLMMV3_SDC1_DATA_PULL_MASK;
+		shft = TLMMV3_SDC1_DATA_PULL_SHFT;
+		break;
+	case MSM_TLMM_SDC1_CMD_DRV:
+		mask = TLMMV3_SDC1_CMD_DRV_MASK;
+		shft = TLMMV3_SDC1_CMD_DRV_SHFT;
+		break;
+	case MSM_TLMM_SDC1_CMD_PULL:
+		mask = TLMMV3_SDC1_CMD_PULL_MASK;
+		shft = TLMMV3_SDC1_CMD_PULL_SHFT;
+		break;
+	case MSM_TLMM_SDC2_CLK_DRV:
+		cfg_reg = TLMMV3_SDC2_CFG(cfg_reg);
+		mask = TLMMV3_SDC2_CLK_DRV_MASK;
+		shft = TLMMV3_SDC2_CLK_DRV_SHFT;
+		break;
+	case MSM_TLMM_SDC2_CLK_PULL:
+		cfg_reg = TLMMV3_SDC2_CFG(cfg_reg);
+		mask = TLMMV3_SDC2_CLK_PULL_MASK;
+		shft = TLMMV3_SDC2_CLK_PULL_SHFT;
+		break;
+	case MSM_TLMM_SDC2_DATA_DRV:
+		cfg_reg = TLMMV3_SDC2_CFG(cfg_reg);
+		mask = TLMMV3_SDC2_DATA_DRV_MASK;
+		shft = TLMMV3_SDC2_DATA_DRV_SHFT;
+		break;
+	case MSM_TLMM_SDC2_DATA_PULL:
+		cfg_reg = TLMMV3_SDC2_CFG(cfg_reg);
+		mask = TLMMV3_SDC2_DATA_PULL_MASK;
+		shft = TLMMV3_SDC2_DATA_PULL_SHFT;
+		break;
+	case MSM_TLMM_SDC2_CMD_DRV:
+		cfg_reg = TLMMV3_SDC2_CFG(cfg_reg);
+		mask = TLMMV3_SDC2_CMD_DRV_MASK;
+		shft = TLMMV3_SDC2_CMD_DRV_SHFT;
+		break;
+	case MSM_TLMM_SDC2_CMD_PULL:
+		cfg_reg = TLMMV3_SDC2_CFG(cfg_reg);
+		mask = TLMMV3_SDC2_CMD_PULL_MASK;
+		shft = TLMMV3_SDC2_CMD_PULL_SHFT;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	val = readl_relaxed(cfg_reg);
+	if (write) {
+		data = MSM_TLMM_UNPACK_CFG(*config);
+		val &= ~(mask << shft);
+		val |= (data << shft);
+		writel_relaxed(val, cfg_reg);
+	} else {
+		val >>= shft;
+		val &= mask;
+		*config = MSM_TLMM_PACK_CFG(val, id);
+	}
+	return 0;
+}
+
+static void msm_tlmm_v3_sdc_set_reg_base(void __iomem **ptype_base,
+							void __iomem *tlmm_base)
+{
+	*ptype_base = tlmm_base + 0x2044;
+}
+
+static int msm_tlmm_v3_gp_cfg(uint pin_no, unsigned long *config,
+						void *reg_base, bool write)
+{
+	unsigned int val, id, data;
+	u32 mask, shft;
+	void __iomem *inout_reg = NULL;
+	void __iomem *cfg_reg = TLMMV3_GP_CFG(reg_base, pin_no);
+
+	id = MSM_TLMM_UNPACK_ARG(*config);
+	/* Get mask and shft values for this config type */
+	switch (id) {
+	case MSM_TLMM_GP_DRV:
+		mask = TLMMV3_GP_DRV_MASK;
+		shft = TLMMV3_GP_DRV_SHFT;
+		break;
+	case MSM_TLMM_GP_PULL:
+		mask = TLMMV3_GP_PULL_MASK;
+		shft = TLMMV3_GP_PULL_SHFT;
+		break;
+	case MSM_TLMM_GP_DIR:
+		mask = TLMMV3_GP_DIR_MASK;
+		shft = TLMMV3_GP_DIR_SHFT;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	val = readl_relaxed(cfg_reg);
+	if (write) {
+		data = MSM_TLMM_UNPACK_CFG(*config);
+		if (id == MSM_TLMM_GP_DIR) {
+			if (data == TLMMV3_GP_OUT) {
+				inout_reg = TLMMV3_GP_INOUT(reg_base, pin_no);
+				writel_relaxed(TLMMV3_GP_OUT, inout_reg);
+			}
+			if (data > TLMMV3_GP_IN)
+				data = TLMMV3_GP_OUT;
+		}
+		val &= ~(mask << shft);
+		val |= (data << shft);
+		writel_relaxed(val, cfg_reg);
+	} else {
+		val >>= shft;
+		val &= mask;
+		*config = MSM_TLMM_PACK_CFG(val, id);
+	}
+	return 0;
+}
+
+static void msm_tlmm_v3_gp_fn(uint pin_no, u32 func, void *reg_base,
+								bool enable)
+{
+	unsigned int val;
+	void __iomem *cfg_reg = TLMMV3_GP_CFG(reg_base, pin_no);
+	val = readl_relaxed(cfg_reg);
+	val &= ~(TLMMV3_GP_FUNC_MASK << TLMMV3_GP_FUNC_SHFT);
+	if (enable)
+		val |= (func << TLMMV3_GP_FUNC_SHFT);
+	writel_relaxed(val, cfg_reg);
+}
+
+static void msm_tlmm_v3_gp_set_reg_base(void __iomem **ptype_base,
+						void __iomem *tlmm_base)
+{
+	*ptype_base = tlmm_base;
+}
+
+static unsigned long msm_tlmm_v3_pack_cfg(u32 val,
+					struct msm_tlmm_cfg_params const
+								*cfg_param)
+{
+	return MSM_TLMM_PACK_CFG(val, cfg_param->id);
+}
+
+static struct msm_pintype_info tlmm_v3_pininfo[] = {
+	{
+		.prg_cfg = msm_tlmm_v3_gp_cfg,
+		.prg_func = msm_tlmm_v3_gp_fn,
+		.pack_cfg = msm_tlmm_v3_pack_cfg,
+		.set_reg_base = msm_tlmm_v3_gp_set_reg_base,
+		.tlmm_cfg_param = msm_tlmm_v3_gp_cfg_param,
+		.num_cfg_params = ARRAY_SIZE(msm_tlmm_v3_gp_cfg_param),
+		.reg_data = NULL,
+		.name = "gp",
+	},
+	{
+		.prg_cfg = msm_tlmm_v3_sdc_cfg,
+		.pack_cfg = msm_tlmm_v3_pack_cfg,
+		.set_reg_base = msm_tlmm_v3_sdc_set_reg_base,
+		.tlmm_cfg_param = msm_tlmm_v3_sdc_cfg_param,
+		.num_cfg_params = ARRAY_SIZE(msm_tlmm_v3_sdc_cfg_param),
+		.reg_data = NULL,
+		.name = "sdc",
+	}
+};
+
+struct msm_tlmm tlmm_v3_pintypes = {
+	.num_entries = ARRAY_SIZE(tlmm_v3_pininfo),
+	.pintype_info = tlmm_v3_pininfo,
+};
+
diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
new file mode 100644
index 0000000..97b96e1
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-msm.c
@@ -0,0 +1,774 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/err.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include "core.h"
+#include "pinctrl-msm.h"
+
+/**
+ * struct msm_pinctrl_dd: represents the pinctrol driver data.
+ * @base: virtual base of TLMM.
+ * @irq: interrupt number for TLMM summary interrupt.
+ * @num_pins: Number of total pins present on TLMM.
+ * @msm_pindesc: list of descriptors for each pin.
+ * @num_pintypes: number of pintypes on TLMM.
+ * @msm_pintype: points to the representation of all pin types supported.
+ * @pctl: pin controller instance managed by the driver.
+ * @pctl_dev: pin controller descriptor registered with the pinctrl subsystem.
+ * @pin_grps: list of pin groups available to the driver.
+ * @num_grps: number of groups.
+ * @pmx_funcs:list of pin functions available to the driver
+ * @num_funcs: number of functions.
+ * @dev: pin contol device.
+ */
+struct msm_pinctrl_dd {
+	void __iomem *base;
+	int	irq;
+	unsigned int num_pins;
+	struct msm_pindesc *msm_pindesc;
+	unsigned int num_pintypes;
+	struct msm_pintype_info *msm_pintype;
+	struct pinctrl_desc pctl;
+	struct pinctrl_dev *pctl_dev;
+	struct msm_pin_grps *pin_grps;
+	unsigned int num_grps;
+	struct  msm_pmx_funcs *pmx_funcs;
+	unsigned int num_funcs;
+	struct device *dev;
+};
+
+static int msm_pmx_functions_count(struct pinctrl_dev *pctldev)
+{
+	struct msm_pinctrl_dd *dd;
+
+	dd = pinctrl_dev_get_drvdata(pctldev);
+	return dd->num_funcs;
+}
+
+/* return the name of the pin function specified */
+static const char *msm_pmux_get_fname(struct pinctrl_dev *pctldev,
+						unsigned selector)
+{
+	struct msm_pinctrl_dd *dd;
+
+	dd = pinctrl_dev_get_drvdata(pctldev);
+	return dd->pmx_funcs[selector].name;
+}
+
+/* return the groups associated for the specified function selector */
+static int msm_pmux_get_groups(struct pinctrl_dev *pctldev,
+		unsigned selector, const char * const **groups,
+		unsigned * const num_groups)
+{
+	struct msm_pinctrl_dd *dd;
+
+	dd = pinctrl_dev_get_drvdata(pctldev);
+	*groups = dd->pmx_funcs[selector].gps;
+	*num_groups = dd->pmx_funcs[selector].num_grps;
+	return 0;
+}
+
+/* enable or disable a pinmux function */
+static void msm_pmux_prg_fn(struct pinctrl_dev *pctldev, unsigned selector,
+					unsigned group, bool enable)
+{
+	struct msm_pinctrl_dd *dd;
+	const unsigned int *pins;
+	struct msm_pindesc *pindesc;
+	struct msm_pintype_info *pintype;
+	unsigned int pin, cnt, func;
+
+	dd = pinctrl_dev_get_drvdata(pctldev);
+	pins = dd->pin_grps[group].pins;
+	pindesc = dd->msm_pindesc;
+
+	/*
+	 * for each pin in the pin group selected, program the correspoding
+	 * pin function number in the config register.
+	 */
+	for (cnt = 0; cnt < dd->pin_grps[group].num_pins; cnt++) {
+		pin = pins[cnt];
+		/* Obtain the pin type for given pin */
+		pintype = pindesc[pin].pin_info;
+		/* Obtain the pin number within the pin type */
+		pin = pin - pintype->pin_start;
+		func = dd->pin_grps[group].func;
+		/* program the function value for the given pin type */
+		pintype->prg_func(pin, func, pintype->reg_data, enable);
+	}
+}
+
+/* enable a specified pinmux by writing to registers */
+static int msm_pmux_enable(struct pinctrl_dev *pctldev, unsigned selector,
+					unsigned group)
+{
+	msm_pmux_prg_fn(pctldev, selector, group, true);
+	return 0;
+}
+
+/* disable a specified pinmux by writing to registers */
+static void msm_pmux_disable(struct pinctrl_dev *pctldev,
+					unsigned selector, unsigned group)
+{
+	msm_pmux_prg_fn(pctldev, selector, group, false);
+}
+
+static struct pinmux_ops msm_pmxops = {
+	.get_functions_count	= msm_pmx_functions_count,
+	.get_function_name	= msm_pmux_get_fname,
+	.get_function_groups	= msm_pmux_get_groups,
+	.enable			= msm_pmux_enable,
+	.disable		= msm_pmux_disable,
+};
+
+/* set or get the pin config settings for a specified pin */
+static int msm_pconf_prg(struct pinctrl_dev *pctldev, unsigned int pin,
+				unsigned long *config, bool rw)
+{
+	struct msm_pinctrl_dd *dd;
+	struct msm_pindesc *pindesc;
+	struct msm_pintype_info *pintype;
+
+	dd = pinctrl_dev_get_drvdata(pctldev);
+	pindesc = dd->msm_pindesc;
+	/* Get pin type for given pin */
+	pintype = pindesc[pin].pin_info;
+	/* Get pin offset from the pintype start pin number */
+	pin = pin - pintype->pin_start;
+	/* Program the config value for pin type */
+	return pintype->prg_cfg(pin, config, pintype->reg_data, rw);
+}
+
+/* set the pin config settings for a specified pin */
+static int msm_pconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
+				unsigned long config)
+{
+	return msm_pconf_prg(pctldev, pin, &config, true);
+}
+
+/* get the pin config settings for a specified pin */
+static int msm_pconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
+					unsigned long *config)
+{
+	return msm_pconf_prg(pctldev, pin, config, false);
+}
+
+/* set the pin config settings for a specified pin group */
+static int msm_pconf_group_set(struct pinctrl_dev *pctldev,
+			unsigned group, unsigned long config)
+{
+	struct msm_pinctrl_dd *dd;
+	const unsigned int *pins;
+	unsigned int cnt;
+
+	dd = pinctrl_dev_get_drvdata(pctldev);
+	pins = dd->pin_grps[group].pins;
+
+	for (cnt = 0; cnt < dd->pin_grps[group].num_pins; cnt++)
+		msm_pconf_set(pctldev, pins[cnt], config);
+
+	return 0;
+}
+
+/* get the pin config settings for a specified pin group */
+static int msm_pconf_group_get(struct pinctrl_dev *pctldev,
+				unsigned int group, unsigned long *config)
+{
+	struct msm_pinctrl_dd *dd;
+	const unsigned int *pins;
+
+	dd = pinctrl_dev_get_drvdata(pctldev);
+	pins = dd->pin_grps[group].pins;
+	msm_pconf_get(pctldev, pins[0], config);
+	return 0;
+}
+
+static struct pinconf_ops msm_pinconfops = {
+	.pin_config_get		= msm_pconf_get,
+	.pin_config_set		= msm_pconf_set,
+	.pin_config_group_get	= msm_pconf_group_get,
+	.pin_config_group_set	= msm_pconf_group_set,
+};
+
+/* check if the selector is a valid pin group selector */
+static int msm_get_grps_count(struct pinctrl_dev *pctldev)
+{
+	struct msm_pinctrl_dd *dd;
+
+	dd = pinctrl_dev_get_drvdata(pctldev);
+	return dd->num_grps;
+}
+
+/* return the name of the group selected by the group selector */
+static const char *msm_get_grps_name(struct pinctrl_dev *pctldev,
+						unsigned selector)
+{
+	struct msm_pinctrl_dd *dd;
+
+	dd = pinctrl_dev_get_drvdata(pctldev);
+	return dd->pin_grps[selector].name;
+}
+
+/* return the pin numbers associated with the specified group */
+static int msm_get_grps_pins(struct pinctrl_dev *pctldev,
+		unsigned selector, const unsigned **pins, unsigned *num_pins)
+{
+	struct msm_pinctrl_dd *dd;
+
+	dd = pinctrl_dev_get_drvdata(pctldev);
+	*pins = dd->pin_grps[selector].pins;
+	*num_pins = dd->pin_grps[selector].num_pins;
+	return 0;
+}
+
+static struct msm_pintype_info *msm_pgrp_to_pintype(struct device_node *nd,
+						struct msm_pinctrl_dd *dd)
+{
+	struct device_node *ptype_nd;
+	struct msm_pintype_info *pinfo = NULL;
+	int idx = 0;
+
+	/*Extract pin type node from parent node */
+	ptype_nd = of_parse_phandle(nd, "qcom,pins", 0);
+	/* find the pin type info for this pin type node */
+	for (idx = 0; idx < dd->num_pintypes; idx++) {
+		pinfo = &dd->msm_pintype[idx];
+		if (ptype_nd == pinfo->node) {
+			of_node_put(ptype_nd);
+			break;
+		}
+	}
+	return pinfo;
+}
+
+/* create pinctrl_map entries by parsing device tree nodes */
+static int msm_dt_node_to_map(struct pinctrl_dev *pctldev,
+			struct device_node *cfg_np, struct pinctrl_map **maps,
+			unsigned *nmaps)
+{
+	struct msm_pinctrl_dd *dd;
+	struct device_node *parent;
+	struct msm_pindesc *pindesc;
+	struct msm_pintype_info *pinfo;
+	struct msm_tlmm_cfg_params const *cfg_params;
+	struct pinctrl_map *map;
+	char *grp_name, *fn_name;
+	u32 val , num_cfg_params;
+	struct device *dev = pctldev->dev;
+	unsigned long *cfg = NULL;
+	int cfg_cnt = 0, map_cnt = 0, idx = 0, func_cnt = 0, ret = 0;
+
+	dd = pinctrl_dev_get_drvdata(pctldev);
+	pindesc = dd->msm_pindesc;
+	/* get parent node of config node */
+	parent = of_get_parent(cfg_np);
+	/*
+	 * parent node contains pin grouping
+	 * get pin type from pin grouping
+	 */
+	pinfo = msm_pgrp_to_pintype(parent, dd);
+	/* check if there is a function associated with the parent pin group */
+	if (of_find_property(parent, "qcom,pin-func", NULL))
+		func_cnt++;
+	/*
+	 * Count the config parameters specified in the node
+	 * and supported by this pin type
+	 */
+	cfg_params = pinfo->tlmm_cfg_param;
+	num_cfg_params = pinfo->num_cfg_params;
+
+	for (idx = 0; idx < num_cfg_params; idx++) {
+		if (!of_find_property(cfg_np, cfg_params[idx].name, NULL))
+			continue;
+		cfg_cnt++;
+	}
+	if (!cfg_cnt) {
+		dev_err(dd->dev, "no matching config specified\n");
+		of_node_put(parent);
+		return -EINVAL;
+	}
+	map_cnt = cfg_cnt + func_cnt;
+
+	/* Allocate memory for pin-map entries */
+	map = kzalloc(sizeof(*map) * map_cnt, GFP_KERNEL);
+	if (!map)
+		return -ENOMEM;
+	*nmaps = 0;
+	/* Allocate memory to hold configs */
+	cfg = kzalloc(sizeof(*cfg) * cfg_cnt, GFP_KERNEL);
+	if (!cfg) {
+		ret = -ENOMEM;
+		goto cfg_err;
+	}
+
+	/* read cfg property values from cfg device tree node */
+	for (idx = 0, cfg_cnt = 0; idx < num_cfg_params; idx++) {
+		if (!of_find_property(cfg_np, cfg_params[idx].name, NULL))
+			continue;
+		of_property_read_u32(cfg_np, cfg_params[idx].name, &val);
+		cfg[cfg_cnt++] = pinfo->pack_cfg(val, &cfg_params[idx]);
+	}
+	grp_name = devm_kzalloc(dev, strlen(parent->name) + 1, GFP_KERNEL);
+	if (!grp_name) {
+		ret = -ENOMEM;
+		goto grp_err;
+	}
+	snprintf(grp_name, strlen(parent->name) + 1, "%s", parent->name);
+
+	/* create the config map entry */
+	map[*nmaps].data.configs.group_or_pin = grp_name;
+	map[*nmaps].data.configs.configs = cfg;
+	map[*nmaps].data.configs.num_configs = cfg_cnt;
+	map[*nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
+	*nmaps += 1;
+
+	/* If there is no function specified in device tree return */
+	if (func_cnt == 0) {
+		*maps = map;
+		goto no_func;
+	}
+	/* Get function mapping */
+	of_property_read_u32(parent, "qcom,pin-func", &val);
+	fn_name = devm_kzalloc(dev, strlen(grp_name) +
+						 strlen("-func"),
+						GFP_KERNEL);
+	if (!fn_name) {
+		ret = -ENOMEM;
+		goto func_err;
+	}
+	snprintf(fn_name, strlen(grp_name) + strlen("-func") + 1, "%s%s",
+						parent->name, "-func");
+	map[*nmaps].data.mux.group = grp_name;
+	map[*nmaps].data.mux.function = fn_name;
+	map[*nmaps].type = PIN_MAP_TYPE_MUX_GROUP;
+	*nmaps += 1;
+	*maps = map;
+	of_node_put(parent);
+	return 0;
+
+func_err:
+	kfree(grp_name);
+grp_err:
+	kfree(map);
+cfg_err:
+	kfree(cfg);
+no_func:
+	of_node_put(parent);
+	return ret;
+}
+
+/* free the memory allocated to hold the pin-map table */
+static void msm_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_CONFIGS_GROUP) {
+			kfree(map[idx].data.configs.group_or_pin);
+			kfree(map[idx].data.configs.configs);
+		} else if (map->type == PIN_MAP_TYPE_MUX_GROUP)
+			kfree(map[idx].data.mux.function);
+	};
+
+	kfree(map);
+}
+
+static struct pinctrl_ops msm_pctrlops = {
+	.get_groups_count	= msm_get_grps_count,
+	.get_group_name		= msm_get_grps_name,
+	.get_group_pins		= msm_get_grps_pins,
+	.dt_node_to_map		= msm_dt_node_to_map,
+	.dt_free_map		= msm_dt_free_map,
+};
+
+static int msm_of_get_pin(struct device_node *np, int index,
+				struct msm_pinctrl_dd *dd, uint *pin)
+{
+	struct of_phandle_args pargs;
+	struct msm_pintype_info *pinfo;
+	int num_pintypes;
+	int ret, i;
+
+	ret = of_parse_phandle_with_args(np, "qcom,pins", "#qcom,pin-cells",
+								index, &pargs);
+	if (ret)
+		return ret;
+	pinfo = dd->msm_pintype;
+	num_pintypes = dd->num_pintypes;
+	for (i = 0; i < num_pintypes; i++)  {
+		/* Find the matching pin type node */
+		if (pargs.np != pinfo->node)
+			continue;
+		/* Check if arg specified is in valid range for pin type */
+		if (pargs.args[0] > pinfo->num_pins) {
+			ret = -EINVAL;
+			goto out;
+		}
+		/*
+		 * Pin number = index within pin type + start of pin numbers
+		 * for this pin type
+		 */
+		*pin = pargs.args[0] + pinfo->pin_start;
+	}
+out:
+	of_node_put(pargs.np);
+	return 0;
+}
+
+static int msm_pinctrl_dt_parse_pins(struct device_node *dev_node,
+						struct msm_pinctrl_dd *dd)
+{
+	struct device *dev;
+	struct device_node *pgrp_np;
+	struct msm_pin_grps *pin_grps, *curr_grp;
+	struct msm_pmx_funcs *pmx_funcs, *curr_func;
+	char *grp_name, *func_name;
+	int ret, i, grp_index = 0, func_index = 0;
+	uint pin = 0, *pins, num_grps = 0, num_pins = 0;
+	uint num_funcs = 0;
+	u32 func = 0;
+
+	dev = dd->dev;
+	for_each_child_of_node(dev_node, pgrp_np) {
+		if (!of_find_property(pgrp_np, "qcom,pins", NULL))
+			continue;
+		num_grps++;
+	}
+
+	for_each_child_of_node(dev_node, pgrp_np) {
+		if (!of_find_property(pgrp_np, "qcom,pin-func", NULL))
+			continue;
+		num_funcs++;
+	}
+
+	pin_grps = (struct msm_pin_grps *)devm_kzalloc(dd->dev,
+						sizeof(*pin_grps) * num_grps,
+						GFP_KERNEL);
+	if (!pin_grps) {
+		dev_err(dev, "Failed to allocate grp desc\n");
+		return -ENOMEM;
+	}
+	pmx_funcs = (struct msm_pmx_funcs *)devm_kzalloc(dd->dev,
+						sizeof(*pmx_funcs) * num_funcs,
+						GFP_KERNEL);
+	if (!pmx_funcs) {
+		dev_err(dev, "Failed to allocate grp desc\n");
+		return -ENOMEM;
+	}
+	/*
+	 * Iterate over all child nodes, and for nodes containing pin lists
+	 * populate corresponding pin group, and if provided, corresponding
+	 * function
+	 */
+	for_each_child_of_node(dev_node, pgrp_np) {
+		if (!of_find_property(pgrp_np, "qcom,pins", NULL))
+			continue;
+		curr_grp = pin_grps + grp_index;
+		/* Node name is group name */
+		grp_name = devm_kzalloc(dev, strlen(pgrp_np->name) + 1,
+							GFP_KERNEL);
+		if (!grp_name) {
+			dev_err(dev, "Unable to allocate group name\n");
+			return -ENOMEM;
+		}
+		snprintf(grp_name, strlen(pgrp_np->name) + 1, "%s",
+							pgrp_np->name);
+
+		num_pins = of_count_phandle_with_args(pgrp_np,
+							"qcom,pins",
+							"qcom,pin-cells");
+		if (IS_ERR_VALUE(num_pins)) {
+			dev_err(dev, "pin count not specified for groups %s\n",
+								grp_name);
+			return ret;
+		}
+		pins = devm_kzalloc(dd->dev, sizeof(unsigned int) * num_pins,
+						GFP_KERNEL);
+		if (!pins) {
+			dev_err(dev, "Unable to allocte pins for %s\n",
+								grp_name);
+		}
+		for (i = 0; i < num_pins; i++) {
+			ret = msm_of_get_pin(pgrp_np, i, dd, &pin);
+			if (ret) {
+				dev_err(dev, "Pin grp %s does not have pins\n",
+								grp_name);
+				return ret;
+			}
+			pins[i] = pin;
+		}
+		curr_grp->pins = pins;
+		curr_grp->num_pins = num_pins;
+		curr_grp->name = grp_name;
+		grp_index++;
+		/* Check if func specified */
+		if (!of_find_property(pgrp_np, "qcom,pin-func", NULL))
+			continue;
+		curr_func = pmx_funcs + func_index;
+		func_name = devm_kzalloc(dev, strlen(grp_name) +
+							strlen("-func") + 1,
+							GFP_KERNEL);
+		if (!func_name) {
+			dev_err(dev, "Cannot allocate func name for grp %s",
+								grp_name);
+			return -ENOMEM;
+		}
+		snprintf(func_name, strlen(grp_name)+strlen("-func") + 1,
+					"%s%s", grp_name, "-func");
+		curr_func->name = func_name;
+		curr_func->gps = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL);
+		if (!curr_func->gps) {
+			dev_err(dev, "failed to alloc memory for group list ");
+			return -ENOMEM;
+		}
+		of_property_read_u32(pgrp_np, "qcom,pin-func", &func);
+		curr_grp->func = func;
+		curr_func->gps[0] = grp_name;
+		curr_func->num_grps = 1;
+		func_index++;
+	}
+	dd->pin_grps = pin_grps;
+	dd->num_grps = num_grps;
+	dd->pmx_funcs = pmx_funcs;
+	dd->num_funcs = num_funcs;
+	return 0;
+}
+
+static void msm_populate_pindesc(struct msm_pintype_info *pinfo,
+					struct msm_pindesc *msm_pindesc)
+{
+	int i;
+	struct msm_pindesc *pindesc;
+
+	for (i = 0; i < pinfo->num_pins; i++) {
+		pindesc = &msm_pindesc[i + pinfo->pin_start];
+		pindesc->pin_info = pinfo;
+		snprintf(pindesc->name, sizeof(pindesc->name),
+					"%s-%d", pinfo->name, i);
+	}
+}
+
+static int msm_pinctrl_dt_parse_pintype(struct device_node *dev_node,
+						struct msm_pinctrl_dd *dd)
+{
+	struct device_node *pt_node;
+	struct msm_pindesc *msm_pindesc;
+	struct msm_pintype_info *pintype, *pinfo;
+	void __iomem **ptype_base;
+	u32 num_pins, pinfo_entries, curr_pins;
+	int i;
+	uint total_pins = 0;
+
+	for_each_child_of_node(dev_node, pt_node) {
+		if (!of_find_property(pt_node, "qcom,pin-type", NULL))
+			break;
+		else {
+			of_property_read_u32(pt_node, "qcom,num-pins",
+								&num_pins);
+			total_pins += num_pins;
+		}
+	}
+	dd->msm_pindesc = devm_kzalloc(dd->dev,
+						sizeof(struct msm_pindesc) *
+						total_pins, GFP_KERNEL);
+	if (!dd->msm_pindesc) {
+		dev_err(dd->dev, "Unable to allocate msm pindesc");
+		return -ENOMEM;
+	}
+	pinfo = dd->msm_pintype;
+	pinfo_entries = dd->num_pintypes;
+	dd->num_pins = total_pins;
+	msm_pindesc = dd->msm_pindesc;
+	curr_pins = 0;
+	/*
+	 * Populate pin descriptor based on each pin type present in Device
+	 * tree and supported by the driver
+	 */
+	for_each_child_of_node(dev_node, pt_node) {
+		if (!of_find_property(pt_node, "qcom,pin-type", NULL))
+			break;
+		else {
+			for (i = 0; i < pinfo_entries; i++) {
+				pintype = &pinfo[i];
+				if (!of_property_match_string(pt_node,
+							"qcom,pin-type",
+							pintype->name)) {
+					of_property_read_u32(pt_node,
+							"qcom,num-pins",
+							&num_pins);
+					pintype->num_pins = num_pins;
+					pintype->pin_start = curr_pins;
+					pintype->pin_end = curr_pins +
+								num_pins;
+					of_node_get(pt_node);
+					pintype->node = pt_node;
+					ptype_base = &pintype->reg_data;
+					pintype->set_reg_base(ptype_base,
+								dd->base);
+					msm_populate_pindesc(pintype,
+								msm_pindesc);
+					curr_pins += num_pins;
+				}
+
+			}
+		}
+	}
+	return 0;
+}
+
+static const struct of_device_id msm_pinctrl_dt_match[] = {
+	{ .compatible = "qcom,msm-tlmm-v3",
+		.data = &tlmm_v3_pintypes, },
+	{},
+};
+MODULE_DEVICE_TABLE(of, msm_pinctrl_dt_match);
+
+static int msm_pinctrl_get_drvdata(struct msm_pinctrl_dd *dd,
+						struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	const struct msm_tlmm *tlmm_info;
+	int ret, i;
+	struct device_node *node = pdev->dev.of_node;
+
+	match = of_match_node(msm_pinctrl_dt_match, node);
+	if (IS_ERR(match))
+		return PTR_ERR(match);
+	tlmm_info = match->data;
+	dd->msm_pintype = tlmm_info->pintype_info;
+	dd->num_pintypes = tlmm_info->num_entries;
+	ret = msm_pinctrl_dt_parse_pintype(node, dd);
+	if (ret)
+		goto out;
+
+	ret = msm_pinctrl_dt_parse_pins(node, dd);
+	if (ret)
+		goto pin_err;
+
+pin_err:
+	for (i = 0; i < dd->num_pintypes; i++)
+		of_node_put(dd->msm_pintype[i].node);
+
+out:
+	return ret;
+}
+
+static int msm_register_pinctrl(struct msm_pinctrl_dd *dd)
+{
+	int i;
+	struct pinctrl_pin_desc *pindesc;
+	struct pinctrl_desc *ctrl_desc = &dd->pctl;
+
+	ctrl_desc->name = "msm-pinctrl";
+	ctrl_desc->owner = THIS_MODULE;
+	ctrl_desc->pmxops = &msm_pmxops;
+	ctrl_desc->confops = &msm_pinconfops;
+	ctrl_desc->pctlops = &msm_pctrlops;
+
+	pindesc = devm_kzalloc(dd->dev, sizeof(*pindesc) * dd->num_pins,
+							 GFP_KERNEL);
+	if (!pindesc) {
+		dev_err(dd->dev, "Failed to allocate pinctrl pin desc\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < dd->num_pins; i++) {
+		pindesc[i].number = i;
+		pindesc[i].name = dd->msm_pindesc[i].name;
+	}
+	ctrl_desc->pins = pindesc;
+	ctrl_desc->npins = dd->num_pins;
+	dd->pctl_dev = pinctrl_register(ctrl_desc, dd->dev, dd);
+	if (!dd->pctl_dev) {
+		dev_err(dd->dev, "could not register pinctrl driver\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void msm_pinctrl_cleanup_dd(struct msm_pinctrl_dd *dd)
+{
+	int i;
+	for (i = 0; i < dd->num_pintypes; i++)
+		of_node_put(dd->msm_pintype[i].node);
+}
+
+static int msm_pinctrl_probe(struct platform_device *pdev)
+{
+	struct msm_pinctrl_dd *dd;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	int ret;
+
+	dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+	if (!dd) {
+		dev_err(dev, "Alloction failed for driver data\n");
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dd->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(dd->base))
+		return PTR_ERR(dd->base);
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res)
+		dd->irq = res->start;
+	dd->dev = dev;
+	ret = msm_pinctrl_get_drvdata(dd, pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "driver data not available\n");
+		return -EINVAL;
+	}
+	ret = msm_register_pinctrl(dd);
+	if (ret) {
+		msm_pinctrl_cleanup_dd(dd);
+		return ret;
+	}
+	platform_set_drvdata(pdev, dd);
+	return 0;
+}
+
+static struct platform_driver msm_pinctrl_driver = {
+	.probe		= msm_pinctrl_probe,
+	.driver = {
+		.name	= "msm-pinctrl",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(msm_pinctrl_dt_match),
+	},
+};
+
+static int __init msm_pinctrl_drv_register(void)
+{
+	return platform_driver_register(&msm_pinctrl_driver);
+}
+postcore_initcall(msm_pinctrl_drv_register);
+
+static void __exit msm_pinctrl_drv_unregister(void)
+{
+	platform_driver_unregister(&msm_pinctrl_driver);
+}
+module_exit(msm_pinctrl_drv_unregister);
+
+MODULE_AUTHOR("Hanumant Singh <hanumant@...eaurora.org>");
+MODULE_LICENSE("GPLv2");
+
diff --git a/drivers/pinctrl/pinctrl-msm.h b/drivers/pinctrl/pinctrl-msm.h
new file mode 100644
index 0000000..7903a8e
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-msm.h
@@ -0,0 +1,112 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef __PINCTRL_MSM_H__
+#define __PINCTRL_MSM_H__
+
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/machine.h>
+
+/**
+ * struct msm_pin_group: group of pins having the same pinmux function.
+ * @name: name of the pin 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 msm_pin_grps {
+	const char *name;
+	unsigned int *pins;
+	unsigned num_pins;
+	u32 func;
+};
+
+/**
+ * struct msm_pmx_funcs: represent a pin function.
+ * @name: name of the pin function.
+ * @gps: one or more names of pin groups that provide this function.
+ * @num_grps: number of groups included in @groups.
+ */
+struct msm_pmx_funcs {
+	const char *name;
+	const char **gps;
+	unsigned num_grps;
+};
+
+/**
+ * struct msm_tlmm_cfgs: represent config properties of a pin type.
+ * @name: name of config.
+ * @id: id of the config.
+ */
+
+struct msm_tlmm_cfg_params {
+	const char *name;
+	unsigned int id;
+};
+
+/**
+ * struct msm_pintype_info: represent a pin type supported by the TLMM.
+ * @prg_cfg: helper to program a given config for a pintype.
+ * @prg_func: helper to program a given func for a pintype.
+ * @pack_cfg: helper to pack a parsed config as per a pintype.
+ * @set_reg_base: helper to set the register base address for a pintype.
+ * @reg_data: register base for a pintype.
+ * @tlmm_cfg_param: config parameters for pins of a given pintype.
+ * @name: name of pintype.
+ * @num_pins: number of pins of given pintype.
+ * @pin_start: starting pin number for the given pintype within pinctroller.
+ * @pin_end: ending pin number for the given pintype within pinctroller.
+ * @node: device node for the pintype.
+ */
+struct msm_pintype_info {
+	int (*prg_cfg)(uint pin_no, unsigned long *config, void *reg_data,
+								bool rw);
+	void (*prg_func)(uint pin_no, u32 func, void *reg_data, bool enable);
+	unsigned long (*pack_cfg)(u32 val,
+				struct msm_tlmm_cfg_params const *param);
+	void (*set_reg_base)(void __iomem **ptype_base,
+						void __iomem *tlmm_base);
+	void __iomem *reg_data;
+	struct msm_tlmm_cfg_params const *tlmm_cfg_param;
+	u32 num_cfg_params;
+	const char *name;
+	u32 num_pins;
+	int pin_start;
+	int pin_end;
+	struct device_node *node;
+};
+
+/**
+ * struct msm_tlmm: represents all the TLMM pintypes for a given TLMM version.
+ * @num_entries: number of pintypes.
+ * @pintype_info: descriptor for the pintypes. One for each present.
+ */
+struct msm_tlmm {
+	const uint num_entries;
+	struct msm_pintype_info *pintype_info;
+};
+
+/**
+ * struct msm_pindesc: descriptor for all pins maintained by pinctrl driver
+ * @pin_info: pintype for a given pin.
+ * @name: name of the pin.
+ */
+struct msm_pindesc {
+	struct msm_pintype_info *pin_info;
+	char name[20];
+};
+
+/* TLMM version specific data */
+extern struct msm_tlmm tlmm_v3_pintypes;
+#endif
+
-- 
1.8.2.1

--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, hosted by The Linux Foundation
--

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ