linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/4] Qualcomm SPMI PMIC pin controller drivers
@ 2014-09-15 14:44 Ivan T. Ivanov
  2014-09-15 14:44 ` [PATCH v4 1/4] pinctrl: Device tree bindings for Qualcomm PMIC GPIO block Ivan T. Ivanov
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Ivan T. Ivanov @ 2014-09-15 14:44 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Linus Walleij, Grant Likely
  Cc: Ivan T. Ivanov, Bjorn Andersson, David Collins, Wu Fenglin,
	linux-kernel, devicetree, linux-arm-msm

Hi, 

This is forth version of the patches posted earlier here[1].

Changes:
- Driver for SSBI PMIC dropped from this patchest - let Bjorn to handle it.
- Separate MPP and GPIO drivers.
- Second value in 'reg' propriety is used to specify address range length
  and this number of GPIO/MPP's that drivers should handle.
- Using generic compatible = "qcom,spmi-pmic-gpio" in GPIO driver to handle
  any PMIC SPMI based GPIO block.
- Using generic compatible = "qcom,spmi-pmic-mpp" in MPP driver to handle
  any PMIC SPMI based MPP block.
- Analog functionality for MPP's is activated with qcom,analog-mode.
- Fixed review comments and driver issues.

Short description:

Patches adds pin control drivers for Multi-Purpose Pin (MPP) and
General-purpose pin (GPIO) controllers found in Qualcomm SPMI based
PMIC chips.

MPP's are enhanced GPIO's with analog circuits, which support 
analog and digital input/output.

[1] https://www.mail-archive.com/linux-arm-msm@vger.kernel.org/msg10095.html

Bjorn Andersson (1):
  pinctrl: Device tree bindings for Qualcomm PMIC GPIO block

Ivan T. Ivanov (3):
  pinctrl: Device tree bindings for Qualcomm PMIC MPP block
  pinctrl: Qualcomm SPMI PMIC GPIO pin controller driver
  pinctrl: Qualcomm SPMI PMIC MPP pin controller driver

 .../devicetree/bindings/pinctrl/qcom,pmic-gpio.txt | 214 +++++
 .../devicetree/bindings/pinctrl/qcom,pmic-mpp.txt  | 159 ++++
 drivers/pinctrl/qcom/Kconfig                       |  12 +
 drivers/pinctrl/qcom/Makefile                      |   2 +
 drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c      | 944 ++++++++++++++++++++
 drivers/pinctrl/qcom/pinctrl-spmi-pmic-mpp.c       | 958 +++++++++++++++++++++
 include/dt-bindings/pinctrl/qcom,pmic-gpio.h       | 142 +++
 include/dt-bindings/pinctrl/qcom,pmic-mpp.h        |  44 +
 8 files changed, 2475 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
 create mode 100644 Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
 create mode 100644 drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c
 create mode 100644 drivers/pinctrl/qcom/pinctrl-spmi-pmic-mpp.c
 create mode 100644 include/dt-bindings/pinctrl/qcom,pmic-gpio.h
 create mode 100644 include/dt-bindings/pinctrl/qcom,pmic-mpp.h

-- 
1.9.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v4 1/4] pinctrl: Device tree bindings for Qualcomm PMIC GPIO block
  2014-09-15 14:44 [PATCH v4 0/4] Qualcomm SPMI PMIC pin controller drivers Ivan T. Ivanov
@ 2014-09-15 14:44 ` Ivan T. Ivanov
  2014-09-15 14:44 ` [PATCH v4 2/4] pinctrl: Device tree bindings for Qualcomm PMIC MPP block Ivan T. Ivanov
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Ivan T. Ivanov @ 2014-09-15 14:44 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala
  Cc: Bjorn Andersson, David Collins, Wu Fenglin, linux-kernel,
	devicetree, linux-arm-msm, Ivan T. Ivanov

From: Bjorn Andersson <bjorn.andersson@sonymobile.com>

This introduced the device tree bindings for the GPIO block found
in PMIC's from Qualcomm.

Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
---
 .../devicetree/bindings/pinctrl/qcom,pmic-gpio.txt | 214 +++++++++++++++++++++
 include/dt-bindings/pinctrl/qcom,pmic-gpio.h       | 142 ++++++++++++++
 2 files changed, 356 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
 create mode 100644 include/dt-bindings/pinctrl/qcom,pmic-gpio.h

diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
new file mode 100644
index 0000000..e146496
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
@@ -0,0 +1,214 @@
+Qualcomm PMIC GPIO block
+
+This binding describes the GPIO block(s) found in the 8xxx series of
+PMIC's from Qualcomm.
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be one of:
+		    "qcom,pm8018-gpio"
+		    "qcom,pm8038-gpio"
+		    "qcom,pm8058-gpio"
+		    "qcom,pm8917-gpio"
+		    "qcom,pm8921-gpio"
+		    "qcom,spmi-pmic-gpio"
+
+- reg:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: Register base of the GPIO block and length.
+
+- interrupts:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: Must contain an array of encoded interrupt specifiers for
+		    each available GPIO
+
+- gpio-controller:
+	Usage: required
+	Value type: <none>
+	Definition: Mark the device node as a GPIO controller
+
+- #gpio-cells:
+	Usage: required
+	Value type: <u32>
+	Definition: Must be 2;
+		    the first cell will be used to define gpio number and the
+		    second denotes the flags for this gpio
+
+Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
+a general description of GPIO and interrupt bindings.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+The pin configuration nodes act as a container for an arbitrary number of
+subnodes. Each of these subnodes represents some desired configuration for a
+pin or a list of pins. This configuration can include the
+mux function to select on those pin(s), and various pin configuration
+parameters, as listed below.
+
+
+SUBNODES:
+
+The name of each subnode is not important; all subnodes should be enumerated
+and processed purely based on their content.
+
+Each subnode only affects those parameters that are explicitly listed. In
+other words, a subnode that lists a mux function but no pin configuration
+parameters implies no information about any pin configuration parameters.
+Similarly, a pin subnode that describes a pullup parameter implies no
+information about e.g. the mux function.
+
+The following generic properties as defined in pinctrl-bindings.txt are valid
+to specify in a pin configuration subnode:
+
+- pins:
+	Usage: required
+	Value type: <string-array>
+	Definition: List of gpio pins affected by the properties specified in
+		    this subnode.  Valid pins are:
+		    gpio1-gpio6 for pm8018
+		    gpio1-gpio12 for pm8038
+		    gpio1-gpio40 for pm8058
+		    gpio1-gpio38 for pm8917
+		    gpio1-gpio44 for pm8921
+		    gpio1-gpio36 for pm8941
+		    gpio1-gpio22 for pma8084
+
+- function:
+	Usage: required
+	Value type: <string>
+	Definition: Specify the alternative function to be configured for the
+		    specified pins.  Valid values are:
+		    "normal",
+		    "paired",
+		    "func1",
+		    "func2",
+		    "dtest1",
+		    "dtest2",
+		    "dtest3",
+		    "dtest4"
+
+- bias-disable:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins should be configured as no pull.
+
+- bias-pull-down:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins should be configured as pull down.
+
+- bias-pull-up:
+	Usage: optional
+	Value type: <empty>
+	Definition: The specified pins should be configured as pull up.
+
+- qcom,pull-up-strength:
+	Usage: optional
+	Value type: <u32>
+	Definition: Specifies the strength to use for pull up, if selected.
+		    Valid values are; as defined in
+		    <dt-bindings/pinctrl/qcom,pmic-gpio.h>:
+		    1: 30uA                     (PMIC_GPIO_PULL_UP_30)
+		    2: 1.5uA                    (PMIC_GPIO_PULL_UP_1P5)
+		    3: 31.5uA                   (PMIC_GPIO_PULL_UP_31P5)
+		    4: 1.5uA + 30uA boost       (PMIC_GPIO_PULL_UP_1P5_30)
+		    If this property is ommited 30uA strength will be used if
+		    pull up is selected
+
+- bias-high-impedance:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins will put in high-Z mode and disabled.
+
+- input-enable:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are put in input mode.
+
+- output-high:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are configured in output mode, driven
+		    high.
+
+- output-low:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are configured in output mode, driven
+		    low.
+
+- power-source:
+	Usage: optional
+	Value type: <u32>
+	Definition: Selects the power source for the specified pins. Valid
+		    power sources are defined per chip in
+		    <dt-bindings/pinctrl/qcom,pmic-gpio.h>
+
+- qcom,drive-strength:
+	Usage: optional
+	Value type: <u32>
+	Definition: Selects the drive strength for the specified pins. Value
+		    drive strengths are:
+		    0: no (PMIC_GPIO_STRENGTH_NO)
+		    1: high (PMIC_GPIO_STRENGTH_HIGH) 0.9mA @ 1.8V - 1.9mA @ 2.6V
+		    2: medium (PMIC_GPIO_STRENGTH_MED) 0.6mA @ 1.8V - 1.25mA @ 2.6V
+		    3: low (PMIC_GPIO_STRENGTH_LOW) 0.15mA @ 1.8V - 0.3mA @ 2.6V
+		    as defined in <dt-bindings/pinctrl/qcom,pmic-gpio.h>
+
+- drive-push-pull:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are configured in push-pull mode.
+
+- drive-open-drain:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are configured in open-drain mode.
+
+- drive-open-source:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are configured in open-source mode.
+
+Example:
+
+	pm8921_gpio: gpio@150 {
+		compatible = "qcom,pm8921-gpio";
+		reg = <0x150 0x160>;
+		interrupts = <192 1>, <193 1>, <194 1>,
+			     <195 1>, <196 1>, <197 1>,
+			     <198 1>, <199 1>, <200 1>,
+			     <201 1>, <202 1>, <203 1>,
+			     <204 1>, <205 1>, <206 1>,
+			     <207 1>, <208 1>, <209 1>,
+			     <210 1>, <211 1>, <212 1>,
+			     <213 1>, <214 1>, <215 1>,
+			     <216 1>, <217 1>, <218 1>,
+			     <219 1>, <220 1>, <221 1>,
+			     <222 1>, <223 1>, <224 1>,
+			     <225 1>, <226 1>, <227 1>,
+			     <228 1>, <229 1>, <230 1>,
+			     <231 1>, <232 1>, <233 1>,
+			     <234 1>, <235 1>;
+
+		gpio-controller;
+		#gpio-cells = <2>;
+
+		pm8921_gpio_keys: gpio-keys {
+			volume-keys {
+				pins = "gpio20", "gpio21";
+				function = "normal";
+
+				input-enable;
+				bias-pull-up;
+				drive-push-pull;
+				qcom,drive-strength = <PMIC_GPIO_STRENGTH_NO>;
+				power-source = <PM8921_GPIO_S4>;
+			};
+		};
+	};
diff --git a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
new file mode 100644
index 0000000..fa74d7c
--- /dev/null
+++ b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
@@ -0,0 +1,142 @@
+/*
+ * This header provides constants for the Qualcomm PMIC GPIO binding.
+ */
+
+#ifndef _DT_BINDINGS_PINCTRL_QCOM_PMIC_GPIO_H
+#define _DT_BINDINGS_PINCTRL_QCOM_PMIC_GPIO_H
+
+#define PMIC_GPIO_PULL_UP_30		0
+#define PMIC_GPIO_PULL_UP_1P5		1
+#define PMIC_GPIO_PULL_UP_31P5		2
+#define PMIC_GPIO_PULL_UP_1P5_30	3
+
+#define PMIC_GPIO_STRENGTH_NO		0
+#define PMIC_GPIO_STRENGTH_HIGH		1
+#define PMIC_GPIO_STRENGTH_MED		2
+#define PMIC_GPIO_STRENGTH_LOW		3
+
+/*
+ * Note: PM8018 GPIO3 and GPIO4 are supporting
+ * only S3 and L2 options (1.8V)
+ */
+#define PM8018_GPIO_L6			0
+#define PM8018_GPIO_L5			1
+#define PM8018_GPIO_S3			2
+#define PM8018_GPIO_L14			3
+#define PM8018_GPIO_L2			4
+#define PM8018_GPIO_L4			5
+#define PM8018_GPIO_VDD			6
+
+/*
+ * Note: PM8038 GPIO7 and GPIO8 are supporting
+ * only L11 and L4 options (1.8V)
+ */
+#define PM8038_GPIO_VPH			0
+#define PM8038_GPIO_BB			1
+#define PM8038_GPIO_L11			2
+#define PM8038_GPIO_L15			3
+#define PM8038_GPIO_L4			4
+#define PM8038_GPIO_L3			5
+#define PM8038_GPIO_L17			6
+
+#define PM8058_GPIO_VPH			0
+#define PM8058_GPIO_BB			1
+#define PM8058_GPIO_S3			2
+#define PM8058_GPIO_L3			3
+#define PM8058_GPIO_L7			4
+#define PM8058_GPIO_L6			5
+#define PM8058_GPIO_L5			6
+#define PM8058_GPIO_L2			7
+
+#define PM8917_GPIO_VPH			0
+#define PM8917_GPIO_S4			2
+#define PM8917_GPIO_L15			3
+#define PM8917_GPIO_L4			4
+#define PM8917_GPIO_L3			5
+#define PM8917_GPIO_L17			6
+
+#define PM8921_GPIO_VPH			0
+#define PM8921_GPIO_BB			1
+#define PM8921_GPIO_S4			2
+#define PM8921_GPIO_L15			3
+#define PM8921_GPIO_L4			4
+#define PM8921_GPIO_L3			5
+#define PM8921_GPIO_L17			6
+
+/*
+ * Note: PM8941 gpios from 15 to 18 are supporting
+ * only S3 and L6 options (1.8V)
+ */
+#define PM8941_GPIO_VPH			0
+#define PM8941_GPIO_L1			1
+#define PM8941_GPIO_S3			2
+#define PM8941_GPIO_L6			3
+
+/*
+ * Note: PMA8084 gpios from 15 to 18 are supporting
+ * only S4 and L6 options (1.8V)
+ */
+#define PMA8084_GPIO_VPH		0
+#define PMA8084_GPIO_L1			1
+#define PMA8084_GPIO_S4			2
+#define PMA8084_GPIO_L6			3
+
+/* To be used with "function" */
+#define PMIC_GPIO_FUNC_NORMAL		"normal"
+#define PMIC_GPIO_FUNC_PAIRED		"paired"
+#define PMIC_GPIO_FUNC_FUNC1		"func1"
+#define PMIC_GPIO_FUNC_FUNC2		"func2"
+#define PMIC_GPIO_FUNC_DTEST1		"dtest1"
+#define PMIC_GPIO_FUNC_DTEST2		"dtest2"
+#define PMIC_GPIO_FUNC_DTEST3		"dtest3"
+#define PMIC_GPIO_FUNC_DTEST4		"dtest4"
+
+#define PM8038_GPIO1_2_LPG_DRV		PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO3_5V_BOOST_EN	PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO4_SSBI_ALT_CLK	PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO5_6_EXT_REG_EN	PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO10_11_EXT_REG_EN	PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO6_7_CLK		PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO9_BAT_ALRM_OUT	PMIC_GPIO_FUNC_FUNC1
+#define PM8038_GPIO6_12_KYPD_DRV	PMIC_GPIO_FUNC_FUNC2
+
+#define PM8058_GPIO7_8_MP3_CLK		PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO7_8_BCLK_19P2MHZ	PMIC_GPIO_FUNC_FUNC2
+#define PM8058_GPIO9_26_KYPD_DRV	PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO21_23_UART_TX	PMIC_GPIO_FUNC_FUNC2
+#define PM8058_GPIO24_26_LPG_DRV	PMIC_GPIO_FUNC_FUNC2
+#define PM8058_GPIO33_BCLK_19P2MHZ	PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO34_35_MP3_CLK	PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO36_BCLK_19P2MHZ	PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO37_UPL_OUT		PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO37_UART_M_RX		PMIC_GPIO_FUNC_FUNC2
+#define PM8058_GPIO38_XO_SLEEP_CLK	PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO38_39_CLK_32KHZ	PMIC_GPIO_FUNC_FUNC2
+#define PM8058_GPIO39_MP3_CLK		PMIC_GPIO_FUNC_FUNC1
+#define PM8058_GPIO40_EXT_BB_EN		PMIC_GPIO_FUNC_FUNC1
+
+#define PM8917_GPIO9_18_KEYP_DRV	PMIC_GPIO_FUNC_FUNC1
+#define PM8917_GPIO20_BAT_ALRM_OUT	PMIC_GPIO_FUNC_FUNC1
+#define PM8917_GPIO21_23_UART_TX	PMIC_GPIO_FUNC_FUNC2
+#define PM8917_GPIO25_26_EXT_REG_EN	PMIC_GPIO_FUNC_FUNC1
+#define PM8917_GPIO37_38_XO_SLEEP_CLK	PMIC_GPIO_FUNC_FUNC1
+#define PM8917_GPIO37_38_MP3_CLK	PMIC_GPIO_FUNC_FUNC2
+
+#define PM8941_GPIO9_14_KYPD_DRV	PMIC_GPIO_FUNC_FUNC1
+#define PM8941_GPIO15_18_DIV_CLK	PMIC_GPIO_FUNC_FUNC1
+#define PM8941_GPIO15_18_SLEEP_CLK	PMIC_GPIO_FUNC_FUNC2
+#define PM8941_GPIO23_26_KYPD_DRV	PMIC_GPIO_FUNC_FUNC1
+#define PM8941_GPIO23_26_LPG_DRV_HI	PMIC_GPIO_FUNC_FUNC2
+#define PM8941_GPIO31_BAT_ALRM_OUT	PMIC_GPIO_FUNC_FUNC1
+#define PM8941_GPIO33_36_LPG_DRV_3D	PMIC_GPIO_FUNC_FUNC1
+#define PM8941_GPIO33_36_LPG_DRV_HI	PMIC_GPIO_FUNC_FUNC2
+
+#define PMA8084_GPIO4_5_LPG_DRV		PMIC_GPIO_FUNC_FUNC1
+#define PMA8084_GPIO7_10_LPG_DRV	PMIC_GPIO_FUNC_FUNC1
+#define PMA8084_GPIO5_14_KEYP_DRV	PMIC_GPIO_FUNC_FUNC2
+#define PMA8084_GPIO19_21_KEYP_DRV	PMIC_GPIO_FUNC_FUNC2
+#define PMA8084_GPIO15_18_DIV_CLK	PMIC_GPIO_FUNC_FUNC1
+#define PMA8084_GPIO15_18_SLEEP_CLK	PMIC_GPIO_FUNC_FUNC2
+#define PMA8084_GPIO22_BAT_ALRM_OUT	PMIC_GPIO_FUNC_FUNC1
+
+#endif
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 2/4] pinctrl: Device tree bindings for Qualcomm PMIC MPP block
  2014-09-15 14:44 [PATCH v4 0/4] Qualcomm SPMI PMIC pin controller drivers Ivan T. Ivanov
  2014-09-15 14:44 ` [PATCH v4 1/4] pinctrl: Device tree bindings for Qualcomm PMIC GPIO block Ivan T. Ivanov
