All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/6] Qualcomm PMIC pin controller drivers
@ 2014-08-11 15:40 Ivan T. Ivanov
  2014-08-11 15:40 ` [PATCH v3 1/6] pinctrl: Device tree bindings for Qualcomm pm8xxx gpio block Ivan T. Ivanov
                   ` (5 more replies)
  0 siblings, 6 replies; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-11 15:40 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Russell King, Linus Walleij, Grant Likely
  Cc: Ivan T. Ivanov, Bjorn Andersson, linux-arm-msm, devicetree, linux-kernel

From: "Ivan T. Ivanov" <iivanov@mm-sol.com>

Hi, 

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

Changes:
- pm8xxx-gpio driver renamed to ssbi-pmic-gpio and qpnp-pinctrl
  to spmi-pmic-pinctrl
- Functions are described with strings. Available functions for
  each PMIC GPIO are described in DT include file.
- Available 'power-sources' are described per PMIC in DT include files
  and per chip definitions from pm8xxx-gpio driver are removed.
- Property 'qcom,pull-up' renamed 'qcom,pull-up-strength' possible
  are listed in DT include file.
- Property 'qcom,strength' renamed to 'qcom,drive-strength'
- Added parsing code for above 2 properties to ssbi-pmic-gpio driver
- Added AP8074 Dragonboard PMIC GPIO DT files.
- Added qcom,mode property for Multi-Purpose Pins
- 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 SSBI and 
SPMI based PMIC chips.

MPP's are enhanced GPIO's with analog circuits, which support 
following functions in addition to digital input/output: analog
input/output and current sinks. 

[1] http://thread.gmane.org/gmane.linux.kernel/1749903

Bjorn Andersson (2):
  pinctrl: Device tree bindings for Qualcomm pm8xxx gpio block
  pinctrl: Introduce pinctrl driver for Qualcomm SSBI PMIC's

Ivan T. Ivanov (4):
  pinctrl: Add documentation for SPMI PMIC pinctrl driver bindings
  pinctrl: Qualcomm SPMI PMIC pin controller driver
  ARM: dts: qcom: Add PM8941 and PM8841 pinctrl nodes
  ARM: dts: qcom: Add APQ8074 Dragonboard PMIC GPIO bindings

 .../devicetree/bindings/pinctrl/qcom,pmic-gpio.txt |  212 +++
 .../devicetree/bindings/pinctrl/qcom,pmic-mpp.txt  |  179 +++
 .../dts/qcom-apq8074-dragonboard-pmics-pins.dtsi   |  103 ++
 arch/arm/boot/dts/qcom-apq8074-dragonboard.dts     |    1 +
 arch/arm/boot/dts/qcom-msm8974.dtsi                |   33 +
 drivers/pinctrl/qcom/Kconfig                       |   23 +
 drivers/pinctrl/qcom/Makefile                      |    2 +
 drivers/pinctrl/qcom/pinctrl-spmi-pmic.c           | 1532 ++++++++++++++++++++
 drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c           |  870 +++++++++++
 include/dt-bindings/pinctrl/qcom,pmic-gpio.h       |  142 ++
 include/dt-bindings/pinctrl/qcom,pmic-mpp.h        |   70 +
 11 files changed, 3167 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 arch/arm/boot/dts/qcom-apq8074-dragonboard-pmics-pins.dtsi
 create mode 100644 drivers/pinctrl/qcom/pinctrl-spmi-pmic.c
 create mode 100644 drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
 create mode 100644 include/dt-bindings/pinctrl/qcom,pmic-gpio.h
 create mode 100644 include/dt-bindings/pinctrl/qcom,pmic-mpp.h

-- 
1.8.3.2

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

* [PATCH v3 1/6] pinctrl: Device tree bindings for Qualcomm pm8xxx gpio block
  2014-08-11 15:40 [PATCH v3 0/6] Qualcomm PMIC pin controller drivers Ivan T. Ivanov
@ 2014-08-11 15:40 ` Ivan T. Ivanov
  2014-08-16 15:24   ` Daniel
  2014-08-20 22:27   ` Bjorn Andersson
  2014-08-11 15:40 ` [PATCH v3 2/6] pinctrl: Introduce pinctrl driver for Qualcomm SSBI PMIC's Ivan T. Ivanov
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-11 15:40 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala
  Cc: Bjorn Andersson, linux-arm-msm, devicetree, linux-kernel, Ivan T. Ivanov

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

This introduced the device tree bindings for the gpio block found in
pm8018, pm8038, pm8058, pm8917 and pm8921 pmics 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 | 208 +++++++++++++++++++++
 include/dt-bindings/pinctrl/qcom,pmic-gpio.h       | 107 +++++++++++
 2 files changed, 315 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..7e57102
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
@@ -0,0 +1,208 @@
+Qualcomm PMIC GPIO block
+
+This binding describes the GPIO block(s) found in the 8xxx series of pmics 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"
+
+- reg:
+	Usage: required
+	Value type: <u32>
+	Definition: Register base of the gpio block
+
+- 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 abitrary 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
+
+- 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 configued as no pull.
+
+- bias-pull-down:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins should be configued 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>
+		    xxxx_GPIO_L6, xxxx_GPIO_L5...
+
+- 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.
+
+
+Example:
+
+	pm8921_gpio: gpio@150 {
+		compatible = "qcom,pm8921-gpio";
+		reg = <0x150>;
+		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..994e748
--- /dev/null
+++ b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
@@ -0,0 +1,107 @@
+/*
+ * 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		1
+#define PMIC_GPIO_PULL_UP_1P5		2
+#define PMIC_GPIO_PULL_UP_31P5		3
+#define PMIC_GPIO_PULL_UP_1P5_30	4
+
+#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
+
+/* 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
+
+#endif
-- 
1.8.3.2

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

* [PATCH v3 2/6] pinctrl: Introduce pinctrl driver for Qualcomm SSBI PMIC's
  2014-08-11 15:40 [PATCH v3 0/6] Qualcomm PMIC pin controller drivers Ivan T. Ivanov
  2014-08-11 15:40 ` [PATCH v3 1/6] pinctrl: Device tree bindings for Qualcomm pm8xxx gpio block Ivan T. Ivanov
@ 2014-08-11 15:40 ` Ivan T. Ivanov
       [not found]   ` <1407771634-14946-3-git-send-email-iivanov-NEYub+7Iv8PQT0dZR+AlfA@public.gmane.org>
  2014-08-11 15:40 ` [PATCH v3 3/6] pinctrl: Add documentation for SPMI PMIC pinctrl driver bindings Ivan T. Ivanov
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-11 15:40 UTC (permalink / raw)
  To: Linus Walleij, Grant Likely, Rob Herring
  Cc: Bjorn Andersson, linux-arm-msm, devicetree, linux-kernel, Ivan T. Ivanov

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

This introduces a pinctrl, pinconf, pinmux and gpio driver for the gpio
block found in pm8018, pm8038, pm8058, pm8917 and pm8921 pmics from
Qualcomm.

Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
---
 drivers/pinctrl/qcom/Kconfig             |  11 +
 drivers/pinctrl/qcom/Makefile            |   1 +
 drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c | 870 +++++++++++++++++++++++++++++++
 3 files changed, 882 insertions(+)
 create mode 100644 drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c

diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index d160a71..6bd4e4d 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -39,4 +39,15 @@ 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_SSBI_PMIC
+       tristate "Qualcomm SSBI PMIC pin controller driver"
+       depends on GPIOLIB && OF
+       select PINCONF
+       select PINMUX
+       select GENERIC_PINCONF
+       help
+         This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+         Qualcomm GPIO blocks found in the pm8018, pm8038, pm8058, pm8917 and
+         pm8921 pmics from Qualcomm.
+
 endif
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 2a02602..8faa2ca 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_SSBI_PMIC) += pinctrl-ssbi-pmic.o
diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
new file mode 100644
index 0000000..9a1b443
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
@@ -0,0 +1,870 @@
+/*
+ * Copyright (c) 2014, Sony Mobile Communications AB.
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pm8921-core.h>
+
+#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
+
+#include "../core.h"
+#include "../pinconf.h"
+#include "../pinctrl-utils.h"
+
+/* direction */
+#define PM8XXX_GPIO_DIR_OUT		BIT(0)
+#define PM8XXX_GPIO_DIR_IN		BIT(1)
+
+/* output buffer */
+#define PM8XXX_GPIO_PUSH_PULL		0
+#define PM8XXX_GPIO_OPEN_DRAIN		1
+
+/* bias */
+#define PM8XXX_GPIO_BIAS_PU_30		0
+#define PM8XXX_GPIO_BIAS_PU_1P5		1
+#define PM8XXX_GPIO_BIAS_PU_31P5	2
+#define PM8XXX_GPIO_BIAS_PU_1P5_30	3
+#define PM8XXX_GPIO_BIAS_PD		4
+#define PM8XXX_GPIO_BIAS_NP		5
+
+/* GPIO registers */
+#define SSBI_REG_ADDR_GPIO_BASE		0x150
+#define SSBI_REG_ADDR_GPIO(n)		(SSBI_REG_ADDR_GPIO_BASE + n)
+
+#define PM8XXX_GPIO_WRITE		BIT(7)
+
+#define PM8XXX_MAX_GPIOS		44
+
+/* Qualcomm specific pin configurations */
+#define PM8XXX_PINCONF_PULL_UP		(PIN_CONFIG_END + 1)
+#define PM8XXX_PINCONF_STRENGTH		(PIN_CONFIG_END + 2)
+
+struct pm8xxx_pinbindings {
+	const char *property;
+	unsigned param;
+	u32 default_value;
+};
+static struct pm8xxx_pinbindings pm8xxx_pinbindings[] = {
+	/* PMIC_GPIO_PULL_UP_30...  */
+	{"qcom,pull-up-strength",	PM8XXX_PINCONF_PULL_UP, 0},
+	/* PMIC_GPIO_STRENGTH_NO... */
+	{"qcom,drive-strength",		PM8XXX_PINCONF_STRENGTH, 0},
+};
+
+struct pm8xxx_gpio_pin {
+	int irq;
+
+	u8 power_source;
+	u8 direction;
+	u8 output_buffer;
+	u8 output_value;
+	u8 bias;
+	u8 output_strength;
+	u8 disable;
+	u8 function;
+	u8 non_inverted;
+};
+
+struct pm8xxx_gpio_data {
+	int ngpio;
+};
+
+struct pm8xxx_gpio {
+	struct device *dev;
+	struct regmap *regmap;
+	struct pinctrl_dev *pctrl;
+	struct gpio_chip chip;
+
+	const struct pm8xxx_gpio_data *data;
+
+	struct pm8xxx_gpio_pin pins[PM8XXX_MAX_GPIOS];
+};
+
+static inline struct pm8xxx_gpio *to_pm8xxx_gpio(struct gpio_chip *chip)
+{
+	return container_of(chip, struct pm8xxx_gpio, chip);
+};
+
+static const char * const pm8xxx_gpio_groups[PM8XXX_MAX_GPIOS] = {
+	"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",
+	"gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43",
+	"gpio44",
+};
+
+static const char * const pm8xxx_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 pm8xxx_gpio_read(struct pm8xxx_gpio *pctrl, int pin, int bank)
+{
+	int reg = SSBI_REG_ADDR_GPIO(pin);
+	unsigned int val = bank << 4;
+	int ret;
+
+	ret = regmap_write(pctrl->regmap, reg, val);
+	if (ret) {
+		dev_err(pctrl->dev,
+			"failed to select bank %d of pin %d\n", bank, pin);
+		return ret;
+	}
+
+	ret = regmap_read(pctrl->regmap, reg, &val);
+	if (ret) {
+		dev_err(pctrl->dev,
+			"failed to read register %d of pin %d\n", bank, pin);
+		return ret;
+	}
+
+	return val;
+}
+
+static int pm8xxx_gpio_write(struct pm8xxx_gpio *pctrl,
+			     int pin, int bank, u8 val)
+{
+	int ret;
+
+	val |= PM8XXX_GPIO_WRITE;
+	val |= bank << 4;
+
+	ret = regmap_write(pctrl->regmap, SSBI_REG_ADDR_GPIO(pin), val);
+	if (ret)
+		dev_err(pctrl->dev, "failed to write register\n");
+
+	return ret;
+}
+
+static int pm8xxx_gpio_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	return pctrl->data->ngpio;
+}
+
+static const char *pm8xxx_gpio_get_group_name(struct pinctrl_dev *pctldev,
+				      unsigned group)
+{
+	return pm8xxx_gpio_groups[group];
+}
+
+static int pm8xxx_parse_dt_config(struct device *dev, struct device_node *np,
+			unsigned long **configs, unsigned int *nconfigs)
+{
+	struct pm8xxx_pinbindings *par;
+	unsigned long cfg[ARRAY_SIZE(pm8xxx_pinbindings)];
+	unsigned int ncfg = 0;
+	int ret, idx;
+	u32 val;
+
+	if (!np)
+		return -EINVAL;
+
+	for (idx = 0; idx < ARRAY_SIZE(pm8xxx_pinbindings); idx++) {
+		par = &pm8xxx_pinbindings[idx];
+		ret = of_property_read_u32(np, par->property, &val);
+
+		/* property not found */
+		if (ret == -EINVAL)
+			continue;
+
+		/* use default value, when no value is specified */
+		if (ret)
+			val = par->default_value;
+
+		dev_dbg(dev, "found %s with value %u\n", par->property, val);
+		cfg[ncfg] = pinconf_to_config_packed(par->param, val);
+		ncfg++;
+	}
+
+	ret = 0;
+
+	/* no configs found at qchip->npads */
+	if (ncfg == 0) {
+		*configs = NULL;
+		*nconfigs = 0;
+		goto out;
+	}
+
+	/*
+	 * Now limit the number of configs to the real number of
+	 * found properties.
+	 */
+	*configs = kcalloc(ncfg, sizeof(unsigned long), GFP_KERNEL);
+	if (!*configs) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memcpy(*configs, cfg, ncfg * sizeof(unsigned long));
+	*nconfigs = ncfg;
+
+out:
+	return ret;
+}
+
+static int pm8xxx_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 num_configs = 0;
+	struct property *prop;
+	const char *group;
+	int ret;
+
+	ret = pm8xxx_parse_dt_config(pctldev->dev, np, &configs, &num_configs);
+	if (ret < 0)
+		return ret;
+
+	if (!num_configs)
+		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,
+				num_configs, type);
+		if (ret < 0)
+			break;
+	}
+exit:
+	kfree(configs);
+	return ret;
+}
+
+static int pm8xxx_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 = pm8xxx_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 pm8xxx_gpio_pinctrl_ops = {
+	.get_groups_count	= pm8xxx_gpio_get_groups_count,
+	.get_group_name		= pm8xxx_gpio_get_group_name,
+	.dt_node_to_map		= pm8xxx_dt_node_to_map,
+	.dt_free_map		= pinctrl_utils_dt_free_map,
+};
+
+static int pm8xxx_get_functions_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(pm8xxx_gpio_functions);
+}
+
+static const char *pm8xxx_get_function_name(struct pinctrl_dev *pctldev,
+					 unsigned function)
+{
+	return pm8xxx_gpio_functions[function];
+}
+
+static int pm8xxx_get_function_groups(struct pinctrl_dev *pctldev,
+				   unsigned function,
+				   const char * const **groups,
+				   unsigned * const num_groups)
+{
+	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	*groups = pm8xxx_gpio_groups;
+	*num_groups = pctrl->data->ngpio;
+	return 0;
+}
+
+static int pm8xxx_pinmux_enable(struct pinctrl_dev *pctldev,
+			     unsigned function,
+			     unsigned group)
+{
+	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+	struct pm8xxx_gpio_pin *pin = &pctrl->pins[group];
+	u8 val;
+
+	pin->function = function;
+	val = pin->function << 1;
+
+	pm8xxx_gpio_write(pctrl, group, 4, val);
+
+	return 0;
+}
+
+static const struct pinmux_ops pm8xxx_pinmux_ops = {
+	.get_functions_count	= pm8xxx_get_functions_count,
+	.get_function_name	= pm8xxx_get_function_name,
+	.get_function_groups	= pm8xxx_get_function_groups,
+	.enable			= pm8xxx_pinmux_enable,
+};
+
+static int pm8xxx_gpio_config_get(struct pinctrl_dev *pctldev,
+			  unsigned int offset,
+			  unsigned long *config)
+{
+	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
+	unsigned param = pinconf_to_config_param(*config);
+	unsigned arg;
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_DISABLE:
+		arg = pin->bias == PM8XXX_GPIO_BIAS_NP;
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		arg = pin->bias == PM8XXX_GPIO_BIAS_PD;
+		break;
+	case PM8XXX_PINCONF_PULL_UP:
+		if (pin->bias >= PM8XXX_GPIO_BIAS_PU_30 &&
+		    pin->bias <= PM8XXX_GPIO_BIAS_PU_1P5_30)
+			arg = PMIC_GPIO_PULL_UP_30 + pin->bias;
+		else
+			arg = 0;
+		break;
+	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+		arg = pin->disable;
+		break;
+	case PIN_CONFIG_INPUT_ENABLE:
+		arg = pin->direction == PM8XXX_GPIO_DIR_IN;
+		break;
+	case PIN_CONFIG_OUTPUT:
+		arg = pin->output_value;
+		break;
+	case PIN_CONFIG_POWER_SOURCE:
+		arg = pin->power_source;
+		break;
+	case PM8XXX_PINCONF_STRENGTH:
+		arg = pin->output_strength;
+		break;
+	case PIN_CONFIG_DRIVE_PUSH_PULL:
+		arg = pin->output_buffer == PM8XXX_GPIO_PUSH_PULL;
+		break;
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		arg = pin->output_buffer == PM8XXX_GPIO_OPEN_DRAIN;
+		break;
+	default:
+		dev_err(pctrl->dev,
+			"unsupported config parameter: %x\n",
+			param);
+		return -EINVAL;
+	}
+
+	*config = pinconf_to_config_packed(param, arg);
+
+	return 0;
+}
+
+static int pm8xxx_gpio_config_set(struct pinctrl_dev *pctldev,
+				  unsigned int offset,
+				  unsigned long *configs,
+				  unsigned num_configs)
+{
+	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
+	unsigned param;
+	unsigned arg;
+	unsigned i;
+	u8 banks = 0;
+	u8 val;
+
+	for (i = 0; i < num_configs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		switch (param) {
+		case PIN_CONFIG_BIAS_DISABLE:
+			pin->bias = PM8XXX_GPIO_BIAS_NP;
+			banks |= BIT(2);
+			pin->disable = 0;
+			banks |= BIT(3);
+			break;
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			pin->bias = PM8XXX_GPIO_BIAS_PD;
+			banks |= BIT(2);
+			pin->disable = 0;
+			banks |= BIT(3);
+			break;
+		case PM8XXX_PINCONF_PULL_UP:
+			if (arg < PMIC_GPIO_PULL_UP_30 ||
+			    arg > PMIC_GPIO_PULL_UP_1P5_30) {
+				dev_err(pctrl->dev, "invalid pull-up level\n");
+				return -EINVAL;
+			}
+			pin->bias = arg - PM8XXX_GPIO_BIAS_PU_30;
+			banks |= BIT(2);
+			pin->disable = 0;
+			banks |= BIT(3);
+			break;
+		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+			pin->disable = 1;
+			banks |= BIT(3);
+			break;
+		case PIN_CONFIG_INPUT_ENABLE:
+			pin->direction = PM8XXX_GPIO_DIR_IN;
+			banks |= BIT(1);
+			break;
+		case PIN_CONFIG_OUTPUT:
+			pin->direction = PM8XXX_GPIO_DIR_OUT;
+			pin->output_value = !!arg;
+			banks |= BIT(1);
+			break;
+		case PIN_CONFIG_POWER_SOURCE:
+			pin->power_source = arg;
+			banks |= BIT(0);
+			break;
+		case PM8XXX_PINCONF_STRENGTH:
+			if (arg > PMIC_GPIO_STRENGTH_LOW) {
+				dev_err(pctrl->dev, "invalid drive strength\n");
+				return -EINVAL;
+			}
+			pin->output_strength = arg;
+			banks |= BIT(3);
+			break;
+		case PIN_CONFIG_DRIVE_PUSH_PULL:
+			pin->output_buffer = PM8XXX_GPIO_PUSH_PULL;
+			banks |= BIT(1);
+			break;
+		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+			pin->output_buffer = PM8XXX_GPIO_OPEN_DRAIN;
+			banks |= BIT(1);
+			break;
+		default:
+			dev_err(pctrl->dev,
+					"unsupported config parameter: %x\n",
+					param);
+			return -EINVAL;
+		}
+	}
+
+	if (banks & BIT(0))
+		pm8xxx_gpio_write(pctrl, offset, 0, pin->power_source << 1);
+
+	if (banks & BIT(1)) {
+		val = pin->direction << 2;
+		val |= pin->output_buffer << 1;
+		val |= pin->output_value;
+		pm8xxx_gpio_write(pctrl, offset, 1, val);
+	}
+
+	if (banks & BIT(2)) {
+		val = pin->bias << 1;
+		pm8xxx_gpio_write(pctrl, offset, 2, val);
+	}
+
+	if (banks & BIT(3)) {
+		val = pin->output_strength << 2;
+		val |= pin->disable;
+		pm8xxx_gpio_write(pctrl, offset, 3, val);
+	}
+
+	if (banks & BIT(4)) {
+		val = pin->function << 1;
+		pm8xxx_gpio_write(pctrl, offset, 4, val);
+	}
+
+	if (banks & BIT(5)) {
+		val = 0;
+		if (pin->non_inverted)
+			val |= BIT(3);
+		pm8xxx_gpio_write(pctrl, offset, 5, val);
+	}
+
+	return 0;
+}
+
+static const struct pinconf_ops pm8xxx_gpio_pinconf_ops = {
+	.pin_config_group_get = pm8xxx_gpio_config_get,
+	.pin_config_group_set = pm8xxx_gpio_config_set,
+};
+
+static struct pinctrl_desc pm8xxx_gpio_desc = {
+	.pctlops = &pm8xxx_gpio_pinctrl_ops,
+	.pmxops = &pm8xxx_pinmux_ops,
+	.confops = &pm8xxx_gpio_pinconf_ops,
+	.owner = THIS_MODULE,
+};
+
+static int pm8xxx_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+	u8 val;
+
+	pin->direction = PM8XXX_GPIO_DIR_IN;
+	val = pin->direction << 2;
+
+	pm8xxx_gpio_write(pctrl, offset, 1, val);
+
+	return 0;
+}
+
+static int pm8xxx_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset,
+					int value)
+{
+	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+	u8 val;
+
+	pin->direction = PM8XXX_GPIO_DIR_OUT;
+	pin->output_value = !!value;
+
+	val = pin->direction << 2;
+	val |= pin->output_buffer << 1;
+	val |= pin->output_value;
+
+	pm8xxx_gpio_write(pctrl, offset, 1, val);
+
+	return 0;
+}
+
+static int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+
+	if (pin->direction == PM8XXX_GPIO_DIR_OUT)
+		return pin->output_value;
+
+	return pm8xxx_read_irq_status(pin->irq);
+}
+
+static void pm8xxx_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(gc);
+	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+	u8 val;
+
+	pin->output_value = !!value;
+
+	val = pin->direction << 2;
+	val |= pin->output_buffer << 1;
+	val |= pin->output_value;
+
+	pm8xxx_gpio_write(pctrl, offset, 1, val);
+}
+
+static int pm8xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+
+	return pin->irq;
+}
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/seq_file.h>
+
+static void pm8xxx_gpio_dbg_show_one(struct seq_file *s,
+				  struct pinctrl_dev *pctldev,
+				  struct gpio_chip *chip,
+				  unsigned offset,
+				  unsigned gpio)
+{
+	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
+
+	static const char * const directions[] = {
+		"off", "out", "in", "both"
+	};
+	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"
+	};
+	static const char * const strengths[] = {
+		"no", "high", "medium", "low"
+	};
+
+	seq_printf(s, " gpio%-2d:", offset + 1);
+	if (pin->disable) {
+		seq_puts(s, " ---");
+	} else {
+		seq_printf(s, " %-4s", directions[pin->direction]);
+		seq_printf(s, " %-7s", pm8xxx_gpio_functions[pin->function]);
+		seq_printf(s, " VIN%d", pin->power_source);
+		seq_printf(s, " %-27s", biases[pin->bias]);
+		seq_printf(s, " %-10s", buffer_types[pin->output_buffer]);
+		seq_printf(s, " %-4s", pin->output_value ? "high" : "low");
+		seq_printf(s, " %-7s", strengths[pin->output_strength]);
+		if (!pin->non_inverted)
+			seq_puts(s, " inverted");
+	}
+}
+
+static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+	unsigned gpio = chip->base;
+	unsigned i;
+
+	for (i = 0; i < chip->ngpio; i++, gpio++) {
+		pm8xxx_gpio_dbg_show_one(s, NULL, chip, i, gpio);
+		seq_puts(s, "\n");
+	}
+}
+
+#else
+#define msm_gpio_dbg_show NULL
+#endif
+
+static struct gpio_chip pm8xxx_gpio_template = {
+	.direction_input = pm8xxx_gpio_direction_input,
+	.direction_output = pm8xxx_gpio_direction_output,
+	.get = pm8xxx_gpio_get,
+	.set = pm8xxx_gpio_set,
+	.to_irq = pm8xxx_gpio_to_irq,
+	.dbg_show = pm8xxx_gpio_dbg_show,
+	.owner = THIS_MODULE,
+};
+
+static int pm8xxx_gpio_populate(struct pm8xxx_gpio *pctrl)
+{
+	struct pm8xxx_gpio_pin *pin;
+	int val;
+	int i;
+
+	for (i = 0; i < pctrl->data->ngpio; i++) {
+		pin = &pctrl->pins[i];
+
+		val = pm8xxx_gpio_read(pctrl, i, 0);
+		if (val < 0)
+			return val;
+
+		pin->power_source = (val >> 1) & 0x7;
+
+		val = pm8xxx_gpio_read(pctrl, i, 1);
+		if (val < 0)
+			return val;
+
+		pin->direction = (val >> 2) & 0x3;
+		pin->output_buffer = !!(val & BIT(1));
+		pin->output_value = val & BIT(0);
+
+		val = pm8xxx_gpio_read(pctrl, i, 2);
+		if (val < 0)
+			return val;
+
+		pin->bias = (val >> 1) & 0x7;
+
+		val = pm8xxx_gpio_read(pctrl, i, 3);
+		if (val < 0)
+			return val;
+
+		pin->output_strength = (val >> 2) & 0x3;
+		pin->disable = val & BIT(0);
+
+		val = pm8xxx_gpio_read(pctrl, i, 4);
+		if (val < 0)
+			return val;
+
+		pin->function = (val >> 1) & 0x7;
+
+		val = pm8xxx_gpio_read(pctrl, i, 5);
+		if (val < 0)
+			return val;
+
+		pin->non_inverted = !!(val & BIT(3));
+	}
+
+	return 0;
+}
+
+static const struct pm8xxx_gpio_data pm8018_gpio_data = {
+	.ngpio = 6,
+};
+
+static const struct pm8xxx_gpio_data pm8038_gpio_data = {
+	.ngpio = 12,
+};
+
+static const struct pm8xxx_gpio_data pm8058_gpio_data = {
+	.ngpio = 40,
+};
+static const struct pm8xxx_gpio_data pm8917_gpio_data = {
+	.ngpio = 38,
+};
+
+static const struct pm8xxx_gpio_data pm8921_gpio_data = {
+	.ngpio = 44,
+};
+
+static const struct of_device_id pm8xxx_gpio_of_match[] = {
+	{ .compatible = "qcom,pm8018-gpio", .data = &pm8018_gpio_data },
+	{ .compatible = "qcom,pm8038-gpio", .data = &pm8038_gpio_data },
+	{ .compatible = "qcom,pm8058-gpio", .data = &pm8058_gpio_data },
+	{ .compatible = "qcom,pm8917-gpio", .data = &pm8917_gpio_data },
+	{ .compatible = "qcom,pm8921-gpio", .data = &pm8921_gpio_data },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
+
+static int pm8xxx_gpio_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct pm8xxx_gpio *pctrl;
+	int ret;
+	int i;
+
+	match = of_match_node(pm8xxx_gpio_of_match, pdev->dev.of_node);
+	if (!match)
+		return -ENXIO;
+
+	pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
+	if (!pctrl)
+		return -ENOMEM;
+
+	pctrl->dev = &pdev->dev;
+	pctrl->data = match->data;
+
+	BUG_ON(pctrl->data->ngpio > PM8XXX_MAX_GPIOS);
+
+	pctrl->chip = pm8xxx_gpio_template;
+	pctrl->chip.base = -1;
+	pctrl->chip.dev = &pdev->dev;
+	pctrl->chip.of_node = pdev->dev.of_node;
+	pctrl->chip.label = dev_name(pctrl->dev);
+	pctrl->chip.ngpio = pctrl->data->ngpio;
+
+	pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!pctrl->regmap) {
+		dev_err(&pdev->dev, "parent regmap unavailable\n");
+		return -ENXIO;
+	}
+
+	for (i = 0; i < pctrl->data->ngpio; i++) {
+		ret = platform_get_irq(pdev, i);
+		if (ret < 0) {
+			dev_err(&pdev->dev,
+				"missing interrupts for pin %d\n", i);
+			return ret;
+		}
+
+		pctrl->pins[i].irq = ret;
+	}
+
+	ret = pm8xxx_gpio_populate(pctrl);
+	if (ret)
+		return ret;
+
+	pm8xxx_gpio_desc.name = dev_name(&pdev->dev);
+	pctrl->pctrl = pinctrl_register(&pm8xxx_gpio_desc, &pdev->dev, pctrl);
+	if (!pctrl->pctrl) {
+		dev_err(&pdev->dev, "couldn't register pm8xxx gpio driver\n");
+		return -ENODEV;
+	}
+
+	ret = gpiochip_add(&pctrl->chip);
+	if (ret) {
+		dev_err(&pdev->dev, "failed register gpiochip\n");
+		goto unregister_pinctrl;
+	}
+
+	ret = gpiochip_add_pin_range(&pctrl->chip,
+				     dev_name(pctrl->dev),
+				     1, 0, pctrl->data->ngpio);
+	if (ret) {
+		dev_err(pctrl->dev, "failed to add pin range\n");
+		goto unregister_gpiochip;
+	}
+
+	platform_set_drvdata(pdev, pctrl);
+
+	dev_dbg(&pdev->dev, "Qualcomm pm8xxx gpio driver probed\n");
+
+	return 0;
+
+unregister_pinctrl:
+	pinctrl_unregister(pctrl->pctrl);
+
+unregister_gpiochip:
+	if (gpiochip_remove(&pctrl->chip))
+		dev_err(&pdev->dev, "unable to unregister gpiochip\n");
+
+	return ret;
+}
+
+static int pm8xxx_gpio_remove(struct platform_device *pdev)
+{
+	struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev);
+
+	gpiochip_remove(&pctrl->chip);
+
+	pinctrl_unregister(pctrl->pctrl);
+
+	return 0;
+}
+
+static struct platform_driver pm8xxx_gpio_driver = {
+	.driver = {
+		.name = "ssbi-pmic-gpio",
+		.owner = THIS_MODULE,
+		.of_match_table = pm8xxx_gpio_of_match,
+	},
+	.probe = pm8xxx_gpio_probe,
+	.remove = pm8xxx_gpio_remove,
+};
+
+module_platform_driver(pm8xxx_gpio_driver);
+
+MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
+MODULE_DESCRIPTION("Qualcomm SSBI PMIC GPIO driver");
+MODULE_LICENSE("GPL v2");
-- 
1.8.3.2

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

* [PATCH v3 3/6] pinctrl: Add documentation for SPMI PMIC pinctrl driver bindings
  2014-08-11 15:40 [PATCH v3 0/6] Qualcomm PMIC pin controller drivers Ivan T. Ivanov
  2014-08-11 15:40 ` [PATCH v3 1/6] pinctrl: Device tree bindings for Qualcomm pm8xxx gpio block Ivan T. Ivanov
  2014-08-11 15:40 ` [PATCH v3 2/6] pinctrl: Introduce pinctrl driver for Qualcomm SSBI PMIC's Ivan T. Ivanov