@ 2014-09-15 14:44 ` Ivan T. Ivanov
  2014-09-15 14:44 ` [PATCH v4 3/4] pinctrl: Qualcomm SPMI PMIC GPIO pin controller driver Ivan T. Ivanov
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Ivan T. Ivanov @ 2014-09-15 14:44 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala
  Cc: Ivan T. Ivanov, Bjorn Andersson, David Collins, Wu Fenglin,
	linux-kernel, devicetree, linux-arm-msm

DeviceTree binding documentation for Qualcomm SPMI PMIC MPP
pinctrl drivers.

Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
---
 .../devicetree/bindings/pinctrl/qcom,pmic-mpp.txt  | 159 +++++++++++++++++++++
 include/dt-bindings/pinctrl/qcom,pmic-mpp.h        |  44 ++++++
 2 files changed, 203 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
 create mode 100644 include/dt-bindings/pinctrl/qcom,pmic-mpp.h

diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
new file mode 100644
index 0000000..9d4779f
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
@@ -0,0 +1,159 @@
+Qualcomm PMIC Multi-Purpose Pin (MPP) block
+
+This binding describes the MPP block(s) found in the 8xxx series
+of PMIC's from Qualcomm.
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: Should contain "qcom,spmi-pmic-mpp"
+
+- reg:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: Register base of the MPP block and length.
+
+- interrupts:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: Must contain an array of encoded interrupt specifiers for
+		    each available gpio
+
+- gpio-controller:
+	Usage: required
+	Value type: <none>
+	Definition: Mark the device node as a GPIO controller
+
+- #gpio-cells:
+	Usage: required
+	Value type: <u32>
+	Definition: Must be 2;
+		    the first cell will be used to define gpio number and the
+		    second denotes the flags for this gpio
+
+Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
+a general description of GPIO and interrupt bindings.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+The pin configuration nodes act as a container for an arbitrary number of
+subnodes. Each of these subnodes represents some desired configuration for a
+pin or a list of pins. This configuration can include the
+mux function to select on those pin(s), and various pin configuration
+parameters, as listed below.
+
+SUBNODES:
+
+The name of each subnode is not important; all subnodes should be enumerated
+and processed purely based on their content.
+
+Each subnode only affects those parameters that are explicitly listed. In
+other words, a subnode that lists a mux function but no pin configuration
+parameters implies no information about any pin configuration parameters.
+Similarly, a pin subnode that describes a pullup parameter implies no
+information about e.g. the mux function.
+
+The following generic properties as defined in pinctrl-bindings.txt are valid
+to specify in a pin configuration subnode:
+
+- pins:
+	Usage: required
+	Value type: <string-array>
+	Definition: List of gpio pins affected by the properties specified in
+		    this subnode.  Valid pins are:
+		    mpp1-mpp4 for pm8841
+		    mpp1-mpp8 for pm8941
+		    mpp1-mpp4 for pma8084
+
+- function:
+	Usage: required
+	Value type: <string>
+	Definition: Specify the alternative function to be configured for the
+		    specified pins.  Valid values are:
+		    "normal",
+		    "paired",
+		    "dtest1",
+		    "dtest2",
+		    "dtest3",
+		    "dtest4"
+
+- bias-disable:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins should be configured as no pull.
+
+- bias-pull-up:
+	Usage: optional
+	Value type: <u32>
+	Definition: The specified pins should be configured as pull up.
+		    Valid values are 600, 10000 and 30000 in bidirectional mode only,
+		    i.e. when operating in qcom,analog-mode and input and outputs are
+		    enabled. The hardware ignores the configuration when operating in
+		    other modes.
+
+- bias-high-impedance:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins will put in high-Z mode and disabled.
+
+- input-enable:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are put in input mode, i.e. thier input
+		    buffer is enabled
+
+- output-high:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are configured in output mode, driven
+		    high.
+
+- output-low:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are configured in output mode, driven
+		    low.
+
+- power-source:
+	Usage: optional
+	Value type: <u32>
+	Definition: Selects the power source for the specified pins. Valid power
+		    sources are defined in <dt-bindings/pinctrl/qcom,pmic-mpp.h>
+
+- qcom,analog-mode:
+	Usage: optional
+	Value type: <none>
+	Definition: Selects Analog mode of operation: combined with input-enable
+		    and/or output-high, output-log MPP could operate as Bidirectional
+		    Logic, Analog Input, Analog Output.
+
+- qcom,amux-route:
+	Usage: optional
+	Value type: <u32>
+	Definition: Selects the source for analog input. Valid values are
+		    defined in <dt-bindings/pinctrl/qcom,pmic-mpp.h>
+		    PMIC_MPP_AMUX_ROUTE_CH5, PMIC_MPP_AMUX_ROUTE_CH6...
+
+Example:
+
+	mpps@a000 {
+		compatible = "qcom,pm8841-mpp";
+		reg = <0xa000>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupts = <4 0xa0 0 0>, <4 0xa1 0 0>, <4 0xa2 0 0>, <4 0xa3 0 0>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&pm8841_default>;
+
+		pm8841_default: default {
+			gpio {
+				pins = "mpp1", "mpp2", "mpp3", "mpp4";
+				function = "normal";
+				input-enable;
+				power-source = <PM8841_MPP_S3>;
+			};
+		};
+	};
diff --git a/include/dt-bindings/pinctrl/qcom,pmic-mpp.h b/include/dt-bindings/pinctrl/qcom,pmic-mpp.h
new file mode 100644
index 0000000..d2c7dab
--- /dev/null
+++ b/include/dt-bindings/pinctrl/qcom,pmic-mpp.h
@@ -0,0 +1,44 @@
+/*
+ * This header provides constants for the Qualcomm PMIC's
+ * Multi-Purpose Pin binding.
+ */
+
+#ifndef _DT_BINDINGS_PINCTRL_QCOM_PMIC_MPP_H
+#define _DT_BINDINGS_PINCTRL_QCOM_PMIC_MPP_H
+
+/* power-source */
+#define PM8841_MPP_VPH			0
+#define PM8841_MPP_S3			2
+
+#define PM8941_MPP_VPH			0
+#define PM8941_MPP_L1			1
+#define PM8941_MPP_S3			2
+#define PM8941_MPP_L6			3
+
+#define PMA8084_MPP_VPH			0
+#define PMA8084_MPP_L1			1
+#define PMA8084_MPP_S4			2
+#define PMA8084_MPP_L6			3
+
+/*
+ * Analog Input - Set the source for analog input.
+ * To be used with "qcom,amux-route" property
+ */
+#define PMIC_MPP_AMUX_ROUTE_CH5		0
+#define PMIC_MPP_AMUX_ROUTE_CH6		1
+#define PMIC_MPP_AMUX_ROUTE_CH7		2
+#define PMIC_MPP_AMUX_ROUTE_CH8		3
+#define PMIC_MPP_AMUX_ROUTE_ABUS1	4
+#define PMIC_MPP_AMUX_ROUTE_ABUS2	5
+#define PMIC_MPP_AMUX_ROUTE_ABUS3	6
+#define PMIC_MPP_AMUX_ROUTE_ABUS4	7
+
+/* To be used with "function" */
+#define PMIC_MPP_FUNC_NORMAL		"normal"
+#define PMIC_MPP_FUNC_PAIRED		"paired"
+#define PMIC_MPP_FUNC_DTEST1		"dtest1"
+#define PMIC_MPP_FUNC_DTEST2		"dtest2"
+#define PMIC_MPP_FUNC_DTEST3		"dtest3"
+#define PMIC_MPP_FUNC_DTEST4		"dtest4"
+
+#endif
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 3/4] pinctrl: Qualcomm SPMI PMIC GPIO pin controller driver
  2014-09-15 14:44 [PATCH v4 0/4] Qualcomm SPMI PMIC pin controller drivers Ivan T. Ivanov
  2014-09-15 14:44 ` [PATCH v4 1/4] pinctrl: Device tree bindings for Qualcomm PMIC GPIO block Ivan T. Ivanov
  2014-09-15 14:44 ` [PATCH v4 2/4] pinctrl: Device tree bindings for Qualcomm PMIC MPP block Ivan T. Ivanov
@ 2014-09-15 14:44 ` Ivan T. Ivanov
  2014-09-24  4:18   ` Bjorn Andersson
  2014-09-15 14:44 ` [PATCH v4 4/4] pinctrl: Qualcomm SPMI PMIC MPP " Ivan T. Ivanov
  2014-09-23 15:16 ` [PATCH v4 0/4] Qualcomm SPMI PMIC pin controller drivers Linus Walleij
  4 siblings, 1 reply; 11+ messages in thread
From: Ivan T. Ivanov @ 2014-09-15 14:44 UTC (permalink / raw)
  To: Linus Walleij, Grant Likely, Rob Herring
  Cc: Ivan T. Ivanov, Bjorn Andersson, David Collins, Wu Fenglin,
	linux-kernel, devicetree, linux-arm-msm

This is the pinctrl, pinmux, pinconf and gpiolib driver for the
Qualcomm GPIO sub-function blocks found in the PMIC chips.

Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
---
 drivers/pinctrl/qcom/Kconfig                  |  12 +
 drivers/pinctrl/qcom/Makefile                 |   1 +
 drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c | 942 ++++++++++++++++++++++++++
 3 files changed, 955 insertions(+)
 create mode 100644 drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c

diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index d160a71..123248f 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -39,4 +39,16 @@ config PINCTRL_MSM8X74
 	  This is the pinctrl, pinmux, pinconf and gpiolib driver for the
 	  Qualcomm TLMM block found in the Qualcomm 8974 platform.
 
+config PINCTRL_SPMI_PMIC
+       tristate "Qualcomm SPMI PMIC pin controller driver"
+       depends on GPIOLIB && OF
+       select PINMUX
+       select PINCONF
+       select GENERIC_PINCONF
+       help
+         This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+         Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips,
+         which are using SPMI for communication with SoC. Example PMIC's
+         devices are pm8841, pm8941 and pma8084.
+
 endif
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 2a02602..396130c 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_PINCTRL_APQ8064)	+= pinctrl-apq8064.o
 obj-$(CONFIG_PINCTRL_IPQ8064)	+= pinctrl-ipq8064.o
 obj-$(CONFIG_PINCTRL_MSM8960)	+= pinctrl-msm8960.o
 obj-$(CONFIG_PINCTRL_MSM8X74)	+= pinctrl-msm8x74.o
+obj-$(CONFIG_PINCTRL_SPMI_PMIC)	+= pinctrl-spmi-pmic-gpio.o
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c
new file mode 100644
index 0000000..493f0d3
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c
@@ -0,0 +1,942 @@
+/*
+ * Copyright (c) 2012-2014, 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/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
+
+#include "../core.h"
+#include "../pinctrl-utils.h"
+
+#define PMIC_GPIO_ADDRESS_RANGE			0x100
+
+/* type and subtype registers base address offsets */
+#define PMIC_GPIO_REG_TYPE			0x4
+#define PMIC_GPIO_REG_SUBTYPE			0x5
+
+/* GPIO peripheral type and subtype out_values */
+#define PMIC_GPIO_TYPE				0x10
+#define PMIC_GPIO_SUBTYPE_GPIO_4CH		0x1
+#define PMIC_GPIO_SUBTYPE_GPIOC_4CH		0x5
+#define PMIC_GPIO_SUBTYPE_GPIO_8CH		0x9
+#define PMIC_GPIO_SUBTYPE_GPIOC_8CH		0xd
+
+#define PMIC_MPP_REG_RT_STS			0x10
+#define PMIC_MPP_REG_RT_STS_VAL_MASK		0x1
+
+/* control register base address offsets */
+#define PMIC_GPIO_REG_MODE_CTL			0x40
+#define PMIC_GPIO_REG_DIG_VIN_CTL		0x41
+#define PMIC_GPIO_REG_DIG_PULL_CTL		0x42
+#define PMIC_GPIO_REG_DIG_OUT_CTL		0x45
+#define PMIC_GPIO_REG_EN_CTL			0x46
+
+/* PMIC_GPIO_REG_MODE_CTL */
+#define PMIC_GPIO_REG_MODE_VALUE_SHIFT		0x1
+#define PMIC_GPIO_REG_MODE_FUNCTION_SHIFT	1
+#define PMIC_GPIO_REG_MODE_FUNCTION_MASK	0x7
+#define PMIC_GPIO_REG_MODE_DIR_SHIFT		4
+#define PMIC_GPIO_REG_MODE_DIR_MASK		0x7
+
+/* PMIC_GPIO_REG_DIG_VIN_CTL */
+#define PMIC_GPIO_REG_VIN_SHIFT			0
+#define PMIC_GPIO_REG_VIN_MASK			0x7
+
+/* PMIC_GPIO_REG_DIG_PULL_CTL */
+#define PMIC_GPIO_REG_PULL_SHIFT		0
+#define PMIC_GPIO_REG_PULL_MASK			0x7
+
+#define PMIC_GPIO_PULL_DOWN			4
+#define PMIC_GPIO_PULL_DISABLE			5
+
+/* PMIC_GPIO_REG_DIG_OUT_CTL */
+#define PMIC_GPIO_REG_OUT_STRENGTH_SHIFT	0
+#define PMIC_GPIO_REG_OUT_STRENGTH_MASK		0x3
+#define PMIC_GPIO_REG_OUT_TYPE_SHIFT		4
+#define PMIC_GPIO_REG_OUT_TYPE_MASK		0x3
+
+/*
+ * Output type - indicates pin should be configured as push-pull,
+ * open drain or open source.
+ */
+#define PMIC_GPIO_OUT_BUF_CMOS			0
+#define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS	1
+#define PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS	2
+
+/* PMIC_GPIO_REG_EN_CTL */
+#define PMIC_GPIO_REG_MASTER_EN_SHIFT		7
+
+#define PMIC_GPIO_PHYSICAL_OFFSET		1
+
+/* Qualcomm specific pin configurations */
+#define PMIC_GPIO_CONF_PULL_UP			(PIN_CONFIG_END + 1)
+#define PMIC_GPIO_CONF_STRENGTH			(PIN_CONFIG_END + 2)
+
+/**
+ * struct pmic_gpio_pad - keep current GPIO settings
+ * @base: Address base in SPMI device.
+ * @irq: IRQ number which this GPIO generate.
+ * @is_enabled: Set to false when GPIO should be put in high Z state.
+ * @out_value: Cached pin output value
+ * @have_buffer: Set to true if GPIO output could be configured in push-pull,
+ *	open-drain or open-source mode.
+ * @output_enabled: Set to true if GPIO output logic is enabled.
+ * @input_enabled: Set to true if GPIO input buffer logic is enabled.
+ * @num_sources: Number of power-sources supported by this GPIO.
+ * @power_source: Current power-source used.
+ * @buffer_type: Push-pull, open-drain or open-source.
+ * @pullup: Constant current which flow trough GPIO output buffer.
+ * @strength: No, Low, Medium, High
+ * @function: See pmic_gpio_functions[]
+ */
+struct pmic_gpio_pad {
+	u16 base;
+	int irq;
+	bool is_enabled;
+	bool out_value;
+	bool have_buffer;
+	bool output_enabled;
+	bool input_enabled;
+	unsigned int num_sources;
+	unsigned int power_source;
+	unsigned int buffer_type;
+	unsigned int pullup;
+	unsigned int strength;
+	unsigned int function;
+};
+
+struct pmic_gpio_state {
+	struct device *dev;
+	struct regmap *map;
+	struct pinctrl_dev *ctrl;
+	struct gpio_chip chip;
+};
+
+struct pmic_gpio_bindings {
+	const char *property;
+	unsigned param;
+};
+
+static struct pmic_gpio_bindings pmic_gpio_bindings[] = {
+	{"qcom,pull-up-strength",	PMIC_GPIO_CONF_PULL_UP},
+	{"qcom,drive-strength",		PMIC_GPIO_CONF_STRENGTH},
+};
+
+static const char *const pmic_gpio_groups[] = {
+	"gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8",
+	"gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15",
+	"gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
+	"gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29",
+	"gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36",
+};
+
+static const char *const pmic_gpio_functions[] = {
+	PMIC_GPIO_FUNC_NORMAL, PMIC_GPIO_FUNC_PAIRED,
+	PMIC_GPIO_FUNC_FUNC1, PMIC_GPIO_FUNC_FUNC2,
+	PMIC_GPIO_FUNC_DTEST1, PMIC_GPIO_FUNC_DTEST2,
+	PMIC_GPIO_FUNC_DTEST3, PMIC_GPIO_FUNC_DTEST4,
+};
+
+static int pmic_gpio_read(struct pmic_gpio_state *state,
+			  struct pmic_gpio_pad *pad, unsigned int addr)
+{
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(state->map, pad->base + addr, &val);
+	if (ret < 0)
+		dev_err(state->dev, "read 0x%x failed\n", addr);
+	else
+		ret = val;
+
+	return ret;
+}
+
+static int pmic_gpio_write(struct pmic_gpio_state *state,
+			   struct pmic_gpio_pad *pad, unsigned int addr,
+			   unsigned int val)
+{
+	int ret;
+
+	ret = regmap_write(state->map, pad->base + addr, val);
+	if (ret < 0)
+		dev_err(state->dev, "write 0x%x failed\n", addr);
+
+	return ret;
+}
+
+static int pmic_gpio_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	/* Every PIN is a group */
+	return pctldev->desc->npins;
+}
+
+static const char *pmic_gpio_get_group_name(struct pinctrl_dev *pctldev,
+					    unsigned pin)
+{
+	return pctldev->desc->pins[pin].name;
+}
+
+static int pmic_gpio_get_group_pins(struct pinctrl_dev *pctldev, unsigned pin,
+				    const unsigned **pins, unsigned *num_pins)
+{
+	*pins = &pctldev->desc->pins[pin].number;
+	*num_pins = 1;
+	return 0;
+}
+
+static int pmic_gpio_parse_dt_config(struct device_node *np,
+				     struct pinctrl_dev *pctldev,
+				     unsigned long **configs,
+				     unsigned int *nconfs)
+{
+	struct pmic_gpio_bindings *par;
+	unsigned long cfg;
+	int ret, i;
+	u32 val;
+
+	for (i = 0; i < ARRAY_SIZE(pmic_gpio_bindings); i++) {
+
+		par = &pmic_gpio_bindings[i];
+		ret = of_property_read_u32(np, par->property, &val);
+
+		/* property not found */
+		if (ret == -EINVAL)
+			continue;
+
+		/* use zero as default value */
+		if (ret)
+			val = 0;
+
+		dev_dbg(pctldev->dev, "found %s with value %u\n",
+			par->property, val);
+
+		cfg = pinconf_to_config_packed(par->param, val);
+
+		ret = pinctrl_utils_add_config(pctldev, configs, nconfs, cfg);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int pmic_gpio_dt_subnode_to_map(struct pinctrl_dev *pctldev,
+				       struct device_node *np,
+				       struct pinctrl_map **map,
+				       unsigned *reserv, unsigned *nmaps,
+				       enum pinctrl_map_type type)
+{
+	unsigned long *configs = NULL;
+	unsigned nconfs = 0;
+	struct property *prop;
+	const char *group;
+	int ret;
+
+	ret = pmic_gpio_parse_dt_config(np, pctldev, &configs, &nconfs);
+	if (ret < 0)
+		return ret;
+
+	if (!nconfs)
+		return 0;
+
+	ret = of_property_count_strings(np, "pins");
+	if (ret < 0)
+		goto exit;
+
+	ret = pinctrl_utils_reserve_map(pctldev, map, reserv, nmaps, ret);
+	if (ret < 0)
+		goto exit;
+
+	of_property_for_each_string(np, "pins", prop, group) {
+		ret = pinctrl_utils_add_map_configs(pctldev, map,
+						    reserv, nmaps, group,
+						    configs, nconfs, type);
+		if (ret < 0)
+			break;
+	}
+exit:
+	kfree(configs);
+	return ret;
+}
+
+static int pmic_gpio_dt_node_to_map(struct pinctrl_dev *pctldev,
+				    struct device_node *np_config,
+				    struct pinctrl_map **map, unsigned *nmaps)
+{
+	enum pinctrl_map_type type;
+	struct device_node *np;
+	unsigned reserv;
+	int ret;
+
+	ret = 0;
+	*map = NULL;
+	*nmaps = 0;
+	reserv = 0;
+	type = PIN_MAP_TYPE_CONFIGS_GROUP;
+
+	for_each_child_of_node(np_config, np) {
+
+		ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map,
+							&reserv, nmaps, type);
+		if (ret)
+			break;
+
+		ret = pmic_gpio_dt_subnode_to_map(pctldev, np, map, &reserv,
+						  nmaps, type);
+		if (ret)
+			break;
+	}
+
+	if (ret < 0)
+		pinctrl_utils_dt_free_map(pctldev, *map, *nmaps);
+
+	return ret;
+}
+
+static const struct pinctrl_ops pmic_gpio_pinctrl_ops = {
+	.get_groups_count	= pmic_gpio_get_groups_count,
+	.get_group_name		= pmic_gpio_get_group_name,
+	.get_group_pins		= pmic_gpio_get_group_pins,
+	.dt_node_to_map		= pmic_gpio_dt_node_to_map,
+	.dt_free_map		= pinctrl_utils_dt_free_map,
+};
+
+static int pmic_gpio_get_functions_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(pmic_gpio_functions);
+}
+
+static const char *pmic_gpio_get_function_name(struct pinctrl_dev *pctldev,
+					       unsigned function)
+{
+	return pmic_gpio_functions[function];
+}
+
+static int pmic_gpio_get_function_groups(struct pinctrl_dev *pctldev,
+					 unsigned function,
+					 const char *const **groups,
+					 unsigned *const num_qgroups)
+{
+	*groups = pmic_gpio_groups;
+	*num_qgroups = pctldev->desc->npins;
+	return 0;
+}
+
+static int pmic_gpio_pinmux_enable(struct pinctrl_dev *pctldev,
+				   unsigned function, unsigned pin)
+{
+	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
+	struct pmic_gpio_pad *pad;
+	unsigned int val;
+	int ret;
+
+	pad = pctldev->desc->pins[pin].drv_data;
+
+	pad->function = function;
+
+	val = 0;
+	if (pad->output_enabled) {
+		if (pad->input_enabled)
+			val = 2;
+		else
+			val = 1;
+	}
+
+	val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
+	val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
+
+	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
+	if (ret < 0)
+		return ret;
+
+	val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT;
+
+	return pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val);
+}
+
+static const struct pinmux_ops pmic_gpio_pinmux_ops = {
+	.get_functions_count	= pmic_gpio_get_functions_count,
+	.get_function_name	= pmic_gpio_get_function_name,
+	.get_function_groups	= pmic_gpio_get_function_groups,
+	.enable			= pmic_gpio_pinmux_enable,
+};
+
+static int pmic_gpio_config_get(struct pinctrl_dev *pctldev,
+				unsigned int pin, unsigned long *config)
+{
+	unsigned param = pinconf_to_config_param(*config);
+	struct pmic_gpio_pad *pad;
+	unsigned arg;
+
+	pad = pctldev->desc->pins[pin].drv_data;
+
+	switch (param) {
+	case PIN_CONFIG_DRIVE_PUSH_PULL:
+		arg = pad->buffer_type == PMIC_GPIO_OUT_BUF_CMOS;
+		break;
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		arg = pad->buffer_type == PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS;
+		break;
+	case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+		arg = pad->buffer_type == PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS;
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		arg = pad->pullup == PMIC_GPIO_PULL_DOWN;
+		break;
+	case PIN_CONFIG_BIAS_DISABLE:
+		arg = pad->pullup = PMIC_GPIO_PULL_DISABLE;
+		break;
+	case PIN_CONFIG_BIAS_PULL_UP:
+		arg = pad->pullup == PMIC_GPIO_PULL_UP_30;
+		break;
+		break;
+	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+		arg = !pad->is_enabled;
+		break;
+	case PIN_CONFIG_POWER_SOURCE:
+		arg = pad->power_source;
+		break;
+	case PIN_CONFIG_INPUT_ENABLE:
+		arg = pad->input_enabled;
+		break;
+	case PIN_CONFIG_OUTPUT:
+		arg = pad->out_value;
+		break;
+	case PMIC_GPIO_CONF_PULL_UP:
+		arg = pad->pullup;
+		break;
+	case PMIC_GPIO_CONF_STRENGTH:
+		arg = pad->strength;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*config = pinconf_to_config_packed(param, arg);
+	return 0;
+}
+
+static int pmic_gpio_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
+				unsigned long *configs, unsigned nconfs)
+{
+	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
+	struct pmic_gpio_pad *pad;
+	unsigned param, arg;
+	unsigned int val;
+	int i, ret;
+
+	pad = pctldev->desc->pins[pin].drv_data;
+
+	for (i = 0; i < nconfs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		switch (param) {
+		case PIN_CONFIG_DRIVE_PUSH_PULL:
+			pad->buffer_type = PMIC_GPIO_OUT_BUF_CMOS;
+			break;
+		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+			if (!pad->have_buffer)
+				return -EINVAL;
+			pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS;
+			break;
+		case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+			if (!pad->have_buffer)
+				return -EINVAL;
+			pad->buffer_type = PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS;
+			break;
+		case PIN_CONFIG_BIAS_DISABLE:
+			pad->pullup = PMIC_GPIO_PULL_DISABLE;
+			break;
+		case PIN_CONFIG_BIAS_PULL_UP:
+			pad->pullup = PMIC_GPIO_PULL_UP_30;
+			break;
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			if (arg)
+				pad->pullup = PMIC_GPIO_PULL_DOWN;
+			else
+				pad->pullup = PMIC_GPIO_PULL_DISABLE;
+			break;
+		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+			pad->is_enabled = false;
+			break;
+		case PIN_CONFIG_POWER_SOURCE:
+			if (arg > pad->num_sources)
+				return -EINVAL;
+			pad->power_source = arg;
+			break;
+		case PIN_CONFIG_INPUT_ENABLE:
+			pad->input_enabled = arg ? true : false;
+			break;
+		case PIN_CONFIG_OUTPUT:
+			pad->output_enabled = true;
+			pad->out_value = arg;
+			break;
+		case PMIC_GPIO_CONF_PULL_UP:
+			if (arg > PMIC_GPIO_PULL_UP_1P5_30)
+				return -EINVAL;
+			pad->pullup = arg;
+			break;
+		case PMIC_GPIO_CONF_STRENGTH:
+			if (arg > PMIC_GPIO_STRENGTH_LOW)
+				return -EINVAL;
+			pad->strength = arg;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	val = pad->power_source << PMIC_GPIO_REG_VIN_SHIFT;
+
+	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL, val);
+	if (ret < 0)
+		return ret;
+
+	val = pad->pullup << PMIC_GPIO_REG_PULL_SHIFT;
+
+	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL, val);
+	if (ret < 0)
+		return ret;
+
+	val = pad->buffer_type << PMIC_GPIO_REG_OUT_TYPE_SHIFT;
+	val = pad->strength << PMIC_GPIO_REG_OUT_STRENGTH_SHIFT;
+
+	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL, val);
+	if (ret < 0)
+		return ret;
+
+	val = 0;
+	if (pad->output_enabled) {
+		if (pad->input_enabled)
+			val = 2;
+		else
+			val = 1;
+	}
+
+	val = val << PMIC_GPIO_REG_MODE_DIR_SHIFT;
+	val |= pad->function << PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
+	val |= pad->out_value & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
+
+	return pmic_gpio_write(state, pad, PMIC_GPIO_REG_MODE_CTL, val);
+}
+
+static void pmic_gpio_config_dbg_show(struct pinctrl_dev *pctldev,
+				      struct seq_file *s, unsigned pin)
+{
+	struct pmic_gpio_state *state = pinctrl_dev_get_drvdata(pctldev);
+	struct pmic_gpio_pad *pad;
+	int ret, val;
+
+	static const char *const biases[] = {
+		"pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA",
+		"pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull"
+	};
+	static const char *const buffer_types[] = {
+		"push-pull", "open-drain", "open-source"
+	};
+	static const char *const strengths[] = {
+		"no", "high", "medium", "low"
+	};
+
+	pad = pctldev->desc->pins[pin].drv_data;
+
+	seq_printf(s, " gpio%-2d:", pin + PMIC_GPIO_PHYSICAL_OFFSET);
+
+	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_EN_CTL);
+
+	if (val < 0 || !(val >> PMIC_GPIO_REG_MASTER_EN_SHIFT)) {
+		seq_puts(s, " ---");
+	} else {
+
+		if (!pad->input_enabled) {
+			ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS);
+			if (!ret) {
+				ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
+				pad->out_value = ret;
+			}
+		}
+
+		seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in");
+		seq_printf(s, " %-7s", pmic_gpio_functions[pad->function]);
+		seq_printf(s, " vin-%d", pad->power_source);
+		seq_printf(s, " %-27s", biases[pad->pullup]);
+		seq_printf(s, " %-10s", buffer_types[pad->buffer_type]);
+		seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
+		seq_printf(s, " %-7s", strengths[pad->strength]);
+	}
+}
+
+static const struct pinconf_ops pmic_gpio_pinconf_ops = {
+	.pin_config_group_get		= pmic_gpio_config_get,
+	.pin_config_group_set		= pmic_gpio_config_set,
+	.pin_config_group_dbg_show	= pmic_gpio_config_dbg_show,
+};
+
+static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
+{
+	struct pmic_gpio_state *state;
+	unsigned long config;
+
+	state = container_of(chip, struct pmic_gpio_state, chip);
+	config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
+
+	return pmic_gpio_config_set(state->ctrl, pin, &config, 1);
+}
+
+static int pmic_gpio_direction_output(struct gpio_chip *chip,
+				      unsigned pin, int val)
+{
+	struct pmic_gpio_state *state;
+	unsigned long config;
+
+	state = container_of(chip, struct pmic_gpio_state, chip);
+	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
+
+	return pmic_gpio_config_set(state->ctrl, pin, &config, 1);
+}
+
+static int pmic_gpio_get(struct gpio_chip *chip, unsigned pin)
+{
+	struct pmic_gpio_state *state;
+	struct pmic_gpio_pad *pad;
+	int ret;
+
+	state = container_of(chip, struct pmic_gpio_state, chip);
+	pad = state->ctrl->desc->pins[pin].drv_data;
+
+	if (!pad->is_enabled)
+		return -EINVAL;
+
+	if (pad->input_enabled) {
+		ret = pmic_gpio_read(state, pad, PMIC_MPP_REG_RT_STS);
+		if (ret < 0)
+			return ret;
+
+		pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK;
+	}
+
+	return pad->out_value;
+}
+
+static void pmic_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
+{
+	struct pmic_gpio_state *state;
+	unsigned long config;
+
+	state = container_of(chip, struct pmic_gpio_state, chip);
+	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
+
+	pmic_gpio_config_set(state->ctrl, pin, &config, 1);
+}
+
+static int pmic_gpio_request(struct gpio_chip *chip, unsigned base)
+{
+	return pinctrl_request_gpio(chip->base + base);
+}
+
+static void pmic_gpio_free(struct gpio_chip *chip, unsigned base)
+{
+	pinctrl_free_gpio(chip->base + base);
+}
+
+static int pmic_gpio_of_xlate(struct gpio_chip *chip,
+			      const struct of_phandle_args *gpio_desc,
+			      u32 *flags)
+{
+	if (chip->of_gpio_n_cells < 2)
+		return -EINVAL;
+
+	if (flags)
+		*flags = gpio_desc->args[1];
+
+	return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET;
+}
+
+static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
+{
+	struct pmic_gpio_state *state;
+	struct pmic_gpio_pad *pad;
+
+	state = container_of(chip, struct pmic_gpio_state, chip);
+	pad = state->ctrl->desc->pins[pin].drv_data;
+
+	return pad->irq;
+}
+
+static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+	struct pmic_gpio_state *state;
+	unsigned i;
+
+	state = container_of(chip, struct pmic_gpio_state, chip);
+
+	for (i = 0; i < chip->ngpio; i++) {
+		pmic_gpio_config_dbg_show(state->ctrl, s, i);
+		seq_puts(s, "\n");
+	}
+}
+
+static const struct gpio_chip pmic_gpio_gpio_template = {
+	.direction_input	= pmic_gpio_direction_input,
+	.direction_output	= pmic_gpio_direction_output,
+	.get			= pmic_gpio_get,
+	.set			= pmic_gpio_set,
+	.request		= pmic_gpio_request,
+	.free			= pmic_gpio_free,
+	.of_xlate		= pmic_gpio_of_xlate,
+	.to_irq			= pmic_gpio_to_irq,
+	.dbg_show		= pmic_gpio_dbg_show,
+};
+
+static int pmic_gpio_populate(struct pmic_gpio_state *state,
+			      struct pmic_gpio_pad *pad)
+{
+	int type, subtype, val, dir;
+
+	type = pmic_gpio_read(state, pad, PMIC_GPIO_REG_TYPE);
+	if (type < 0)
+		return type;
+
+	if (type != PMIC_GPIO_TYPE) {
+		dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n",
+			type, pad->base);
+		return -ENODEV;
+	}
+
+	subtype = pmic_gpio_read(state, pad, PMIC_GPIO_REG_SUBTYPE);
+	if (subtype < 0)
+		return subtype;
+
+	switch (subtype) {
+	case PMIC_GPIO_SUBTYPE_GPIO_4CH:
+		pad->have_buffer = true;
+	case PMIC_GPIO_SUBTYPE_GPIOC_4CH:
+		pad->num_sources = 4;
+		break;
+	case PMIC_GPIO_SUBTYPE_GPIO_8CH:
+		pad->have_buffer = true;
+	case PMIC_GPIO_SUBTYPE_GPIOC_8CH:
+		pad->num_sources = 8;
+		break;
+	default:
+		dev_err(state->dev, "unknown GPIO type 0x%x\n", subtype);
+		return -ENODEV;
+	}
+
+	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_MODE_CTL);
+	if (val < 0)
+		return val;
+
+	pad->out_value = val & PMIC_GPIO_REG_MODE_VALUE_SHIFT;
+
+	dir = val >> PMIC_GPIO_REG_MODE_DIR_SHIFT;
+	dir &= PMIC_GPIO_REG_MODE_DIR_MASK;
+	switch (dir) {
+	case 0:
+		pad->input_enabled = true;
+		pad->output_enabled = false;
+		break;
+	case 1:
+		pad->input_enabled = false;
+		pad->output_enabled = true;
+		break;
+	case 2:
+		pad->input_enabled = true;
+		pad->output_enabled = true;
+		break;
+	default:
+		dev_err(state->dev, "unknown GPIO direction\n");
+		return -ENODEV;
+	}
+
+	pad->function = val >> PMIC_GPIO_REG_MODE_FUNCTION_SHIFT;
+	pad->function &= PMIC_GPIO_REG_MODE_FUNCTION_MASK;
+
+	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_VIN_CTL);
+	if (val < 0)
+		return val;
+
+	pad->power_source = val >> PMIC_GPIO_REG_VIN_SHIFT;
+	pad->power_source &= PMIC_GPIO_REG_VIN_MASK;
+
+	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_PULL_CTL);
+	if (val < 0)
+		return val;
+
+	pad->pullup = val >> PMIC_GPIO_REG_PULL_SHIFT;
+	pad->pullup &= PMIC_GPIO_REG_PULL_MASK;
+
+	val = pmic_gpio_read(state, pad, PMIC_GPIO_REG_DIG_OUT_CTL);
+	if (val < 0)
+		return val;
+
+	pad->strength = val >> PMIC_GPIO_REG_OUT_STRENGTH_SHIFT;
+	pad->strength &= PMIC_GPIO_REG_OUT_STRENGTH_MASK;
+
+	pad->buffer_type = val >> PMIC_GPIO_REG_OUT_TYPE_SHIFT;
+	pad->buffer_type &= PMIC_GPIO_REG_OUT_TYPE_MASK;
+
+	/* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */
+	pad->is_enabled = true;
+	return 0;
+}
+
+static int pmic_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct pinctrl_pin_desc *pindesc;
+	struct pinctrl_desc *pctrldesc;
+	struct pmic_gpio_pad *pad, *pads;
+	struct pmic_gpio_state *state;
+	int ret, npins, i;
+	u32 res[2];
+
+	ret = of_property_read_u32_array(dev->of_node, "reg", res, 2);
+	if (ret < 0) {
+		dev_err(dev, "missing base address and/or range");
+		return ret;
+	}
+
+	npins = res[1] / PMIC_GPIO_ADDRESS_RANGE;
+
+	if (!npins)
+		return -EINVAL;
+
+	BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups));
+
+	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, state);
+
+	state->dev = &pdev->dev;
+	state->map = dev_get_regmap(dev->parent, NULL);
+
+	pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
+	if (!pindesc)
+		return -ENOMEM;
+
+	pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
+	if (!pads)
+		return -ENOMEM;
+
+	pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
+	if (!pctrldesc)
+		return -ENOMEM;
+
+	pctrldesc->pctlops = &pmic_gpio_pinctrl_ops;
+	pctrldesc->pmxops = &pmic_gpio_pinmux_ops;
+	pctrldesc->confops = &pmic_gpio_pinconf_ops;
+	pctrldesc->owner = THIS_MODULE;
+	pctrldesc->name = dev_name(dev);
+	pctrldesc->pins = pindesc;
+	pctrldesc->npins = npins;
+
+	for (i = 0; i < npins; i++, pindesc++) {
+		pad = &pads[i];
+		pindesc->drv_data = pad;
+		pindesc->number = i;
+		pindesc->name = pmic_gpio_groups[i];
+
+		pad->irq = platform_get_irq(pdev, i);
+		if (pad->irq < 0)
+			return pad->irq;
+
+		pad->base = res[0] + i * PMIC_GPIO_ADDRESS_RANGE;
+
+		ret = pmic_gpio_populate(state, pad);
+		if (ret < 0)
+			return ret;
+	}
+
+	state->chip = pmic_gpio_gpio_template;
+	state->chip.dev = dev;
+	state->chip.base = -1;
+	state->chip.ngpio = npins;
+	state->chip.label = dev_name(dev);
+	state->chip.of_gpio_n_cells = 2;
+	state->chip.can_sleep = false;
+
+	state->ctrl = pinctrl_register(pctrldesc, dev, state);
+	if (!state->ctrl)
+		return -ENODEV;
+
+	ret = gpiochip_add(&state->chip);
+	if (ret) {
+		dev_err(state->dev, "can't add gpio chip\n");
+		goto err_chip;
+	}
+
+	ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
+	if (ret) {
+		dev_err(dev, "failed to add pin range\n");
+		goto err_range;
+	}
+
+	return 0;
+
+err_range:
+	gpiochip_remove(&state->chip);
+err_chip:
+	pinctrl_unregister(state->ctrl);
+	return ret;
+}
+
+static int pmic_gpio_remove(struct platform_device *pdev)
+{
+	struct pmic_gpio_state *state = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = gpiochip_remove(&state->chip);
+	if (ret < 0)
+		return ret;
+
+	pinctrl_unregister(state->ctrl);
+	return 0;
+}
+
+static const struct of_device_id pmic_gpio_of_match[] = {
+	{ .compatible = "qcom,spmi-pmic-gpio" },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, pmic_gpio_of_match);
+
+static struct platform_driver pmic_gpio_driver = {
+	.driver = {
+		   .name = "spmi-pmic-gpio",
+		   .owner = THIS_MODULE,
+		   .of_match_table = pmic_gpio_of_match,
+	},
+	.probe	= pmic_gpio_probe,
+	.remove = pmic_gpio_remove,
+};
+
+module_platform_driver(pmic_gpio_driver);
+
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC GPIO pin control driver");
+MODULE_ALIAS("platform:spmi-pmic-gpio");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 4/4] pinctrl: Qualcomm SPMI PMIC MPP pin controller driver
  2014-09-15 14:44 [PATCH v4 0/4] Qualcomm SPMI PMIC pin controller drivers Ivan T. Ivanov
                   ` (2 preceding siblings ...)
  2014-09-15 14:44 ` [PATCH v4 3/4] pinctrl: Qualcomm SPMI PMIC GPIO pin controller driver Ivan T. Ivanov