@ 2014-08-11 15:40 ` Ivan T. Ivanov
  2014-08-13 14:32   ` Stephen Boyd
  2014-08-11 15:40 ` [PATCH v3 4/6] pinctrl: Qualcomm SPMI PMIC pin controller driver Ivan T. Ivanov
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-11 15:40 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala
  Cc: Ivan T. Ivanov, Bjorn Andersson, linux-arm-msm, devicetree, linux-kernel

From: "Ivan T. Ivanov" <iivanov@mm-sol.com>

DeviceTree binding documentation for Qualcomm SPMI PMIC GPIO and
MPP pinctrl drivers.

Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
---
 .../devicetree/bindings/pinctrl/qcom,pmic-gpio.txt |   4 +
 .../devicetree/bindings/pinctrl/qcom,pmic-mpp.txt  | 179 +++++++++++++++++++++
 include/dt-bindings/pinctrl/qcom,pmic-gpio.h       |  35 ++++
 include/dt-bindings/pinctrl/qcom,pmic-mpp.h        |  70 ++++++++
 4 files changed, 288 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-gpio.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
index 7e57102..c2d01b4 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
@@ -12,6 +12,8 @@ Qualcomm.
 		    "qcom,pm8058-gpio"
 		    "qcom,pm8917-gpio"
 		    "qcom,pm8921-gpio"
+		    "qcom,pm8941-gpio"
+		    "qcom,pma8084-gpio"
 
 - reg:
 	Usage: required
@@ -74,6 +76,8 @@ to specify in a pin configuration subnode:
 		    gpio1-gpio40 for pm8058
 		    gpio1-gpio38 for pm8917
 		    gpio1-gpio44 for pm8921
+		    gpio1-gpio36 for pm8941
+		    gpio1-gpio22 for pma8084
 
 - function:
 	Usage: required
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..0a64567
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
@@ -0,0 +1,179 @@
+Qualcomm PMIC Multi-Purpose Pin (MPP) block
+
+This binding describes the MPP block(s) found in the 8xxx series of pmics from
+Qualcomm.
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be one of:
+		    "qcom,pm8841-mpp"
+		    "qcom,pm8941-mpp"
+		    "qcom,pma8084-mpp"
+
+- reg:
+	Usage: required
+	Value type: <u32>
+	Definition: Register base of the gpio block
+
+- 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 abitrary 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: optional
+	Value type: <string>
+	Definition: Specify the alternative function to be configured for the
+		    specified pins.  Valid values are:
+		    "normal",
+		    "paired",
+		    "dtest1",
+		    "dtest2",
+		    "dtest3",
+		    "dtest4"
+
+- qcom,mode:
+	Usage: optional
+	Value type: <u32>
+	Definition: Selects MPP mode of operation: Digital Input, Digital Output,
+		    Digital Input and Digital Output, Bidirectional Logic,
+		    Analog Input, Analog Output, Current Sink Valid values are
+		    defined in <dt-bindings/pinctrl/qcom,pmic-mpp.h>
+		    PMIC_MPP_MODE_DI, PMIC_MPP_MODE_DO, PMIC_MPP_MODE_DIO...
+
+- bias-disable:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins should be configued as no pull.
+
+- bias-pull-up:
+	Usage: optional
+	Value type:  <u32>
+	Definition: The specified pins should be configued as pull up.
+		    Valid values are 600, 10000 and 30000.
+
+- 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 only
+		    when pin operate in Digital I/O mode ("gpio"). Valid
+		    power sources are defined in
+		    <dt-bindings/pinctrl/qcom,pmic-mpp.h>
+
+- drive-strength:
+	Usage: optional
+	Value type: <u32>
+	Definition: Selects the drive strength for the specified pins. Value
+		    drive strengths are: 5-40mA with 5mA step
+
+- 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...
+
+- qcom,vrefence:
+	Usage: optional
+	Value type: <u32>
+	Definition: Selects the analog output voltage reference. Valid values
+		    are defined in <dt-bindings/pinctrl/qcom,pmic-mpp.h>
+		    PMIC_MPP_VREFERENCE_1V25, PMIC_MPP_VREFERENCE_0V625...
+
+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>;
+				bias-pull-up = <600>;
+			};
+		};
+	};
diff --git a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
index 994e748..b208fdd 100644
--- a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
+++ b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
@@ -63,6 +63,24 @@
 #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"
@@ -104,4 +122,21 @@
 #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
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..1bda99d
--- /dev/null
+++ b/include/dt-bindings/pinctrl/qcom,pmic-mpp.h
@@ -0,0 +1,70 @@
+/*
+ * 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 Output - Set the analog output voltage reference.
+ * To be used with "qcom,vrefence = <>"
+ */
+#define PMIC_MPP_VREFERENCE_1V25	0
+#define PMIC_MPP_VREFERENCE_0V625	1
+#define PMIC_MPP_VREFERENCE_0V3125	2
+#define PMIC_MPP_VREFERENCE_PAIRED_MPP	3
+#define PMIC_MPP_VREFERENCE_ABUS1	4
+#define PMIC_MPP_VREFERENCE_ABUS2	5
+#define PMIC_MPP_VREFERENCE_ABUS3	6
+#define PMIC_MPP_VREFERENCE_ABUS4	7
+
+/*
+ * Analog Input - Set the source for analog input.
+ * To be used with "qcom,amux-route = <>"
+ */
+#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
+
+/*
+ * Mode select - indicates whether the pin should be digital input, output, both
+ * or analog input, output or current sink. To be used with "qcom,mode = <>"
+ */
+#define PMIC_MPP_MODE_DI		0
+#define PMIC_MPP_MODE_DO		1
+#define PMIC_MPP_MODE_DIO		2
+#define PMIC_MPP_MODE_AIO		3
+#define PMIC_MPP_MODE_AI		4
+#define PMIC_MPP_MODE_AO		5
+#define PMIC_MPP_MODE_CS		6
+
+/* 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.8.3.2

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

* [PATCH v3 4/6] pinctrl: Qualcomm SPMI PMIC pin controller driver
  2014-08-11 15:40 [PATCH v3 0/6] Qualcomm PMIC pin controller drivers Ivan T. Ivanov
                   ` (2 preceding siblings ...)
  2014-08-11 15:40 ` [PATCH v3 3/6] pinctrl: Add documentation for SPMI PMIC pinctrl driver bindings Ivan T. Ivanov
@ 2014-08-11 15:40 ` Ivan T. Ivanov
  2014-08-21  6:16   ` Bjorn Andersson
  2014-08-11 15:40 ` [PATCH v3 5/6] ARM: dts: qcom: Add PM8941 and PM8841 pinctrl nodes Ivan T. Ivanov
  2014-08-11 15:40 ` [PATCH v3 6/6] ARM: dts: qcom: Add APQ8074 Dragonboard PMIC GPIO bindings Ivan T. Ivanov
  5 siblings, 1 reply; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-11 15:40 UTC (permalink / raw)
  To: Linus Walleij, Grant Likely, Rob Herring
  Cc: Ivan T. Ivanov, Bjorn Andersson, linux-arm-msm, devicetree, linux-kernel

From: "Ivan T. Ivanov" <iivanov@mm-sol.com>

This is the pinctrl, pinmux, pinconf and gpiolib driver for the
Qualcomm GPIO and MPP 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.c | 1532 ++++++++++++++++++++++++++++++
 3 files changed, 1545 insertions(+)
 create mode 100644 drivers/pinctrl/qcom/pinctrl-spmi-pmic.c

diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index 6bd4e4d..8a24fae 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -39,6 +39,18 @@ 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.
+
 config PINCTRL_SSBI_PMIC
        tristate "Qualcomm SSBI PMIC pin controller driver"
        depends on GPIOLIB && OF
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 8faa2ca..d098058 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -4,4 +4,5 @@ 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.o
 obj-$(CONFIG_PINCTRL_SSBI_PMIC) += pinctrl-ssbi-pmic.o
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-pmic.c b/drivers/pinctrl/qcom/pinctrl-spmi-pmic.c
new file mode 100644
index 0000000..ba19979
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-pmic.c
@@ -0,0 +1,1532 @@
+/* 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/export.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.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 <dt-bindings/pinctrl/qcom,pmic-gpio.h>
+
+#include "../core.h"
+#include "../pinctrl-utils.h"
+
+/*
+ * Mode select - indicates whether the pin should be input, output, or both
+ * for GPIOs. MPP pins also support bidirectional, analog input, analog output
+ * and current sink.
+ */
+#define QPNP_PIN_MODE_DIG_IN			0
+#define QPNP_PIN_MODE_DIG_OUT			1
+#define QPNP_PIN_MODE_DIG_IN_OUT		2
+#define QPNP_PIN_MODE_BIDIR			3
+#define QPNP_PIN_MODE_AIN			4
+#define QPNP_PIN_MODE_AOUT			5
+#define QPNP_PIN_MODE_SINK			6
+
+/*
+ * Voltage select (GPIO, MPP) - specifies the voltage level when the output
+ * is set to 1. For an input GPIO specifies the voltage level at which
+ * the input is interpreted as a logical 1
+ * To be used with "power-func = <>"
+ */
+#define QPNP_PIN_VIN_4CH_INVALID		5
+#define QPNP_PIN_VIN_8CH_INVALID		8
+
+/*
+ * Analog Output - Set the analog output reference.
+ * See PM8XXX_MPP_AOUT_XXX. To be used with "qcom,aout = <>"
+ */
+#define QPNP_MPP_AOUT_INVALID			8
+
+/*
+ * Analog Input - Set the func for analog input.
+ * See PM8XXX_MPP_AIN_XXX. To be used with "qcom,ain = <>"
+ */
+#define QPNP_MPP_AIN_INVALID			8
+
+/*
+ * Output type - indicates pin should be configured as CMOS or
+ * open drain.
+ */
+#define QPNP_GPIO_OUT_BUF_CMOS			0
+#define QPNP_GPIO_OUT_BUF_OPEN_DRAIN_NMOS	1
+#define QPNP_GPIO_OUT_BUF_OPEN_DRAIN_PMOS	2
+
+/*
+ * Pull Up Values - it indicates whether a pull up or pull down
+ * should be applied. If a pull-up is required the current strength needs
+ * to be specified. Current values of 30uA, 1.5uA, 31.5uA, 1.5uA with 30uA
+ * boost are supported.
+ * Note that the hardware ignores this configuration if the GPIO is not set
+ * to input or output open-drain mode.
+ */
+#define QPNP_GPIO_PULL_UP_30			0
+#define QPNP_GPIO_PULL_UP_1P5			1
+#define QPNP_GPIO_PULL_UP_31P5			2
+#define QPNP_GPIO_PULL_UP_1P5_30		3
+#define QPNP_GPIO_PULL_DN			4
+#define QPNP_GPIO_PULL_NO			5
+
+/*
+ * 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 QPNP_MPP_PULL_UP_0P6KOHM		0
+#define QPNP_MPP_PULL_UP_10KOHM			1
+#define QPNP_MPP_PULL_UP_30KOHM			2
+#define QPNP_MPP_PULL_UP_OPEN			3
+
+/* Out Strength (GPIO) - the amount of current supplied for an output GPIO */
+#define QPNP_GPIO_STRENGTH_LOW			1
+#define QPNP_GPIO_STRENGTH_MED			2
+#define QPNP_GPIO_STRENGTH_HIGH			3
+
+/*
+ * Master enable (GPIO, MPP) - Enable features within the pin block based on
+ * configurations. QPNP_PIN_MASTER_DISABLE = Completely disable the pin
+ * lock and let the pin float with high impedance regardless of other settings.
+ */
+#define QPNP_PIN_MASTER_DISABLE                 0
+#define QPNP_PIN_MASTER_ENABLE			1
+
+/* revision registers base address offsets */
+#define QPNP_REG_DIG_MINOR_REV			0x0
+#define QPNP_REG_DIG_MAJOR_REV			0x1
+#define QPNP_REG_ANA_MINOR_REV			0x2
+
+/* type registers base address offsets */
+#define QPNP_REG_TYPE				0x4
+#define QPNP_REG_SUBTYPE			0x5
+
+/* GPIO peripheral type and subtype values */
+#define QPNP_GPIO_TYPE				0x10
+#define QPNP_GPIO_SUBTYPE_GPIO_4CH		0x1
+#define QPNP_GPIO_SUBTYPE_GPIOC_4CH		0x5
+#define QPNP_GPIO_SUBTYPE_GPIO_8CH		0x9
+#define QPNP_GPIO_SUBTYPE_GPIOC_8CH		0xd
+
+/* mpp peripheral type and subtype values */
+#define QPNP_MPP_TYPE				0x11
+#define QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT		0x3
+#define QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT	0x4
+#define QPNP_MPP_SUBTYPE_4CH_NO_SINK		0x5
+#define QPNP_MPP_SUBTYPE_ULT_4CH_NO_SINK	0x6
+#define QPNP_MPP_SUBTYPE_4CH_FULL_FUNC		0x7
+#define QPNP_MPP_SUBTYPE_8CH_FULL_FUNC		0xf
+
+#define QPNP_REG_STATUS1			0x8
+#define QPNP_REG_STATUS1_VAL_MASK		0x1
+#define QPNP_REG_STATUS1_GPIO_EN_REV0_MASK	0x2
+#define QPNP_REG_STATUS1_GPIO_EN_MASK		0x80
+#define QPNP_REG_STATUS1_MPP_EN_MASK		0x80
+
+/* control register base address offsets */
+#define QPNP_REG_MODE_CTL			0x40
+#define QPNP_REG_DIG_VIN_CTL			0x41
+#define QPNP_REG_DIG_PULL_CTL			0x42
+#define QPNP_REG_DIG_IN_CTL			0x43
+#define QPNP_REG_DIG_OUT_CTL			0x45
+#define QPNP_REG_EN_CTL				0x46
+#define QPNP_REG_AOUT_CTL			0x4b
+#define QPNP_REG_AIN_CTL			0x4a
+#define QPNP_REG_SINK_CTL			0x4c
+
+/* QPNP_REG_MODE_CTL */
+#define QPNP_REG_MODE_INVERT_SHIFT		0
+#define QPNP_REG_MODE_INVERT_MASK		0x1
+#define QPNP_REG_MODE_FUNCTION_SHIFT		1
+#define QPNP_REG_MODE_FUNCTION_MASK		0x6
+#define QPNP_REG_MODE_SEL_SHIFT			4
+#define QPNP_REG_MODE_SEL_MASK			0x70
+
+/* QPNP_REG_DIG_VIN_CTL */
+#define QPNP_REG_VIN_SHIFT			0
+#define QPNP_REG_VIN_MASK			0x7
+
+/* QPNP_REG_DIG_PULL_CTL */
+#define QPNP_REG_PULL_SHIFT			0
+#define QPNP_REG_PULL_MASK			0x7
+
+/* QPNP_REG_DIG_OUT_CTL */
+#define QPNP_REG_OUT_STRENGTH_SHIFT		0
+#define QPNP_REG_OUT_STRENGTH_MASK		0x3
+#define QPNP_REG_OUT_TYPE_SHIFT			4
+#define QPNP_REG_OUT_TYPE_MASK			0x30
+
+/* QPNP_REG_EN_CTL */
+#define QPNP_REG_MASTER_EN_SHIFT		7
+#define QPNP_REG_MASTER_EN_MASK			0x80
+
+/* QPNP_REG_AOUT_CTL */
+#define QPNP_REG_AOUT_REF_SHIFT			0
+#define QPNP_REG_AOUT_REF_MASK			0x7
+
+/* QPNP_REG_AIN_CTL */
+#define QPNP_REG_AIN_ROUTE_SHIFT		0
+#define QPNP_REG_AIN_ROUTE_MASK			0x7
+
+/* QPNP_REG_SINK_CTL */
+#define QPNP_REG_CS_OUT_SHIFT			0
+#define QPNP_REG_CS_OUT_MASK			0x7
+
+#define QPNP_PIN_PHYSICAL_OFFSET		1
+
+/* Qualcomm specific pin configurations */
+#define QPNP_PINCONF_PULL_UP			(PIN_CONFIG_END + 1)
+#define QPNP_PINCONF_STRENGTH			(PIN_CONFIG_END + 2)
+#define QPNP_PINCONF_AMUX_ROUTE			(PIN_CONFIG_END + 3)
+#define QPNP_PINCONF_VREFENCE			(PIN_CONFIG_END + 4)
+#define QPNP_PINCONF_MODE			(PIN_CONFIG_END + 5)
+
+struct qpnp_chipinfo {
+	unsigned npins;
+	unsigned base;
+	unsigned type;
+};
+
+struct qpnp_padinfo {
+	u16 offset;		/* address offset in SPMI device */
+	int irq;
+	const char *name;	/* pin name */
+	unsigned int modes;	/* supported modes: DI, DO, DIO, AI, AO... */
+	unsigned int type;	/* peripheral hardware type */
+	unsigned int subtype;	/* peripheral hardware subtype */
+	unsigned int major;	/* digital major version */
+};
+
+#define QPNP_REG_ADDR(pad, reg) ((pad)->offset + reg)
+#define QPNP_GET(buff, shift, mask) ((buff & mask) >> shift)
+
+struct qpnp_pinctrl {
+	struct device *dev;
+	struct regmap *map;
+	struct pinctrl_dev *ctrl;
+	struct pinctrl_desc desc;
+	struct gpio_chip chip;
+
+	struct qpnp_padinfo *pads;
+	const struct qpnp_chipinfo *info;
+	const char *const *groups;
+	const char *const *functions;
+};
+
+static inline struct qpnp_pinctrl *to_qpnp_pinctrl(struct gpio_chip *chip)
+{
+	return container_of(chip, struct qpnp_pinctrl, chip);
+};
+
+struct qpnp_pinbindings {
+	const char *property;
+	unsigned param;
+	u32 default_value;
+};
+
+struct qpnp_pinattrib {
+	unsigned addr;
+	unsigned shift;
+	unsigned mask;
+	unsigned val;
+};
+
+static struct qpnp_pinbindings qpnp_pinbindings[] = {
+	/* QCOM_BIAS_PULL_UP_30...  */
+	{"qcom,pull-up-strength", QPNP_PINCONF_PULL_UP, 0},
+	/* QCOM_DRIVE_STRENGTH_NO... */
+	{"qcom,drive-strength",	QPNP_PINCONF_STRENGTH, 0},
+	/* PMIC_MPP_AMUX_ROUTE_CH5 ... */
+	{"qcom,amux-route",	QPNP_PINCONF_AMUX_ROUTE, 0},
+	/* PMIC_MPP_VREFERENCE_1V25 ... */
+	{"qcom,vrefence",	QPNP_PINCONF_VREFENCE, 0},
+	/* PMIC_MPP_MODE.. */
+	{"qcom,mode",	        QPNP_PINCONF_MODE, 0},
+};
+
+static const char * const qpnp_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 qpnp_mpp_groups[] = {
+	"mpp1", "mpp2", "mpp3", "mpp4", "mpp5", "mpp6", "mpp7", "mpp8",
+};
+
+static const char * const qpnp_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 const char * const qpnp_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 inline struct qpnp_padinfo *qpnp_get_desc(struct qpnp_pinctrl *qctrl,
+						 unsigned pin)
+{
+	if (pin >= qctrl->desc.npins) {
+		dev_warn(qctrl->dev, "invalid pin number %d", pin);
+		return NULL;
+	}
+
+	return &qctrl->pads[pin];
+}
+
+static int qpnp_conv_to_pin(struct qpnp_pinctrl *qctrl,
+			   struct qpnp_padinfo *pad, unsigned param,
+			   unsigned val)
+{
+	struct qpnp_pinattrib attr[3];
+	unsigned int type, subtype;
+	int nattrs = 1, idx, ret;
+
+	type = pad->type;
+	subtype = pad->subtype;
+
+	switch (param) {
+	case PIN_CONFIG_DRIVE_PUSH_PULL:
+		if (type != QPNP_GPIO_TYPE)
+			return -ENXIO;
+		attr[0].addr  = QPNP_REG_DIG_OUT_CTL;
+		attr[0].shift = QPNP_REG_OUT_TYPE_SHIFT;
+		attr[0].mask  = QPNP_REG_OUT_TYPE_MASK;
+		attr[0].val   = QPNP_GPIO_OUT_BUF_CMOS;
+		break;
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		if (type != QPNP_GPIO_TYPE)
+			return -ENXIO;
+		if (subtype == QPNP_GPIO_SUBTYPE_GPIOC_4CH ||
+		    subtype == QPNP_GPIO_SUBTYPE_GPIOC_8CH)
+			return -EINVAL;
+		attr[0].addr  = QPNP_REG_DIG_OUT_CTL;
+		attr[0].shift = QPNP_REG_OUT_TYPE_SHIFT;
+		attr[0].mask  = QPNP_REG_OUT_TYPE_MASK;
+		attr[0].val   = QPNP_GPIO_OUT_BUF_OPEN_DRAIN_NMOS;
+		break;
+	case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+		if (type != QPNP_GPIO_TYPE)
+			return -ENXIO;
+		if (subtype == QPNP_GPIO_SUBTYPE_GPIOC_4CH ||
+		    subtype == QPNP_GPIO_SUBTYPE_GPIOC_8CH)
+			return -EINVAL;
+		attr[0].addr  = QPNP_REG_DIG_OUT_CTL;
+		attr[0].shift = QPNP_REG_OUT_TYPE_SHIFT;
+		attr[0].mask  = QPNP_REG_OUT_TYPE_MASK;
+		attr[0].val   = QPNP_GPIO_OUT_BUF_OPEN_DRAIN_PMOS;
+		break;
+	case PIN_CONFIG_BIAS_DISABLE:
+		attr[0].addr  = QPNP_REG_DIG_PULL_CTL;
+		attr[0].shift = QPNP_REG_PULL_SHIFT;
+		attr[0].mask  = QPNP_REG_PULL_MASK;
+		if (type == QPNP_GPIO_TYPE)
+			attr[0].val = QPNP_GPIO_PULL_NO;
+		else
+			attr[0].val = QPNP_MPP_PULL_UP_OPEN;
+		break;
+	case PIN_CONFIG_BIAS_PULL_UP:
+		if (type != QPNP_MPP_TYPE)
+			return -EINVAL;
+		switch (val) {
+		case 0:
+			val = QPNP_MPP_PULL_UP_OPEN;
+			break;
+		case 600:
+			val = QPNP_MPP_PULL_UP_0P6KOHM;
+			break;
+		case 10000:
+			val = QPNP_MPP_PULL_UP_10KOHM;
+			break;
+		case 30000:
+			val = QPNP_MPP_PULL_UP_30KOHM;
+			break;
+		default:
+			return -EINVAL;
+		}
+		attr[0].addr  = QPNP_REG_DIG_PULL_CTL;
+		attr[0].shift = QPNP_REG_PULL_SHIFT;
+		attr[0].mask  = QPNP_REG_PULL_MASK;
+		attr[0].val   = val;
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		if (type != QPNP_GPIO_TYPE)
+			return -EINVAL;
+		attr[0].addr  = QPNP_REG_DIG_PULL_CTL;
+		attr[0].shift = QPNP_REG_PULL_SHIFT;
+		attr[0].mask  = QPNP_REG_PULL_MASK;
+		if (val)
+			attr[0].val = QPNP_GPIO_PULL_DN;
+		else
+			attr[0].val = QPNP_GPIO_PULL_NO;
+		break;
+	case PIN_CONFIG_POWER_SOURCE:
+		if (val >= QPNP_PIN_VIN_8CH_INVALID)
+			return -EINVAL;
+		if (val >= QPNP_PIN_VIN_4CH_INVALID) {
+			if (type == QPNP_GPIO_TYPE &&
+			   (subtype == QPNP_GPIO_SUBTYPE_GPIO_4CH ||
+			    subtype == QPNP_GPIO_SUBTYPE_GPIOC_4CH))
+				return -EINVAL;
+			if (type == QPNP_MPP_TYPE &&
+			   (subtype == QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
+			    subtype == QPNP_MPP_SUBTYPE_4CH_NO_SINK ||
+			    subtype == QPNP_MPP_SUBTYPE_4CH_FULL_FUNC ||
+			    subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT ||
+			    subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_SINK))
+				return -EINVAL;
+		}
+		attr[0].addr  = QPNP_REG_DIG_VIN_CTL;
+		attr[0].shift = QPNP_REG_VIN_SHIFT;
+		attr[0].mask  = QPNP_REG_VIN_MASK;
+		attr[0].val   = val;
+		break;
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		if (type != QPNP_MPP_TYPE)
+			return -EINVAL;
+		if (subtype == QPNP_MPP_SUBTYPE_4CH_NO_SINK ||
+		    subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_SINK)
+			return -ENXIO;
+		if (val > 50)	/* mA */
+			return -EINVAL;
+		attr[0].addr  = QPNP_REG_SINK_CTL;
+		attr[0].shift = QPNP_REG_CS_OUT_SHIFT;
+		attr[0].mask  = QPNP_REG_CS_OUT_MASK;
+		attr[0].val   = (val / 5) - 1;
+		break;
+	case PIN_CONFIG_INPUT_ENABLE:
+		nattrs = 2;
+		attr[0].addr  = QPNP_REG_MODE_CTL;
+		attr[0].shift = QPNP_REG_MODE_SEL_SHIFT;
+		attr[0].mask  = QPNP_REG_MODE_SEL_MASK;
+		attr[0].val   = QPNP_PIN_MODE_DIG_IN;
+		attr[1].addr  = QPNP_REG_EN_CTL;
+		attr[1].shift = QPNP_REG_MASTER_EN_SHIFT;
+		attr[1].mask  = QPNP_REG_MASTER_EN_MASK;
+		attr[1].val   = 1;
+		if (val)
+			break;
+	/* Fallthrough */
+	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+		attr[1].addr  = QPNP_REG_EN_CTL;
+		attr[1].shift = QPNP_REG_MASTER_EN_SHIFT;
+		attr[1].mask  = QPNP_REG_MASTER_EN_MASK;
+		attr[1].val   = 0;
+		break;
+	case PIN_CONFIG_OUTPUT:
+		nattrs = 3;
+		attr[0].addr  = QPNP_REG_MODE_CTL;
+		attr[0].shift = QPNP_REG_MODE_INVERT_SHIFT;
+		attr[0].mask  = QPNP_REG_MODE_INVERT_MASK;
+		attr[0].val   = !!val;
+		attr[1].addr  = QPNP_REG_MODE_CTL;
+		attr[1].shift = QPNP_REG_MODE_SEL_SHIFT;
+		attr[1].mask  = QPNP_REG_MODE_SEL_MASK;
+		attr[1].val   = QPNP_PIN_MODE_DIG_OUT;
+		attr[2].addr  = QPNP_REG_EN_CTL;
+		attr[2].shift = QPNP_REG_MASTER_EN_SHIFT;
+		attr[2].mask  = QPNP_REG_MASTER_EN_MASK;
+		attr[2].val   = 1;
+		break;
+	case QPNP_PINCONF_PULL_UP:
+		if (type != QPNP_GPIO_TYPE)
+			return -EINVAL;
+		switch (val) {
+		default:
+			return -EINVAL;
+		case 0:
+			val = QPNP_GPIO_PULL_NO;
+			break;
+		case PMIC_GPIO_PULL_UP_30:
+			val = QPNP_GPIO_PULL_UP_30;
+			break;
+		case PMIC_GPIO_PULL_UP_1P5:
+			val = QPNP_GPIO_PULL_UP_1P5;
+			break;
+		case PMIC_GPIO_PULL_UP_31P5:
+			val = QPNP_GPIO_PULL_UP_31P5;
+			break;
+		case PMIC_GPIO_PULL_UP_1P5_30:
+			val = QPNP_GPIO_PULL_UP_1P5_30;
+			break;
+		}
+		attr[0].addr  = QPNP_REG_DIG_PULL_CTL;
+		attr[0].shift = QPNP_REG_PULL_SHIFT;
+		attr[0].mask  = QPNP_REG_PULL_MASK;
+		attr[0].val   = val;
+		break;
+	case QPNP_PINCONF_STRENGTH:
+		if (type != QPNP_GPIO_TYPE)
+			return -EINVAL;
+		switch (val) {
+		default:
+		case PMIC_GPIO_STRENGTH_NO:
+			return -EINVAL;
+		case PMIC_GPIO_STRENGTH_LOW:
+			attr[0].val = QPNP_GPIO_STRENGTH_LOW;
+			break;
+		case PMIC_GPIO_STRENGTH_MED:
+			attr[0].val = QPNP_GPIO_STRENGTH_MED;
+			break;
+		case PMIC_GPIO_STRENGTH_HIGH:
+			attr[0].val = QPNP_GPIO_STRENGTH_HIGH;
+			break;
+		}
+		attr[0].addr  = QPNP_REG_DIG_OUT_CTL;
+		attr[0].shift = QPNP_REG_OUT_STRENGTH_SHIFT;
+		attr[0].mask  = QPNP_REG_OUT_STRENGTH_MASK;
+		break;
+	case QPNP_PINCONF_VREFENCE:
+		if (type != QPNP_MPP_TYPE)
+			return -ENXIO;
+		if (val >= QPNP_MPP_AOUT_INVALID)
+			return -EINVAL;
+		if (subtype == QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
+		    subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT)
+			return -ENXIO;
+		attr[0].addr  = QPNP_REG_AOUT_CTL;
+		attr[0].shift = QPNP_REG_AOUT_REF_SHIFT;
+		attr[0].mask  = QPNP_REG_AOUT_REF_MASK;
+		attr[0].val   = val;
+		break;
+	case QPNP_PINCONF_AMUX_ROUTE:
+		if (type != QPNP_MPP_TYPE)
+			return -ENXIO;
+		if (val >= QPNP_MPP_AIN_INVALID)
+			return -EINVAL;
+		attr[0].addr  = QPNP_REG_AIN_CTL;
+		attr[0].shift = QPNP_REG_AIN_ROUTE_SHIFT;
+		attr[0].mask  = QPNP_REG_AIN_ROUTE_MASK;
+		attr[0].val   = val;
+		break;
+	case QPNP_PINCONF_MODE:
+		if ((pad->modes & BIT(val)) == 0)
+			return -ENXIO;
+		attr[0].addr  = QPNP_REG_MODE_CTL;
+		attr[0].shift = QPNP_REG_MODE_SEL_SHIFT;
+		attr[0].mask  = QPNP_REG_MODE_SEL_MASK;
+		attr[0].val   = val;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	for (idx = 0; idx < nattrs; idx++) {
+		/* add base offset */
+		attr[idx].addr = QPNP_REG_ADDR(pad, attr[idx].addr);
+		ret = regmap_update_bits(qctrl->map, attr[idx].addr,
+					 attr[idx].mask,
+					 attr[idx].val << attr[idx].shift);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+
+static int qpnp_conv_from_pin(struct qpnp_pinctrl *qctrl,
+			     struct qpnp_padinfo *pad,
+			     unsigned param, unsigned *val)
+{
+	struct qpnp_pinattrib attr;
+	unsigned int type, subtype, field;
+	unsigned int addr, buff;
+	int ret;
+
+	*val = 0;
+	type = pad->type;
+	subtype = pad->subtype;
+
+	switch (param) {
+	case PIN_CONFIG_DRIVE_PUSH_PULL:
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+	case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+		if (type != QPNP_GPIO_TYPE)
+			return -ENXIO;
+		attr.addr  = QPNP_REG_DIG_OUT_CTL;
+		attr.shift = QPNP_REG_OUT_TYPE_SHIFT;
+		attr.mask  = QPNP_REG_OUT_TYPE_MASK;
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		if (type != QPNP_GPIO_TYPE)
+			return -ENXIO;
+	/* Fallthrough */
+	case PIN_CONFIG_BIAS_DISABLE:
+	case PIN_CONFIG_BIAS_PULL_UP:
+		attr.addr  = QPNP_REG_DIG_PULL_CTL;
+		attr.shift = QPNP_REG_PULL_SHIFT;
+		attr.mask  = QPNP_REG_PULL_MASK;
+		break;
+	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+		attr.addr  = QPNP_REG_EN_CTL;
+		attr.shift = QPNP_REG_MASTER_EN_SHIFT;
+		attr.mask  = QPNP_REG_MASTER_EN_MASK;
+		break;
+	case PIN_CONFIG_POWER_SOURCE:
+		attr.addr  = QPNP_REG_DIG_VIN_CTL;
+		attr.shift = QPNP_REG_VIN_SHIFT;
+		attr.mask  = QPNP_REG_VIN_MASK;
+		break;
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		if (type != QPNP_MPP_TYPE)
+			return -ENXIO;
+		attr.addr  = QPNP_REG_SINK_CTL;
+		attr.shift = QPNP_REG_CS_OUT_SHIFT;
+		attr.mask  = QPNP_REG_CS_OUT_MASK;
+		break;
+	case PIN_CONFIG_INPUT_ENABLE:
+		attr.addr  = QPNP_REG_EN_CTL;
+		attr.shift = QPNP_REG_MASTER_EN_SHIFT;
+		attr.mask  = QPNP_REG_MASTER_EN_MASK;
+		break;
+	case PIN_CONFIG_OUTPUT:
+		attr.addr  = QPNP_REG_MODE_CTL;
+		attr.shift = QPNP_REG_MODE_INVERT_SHIFT;
+		attr.mask  = QPNP_REG_MODE_INVERT_MASK;
+		break;
+	case QPNP_PINCONF_PULL_UP:
+		if (type != QPNP_GPIO_TYPE)
+			return -ENXIO;
+		attr.addr  = QPNP_REG_DIG_PULL_CTL;
+		attr.shift = QPNP_REG_PULL_SHIFT;
+		attr.mask  = QPNP_REG_PULL_MASK;
+		break;
+	case QPNP_PINCONF_STRENGTH:
+		if (type != QPNP_GPIO_TYPE)
+			return -ENXIO;
+		attr.addr  = QPNP_REG_DIG_OUT_CTL;
+		attr.shift = QPNP_REG_OUT_STRENGTH_SHIFT;
+		attr.mask  = QPNP_REG_OUT_STRENGTH_MASK;
+		break;
+	case QPNP_PINCONF_VREFENCE:
+		if (type != QPNP_MPP_TYPE)
+			return -ENXIO;
+		if (subtype == QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
+		    subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT)
+			return -ENXIO;
+		attr.addr  = QPNP_REG_AOUT_CTL;
+		attr.shift = QPNP_REG_AOUT_REF_SHIFT;
+		attr.mask  = QPNP_REG_AOUT_REF_MASK;
+		break;
+	case QPNP_PINCONF_AMUX_ROUTE:
+		if (type != QPNP_MPP_TYPE)
+			return -ENXIO;
+		attr.addr  = QPNP_REG_AIN_CTL;
+		attr.shift = QPNP_REG_AIN_ROUTE_SHIFT;
+		attr.mask  = QPNP_REG_AIN_ROUTE_MASK;
+		break;
+	case QPNP_PINCONF_MODE:
+		attr.addr  = QPNP_REG_MODE_CTL;
+		attr.shift = QPNP_REG_MODE_SEL_SHIFT;
+		attr.mask  = QPNP_REG_MODE_SEL_MASK;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	addr = QPNP_REG_ADDR(pad, attr.addr);
+	ret = regmap_read(qctrl->map, addr, &buff);
+	if (ret < 0)
+		return ret;
+
+	field = QPNP_GET(buff, attr.shift, attr.mask);
+
+	switch (param) {
+	case PIN_CONFIG_DRIVE_PUSH_PULL:
+		if (field == QPNP_GPIO_OUT_BUF_CMOS)
+			*val = 1;
+		break;
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		if (field == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_NMOS)
+			*val = 1;
+		break;
+	case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+		if (field == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_PMOS)
+			*val = 1;
+		break;
+	case PIN_CONFIG_BIAS_DISABLE:
+		if (type == QPNP_GPIO_TYPE) {
+			if (field == QPNP_GPIO_PULL_NO)
+				*val = 1;
+		} else {
+			if (field == QPNP_MPP_PULL_UP_OPEN)
+				*val = 1;
+		}
+		break;
+	case PIN_CONFIG_BIAS_PULL_UP:
+		switch (field) {
+		default:
+		case QPNP_MPP_PULL_UP_OPEN:
+			*val = 0;
+			break;
+		case QPNP_MPP_PULL_UP_0P6KOHM:
+			*val = 600;
+			break;
+		case QPNP_MPP_PULL_UP_10KOHM:
+			*val = 10000;
+			break;
+		case QPNP_MPP_PULL_UP_30KOHM:
+			*val = 30000;
+			break;
+		}
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		if (field == QPNP_GPIO_PULL_DN)
+			*val = 1;
+		break;
+	case PIN_CONFIG_POWER_SOURCE:
+		*val = field;
+		break;
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		*val = (field + 1) * 5;
+		break;
+	case PIN_CONFIG_INPUT_ENABLE:
+		*val = field;
+		break;
+	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+		if (field == QPNP_PIN_MASTER_DISABLE)
+			*val = 1;
+		break;
+	case PIN_CONFIG_OUTPUT:
+		*val = field;
+		break;
+	case QPNP_PINCONF_PULL_UP:
+		switch (field) {
+		case QPNP_GPIO_PULL_NO:
+			field = 0;
+			break;
+		case QPNP_GPIO_PULL_UP_30:
+			field = PMIC_GPIO_PULL_UP_30;
+			break;
+		case QPNP_GPIO_PULL_UP_1P5:
+			field = PMIC_GPIO_PULL_UP_1P5;
+			break;
+		case QPNP_GPIO_PULL_UP_31P5:
+			field = PMIC_GPIO_PULL_UP_31P5;
+			break;
+
+		case QPNP_GPIO_PULL_UP_1P5_30:
+			field = PMIC_GPIO_PULL_UP_1P5_30;
+			break;
+		}
+		*val = field;
+		break;
+	case QPNP_PINCONF_STRENGTH:
+		switch (field) {
+		case QPNP_GPIO_STRENGTH_HIGH:
+			field = PMIC_GPIO_STRENGTH_HIGH;
+			break;
+		case QPNP_GPIO_STRENGTH_MED:
+			field = PMIC_GPIO_STRENGTH_MED;
+			break;
+		case QPNP_GPIO_STRENGTH_LOW:
+			field = PMIC_GPIO_STRENGTH_LOW;
+			break;
+		}
+		*val = field;
+		break;
+	case QPNP_PINCONF_MODE:
+	case QPNP_PINCONF_VREFENCE:
+	case QPNP_PINCONF_AMUX_ROUTE:
+		*val = field;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	struct qpnp_pinctrl *qpctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	/* Every PIN is a group */
+	return qpctrl->desc.npins;
+}
+
+static const char *qpnp_get_group_name(struct pinctrl_dev *pctldev,
+				       unsigned pin)
+{
+	struct qpnp_pinctrl *qpctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	/* Every PIN is a group */
+	return qpctrl->desc.pins[pin].name;
+}
+
+static int qpnp_get_group_pins(struct pinctrl_dev *pctldev,
+			      unsigned pin,
+			      const unsigned **pins,
+			      unsigned *num_pins)
+{
+	struct qpnp_pinctrl *qpctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	/* Every PIN is a group */
+	*pins = &qpctrl->desc.pins[pin].number;
+	*num_pins = 1;
+	return 0;
+}
+
+static int qpnp_parse_dt_config(struct device_node *np,
+			      struct pinctrl_dev *pctldev,
+			      unsigned long **configs, unsigned int *nconfs)
+{
+	struct qpnp_pinbindings *par;
+	unsigned long cfg;
+	int ret, idx;
+	u32 val;
+
+	for (idx = 0; idx < ARRAY_SIZE(qpnp_pinbindings); idx++) {
+
+		par = &qpnp_pinbindings[idx];
+		ret = of_property_read_u32(np, par->property, &val);
+
+		/* property not found */
+		if (ret == -EINVAL)
+			continue;
+
+		/* use default value, when no value is specified */
+		if (ret)
+			val = par->default_value;
+
+		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 qpnp_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 = qpnp_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 qpnp_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 = qpnp_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 qpnp_pinctrl_ops = {
+	.get_groups_count	= qpnp_get_groups_count,
+	.get_group_name		= qpnp_get_group_name,
+	.get_group_pins		= qpnp_get_group_pins,
+	.dt_node_to_map		= qpnp_dt_node_to_map,
+	.dt_free_map		= pinctrl_utils_dt_free_map,
+};
+
+static int qpnp_get_functions_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(qpnp_gpio_functions);
+}
+
+static const char *qpnp_get_function_name(struct pinctrl_dev *pctldev,
+					 unsigned function)
+{
+	struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	return qctrl->functions[function];
+}
+
+static int qpnp_get_function_groups(struct pinctrl_dev *pctldev,
+				  unsigned function,
+				  const char *const **groups,
+				  unsigned *const num_qgroups)
+{
+	struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+
+	*groups = qctrl->groups;
+	*num_qgroups = qctrl->desc.npins;
+	return 0;
+}
+
+static int qpnp_pinmux_enable(struct pinctrl_dev *pctldev,
+			     unsigned function,
+			     unsigned pin)
+{
+	struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+	struct qpnp_padinfo *pad;
+	unsigned int addr, val, mask;
+	int ret;
+
+	pad = qpnp_get_desc(qctrl, pin);
+	if (!pad)
+		return -EINVAL;
+
+	addr = QPNP_REG_ADDR(pad, QPNP_REG_MODE_CTL);
+	val = function << QPNP_REG_MODE_FUNCTION_SHIFT;
+	mask = QPNP_REG_MODE_FUNCTION_MASK;
+	ret = regmap_update_bits(qctrl->map, addr, mask, val);
+	if (ret)
+		return ret;
+
+	addr = QPNP_REG_ADDR(pad, QPNP_REG_EN_CTL);
+	val = BIT(QPNP_REG_MASTER_EN_SHIFT);
+	mask = QPNP_REG_MASTER_EN_MASK;
+	ret = regmap_update_bits(qctrl->map, addr, mask, val);
+
+	return ret;
+}
+
+static const struct pinmux_ops qpnp_pinmux_ops = {
+	.get_functions_count	= qpnp_get_functions_count,
+	.get_function_name	= qpnp_get_function_name,
+	.get_function_groups	= qpnp_get_function_groups,
+	.enable			= qpnp_pinmux_enable,
+};
+
+static int qpnp_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+	struct qpnp_padinfo *pad;
+	unsigned int val, en_mask, buff, addr;
+	int ret;
+
+	pad = qpnp_get_desc(qctrl, offset);
+	if (!pad)
+		return -EINVAL;
+
+	addr = QPNP_REG_ADDR(pad, QPNP_REG_MODE_CTL);
+	ret = regmap_read(qctrl->map, addr, &buff);
+	if (ret < 0)
+		return ret;
+
+	/* GPIO val is from RT status if input is enabled */
+	if ((buff & QPNP_REG_MODE_SEL_MASK) ==
+	    (QPNP_PIN_MODE_DIG_IN << QPNP_REG_MODE_SEL_SHIFT)) {
+
+		addr = QPNP_REG_ADDR(pad, QPNP_REG_STATUS1);
+		ret = regmap_read(qctrl->map, addr, &val);
+		if (ret < 0)
+			return ret;
+
+		if (pad->type == QPNP_GPIO_TYPE && pad->major == 0)
+			en_mask = QPNP_REG_STATUS1_GPIO_EN_REV0_MASK;
+		else if (pad->type == QPNP_GPIO_TYPE &&
+			 pad->major > 0)
+			en_mask = QPNP_REG_STATUS1_GPIO_EN_MASK;
+		else		/* MPP */
+			en_mask = QPNP_REG_STATUS1_MPP_EN_MASK;
+
+		if (!(val & en_mask))
+			return -EPERM;
+
+		ret = val & QPNP_REG_STATUS1_VAL_MASK;
+
+	} else {
+		ret = buff & QPNP_REG_MODE_INVERT_MASK;
+		ret = ret >> QPNP_REG_MODE_INVERT_SHIFT;
+	}
+
+	return !!ret;
+}
+
+static void qpnp_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+	struct qpnp_padinfo *pad;
+	unsigned int addr, buff;
+
+	pad = qpnp_get_desc(qctrl, offset);
+	if (!pad)
+		return;
+
+	addr = QPNP_REG_ADDR(pad, QPNP_REG_MODE_CTL);
+	buff = !!value << QPNP_REG_MODE_INVERT_SHIFT;
+
+	regmap_update_bits(qctrl->map, addr, QPNP_REG_MODE_INVERT_MASK, buff);
+}
+
+static int qpnp_config_get(struct pinctrl_dev *pctldev,
+			  unsigned int pin,
+			  unsigned long *config)
+{
+	struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+	unsigned param = pinconf_to_config_param(*config);
+	struct qpnp_padinfo *pad;
+	unsigned arg;
+	int ret;
+
+	pad = qpnp_get_desc(qctrl, pin);
+	if (!pad)
+		return -EINVAL;
+
+	/* Convert pinconf values to register values */
+	ret = qpnp_conv_from_pin(qctrl, pad, param, &arg);
+	if (ret)
+		return ret;
+
+	/* Convert register value to pinconf value */
+	*config = pinconf_to_config_packed(param, arg);
+	return 0;
+}
+
+static int qpnp_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
+			  unsigned long *configs, unsigned nconfs)
+{
+	struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+	struct qpnp_padinfo *pad;
+	unsigned param;
+	unsigned arg;
+	int idx, ret;
+
+	pad = qpnp_get_desc(qctrl, pin);
+	if (!pad)
+		return -EINVAL;
+
+	for (idx = 0; idx < nconfs; idx++) {
+		param = pinconf_to_config_param(configs[idx]);
+		arg = pinconf_to_config_argument(configs[idx]);
+
+		/* Convert pinconf values to register values */
+		ret = qpnp_conv_to_pin(qctrl, pad, param, arg);
+		if (ret < 0) {
+			dev_err(pctldev->dev, "config %u = %u failed\n",
+				param, arg);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void qpnp_config_dbg_show(struct pinctrl_dev *pctldev,
+				 struct seq_file *seq, unsigned pin)
+{
+	struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+	unsigned int en, buff, pull, out, drive, addr, mode, func, inv;
+	struct qpnp_padinfo *pad;
+	const char *name;
+	int ret;
+
+	static const char *const modes[] = {
+		"di", "do", "dio", "ai", "ao", "aio", "cs"
+	};
+	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-func"
+	};
+	static const char * const strengths[] = {
+		"no", "low", "medium", "high"
+	};
+
+	pad = qpnp_get_desc(qctrl, pin);
+	if (!pad)
+		return;
+
+	name = pad->name;
+
+	addr = QPNP_REG_ADDR(pad, QPNP_REG_MODE_CTL);
+	ret = regmap_read(qctrl->map, addr, &buff);
+	if (ret < 0) {
+		seq_printf(seq, " %-8s: read error %d", name, ret);
+		return;
+	}
+
+	mode = QPNP_GET(buff, QPNP_REG_MODE_SEL_SHIFT,
+			QPNP_REG_MODE_SEL_MASK);
+	func = QPNP_GET(buff, QPNP_REG_MODE_FUNCTION_SHIFT,
+			QPNP_REG_MODE_FUNCTION_MASK);
+	inv  = QPNP_GET(buff, QPNP_REG_MODE_INVERT_SHIFT,
+			QPNP_REG_MODE_INVERT_MASK);
+
+	addr = QPNP_REG_ADDR(pad, QPNP_REG_DIG_PULL_CTL);
+	ret = regmap_read(qctrl->map, addr, &buff);
+	if (ret < 0) {
+		seq_printf(seq, " %-8s: read error %d", name, ret);
+		return;
+	}
+	pull = QPNP_GET(buff, QPNP_REG_PULL_SHIFT, QPNP_REG_PULL_MASK);
+
+	addr = QPNP_REG_ADDR(pad, QPNP_REG_DIG_OUT_CTL);
+	ret = regmap_read(qctrl->map, addr, &buff);
+	if (ret < 0) {
+		seq_printf(seq, " %-8s: read error %d", name, ret);
+		return;
+	}
+	out = QPNP_GET(buff, QPNP_REG_OUT_TYPE_SHIFT, QPNP_REG_OUT_TYPE_MASK);
+	drive = QPNP_GET(buff, QPNP_REG_OUT_STRENGTH_SHIFT,
+			 QPNP_REG_OUT_STRENGTH_MASK);
+
+	addr = QPNP_REG_ADDR(pad, QPNP_REG_EN_CTL);
+	ret = regmap_read(qctrl->map, addr, &buff);
+	if (ret < 0) {
+		seq_printf(seq, " %-8s: read error %d", name, ret);
+		return;
+	}
+	en = QPNP_GET(buff, QPNP_REG_MASTER_EN_SHIFT, QPNP_REG_MASTER_EN_MASK);
+
+	seq_printf(seq, " %-8s: %-4s", name, modes[mode]);
+	seq_printf(seq, " %-7s", qctrl->functions[func]);
+	seq_printf(seq, " %-24s %-12s", biases[pull], buffer_types[out]);
+	seq_printf(seq, " %-6s %-4s", strengths[drive], inv ? "inv" : "");
+	seq_printf(seq, " %s", !en ? "high-Z" : "");
+
+}
+
+static const struct pinconf_ops qpnp_pinconf_ops = {
+	.pin_config_group_get	= qpnp_config_get,
+	.pin_config_group_set	= qpnp_config_set,
+	.pin_config_group_dbg_show = qpnp_config_dbg_show,
+};
+
+static int qpnp_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+	unsigned long config;
+
+	config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
+
+	return qpnp_config_set(qctrl->ctrl, offset, &config, 1);
+}
+
+static int qpnp_direction_output(struct gpio_chip *chip,
+			      unsigned offset, int val)
+{
+	struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+	unsigned long config;
+
+	config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val);
+	return qpnp_config_set(qctrl->ctrl, offset, &config, 1);
+}
+
+static int qpnp_request(struct gpio_chip *chip, unsigned offset)
+{
+	return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void qpnp_free(struct gpio_chip *chip, unsigned offset)
+{
+	pinctrl_free_gpio(chip->base + offset);
+}
+
+static int qpnp_of_xlate(struct gpio_chip *chip,
+		       const struct of_phandle_args *gpio_desc, u32 *flags)
+{
+	struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+	struct qpnp_padinfo *pad;
+	unsigned pin = gpio_desc->args[0] - QPNP_PIN_PHYSICAL_OFFSET;
+
+	if (chip->of_gpio_n_cells < 2)
+		return -EINVAL;
+
+	pad = qpnp_get_desc(qctrl, pin);
+	if (!pad)
+		return -EINVAL;
+
+	if (flags)
+		*flags = gpio_desc->args[1];
+
+	return pin;
+}
+
+static int qpnp_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+	struct qpnp_padinfo *pad;
+
+	pad = qpnp_get_desc(qctrl, offset);
+	if (!pad)
+		return -EINVAL;
+
+	return pad->irq;
+}
+
+static void qpnp_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+	struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
+	unsigned idx;
+
+	for (idx = 0; idx < chip->ngpio; idx++) {
+		qpnp_config_dbg_show(qctrl->ctrl, s, idx);
+		seq_puts(s, "\n");
+	}
+}
+
+static const struct gpio_chip qpnp_gpio_template = {
+	.direction_input  = qpnp_direction_input,
+	.direction_output = qpnp_direction_output,
+	.get              = qpnp_get,
+	.set              = qpnp_set,
+	.request          = qpnp_request,
+	.free             = qpnp_free,
+	.of_xlate	  = qpnp_of_xlate,
+	.to_irq		  = qpnp_to_irq,
+	.dbg_show         = qpnp_dbg_show,
+};
+
+static int qpnp_control_init(struct qpnp_pinctrl *qctrl,
+			  struct qpnp_padinfo *pad)
+{
+	pad->modes = 0;
+
+	if (pad->type == QPNP_GPIO_TYPE) {
+		switch (pad->subtype) {
+		case QPNP_GPIO_SUBTYPE_GPIO_4CH:
+		case QPNP_GPIO_SUBTYPE_GPIOC_4CH:
+		case QPNP_GPIO_SUBTYPE_GPIO_8CH:
+		case QPNP_GPIO_SUBTYPE_GPIOC_8CH:
+
+			/* only GPIO is supported*/
+			pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
+			pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
+			pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
+			break;
+		default:
+			dev_err(qctrl->dev, "invalid GPIO subtype 0x%x\n",
+				pad->subtype);
+			return -EINVAL;
+		}
+
+	} else if (pad->type == QPNP_MPP_TYPE) {
+
+		switch (pad->subtype) {
+		case QPNP_MPP_SUBTYPE_4CH_NO_SINK:
+		case QPNP_MPP_SUBTYPE_ULT_4CH_NO_SINK:
+
+			/* Current sink not supported*/
+			pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
+			pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
+			pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
+			pad->modes |= BIT(QPNP_PIN_MODE_BIDIR);
+			pad->modes |= BIT(QPNP_PIN_MODE_AIN);
+			pad->modes |= BIT(QPNP_PIN_MODE_AOUT);
+			break;
+		case QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT:
+		case QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
+
+			/* Analog output not supported */
+			pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
+			pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
+			pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
+			pad->modes |= BIT(QPNP_PIN_MODE_BIDIR);
+			pad->modes |= BIT(QPNP_PIN_MODE_AIN);
+			pad->modes |= BIT(QPNP_PIN_MODE_SINK);
+			break;
+		case QPNP_MPP_SUBTYPE_4CH_FULL_FUNC:
+		case QPNP_MPP_SUBTYPE_8CH_FULL_FUNC:
+
+			pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
+			pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
+			pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
+			pad->modes |= BIT(QPNP_PIN_MODE_BIDIR);
+			pad->modes |= BIT(QPNP_PIN_MODE_AIN);
+			pad->modes |= BIT(QPNP_PIN_MODE_AOUT);
+			pad->modes |= BIT(QPNP_PIN_MODE_SINK);
+			break;
+		default:
+			dev_err(qctrl->dev, "invalid MPP subtype 0x%x\n",
+				pad->subtype);
+			return -EINVAL;
+		}
+	} else {
+		dev_err(qctrl->dev, "invalid type 0x%x\n", pad->type);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_discover(struct platform_device *pdev,
+			struct qpnp_pinctrl *qctrl)
+{
+	struct device *dev = qctrl->dev;
+	struct pinctrl_pin_desc *desc, *descs;
+	struct qpnp_padinfo *pad, *pads;
+	unsigned int addr, npins;
+	int idx, ret;
+
+	npins = qctrl->info->npins;
+	pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
+	if (!pads)
+		return -ENOMEM;
+
+	descs = devm_kcalloc(dev, npins, sizeof(*descs), GFP_KERNEL);
+	if (!descs)
+		return -ENOMEM;
+
+	for (idx = 0; idx < npins; idx++) {
+
+		pad = &pads[idx];
+		desc = &descs[idx];
+
+		pad->irq = platform_get_irq(pdev, idx);
+		if (pad->irq < 0)
+			return pad->irq;
+
+		pad->offset = qctrl->info->base + (idx * 0x100);
+
+		addr = QPNP_REG_ADDR(pad, QPNP_REG_DIG_MAJOR_REV);
+		ret = regmap_read(qctrl->map, addr, &pad->major);
+		if (ret < 0)
+			return ret;
+
+		addr = QPNP_REG_ADDR(pad, QPNP_REG_TYPE);
+		ret = regmap_read(qctrl->map, addr, &pad->type);
+		if (ret < 0)
+			return ret;
+
+		if (pad->type != qctrl->info->type) {
+			dev_err(dev, "Expected %x, found %x\n",
+				qctrl->info->type, pad->type);
+			return -EINVAL;
+		}
+
+		addr = QPNP_REG_ADDR(pad, QPNP_REG_SUBTYPE);
+		ret = regmap_read(qctrl->map, addr, &pad->subtype);
+		if (ret < 0)
+			return ret;
+
+		ret = qpnp_control_init(qctrl, pad);
+		if (ret)
+			return ret;
+
+		pad->name = qctrl->groups[idx];
+		desc->number = idx;
+		desc->name = pad->name;
+	}
+
+	qctrl->pads = pads;
+
+	qctrl->chip = qpnp_gpio_template;
+	qctrl->chip.dev = dev;
+	qctrl->chip.base = -1;
+	qctrl->chip.ngpio = qctrl->info->npins;
+	qctrl->chip.label = dev_name(dev);
+	qctrl->chip.of_gpio_n_cells = 2;
+	qctrl->chip.can_sleep = true;
+
+	qctrl->desc.pctlops = &qpnp_pinctrl_ops,
+	qctrl->desc.pmxops = &qpnp_pinmux_ops,
+	qctrl->desc.confops = &qpnp_pinconf_ops,
+	qctrl->desc.owner = THIS_MODULE,
+	qctrl->desc.name = dev_name(dev);
+	qctrl->desc.pins = descs;
+	qctrl->desc.npins = npins;
+
+	qctrl->ctrl = pinctrl_register(&qctrl->desc, dev, qctrl);
+	if (!qctrl->ctrl)
+		return -ENODEV;
+
+	ret = gpiochip_add(&qctrl->chip);
+	if (ret) {
+		dev_err(qctrl->dev, "can't add gpio chip\n");
+		goto err_chip;
+	}
+
+	ret = gpiochip_add_pin_range(&qctrl->chip, dev_name(dev),
+				     0, 0, npins);
+	if (ret) {
+		dev_err(dev, "failed to add pin range\n");
+		goto err_range;
+	}
+
+	return 0;
+
+err_chip:
+	pinctrl_unregister(qctrl->ctrl);
+
+err_range:
+	gpiochip_remove(&qctrl->chip);
+
+	return ret;
+
+}
+
+static const struct of_device_id qpnp_pinctrl_of_match[];
+
+static int qpnp_pinctrl_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct qpnp_chipinfo *qchip;
+	struct qpnp_pinctrl *qctrl;
+
+	qctrl = devm_kzalloc(dev, sizeof(*qctrl), GFP_KERNEL);
+	if (!qctrl)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, qctrl);
+
+	qchip = of_match_node(qpnp_pinctrl_of_match, dev->of_node)->data;
+
+	qctrl->info = qchip;
+	qctrl->dev = &pdev->dev;
+	qctrl->map = dev_get_regmap(dev->parent, NULL);
+
+	if (qchip->type == QPNP_GPIO_TYPE) {
+		if (WARN_ON(qchip->npins > ARRAY_SIZE(qpnp_gpio_groups)))
+			return -EINVAL;
+		qctrl->groups = qpnp_gpio_groups;
+		qctrl->functions = qpnp_gpio_functions;
+	} else {
+		if (WARN_ON(qchip->npins > ARRAY_SIZE(qpnp_mpp_groups)))
+			return -EINVAL;
+		qctrl->groups = qpnp_mpp_groups;
+		qctrl->functions = qpnp_mpp_functions;
+	}
+
+	return qpnp_discover(pdev, qctrl);
+}
+
+static int qpnp_pinctrl_remove(struct platform_device *pdev)
+{
+	struct qpnp_pinctrl *qctrl = platform_get_drvdata(pdev);
+
+	gpiochip_remove(&qctrl->chip);
+	pinctrl_unregister(qctrl->ctrl);
+
+	return 0;
+}
+
+static const struct qpnp_chipinfo qpnp_pm8841_mpp_info = {
+	.npins	= 4,
+	.base	= 0xa000,
+	.type   = QPNP_MPP_TYPE,
+};
+
+static const struct qpnp_chipinfo qpnp_pm8941_gpio_info = {
+	.npins	= 36,
+	.base	= 0xc000,
+	.type   = QPNP_GPIO_TYPE,
+};
+
+static const struct qpnp_chipinfo qpnp_pm8941_mpp_info = {
+	.npins	= 8,
+	.base	= 0xa000,
+	.type   = QPNP_MPP_TYPE,
+};
+
+static const struct qpnp_chipinfo qpnp_pma8084_mpp_info = {
+	.npins	= 4,
+	.base	= 0xa000,
+	.type   = QPNP_MPP_TYPE,
+};
+
+static const struct qpnp_chipinfo qpnp_pma8084_gpio_info = {
+	.npins	= 22,
+	.base	= 0xc000,
+	.type   = QPNP_GPIO_TYPE,
+};
+
+static const struct of_device_id qpnp_pinctrl_of_match[] = {
+	{ .compatible = "qcom,pm8941-gpio", .data = &qpnp_pm8941_gpio_info },
+	{ .compatible = "qcom,pm8941-mpp", .data = &qpnp_pm8941_mpp_info },
+	{ .compatible = "qcom,pm8841-mpp", .data = &qpnp_pm8841_mpp_info },
+	{ .compatible = "qcom,pma8084-gpio", .data = &qpnp_pma8084_gpio_info },
+	{ .compatible = "qcom,pma8084-mpp", .data = &qpnp_pma8084_mpp_info },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, qpnp_pinctrl_of_match);
+
+static struct platform_driver qpnp_pinctrl_driver = {
+	.driver = {
+		.name = "spmi-pmic-pinctrl",
+		.owner = THIS_MODULE,
+		.of_match_table = qpnp_pinctrl_of_match,
+	},
+	.probe = qpnp_pinctrl_probe,
+	.remove = qpnp_pinctrl_remove,
+};
+module_platform_driver(qpnp_pinctrl_driver);
+
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC pin control driver");
+MODULE_ALIAS("platform:spmi-pmic-pinctrl");
+MODULE_LICENSE("GPL v2");
-- 
1.8.3.2

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

* [PATCH v3 5/6] ARM: dts: qcom: Add PM8941 and PM8841 pinctrl nodes
  2014-08-11 15:40 [PATCH v3 0/6] Qualcomm PMIC pin controller drivers Ivan T. Ivanov
                   ` (3 preceding siblings ...)
  2014-08-11 15:40 ` [PATCH v3 4/6] pinctrl: Qualcomm SPMI PMIC pin controller driver Ivan T. Ivanov
@ 2014-08-11 15:40 ` Ivan T. Ivanov
  2014-08-14  7:33   ` Pramod Gurav
  2014-08-11 15:40 ` [PATCH v3 6/6] ARM: dts: qcom: Add APQ8074 Dragonboard PMIC GPIO bindings Ivan T. Ivanov
  5 siblings, 1 reply; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-11 15:40 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Russell King
  Cc: Ivan T. Ivanov, Bjorn Andersson, linux-arm-msm, devicetree, linux-kernel