@ 2014-09-15 14:44 ` Ivan T. Ivanov
  2014-09-23 15:16 ` [PATCH v4 0/4] Qualcomm SPMI PMIC pin controller drivers Linus Walleij
  4 siblings, 0 replies; 11+ messages in thread
From: Ivan T. Ivanov @ 2014-09-15 14:44 UTC (permalink / raw)
  To: Linus Walleij, Grant Likely, Rob Herring
  Cc: Ivan T. Ivanov, Bjorn Andersson, David Collins, Wu Fenglin,
	linux-kernel, devicetree, linux-arm-msm

This is the pinctrl, pinmux, pinconf and gpiolib driver for the
Qualcomm MPP sub-function blocks found in the PMIC chips.

Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
---
 drivers/pinctrl/qcom/Makefile                |   1 +
 drivers/pinctrl/qcom/pinctrl-spmi-pmic-mpp.c | 956 +++++++++++++++++++++++++++
 2 files changed, 957 insertions(+)
 create mode 100644 drivers/pinctrl/qcom/pinctrl-spmi-pmic-mpp.c

diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 396130c..1c3d14c 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_PINCTRL_IPQ8064)	+= pinctrl-ipq8064.o
 obj-$(CONFIG_PINCTRL_MSM8960)	+= pinctrl-msm8960.o
 obj-$(CONFIG_PINCTRL_MSM8X74)	+= pinctrl-msm8x74.o
 obj-$(CONFIG_PINCTRL_SPMI_PMIC)	+= pinctrl-spmi-pmic-gpio.o
+obj-$(CONFIG_PINCTRL_SPMI_PMIC)	+= pinctrl-spmi-pmic-mpp.o
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-pmic-mpp.c b/drivers/pinctrl/qcom/pinctrl-spmi-pmic-mpp.c
new file mode 100644
index 0000000..23b13e2
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-pmic-mpp.c
@@ -0,0 +1,956 @@
+/*
+ * Copyright (c) 2012-2014, 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/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <dt-bindings/pinctrl/qcom,pmic-mpp.h>
+
+#include "../core.h"
+#include "../pinctrl-utils.h"
+
+#define PMIC_MPP_ADDRESS_RANGE			0x100
+
+/*
+ * Pull Up Values - it indicates whether a pull-up should be
+ * applied for bidirectional mode only. The hardware ignores the
+ * configuration when operating in other modes.
+ */
+#define PMIC_MPP_PULL_UP_0P6KOHM		0
+#define PMIC_MPP_PULL_UP_10KOHM			1
+#define PMIC_MPP_PULL_UP_30KOHM			2
+#define PMIC_MPP_PULL_UP_OPEN			3
+
+/* type registers base address bases */
+#define PMIC_MPP_REG_TYPE			0x4
+#define PMIC_MPP_REG_SUBTYPE			0x5
+
+/* mpp peripheral type and subtype values */
+#define PMIC_MPP_TYPE				0x11
+#define PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT		0x3
+#define PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT	0x4
+#define PMIC_MPP_SUBTYPE_4CH_NO_SINK		0x5
+#define PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK	0x6
+#define PMIC_MPP_SUBTYPE_4CH_FULL_FUNC		0x7
+#define PMIC_MPP_SUBTYPE_8CH_FULL_FUNC		0xf
+
+#define PMIC_MPP_REG_RT_STS			0x10
+#define PMIC_MPP_REG_RT_STS_VAL_MASK		0x1
+
+/* control register base address bases */
+#define PMIC_MPP_REG_MODE_CTL			0x40
+#define PMIC_MPP_REG_DIG_VIN_CTL		0x41
+#define PMIC_MPP_REG_DIG_PULL_CTL		0x42
+#define PMIC_MPP_REG_DIG_IN_CTL			0x43
+#define PMIC_MPP_REG_EN_CTL			0x46
+#define PMIC_MPP_REG_AIN_CTL			0x4a
+
+/* PMIC_MPP_REG_MODE_CTL */
+#define PMIC_MPP_REG_MODE_VALUE_MASK		0x1
+#define PMIC_MPP_REG_MODE_FUNCTION_SHIFT	1
+#define PMIC_MPP_REG_MODE_FUNCTION_MASK		0x7
+#define PMIC_MPP_REG_MODE_DIR_SHIFT		4
+#define PMIC_MPP_REG_MODE_DIR_MASK		0x7
+
+/* PMIC_MPP_REG_DIG_VIN_CTL */
+#define PMIC_MPP_REG_VIN_SHIFT			0
+#define PMIC_MPP_REG_VIN_MASK			0x7
+
+/* PMIC_MPP_REG_DIG_PULL_CTL */
+#define PMIC_MPP_REG_PULL_SHIFT			0
+#define PMIC_MPP_REG_PULL_MASK			0x7
+
+/* PMIC_MPP_REG_EN_CTL */
+#define PMIC_MPP_REG_MASTER_EN_SHIFT		7
+
+/* PMIC_MPP_REG_AIN_CTL */
+#define PMIC_MPP_REG_AIN_ROUTE_SHIFT		0
+#define PMIC_MPP_REG_AIN_ROUTE_MASK		0x7
+
+#define PMIC_MPP_PHYSICAL_OFFSET		1
+
+/* Qualcomm specific pin configurations */
+#define PMIC_MPP_CONF_AMUX_ROUTE		(PIN_CONFIG_END + 1)
+#define PMIC_MPP_CONF_ANALOG_MODE		(PIN_CONFIG_END + 2)
+
+/**
+ * struct pmic_mpp_pad - keep current MPP settings
+ * @base: Address base in SPMI device.
+ * @irq: IRQ number which this MPP generate.
+ * @is_enabled: Set to false when MPP should be put in high Z state.
+ * @out_value: Cached pin output value.
+ * @output_enabled: Set to true if MPP output logic is enabled.
+ * @input_enabled: Set to true if MPP input buffer logic is enabled.
+ * @analog_mode: Set to true when MPP should operate in Analog Input, Analog
+ *	Output or Bidirectional Analog mode.
+ * @num_sources: Number of power-sources supported by this MPP.
+ * @power_source: Current power-source used.
+ * @amux_input: Set the source for analog input.
+ * @pullup: Pullup resistor value. Valid in Bidirectional mode only.
+ * @function: See pmic_mpp_functions[].
+ */
+struct pmic_mpp_pad {
+	u16 base;
+	int irq;
+	bool is_enabled;
+	bool out_value;
+	bool output_enabled;
+	bool input_enabled;
+	bool analog_mode;
+	unsigned int num_sources;
+	unsigned int power_source;
+	unsigned int amux_input;
+	unsigned int pullup;
+	unsigned int function;
+};
+
+struct pmic_mpp_state {
+	struct device *dev;
+	struct regmap *map;
+	struct pinctrl_dev *ctrl;
+	struct gpio_chip chip;
+};
+
+struct pmic_mpp_bindings {
+	const char *property;
+	unsigned param;
+};
+
+static struct pmic_mpp_bindings pmic_mpp_bindings[] = {
+	{"qcom,amux-route",	PMIC_MPP_CONF_AMUX_ROUTE},
+	{"qcom,analog-mode",	PMIC_MPP_CONF_ANALOG_MODE},
+};
+
+static const char *const pmic_mpp_groups[] = {
+	"mpp1", "mpp2", "mpp3", "mpp4", "mpp5", "mpp6", "mpp7", "mpp8",
+};
+
+static const char *const pmic_mpp_functions[] = {
+	PMIC_MPP_FUNC_NORMAL, PMIC_MPP_FUNC_PAIRED,
+	"reserved1", "reserved2",
+	PMIC_MPP_FUNC_DTEST1, PMIC_MPP_FUNC_DTEST2,
+	PMIC_MPP_FUNC_DTEST3, PMIC_MPP_FUNC_DTEST4,
+};
+
+static int pmic_mpp_read(struct pmic_mpp_state *state,
+			 struct pmic_mpp_pad *pad, unsigned int addr)
+{
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(state->map, pad->base + addr, &val);
+	if (ret < 0)
+		dev_err(state->dev, "read 0x%x failed\n", addr);
+	else
+		ret = val;
+
+	return ret;
+}
+
+static int pmic_mpp_write(struct pmic_mpp_state *state,
+			  struct pmic_mpp_pad *pad, unsigned int addr,
+			  unsigned int val)
+{
+	int ret;
+
+	ret = regmap_write(state->map, pad->base + addr, val);
+	if (ret < 0)
+		dev_err(state->dev, "write 0x%x failed\n", addr);
+
+	return ret;
+}
+
+static int pmic_mpp_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	/* Every PIN is a group */
+	return pctldev->desc->npins;
+}
+
+static const char *pmic_mpp_get_group_name(struct pinctrl_dev *pctldev,
+					   unsigned pin)
+{
+	return pctldev->desc->pins[pin].name;
+}
+
+static int pmic_mpp_get_group_pins(struct pinctrl_dev *pctldev,
+				   unsigned pin,
+				   const unsigned **pins, unsigned *num_pins)
+{
+	*pins = &pctldev->desc->pins[pin].number;
+	*num_pins = 1;
+	return 0;
+}
+
+static int pmic_mpp_parse_dt_config(struct device_node *np,
+				    struct pinctrl_dev *pctldev,
+				    unsigned long **configs,
+				    unsigned int *nconfs)
+{
+	struct pmic_mpp_bindings *par;
+	unsigned long cfg;
+	int ret, i;
+	u32 val;
+
+	for (i = 0; i < ARRAY_SIZE(pmic_mpp_bindings); i++) {
+
+		par = &pmic_mpp_bindings[i];
+		ret = of_property_read_u32(np, par->property, &val);
+
+		/* property not found */
+		if (ret == -EINVAL)
+			continue;
+
+		/* use zero as default value, when no value is specified */
+		if (ret)
+			val = 0;
+
+		dev_dbg(pctldev->dev, "found %s with value %u\n",
+			par->property, val);
+
+		cfg = pinconf_to_config_packed(par->param, val);
+
+		ret = pinctrl_utils_add_config(pctldev, configs, nconfs, cfg);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int pmic_mpp_dt_subnode_to_map(struct pinctrl_dev *pctldev,
+				      struct device_node *np,
+				      struct pinctrl_map **map,
+				      unsigned *reserv, unsigned *nmaps,
+				      enum pinctrl_map_type type)
+{
+	unsigned long *configs = NULL;
+	unsigned nconfs = 0;
+	struct property *prop;
+	const char *group;
+	int ret;
+
+	ret = pmic_mpp_parse_dt_config(np, pctldev, &configs, &nconfs);
+	if (ret < 0)
+		return ret;
+
+	if (!nconfs)
+		return 0;
+
+	ret = of_property_count_strings(np, "pins");
+	if (ret < 0)
+		goto exit;
+
+	ret = pinctrl_utils_reserve_map(pctldev, map, reserv, nmaps, ret);
+	if (ret < 0)
+		goto exit;
+
+	of_property_for_each_string(np, "pins", prop, group) {
+		ret = pinctrl_utils_add_map_configs(pctldev, map,
+						    reserv, nmaps, group,
+						    configs, nconfs, type);
+		if (ret < 0)
+			break;
+	}
+exit:
+	kfree(configs);
+	return ret;
+}
+
+static int pmic_mpp_dt_node_to_map(struct pinctrl_dev *pctldev,
+				   struct device_node *np_config,
+				   struct pinctrl_map **map, unsigned *nmaps)
+{
+	struct device_node *np;
+	enum pinctrl_map_type type;
+	unsigned reserv;
+	int ret;
+
+	ret = 0;
+	*map = NULL;
+	*nmaps = 0;
+	reserv = 0;
+	type = PIN_MAP_TYPE_CONFIGS_GROUP;
+
+	for_each_child_of_node(np_config, np) {
+
+		ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map,
+							&reserv, nmaps, type);
+		if (ret)
+			break;
+
+		ret = pmic_mpp_dt_subnode_to_map(pctldev, np, map, &reserv,
+						 nmaps, type);
+		if (ret)
+			break;
+	}
+
+	if (ret < 0)
+		pinctrl_utils_dt_free_map(pctldev, *map, *nmaps);
+
+	return ret;
+}
+
+static const struct pinctrl_ops pmic_mpp_pinctrl_ops = {
+	.get_groups_count	= pmic_mpp_get_groups_count,
+	.get_group_name		= pmic_mpp_get_group_name,
+	.get_group_pins		= pmic_mpp_get_group_pins,
+	.dt_node_to_map		= pmic_mpp_dt_node_to_map,
+	.dt_free_map		= pinctrl_utils_dt_free_map,
+};
+
+static int pmic_mpp_get_functions_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(pmic_mpp_functions);
+}
+
+static const char *pmic_mpp_get_function_name(struct pinctrl_dev *pctldev,
+					      unsigned function)
+{
+	return pmic_mpp_functions[function];
+}
+
+static int pmic_mpp_get_function_groups(struct pinctrl_dev *pctldev,
+					unsigned function,
+					const char *const **groups,
+					unsigned *const num_qgroups)
+{
+	*groups = pmic_mpp_groups;
+	*num_qgroups = pctldev->desc->npins;
+	return 0;
+}
+
+static int pmic_mpp_pinmux_enable(struct pinctrl_dev *pctldev,
+				  unsigned function, unsigned pin)
+{
+	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
+	struct pmic_mpp_pad *pad;
+	unsigned int val;
+	int ret;
+
+	pad = pctldev->desc->pins[pin].drv_data;
+
+	pad->function = function;
+
+	if (!pad->analog_mode) {
+		val = 0;	/* just digital input */
+		if (pad->output_enabled) {
+			if (pad->input_enabled)
+				val = 2; /* digital input and output */
+			else
+				val = 1; /* just digital output */
+		}
+	} else {
+		val = 4;	/* just analog input */
+		if (pad->output_enabled) {
+			if (pad->input_enabled)
+				val = 3; /* analog input and output */
+			else
+				val = 5; /* just analog output */
+		}
+	}
+
+	val |= pad->function << PMIC_MPP_REG_MODE_FUNCTION_SHIFT;
+	val |= pad->out_value & PMIC_MPP_REG_MODE_VALUE_MASK;
+
+	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_MODE_CTL, val);
+	if (ret < 0)
+		return ret;
+
+	val = pad->is_enabled << PMIC_MPP_REG_MASTER_EN_SHIFT;
+
+	return pmic_mpp_write(state, pad, PMIC_MPP_REG_EN_CTL, val);
+}
+
+static const struct pinmux_ops pmic_mpp_pinmux_ops = {
+	.get_functions_count	= pmic_mpp_get_functions_count,
+	.get_function_name	= pmic_mpp_get_function_name,
+	.get_function_groups	= pmic_mpp_get_function_groups,
+	.enable			= pmic_mpp_pinmux_enable,
+};
+
+static int pmic_mpp_config_get(struct pinctrl_dev *pctldev,
+			       unsigned int pin, unsigned long *config)
+{
+	unsigned param = pinconf_to_config_param(*config);
+	struct pmic_mpp_pad *pad;
+	unsigned arg = 0;
+
+	pad = pctldev->desc->pins[pin].drv_data;
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_DISABLE:
+		arg = pad->pullup == PMIC_MPP_PULL_UP_OPEN;
+		break;
+	case PIN_CONFIG_BIAS_PULL_UP:
+		switch (pad->pullup) {
+		case PMIC_MPP_PULL_UP_OPEN:
+			arg = 0;
+			break;
+		case PMIC_MPP_PULL_UP_0P6KOHM:
+			arg = 600;
+			break;
+		case PMIC_MPP_PULL_UP_10KOHM:
+			arg = 10000;
+			break;
+		case PMIC_MPP_PULL_UP_30KOHM:
+			arg = 30000;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+		arg = !pad->is_enabled;
+		break;
+	case PIN_CONFIG_POWER_SOURCE:
+		arg = pad->power_source;
+		break;
+	case PIN_CONFIG_INPUT_ENABLE:
+		arg = pad->input_enabled;
+		break;
+	case PIN_CONFIG_OUTPUT:
+		arg = pad->out_value;
+		break;
+	case PMIC_MPP_CONF_AMUX_ROUTE:
+		arg = pad->amux_input;
+		break;
+	case PMIC_MPP_CONF_ANALOG_MODE:
+		arg = pad->analog_mode;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Convert register value to pinconf value */
+	*config = pinconf_to_config_packed(param, arg);
+	return 0;
+}
+
+static int pmic_mpp_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
+			       unsigned long *configs, unsigned nconfs)
+{
+	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
+	struct pmic_mpp_pad *pad;
+	unsigned param, arg;
+	unsigned int val;
+	int i, ret;
+
+	pad = pctldev->desc->pins[pin].drv_data;
+
+	for (i = 0; i < nconfs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		switch (param) {
+		case PIN_CONFIG_BIAS_DISABLE:
+			pad->pullup = PMIC_MPP_PULL_UP_OPEN;
+			break;
+		case PIN_CONFIG_BIAS_PULL_UP:
+			switch (arg) {
+			case 600:
+				pad->pullup = PMIC_MPP_PULL_UP_0P6KOHM;
+				break;
+			case 10000:
+				pad->pullup = PMIC_MPP_PULL_UP_10KOHM;
+				break;
+			case 30000:
+				pad->pullup = PMIC_MPP_PULL_UP_30KOHM;
+				break;
+			default:
+				return -EINVAL;
+			}
+			break;
+		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+			pad->is_enabled = false;
+			break;
+		case PIN_CONFIG_POWER_SOURCE:
+			if (arg >= pad->num_sources)
+				return -EINVAL;
+			pad->power_source = arg;
+			break;
+		case PIN_CONFIG_INPUT_ENABLE:
+			pad->input_enabled = arg ? true : false;
+			break;
+		case PIN_CONFIG_OUTPUT:
+			pad->output_enabled = true;
+			pad->out_value = arg;
+			break;
+		case PMIC_MPP_CONF_AMUX_ROUTE:
+			if (arg >= PMIC_MPP_AMUX_ROUTE_ABUS4)
+				return -EINVAL;
+			pad->amux_input = arg;
+			break;
+		case PMIC_MPP_CONF_ANALOG_MODE:
+			pad->analog_mode = true;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	val = pad->power_source << PMIC_MPP_REG_VIN_SHIFT;
+
+	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_VIN_CTL, val);
+	if (ret < 0)
+		return ret;
+
+	val = pad->pullup << PMIC_MPP_REG_PULL_SHIFT;
+
+	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_DIG_PULL_CTL, val);
+	if (ret < 0)
+		return ret;
+
+	val = pad->amux_input & PMIC_MPP_REG_AIN_ROUTE_MASK;
+
+	ret = pmic_mpp_write(state, pad, PMIC_MPP_REG_AIN_CTL, val);
+	if (ret < 0)
+		return ret;
+
+	if (!pad->analog_mode) {
+		val = 0;	/* just digital input */
+		if (pad->output_enabled) {
+			if (pad->input_enabled)
+				val = 2; /* digital input and output */
+			else
+				val = 1; /* just digital output */
+		}
+	} else {
+		val = 4;	/* just analog input */
+		if (pad->output_enabled) {
+			if (pad->input_enabled)
+				val = 3; /* analog input and output */
+			else
+				val = 5; /* just analog output */
+		}
+	}
+
+	val = val << PMIC_MPP_REG_MODE_DIR_SHIFT;
+	val |= pad->function << PMIC_MPP_REG_MODE_FUNCTION_SHIFT;
+	val |= pad->out_value & PMIC_MPP_REG_MODE_VALUE_MASK;
+
+	return pmic_mpp_write(state, pad, PMIC_MPP_REG_MODE_CTL, val);
+}
+
+static void pmic_mpp_config_dbg_show(struct pinctrl_dev *pctldev,
+				     struct seq_file *s, unsigned pin)
+{
+	struct pmic_mpp_state *state = pinctrl_dev_get_drvdata(pctldev);
+	struct pmic_mpp_pad *pad;
+	int ret, val;
+
+	static const char *const biases[] = {
+		"0.6kOhm", "10kOhm", "30kOhm", "Disabled"
+	};
+
+
+	pad = pctldev->desc->pins[pin].drv_data;
+
+	seq_printf(s, " mpp%-2d:", pin + PMIC_MPP_PHYSICAL_OFFSET);
+
+	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_EN_CTL);
+
+	if (val < 0 || !(val >> PMIC_MPP_REG_MASTER_EN_SHIFT)) {
+		seq_puts(s, " ---");
+	} else {
+
+		if (pad->input_enabled) {
+			ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS);
+			if (!ret) {
+				ret &= PMIC_MPP_REG_RT_STS_VAL_MASK;
+				pad->out_value = ret;
+			}
+		}
+
+		seq_printf(s, " %-4s", pad->output_enabled ? "out" : "in");
+		seq_printf(s, " %-4s", pad->analog_mode ? "ana" : "dig");
+		seq_printf(s, " %-7s", pmic_mpp_functions[pad->function]);
+		seq_printf(s, " vin-%d", pad->power_source);
+		seq_printf(s, " %-8s", biases[pad->pullup]);
+		seq_printf(s, " %-4s", pad->out_value ? "high" : "low");
+	}
+}
+
+static const struct pinconf_ops pmic_mpp_pinconf_ops = {
+	.pin_config_group_get		= pmic_mpp_config_get,
+	.pin_config_group_set		= pmic_mpp_config_set,
+	.pin_config_group_dbg_show	= pmic_mpp_config_dbg_show,
+};
+
+static int pmic_mpp_direction_input(struct gpio_chip *chip, unsigned pin)
+{
+	struct pmic_mpp_state *state;
+	unsigned long config;
+
+	state = container_of(chip, struct pmic_mpp_state, chip);
+	config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
+
+	return pmic_mpp_config_set(state->ctrl, pin, &config, 1);
+}
+
+static int pmic_mpp_direction_output(struct gpio_chip *chip,
+				     unsigned pin, int val)
+{
+	struct pmic_mpp_state *state;
+	unsigned long config;
+
+	state = container_of(chip, struct pmic_mpp_state, chip);
+	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
+
+	return pmic_mpp_config_set(state->ctrl, pin, &config, 1);
+}
+
+static int pmic_mpp_get(struct gpio_chip *chip, unsigned pin)
+{
+	struct pmic_mpp_state *state;
+	struct pmic_mpp_pad *pad;
+	int ret;
+
+	state = container_of(chip, struct pmic_mpp_state, chip);
+	pad = state->ctrl->desc->pins[pin].drv_data;
+
+	if (pad->input_enabled) {
+		ret = pmic_mpp_read(state, pad, PMIC_MPP_REG_RT_STS);
+		if (ret < 0)
+			return ret;
+
+		pad->out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK;
+	}
+
+	return pad->out_value;
+}
+
+static void pmic_mpp_set(struct gpio_chip *chip, unsigned pin, int value)
+{
+	struct pmic_mpp_state *state;
+	unsigned long config;
+
+	state = container_of(chip, struct pmic_mpp_state, chip);
+	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
+
+	pmic_mpp_config_set(state->ctrl, pin, &config, 1);
+}
+
+static int pmic_mpp_request(struct gpio_chip *chip, unsigned base)
+{
+	return pinctrl_request_gpio(chip->base + base);
+}
+
+static void pmic_mpp_free(struct gpio_chip *chip, unsigned base)
+{
+	pinctrl_free_gpio(chip->base + base);
+}
+
+static int pmic_mpp_of_xlate(struct gpio_chip *chip,
+			     const struct of_phandle_args *gpio_desc,
+			     u32 *flags)
+{
+	if (chip->of_gpio_n_cells < 2)
+		return -EINVAL;
+
+	if (flags)
+		*flags = gpio_desc->args[1];
+
+	return gpio_desc->args[0] - PMIC_MPP_PHYSICAL_OFFSET;
+}
+
+static int pmic_mpp_to_irq(struct gpio_chip *chip, unsigned pin)
+{
+	struct pmic_mpp_state *state;
+	struct pmic_mpp_pad *pad;
+
+	state = container_of(chip, struct pmic_mpp_state, chip);
+	pad = state->ctrl->desc->pins[pin].drv_data;
+
+	return pad->irq;
+}
+
+static void pmic_mpp_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+	struct pmic_mpp_state *state;
+	unsigned i;
+
+	state = container_of(chip, struct pmic_mpp_state, chip);
+
+	for (i = 0; i < chip->ngpio; i++) {
+		pmic_mpp_config_dbg_show(state->ctrl, s, i);
+		seq_puts(s, "\n");
+	}
+}
+
+static const struct gpio_chip pmic_mpp_gpio_template = {
+	.direction_input	= pmic_mpp_direction_input,
+	.direction_output	= pmic_mpp_direction_output,
+	.get			= pmic_mpp_get,
+	.set			= pmic_mpp_set,
+	.request		= pmic_mpp_request,
+	.free			= pmic_mpp_free,
+	.of_xlate		= pmic_mpp_of_xlate,
+	.to_irq			= pmic_mpp_to_irq,
+	.dbg_show		= pmic_mpp_dbg_show,
+};
+
+static int pmic_mpp_populate(struct pmic_mpp_state *state,
+			     struct pmic_mpp_pad *pad)
+{
+	int type, subtype, val, dir;
+
+	type = pmic_mpp_read(state, pad, PMIC_MPP_REG_TYPE);
+	if (type < 0)
+		return type;
+
+	if (type != PMIC_MPP_TYPE) {
+		dev_err(state->dev, "incorrect block type 0x%x at 0x%x\n",
+			type, pad->base);
+		return -ENODEV;
+	}
+
+	subtype = pmic_mpp_read(state, pad, PMIC_MPP_REG_SUBTYPE);
+	if (subtype < 0)
+		return subtype;
+
+	switch (subtype) {
+	case PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT:
+	case PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
+	case PMIC_MPP_SUBTYPE_4CH_NO_SINK:
+	case PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK:
+	case PMIC_MPP_SUBTYPE_4CH_FULL_FUNC:
+		pad->num_sources = 4;
+		break;
+	case PMIC_MPP_SUBTYPE_8CH_FULL_FUNC:
+		pad->num_sources = 8;
+		break;
+	default:
+		dev_err(state->dev, "unknown MPP type 0x%x at 0x%x\n",
+			subtype, pad->base);
+		return -ENODEV;
+	}
+
+	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_MODE_CTL);
+	if (val < 0)
+		return val;
+
+	pad->out_value = val & PMIC_MPP_REG_MODE_VALUE_MASK;
+
+	dir = val >> PMIC_MPP_REG_MODE_DIR_SHIFT;
+	dir &= PMIC_MPP_REG_MODE_DIR_MASK;
+
+	switch (dir) {
+	case 0:
+		pad->input_enabled = true;
+		pad->output_enabled = false;
+		pad->analog_mode = false;
+		break;
+	case 1:
+		pad->input_enabled = false;
+		pad->output_enabled = true;
+		pad->analog_mode = false;
+		break;
+	case 2:
+		pad->input_enabled = true;
+		pad->output_enabled = true;
+		pad->analog_mode = false;
+		break;
+	case 3:
+		pad->input_enabled = true;
+		pad->output_enabled = true;
+		pad->analog_mode = true;
+		break;
+	case 4:
+		pad->input_enabled = true;
+		pad->output_enabled = false;
+		pad->analog_mode = true;
+		break;
+	case 5:
+		pad->input_enabled = false;
+		pad->output_enabled = true;
+		pad->analog_mode = true;
+		break;
+	default:
+		dev_err(state->dev, "unknown MPP direction\n");
+		return -ENODEV;
+	}
+
+	pad->function = val >> PMIC_MPP_REG_MODE_FUNCTION_SHIFT;
+	pad->function &= PMIC_MPP_REG_MODE_FUNCTION_MASK;
+
+	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_VIN_CTL);
+	if (val < 0)
+		return val;
+
+	pad->power_source = val >> PMIC_MPP_REG_VIN_SHIFT;
+	pad->power_source &= PMIC_MPP_REG_VIN_MASK;
+
+	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_DIG_PULL_CTL);
+	if (val < 0)
+		return val;
+
+	pad->pullup = val >> PMIC_MPP_REG_PULL_SHIFT;
+	pad->pullup &= PMIC_MPP_REG_PULL_MASK;
+
+	val = pmic_mpp_read(state, pad, PMIC_MPP_REG_AIN_CTL);
+	if (val < 0)
+		return val;
+
+	pad->amux_input = val >> PMIC_MPP_REG_AIN_ROUTE_SHIFT;
+	pad->amux_input &= PMIC_MPP_REG_AIN_ROUTE_MASK;
+
+	/* Pin could be disabled with PIN_CONFIG_BIAS_HIGH_IMPEDANCE */
+	pad->is_enabled = true;
+	return 0;
+}
+
+static int pmic_mpp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct pinctrl_pin_desc *pindesc;
+	struct pinctrl_desc *pctrldesc;
+	struct pmic_mpp_pad *pad, *pads;
+	struct pmic_mpp_state *state;
+	int ret, npins, i;
+	u32 res[2];
+
+	ret = of_property_read_u32_array(dev->of_node, "reg", res, 2);
+	if (ret < 0) {
+		dev_err(dev, "missing base address and/or range");
+		return ret;
+	}
+
+	npins = res[1] / PMIC_MPP_ADDRESS_RANGE;
+	if (!npins)
+		return -EINVAL;
+
+	BUG_ON(npins > ARRAY_SIZE(pmic_mpp_groups));
+
+	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, state);
+
+	state->dev = &pdev->dev;
+	state->map = dev_get_regmap(dev->parent, NULL);
+
+	pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
+	if (!pindesc)
+		return -ENOMEM;
+
+	pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
+	if (!pads)
+		return -ENOMEM;
+
+	pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL);
+	if (!pctrldesc)
+		return -ENOMEM;
+
+	pctrldesc->pctlops = &pmic_mpp_pinctrl_ops;
+	pctrldesc->pmxops = &pmic_mpp_pinmux_ops;
+	pctrldesc->confops = &pmic_mpp_pinconf_ops;
+	pctrldesc->owner = THIS_MODULE;
+	pctrldesc->name = dev_name(dev);
+	pctrldesc->pins = pindesc;
+	pctrldesc->npins = npins;
+
+	for (i = 0; i < npins; i++, pindesc++) {
+		pad = &pads[i];
+		pindesc->drv_data = pad;
+		pindesc->number = i;
+		pindesc->name = pmic_mpp_groups[i];
+
+		pad->irq = platform_get_irq(pdev, i);
+		if (pad->irq < 0)
+			return pad->irq;
+
+		pad->base = res[0] + i * PMIC_MPP_ADDRESS_RANGE;
+
+		ret = pmic_mpp_populate(state, pad);
+		if (ret < 0)
+			return ret;
+	}
+
+	state->chip = pmic_mpp_gpio_template;
+	state->chip.dev = dev;
+	state->chip.base = -1;
+	state->chip.ngpio = npins;
+	state->chip.label = dev_name(dev);
+	state->chip.of_gpio_n_cells = 2;
+	state->chip.can_sleep = false;
+
+	state->ctrl = pinctrl_register(pctrldesc, dev, state);
+	if (!state->ctrl)
+		return -ENODEV;
+
+	ret = gpiochip_add(&state->chip);
+	if (ret) {
+		dev_err(state->dev, "can't add gpio chip\n");
+		goto err_chip;
+	}
+
+	ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
+	if (ret) {
+		dev_err(dev, "failed to add pin range\n");
+		goto err_range;
+	}
+
+	return 0;
+
+err_range:
+	gpiochip_remove(&state->chip);
+err_chip:
+	pinctrl_unregister(state->ctrl);
+	return ret;
+}
+
+static int pmic_mpp_remove(struct platform_device *pdev)
+{
+	struct pmic_mpp_state *state = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = gpiochip_remove(&state->chip);
+	if (ret < 0)
+		return ret;
+
+	pinctrl_unregister(state->ctrl);
+	return 0;
+}
+
+static const struct of_device_id pmic_mpp_of_match[] = {
+	{ .compatible = "qcom,spmi-pmic-mpp" },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, pmic_mpp_of_match);
+
+static struct platform_driver pmic_mpp_driver = {
+	.driver = {
+		   .name = "spmi-pmic-mpp",
+		   .owner = THIS_MODULE,
+		   .of_match_table = pmic_mpp_of_match,
+	},
+	.probe	= pmic_mpp_probe,
+	.remove = pmic_mpp_remove,
+};
+
+module_platform_driver(pmic_mpp_driver);
+
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC MPP pin control driver");
+MODULE_ALIAS("platform:spmi-pmic-mpp");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v4 0/4] Qualcomm SPMI PMIC pin controller drivers
  2014-09-15 14:44 [PATCH v4 0/4] Qualcomm SPMI PMIC pin controller drivers Ivan T. Ivanov
                   ` (3 preceding siblings ...)
  2014-09-15 14:44 ` [PATCH v4 4/4] pinctrl: Qualcomm SPMI PMIC MPP " Ivan T. Ivanov