From: "Ivan T. Ivanov" <iivanov@mm-sol.com>

Add nodes for PM8941 and PM8841 GPIO and MPP PMIC subfunctions.

Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
---
 arch/arm/boot/dts/qcom-msm8974.dtsi | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
index c7ae7ba..3967797 100644
--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
@@ -267,6 +267,31 @@
 					reg-names = "rtc", "alarm";
 					interrupts = <0x0 0x61 0x1 IRQ_TYPE_EDGE_RISING>;
 				};
+
+				pm8941_gpios: gpios@c000 {
+					compatible = "qcom,pm8941-gpio";
+					reg = <0xc000>;
+					gpio-controller;
+					#gpio-cells = <2>;
+					interrupts = <0 0xc0 0 0>, <0 0xc1 0 0>, <0 0xc2 0 0>, <0 0xc3 0 0>,
+						     <0 0xc4 0 0>, <0 0xc5 0 0>, <0 0xc6 0 0>, <0 0xc7 0 0>,
+						     <0 0xc8 0 0>, <0 0xc9 0 0>, <0 0xca 0 0>, <0 0xcb 0 0>,
+						     <0 0xcc 0 0>, <0 0xcd 0 0>, <0 0xce 0 0>, <0 0xcf 0 0>,
+						     <0 0xd0 0 0>, <0 0xd1 0 0>, <0 0xd2 0 0>, <0 0xd3 0 0>,
+						     <0 0xd4 0 0>, <0 0xd5 0 0>, <0 0xd6 0 0>, <0 0xd7 0 0>,
+						     <0 0xd8 0 0>, <0 0xd9 0 0>, <0 0xda 0 0>, <0 0xdb 0 0>,
+						     <0 0xdc 0 0>, <0 0xdd 0 0>, <0 0xde 0 0>, <0 0xdf 0 0>,
+						     <0 0xe0 0 0>, <0 0xe1 0 0>, <0 0xe2 0 0>, <0 0xe3 0 0>;
+				};
+
+				pm8941_mpps: mpps@a000 {
+					compatible = "qcom,pm8941-mpp";
+					reg = <0xa000>;
+					gpio-controller;
+					#gpio-cells = <2>;
+					interrupts = <0 0xa0 0 0>, <0 0xa1 0 0>, <0 0xa2 0 0>, <0 0xa3 0 0>,
+						     <0 0xa4 0 0>, <0 0xa5 0 0>, <0 0xa6 0 0>, <0 0xa7 0 0>;
+				};
 			};
 
 			usid1: pm8941@1 {
@@ -281,6 +306,14 @@
 				reg = <0x4 SPMI_USID>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+
+				pm8841_mpps: 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>;
+				};
 			};
 
 			usid5: pm8841@5 {
-- 
1.8.3.2

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

* [PATCH v3 6/6] ARM: dts: qcom: Add APQ8074 Dragonboard PMIC GPIO bindings
  2014-08-11 15:40 [PATCH v3 0/6] Qualcomm PMIC pin controller drivers Ivan T. Ivanov
                   ` (4 preceding siblings ...)
  2014-08-11 15:40 ` [PATCH v3 5/6] ARM: dts: qcom: Add PM8941 and PM8841 pinctrl nodes Ivan T. Ivanov
@ 2014-08-11 15:40 ` Ivan T. Ivanov
  2014-08-20 23:06   ` Bjorn Andersson
  5 siblings, 1 reply; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-11 15:40 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Russell King
  Cc: Ivan T. Ivanov, Bjorn Andersson, linux-arm-msm, devicetree, linux-kernel

From: "Ivan T. Ivanov" <iivanov@mm-sol.com>

Add default states for PMIC GPIO's on APQ8074 Dragonboard

Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
---
 .../dts/qcom-apq8074-dragonboard-pmics-pins.dtsi   | 103 +++++++++++++++++++++
 arch/arm/boot/dts/qcom-apq8074-dragonboard.dts     |   1 +
 2 files changed, 104 insertions(+)
 create mode 100644 arch/arm/boot/dts/qcom-apq8074-dragonboard-pmics-pins.dtsi

diff --git a/arch/arm/boot/dts/qcom-apq8074-dragonboard-pmics-pins.dtsi b/arch/arm/boot/dts/qcom-apq8074-dragonboard-pmics-pins.dtsi
new file mode 100644
index 0000000..c69ecc2
--- /dev/null
+++ b/arch/arm/boot/dts/qcom-apq8074-dragonboard-pmics-pins.dtsi
@@ -0,0 +1,103 @@
+
+#include <dt-bindings/pinctrl/qcom,pmic-mpp.h>
+#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
+
+&pm8941_gpios {
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&pm8941_gpios_default>;
+
+	pm8941_gpios_default: default {
+		group-1 {
+			pins = "gpio1", "gpio2", "gpio5", "gpio29";
+			function = PMIC_GPIO_FUNC_NORMAL;
+			input-enable;
+			power-source = <PM8941_GPIO_S3>;
+			qcom,pull-up-strength = <PMIC_GPIO_PULL_UP_30>;
+		};
+		group-6 {	/* TUSB3_HUB-RESET */
+			pins = "gpio6";
+			function = PMIC_GPIO_FUNC_NORMAL;
+			output-high;
+			drive-push-pull;
+			power-source = <PM8941_GPIO_VPH>;
+			qcom,pull-up-strength = <PMIC_GPIO_PULL_UP_30>;
+			qcom,drive-strength = <PMIC_GPIO_STRENGTH_MED>;
+		};
+		group-8 {	/* HSIC_HUB-RESET */
+			pins = "gpio8";
+			function = PMIC_GPIO_FUNC_NORMAL;
+			output-low;
+			bias-disable;
+			qcom,drive-strength = <PMIC_GPIO_STRENGTH_MED>;
+		};
+		group-9 {	/* GbE_RST_N */
+			pins = "gpio9";
+			function = PMIC_GPIO_FUNC_NORMAL;
+			output-high;
+			drive-push-pull;
+			power-source = <PM8941_GPIO_VPH>;
+			qcom,pull-up-strength = <PMIC_GPIO_PULL_UP_30>;
+			qcom,drive-strength = <PMIC_GPIO_STRENGTH_MED>;
+		};
+		group-10 {	/* SATA_RST_N */
+			pins = "gpio10";
+			function = PMIC_GPIO_FUNC_NORMAL;
+			output-high;
+			drive-push-pull;
+			power-source = <PM8941_GPIO_VPH>;
+			qcom,pull-up-strength = <PMIC_GPIO_PULL_UP_30>;
+			qcom,drive-strength = <PMIC_GPIO_STRENGTH_MED>;
+		};
+		group-15 {	/* DIVCLK1_CODEC */
+			pins = "gpio15";
+			function = PM8941_GPIO15_18_DIV_CLK;
+			output-high;
+			drive-push-pull;
+			bias-disable;
+			power-source = <PM8941_GPIO_S3>;
+			qcom,drive-strength = <PMIC_GPIO_STRENGTH_LOW>;
+		};
+		group-19 {	/* MIPI_DSI0_RESET_N */
+			pins = "gpio19";
+			function = PMIC_GPIO_FUNC_NORMAL;
+			output-low;
+			drive-push-pull;
+			bias-disable;
+			power-source = <PM8941_GPIO_S3>;
+			qcom,drive-strength = <PMIC_GPIO_STRENGTH_MED>;
+		};
+		group-29 {	/* SD_WP_N */
+			pins = "gpio29";
+			function = PMIC_GPIO_FUNC_NORMAL;
+			input-enable;
+			power-source = <PM8941_GPIO_S3>;
+			qcom,pull-up-strength = <PMIC_GPIO_PULL_UP_30>;
+		};
+		group-33 {	/* WLAN_PWD_L */
+			pins = "gpio33";
+			function = PMIC_GPIO_FUNC_NORMAL;
+			output-high;
+			drive-push-pull;
+			bias-disable;
+			power-source = <PM8941_GPIO_S3>;
+			qcom,drive-strength = <PMIC_GPIO_STRENGTH_MED>;
+		};
+		group-34 {	/* BT_PWD_L */
+			pins = "gpio34";
+			function = PMIC_GPIO_FUNC_NORMAL;
+			output-low;
+			drive-push-pull;
+			bias-disable;
+			power-source = <PM8941_GPIO_S3>;
+			qcom,drive-strength = <PMIC_GPIO_STRENGTH_MED>;
+		};
+	};
+};
+
+&pm8941_mpps {
+};
+
+&pm8841_mpps {
+};
+
diff --git a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts
index b4dfb01..87b90e3 100644
--- a/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts
+++ b/arch/arm/boot/dts/qcom-apq8074-dragonboard.dts
@@ -1,4 +1,5 @@
 #include "qcom-msm8974.dtsi"
+#include "qcom-apq8074-dragonboard-pmics-pins.dtsi"
 
 / {
 	model = "Qualcomm APQ8074 Dragonboard";
-- 
1.8.3.2

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

* Re: [PATCH v3 3/6] pinctrl: Add documentation for SPMI PMIC pinctrl driver bindings
  2014-08-11 15:40 ` [PATCH v3 3/6] pinctrl: Add documentation for SPMI PMIC pinctrl driver bindings Ivan T. Ivanov
@ 2014-08-13 14:32   ` Stephen Boyd
  2014-08-25 14:07     ` Ivan T. Ivanov
  0 siblings, 1 reply; 27+ messages in thread
From: Stephen Boyd @ 2014-08-13 14:32 UTC (permalink / raw)
  To: Ivan T. Ivanov
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Bjorn Andersson, linux-arm-msm, devicetree, linux-kernel

On 08/11, Ivan T. Ivanov wrote:
> 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..0a64567
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
> @@ -0,0 +1,179 @@
> +Qualcomm PMIC Multi-Purpose Pin (MPP) block
> +
> +This binding describes the MPP block(s) found in the 8xxx series of pmics from
> +Qualcomm.
> +
> +- compatible:
> +	Usage: required
> +	Value type: <string>
> +	Definition: must be one of:
> +		    "qcom,pm8841-mpp"
> +		    "qcom,pm8941-mpp"
> +		    "qcom,pma8084-mpp"
> +
> +- reg:
> +	Usage: required
> +	Value type: <u32>
> +	Definition: Register base of the gpio block

MPP?

> +
> +- interrupts:
> +	Usage: required
> +	Value type: <prop-encoded-array>
> +	Definition: Must contain an array of encoded interrupt specifiers for
> +		    each available gpio

MPP? Maybe gpio makes sense.

> +
> +- 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
> +

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

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

* Re: [PATCH v3 5/6] ARM: dts: qcom: Add PM8941 and PM8841 pinctrl nodes
  2014-08-11 15:40 ` [PATCH v3 5/6] ARM: dts: qcom: Add PM8941 and PM8841 pinctrl nodes Ivan T. Ivanov
@ 2014-08-14  7:33   ` Pramod Gurav
  2014-08-25 14:06     ` Ivan T. Ivanov
  0 siblings, 1 reply; 27+ messages in thread
From: Pramod Gurav @ 2014-08-14  7:33 UTC (permalink / raw)
  To: Ivan T. Ivanov
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Russell King, Bjorn Andersson, linux-arm-msm, devicetree,
	linux-kernel

Hi Ivan,
This patch fail to apply on linux-next Tree. Probably because this is
based on Stanimir's older patch series "Support for Qualcomm QPNP
PMIC's". He has posted v4 of them on which it fails to apply. Am I correct?

Thanks
Pramod

On Monday 11 August 2014 09:10 PM, Ivan T. Ivanov wrote:
> From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
> 
> Add nodes for PM8941 and PM8841 GPIO and MPP PMIC subfunctions.
> 
> Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
> ---
>  arch/arm/boot/dts/qcom-msm8974.dtsi | 33 +++++++++++++++++++++++++++++++++
>  1 file changed, 33 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
> index c7ae7ba..3967797 100644
> --- a/arch/arm/boot/dts/qcom-msm8974.dtsi
> +++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
> @@ -267,6 +267,31 @@
>  					reg-names = "rtc", "alarm";
>  					interrupts = <0x0 0x61 0x1 IRQ_TYPE_EDGE_RISING>;
>  				};
> +
> +				pm8941_gpios: gpios@c000 {
> +					compatible = "qcom,pm8941-gpio";
> +					reg = <0xc000>;
> +					gpio-controller;
> +					#gpio-cells = <2>;
> +					interrupts = <0 0xc0 0 0>, <0 0xc1 0 0>, <0 0xc2 0 0>, <0 0xc3 0 0>,
> +						     <0 0xc4 0 0>, <0 0xc5 0 0>, <0 0xc6 0 0>, <0 0xc7 0 0>,
> +						     <0 0xc8 0 0>, <0 0xc9 0 0>, <0 0xca 0 0>, <0 0xcb 0 0>,
> +						     <0 0xcc 0 0>, <0 0xcd 0 0>, <0 0xce 0 0>, <0 0xcf 0 0>,
> +						     <0 0xd0 0 0>, <0 0xd1 0 0>, <0 0xd2 0 0>, <0 0xd3 0 0>,
> +						     <0 0xd4 0 0>, <0 0xd5 0 0>, <0 0xd6 0 0>, <0 0xd7 0 0>,
> +						     <0 0xd8 0 0>, <0 0xd9 0 0>, <0 0xda 0 0>, <0 0xdb 0 0>,
> +						     <0 0xdc 0 0>, <0 0xdd 0 0>, <0 0xde 0 0>, <0 0xdf 0 0>,
> +						     <0 0xe0 0 0>, <0 0xe1 0 0>, <0 0xe2 0 0>, <0 0xe3 0 0>;
> +				};
> +
> +				pm8941_mpps: mpps@a000 {
> +					compatible = "qcom,pm8941-mpp";
> +					reg = <0xa000>;
> +					gpio-controller;
> +					#gpio-cells = <2>;
> +					interrupts = <0 0xa0 0 0>, <0 0xa1 0 0>, <0 0xa2 0 0>, <0 0xa3 0 0>,
> +						     <0 0xa4 0 0>, <0 0xa5 0 0>, <0 0xa6 0 0>, <0 0xa7 0 0>;
> +				};
>  			};
>  
>  			usid1: pm8941@1 {
> @@ -281,6 +306,14 @@
>  				reg = <0x4 SPMI_USID>;
>  				#address-cells = <1>;
>  				#size-cells = <0>;
> +
> +				pm8841_mpps: 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>;
> +				};
>  			};
>  
>  			usid5: pm8841@5 {
> 

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

* Re: [PATCH v3 1/6] pinctrl: Device tree bindings for Qualcomm pm8xxx gpio block
  2014-08-11 15:40 ` [PATCH v3 1/6] pinctrl: Device tree bindings for Qualcomm pm8xxx gpio block Ivan T. Ivanov
@ 2014-08-16 15:24   ` Daniel
  2014-08-18  7:16     ` Ivan T. Ivanov
  2014-08-20 22:27   ` Bjorn Andersson
  1 sibling, 1 reply; 27+ messages in thread
From: Daniel @ 2014-08-16 15:24 UTC (permalink / raw)
  To: Ivan T. Ivanov
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Bjorn Andersson, linux-arm-msm, devicetree, linux-kernel

@Ivan: sorry about the double post.

Am 11.08.2014 um 16:40 schrieb Ivan T. Ivanov <iivanov@mm-sol.com>:
> 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..994e748
> --- /dev/null
> +++ b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
> @@ -0,0 +1,107 @@
> +/*
> + * 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		1
> +#define PMIC_GPIO_PULL_UP_1P5		2
> +#define PMIC_GPIO_PULL_UP_31P5		3
> +#define PMIC_GPIO_PULL_UP_1P5_30	4

Looking at drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c, shouldn't these defines start at 0?
e.g. #define PMIC_GPIO_PULL_UP_30 	0

after shifting those 4 defines by -1, /sys/kernel/debug/gpio gives me the proper values for the pins configured to be pull-up:

cat /sys/kernel/debug/gpio
GPIOs 212-255, platform/500000.qcom,ssbi:pmic@0:gpio@150, 500000.qcom,ssbi:pmic@0:gpio@150:
...
gpio4 : in   normal  VIN2 pull-up 30uA                push-pull  low  no      inverted
...
gpio38: in   normal  VIN2 pull-up 30uA                push-pull  low  no      inverted
...
____
dts:
[snip]
                                        pm8921_gpio_keys: gpio-keys {
                                                volume-keys {
                                                        pins = "gpio4", "gpio38";
                                                        function = PMIC_GPIO_FUNC_NORMAL;

                                                        input-enable;
                                                        drive-push-pull;
                                                        qcom,pull-up-strength = <PMIC_GPIO_PULL_UP_30>;
                                                        qcom,drive-strength = <PMIC_GPIO_STRENGTH_NO>;
                                                        power-source = <PM8921_GPIO_S4>;
                                                };
                                        };
[/snip]

However, I still cannot get any data from those 2 pins if I export them through /sys/class/gpio...

Device: Nexus 7 2013 WiFi
Board: APQ8064

Cheers,
D

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

* Re: [PATCH v3 1/6] pinctrl: Device tree bindings for Qualcomm pm8xxx gpio block
  2014-08-16 15:24   ` Daniel
@ 2014-08-18  7:16     ` Ivan T. Ivanov
  2014-08-20 22:10       ` Bjorn Andersson
  0 siblings, 1 reply; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-18  7:16 UTC (permalink / raw)
  To: Daniel
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Bjorn Andersson, linux-arm-msm, devicetree, linux-kernel

On Sat, 2014-08-16 at 16:24 +0100, Daniel wrote:
> @Ivan: sorry about the double post.
> 
> Am 11.08.2014 um 16:40 schrieb Ivan T. Ivanov <iivanov@mm-sol.com>:
> > 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..994e748
> > --- /dev/null
> > +++ b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
> > @@ -0,0 +1,107 @@
> > +/*
> > + * 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		1
> > +#define PMIC_GPIO_PULL_UP_1P5		2
> > +#define PMIC_GPIO_PULL_UP_31P5		3
> > +#define PMIC_GPIO_PULL_UP_1P5_30	4
> 
> Looking at drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c, shouldn't these defines start at 0?
> e.g. #define PMIC_GPIO_PULL_UP_30 	0
> 

Initially "bias-pull-up" was used to set this parameter. 
Zero value for "bias-pull-up" has special meaning "...the 
pin is connected to VDD...". So values in DTS have to have
offset by one. Micro Amps are non-standard for pull-ups, 
thats why I have changed this to "qcom,pull-up-strength", but I 
have made mistake in config_set function. Following patch should 
fix the issue. I will send updated version soon.

> However, I still cannot get any data from those 2 pins if I export them through /sys/class/gpio...

Ensure that you are exporting the right gpio number. 

# cat /sys/kernel/debug/gpio

Will tell you correct gpio range for registered gpio chips.

> 
> Device: Nexus 7 2013 WiFi
> Board: APQ8064

Thanks for testing this.

Regards,
Ivan

---
 drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
index 9a1b443..dd424f4 100644
--- a/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
@@ -446,7 +446,7 @@ static int pm8xxx_gpio_config_set(struct pinctrl_dev
*pctldev,
 				dev_err(pctrl->dev, "invalid pull-up level\n");
 				return -EINVAL;
 			}
-			pin->bias = arg - PM8XXX_GPIO_BIAS_PU_30;
+			pin->bias = arg - PMIC_GPIO_PULL_UP_30;
 			banks |= BIT(2);
 			pin->disable = 0;
 			banks |= BIT(3);

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

* Re: [PATCH v3 2/6] pinctrl: Introduce pinctrl driver for Qualcomm SSBI PMIC's
  2014-08-11 15:40 ` [PATCH v3 2/6] pinctrl: Introduce pinctrl driver for Qualcomm SSBI PMIC's Ivan T. Ivanov
@ 2014-08-20  8:06       ` Srinivas Kandagatla
  0 siblings, 0 replies; 27+ messages in thread
From: Srinivas Kandagatla @ 2014-08-20  8:06 UTC (permalink / raw)
  To: Ivan T. Ivanov, Linus Walleij, Grant Likely, Rob Herring
  Cc: Bjorn Andersson, linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

Hi Bjorn,

Two things which I noticed while trying out this driver to drive a reset 
line.

1> gpio numbering for pinconf vs gpio are not consistent, they differ by 
an offset of 1.

For example to control GPIO43 I had to do something like this in pinconf:

wlan_default_gpios: wlan-gpios {
	pios {
		pins = "gpio43";
		function = "normal";
		bias-disable;
		power-source = <PM8921_GPIO_S4>;
	};
};


for same pin for gpio I had to do:
	reset-gpio = <&pm8921_gpio 42 GPIO_ACTIVE_LOW>;

This offset by 1 is bit confusing and can easily lead to errors which 
are hard to find.

I think this should be easy to fix..

2> Looking back at v3.4 kernel, for gpio modes, BIT(0) of bank 0 is set 
to enable gpio mode. without this bit driver does not work for output pins.

here is the patch which fixes this:

 From 5a5c5171a3371cf38ccd157922ea140bfd0253d5 Mon Sep 17 00:00:00 2001
From: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Date: Wed, 20 Aug 2014 07:18:03 +0100
Subject: [PATCH] pinctrl:qcom:ssbi: Enable gpio mode

Looking back at 3.4 kernel driver, it looks like the gpio enable bit is
missing in this driver.

This patch is just a hack to get the wlan reset line working.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
  drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c | 4 +++-
  1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c 
b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
index 27e5ec9..2633ea1 100644
--- a/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
@@ -49,6 +49,7 @@
  #define SSBI_REG_ADDR_GPIO_BASE		0x150
  #define SSBI_REG_ADDR_GPIO(n)		(SSBI_REG_ADDR_GPIO_BASE + n)

+#define	PM8XXX_GPIO_MODE_ENABLE		BIT(0)
  #define PM8XXX_GPIO_WRITE		BIT(7)

  #define PM8XXX_MAX_GPIOS		44
@@ -493,7 +494,8 @@ static int pm8xxx_gpio_config_set(struct pinctrl_dev 
*pctldev,
  	}

  	if (banks & BIT(0))
-		pm8xxx_gpio_write(pctrl, offset, 0, pin->power_source << 1);
+		pm8xxx_gpio_write(pctrl, offset, 0, pin->power_source << 1 |
+				  PM8XXX_GPIO_MODE_ENABLE);

  	if (banks & BIT(1)) {
  		val = pin->direction << 2;
-- 
2.0.2

with this change am able to reset WLAN chip on IFC6410 which is 
connected to gpio43 of pmic.

thanks,
srini
On 11/08/14 16:40, Ivan T. Ivanov wrote:
> From: Bjorn Andersson <bjorn.andersson-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org>
>
> This introduces a pinctrl, pinconf, pinmux and gpio driver for the gpio
> block found in pm8018, pm8038, pm8058, pm8917 and pm8921 pmics from
> Qualcomm.
>
> Signed-off-by: Bjorn Andersson <bjorn.andersson-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org>
> Signed-off-by: Ivan T. Ivanov <iivanov-NEYub+7Iv8PQT0dZR+AlfA@public.gmane.org>
> ---
>   drivers/pinctrl/qcom/Kconfig             |  11 +
>   drivers/pinctrl/qcom/Makefile            |   1 +
>   drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c | 870 +++++++++++++++++++++++++++++++
>   3 files changed, 882 insertions(+)
>   create mode 100644 drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
>
> diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
> index d160a71..6bd4e4d 100644
> --- a/drivers/pinctrl/qcom/Kconfig
> +++ b/drivers/pinctrl/qcom/Kconfig
> @@ -39,4 +39,15 @@ 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_SSBI_PMIC
> +       tristate "Qualcomm SSBI PMIC pin controller driver"
> +       depends on GPIOLIB && OF
> +       select PINCONF
> +       select PINMUX
> +       select GENERIC_PINCONF
> +       help
> +         This is the pinctrl, pinmux, pinconf and gpiolib driver for the
> +         Qualcomm GPIO blocks found in the pm8018, pm8038, pm8058, pm8917 and
> +         pm8921 pmics from Qualcomm.
> +
>   endif
> diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
> index 2a02602..8faa2ca 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_SSBI_PMIC) += pinctrl-ssbi-pmic.o
> diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
> new file mode 100644
> index 0000000..9a1b443
> --- /dev/null
> +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
> @@ -0,0 +1,870 @@
> +/*
> + * Copyright (c) 2014, Sony Mobile Communications AB.
> + * Copyright (c) 2013, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/pinctrl/pinconf-generic.h>
> +#include <linux/slab.h>
> +#include <linux/regmap.h>
> +#include <linux/gpio.h>
> +#include <linux/mfd/pm8921-core.h>
> +
> +#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
> +
> +#include "../core.h"
> +#include "../pinconf.h"
> +#include "../pinctrl-utils.h"
> +
> +/* direction */
> +#define PM8XXX_GPIO_DIR_OUT		BIT(0)
> +#define PM8XXX_GPIO_DIR_IN		BIT(1)
> +
> +/* output buffer */
> +#define PM8XXX_GPIO_PUSH_PULL		0
> +#define PM8XXX_GPIO_OPEN_DRAIN		1
> +
> +/* bias */
> +#define PM8XXX_GPIO_BIAS_PU_30		0
> +#define PM8XXX_GPIO_BIAS_PU_1P5		1
> +#define PM8XXX_GPIO_BIAS_PU_31P5	2
> +#define PM8XXX_GPIO_BIAS_PU_1P5_30	3
> +#define PM8XXX_GPIO_BIAS_PD		4
> +#define PM8XXX_GPIO_BIAS_NP		5
> +
> +/* GPIO registers */
> +#define SSBI_REG_ADDR_GPIO_BASE		0x150
> +#define SSBI_REG_ADDR_GPIO(n)		(SSBI_REG_ADDR_GPIO_BASE + n)
> +
> +#define PM8XXX_GPIO_WRITE		BIT(7)
> +
> +#define PM8XXX_MAX_GPIOS		44
> +
> +/* Qualcomm specific pin configurations */
> +#define PM8XXX_PINCONF_PULL_UP		(PIN_CONFIG_END + 1)
> +#define PM8XXX_PINCONF_STRENGTH		(PIN_CONFIG_END + 2)
> +
> +struct pm8xxx_pinbindings {
> +	const char *property;
> +	unsigned param;
> +	u32 default_value;
> +};
> +static struct pm8xxx_pinbindings pm8xxx_pinbindings[] = {
> +	/* PMIC_GPIO_PULL_UP_30...  */
> +	{"qcom,pull-up-strength",	PM8XXX_PINCONF_PULL_UP, 0},
> +	/* PMIC_GPIO_STRENGTH_NO... */
> +	{"qcom,drive-strength",		PM8XXX_PINCONF_STRENGTH, 0},
> +};
> +
> +struct pm8xxx_gpio_pin {
> +	int irq;
> +
> +	u8 power_source;
> +	u8 direction;
> +	u8 output_buffer;
> +	u8 output_value;
> +	u8 bias;
> +	u8 output_strength;
> +	u8 disable;
> +	u8 function;
> +	u8 non_inverted;
> +};
> +
> +struct pm8xxx_gpio_data {
> +	int ngpio;
> +};
> +
> +struct pm8xxx_gpio {
> +	struct device *dev;
> +	struct regmap *regmap;
> +	struct pinctrl_dev *pctrl;
> +	struct gpio_chip chip;
> +
> +	const struct pm8xxx_gpio_data *data;
> +
> +	struct pm8xxx_gpio_pin pins[PM8XXX_MAX_GPIOS];
> +};
> +
> +static inline struct pm8xxx_gpio *to_pm8xxx_gpio(struct gpio_chip *chip)
> +{
> +	return container_of(chip, struct pm8xxx_gpio, chip);
> +};
> +
> +static const char * const pm8xxx_gpio_groups[PM8XXX_MAX_GPIOS] = {
> +	"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",
> +	"gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43",
> +	"gpio44",
> +};
> +
> +static const char * const pm8xxx_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 pm8xxx_gpio_read(struct pm8xxx_gpio *pctrl, int pin, int bank)
> +{
> +	int reg = SSBI_REG_ADDR_GPIO(pin);
> +	unsigned int val = bank << 4;
> +	int ret;
> +
> +	ret = regmap_write(pctrl->regmap, reg, val);
> +	if (ret) {
> +		dev_err(pctrl->dev,
> +			"failed to select bank %d of pin %d\n", bank, pin);
> +		return ret;
> +	}
> +
> +	ret = regmap_read(pctrl->regmap, reg, &val);
> +	if (ret) {
> +		dev_err(pctrl->dev,
> +			"failed to read register %d of pin %d\n", bank, pin);
> +		return ret;
> +	}
> +
> +	return val;
> +}
> +
> +static int pm8xxx_gpio_write(struct pm8xxx_gpio *pctrl,
> +			     int pin, int bank, u8 val)
> +{
> +	int ret;
> +
> +	val |= PM8XXX_GPIO_WRITE;
> +	val |= bank << 4;
> +
> +	ret = regmap_write(pctrl->regmap, SSBI_REG_ADDR_GPIO(pin), val);
> +	if (ret)
> +		dev_err(pctrl->dev, "failed to write register\n");
> +
> +	return ret;
> +}
> +
> +static int pm8xxx_gpio_get_groups_count(struct pinctrl_dev *pctldev)
> +{
> +	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +
> +	return pctrl->data->ngpio;
> +}
> +
> +static const char *pm8xxx_gpio_get_group_name(struct pinctrl_dev *pctldev,
> +				      unsigned group)
> +{
> +	return pm8xxx_gpio_groups[group];
> +}
> +
> +static int pm8xxx_parse_dt_config(struct device *dev, struct device_node *np,
> +			unsigned long **configs, unsigned int *nconfigs)
> +{
> +	struct pm8xxx_pinbindings *par;
> +	unsigned long cfg[ARRAY_SIZE(pm8xxx_pinbindings)];
> +	unsigned int ncfg = 0;
> +	int ret, idx;
> +	u32 val;
> +
> +	if (!np)
> +		return -EINVAL;
> +
> +	for (idx = 0; idx < ARRAY_SIZE(pm8xxx_pinbindings); idx++) {
> +		par = &pm8xxx_pinbindings[idx];
> +		ret = of_property_read_u32(np, par->property, &val);
> +
> +		/* property not found */
> +		if (ret == -EINVAL)
> +			continue;
> +
> +		/* use default value, when no value is specified */
> +		if (ret)
> +			val = par->default_value;
> +
> +		dev_dbg(dev, "found %s with value %u\n", par->property, val);
> +		cfg[ncfg] = pinconf_to_config_packed(par->param, val);
> +		ncfg++;
> +	}
> +
> +	ret = 0;
> +
> +	/* no configs found at qchip->npads */
> +	if (ncfg == 0) {
> +		*configs = NULL;
> +		*nconfigs = 0;
> +		goto out;
> +	}
> +
> +	/*
> +	 * Now limit the number of configs to the real number of
> +	 * found properties.
> +	 */
> +	*configs = kcalloc(ncfg, sizeof(unsigned long), GFP_KERNEL);
> +	if (!*configs) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	memcpy(*configs, cfg, ncfg * sizeof(unsigned long));
> +	*nconfigs = ncfg;
> +
> +out:
> +	return ret;
> +}
> +
> +static int pm8xxx_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 num_configs = 0;
> +	struct property *prop;
> +	const char *group;
> +	int ret;
> +
> +	ret = pm8xxx_parse_dt_config(pctldev->dev, np, &configs, &num_configs);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (!num_configs)
> +		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,
> +				num_configs, type);
> +		if (ret < 0)
> +			break;
> +	}
> +exit:
> +	kfree(configs);
> +	return ret;
> +}
> +
> +static int pm8xxx_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 = pm8xxx_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 pm8xxx_gpio_pinctrl_ops = {
> +	.get_groups_count	= pm8xxx_gpio_get_groups_count,
> +	.get_group_name		= pm8xxx_gpio_get_group_name,
> +	.dt_node_to_map		= pm8xxx_dt_node_to_map,
> +	.dt_free_map		= pinctrl_utils_dt_free_map,
> +};
> +
> +static int pm8xxx_get_functions_count(struct pinctrl_dev *pctldev)
> +{
> +	return ARRAY_SIZE(pm8xxx_gpio_functions);
> +}
> +
> +static const char *pm8xxx_get_function_name(struct pinctrl_dev *pctldev,
> +					 unsigned function)
> +{
> +	return pm8xxx_gpio_functions[function];
> +}
> +
> +static int pm8xxx_get_function_groups(struct pinctrl_dev *pctldev,
> +				   unsigned function,
> +				   const char * const **groups,
> +				   unsigned * const num_groups)
> +{
> +	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +
> +	*groups = pm8xxx_gpio_groups;
> +	*num_groups = pctrl->data->ngpio;
> +	return 0;
> +}
> +
> +static int pm8xxx_pinmux_enable(struct pinctrl_dev *pctldev,
> +			     unsigned function,
> +			     unsigned group)
> +{
> +	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[group];
> +	u8 val;
> +
> +	pin->function = function;
> +	val = pin->function << 1;
> +
> +	pm8xxx_gpio_write(pctrl, group, 4, val);
> +
> +	return 0;
> +}
> +
> +static const struct pinmux_ops pm8xxx_pinmux_ops = {
> +	.get_functions_count	= pm8xxx_get_functions_count,
> +	.get_function_name	= pm8xxx_get_function_name,
> +	.get_function_groups	= pm8xxx_get_function_groups,
> +	.enable			= pm8xxx_pinmux_enable,
> +};
> +
> +static int pm8xxx_gpio_config_get(struct pinctrl_dev *pctldev,
> +			  unsigned int offset,
> +			  unsigned long *config)
> +{
> +	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
> +	unsigned param = pinconf_to_config_param(*config);
> +	unsigned arg;
> +
> +	switch (param) {
> +	case PIN_CONFIG_BIAS_DISABLE:
> +		arg = pin->bias == PM8XXX_GPIO_BIAS_NP;
> +		break;
> +	case PIN_CONFIG_BIAS_PULL_DOWN:
> +		arg = pin->bias == PM8XXX_GPIO_BIAS_PD;
> +		break;
> +	case PM8XXX_PINCONF_PULL_UP:
> +		if (pin->bias >= PM8XXX_GPIO_BIAS_PU_30 &&
> +		    pin->bias <= PM8XXX_GPIO_BIAS_PU_1P5_30)
> +			arg = PMIC_GPIO_PULL_UP_30 + pin->bias;
> +		else
> +			arg = 0;
> +		break;
> +	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
> +		arg = pin->disable;
> +		break;
> +	case PIN_CONFIG_INPUT_ENABLE:
> +		arg = pin->direction == PM8XXX_GPIO_DIR_IN;
> +		break;
> +	case PIN_CONFIG_OUTPUT:
> +		arg = pin->output_value;
> +		break;
> +	case PIN_CONFIG_POWER_SOURCE:
> +		arg = pin->power_source;
> +		break;
> +	case PM8XXX_PINCONF_STRENGTH:
> +		arg = pin->output_strength;
> +		break;
> +	case PIN_CONFIG_DRIVE_PUSH_PULL:
> +		arg = pin->output_buffer == PM8XXX_GPIO_PUSH_PULL;
> +		break;
> +	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +		arg = pin->output_buffer == PM8XXX_GPIO_OPEN_DRAIN;
> +		break;
> +	default:
> +		dev_err(pctrl->dev,
> +			"unsupported config parameter: %x\n",
> +			param);
> +		return -EINVAL;
> +	}
> +
> +	*config = pinconf_to_config_packed(param, arg);
> +
> +	return 0;
> +}
> +
> +static int pm8xxx_gpio_config_set(struct pinctrl_dev *pctldev,
> +				  unsigned int offset,
> +				  unsigned long *configs,
> +				  unsigned num_configs)
> +{
> +	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
> +	unsigned param;
> +	unsigned arg;
> +	unsigned i;
> +	u8 banks = 0;
> +	u8 val;
> +
> +	for (i = 0; i < num_configs; i++) {
> +		param = pinconf_to_config_param(configs[i]);
> +		arg = pinconf_to_config_argument(configs[i]);
> +
> +		switch (param) {
> +		case PIN_CONFIG_BIAS_DISABLE:
> +			pin->bias = PM8XXX_GPIO_BIAS_NP;
> +			banks |= BIT(2);
> +			pin->disable = 0;
> +			banks |= BIT(3);
> +			break;
> +		case PIN_CONFIG_BIAS_PULL_DOWN:
> +			pin->bias = PM8XXX_GPIO_BIAS_PD;
> +			banks |= BIT(2);
> +			pin->disable = 0;
> +			banks |= BIT(3);
> +			break;
> +		case PM8XXX_PINCONF_PULL_UP:
> +			if (arg < PMIC_GPIO_PULL_UP_30 ||
> +			    arg > PMIC_GPIO_PULL_UP_1P5_30) {
> +				dev_err(pctrl->dev, "invalid pull-up level\n");
> +				return -EINVAL;
> +			}
> +			pin->bias = arg - PM8XXX_GPIO_BIAS_PU_30;
> +			banks |= BIT(2);
> +			pin->disable = 0;
> +			banks |= BIT(3);
> +			break;
> +		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
> +			pin->disable = 1;
> +			banks |= BIT(3);
> +			break;
> +		case PIN_CONFIG_INPUT_ENABLE:
> +			pin->direction = PM8XXX_GPIO_DIR_IN;
> +			banks |= BIT(1);
> +			break;
> +		case PIN_CONFIG_OUTPUT:
> +			pin->direction = PM8XXX_GPIO_DIR_OUT;
> +			pin->output_value = !!arg;
> +			banks |= BIT(1);
> +			break;
> +		case PIN_CONFIG_POWER_SOURCE:
> +			pin->power_source = arg;
> +			banks |= BIT(0);
> +			break;
> +		case PM8XXX_PINCONF_STRENGTH:
> +			if (arg > PMIC_GPIO_STRENGTH_LOW) {
> +				dev_err(pctrl->dev, "invalid drive strength\n");
> +				return -EINVAL;
> +			}
> +			pin->output_strength = arg;
> +			banks |= BIT(3);
> +			break;
> +		case PIN_CONFIG_DRIVE_PUSH_PULL:
> +			pin->output_buffer = PM8XXX_GPIO_PUSH_PULL;
> +			banks |= BIT(1);
> +			break;
> +		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +			pin->output_buffer = PM8XXX_GPIO_OPEN_DRAIN;
> +			banks |= BIT(1);
> +			break;
> +		default:
> +			dev_err(pctrl->dev,
> +					"unsupported config parameter: %x\n",
> +					param);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (banks & BIT(0))
> +		pm8xxx_gpio_write(pctrl, offset, 0, pin->power_source << 1);
> +
> +	if (banks & BIT(1)) {
> +		val = pin->direction << 2;
> +		val |= pin->output_buffer << 1;
> +		val |= pin->output_value;
> +		pm8xxx_gpio_write(pctrl, offset, 1, val);
> +	}
> +
> +	if (banks & BIT(2)) {
> +		val = pin->bias << 1;
> +		pm8xxx_gpio_write(pctrl, offset, 2, val);
> +	}
> +
> +	if (banks & BIT(3)) {
> +		val = pin->output_strength << 2;
> +		val |= pin->disable;
> +		pm8xxx_gpio_write(pctrl, offset, 3, val);
> +	}
> +
> +	if (banks & BIT(4)) {
> +		val = pin->function << 1;
> +		pm8xxx_gpio_write(pctrl, offset, 4, val);
> +	}
> +
> +	if (banks & BIT(5)) {
> +		val = 0;
> +		if (pin->non_inverted)
> +			val |= BIT(3);
> +		pm8xxx_gpio_write(pctrl, offset, 5, val);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct pinconf_ops pm8xxx_gpio_pinconf_ops = {
> +	.pin_config_group_get = pm8xxx_gpio_config_get,
> +	.pin_config_group_set = pm8xxx_gpio_config_set,
> +};
> +
> +static struct pinctrl_desc pm8xxx_gpio_desc = {
> +	.pctlops = &pm8xxx_gpio_pinctrl_ops,
> +	.pmxops = &pm8xxx_pinmux_ops,
> +	.confops = &pm8xxx_gpio_pinconf_ops,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int pm8xxx_gpio_direction_input(struct gpio_chip *chip,
> +				       unsigned offset)
> +{
> +	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
> +	u8 val;
> +
> +	pin->direction = PM8XXX_GPIO_DIR_IN;
> +	val = pin->direction << 2;
> +
> +	pm8xxx_gpio_write(pctrl, offset, 1, val);
> +
> +	return 0;
> +}
> +
> +static int pm8xxx_gpio_direction_output(struct gpio_chip *chip,
> +					unsigned offset,
> +					int value)
> +{
> +	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
> +	u8 val;
> +
> +	pin->direction = PM8XXX_GPIO_DIR_OUT;
> +	pin->output_value = !!value;
> +
> +	val = pin->direction << 2;
> +	val |= pin->output_buffer << 1;
> +	val |= pin->output_value;
> +
> +	pm8xxx_gpio_write(pctrl, offset, 1, val);
> +
> +	return 0;
> +}
> +
> +static int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset)
> +{
> +	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
> +
> +	if (pin->direction == PM8XXX_GPIO_DIR_OUT)
> +		return pin->output_value;
> +
> +	return pm8xxx_read_irq_status(pin->irq);
> +}
> +
> +static void pm8xxx_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
> +{
> +	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(gc);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
> +	u8 val;
> +
> +	pin->output_value = !!value;
> +
> +	val = pin->direction << 2;
> +	val |= pin->output_buffer << 1;
> +	val |= pin->output_value;
> +
> +	pm8xxx_gpio_write(pctrl, offset, 1, val);
> +}
> +
> +static int pm8xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
> +{
> +	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
> +
> +	return pin->irq;
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +#include <linux/seq_file.h>
> +
> +static void pm8xxx_gpio_dbg_show_one(struct seq_file *s,
> +				  struct pinctrl_dev *pctldev,
> +				  struct gpio_chip *chip,
> +				  unsigned offset,
> +				  unsigned gpio)
> +{
> +	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
> +
> +	static const char * const directions[] = {
> +		"off", "out", "in", "both"
> +	};
> +	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"
> +	};
> +	static const char * const strengths[] = {
> +		"no", "high", "medium", "low"
> +	};
> +
> +	seq_printf(s, " gpio%-2d:", offset + 1);
> +	if (pin->disable) {
> +		seq_puts(s, " ---");
> +	} else {
> +		seq_printf(s, " %-4s", directions[pin->direction]);
> +		seq_printf(s, " %-7s", pm8xxx_gpio_functions[pin->function]);
> +		seq_printf(s, " VIN%d", pin->power_source);
> +		seq_printf(s, " %-27s", biases[pin->bias]);
> +		seq_printf(s, " %-10s", buffer_types[pin->output_buffer]);
> +		seq_printf(s, " %-4s", pin->output_value ? "high" : "low");
> +		seq_printf(s, " %-7s", strengths[pin->output_strength]);
> +		if (!pin->non_inverted)
> +			seq_puts(s, " inverted");
> +	}
> +}
> +
> +static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
> +{
> +	unsigned gpio = chip->base;
> +	unsigned i;
> +
> +	for (i = 0; i < chip->ngpio; i++, gpio++) {
> +		pm8xxx_gpio_dbg_show_one(s, NULL, chip, i, gpio);
> +		seq_puts(s, "\n");
> +	}
> +}
> +
> +#else
> +#define msm_gpio_dbg_show NULL
> +#endif
> +
> +static struct gpio_chip pm8xxx_gpio_template = {
> +	.direction_input = pm8xxx_gpio_direction_input,
> +	.direction_output = pm8xxx_gpio_direction_output,
> +	.get = pm8xxx_gpio_get,
> +	.set = pm8xxx_gpio_set,
> +	.to_irq = pm8xxx_gpio_to_irq,
> +	.dbg_show = pm8xxx_gpio_dbg_show,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int pm8xxx_gpio_populate(struct pm8xxx_gpio *pctrl)
> +{
> +	struct pm8xxx_gpio_pin *pin;
> +	int val;
> +	int i;
> +
> +	for (i = 0; i < pctrl->data->ngpio; i++) {
> +		pin = &pctrl->pins[i];
> +
> +		val = pm8xxx_gpio_read(pctrl, i, 0);
> +		if (val < 0)
> +			return val;
> +
> +		pin->power_source = (val >> 1) & 0x7;
> +
> +		val = pm8xxx_gpio_read(pctrl, i, 1);
> +		if (val < 0)
> +			return val;
> +
> +		pin->direction = (val >> 2) & 0x3;
> +		pin->output_buffer = !!(val & BIT(1));
> +		pin->output_value = val & BIT(0);
> +
> +		val = pm8xxx_gpio_read(pctrl, i, 2);
> +		if (val < 0)
> +			return val;
> +
> +		pin->bias = (val >> 1) & 0x7;
> +
> +		val = pm8xxx_gpio_read(pctrl, i, 3);
> +		if (val < 0)
> +			return val;
> +
> +		pin->output_strength = (val >> 2) & 0x3;
> +		pin->disable = val & BIT(0);
> +
> +		val = pm8xxx_gpio_read(pctrl, i, 4);
> +		if (val < 0)
> +			return val;
> +
> +		pin->function = (val >> 1) & 0x7;
> +
> +		val = pm8xxx_gpio_read(pctrl, i, 5);
> +		if (val < 0)
> +			return val;
> +
> +		pin->non_inverted = !!(val & BIT(3));
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct pm8xxx_gpio_data pm8018_gpio_data = {
> +	.ngpio = 6,
> +};
> +
> +static const struct pm8xxx_gpio_data pm8038_gpio_data = {
> +	.ngpio = 12,
> +};
> +
> +static const struct pm8xxx_gpio_data pm8058_gpio_data = {
> +	.ngpio = 40,
> +};
> +static const struct pm8xxx_gpio_data pm8917_gpio_data = {
> +	.ngpio = 38,
> +};
> +
> +static const struct pm8xxx_gpio_data pm8921_gpio_data = {
> +	.ngpio = 44,
> +};
> +
> +static const struct of_device_id pm8xxx_gpio_of_match[] = {
> +	{ .compatible = "qcom,pm8018-gpio", .data = &pm8018_gpio_data },
> +	{ .compatible = "qcom,pm8038-gpio", .data = &pm8038_gpio_data },
> +	{ .compatible = "qcom,pm8058-gpio", .data = &pm8058_gpio_data },
> +	{ .compatible = "qcom,pm8917-gpio", .data = &pm8917_gpio_data },
> +	{ .compatible = "qcom,pm8921-gpio", .data = &pm8921_gpio_data },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
> +
> +static int pm8xxx_gpio_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct pm8xxx_gpio *pctrl;
> +	int ret;
> +	int i;
> +
> +	match = of_match_node(pm8xxx_gpio_of_match, pdev->dev.of_node);
> +	if (!match)
> +		return -ENXIO;
> +
> +	pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
> +	if (!pctrl)
> +		return -ENOMEM;
> +
> +	pctrl->dev = &pdev->dev;
> +	pctrl->data = match->data;
> +
> +	BUG_ON(pctrl->data->ngpio > PM8XXX_MAX_GPIOS);
> +
> +	pctrl->chip = pm8xxx_gpio_template;
> +	pctrl->chip.base = -1;
> +	pctrl->chip.dev = &pdev->dev;
> +	pctrl->chip.of_node = pdev->dev.of_node;
> +	pctrl->chip.label = dev_name(pctrl->dev);
> +	pctrl->chip.ngpio = pctrl->data->ngpio;
> +
> +	pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> +	if (!pctrl->regmap) {
> +		dev_err(&pdev->dev, "parent regmap unavailable\n");
> +		return -ENXIO;
> +	}
> +
> +	for (i = 0; i < pctrl->data->ngpio; i++) {
> +		ret = platform_get_irq(pdev, i);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev,
> +				"missing interrupts for pin %d\n", i);
> +			return ret;
> +		}
> +
> +		pctrl->pins[i].irq = ret;
> +	}
> +
> +	ret = pm8xxx_gpio_populate(pctrl);
> +	if (ret)
> +		return ret;
> +
> +	pm8xxx_gpio_desc.name = dev_name(&pdev->dev);
> +	pctrl->pctrl = pinctrl_register(&pm8xxx_gpio_desc, &pdev->dev, pctrl);
> +	if (!pctrl->pctrl) {
> +		dev_err(&pdev->dev, "couldn't register pm8xxx gpio driver\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = gpiochip_add(&pctrl->chip);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed register gpiochip\n");
> +		goto unregister_pinctrl;
> +	}
> +
> +	ret = gpiochip_add_pin_range(&pctrl->chip,
> +				     dev_name(pctrl->dev),
> +				     1, 0, pctrl->data->ngpio);
> +	if (ret) {
> +		dev_err(pctrl->dev, "failed to add pin range\n");
> +		goto unregister_gpiochip;
> +	}
> +
> +	platform_set_drvdata(pdev, pctrl);
> +
> +	dev_dbg(&pdev->dev, "Qualcomm pm8xxx gpio driver probed\n");
> +
> +	return 0;
> +
> +unregister_pinctrl:
> +	pinctrl_unregister(pctrl->pctrl);
> +
> +unregister_gpiochip:
> +	if (gpiochip_remove(&pctrl->chip))
> +		dev_err(&pdev->dev, "unable to unregister gpiochip\n");
> +
> +	return ret;
> +}
> +
> +static int pm8xxx_gpio_remove(struct platform_device *pdev)
> +{
> +	struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev);
> +
> +	gpiochip_remove(&pctrl->chip);
> +
> +	pinctrl_unregister(pctrl->pctrl);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver pm8xxx_gpio_driver = {
> +	.driver = {
> +		.name = "ssbi-pmic-gpio",
> +		.owner = THIS_MODULE,
> +		.of_match_table = pm8xxx_gpio_of_match,
> +	},
> +	.probe = pm8xxx_gpio_probe,
> +	.remove = pm8xxx_gpio_remove,
> +};
> +
> +module_platform_driver(pm8xxx_gpio_driver);
> +
> +MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org>");
> +MODULE_DESCRIPTION("Qualcomm SSBI PMIC GPIO driver");
> +MODULE_LICENSE("GPL v2");
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v3 2/6] pinctrl: Introduce pinctrl driver for Qualcomm SSBI PMIC's
@ 2014-08-20  8:06       ` Srinivas Kandagatla
  0 siblings, 0 replies; 27+ messages in thread
From: Srinivas Kandagatla @ 2014-08-20  8:06 UTC (permalink / raw)
  To: Ivan T. Ivanov, Linus Walleij, Grant Likely, Rob Herring
  Cc: Bjorn Andersson, linux-arm-msm, devicetree, linux-kernel

Hi Bjorn,

Two things which I noticed while trying out this driver to drive a reset 
line.

1> gpio numbering for pinconf vs gpio are not consistent, they differ by 
an offset of 1.

For example to control GPIO43 I had to do something like this in pinconf:

wlan_default_gpios: wlan-gpios {
	pios {
		pins = "gpio43";
		function = "normal";
		bias-disable;
		power-source = <PM8921_GPIO_S4>;
	};
};


for same pin for gpio I had to do:
	reset-gpio = <&pm8921_gpio 42 GPIO_ACTIVE_LOW>;

This offset by 1 is bit confusing and can easily lead to errors which 
are hard to find.

I think this should be easy to fix..

2> Looking back at v3.4 kernel, for gpio modes, BIT(0) of bank 0 is set 
to enable gpio mode. without this bit driver does not work for output pins.

here is the patch which fixes this:

 From 5a5c5171a3371cf38ccd157922ea140bfd0253d5 Mon Sep 17 00:00:00 2001
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Date: Wed, 20 Aug 2014 07:18:03 +0100
Subject: [PATCH] pinctrl:qcom:ssbi: Enable gpio mode

Looking back at 3.4 kernel driver, it looks like the gpio enable bit is
missing in this driver.

This patch is just a hack to get the wlan reset line working.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
  drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c | 4 +++-
  1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c 
b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
index 27e5ec9..2633ea1 100644
--- a/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
@@ -49,6 +49,7 @@
  #define SSBI_REG_ADDR_GPIO_BASE		0x150
  #define SSBI_REG_ADDR_GPIO(n)		(SSBI_REG_ADDR_GPIO_BASE + n)

+#define	PM8XXX_GPIO_MODE_ENABLE		BIT(0)
  #define PM8XXX_GPIO_WRITE		BIT(7)

  #define PM8XXX_MAX_GPIOS		44
@@ -493,7 +494,8 @@ static int pm8xxx_gpio_config_set(struct pinctrl_dev 
*pctldev,
  	}

  	if (banks & BIT(0))
-		pm8xxx_gpio_write(pctrl, offset, 0, pin->power_source << 1);
+		pm8xxx_gpio_write(pctrl, offset, 0, pin->power_source << 1 |
+				  PM8XXX_GPIO_MODE_ENABLE);

  	if (banks & BIT(1)) {
  		val = pin->direction << 2;
-- 
2.0.2

with this change am able to reset WLAN chip on IFC6410 which is 
connected to gpio43 of pmic.

thanks,
srini
On 11/08/14 16:40, Ivan T. Ivanov wrote:
> From: Bjorn Andersson <bjorn.andersson@sonymobile.com>
>
> This introduces a pinctrl, pinconf, pinmux and gpio driver for the gpio
> block found in pm8018, pm8038, pm8058, pm8917 and pm8921 pmics from
> Qualcomm.
>
> Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
> Signed-off-by: Ivan T. Ivanov <iivanov@mm-sol.com>
> ---
>   drivers/pinctrl/qcom/Kconfig             |  11 +
>   drivers/pinctrl/qcom/Makefile            |   1 +
>   drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c | 870 +++++++++++++++++++++++++++++++
>   3 files changed, 882 insertions(+)
>   create mode 100644 drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
>
> diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
> index d160a71..6bd4e4d 100644
> --- a/drivers/pinctrl/qcom/Kconfig
> +++ b/drivers/pinctrl/qcom/Kconfig
> @@ -39,4 +39,15 @@ 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_SSBI_PMIC
> +       tristate "Qualcomm SSBI PMIC pin controller driver"
> +       depends on GPIOLIB && OF
> +       select PINCONF
> +       select PINMUX
> +       select GENERIC_PINCONF
> +       help
> +         This is the pinctrl, pinmux, pinconf and gpiolib driver for the
> +         Qualcomm GPIO blocks found in the pm8018, pm8038, pm8058, pm8917 and
> +         pm8921 pmics from Qualcomm.
> +
>   endif
> diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
> index 2a02602..8faa2ca 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_SSBI_PMIC) += pinctrl-ssbi-pmic.o
> diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
> new file mode 100644
> index 0000000..9a1b443
> --- /dev/null
> +++ b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
> @@ -0,0 +1,870 @@
> +/*
> + * Copyright (c) 2014, Sony Mobile Communications AB.
> + * Copyright (c) 2013, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/pinctrl/pinconf-generic.h>
> +#include <linux/slab.h>
> +#include <linux/regmap.h>
> +#include <linux/gpio.h>
> +#include <linux/mfd/pm8921-core.h>
> +
> +#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
> +
> +#include "../core.h"
> +#include "../pinconf.h"
> +#include "../pinctrl-utils.h"
> +
> +/* direction */
> +#define PM8XXX_GPIO_DIR_OUT		BIT(0)
> +#define PM8XXX_GPIO_DIR_IN		BIT(1)
> +
> +/* output buffer */
> +#define PM8XXX_GPIO_PUSH_PULL		0
> +#define PM8XXX_GPIO_OPEN_DRAIN		1
> +
> +/* bias */
> +#define PM8XXX_GPIO_BIAS_PU_30		0
> +#define PM8XXX_GPIO_BIAS_PU_1P5		1
> +#define PM8XXX_GPIO_BIAS_PU_31P5	2
> +#define PM8XXX_GPIO_BIAS_PU_1P5_30	3
> +#define PM8XXX_GPIO_BIAS_PD		4
> +#define PM8XXX_GPIO_BIAS_NP		5
> +
> +/* GPIO registers */
> +#define SSBI_REG_ADDR_GPIO_BASE		0x150
> +#define SSBI_REG_ADDR_GPIO(n)		(SSBI_REG_ADDR_GPIO_BASE + n)
> +
> +#define PM8XXX_GPIO_WRITE		BIT(7)
> +
> +#define PM8XXX_MAX_GPIOS		44
> +
> +/* Qualcomm specific pin configurations */
> +#define PM8XXX_PINCONF_PULL_UP		(PIN_CONFIG_END + 1)
> +#define PM8XXX_PINCONF_STRENGTH		(PIN_CONFIG_END + 2)
> +
> +struct pm8xxx_pinbindings {
> +	const char *property;
> +	unsigned param;
> +	u32 default_value;
> +};
> +static struct pm8xxx_pinbindings pm8xxx_pinbindings[] = {
> +	/* PMIC_GPIO_PULL_UP_30...  */
> +	{"qcom,pull-up-strength",	PM8XXX_PINCONF_PULL_UP, 0},
> +	/* PMIC_GPIO_STRENGTH_NO... */
> +	{"qcom,drive-strength",		PM8XXX_PINCONF_STRENGTH, 0},
> +};
> +
> +struct pm8xxx_gpio_pin {
> +	int irq;
> +
> +	u8 power_source;
> +	u8 direction;
> +	u8 output_buffer;
> +	u8 output_value;
> +	u8 bias;
> +	u8 output_strength;
> +	u8 disable;
> +	u8 function;
> +	u8 non_inverted;
> +};
> +
> +struct pm8xxx_gpio_data {
> +	int ngpio;
> +};
> +
> +struct pm8xxx_gpio {
> +	struct device *dev;
> +	struct regmap *regmap;
> +	struct pinctrl_dev *pctrl;
> +	struct gpio_chip chip;
> +
> +	const struct pm8xxx_gpio_data *data;
> +
> +	struct pm8xxx_gpio_pin pins[PM8XXX_MAX_GPIOS];
> +};
> +
> +static inline struct pm8xxx_gpio *to_pm8xxx_gpio(struct gpio_chip *chip)
> +{
> +	return container_of(chip, struct pm8xxx_gpio, chip);
> +};
> +
> +static const char * const pm8xxx_gpio_groups[PM8XXX_MAX_GPIOS] = {
> +	"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",
> +	"gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43",
> +	"gpio44",
> +};
> +
> +static const char * const pm8xxx_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 pm8xxx_gpio_read(struct pm8xxx_gpio *pctrl, int pin, int bank)
> +{
> +	int reg = SSBI_REG_ADDR_GPIO(pin);
> +	unsigned int val = bank << 4;
> +	int ret;
> +
> +	ret = regmap_write(pctrl->regmap, reg, val);
> +	if (ret) {
> +		dev_err(pctrl->dev,
> +			"failed to select bank %d of pin %d\n", bank, pin);
> +		return ret;
> +	}
> +
> +	ret = regmap_read(pctrl->regmap, reg, &val);
> +	if (ret) {
> +		dev_err(pctrl->dev,
> +			"failed to read register %d of pin %d\n", bank, pin);
> +		return ret;
> +	}
> +
> +	return val;
> +}
> +
> +static int pm8xxx_gpio_write(struct pm8xxx_gpio *pctrl,
> +			     int pin, int bank, u8 val)
> +{
> +	int ret;
> +
> +	val |= PM8XXX_GPIO_WRITE;
> +	val |= bank << 4;
> +
> +	ret = regmap_write(pctrl->regmap, SSBI_REG_ADDR_GPIO(pin), val);
> +	if (ret)
> +		dev_err(pctrl->dev, "failed to write register\n");
> +
> +	return ret;
> +}
> +
> +static int pm8xxx_gpio_get_groups_count(struct pinctrl_dev *pctldev)
> +{
> +	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +
> +	return pctrl->data->ngpio;
> +}
> +
> +static const char *pm8xxx_gpio_get_group_name(struct pinctrl_dev *pctldev,
> +				      unsigned group)
> +{
> +	return pm8xxx_gpio_groups[group];
> +}
> +
> +static int pm8xxx_parse_dt_config(struct device *dev, struct device_node *np,
> +			unsigned long **configs, unsigned int *nconfigs)
> +{
> +	struct pm8xxx_pinbindings *par;
> +	unsigned long cfg[ARRAY_SIZE(pm8xxx_pinbindings)];
> +	unsigned int ncfg = 0;
> +	int ret, idx;
> +	u32 val;
> +
> +	if (!np)
> +		return -EINVAL;
> +
> +	for (idx = 0; idx < ARRAY_SIZE(pm8xxx_pinbindings); idx++) {
> +		par = &pm8xxx_pinbindings[idx];
> +		ret = of_property_read_u32(np, par->property, &val);
> +
> +		/* property not found */
> +		if (ret == -EINVAL)
> +			continue;
> +
> +		/* use default value, when no value is specified */
> +		if (ret)
> +			val = par->default_value;
> +
> +		dev_dbg(dev, "found %s with value %u\n", par->property, val);
> +		cfg[ncfg] = pinconf_to_config_packed(par->param, val);
> +		ncfg++;
> +	}
> +
> +	ret = 0;
> +
> +	/* no configs found at qchip->npads */
> +	if (ncfg == 0) {
> +		*configs = NULL;
> +		*nconfigs = 0;
> +		goto out;
> +	}
> +
> +	/*
> +	 * Now limit the number of configs to the real number of
> +	 * found properties.
> +	 */
> +	*configs = kcalloc(ncfg, sizeof(unsigned long), GFP_KERNEL);
> +	if (!*configs) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	memcpy(*configs, cfg, ncfg * sizeof(unsigned long));
> +	*nconfigs = ncfg;
> +
> +out:
> +	return ret;
> +}
> +
> +static int pm8xxx_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 num_configs = 0;
> +	struct property *prop;
> +	const char *group;
> +	int ret;
> +
> +	ret = pm8xxx_parse_dt_config(pctldev->dev, np, &configs, &num_configs);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (!num_configs)
> +		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,
> +				num_configs, type);
> +		if (ret < 0)
> +			break;
> +	}
> +exit:
> +	kfree(configs);
> +	return ret;
> +}
> +
> +static int pm8xxx_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 = pm8xxx_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 pm8xxx_gpio_pinctrl_ops = {
> +	.get_groups_count	= pm8xxx_gpio_get_groups_count,
> +	.get_group_name		= pm8xxx_gpio_get_group_name,
> +	.dt_node_to_map		= pm8xxx_dt_node_to_map,
> +	.dt_free_map		= pinctrl_utils_dt_free_map,
> +};
> +
> +static int pm8xxx_get_functions_count(struct pinctrl_dev *pctldev)
> +{
> +	return ARRAY_SIZE(pm8xxx_gpio_functions);
> +}
> +
> +static const char *pm8xxx_get_function_name(struct pinctrl_dev *pctldev,
> +					 unsigned function)
> +{
> +	return pm8xxx_gpio_functions[function];
> +}
> +
> +static int pm8xxx_get_function_groups(struct pinctrl_dev *pctldev,
> +				   unsigned function,
> +				   const char * const **groups,
> +				   unsigned * const num_groups)
> +{
> +	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +
> +	*groups = pm8xxx_gpio_groups;
> +	*num_groups = pctrl->data->ngpio;
> +	return 0;
> +}
> +
> +static int pm8xxx_pinmux_enable(struct pinctrl_dev *pctldev,
> +			     unsigned function,
> +			     unsigned group)
> +{
> +	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[group];
> +	u8 val;
> +
> +	pin->function = function;
> +	val = pin->function << 1;
> +
> +	pm8xxx_gpio_write(pctrl, group, 4, val);
> +
> +	return 0;
> +}
> +
> +static const struct pinmux_ops pm8xxx_pinmux_ops = {
> +	.get_functions_count	= pm8xxx_get_functions_count,
> +	.get_function_name	= pm8xxx_get_function_name,
> +	.get_function_groups	= pm8xxx_get_function_groups,
> +	.enable			= pm8xxx_pinmux_enable,
> +};
> +
> +static int pm8xxx_gpio_config_get(struct pinctrl_dev *pctldev,
> +			  unsigned int offset,
> +			  unsigned long *config)
> +{
> +	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
> +	unsigned param = pinconf_to_config_param(*config);
> +	unsigned arg;
> +
> +	switch (param) {
> +	case PIN_CONFIG_BIAS_DISABLE:
> +		arg = pin->bias == PM8XXX_GPIO_BIAS_NP;
> +		break;
> +	case PIN_CONFIG_BIAS_PULL_DOWN:
> +		arg = pin->bias == PM8XXX_GPIO_BIAS_PD;
> +		break;
> +	case PM8XXX_PINCONF_PULL_UP:
> +		if (pin->bias >= PM8XXX_GPIO_BIAS_PU_30 &&
> +		    pin->bias <= PM8XXX_GPIO_BIAS_PU_1P5_30)
> +			arg = PMIC_GPIO_PULL_UP_30 + pin->bias;
> +		else
> +			arg = 0;
> +		break;
> +	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
> +		arg = pin->disable;
> +		break;
> +	case PIN_CONFIG_INPUT_ENABLE:
> +		arg = pin->direction == PM8XXX_GPIO_DIR_IN;
> +		break;
> +	case PIN_CONFIG_OUTPUT:
> +		arg = pin->output_value;
> +		break;
> +	case PIN_CONFIG_POWER_SOURCE:
> +		arg = pin->power_source;
> +		break;
> +	case PM8XXX_PINCONF_STRENGTH:
> +		arg = pin->output_strength;
> +		break;
> +	case PIN_CONFIG_DRIVE_PUSH_PULL:
> +		arg = pin->output_buffer == PM8XXX_GPIO_PUSH_PULL;
> +		break;
> +	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +		arg = pin->output_buffer == PM8XXX_GPIO_OPEN_DRAIN;
> +		break;
> +	default:
> +		dev_err(pctrl->dev,
> +			"unsupported config parameter: %x\n",
> +			param);
> +		return -EINVAL;
> +	}
> +
> +	*config = pinconf_to_config_packed(param, arg);
> +
> +	return 0;
> +}
> +
> +static int pm8xxx_gpio_config_set(struct pinctrl_dev *pctldev,
> +				  unsigned int offset,
> +				  unsigned long *configs,
> +				  unsigned num_configs)
> +{
> +	struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
> +	unsigned param;
> +	unsigned arg;
> +	unsigned i;
> +	u8 banks = 0;
> +	u8 val;
> +
> +	for (i = 0; i < num_configs; i++) {
> +		param = pinconf_to_config_param(configs[i]);
> +		arg = pinconf_to_config_argument(configs[i]);
> +
> +		switch (param) {
> +		case PIN_CONFIG_BIAS_DISABLE:
> +			pin->bias = PM8XXX_GPIO_BIAS_NP;
> +			banks |= BIT(2);
> +			pin->disable = 0;
> +			banks |= BIT(3);
> +			break;
> +		case PIN_CONFIG_BIAS_PULL_DOWN:
> +			pin->bias = PM8XXX_GPIO_BIAS_PD;
> +			banks |= BIT(2);
> +			pin->disable = 0;
> +			banks |= BIT(3);
> +			break;
> +		case PM8XXX_PINCONF_PULL_UP:
> +			if (arg < PMIC_GPIO_PULL_UP_30 ||
> +			    arg > PMIC_GPIO_PULL_UP_1P5_30) {
> +				dev_err(pctrl->dev, "invalid pull-up level\n");
> +				return -EINVAL;
> +			}
> +			pin->bias = arg - PM8XXX_GPIO_BIAS_PU_30;
> +			banks |= BIT(2);
> +			pin->disable = 0;
> +			banks |= BIT(3);
> +			break;
> +		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
> +			pin->disable = 1;
> +			banks |= BIT(3);
> +			break;
> +		case PIN_CONFIG_INPUT_ENABLE:
> +			pin->direction = PM8XXX_GPIO_DIR_IN;
> +			banks |= BIT(1);
> +			break;
> +		case PIN_CONFIG_OUTPUT:
> +			pin->direction = PM8XXX_GPIO_DIR_OUT;
> +			pin->output_value = !!arg;
> +			banks |= BIT(1);
> +			break;
> +		case PIN_CONFIG_POWER_SOURCE:
> +			pin->power_source = arg;
> +			banks |= BIT(0);
> +			break;
> +		case PM8XXX_PINCONF_STRENGTH:
> +			if (arg > PMIC_GPIO_STRENGTH_LOW) {
> +				dev_err(pctrl->dev, "invalid drive strength\n");
> +				return -EINVAL;
> +			}
> +			pin->output_strength = arg;
> +			banks |= BIT(3);
> +			break;
> +		case PIN_CONFIG_DRIVE_PUSH_PULL:
> +			pin->output_buffer = PM8XXX_GPIO_PUSH_PULL;
> +			banks |= BIT(1);
> +			break;
> +		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +			pin->output_buffer = PM8XXX_GPIO_OPEN_DRAIN;
> +			banks |= BIT(1);
> +			break;
> +		default:
> +			dev_err(pctrl->dev,
> +					"unsupported config parameter: %x\n",
> +					param);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (banks & BIT(0))
> +		pm8xxx_gpio_write(pctrl, offset, 0, pin->power_source << 1);
> +
> +	if (banks & BIT(1)) {
> +		val = pin->direction << 2;
> +		val |= pin->output_buffer << 1;
> +		val |= pin->output_value;
> +		pm8xxx_gpio_write(pctrl, offset, 1, val);
> +	}
> +
> +	if (banks & BIT(2)) {
> +		val = pin->bias << 1;
> +		pm8xxx_gpio_write(pctrl, offset, 2, val);
> +	}
> +
> +	if (banks & BIT(3)) {
> +		val = pin->output_strength << 2;
> +		val |= pin->disable;
> +		pm8xxx_gpio_write(pctrl, offset, 3, val);
> +	}
> +
> +	if (banks & BIT(4)) {
> +		val = pin->function << 1;
> +		pm8xxx_gpio_write(pctrl, offset, 4, val);
> +	}
> +
> +	if (banks & BIT(5)) {
> +		val = 0;
> +		if (pin->non_inverted)
> +			val |= BIT(3);
> +		pm8xxx_gpio_write(pctrl, offset, 5, val);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct pinconf_ops pm8xxx_gpio_pinconf_ops = {
> +	.pin_config_group_get = pm8xxx_gpio_config_get,
> +	.pin_config_group_set = pm8xxx_gpio_config_set,
> +};
> +
> +static struct pinctrl_desc pm8xxx_gpio_desc = {
> +	.pctlops = &pm8xxx_gpio_pinctrl_ops,
> +	.pmxops = &pm8xxx_pinmux_ops,
> +	.confops = &pm8xxx_gpio_pinconf_ops,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int pm8xxx_gpio_direction_input(struct gpio_chip *chip,
> +				       unsigned offset)
> +{
> +	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
> +	u8 val;
> +
> +	pin->direction = PM8XXX_GPIO_DIR_IN;
> +	val = pin->direction << 2;
> +
> +	pm8xxx_gpio_write(pctrl, offset, 1, val);
> +
> +	return 0;
> +}
> +
> +static int pm8xxx_gpio_direction_output(struct gpio_chip *chip,
> +					unsigned offset,
> +					int value)
> +{
> +	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
> +	u8 val;
> +
> +	pin->direction = PM8XXX_GPIO_DIR_OUT;
> +	pin->output_value = !!value;
> +
> +	val = pin->direction << 2;
> +	val |= pin->output_buffer << 1;
> +	val |= pin->output_value;
> +
> +	pm8xxx_gpio_write(pctrl, offset, 1, val);
> +
> +	return 0;
> +}
> +
> +static int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset)
> +{
> +	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
> +
> +	if (pin->direction == PM8XXX_GPIO_DIR_OUT)
> +		return pin->output_value;
> +
> +	return pm8xxx_read_irq_status(pin->irq);
> +}
> +
> +static void pm8xxx_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
> +{
> +	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(gc);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
> +	u8 val;
> +
> +	pin->output_value = !!value;
> +
> +	val = pin->direction << 2;
> +	val |= pin->output_buffer << 1;
> +	val |= pin->output_value;
> +
> +	pm8xxx_gpio_write(pctrl, offset, 1, val);
> +}
> +
> +static int pm8xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
> +{
> +	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
> +
> +	return pin->irq;
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +#include <linux/seq_file.h>
> +
> +static void pm8xxx_gpio_dbg_show_one(struct seq_file *s,
> +				  struct pinctrl_dev *pctldev,
> +				  struct gpio_chip *chip,
> +				  unsigned offset,
> +				  unsigned gpio)
> +{
> +	struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
> +	struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
> +
> +	static const char * const directions[] = {
> +		"off", "out", "in", "both"
> +	};
> +	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"
> +	};
> +	static const char * const strengths[] = {
> +		"no", "high", "medium", "low"
> +	};
> +
> +	seq_printf(s, " gpio%-2d:", offset + 1);
> +	if (pin->disable) {
> +		seq_puts(s, " ---");
> +	} else {
> +		seq_printf(s, " %-4s", directions[pin->direction]);
> +		seq_printf(s, " %-7s", pm8xxx_gpio_functions[pin->function]);
> +		seq_printf(s, " VIN%d", pin->power_source);
> +		seq_printf(s, " %-27s", biases[pin->bias]);
> +		seq_printf(s, " %-10s", buffer_types[pin->output_buffer]);
> +		seq_printf(s, " %-4s", pin->output_value ? "high" : "low");
> +		seq_printf(s, " %-7s", strengths[pin->output_strength]);
> +		if (!pin->non_inverted)
> +			seq_puts(s, " inverted");
> +	}
> +}
> +
> +static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
> +{
> +	unsigned gpio = chip->base;
> +	unsigned i;
> +
> +	for (i = 0; i < chip->ngpio; i++, gpio++) {
> +		pm8xxx_gpio_dbg_show_one(s, NULL, chip, i, gpio);
> +		seq_puts(s, "\n");
> +	}
> +}
> +
> +#else
> +#define msm_gpio_dbg_show NULL
> +#endif
> +
> +static struct gpio_chip pm8xxx_gpio_template = {
> +	.direction_input = pm8xxx_gpio_direction_input,
> +	.direction_output = pm8xxx_gpio_direction_output,
> +	.get = pm8xxx_gpio_get,
> +	.set = pm8xxx_gpio_set,
> +	.to_irq = pm8xxx_gpio_to_irq,
> +	.dbg_show = pm8xxx_gpio_dbg_show,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int pm8xxx_gpio_populate(struct pm8xxx_gpio *pctrl)
> +{
> +	struct pm8xxx_gpio_pin *pin;
> +	int val;
> +	int i;
> +
> +	for (i = 0; i < pctrl->data->ngpio; i++) {
> +		pin = &pctrl->pins[i];
> +
> +		val = pm8xxx_gpio_read(pctrl, i, 0);
> +		if (val < 0)
> +			return val;
> +
> +		pin->power_source = (val >> 1) & 0x7;
> +
> +		val = pm8xxx_gpio_read(pctrl, i, 1);
> +		if (val < 0)
> +			return val;
> +
> +		pin->direction = (val >> 2) & 0x3;
> +		pin->output_buffer = !!(val & BIT(1));
> +		pin->output_value = val & BIT(0);
> +
> +		val = pm8xxx_gpio_read(pctrl, i, 2);
> +		if (val < 0)
> +			return val;
> +
> +		pin->bias = (val >> 1) & 0x7;
> +
> +		val = pm8xxx_gpio_read(pctrl, i, 3);
> +		if (val < 0)
> +			return val;
> +
> +		pin->output_strength = (val >> 2) & 0x3;
> +		pin->disable = val & BIT(0);
> +
> +		val = pm8xxx_gpio_read(pctrl, i, 4);
> +		if (val < 0)
> +			return val;
> +
> +		pin->function = (val >> 1) & 0x7;
> +
> +		val = pm8xxx_gpio_read(pctrl, i, 5);
> +		if (val < 0)
> +			return val;
> +
> +		pin->non_inverted = !!(val & BIT(3));
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct pm8xxx_gpio_data pm8018_gpio_data = {
> +	.ngpio = 6,
> +};
> +
> +static const struct pm8xxx_gpio_data pm8038_gpio_data = {
> +	.ngpio = 12,
> +};
> +
> +static const struct pm8xxx_gpio_data pm8058_gpio_data = {
> +	.ngpio = 40,
> +};
> +static const struct pm8xxx_gpio_data pm8917_gpio_data = {
> +	.ngpio = 38,
> +};
> +
> +static const struct pm8xxx_gpio_data pm8921_gpio_data = {
> +	.ngpio = 44,
> +};
> +
> +static const struct of_device_id pm8xxx_gpio_of_match[] = {
> +	{ .compatible = "qcom,pm8018-gpio", .data = &pm8018_gpio_data },
> +	{ .compatible = "qcom,pm8038-gpio", .data = &pm8038_gpio_data },
> +	{ .compatible = "qcom,pm8058-gpio", .data = &pm8058_gpio_data },
> +	{ .compatible = "qcom,pm8917-gpio", .data = &pm8917_gpio_data },
> +	{ .compatible = "qcom,pm8921-gpio", .data = &pm8921_gpio_data },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
> +
> +static int pm8xxx_gpio_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct pm8xxx_gpio *pctrl;
> +	int ret;
> +	int i;
> +
> +	match = of_match_node(pm8xxx_gpio_of_match, pdev->dev.of_node);
> +	if (!match)
> +		return -ENXIO;
> +
> +	pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
> +	if (!pctrl)
> +		return -ENOMEM;
> +
> +	pctrl->dev = &pdev->dev;
> +	pctrl->data = match->data;
> +
> +	BUG_ON(pctrl->data->ngpio > PM8XXX_MAX_GPIOS);
> +
> +	pctrl->chip = pm8xxx_gpio_template;
> +	pctrl->chip.base = -1;
> +	pctrl->chip.dev = &pdev->dev;
> +	pctrl->chip.of_node = pdev->dev.of_node;
> +	pctrl->chip.label = dev_name(pctrl->dev);
> +	pctrl->chip.ngpio = pctrl->data->ngpio;
> +
> +	pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> +	if (!pctrl->regmap) {
> +		dev_err(&pdev->dev, "parent regmap unavailable\n");
> +		return -ENXIO;
> +	}
> +
> +	for (i = 0; i < pctrl->data->ngpio; i++) {
> +		ret = platform_get_irq(pdev, i);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev,
> +				"missing interrupts for pin %d\n", i);
> +			return ret;
> +		}
> +
> +		pctrl->pins[i].irq = ret;
> +	}
> +
> +	ret = pm8xxx_gpio_populate(pctrl);
> +	if (ret)
> +		return ret;
> +
> +	pm8xxx_gpio_desc.name = dev_name(&pdev->dev);
> +	pctrl->pctrl = pinctrl_register(&pm8xxx_gpio_desc, &pdev->dev, pctrl);
> +	if (!pctrl->pctrl) {
> +		dev_err(&pdev->dev, "couldn't register pm8xxx gpio driver\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = gpiochip_add(&pctrl->chip);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed register gpiochip\n");
> +		goto unregister_pinctrl;
> +	}
> +
> +	ret = gpiochip_add_pin_range(&pctrl->chip,
> +				     dev_name(pctrl->dev),
> +				     1, 0, pctrl->data->ngpio);
> +	if (ret) {
> +		dev_err(pctrl->dev, "failed to add pin range\n");
> +		goto unregister_gpiochip;
> +	}
> +
> +	platform_set_drvdata(pdev, pctrl);
> +
> +	dev_dbg(&pdev->dev, "Qualcomm pm8xxx gpio driver probed\n");
> +
> +	return 0;
> +
> +unregister_pinctrl:
> +	pinctrl_unregister(pctrl->pctrl);
> +
> +unregister_gpiochip:
> +	if (gpiochip_remove(&pctrl->chip))
> +		dev_err(&pdev->dev, "unable to unregister gpiochip\n");
> +
> +	return ret;
> +}
> +
> +static int pm8xxx_gpio_remove(struct platform_device *pdev)
> +{
> +	struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev);
> +
> +	gpiochip_remove(&pctrl->chip);
> +
> +	pinctrl_unregister(pctrl->pctrl);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver pm8xxx_gpio_driver = {
> +	.driver = {
> +		.name = "ssbi-pmic-gpio",
> +		.owner = THIS_MODULE,
> +		.of_match_table = pm8xxx_gpio_of_match,
> +	},
> +	.probe = pm8xxx_gpio_probe,
> +	.remove = pm8xxx_gpio_remove,
> +};
> +
> +module_platform_driver(pm8xxx_gpio_driver);
> +
> +MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
> +MODULE_DESCRIPTION("Qualcomm SSBI PMIC GPIO driver");
> +MODULE_LICENSE("GPL v2");
>

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

* Re: [PATCH v3 2/6] pinctrl: Introduce pinctrl driver for Qualcomm SSBI PMIC's
  2014-08-20  8:06       ` Srinivas Kandagatla
  (?)
@ 2014-08-20 21:28       ` Bjorn Andersson
  2014-08-20 22:13         ` Bjorn Andersson
  -1 siblings, 1 reply; 27+ messages in thread
From: Bjorn Andersson @ 2014-08-20 21:28 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Ivan T. Ivanov, Linus Walleij, Grant Likely, Rob Herring,
	linux-arm-msm, devicetree, linux-kernel

On Wed 20 Aug 01:06 PDT 2014, Srinivas Kandagatla wrote:

> Hi Bjorn,
> 

Hi Srinivas,

Thanks for the testing. I'm reworking the driver to incorporate yours, Linus'
and Ivans feedback.

> Two things which I noticed while trying out this driver to drive a reset
> line.
> 
> 1> gpio numbering for pinconf vs gpio are not consistent, they differ by
> an offset of 1.
> 

After scratching my head regarding this I now see that there's a off by one in
most of the gpio functions. I've rewroked this to make more sense now.

The gpio numbers has to start at 1, as all documentation is based on that.

[...]
> 
> 2> Looking back at v3.4 kernel, for gpio modes, BIT(0) of bank 0 is set
> to enable gpio mode. without this bit driver does not work for output pins.
> 

Thanks, I missed that.

Unfortunately, setting that bit results in input not working - the interrupt
bits are never set for gpios that have that bit set. I'm trying to figure out
why this is the case before sending out the new version...

Regards,
Bjorn

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

* Re: [PATCH v3 1/6] pinctrl: Device tree bindings for Qualcomm pm8xxx gpio block
  2014-08-18  7:16     ` Ivan T. Ivanov
@ 2014-08-20 22:10       ` Bjorn Andersson
  0 siblings, 0 replies; 27+ messages in thread
From: Bjorn Andersson @ 2014-08-20 22:10 UTC (permalink / raw)
  To: Ivan T. Ivanov
  Cc: Daniel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, linux-arm-msm, devicetree, linux-kernel

On Mon 18 Aug 00:16 PDT 2014, Ivan T. Ivanov wrote:

> On Sat, 2014-08-16 at 16:24 +0100, Daniel wrote:
> > @Ivan: sorry about the double post.
> > 
> > Am 11.08.2014 um 16:40 schrieb Ivan T. Ivanov <iivanov@mm-sol.com>:
[...]
> > > +#define PMIC_GPIO_PULL_UP_30		1
> > > +#define PMIC_GPIO_PULL_UP_1P5		2
> > > +#define PMIC_GPIO_PULL_UP_31P5		3
> > > +#define PMIC_GPIO_PULL_UP_1P5_30	4
> > 
> > Looking at drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c, shouldn't these defines start at 0?
> > e.g. #define PMIC_GPIO_PULL_UP_30 	0
> > 
> 
> Initially "bias-pull-up" was used to set this parameter. 
> Zero value for "bias-pull-up" has special meaning "...the 
> pin is connected to VDD...". So values in DTS have to have
> offset by one. Micro Amps are non-standard for pull-ups, 
> thats why I have changed this to "qcom,pull-up-strength", but I 
> have made mistake in config_set function. Following patch should 
> fix the issue. I will send updated version soon.
> 

The bias-pull-up is read as u32 and 0 means that it's not pull-up, therefor i
shifted them all. Sorry about that.

Now that we have this in a separate property there's no point in such
"trickery" and  we should make them follow the register values, i.e:
#define PM8XXX_GPIO_BIAS_PU_30          0
#define PM8XXX_GPIO_BIAS_PU_1P5         1
#define PM8XXX_GPIO_BIAS_PU_31P5        2
#define PM8XXX_GPIO_BIAS_PU_1P5_30      3

I find it cleaner and we don't need the translation.

> > However, I still cannot get any data from those 2 pins if I export them through /sys/class/gpio...
> 

Reading should work, but most other gpio operations was off by one it seems. I
have corrected this (and other reported things) and will send out a new version
soon.

> -			pin->bias = arg - PM8XXX_GPIO_BIAS_PU_30;
> +			pin->bias = arg - PMIC_GPIO_PULL_UP_30;

If we just make it follow the register value (starting at 0) we just use arg
straight off.

Regards,
Bjorn

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

* Re: [PATCH v3 2/6] pinctrl: Introduce pinctrl driver for Qualcomm SSBI PMIC's
  2014-08-20 21:28       ` Bjorn Andersson
@ 2014-08-20 22:13         ` Bjorn Andersson
  2014-11-03  8:48           ` Srinivas Kandagatla
  0 siblings, 1 reply; 27+ messages in thread
From: Bjorn Andersson @ 2014-08-20 22:13 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Srinivas Kandagatla, Ivan T. Ivanov, Linus Walleij, Grant Likely,
	Rob Herring, linux-arm-msm, devicetree, linux-kernel

On Wed, Aug 20, 2014 at 2:28 PM, Bjorn Andersson
<bjorn.andersson@sonymobile.com> wrote:
> On Wed 20 Aug 01:06 PDT 2014, Srinivas Kandagatla wrote:
>> 2> Looking back at v3.4 kernel, for gpio modes, BIT(0) of bank 0 is set
>> to enable gpio mode. without this bit driver does not work for output pins.
>>
>
> Thanks, I missed that.
>
> Unfortunately, setting that bit results in input not working - the interrupt
> bits are never set for gpios that have that bit set. I'm trying to figure out
> why this is the case before sending out the new version...

With help from Andy Gross this is now corrected as well, turned out
that BIT(0) in bank0 controls if the "direction" is considered. As I
was tricked by the multiple levels of indirection in the codeaurora
version I got these wrong.

Will send out an updated version shortly.

Regards,
Bjorn

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

* Re: [PATCH v3 1/6] pinctrl: Device tree bindings for Qualcomm pm8xxx gpio block
  2014-08-11 15:40 ` [PATCH v3 1/6] pinctrl: Device tree bindings for Qualcomm pm8xxx gpio block Ivan T. Ivanov
  2014-08-16 15:24   ` Daniel
@ 2014-08-20 22:27   ` Bjorn Andersson
  2014-08-25 13:14     ` Ivan T. Ivanov
  1 sibling, 1 reply; 27+ messages in thread
From: Bjorn Andersson @ 2014-08-20 22:27 UTC (permalink / raw)
  To: Ivan T. Ivanov
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-arm-msm, devicetree, linux-kernel

On Mon 11 Aug 08:40 PDT 2014, Ivan T. Ivanov wrote:
[...]
> diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
[...]
> +SUBNODES:
[...]
> +- 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"
> +

I still think it looks better with "real" functions, but I rather go with this
than discussing it forever.

> +- 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

I would prefer if we decrement this one step, as it will follow the register
values of both the "ssbi" and "spmi" based pmics.

[...]
> +
> +- 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>
> +		    xxxx_GPIO_L6, xxxx_GPIO_L5...

After implementing this my only concern is that debugfs output is not as useful
anymore; saying VIN2 instead of S4. But I can live with this.

> diff --git a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
[...]
> +
> +#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

Stephens argument was that we don't want to have huge tables for the functions
and I can see his point, it will be some work to build all the tables.
Adding all this defines is unfortunately doing just that.

I had a version of my driver that exposed real functions by name from the
driver, following the pattern we have for other pinctrl drivers and making the
dts very easy to read. But if you don't think we should go that route then I
suggest that we just call it func1/func2 and skip these defines.

Regards,
Bjorn

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

* Re: [PATCH v3 6/6] ARM: dts: qcom: Add APQ8074 Dragonboard PMIC GPIO bindings
  2014-08-11 15:40 ` [PATCH v3 6/6] ARM: dts: qcom: Add APQ8074 Dragonboard PMIC GPIO bindings Ivan T. Ivanov
@ 2014-08-20 23:06   ` Bjorn Andersson
  2014-08-25 14:04     ` Ivan T. Ivanov
  0 siblings, 1 reply; 27+ messages in thread
From: Bjorn Andersson @ 2014-08-20 23:06 UTC (permalink / raw)
  To: Ivan T. Ivanov
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Russell King, linux-arm-msm, devicetree, linux-kernel

On Mon 11 Aug 08:40 PDT 2014, Ivan T. Ivanov wrote:
> diff --git a/arch/arm/boot/dts/qcom-apq8074-dragonboard-pmics-pins.dtsi b/arch/arm/boot/dts/qcom-apq8074-dragonboard-pmics-pins.dtsi
[...]
> +
> +&pm8941_gpios {
> +
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&pm8941_gpios_default>;
> +
> +	pm8941_gpios_default: default {
> +		group-1 {
> +			pins = "gpio1", "gpio2", "gpio5", "gpio29";
> +			function = PMIC_GPIO_FUNC_NORMAL;

This looks really strange, I can't get myself to stop thinking that you forgot
the <> around this (I know it's a string). I don't like these defines.

> +			input-enable;
> +			power-source = <PM8941_GPIO_S3>;
> +			qcom,pull-up-strength = <PMIC_GPIO_PULL_UP_30>;
> +		};
> +		group-6 {	/* TUSB3_HUB-RESET */

Why not name the nodes something useful?
If you name it tusb3-hub-reset you can skip the comment.

> +			pins = "gpio6";
> +			function = PMIC_GPIO_FUNC_NORMAL;
> +			output-high;
> +			drive-push-pull;
> +			power-source = <PM8941_GPIO_VPH>;
> +			qcom,pull-up-strength = <PMIC_GPIO_PULL_UP_30>;
> +			qcom,drive-strength = <PMIC_GPIO_STRENGTH_MED>;
> +		};

Regards,
Bjorn

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

* Re: [PATCH v3 4/6] pinctrl: Qualcomm SPMI PMIC pin controller driver
  2014-08-11 15:40 ` [PATCH v3 4/6] pinctrl: Qualcomm SPMI PMIC pin controller driver Ivan T. Ivanov
@ 2014-08-21  6:16   ` Bjorn Andersson
       [not found]     ` <20140821061607.GF16274-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org>
  0 siblings, 1 reply; 27+ messages in thread
From: Bjorn Andersson @ 2014-08-21  6:16 UTC (permalink / raw)
  To: Ivan T. Ivanov
  Cc: Linus Walleij, Grant Likely, Rob Herring, linux-arm-msm,
	devicetree, linux-kernel

On Mon 11 Aug 08:40 PDT 2014, Ivan T. Ivanov wrote:

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

Hi Ivan,

I'm not very fond of the mixing of gpio and mpp in the same driver, there are
so many places where you have extra complexity to handle the fact that both are
handled in the same code paths.

Also, magic constans aren't good, but with all the shifts and maskes used
throughout this driver it's really difficult to follow the code. A lot of this
complexity comes from the fact that you're using regmap_update_bits(), so you
have to have those masks and shifts everywhere.

As you saw I solved this by keeping track of the various properties set by the
driver, another way could be to have a register shadow that you update and
flush - it would save you from the attrs-game in pinconf set/get.


Please find some detailed commends inline.

> diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-pmic.c b/drivers/pinctrl/qcom/pinctrl-spmi-pmic.c

[...]

> +/*
> + * Voltage select (GPIO, MPP) - specifies the voltage level when the output
> + * is set to 1. For an input GPIO specifies the voltage level at which
> + * the input is interpreted as a logical 1
> + * To be used with "power-func = <>"
> + */

This comment doesn't really have anything to do with the two defines here.

> +#define QPNP_PIN_VIN_4CH_INVALID               5
> +#define QPNP_PIN_VIN_8CH_INVALID               8

So for a 8 channel gpio we can select 0-7 and for a 4 channel gpio we can use
selector 0-4? Please rename these QPNP_PIN_4CH_MAX_VIN or LAST_VIN.

> +
> +/*
> + * Analog Output - Set the analog output reference.
> + * See PM8XXX_MPP_AOUT_XXX. To be used with "qcom,aout = <>"
> + */

This comment is related to the define, but not really explaining anything. As
with the VIN I think this should be named to indicate that this is a max.

> +#define QPNP_MPP_AOUT_INVALID                  8
> +
> +/*
> + * Analog Input - Set the func for analog input.
> + * See PM8XXX_MPP_AIN_XXX. To be used with "qcom,ain = <>"
> + */
> +#define QPNP_MPP_AIN_INVALID                   8

Same thing here, comment is only somewhat related and the name should be changed.

> +
> +/*
> + * Output type - indicates pin should be configured as CMOS or
> + * open drain.
> + */

Please update the comment for match the defines.

> +#define QPNP_GPIO_OUT_BUF_CMOS                 0
> +#define QPNP_GPIO_OUT_BUF_OPEN_DRAIN_NMOS      1
> +#define QPNP_GPIO_OUT_BUF_OPEN_DRAIN_PMOS      2

[...]

> +/* Out Strength (GPIO) - the amount of current supplied for an output GPIO */
> +#define QPNP_GPIO_STRENGTH_LOW                 1
> +#define QPNP_GPIO_STRENGTH_MED                 2
> +#define QPNP_GPIO_STRENGTH_HIGH                        3

You could use these straight off qcom,pmic-gpio.h instead of duplicating them here.

> +
> +/*
> + * Master enable (GPIO, MPP) - Enable features within the pin block based on
> + * configurations. QPNP_PIN_MASTER_DISABLE = Completely disable the pin
> + * lock and let the pin float with high impedance regardless of other settings.
> + */
> +#define QPNP_PIN_MASTER_DISABLE                 0
> +#define QPNP_PIN_MASTER_ENABLE                 1

You only need one of these.

> +
> +/* revision registers base address offsets */
> +#define QPNP_REG_DIG_MINOR_REV                 0x0
> +#define QPNP_REG_DIG_MAJOR_REV                 0x1
> +#define QPNP_REG_ANA_MINOR_REV                 0x2

Only QPNP_REG_DIG_MAJOR_REV is used, please drop the others.

> +/* control register base address offsets */
> +#define QPNP_REG_MODE_CTL                      0x40
> +#define QPNP_REG_DIG_VIN_CTL                   0x41
> +#define QPNP_REG_DIG_PULL_CTL                  0x42
> +#define QPNP_REG_DIG_IN_CTL                    0x43

REG_DIG_IN is unused

> +#define QPNP_PIN_PHYSICAL_OFFSET               1

If you just assign the of_node to the gpio_chip and then call
gpiochip_add_pin_range(_, _, 1, 0, _) this will give you the off-by-one you're
looking for.

> +struct qpnp_padinfo {
> +       u16 offset;             /* address offset in SPMI device */

I would suggesting a rename to "base" or something, as this will not be an
offset but rather the absolute address in this spmi device's address space.

> +       int irq;
> +       const char *name;       /* pin name */

This is redundant, as you already have this in the pinctrl_pin_desc.

> +       unsigned int modes;     /* supported modes: DI, DO, DIO, AI, AO... */
> +       unsigned int type;      /* peripheral hardware type */
> +       unsigned int subtype;   /* peripheral hardware subtype */
> +       unsigned int major;     /* digital major version */
> +};
> +
> +#define QPNP_REG_ADDR(pad, reg) ((pad)->offset + reg)

This is always followed by either regmap_read() or regmap_update_bits(), create
two static functions abstracting that construct away.

> +#define QPNP_GET(buff, shift, mask) ((buff & mask) >> shift)

I don't like the name of this macro, it doesn't "get" anything, it simply hides
the shifting and masking.

> +
> +struct qpnp_pinctrl {
> +       struct device *dev;
> +       struct regmap *map;
> +       struct pinctrl_dev *ctrl;
> +       struct pinctrl_desc desc;
> +       struct gpio_chip chip;
> +
> +       struct qpnp_padinfo *pads;

As Linus commented on my code, you shouldn't have a list of pads here, you
should reference them via desc.pins[x].drv_data

> +       const struct qpnp_chipinfo *info;

'info' is only used during probe, no reason to keep a pointer around.

> +       const char *const *groups;
> +       const char *const *functions;
> +};
> +
> +static inline struct qpnp_pinctrl *to_qpnp_pinctrl(struct gpio_chip *chip)
> +{
> +       return container_of(chip, struct qpnp_pinctrl, chip);
> +};

I see little point in breaking out the container_of into its own function.

> +
> +struct qpnp_pinbindings {
> +       const char *property;
> +       unsigned param;
> +       u32 default_value;

Consider dropping default_value as it's not used in your code (always 0).

> +};
> +
> +struct qpnp_pinattrib {

Not really "pin attributes", rather "io tasks" or something.

> +       unsigned addr;
> +       unsigned shift;
> +       unsigned mask;
> +       unsigned val;
> +};
> +
> +static struct qpnp_pinbindings qpnp_pinbindings[] = {

The commends in this list doesn't really add any value.

> +       /* QCOM_BIAS_PULL_UP_30...  */
> +       {"qcom,pull-up-strength", QPNP_PINCONF_PULL_UP, 0},
> +       /* QCOM_DRIVE_STRENGTH_NO... */
> +       {"qcom,drive-strength", QPNP_PINCONF_STRENGTH, 0},
> +       /* PMIC_MPP_AMUX_ROUTE_CH5 ... */
> +       {"qcom,amux-route",     QPNP_PINCONF_AMUX_ROUTE, 0},
> +       /* PMIC_MPP_VREFERENCE_1V25 ... */
> +       {"qcom,vrefence",       QPNP_PINCONF_VREFENCE, 0},
> +       /* PMIC_MPP_MODE.. */
> +       {"qcom,mode",           QPNP_PINCONF_MODE, 0},
> +};
> +

[...]

> +
> +static inline struct qpnp_padinfo *qpnp_get_desc(struct qpnp_pinctrl *qctrl,
> +                                                unsigned pin)
> +{
> +       if (pin >= qctrl->desc.npins) {
> +               dev_warn(qctrl->dev, "invalid pin number %d", pin);
> +               return NULL;
> +       }
> +
> +       return &qctrl->pads[pin];
> +}

Unless you initialize the pinctrl incorrectly this should never fail. So I
think you can safely inline the &qctrl->pads[pin]; in the various places you
need it.

> +
> +static int qpnp_conv_to_pin(struct qpnp_pinctrl *qctrl,
> +                          struct qpnp_padinfo *pad, unsigned param,
> +                          unsigned val)

This is not a good name for a "setter" function.

> +{
> +       struct qpnp_pinattrib attr[3];
> +       unsigned int type, subtype;
> +       int nattrs = 1, idx, ret;

s/idx/i/

> +
> +       type = pad->type;
> +       subtype = pad->subtype;
> +
> +       switch (param) {
> +       case PIN_CONFIG_DRIVE_PUSH_PULL:
> +               if (type != QPNP_GPIO_TYPE)
> +                       return -ENXIO;

Why is this return code different? Shouldn't this just be -EINVAL as you get
for any other "invalid param"?

> +               attr[0].addr  = QPNP_REG_DIG_OUT_CTL;
> +               attr[0].shift = QPNP_REG_OUT_TYPE_SHIFT;
> +               attr[0].mask  = QPNP_REG_OUT_TYPE_MASK;
> +               attr[0].val   = QPNP_GPIO_OUT_BUF_CMOS;
> +               break;
> +       case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +               if (type != QPNP_GPIO_TYPE)
> +                       return -ENXIO;

dito

> +               if (subtype == QPNP_GPIO_SUBTYPE_GPIOC_4CH ||
> +                   subtype == QPNP_GPIO_SUBTYPE_GPIOC_8CH)
> +                       return -EINVAL;
> +               attr[0].addr  = QPNP_REG_DIG_OUT_CTL;
> +               attr[0].shift = QPNP_REG_OUT_TYPE_SHIFT;
> +               attr[0].mask  = QPNP_REG_OUT_TYPE_MASK;
> +               attr[0].val   = QPNP_GPIO_OUT_BUF_OPEN_DRAIN_NMOS;
> +               break;
> +       case PIN_CONFIG_DRIVE_OPEN_SOURCE:
> +               if (type != QPNP_GPIO_TYPE)
> +                       return -ENXIO;

dito

> +               if (subtype == QPNP_GPIO_SUBTYPE_GPIOC_4CH ||
> +                   subtype == QPNP_GPIO_SUBTYPE_GPIOC_8CH)
> +                       return -EINVAL;
> +               attr[0].addr  = QPNP_REG_DIG_OUT_CTL;
> +               attr[0].shift = QPNP_REG_OUT_TYPE_SHIFT;
> +               attr[0].mask  = QPNP_REG_OUT_TYPE_MASK;
> +               attr[0].val   = QPNP_GPIO_OUT_BUF_OPEN_DRAIN_PMOS;
> +               break;
> +       case PIN_CONFIG_BIAS_DISABLE:
> +               attr[0].addr  = QPNP_REG_DIG_PULL_CTL;
> +               attr[0].shift = QPNP_REG_PULL_SHIFT;
> +               attr[0].mask  = QPNP_REG_PULL_MASK;
> +               if (type == QPNP_GPIO_TYPE)
> +                       attr[0].val = QPNP_GPIO_PULL_NO;
> +               else
> +                       attr[0].val = QPNP_MPP_PULL_UP_OPEN;
> +               break;
> +       case PIN_CONFIG_BIAS_PULL_UP:
> +               if (type != QPNP_MPP_TYPE)
> +                       return -EINVAL;

The binding documentation says that it's okay to just state "bias-pull-up" and
it will be configured as 30uA pull up. I would like us to keep the binding
documentation as it is now, but then you need to handle that here.

> +               switch (val) {
> +               case 0:
> +                       val = QPNP_MPP_PULL_UP_OPEN;
> +                       break;
> +               case 600:
> +                       val = QPNP_MPP_PULL_UP_0P6KOHM;
> +                       break;
> +               case 10000:
> +                       val = QPNP_MPP_PULL_UP_10KOHM;
> +                       break;
> +               case 30000:
> +                       val = QPNP_MPP_PULL_UP_30KOHM;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +               }
> +               attr[0].addr  = QPNP_REG_DIG_PULL_CTL;
> +               attr[0].shift = QPNP_REG_PULL_SHIFT;
> +               attr[0].mask  = QPNP_REG_PULL_MASK;
> +               attr[0].val   = val;
> +               break;
> +       case PIN_CONFIG_BIAS_PULL_DOWN:
> +               if (type != QPNP_GPIO_TYPE)
> +                       return -EINVAL;
> +               attr[0].addr  = QPNP_REG_DIG_PULL_CTL;
> +               attr[0].shift = QPNP_REG_PULL_SHIFT;
> +               attr[0].mask  = QPNP_REG_PULL_MASK;
> +               if (val)
> +                       attr[0].val = QPNP_GPIO_PULL_DN;
> +               else
> +                       attr[0].val = QPNP_GPIO_PULL_NO;
> +               break;
> +       case PIN_CONFIG_POWER_SOURCE:
> +               if (val >= QPNP_PIN_VIN_8CH_INVALID)
> +                       return -EINVAL;
> +               if (val >= QPNP_PIN_VIN_4CH_INVALID) {
> +                       if (type == QPNP_GPIO_TYPE &&
> +                          (subtype == QPNP_GPIO_SUBTYPE_GPIO_4CH ||
> +                           subtype == QPNP_GPIO_SUBTYPE_GPIOC_4CH))
> +                               return -EINVAL;
> +                       if (type == QPNP_MPP_TYPE &&
> +                          (subtype == QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
> +                           subtype == QPNP_MPP_SUBTYPE_4CH_NO_SINK ||
> +                           subtype == QPNP_MPP_SUBTYPE_4CH_FULL_FUNC ||
> +                           subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT ||
> +                           subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_SINK))
> +                               return -EINVAL;
> +               }

Maybe better to keep track of the number of channels a pad has, and compare
with that. Would replace all 4 if statements with 1 here.

> +               attr[0].addr  = QPNP_REG_DIG_VIN_CTL;
> +               attr[0].shift = QPNP_REG_VIN_SHIFT;
> +               attr[0].mask  = QPNP_REG_VIN_MASK;
> +               attr[0].val   = val;
> +               break;
> +       case PIN_CONFIG_DRIVE_STRENGTH:
> +               if (type != QPNP_MPP_TYPE)
> +                       return -EINVAL;
> +               if (subtype == QPNP_MPP_SUBTYPE_4CH_NO_SINK ||
> +                   subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_SINK)

Maybe better to have a logical flag indicating if this pad has "sink support"
or not.

> +                       return -ENXIO;

-EINVAL?

> +               if (val > 50)   /* mA */
> +                       return -EINVAL;
> +               attr[0].addr  = QPNP_REG_SINK_CTL;
> +               attr[0].shift = QPNP_REG_CS_OUT_SHIFT;
> +               attr[0].mask  = QPNP_REG_CS_OUT_MASK;
> +               attr[0].val   = (val / 5) - 1;
> +               break;
> +       case PIN_CONFIG_INPUT_ENABLE:
> +               nattrs = 2;
> +               attr[0].addr  = QPNP_REG_MODE_CTL;
> +               attr[0].shift = QPNP_REG_MODE_SEL_SHIFT;
> +               attr[0].mask  = QPNP_REG_MODE_SEL_MASK;
> +               attr[0].val   = QPNP_PIN_MODE_DIG_IN;
> +               attr[1].addr  = QPNP_REG_EN_CTL;
> +               attr[1].shift = QPNP_REG_MASTER_EN_SHIFT;
> +               attr[1].mask  = QPNP_REG_MASTER_EN_MASK;
> +               attr[1].val   = 1;
> +               if (val)
> +                       break;

input-disable is not the same thing as setting the pin in high-Z; i.e.
disabling the pin.

> +       /* Fallthrough */
> +       case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
> +               attr[1].addr  = QPNP_REG_EN_CTL;
> +               attr[1].shift = QPNP_REG_MASTER_EN_SHIFT;
> +               attr[1].mask  = QPNP_REG_MASTER_EN_MASK;
> +               attr[1].val   = 0;

Setting bias-high-impedence will use uninitialized attr[0] only, as nattrs will
be 1 here.

> +               break;
> +       case PIN_CONFIG_OUTPUT:
> +               nattrs = 3;
> +               attr[0].addr  = QPNP_REG_MODE_CTL;
> +               attr[0].shift = QPNP_REG_MODE_INVERT_SHIFT;
> +               attr[0].mask  = QPNP_REG_MODE_INVERT_MASK;
> +               attr[0].val   = !!val;
> +               attr[1].addr  = QPNP_REG_MODE_CTL;
> +               attr[1].shift = QPNP_REG_MODE_SEL_SHIFT;
> +               attr[1].mask  = QPNP_REG_MODE_SEL_MASK;
> +               attr[1].val   = QPNP_PIN_MODE_DIG_OUT;
> +               attr[2].addr  = QPNP_REG_EN_CTL;
> +               attr[2].shift = QPNP_REG_MASTER_EN_SHIFT;
> +               attr[2].mask  = QPNP_REG_MASTER_EN_MASK;
> +               attr[2].val   = 1;

Why are you setting master enable here? You should either have it all over the
place, or probably more sanely in the various bias cases.

> +               break;
> +       case QPNP_PINCONF_PULL_UP:
> +               if (type != QPNP_GPIO_TYPE)
> +                       return -EINVAL;
> +               switch (val) {
> +               default:
> +                       return -EINVAL;
> +               case 0:
> +                       val = QPNP_GPIO_PULL_NO;
> +                       break;
> +               case PMIC_GPIO_PULL_UP_30:
> +                       val = QPNP_GPIO_PULL_UP_30;
> +                       break;
> +               case PMIC_GPIO_PULL_UP_1P5:
> +                       val = QPNP_GPIO_PULL_UP_1P5;
> +                       break;
> +               case PMIC_GPIO_PULL_UP_31P5:
> +                       val = QPNP_GPIO_PULL_UP_31P5;
> +                       break;
> +               case PMIC_GPIO_PULL_UP_1P5_30:
> +                       val = QPNP_GPIO_PULL_UP_1P5_30;
> +                       break;
> +               }
> +               attr[0].addr  = QPNP_REG_DIG_PULL_CTL;
> +               attr[0].shift = QPNP_REG_PULL_SHIFT;
> +               attr[0].mask  = QPNP_REG_PULL_MASK;
> +               attr[0].val   = val;
> +               break;
> +       case QPNP_PINCONF_STRENGTH:
> +               if (type != QPNP_GPIO_TYPE)
> +                       return -EINVAL;
> +               switch (val) {
> +               default:
> +               case PMIC_GPIO_STRENGTH_NO:
> +                       return -EINVAL;
> +               case PMIC_GPIO_STRENGTH_LOW:
> +                       attr[0].val = QPNP_GPIO_STRENGTH_LOW;
> +                       break;
> +               case PMIC_GPIO_STRENGTH_MED:
> +                       attr[0].val = QPNP_GPIO_STRENGTH_MED;
> +                       break;
> +               case PMIC_GPIO_STRENGTH_HIGH:
> +                       attr[0].val = QPNP_GPIO_STRENGTH_HIGH;
> +                       break;
> +               }

These are 1:1, you can do attr[0].val = val;

> +               attr[0].addr  = QPNP_REG_DIG_OUT_CTL;
> +               attr[0].shift = QPNP_REG_OUT_STRENGTH_SHIFT;
> +               attr[0].mask  = QPNP_REG_OUT_STRENGTH_MASK;
> +               break;
> +       case QPNP_PINCONF_VREFENCE:
> +               if (type != QPNP_MPP_TYPE)
> +                       return -ENXIO;
> +               if (val >= QPNP_MPP_AOUT_INVALID)
> +                       return -EINVAL;
> +               if (subtype == QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
> +                   subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT)
> +                       return -ENXIO;

-EINVAL?

And maybe have a logical flag on the pad saying if we can do "analog output".

> +               attr[0].addr  = QPNP_REG_AOUT_CTL;
> +               attr[0].shift = QPNP_REG_AOUT_REF_SHIFT;
> +               attr[0].mask  = QPNP_REG_AOUT_REF_MASK;
> +               attr[0].val   = val;
> +               break;
> +       case QPNP_PINCONF_AMUX_ROUTE:
> +               if (type != QPNP_MPP_TYPE)
> +                       return -ENXIO;

-EINVAL?

> +               if (val >= QPNP_MPP_AIN_INVALID)
> +                       return -EINVAL;
> +               attr[0].addr  = QPNP_REG_AIN_CTL;
> +               attr[0].shift = QPNP_REG_AIN_ROUTE_SHIFT;
> +               attr[0].mask  = QPNP_REG_AIN_ROUTE_MASK;
> +               attr[0].val   = val;
> +               break;
> +       case QPNP_PINCONF_MODE:
> +               if ((pad->modes & BIT(val)) == 0)
> +                       return -ENXIO;

-EINVAL?

> +               attr[0].addr  = QPNP_REG_MODE_CTL;
> +               attr[0].shift = QPNP_REG_MODE_SEL_SHIFT;
> +               attr[0].mask  = QPNP_REG_MODE_SEL_MASK;
> +               attr[0].val   = val;

What happens here if I day output-high; qcom,mode = "digital-input"; ?

> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       for (idx = 0; idx < nattrs; idx++) {
> +               /* add base offset */

This comment is uneccessary...

> +               attr[idx].addr = QPNP_REG_ADDR(pad, attr[idx].addr);
> +               ret = regmap_update_bits(qctrl->map, attr[idx].addr,
> +                                        attr[idx].mask,
> +                                        attr[idx].val << attr[idx].shift);
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +
> +static int qpnp_conv_from_pin(struct qpnp_pinctrl *qctrl,
> +                            struct qpnp_padinfo *pad,
> +                            unsigned param, unsigned *val)

This is not a good name for a "getter" function.

> +{
> +       struct qpnp_pinattrib attr;
> +       unsigned int type, subtype, field;
> +       unsigned int addr, buff;
> +       int ret;
> +
> +       *val = 0;
> +       type = pad->type;
> +       subtype = pad->subtype;
> +
> +       switch (param) {
> +       case PIN_CONFIG_DRIVE_PUSH_PULL:
> +       case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +       case PIN_CONFIG_DRIVE_OPEN_SOURCE:
> +               if (type != QPNP_GPIO_TYPE)
> +                       return -ENXIO;

-EINVAL?

> +               attr.addr  = QPNP_REG_DIG_OUT_CTL;
> +               attr.shift = QPNP_REG_OUT_TYPE_SHIFT;
> +               attr.mask  = QPNP_REG_OUT_TYPE_MASK;
> +               break;
> +       case PIN_CONFIG_BIAS_PULL_DOWN:
> +               if (type != QPNP_GPIO_TYPE)
> +                       return -ENXIO;

-EINVAL?

> +       /* Fallthrough */
> +       case PIN_CONFIG_BIAS_DISABLE:
> +       case PIN_CONFIG_BIAS_PULL_UP:
> +               attr.addr  = QPNP_REG_DIG_PULL_CTL;
> +               attr.shift = QPNP_REG_PULL_SHIFT;
> +               attr.mask  = QPNP_REG_PULL_MASK;
> +               break;
> +       case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
> +               attr.addr  = QPNP_REG_EN_CTL;
> +               attr.shift = QPNP_REG_MASTER_EN_SHIFT;
> +               attr.mask  = QPNP_REG_MASTER_EN_MASK;
> +               break;
> +       case PIN_CONFIG_POWER_SOURCE:
> +               attr.addr  = QPNP_REG_DIG_VIN_CTL;
> +               attr.shift = QPNP_REG_VIN_SHIFT;
> +               attr.mask  = QPNP_REG_VIN_MASK;
> +               break;
> +       case PIN_CONFIG_DRIVE_STRENGTH:
> +               if (type != QPNP_MPP_TYPE)
> +                       return -ENXIO;

-EINVAL?

> +               attr.addr  = QPNP_REG_SINK_CTL;
> +               attr.shift = QPNP_REG_CS_OUT_SHIFT;
> +               attr.mask  = QPNP_REG_CS_OUT_MASK;
> +               break;
> +       case PIN_CONFIG_INPUT_ENABLE:
> +               attr.addr  = QPNP_REG_EN_CTL;
> +               attr.shift = QPNP_REG_MASTER_EN_SHIFT;
> +               attr.mask  = QPNP_REG_MASTER_EN_MASK;
> +               break;
> +       case PIN_CONFIG_OUTPUT:
> +               attr.addr  = QPNP_REG_MODE_CTL;
> +               attr.shift = QPNP_REG_MODE_INVERT_SHIFT;
> +               attr.mask  = QPNP_REG_MODE_INVERT_MASK;
> +               break;
> +       case QPNP_PINCONF_PULL_UP:
> +               if (type != QPNP_GPIO_TYPE)
> +                       return -ENXIO;

-EINVAL?

> +               attr.addr  = QPNP_REG_DIG_PULL_CTL;
> +               attr.shift = QPNP_REG_PULL_SHIFT;
> +               attr.mask  = QPNP_REG_PULL_MASK;
> +               break;
> +       case QPNP_PINCONF_STRENGTH:
> +               if (type != QPNP_GPIO_TYPE)
> +                       return -ENXIO;

-EINVAL?

> +               attr.addr  = QPNP_REG_DIG_OUT_CTL;
> +               attr.shift = QPNP_REG_OUT_STRENGTH_SHIFT;
> +               attr.mask  = QPNP_REG_OUT_STRENGTH_MASK;
> +               break;
> +       case QPNP_PINCONF_VREFENCE:
> +               if (type != QPNP_MPP_TYPE)
> +                       return -ENXIO;

-EINVAL?

> +               if (subtype == QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
> +                   subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT)
> +                       return -ENXIO;

-EINVAL?

> +               attr.addr  = QPNP_REG_AOUT_CTL;
> +               attr.shift = QPNP_REG_AOUT_REF_SHIFT;
> +               attr.mask  = QPNP_REG_AOUT_REF_MASK;
> +               break;
> +       case QPNP_PINCONF_AMUX_ROUTE:
> +               if (type != QPNP_MPP_TYPE)
> +                       return -ENXIO;

-EINVAL?

> +               attr.addr  = QPNP_REG_AIN_CTL;
> +               attr.shift = QPNP_REG_AIN_ROUTE_SHIFT;
> +               attr.mask  = QPNP_REG_AIN_ROUTE_MASK;
> +               break;
> +       case QPNP_PINCONF_MODE:
> +               attr.addr  = QPNP_REG_MODE_CTL;
> +               attr.shift = QPNP_REG_MODE_SEL_SHIFT;
> +               attr.mask  = QPNP_REG_MODE_SEL_MASK;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       addr = QPNP_REG_ADDR(pad, attr.addr);
> +       ret = regmap_read(qctrl->map, addr, &buff);
> +       if (ret < 0)
> +               return ret;
> +
> +       field = QPNP_GET(buff, attr.shift, attr.mask);
> +
> +       switch (param) {
> +       case PIN_CONFIG_DRIVE_PUSH_PULL:
> +               if (field == QPNP_GPIO_OUT_BUF_CMOS)
> +                       *val = 1;
> +               break;
> +       case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> +               if (field == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_NMOS)
> +                       *val = 1;
> +               break;
> +       case PIN_CONFIG_DRIVE_OPEN_SOURCE:
> +               if (field == QPNP_GPIO_OUT_BUF_OPEN_DRAIN_PMOS)
> +                       *val = 1;
> +               break;
> +       case PIN_CONFIG_BIAS_DISABLE:
> +               if (type == QPNP_GPIO_TYPE) {
> +                       if (field == QPNP_GPIO_PULL_NO)
> +                               *val = 1;
> +               } else {
> +                       if (field == QPNP_MPP_PULL_UP_OPEN)
> +                               *val = 1;
> +               }
> +               break;
> +       case PIN_CONFIG_BIAS_PULL_UP:
> +               switch (field) {
> +               default:
> +               case QPNP_MPP_PULL_UP_OPEN:
> +                       *val = 0;
> +                       break;
> +               case QPNP_MPP_PULL_UP_0P6KOHM:
> +                       *val = 600;
> +                       break;
> +               case QPNP_MPP_PULL_UP_10KOHM:
> +                       *val = 10000;
> +                       break;
> +               case QPNP_MPP_PULL_UP_30KOHM:
> +                       *val = 30000;
> +                       break;
> +               }
> +               break;
> +       case PIN_CONFIG_BIAS_PULL_DOWN:
> +               if (field == QPNP_GPIO_PULL_DN)
> +                       *val = 1;
> +               break;
> +       case PIN_CONFIG_POWER_SOURCE:
> +               *val = field;
> +               break;
> +       case PIN_CONFIG_DRIVE_STRENGTH:
> +               *val = (field + 1) * 5;
> +               break;
> +       case PIN_CONFIG_INPUT_ENABLE:
> +               *val = field;
> +               break;
> +       case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
> +               if (field == QPNP_PIN_MASTER_DISABLE)
> +                       *val = 1;

Checking for if (!field) would seem more logical.

> +               break;
> +       case PIN_CONFIG_OUTPUT:
> +               *val = field;
> +               break;
> +       case QPNP_PINCONF_PULL_UP:
> +               switch (field) {
> +               case QPNP_GPIO_PULL_NO:
> +                       field = 0;
> +                       break;
> +               case QPNP_GPIO_PULL_UP_30:
> +                       field = PMIC_GPIO_PULL_UP_30;

Why don't you just assign *val directly here, like you do in the rest of the
cases?

> +                       break;
> +               case QPNP_GPIO_PULL_UP_1P5:
> +                       field = PMIC_GPIO_PULL_UP_1P5;
> +                       break;
> +               case QPNP_GPIO_PULL_UP_31P5:
> +                       field = PMIC_GPIO_PULL_UP_31P5;
> +                       break;
> +
> +               case QPNP_GPIO_PULL_UP_1P5_30:
> +                       field = PMIC_GPIO_PULL_UP_1P5_30;
> +                       break;
> +               }
> +               *val = field;
> +               break;
> +       case QPNP_PINCONF_STRENGTH:
> +               switch (field) {
> +               case QPNP_GPIO_STRENGTH_HIGH:
> +                       field = PMIC_GPIO_STRENGTH_HIGH;
> +                       break;
> +               case QPNP_GPIO_STRENGTH_MED:
> +                       field = PMIC_GPIO_STRENGTH_MED;
> +                       break;
> +               case QPNP_GPIO_STRENGTH_LOW:
> +                       field = PMIC_GPIO_STRENGTH_LOW;
> +                       break;
> +               }
> +               *val = field;
> +               break;
> +       case QPNP_PINCONF_MODE:
> +       case QPNP_PINCONF_VREFENCE:
> +       case QPNP_PINCONF_AMUX_ROUTE:
> +               *val = field;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +

[...]

> +
> +static int qpnp_parse_dt_config(struct device_node *np,
> +                             struct pinctrl_dev *pctldev,
> +                             unsigned long **configs, unsigned int *nconfs)
> +{
> +       struct qpnp_pinbindings *par;
> +       unsigned long cfg;
> +       int ret, idx;
> +       u32 val;
> +
> +       for (idx = 0; idx < ARRAY_SIZE(qpnp_pinbindings); idx++) {
> +
> +               par = &qpnp_pinbindings[idx];
> +               ret = of_property_read_u32(np, par->property, &val);
> +
> +               /* property not found */
> +               if (ret == -EINVAL)
> +                       continue;
> +
> +               /* use default value, when no value is specified */
> +               if (ret)
> +                       val = par->default_value;

These are all 0 in your case.

> +
> +               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 qpnp_get_functions_count(struct pinctrl_dev *pctldev)
> +{
> +       return ARRAY_SIZE(qpnp_gpio_functions);

What about the qpnp_mpp_functions?

> +}
> +

[...]

> +
> +static int qpnp_get(struct gpio_chip *chip, unsigned offset)
> +{
> +       struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
> +       struct qpnp_padinfo *pad;
> +       unsigned int val, en_mask, buff, addr;
> +       int ret;
> +
> +       pad = qpnp_get_desc(qctrl, offset);
> +       if (!pad)
> +               return -EINVAL;
> +
> +       addr = QPNP_REG_ADDR(pad, QPNP_REG_MODE_CTL);
> +       ret = regmap_read(qctrl->map, addr, &buff);
> +       if (ret < 0)
> +               return ret;
> +
> +       /* GPIO val is from RT status if input is enabled */
> +       if ((buff & QPNP_REG_MODE_SEL_MASK) ==
> +           (QPNP_PIN_MODE_DIG_IN << QPNP_REG_MODE_SEL_SHIFT)) {

Logic like this would be far cleaner if you keep track of the configured state,
such as if this is input or output.

> +
> +               addr = QPNP_REG_ADDR(pad, QPNP_REG_STATUS1);
> +               ret = regmap_read(qctrl->map, addr, &val);
> +               if (ret < 0)
> +                       return ret;
> +

>From here...

> +               if (pad->type == QPNP_GPIO_TYPE && pad->major == 0)
> +                       en_mask = QPNP_REG_STATUS1_GPIO_EN_REV0_MASK;
> +               else if (pad->type == QPNP_GPIO_TYPE &&
> +                        pad->major > 0)
> +                       en_mask = QPNP_REG_STATUS1_GPIO_EN_MASK;
> +               else            /* MPP */
> +                       en_mask = QPNP_REG_STATUS1_MPP_EN_MASK;
> +
> +               if (!(val & en_mask))
> +                       return -EPERM;

...to here, checks if the gpio is enabled. If you keep track of that you could
drop this chunk.

> +
> +               ret = val & QPNP_REG_STATUS1_VAL_MASK;

You should be able to ignore the above checks altogether and read the real time
status bit register (0x10) and use BIT(0) from it - but I haven't tested this.


If that doesn't fly we could simply drop "rev0" support and just have 1 mask
(as it's the same for gpio and mpp) and drop all the if block.

> +
> +       } else {
> +               ret = buff & QPNP_REG_MODE_INVERT_MASK;
> +               ret = ret >> QPNP_REG_MODE_INVERT_SHIFT;
> +       }
> +
> +       return !!ret;
> +}
> +

[...]

> +
> +static int qpnp_config_get(struct pinctrl_dev *pctldev,
> +                         unsigned int pin,
> +                         unsigned long *config)
> +{
> +       struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
> +       unsigned param = pinconf_to_config_param(*config);
> +       struct qpnp_padinfo *pad;
> +       unsigned arg;
> +       int ret;
> +
> +       pad = qpnp_get_desc(qctrl, pin);
> +       if (!pad)
> +               return -EINVAL;
> +
> +       /* Convert pinconf values to register values */

Not only does it convert the values, it reads them too...

> +       ret = qpnp_conv_from_pin(qctrl, pad, param, &arg);
> +       if (ret)
> +               return ret;
> +
> +       /* Convert register value to pinconf value */
> +       *config = pinconf_to_config_packed(param, arg);
> +       return 0;
> +}
> +
> +static int qpnp_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
> +                         unsigned long *configs, unsigned nconfs)
> +{
> +       struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
> +       struct qpnp_padinfo *pad;
> +       unsigned param;
> +       unsigned arg;
> +       int idx, ret;
> +
> +       pad = qpnp_get_desc(qctrl, pin);
> +       if (!pad)
> +               return -EINVAL;
> +
> +       for (idx = 0; idx < nconfs; idx++) {
> +               param = pinconf_to_config_param(configs[idx]);
> +               arg = pinconf_to_config_argument(configs[idx]);
> +
> +               /* Convert pinconf values to register values */

...and update the registers...

> +               ret = qpnp_conv_to_pin(qctrl, pad, param, arg);
> +               if (ret < 0) {
> +                       dev_err(pctldev->dev, "config %u = %u failed\n",
> +                               param, arg);
> +                       return ret;
> +               }
> +       }
> +
> +       return 0;
> +}
> +

[...]

> +
> +static int qpnp_of_xlate(struct gpio_chip *chip,
> +                      const struct of_phandle_args *gpio_desc, u32 *flags)
> +{

Assign chip.of_node when registering the gpio_chip and remove this function.

> +       struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
> +       struct qpnp_padinfo *pad;
> +       unsigned pin = gpio_desc->args[0] - QPNP_PIN_PHYSICAL_OFFSET;
> +
> +       if (chip->of_gpio_n_cells < 2)
> +               return -EINVAL;
> +
> +       pad = qpnp_get_desc(qctrl, pin);
> +       if (!pad)
> +               return -EINVAL;
> +
> +       if (flags)
> +               *flags = gpio_desc->args[1];
> +
> +       return pin;
> +}
> +

[...]

> +
> +static int qpnp_control_init(struct qpnp_pinctrl *qctrl,
> +                         struct qpnp_padinfo *pad)

This is not "initializing any control", it's rather a
qpnp_get_available_modes(type, subtype) function.

> +{
> +       pad->modes = 0;
> +
> +       if (pad->type == QPNP_GPIO_TYPE) {
> +               switch (pad->subtype) {
> +               case QPNP_GPIO_SUBTYPE_GPIO_4CH:
> +               case QPNP_GPIO_SUBTYPE_GPIOC_4CH:
> +               case QPNP_GPIO_SUBTYPE_GPIO_8CH:
> +               case QPNP_GPIO_SUBTYPE_GPIOC_8CH:

Looks like this switch is not of much use, consider dropping it.

> +
> +                       /* only GPIO is supported*/
> +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
> +                       break;
> +               default:
> +                       dev_err(qctrl->dev, "invalid GPIO subtype 0x%x\n",
> +                               pad->subtype);

Is this really going to happen? This would indicate that we have "compatible"
chips that suddenly have some new type of gpio/mpp block.

> +                       return -EINVAL;
> +               }
> +
> +       } else if (pad->type == QPNP_MPP_TYPE) {
> +
> +               switch (pad->subtype) {
> +               case QPNP_MPP_SUBTYPE_4CH_NO_SINK:
> +               case QPNP_MPP_SUBTYPE_ULT_4CH_NO_SINK:
> +
> +                       /* Current sink not supported*/
> +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_BIDIR);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_AIN);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_AOUT);

It's hard to get a quick understanding of what is common and what is not. If
you move all the common modes to before the switch statement and then just add
the special ones in the three cases things would be easier to read.

> +                       break;
> +               case QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT:
> +               case QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
> +
> +                       /* Analog output not supported */
> +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_BIDIR);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_AIN);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_SINK);
> +                       break;
> +               case QPNP_MPP_SUBTYPE_4CH_FULL_FUNC:
> +               case QPNP_MPP_SUBTYPE_8CH_FULL_FUNC:
> +
> +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_BIDIR);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_AIN);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_AOUT);
> +                       pad->modes |= BIT(QPNP_PIN_MODE_SINK);
> +                       break;
> +               default:
> +                       dev_err(qctrl->dev, "invalid MPP subtype 0x%x\n",
> +                               pad->subtype);

Same as for gpios.

> +                       return -EINVAL;
> +               }
> +       } else {
> +               dev_err(qctrl->dev, "invalid type 0x%x\n", pad->type);
> +               return -EINVAL;

This is not possible, because you already checked type in discover.

> +       }
> +
> +       return 0;
> +}
> +
> +static int qpnp_discover(struct platform_device *pdev,
> +                       struct qpnp_pinctrl *qctrl)
> +{
> +       struct device *dev = qctrl->dev;
> +       struct pinctrl_pin_desc *desc, *descs;
> +       struct qpnp_padinfo *pad, *pads;
> +       unsigned int addr, npins;
> +       int idx, ret;
> +
> +       npins = qctrl->info->npins;
> +       pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL);
> +       if (!pads)
> +               return -ENOMEM;
> +
> +       descs = devm_kcalloc(dev, npins, sizeof(*descs), GFP_KERNEL);
> +       if (!descs)
> +               return -ENOMEM;
> +
> +       for (idx = 0; idx < npins; idx++) {
> +
> +               pad = &pads[idx];
> +               desc = &descs[idx];
> +
> +               pad->irq = platform_get_irq(pdev, idx);
> +               if (pad->irq < 0)
> +                       return pad->irq;
> +
> +               pad->offset = qctrl->info->base + (idx * 0x100);
> +
> +               addr = QPNP_REG_ADDR(pad, QPNP_REG_DIG_MAJOR_REV);
> +               ret = regmap_read(qctrl->map, addr, &pad->major);
> +               if (ret < 0)
> +                       return ret;

The major is only used to figure out if the gpio is enabled to see if we can
trust the gpio value, if you rework that (or use rt status) then you can drop
this as well.

> +
> +               addr = QPNP_REG_ADDR(pad, QPNP_REG_TYPE);
> +               ret = regmap_read(qctrl->map, addr, &pad->type);
> +               if (ret < 0)
> +                       return ret;
> +
> +               if (pad->type != qctrl->info->type) {
> +                       dev_err(dev, "Expected %x, found %x\n",
> +                               qctrl->info->type, pad->type);

The only reason for this is if you end up here matching a gpio compatible on a
mpp block or vice versa. In the current implementation that's impossible, with
my suggestion of reading the reg from dt this will be possible; but I think you
should make the error message indicate what the problem actually is.

Like "Incorrect block type 0x%x" or so..

> +                       return -EINVAL;
> +               }
> +
> +               addr = QPNP_REG_ADDR(pad, QPNP_REG_SUBTYPE);
> +               ret = regmap_read(qctrl->map, addr, &pad->subtype);
> +               if (ret < 0)
> +                       return ret;
> +
> +               ret = qpnp_control_init(qctrl, pad);
> +               if (ret)
> +                       return ret;
> +
> +               pad->name = qctrl->groups[idx];

No need to keep this in the pad and desc, as stated before.

> +               desc->number = idx;
> +               desc->name = pad->name;
> +       }
> +
> +       qctrl->pads = pads;
> +
> +       qctrl->chip = qpnp_gpio_template;
> +       qctrl->chip.dev = dev;
> +       qctrl->chip.base = -1;
> +       qctrl->chip.ngpio = qctrl->info->npins;
> +       qctrl->chip.label = dev_name(dev);
> +       qctrl->chip.of_gpio_n_cells = 2;
> +       qctrl->chip.can_sleep = true;

Assign chip.of_node as well and it will give you the xlate stuff.

> +
> +       qctrl->desc.pctlops = &qpnp_pinctrl_ops,
> +       qctrl->desc.pmxops = &qpnp_pinmux_ops,
> +       qctrl->desc.confops = &qpnp_pinconf_ops,
> +       qctrl->desc.owner = THIS_MODULE,
> +       qctrl->desc.name = dev_name(dev);
> +       qctrl->desc.pins = descs;
> +       qctrl->desc.npins = npins;
> +
> +       qctrl->ctrl = pinctrl_register(&qctrl->desc, dev, qctrl);
> +       if (!qctrl->ctrl)
> +               return -ENODEV;
> +
> +       ret = gpiochip_add(&qctrl->chip);
> +       if (ret) {
> +               dev_err(qctrl->dev, "can't add gpio chip\n");
> +               goto err_chip;
> +       }
> +
> +       ret = gpiochip_add_pin_range(&qctrl->chip, dev_name(dev),
> +                                    0, 0, npins);
> +       if (ret) {
> +               dev_err(dev, "failed to add pin range\n");
> +               goto err_range;
> +       }
> +
> +       return 0;
> +

You got these in the wrong order, err_range should pop both, err_chip should
only pop the pinctrl.

> +err_chip:
> +       pinctrl_unregister(qctrl->ctrl);
> +
> +err_range:
> +       gpiochip_remove(&qctrl->chip);
> +
> +       return ret;
> +
> +}
> +
> +static const struct of_device_id qpnp_pinctrl_of_match[];
> +
> +static int qpnp_pinctrl_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       const struct qpnp_chipinfo *qchip;
> +       struct qpnp_pinctrl *qctrl;
> +
> +       qctrl = devm_kzalloc(dev, sizeof(*qctrl), GFP_KERNEL);
> +       if (!qctrl)
> +               return -ENOMEM;
> +
> +       platform_set_drvdata(pdev, qctrl);
> +
> +       qchip = of_match_node(qpnp_pinctrl_of_match, dev->of_node)->data;
> +
> +       qctrl->info = qchip;

No need to keep track of the info after qpnp_discover().

> +       qctrl->dev = &pdev->dev;
> +       qctrl->map = dev_get_regmap(dev->parent, NULL);
> +
> +       if (qchip->type == QPNP_GPIO_TYPE) {
> +               if (WARN_ON(qchip->npins > ARRAY_SIZE(qpnp_gpio_groups)))

Either remove this, or make it a BUG_ON(), depending on you trusting future
developers.

> +                       return -EINVAL;
> +               qctrl->groups = qpnp_gpio_groups;
> +               qctrl->functions = qpnp_gpio_functions;
> +       } else {
> +               if (WARN_ON(qchip->npins > ARRAY_SIZE(qpnp_mpp_groups)))

dito

> +                       return -EINVAL;
> +               qctrl->groups = qpnp_mpp_groups;
> +               qctrl->functions = qpnp_mpp_functions;
> +       }
> +
> +       return qpnp_discover(pdev, qctrl);
> +}
> +
> +static int qpnp_pinctrl_remove(struct platform_device *pdev)
> +{
> +       struct qpnp_pinctrl *qctrl = platform_get_drvdata(pdev);
> +
> +       gpiochip_remove(&qctrl->chip);

I think Linus talked about removing the return value, but for now I think you
should abort if the gpiochip is busy.

> +       pinctrl_unregister(qctrl->ctrl);
> +
> +       return 0;
> +}
> +
> +static const struct qpnp_chipinfo qpnp_pm8841_mpp_info = {
> +       .npins  = 4,
> +       .base   = 0xa000,

Read this from the reg property instead.

> +       .type   = QPNP_MPP_TYPE,
> +};
> +
> +static const struct qpnp_chipinfo qpnp_pm8941_gpio_info = {
> +       .npins  = 36,
> +       .base   = 0xc000,

dito

> +       .type   = QPNP_GPIO_TYPE,
> +};
> +
> +static const struct qpnp_chipinfo qpnp_pm8941_mpp_info = {
> +       .npins  = 8,
> +       .base   = 0xa000,

dito

> +       .type   = QPNP_MPP_TYPE,
> +};
> +
> +static const struct qpnp_chipinfo qpnp_pma8084_mpp_info = {
> +       .npins  = 4,
> +       .base   = 0xa000,

dito

> +       .type   = QPNP_MPP_TYPE,
> +};
> +
> +static const struct qpnp_chipinfo qpnp_pma8084_gpio_info = {
> +       .npins  = 22,
> +       .base   = 0xc000,

dito

> +       .type   = QPNP_GPIO_TYPE,
> +};
> +

Regards,
Bjorn

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

* Re: [PATCH v3 1/6] pinctrl: Device tree bindings for Qualcomm pm8xxx gpio block
  2014-08-20 22:27   ` Bjorn Andersson
@ 2014-08-25 13:14     ` Ivan T. Ivanov
  0 siblings, 0 replies; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-25 13:14 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-arm-msm, devicetree, linux-kernel

On Wed, 2014-08-20 at 15:27 -0700, Bjorn Andersson wrote:
> On Mon 11 Aug 08:40 PDT 2014, Ivan T. Ivanov wrote:
> [...]
> > diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
> [...]
> > +SUBNODES:
> [...]
> > +- 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"
> > +
> 
> I still think it looks better with "real" functions, but I rather go with this
> than discussing it forever.
> 
> > +- 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
> 
> I would prefer if we decrement this one step, as it will follow the register
> values of both the "ssbi" and "spmi" based pmics.

Ok.

> 
> [...]
> > +
> > +- 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>
> > +		    xxxx_GPIO_L6, xxxx_GPIO_L5...
> 
> After implementing this my only concern is that debugfs output is not as useful
> anymore; saying VIN2 instead of S4. But I can live with this.
> 
> > diff --git a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h
> [...]
> > +
> > +#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
> 
> Stephens argument was that we don't want to have huge tables for the functions
> and I can see his point, it will be some work to build all the tables.
> Adding all this defines is unfortunately doing just that.

It is still little bit simpler :-)

> 
> I had a version of my driver that exposed real functions by name from the
> driver, following the pattern we have for other pinctrl drivers and making the
> dts very easy to read. But if you don't think we should go that route then I
> suggest that we just call it func1/func2 and skip these defines.
> 
> Regards,
> Bjorn

Regards,
Ivan

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

* Re: [PATCH v3 4/6] pinctrl: Qualcomm SPMI PMIC pin controller driver
  2014-08-21  6:16   ` Bjorn Andersson
@ 2014-08-25 14:00         ` Ivan T. Ivanov
  0 siblings, 0 replies; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-25 14:00 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Linus Walleij, Grant Likely, Rob Herring,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On Wed, 2014-08-20 at 23:16 -0700, Bjorn Andersson wrote:
> On Mon 11 Aug 08:40 PDT 2014, Ivan T. Ivanov wrote:
> 
> > From: "Ivan T. Ivanov" <iivanov-NEYub+7Iv8PQT0dZR+AlfA@public.gmane.org>
> > 
> > This is the pinctrl, pinmux, pinconf and gpiolib driver for the
> > Qualcomm GPIO and MPP sub-function blocks found in the PMIC chips.
> > 
> 
> Hi Ivan,
> 
> I'm not very fond of the mixing of gpio and mpp in the same driver, there are
> so many places where you have extra complexity to handle the fact that both are
> handled in the same code paths.

I could split them. Downstream driver uses one driver for GPIO and MPP
from all PMIC's. Initially to me it looks like GPIO's are MPP's with
limited functionality, but it turn out that they are 2 different HW
block with some similarities.

> 
> Also, magic constans aren't good, but with all the shifts and maskes used
> throughout this driver it's really difficult to follow the code. A lot of this
> complexity comes from the fact that you're using regmap_update_bits(), so you
> have to have those masks and shifts everywhere.
> 
> As you saw I solved this by keeping track of the various properties set by the
> driver, another way could be to have a register shadow that you update and
> flush - it would save you from the attrs-game in pinconf set/get.
> 

Yep. I agree. Currently my implementation is fragile because it depend
on order of the properties in the DT files. Probably will be best if all
settings accumulated by pin_config_group_get() are applied in correct
order in pinmux.enable().

<snip>

> 
> > +#define QPNP_PIN_PHYSICAL_OFFSET               1
> 
> If you just assign the of_node to the gpio_chip and then call
> gpiochip_add_pin_range(_, _, 1, 0, _) this will give you the off-by-one you're
> looking for.

I have tried this, but something went wrong, I don't remember what. 
Will check again.

> > +
> > +struct qpnp_pinctrl {
> > +       struct device *dev;
> > +       struct regmap *map;
> > +       struct pinctrl_dev *ctrl;
> > +       struct pinctrl_desc desc;
> > +       struct gpio_chip chip;
> > +
> > +       struct qpnp_padinfo *pads;
> 
> As Linus commented on my code, you shouldn't have a list of pads here, you
> should reference them via desc.pins[x].drv_data

I was trying to avoid multiple small allocation. Will check again.

<snip>

> 
> > +       const struct qpnp_chipinfo *info;
> 
> 'info' is only used during probe, no reason to keep a pointer around.

probe and discover functions.

> 
> > +       const char *const *groups;
> > +       const char *const *functions;
> > +};
> > +
> > +static inline struct qpnp_pinctrl *to_qpnp_pinctrl(struct gpio_chip *chip)
> > +{
> > +       return container_of(chip, struct qpnp_pinctrl, chip);
> > +};
> 
> I see little point in breaking out the container_of into its own function.

It fit driver state dereferencing to 80 columns.

> > +
> > +static int qpnp_conv_to_pin(struct qpnp_pinctrl *qctrl,
> > +                          struct qpnp_padinfo *pad, unsigned param,

<snip>

> > +       case PIN_CONFIG_BIAS_PULL_UP:
> > +               if (type != QPNP_MPP_TYPE)
> > +                       return -EINVAL;
> 
> The binding documentation says that it's okay to just state "bias-pull-up" and
> it will be configured as 30uA pull up. 

But is valid only for MPP's.

> I would like us to keep the binding
> documentation as it is now, but then you need to handle that here.
> 

Right.

> > +               switch (val) {
> > +               case 0:
> > +                       val = QPNP_MPP_PULL_UP_OPEN;
> > +                       break;
> > +               case 600:
> > +                       val = QPNP_MPP_PULL_UP_0P6KOHM;
> > +                       break;
> > +               case 10000:
> > +                       val = QPNP_MPP_PULL_UP_10KOHM;
> > +                       break;
> > +               case 30000:
> > +                       val = QPNP_MPP_PULL_UP_30KOHM;
> > +                       break;
> > +               default:
> > +                       return -EINVAL;
> > +               }

<snip>

> > +       case QPNP_PINCONF_MODE:
> > +               if ((pad->modes & BIT(val)) == 0)
> > +                       return -ENXIO;
> 
> -EINVAL?
> 
> > +               attr[0].addr  = QPNP_REG_MODE_CTL;
> > +               attr[0].shift = QPNP_REG_MODE_SEL_SHIFT;
> > +               attr[0].mask  = QPNP_REG_MODE_SEL_MASK;
> > +               attr[0].val   = val;
> 
> What happens here if I day output-high; qcom,mode = "digital-input"; ?

It depends on the order of which they are found in DT files :-).
Will fix it.

> > +
> > +static int qpnp_of_xlate(struct gpio_chip *chip,
> > +                      const struct of_phandle_args *gpio_desc, u32 *flags)
> > +{
> 
> Assign chip.of_node when registering the gpio_chip and remove this function.
> 
> > +       struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
> > +       struct qpnp_padinfo *pad;
> > +       unsigned pin = gpio_desc->args[0] - QPNP_PIN_PHYSICAL_OFFSET;

This way gpiolib see pin numbers like pinctrl framework, starting from 1.
I will try to offset gpio range with 1 in registration.
> 
> > +
> > +static int qpnp_control_init(struct qpnp_pinctrl *qctrl,
> > +                         struct qpnp_padinfo *pad)
> 
> This is not "initializing any control", it's rather a
> qpnp_get_available_modes(type, subtype) function.
> 
> > +{
> > +       pad->modes = 0;
> > +
> > +       if (pad->type == QPNP_GPIO_TYPE) {
> > +               switch (pad->subtype) {
> > +               case QPNP_GPIO_SUBTYPE_GPIO_4CH:
> > +               case QPNP_GPIO_SUBTYPE_GPIOC_4CH:
> > +               case QPNP_GPIO_SUBTYPE_GPIO_8CH:
> > +               case QPNP_GPIO_SUBTYPE_GPIOC_8CH:
> 
> Looks like this switch is not of much use, consider dropping it.
> 
> > +
> > +                       /* only GPIO is supported*/
> > +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
> > +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
> > +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
> > +                       break;
> > +               default:
> > +                       dev_err(qctrl->dev, "invalid GPIO subtype 0x%x\n",
> > +                               pad->subtype);
> 
> Is this really going to happen? This would indicate that we have "compatible"
> chips that suddenly have some new type of gpio/mpp block.

And we should be somehow be noticed, right?

> 
> > +                       return -EINVAL;
> > +               }

<snip>

> > +static int qpnp_pinctrl_probe(struct platform_device *pdev)
> > +{
> > +       struct device *dev = &pdev->dev;
> > +       const struct qpnp_chipinfo *qchip;
> > +       struct qpnp_pinctrl *qctrl;
> > +
> > +       qctrl = devm_kzalloc(dev, sizeof(*qctrl), GFP_KERNEL);
> > +       if (!qctrl)
> > +               return -ENOMEM;
> > +
> > +       platform_set_drvdata(pdev, qctrl);
> > +
> > +       qchip = of_match_node(qpnp_pinctrl_of_match, dev->of_node)->data;
> > +
> > +       qctrl->info = qchip;
> 
> No need to keep track of the info after qpnp_discover().

Right.

<snip>

> > +
> > +static const struct qpnp_chipinfo qpnp_pm8841_mpp_info = {
> > +       .npins  = 4,
> > +       .base   = 0xa000,
> 
> Read this from the reg property instead.

If SPMI bindings are changed a little bit, there will be no
need to hard code even number of pins :-). Will do.

> Regards,
> Bjorn

Thank you for detailed review. I agree with sniped comments
and will fix them.

Regards,
Ivan


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v3 4/6] pinctrl: Qualcomm SPMI PMIC pin controller driver
@ 2014-08-25 14:00         ` Ivan T. Ivanov
  0 siblings, 0 replies; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-25 14:00 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Linus Walleij, Grant Likely, Rob Herring, linux-arm-msm,
	devicetree, linux-kernel

On Wed, 2014-08-20 at 23:16 -0700, Bjorn Andersson wrote:
> On Mon 11 Aug 08:40 PDT 2014, Ivan T. Ivanov wrote:
> 
> > From: "Ivan T. Ivanov" <iivanov@mm-sol.com>
> > 
> > This is the pinctrl, pinmux, pinconf and gpiolib driver for the
> > Qualcomm GPIO and MPP sub-function blocks found in the PMIC chips.
> > 
> 
> Hi Ivan,
> 
> I'm not very fond of the mixing of gpio and mpp in the same driver, there are
> so many places where you have extra complexity to handle the fact that both are
> handled in the same code paths.

I could split them. Downstream driver uses one driver for GPIO and MPP
from all PMIC's. Initially to me it looks like GPIO's are MPP's with
limited functionality, but it turn out that they are 2 different HW
block with some similarities.

> 
> Also, magic constans aren't good, but with all the shifts and maskes used
> throughout this driver it's really difficult to follow the code. A lot of this
> complexity comes from the fact that you're using regmap_update_bits(), so you
> have to have those masks and shifts everywhere.
> 
> As you saw I solved this by keeping track of the various properties set by the
> driver, another way could be to have a register shadow that you update and
> flush - it would save you from the attrs-game in pinconf set/get.
> 

Yep. I agree. Currently my implementation is fragile because it depend
on order of the properties in the DT files. Probably will be best if all
settings accumulated by pin_config_group_get() are applied in correct
order in pinmux.enable().

<snip>

> 
> > +#define QPNP_PIN_PHYSICAL_OFFSET               1
> 
> If you just assign the of_node to the gpio_chip and then call
> gpiochip_add_pin_range(_, _, 1, 0, _) this will give you the off-by-one you're
> looking for.

I have tried this, but something went wrong, I don't remember what. 
Will check again.

> > +
> > +struct qpnp_pinctrl {
> > +       struct device *dev;
> > +       struct regmap *map;
> > +       struct pinctrl_dev *ctrl;
> > +       struct pinctrl_desc desc;
> > +       struct gpio_chip chip;
> > +
> > +       struct qpnp_padinfo *pads;
> 
> As Linus commented on my code, you shouldn't have a list of pads here, you
> should reference them via desc.pins[x].drv_data

I was trying to avoid multiple small allocation. Will check again.

<snip>

> 
> > +       const struct qpnp_chipinfo *info;
> 
> 'info' is only used during probe, no reason to keep a pointer around.

probe and discover functions.

> 
> > +       const char *const *groups;
> > +       const char *const *functions;
> > +};
> > +
> > +static inline struct qpnp_pinctrl *to_qpnp_pinctrl(struct gpio_chip *chip)
> > +{
> > +       return container_of(chip, struct qpnp_pinctrl, chip);
> > +};
> 
> I see little point in breaking out the container_of into its own function.

It fit driver state dereferencing to 80 columns.

> > +
> > +static int qpnp_conv_to_pin(struct qpnp_pinctrl *qctrl,
> > +                          struct qpnp_padinfo *pad, unsigned param,

<snip>

> > +       case PIN_CONFIG_BIAS_PULL_UP:
> > +               if (type != QPNP_MPP_TYPE)
> > +                       return -EINVAL;
> 
> The binding documentation says that it's okay to just state "bias-pull-up" and
> it will be configured as 30uA pull up. 

But is valid only for MPP's.

> I would like us to keep the binding
> documentation as it is now, but then you need to handle that here.
> 

Right.

> > +               switch (val) {
> > +               case 0:
> > +                       val = QPNP_MPP_PULL_UP_OPEN;
> > +                       break;
> > +               case 600:
> > +                       val = QPNP_MPP_PULL_UP_0P6KOHM;
> > +                       break;
> > +               case 10000:
> > +                       val = QPNP_MPP_PULL_UP_10KOHM;
> > +                       break;
> > +               case 30000:
> > +                       val = QPNP_MPP_PULL_UP_30KOHM;
> > +                       break;
> > +               default:
> > +                       return -EINVAL;
> > +               }

<snip>

> > +       case QPNP_PINCONF_MODE:
> > +               if ((pad->modes & BIT(val)) == 0)
> > +                       return -ENXIO;
> 
> -EINVAL?
> 
> > +               attr[0].addr  = QPNP_REG_MODE_CTL;
> > +               attr[0].shift = QPNP_REG_MODE_SEL_SHIFT;
> > +               attr[0].mask  = QPNP_REG_MODE_SEL_MASK;
> > +               attr[0].val   = val;
> 
> What happens here if I day output-high; qcom,mode = "digital-input"; ?

It depends on the order of which they are found in DT files :-).
Will fix it.

> > +
> > +static int qpnp_of_xlate(struct gpio_chip *chip,
> > +                      const struct of_phandle_args *gpio_desc, u32 *flags)
> > +{
> 
> Assign chip.of_node when registering the gpio_chip and remove this function.
> 
> > +       struct qpnp_pinctrl *qctrl = to_qpnp_pinctrl(chip);
> > +       struct qpnp_padinfo *pad;
> > +       unsigned pin = gpio_desc->args[0] - QPNP_PIN_PHYSICAL_OFFSET;

This way gpiolib see pin numbers like pinctrl framework, starting from 1.
I will try to offset gpio range with 1 in registration.
> 
> > +
> > +static int qpnp_control_init(struct qpnp_pinctrl *qctrl,
> > +                         struct qpnp_padinfo *pad)
> 
> This is not "initializing any control", it's rather a
> qpnp_get_available_modes(type, subtype) function.
> 
> > +{
> > +       pad->modes = 0;
> > +
> > +       if (pad->type == QPNP_GPIO_TYPE) {
> > +               switch (pad->subtype) {
> > +               case QPNP_GPIO_SUBTYPE_GPIO_4CH:
> > +               case QPNP_GPIO_SUBTYPE_GPIOC_4CH:
> > +               case QPNP_GPIO_SUBTYPE_GPIO_8CH:
> > +               case QPNP_GPIO_SUBTYPE_GPIOC_8CH:
> 
> Looks like this switch is not of much use, consider dropping it.
> 
> > +
> > +                       /* only GPIO is supported*/
> > +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN);
> > +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_OUT);
> > +                       pad->modes |= BIT(QPNP_PIN_MODE_DIG_IN_OUT);
> > +                       break;
> > +               default:
> > +                       dev_err(qctrl->dev, "invalid GPIO subtype 0x%x\n",
> > +                               pad->subtype);
> 
> Is this really going to happen? This would indicate that we have "compatible"
> chips that suddenly have some new type of gpio/mpp block.

And we should be somehow be noticed, right?

> 
> > +                       return -EINVAL;
> > +               }

<snip>

> > +static int qpnp_pinctrl_probe(struct platform_device *pdev)
> > +{
> > +       struct device *dev = &pdev->dev;
> > +       const struct qpnp_chipinfo *qchip;
> > +       struct qpnp_pinctrl *qctrl;
> > +
> > +       qctrl = devm_kzalloc(dev, sizeof(*qctrl), GFP_KERNEL);
> > +       if (!qctrl)
> > +               return -ENOMEM;
> > +
> > +       platform_set_drvdata(pdev, qctrl);
> > +
> > +       qchip = of_match_node(qpnp_pinctrl_of_match, dev->of_node)->data;
> > +
> > +       qctrl->info = qchip;
> 
> No need to keep track of the info after qpnp_discover().

Right.

<snip>

> > +
> > +static const struct qpnp_chipinfo qpnp_pm8841_mpp_info = {
> > +       .npins  = 4,
> > +       .base   = 0xa000,
> 
> Read this from the reg property instead.

If SPMI bindings are changed a little bit, there will be no
need to hard code even number of pins :-). Will do.

> Regards,
> Bjorn

Thank you for detailed review. I agree with sniped comments
and will fix them.

Regards,
Ivan



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

* Re: [PATCH v3 6/6] ARM: dts: qcom: Add APQ8074 Dragonboard PMIC GPIO bindings
  2014-08-20 23:06   ` Bjorn Andersson
@ 2014-08-25 14:04     ` Ivan T. Ivanov
  0 siblings, 0 replies; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-25 14:04 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Russell King, linux-arm-msm, devicetree, linux-kernel

On Wed, 2014-08-20 at 16:06 -0700, Bjorn Andersson wrote:
> On Mon 11 Aug 08:40 PDT 2014, Ivan T. Ivanov wrote:
> > diff --git a/arch/arm/boot/dts/qcom-apq8074-dragonboard-pmics-pins.dtsi b/arch/arm/boot/dts/qcom-apq8074-dragonboard-pmics-pins.dtsi
> [...]
> > +
> > +&pm8941_gpios {
> > +
> > +	pinctrl-names = "default";
> > +	pinctrl-0 = <&pm8941_gpios_default>;
> > +
> > +	pm8941_gpios_default: default {
> > +		group-1 {
> > +			pins = "gpio1", "gpio2", "gpio5", "gpio29";
> > +			function = PMIC_GPIO_FUNC_NORMAL;
> 
> This looks really strange, I can't get myself to stop thinking that you forgot
> the <> around this (I know it's a string). I don't like these defines.

Same here, any suggestions? Except function tables in driver :-)

> 
> > +			input-enable;
> > +			power-source = <PM8941_GPIO_S3>;
> > +			qcom,pull-up-strength = <PMIC_GPIO_PULL_UP_30>;
> > +		};
> > +		group-6 {	/* TUSB3_HUB-RESET */
> 
> Why not name the nodes something useful?
> If you name it tusb3-hub-reset you can skip the comment.

Sure.
 
> Regards,
> Bjorn

Thanks, 
Ivan

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

* Re: [PATCH v3 5/6] ARM: dts: qcom: Add PM8941 and PM8841 pinctrl nodes
  2014-08-14  7:33   ` Pramod Gurav
@ 2014-08-25 14:06     ` Ivan T. Ivanov
  0 siblings, 0 replies; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-25 14:06 UTC (permalink / raw)
  To: Pramod Gurav
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Russell King, Bjorn Andersson, linux-arm-msm, devicetree,
	linux-kernel

On Thu, 2014-08-14 at 13:03 +0530, Pramod Gurav wrote:
> Hi Ivan,
> This patch fail to apply on linux-next Tree. Probably because this is
> based on Stanimir's older patch series "Support for Qualcomm QPNP
> PMIC's". He has posted v4 of them on which it fails to apply. Am I correct?

Right, sorry about that. I should clearly stated that this is based on Stan's patches.

Regards,
Ivan

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

* Re: [PATCH v3 3/6] pinctrl: Add documentation for SPMI PMIC pinctrl driver bindings
  2014-08-13 14:32   ` Stephen Boyd
@ 2014-08-25 14:07     ` Ivan T. Ivanov
  0 siblings, 0 replies; 27+ messages in thread
From: Ivan T. Ivanov @ 2014-08-25 14:07 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Bjorn Andersson, linux-arm-msm, devicetree, linux-kernel

On Wed, 2014-08-13 at 07:32 -0700, Stephen Boyd wrote:
> On 08/11, Ivan T. Ivanov wrote:
> > 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..0a64567
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
> > @@ -0,0 +1,179 @@
> > +Qualcomm PMIC Multi-Purpose Pin (MPP) block
> > +
> > +This binding describes the MPP block(s) found in the 8xxx series of pmics from
> > +Qualcomm.
> > +
> > +- compatible:
> > +	Usage: required
> > +	Value type: <string>
> > +	Definition: must be one of:
> > +		    "qcom,pm8841-mpp"
> > +		    "qcom,pm8941-mpp"
> > +		    "qcom,pma8084-mpp"
> > +
> > +- reg:
> > +	Usage: required
> > +	Value type: <u32>
> > +	Definition: Register base of the gpio block
> 
> MPP?
> 
> > +
> > +- interrupts:
> > +	Usage: required
> > +	Value type: <prop-encoded-array>
> > +	Definition: Must contain an array of encoded interrupt specifiers for
> > +		    each available gpio
> 
> MPP? Maybe gpio makes sense.

Thank you Stephen. Will fix them in next version.

regards,
Ivan

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

* Re: [PATCH v3 2/6] pinctrl: Introduce pinctrl driver for Qualcomm SSBI PMIC's
  2014-08-20 22:13         ` Bjorn Andersson
@ 2014-11-03  8:48           ` Srinivas Kandagatla
  2014-11-04  2:25             ` Bjorn Andersson
  0 siblings, 1 reply; 27+ messages in thread
From: Srinivas Kandagatla @ 2014-11-03  8:48 UTC (permalink / raw)
  To: Bjorn Andersson, Bjorn Andersson
  Cc: Ivan T. Ivanov, Linus Walleij, Grant Likely, Rob Herring,
	linux-arm-msm, devicetree, linux-kernel

Hi Bjorn,

On 20/08/14 23:13, Bjorn Andersson wrote:
> On Wed, Aug 20, 2014 at 2:28 PM, Bjorn Andersson
> <bjorn.andersson@sonymobile.com> wrote:
>> On Wed 20 Aug 01:06 PDT 2014, Srinivas Kandagatla wrote:
>>> 2> Looking back at v3.4 kernel, for gpio modes, BIT(0) of bank 0 is set
>>> to enable gpio mode. without this bit driver does not work for output pins.
>>>
>>
>> Thanks, I missed that.
>>
>> Unfortunately, setting that bit results in input not working - the interrupt
>> bits are never set for gpios that have that bit set. I'm trying to figure out
>> why this is the case before sending out the new version...
>
> With help from Andy Gross this is now corrected as well, turned out
> that BIT(0) in bank0 controls if the "direction" is considered. As I
> was tricked by the multiple levels of indirection in the codeaurora
> version I got these wrong.
>
> Will send out an updated version shortly.
Did you get a chance to spin a new version of this patcheset?

--srini
>
> Regards,
> Bjorn
>

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

* Re: [PATCH v3 2/6] pinctrl: Introduce pinctrl driver for Qualcomm SSBI PMIC's
  2014-11-03  8:48           ` Srinivas Kandagatla
@ 2014-11-04  2:25             ` Bjorn Andersson
  0 siblings, 0 replies; 27+ messages in thread
From: Bjorn Andersson @ 2014-11-04  2:25 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Bjorn Andersson, Ivan T. Ivanov, Linus Walleij, Grant Likely,
	Rob Herring, linux-arm-msm, devicetree, linux-kernel

On Mon 03 Nov 00:48 PST 2014, Srinivas Kandagatla wrote:

> Hi Bjorn,
> 
> On 20/08/14 23:13, Bjorn Andersson wrote:
> > On Wed, Aug 20, 2014 at 2:28 PM, Bjorn Andersson
> > <bjorn.andersson@sonymobile.com> wrote:
> >> On Wed 20 Aug 01:06 PDT 2014, Srinivas Kandagatla wrote:
> >>> 2> Looking back at v3.4 kernel, for gpio modes, BIT(0) of bank 0 is set
> >>> to enable gpio mode. without this bit driver does not work for output pins.
> >>>
> >>
> >> Thanks, I missed that.
> >>
> >> Unfortunately, setting that bit results in input not working - the interrupt
> >> bits are never set for gpios that have that bit set. I'm trying to figure out
> >> why this is the case before sending out the new version...
> >
> > With help from Andy Gross this is now corrected as well, turned out
> > that BIT(0) in bank0 controls if the "direction" is considered. As I
> > was tricked by the multiple levels of indirection in the codeaurora
> > version I got these wrong.
> >
> > Will send out an updated version shortly.
> Did you get a chance to spin a new version of this patcheset?
> 

Hi Srini,

Unfortunately it ended up on my todo list waiting for a discussion on
irq_read_line(). As this seems to fall in place I will try to get this updated
and sent out.

Sorry for the delays.

Regards,
Bjorn

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

end of thread, other threads:[~2014-11-04  2:25 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-11 15:40 [PATCH v3 0/6] Qualcomm PMIC pin controller drivers Ivan T. Ivanov
2014-08-11 15:40 ` [PATCH v3 1/6] pinctrl: Device tree bindings for Qualcomm pm8xxx gpio block Ivan T. Ivanov
2014-08-16 15:24   ` Daniel
2014-08-18  7:16     ` Ivan T. Ivanov
2014-08-20 22:10       ` Bjorn Andersson
2014-08-20 22:27   ` Bjorn Andersson
2014-08-25 13:14     ` Ivan T. Ivanov
2014-08-11 15:40 ` [PATCH v3 2/6] pinctrl: Introduce pinctrl driver for Qualcomm SSBI PMIC's Ivan T. Ivanov
     [not found]   ` <1407771634-14946-3-git-send-email-iivanov-NEYub+7Iv8PQT0dZR+AlfA@public.gmane.org>
2014-08-20  8:06     ` Srinivas Kandagatla
2014-08-20  8:06       ` Srinivas Kandagatla
2014-08-20 21:28       ` Bjorn Andersson
2014-08-20 22:13         ` Bjorn Andersson
2014-11-03  8:48           ` Srinivas Kandagatla
2014-11-04  2:25             ` Bjorn Andersson
2014-08-11 15:40 ` [PATCH v3 3/6] pinctrl: Add documentation for SPMI PMIC pinctrl driver bindings Ivan T. Ivanov
2014-08-13 14:32   ` Stephen Boyd
2014-08-25 14:07     ` Ivan T. Ivanov
2014-08-11 15:40 ` [PATCH v3 4/6] pinctrl: Qualcomm SPMI PMIC pin controller driver Ivan T. Ivanov
2014-08-21  6:16   ` Bjorn Andersson
     [not found]     ` <20140821061607.GF16274-/MT0OVThwyLZJqsBc5GL+g@public.gmane.org>
2014-08-25 14:00       ` Ivan T. Ivanov
2014-08-25 14:00         ` Ivan T. Ivanov
2014-08-11 15:40 ` [PATCH v3 5/6] ARM: dts: qcom: Add PM8941 and PM8841 pinctrl nodes Ivan T. Ivanov
2014-08-14  7:33   ` Pramod Gurav
2014-08-25 14:06     ` Ivan T. Ivanov
2014-08-11 15:40 ` [PATCH v3 6/6] ARM: dts: qcom: Add APQ8074 Dragonboard PMIC GPIO bindings Ivan T. Ivanov
2014-08-20 23:06   ` Bjorn Andersson
2014-08-25 14:04     ` Ivan T. Ivanov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.