@ 2014-09-23 15:16 ` Linus Walleij
  4 siblings, 0 replies; 11+ messages in thread
From: Linus Walleij @ 2014-09-23 15:16 UTC (permalink / raw)
  To: Ivan T. Ivanov
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Grant Likely, Bjorn Andersson, David Collins, Wu Fenglin,
	linux-kernel, devicetree, linux-arm-msm

On Mon, Sep 15, 2014 at 4:44 PM, Ivan T. Ivanov <iivanov@mm-sol.com> wrote:

> This is forth version of the patches posted earlier here[1].

I mainly want you and Björn to cross-ACK each others' patches
and then I'm ready to merge this.

Björn are these 4 patches OK with you?

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v4 3/4] pinctrl: Qualcomm SPMI PMIC GPIO pin controller driver
  2014-09-15 14:44 ` [PATCH v4 3/4] pinctrl: Qualcomm SPMI PMIC GPIO pin controller driver Ivan T. Ivanov
@ 2014-09-24  4:18   ` Bjorn Andersson
  2014-09-24  8:14     ` Ivan T. Ivanov
  2014-09-24 13:09     ` Ivan T. Ivanov
  0 siblings, 2 replies; 11+ messages in thread
From: Bjorn Andersson @ 2014-09-24  4:18 UTC (permalink / raw)
  To: Ivan T. Ivanov
  Cc: Linus Walleij, Grant Likely, Rob Herring, David Collins,
	Wu Fenglin, linux-kernel, devicetree, linux-arm-msm

On Mon 15 Sep 07:44 PDT 2014, Ivan T. Ivanov wrote:

> This is the pinctrl, pinmux, pinconf and gpiolib driver for the
> Qualcomm GPIO sub-function blocks found in the PMIC chips.
> 
> Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>

I think this looks pretty good, just some minor comments. Mostly on the future
compatibility of the Kconfig and compatible.

It's much in line with what I hacked up for pm8xxx, I was just hoping to get
something from Thomas regarding irq_read_line() before pushing it again...

> ---
>  drivers/pinctrl/qcom/Kconfig                  |  12 +
>  drivers/pinctrl/qcom/Makefile                 |   1 +
>  drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c | 942 ++++++++++++++++++++++++++
>  3 files changed, 955 insertions(+)
>  create mode 100644 drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c
> 
> diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
> index d160a71..123248f 100644
> --- a/drivers/pinctrl/qcom/Kconfig
> +++ b/drivers/pinctrl/qcom/Kconfig
> @@ -39,4 +39,16 @@ config PINCTRL_MSM8X74
>           This is the pinctrl, pinmux, pinconf and gpiolib driver for the
>           Qualcomm TLMM block found in the Qualcomm 8974 platform.
> 
> +config PINCTRL_SPMI_PMIC

As SPMI is a MIPI specification for doing power management I think it's safe to
assume that this is not going to be the only "spmi pmic" pinctrl driver.
So please make this a little bit more specific by throwing in a QCOM here.

> +       tristate "Qualcomm SPMI PMIC pin controller driver"
> +       depends on GPIOLIB && OF
> +       select PINMUX
> +       select PINCONF
> +       select GENERIC_PINCONF
> +       help
> +         This is the pinctrl, pinmux, pinconf and gpiolib driver for the
> +         Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips,
> +         which are using SPMI for communication with SoC. Example PMIC's
> +         devices are pm8841, pm8941 and pma8084.
> +
>  endif
> diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
> index 2a02602..396130c 100644
> --- a/drivers/pinctrl/qcom/Makefile
> +++ b/drivers/pinctrl/qcom/Makefile
> @@ -4,3 +4,4 @@ obj-$(CONFIG_PINCTRL_APQ8064)   += pinctrl-apq8064.o
>  obj-$(CONFIG_PINCTRL_IPQ8064)  += pinctrl-ipq8064.o
>  obj-$(CONFIG_PINCTRL_MSM8960)  += pinctrl-msm8960.o
>  obj-$(CONFIG_PINCTRL_MSM8X74)  += pinctrl-msm8x74.o
> +obj-$(CONFIG_PINCTRL_SPMI_PMIC)        += pinctrl-spmi-pmic-gpio.o
> diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c
> new file mode 100644
> index 0000000..493f0d3
> --- /dev/null
> +++ b/drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c

This is fine, no need to change as we have qcom in the path...

[..]
> +
> +/**
> + * struct pmic_gpio_pad - keep current GPIO settings

Some indentation in this table would be nice.
Content look sane though.

> + * @base: Address base in SPMI device.
> + * @irq: IRQ number which this GPIO generate.
> + * @is_enabled: Set to false when GPIO should be put in high Z state.
> + * @out_value: Cached pin output value
> + * @have_buffer: Set to true if GPIO output could be configured in push-pull,
> + *     open-drain or open-source mode.
> + * @output_enabled: Set to true if GPIO output logic is enabled.
> + * @input_enabled: Set to true if GPIO input buffer logic is enabled.
> + * @num_sources: Number of power-sources supported by this GPIO.
> + * @power_source: Current power-source used.
> + * @buffer_type: Push-pull, open-drain or open-source.
> + * @pullup: Constant current which flow trough GPIO output buffer.
> + * @strength: No, Low, Medium, High
> + * @function: See pmic_gpio_functions[]
> + */
> +struct pmic_gpio_pad {
> +       u16 base;
> +       int irq;
> +       bool is_enabled;
> +       bool out_value;
> +       bool have_buffer;
> +       bool output_enabled;
> +       bool input_enabled;
> +       unsigned int num_sources;
> +       unsigned int power_source;
> +       unsigned int buffer_type;
> +       unsigned int pullup;
> +       unsigned int strength;
> +       unsigned int function;
> +};
> +
[..]
> +
> +static int pmic_gpio_parse_dt_config(struct device_node *np,
> +                                    struct pinctrl_dev *pctldev,
> +                                    unsigned long **configs,
> +                                    unsigned int *nconfs)
> +{
> +       struct pmic_gpio_bindings *par;
> +       unsigned long cfg;
> +       int ret, i;
> +       u32 val;
> +
> +       for (i = 0; i < ARRAY_SIZE(pmic_gpio_bindings); i++) {
> +

Empty line

> +               par = &pmic_gpio_bindings[i];
> +               ret = of_property_read_u32(np, par->property, &val);
> +
> +               /* property not found */
> +               if (ret == -EINVAL)
> +                       continue;
> +
> +               /* use zero as default value */
> +               if (ret)
> +                       val = 0;
> +
> +               dev_dbg(pctldev->dev, "found %s with value %u\n",
> +                       par->property, val);
> +
> +               cfg = pinconf_to_config_packed(par->param, val);
> +
> +               ret = pinctrl_utils_add_config(pctldev, configs, nconfs, cfg);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
[..]
> +
> +static int pmic_gpio_dt_node_to_map(struct pinctrl_dev *pctldev,
> +                                   struct device_node *np_config,
> +                                   struct pinctrl_map **map, unsigned *nmaps)
> +{
> +       enum pinctrl_map_type type;
> +       struct device_node *np;
> +       unsigned reserv;
> +       int ret;
> +
> +       ret = 0;
> +       *map = NULL;
> +       *nmaps = 0;
> +       reserv = 0;
> +       type = PIN_MAP_TYPE_CONFIGS_GROUP;
> +
> +       for_each_child_of_node(np_config, np) {
> +

Empty line

> +               ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map,
> +                                                       &reserv, nmaps, type);
> +               if (ret)
> +                       break;
> +
> +               ret = pmic_gpio_dt_subnode_to_map(pctldev, np, map, &reserv,
> +                                                 nmaps, type);
> +               if (ret)
> +                       break;
> +       }
> +
> +       if (ret < 0)
> +               pinctrl_utils_dt_free_map(pctldev, *map, *nmaps);
> +
> +       return ret;
> +}
> +
[..]
> +
> +static int pmic_gpio_config_get(struct pinctrl_dev *pctldev,
> +                               unsigned int pin, unsigned long *config)
> +{
> +       unsigned param = pinconf_to_config_param(*config);
> +       struct pmic_gpio_pad *pad;
> +       unsigned arg;
> +
> +       pad = pctldev->desc->pins[pin].drv_data;
> +
> +       switch (param) {
> +       case PIN_CONFIG_DRIVE_PUSH_PULL:
> +               arg = pad->buffer_type == PMIC_GPIO_OUT_BUF_CMOS;
> +               break;
> +       case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +               arg = pad->buffer_type == PMIC_GPIO_OUT_BUF_OPEN_DRAIN_NMOS;
> +               break;
> +       case PIN_CONFIG_DRIVE_OPEN_SOURCE:
> +               arg = pad->buffer_type == PMIC_GPIO_OUT_BUF_OPEN_DRAIN_PMOS;
> +               break;
> +       case PIN_CONFIG_BIAS_PULL_DOWN:
> +               arg = pad->pullup == PMIC_GPIO_PULL_DOWN;
> +               break;
> +       case PIN_CONFIG_BIAS_DISABLE:
> +               arg = pad->pullup = PMIC_GPIO_PULL_DISABLE;
> +               break;
> +       case PIN_CONFIG_BIAS_PULL_UP:
> +               arg = pad->pullup == PMIC_GPIO_PULL_UP_30;
> +               break;

Extra break;

> +               break;
> +       case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
> +               arg = !pad->is_enabled;
> +               break;
> +       case PIN_CONFIG_POWER_SOURCE:
> +               arg = pad->power_source;
> +               break;
> +       case PIN_CONFIG_INPUT_ENABLE:
> +               arg = pad->input_enabled;
> +               break;
> +       case PIN_CONFIG_OUTPUT:
> +               arg = pad->out_value;
> +               break;
> +       case PMIC_GPIO_CONF_PULL_UP:
> +               arg = pad->pullup;
> +               break;
> +       case PMIC_GPIO_CONF_STRENGTH:
> +               arg = pad->strength;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       *config = pinconf_to_config_packed(param, arg);
> +       return 0;
> +}
> +
[..]
> +static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
> +{
> +       struct pmic_gpio_state *state;
> +       unsigned long config;
> +
> +       state = container_of(chip, struct pmic_gpio_state, chip);

You use this container_of plenty of times, consider breaking it out to a
to_gpio_state() helper function and you can fit it on the declaration line.

> +       config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
> +
> +       return pmic_gpio_config_set(state->ctrl, pin, &config, 1);
> +}
> +
[..]
> +
> +static int pmic_gpio_of_xlate(struct gpio_chip *chip,
> +                             const struct of_phandle_args *gpio_desc,
> +                             u32 *flags)
> +{
> +       if (chip->of_gpio_n_cells < 2)
> +               return -EINVAL;
> +
> +       if (flags)
> +               *flags = gpio_desc->args[1];
> +
> +       return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET;
> +}

If you change:
 gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
to:
 gpiochip_add_pin_range(&state->chip, dev_name(dev), 1, 0, npins);

And you treat the gpio functions as taking the gpio number instead of pinctrl
number (i.e. subtract 1 in those), then gpiolib will provide this function for
you.

[..]
> +
> +static const struct of_device_id pmic_gpio_of_match[] = {
> +       { .compatible = "qcom,spmi-pmic-gpio" },

I think this should be more specific, because hopefully the spmi specification
will outlive the current pmic gpio block.

So I think you need to list the pmic blocks here (e.g. "qcom,pm8941-gpio").

> +       { },
> +};
> +
> +MODULE_DEVICE_TABLE(of, pmic_gpio_of_match);
> +
> +static struct platform_driver pmic_gpio_driver = {
> +       .driver = {
> +                  .name = "spmi-pmic-gpio",

The name should include "qcom" as well, to make it less prone to collisions.

> +                  .owner = THIS_MODULE,

owner filled in for you by module_platform_driver()

> +                  .of_match_table = pmic_gpio_of_match,
> +       },
> +       .probe  = pmic_gpio_probe,
> +       .remove = pmic_gpio_remove,
> +};
> +
> +module_platform_driver(pmic_gpio_driver);
> +

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v4 3/4] pinctrl: Qualcomm SPMI PMIC GPIO pin controller driver
  2014-09-24  4:18   ` Bjorn Andersson
@ 2014-09-24  8:14     ` Ivan T. Ivanov
  2014-09-24 13:09     ` Ivan T. Ivanov
  1 sibling, 0 replies; 11+ messages in thread
From: Ivan T. Ivanov @ 2014-09-24  8:14 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Linus Walleij, Grant Likely, Rob Herring, David Collins,
	Wu Fenglin, linux-kernel, devicetree, linux-arm-msm

On Tue, 2014-09-23 at 21:18 -0700, Bjorn Andersson wrote:
> On Mon 15 Sep 07:44 PDT 2014, Ivan T. Ivanov wrote:
> 
> > This is the pinctrl, pinmux, pinconf and gpiolib driver for the
> > Qualcomm GPIO sub-function blocks found in the PMIC chips.
> > 
> > Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
> 
> I think this looks pretty good, just some minor comments. Mostly on the future
> compatibility of the Kconfig and compatible.

Thanks.

> 
> It's much in line with what I hacked up for pm8xxx, I was just hoping to get
> something from Thomas regarding irq_read_line() before pushing it again...
> 
> > ---
> >  drivers/pinctrl/qcom/Kconfig                  |  12 +
> >  drivers/pinctrl/qcom/Makefile                 |   1 +
> >  drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c | 942 ++++++++++++++++++++++++++
> >  3 files changed, 955 insertions(+)
> >  create mode 100644 drivers/pinctrl/qcom/pinctrl-spmi-pmic-gpio.c

I am planing to remove pmic part from the file name.

Comments will be fixed in next version.

Thank you.
Ivan


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v4 3/4] pinctrl: Qualcomm SPMI PMIC GPIO pin controller driver
  2014-09-24  4:18   ` Bjorn Andersson
  2014-09-24  8:14     ` Ivan T. Ivanov
@ 2014-09-24 13:09     ` Ivan T. Ivanov
  2014-09-30 17:02       ` Bjorn Andersson
  1 sibling, 1 reply; 11+ messages in thread
From: Ivan T. Ivanov @ 2014-09-24 13:09 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Linus Walleij, Grant Likely, Rob Herring, David Collins,
	Wu Fenglin, linux-kernel, devicetree, linux-arm-msm

On Tue, 2014-09-23 at 21:18 -0700, Bjorn Andersson wrote:
> On Mon 15 Sep 07:44 PDT 2014, Ivan T. Ivanov wrote:
> 

<snip>

> > +static int pmic_gpio_of_xlate(struct gpio_chip *chip,
> > +                             const struct of_phandle_args *gpio_desc,
> > +                             u32 *flags)
> > +{
> > +       if (chip->of_gpio_n_cells < 2)
> > +               return -EINVAL;
> > +
> > +       if (flags)
> > +               *flags = gpio_desc->args[1];
> > +
> > +       return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET;
> > +}
> 
> If you change:
>  gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
> to:
>  gpiochip_add_pin_range(&state->chip, dev_name(dev), 1, 0, npins);
> 
> And you treat the gpio functions as taking the gpio number instead of pinctrl
> number (i.e. subtract 1 in those), then gpiolib will provide this function for
> you.
> 

I am unable to make this work. of_gpio_simple_xlate() didn't know that
GPIO range is offset with 1. Requesting last GPIO return error. And
debug output looks weird, for example:

# cat /sys/kernel/debug/gpio
...
GPIOs 220-255, platform/0.c000.gpios, 0.c000.gpios:
...

# cat /sys/kernel/debug/pinctrl/0.c000.gpios/gpio-ranges 
GPIO ranges handled:
1: 0.c000.gpios GPIOS [221 - 256] PINS [0 - 35]

Advice, please.

> [..]
> > +
> > +static const struct of_device_id pmic_gpio_of_match[] = {
> > +       { .compatible = "qcom,spmi-pmic-gpio" },
> 
> I think this should be more specific, because hopefully the spmi specification
> will outlive the current pmic gpio block.
> 
> So I think you need to list the pmic blocks here (e.g. "qcom,pm8941-gpio").

I can rename this to lovely "qcom,qpnp-gpio" :-), in this way driver
can outlive bus on which device is connected this time.

Regards,
Ivan



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v4 3/4] pinctrl: Qualcomm SPMI PMIC GPIO pin controller driver
  2014-09-24 13:09     ` Ivan T. Ivanov
@ 2014-09-30 17:02       ` Bjorn Andersson
  2014-10-07 14:14         ` Linus Walleij
  0 siblings, 1 reply; 11+ messages in thread
From: Bjorn Andersson @ 2014-09-30 17:02 UTC (permalink / raw)
  To: Ivan T. Ivanov, Linus Walleij
  Cc: Grant Likely, Rob Herring, David Collins, Wu Fenglin,
	linux-kernel, devicetree, linux-arm-msm

On Wed 24 Sep 06:09 PDT 2014, Ivan T. Ivanov wrote:

> On Tue, 2014-09-23 at 21:18 -0700, Bjorn Andersson wrote:
> > On Mon 15 Sep 07:44 PDT 2014, Ivan T. Ivanov wrote:
> > 
> 
> <snip>
> 
> > > +static int pmic_gpio_of_xlate(struct gpio_chip *chip,
> > > +                             const struct of_phandle_args *gpio_desc,
> > > +                             u32 *flags)
> > > +{
> > > +       if (chip->of_gpio_n_cells < 2)
> > > +               return -EINVAL;
> > > +
> > > +       if (flags)
> > > +               *flags = gpio_desc->args[1];
> > > +
> > > +       return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET;
> > > +}
> > 
> > If you change:
> >  gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins);
> > to:
> >  gpiochip_add_pin_range(&state->chip, dev_name(dev), 1, 0, npins);
> > 
> > And you treat the gpio functions as taking the gpio number instead of pinctrl
> > number (i.e. subtract 1 in those), then gpiolib will provide this function for
> > you.
> > 
> 
> I am unable to make this work. of_gpio_simple_xlate() didn't know that
> GPIO range is offset with 1. Requesting last GPIO return error. And
> debug output looks weird, for example:
> 
> # cat /sys/kernel/debug/gpio
> ...
> GPIOs 220-255, platform/0.c000.gpios, 0.c000.gpios:
> ...
> 
> # cat /sys/kernel/debug/pinctrl/0.c000.gpios/gpio-ranges 
> GPIO ranges handled:
> 1: 0.c000.gpios GPIOS [221 - 256] PINS [0 - 35]
> 
> Advice, please.
> 

Looking at of_gpio_simple_xlate() shows that you're right. It doesn't seem to
be possible to have a gpiochip that is not 0-based. Then I guess you have to
have your own "off-by-one-xlate".

@Linus, any comments on this? All documentation states that we have
gpio1-gpioXX in these chips, so we have to expose it as such or things will be
messy.

> > [..]
> > > +
> > > +static const struct of_device_id pmic_gpio_of_match[] = {
> > > +       { .compatible = "qcom,spmi-pmic-gpio" },
> > 
> > I think this should be more specific, because hopefully the spmi specification
> > will outlive the current pmic gpio block.
> > 
> > So I think you need to list the pmic blocks here (e.g. "qcom,pm8941-gpio").
> 
> I can rename this to lovely "qcom,qpnp-gpio" :-), in this way driver
> can outlive bus on which device is connected this time.
> 

If the mythical qpnp spec actually include a specification for how a gpio block
looks like then sure. Otherwise you would have to list all the pmics that
contain this block.

Regards,
Bjorn

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v4 3/4] pinctrl: Qualcomm SPMI PMIC GPIO pin controller driver
  2014-09-30 17:02       ` Bjorn Andersson
@ 2014-10-07 14:14         ` Linus Walleij
  0 siblings, 0 replies; 11+ messages in thread
From: Linus Walleij @ 2014-10-07 14:14 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Ivan T. Ivanov, Grant Likely, Rob Herring, David Collins,
	Wu Fenglin, linux-kernel, devicetree, linux-arm-msm

On Tue, Sep 30, 2014 at 7:02 PM, Bjorn Andersson
<bjorn.andersson@sonymobile.com> wrote:
> On Wed 24 Sep 06:09 PDT 2014, Ivan T. Ivanov wrote:

>> I am unable to make this work. of_gpio_simple_xlate() didn't know that
>> GPIO range is offset with 1. Requesting last GPIO return error. And
>> debug output looks weird, for example:
>>
>> # cat /sys/kernel/debug/gpio
>> ...
>> GPIOs 220-255, platform/0.c000.gpios, 0.c000.gpios:
>> ...
>>
>> # cat /sys/kernel/debug/pinctrl/0.c000.gpios/gpio-ranges
>> GPIO ranges handled:
>> 1: 0.c000.gpios GPIOS [221 - 256] PINS [0 - 35]
>>
>> Advice, please.
>
> Looking at of_gpio_simple_xlate() shows that you're right. It doesn't seem to
> be possible to have a gpiochip that is not 0-based. Then I guess you have to
> have your own "off-by-one-xlate".
>
> @Linus, any comments on this? All documentation states that we have
> gpio1-gpioXX in these chips, so we have to expose it as such or things will be
> messy.

I remember that drivers/pinctrl/nomadik/pinctrl-abx500.c had the
same problem, but I don't remember how we solved it sadly :-/

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2014-10-07 14:14 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-09-15 14:44 [PATCH v4 0/4] Qualcomm SPMI PMIC pin controller drivers Ivan T. Ivanov
2014-09-15 14:44 ` [PATCH v4 1/4] pinctrl: Device tree bindings for Qualcomm PMIC GPIO block Ivan T. Ivanov
2014-09-15 14:44 ` [PATCH v4 2/4] pinctrl: Device tree bindings for Qualcomm PMIC MPP block Ivan T. Ivanov
2014-09-15 14:44 ` [PATCH v4 3/4] pinctrl: Qualcomm SPMI PMIC GPIO pin controller driver Ivan T. Ivanov
2014-09-24  4:18   ` Bjorn Andersson
2014-09-24  8:14     ` Ivan T. Ivanov
2014-09-24 13:09     ` Ivan T. Ivanov
2014-09-30 17:02       ` Bjorn Andersson
2014-10-07 14:14         ` Linus Walleij
2014-09-15 14:44 ` [PATCH v4 4/4] pinctrl: Qualcomm SPMI PMIC MPP " Ivan T. Ivanov
2014-09-23 15:16 ` [PATCH v4 0/4] Qualcomm SPMI PMIC pin controller drivers Linus Walleij

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).