2014-07-07 15:12:24

by Ivan T. Ivanov

[permalink] [raw]
Subject: [PATCH 0/4] New Qualcomm PMIC pin controller drivers

From: "Ivan T. Ivanov" <[email protected]>

Hi,

This set of patches adds pin control drivers for Multi-purpose
pin (MPP) and General-purpose pin (GPIO) controllers found
in Qualcomm 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.

PMIC PM8941 have 8 MPP's and 36 GPIO's. PMIC PM8841 have 4 MPP's.

Ivan T. Ivanov (4):
pinctrl: qpnp: Qualcomm PMIC pin controller driver
pinctrl: qcom: Add documentation for pinctrl-qpnp binding
pinctrl: qcom: Add PM8941 and PM8941 pinctrl drivers
ARM: dts: qcom: Add PM8941 and PM8841 pinctrl nodes

.../bindings/pinctrl/qcom,qpnp-pinctrl.txt | 78 ++
arch/arm/boot/dts/qcom-msm8974.dtsi | 16 +
drivers/pinctrl/Kconfig | 27 +
drivers/pinctrl/Makefile | 3 +
drivers/pinctrl/pinctrl-pm8841.c | 84 ++
drivers/pinctrl/pinctrl-pm8941.c | 96 ++
drivers/pinctrl/pinctrl-qpnp.c | 1454 ++++++++++++++++++++
drivers/pinctrl/pinctrl-qpnp.h | 41 +
include/dt-bindings/pinctrl/pinctrl-qpnp.h | 76 +
9 files changed, 1875 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/qcom,qpnp-pinctrl.txt
create mode 100644 drivers/pinctrl/pinctrl-pm8841.c
create mode 100644 drivers/pinctrl/pinctrl-pm8941.c
create mode 100644 drivers/pinctrl/pinctrl-qpnp.c
create mode 100644 drivers/pinctrl/pinctrl-qpnp.h
create mode 100644 include/dt-bindings/pinctrl/pinctrl-qpnp.h

--
1.8.3.2


2014-07-07 15:12:25

by Ivan T. Ivanov

[permalink] [raw]
Subject: [PATCH 4/4] ARM: dts: qcom: Add PM8941 and PM8841 pinctrl nodes

From: "Ivan T. Ivanov" <[email protected]>

Add nodes for PM8941 and PM8841 GPIO and MPP subfunctions.

Signed-off-by: Ivan T. Ivanov <[email protected]>
---
arch/arm/boot/dts/qcom-msm8974.dtsi | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
index c7ae7ba..7daca4b 100644
--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
@@ -267,6 +267,14 @@
reg-names = "rtc", "alarm";
interrupts = <0x0 0x61 0x1 IRQ_TYPE_EDGE_RISING>;
};
+
+ pm8941_pinctrl: pins@a000 {
+ compatible = "qcom,pm8941-pinctrl";
+ reg = <0xa000>, <0xc000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ label = "pm8941-pinctrl";
+ };
};

usid1: pm8941@1 {
@@ -281,6 +289,14 @@
reg = <0x4 SPMI_USID>;
#address-cells = <1>;
#size-cells = <0>;
+
+ pm8841_pinctrl: pins@a000 {
+ compatible = "qcom,pm8841-pinctrl";
+ reg = <0xa000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ label = "pm8841-pinctrl";
+ };
};

usid5: pm8841@5 {
--
1.8.3.2

2014-07-07 15:12:48

by Ivan T. Ivanov

[permalink] [raw]
Subject: [PATCH 1/4] pinctrl: qpnp: Qualcomm PMIC pin controller driver

From: "Ivan T. Ivanov" <[email protected]>

This is the pinctrl, pinmux, pinconf and gpiolib driver for the
Qualcomm GPIO and MPP subfunction blocks found in the PMIC chips.

Signed-off-by: Ivan T. Ivanov <[email protected]>
---
drivers/pinctrl/Kconfig | 10 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-qpnp.c | 1454 ++++++++++++++++++++++++++++
drivers/pinctrl/pinctrl-qpnp.h | 41 +
include/dt-bindings/pinctrl/pinctrl-qpnp.h | 76 ++
5 files changed, 1582 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-qpnp.c
create mode 100644 drivers/pinctrl/pinctrl-qpnp.h
create mode 100644 include/dt-bindings/pinctrl/pinctrl-qpnp.h

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 0042ccb..297c84d 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -383,6 +383,16 @@ config PINCTRL_PALMAS
open drain configuration for the Palmas series devices like
TPS65913, TPS80036 etc.

+config PINCTRL_QPNP
+ bool
+ select PINMUX
+ select PINCONF
+ select GENERIC_PINCONF
+ select GPIOLIB
+ help
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+ Qualcomm GPIO and MPP blocks found in the Qualcomm PMIC's chips.
+
config PINCTRL_S3C24XX
bool "Samsung S3C24XX SoC pinctrl driver"
depends on ARCH_S3C24XX
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index c4b5d40..bfbdba1 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o
obj-$(CONFIG_PINCTRL_DB8500) += pinctrl-nomadik-db8500.o
obj-$(CONFIG_PINCTRL_DB8540) += pinctrl-nomadik-db8540.o
obj-$(CONFIG_PINCTRL_PALMAS) += pinctrl-palmas.o
+obj-$(CONFIG_PINCTRL_QPNP) += pinctrl-qpnp.o
obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_SIRF) += sirf/
diff --git a/drivers/pinctrl/pinctrl-qpnp.c b/drivers/pinctrl/pinctrl-qpnp.c
new file mode 100644
index 0000000..9d4ef70
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-qpnp.c
@@ -0,0 +1,1454 @@
+/* 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/pinctrl-qpnp.h>
+
+#include "core.h"
+#include "pinctrl-utils.h"
+#include "pinctrl-qpnp.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
+
+#define QPNP_PIN_MODE_GPIO_INVALID 3
+#define QPNP_PIN_MODE_MPP_INVALID 7
+
+/*
+ * 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-source = <>"
+ */
+#define QPNP_PIN_VIN_4CH_INVALID 5
+#define QPNP_PIN_VIN_8CH_INVALID 8
+
+/*
+ * Source Select (GPIO) / Enable Select (MPP) - select alternate function for
+ * the pin. Certain pins can be paired (shorted) with each other. Some pins
+ * can act as alternate functions. In the context of GPIO, this acts as a
+ * source select. For MPPs, this is an enable select.
+ * To be used with "output-high = <>"
+ */
+#define QPNP_PIN_SEL_INVALID 8
+
+/*
+ * Analog Output (MPP) - Set the analog output reference.
+ * To be used with "qcom,aout-ctrl = <>"
+ */
+#define QPNP_PIN_AOUT_INVALID 8
+
+/*
+ * Analog Input (MPP) - Set the source for analog input.
+ * To be used with "qcom,ain-ctrl = <>"
+ */
+#define QPNP_PIN_AIN_INVALID 8
+
+/*
+ * Output type (GPIO) - indicates pin should be configured as CMOS or
+ * open drain.
+ */
+#define QPNP_PIN_OUT_BUF_CMOS 0
+#define QPNP_PIN_OUT_BUF_OPEN_DRAIN_NMOS 1
+#define QPNP_PIN_OUT_BUF_OPEN_DRAIN_PMOS 2
+
+/*
+ * Pull Up Values (GPIO) - 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_PIN_GPIO_PULL_UP_30 0
+#define QPNP_PIN_GPIO_PULL_UP_1P5 1
+#define QPNP_PIN_GPIO_PULL_UP_31P5 2
+#define QPNP_PIN_GPIO_PULL_UP_1P5_30 3
+#define QPNP_PIN_GPIO_PULL_DN 4
+#define QPNP_PIN_GPIO_PULL_NO 5
+#define QPNP_PIN_GPIO_PULL_INVALID 6
+
+/*
+ * Pull Up Values (MPP) - 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_PIN_MPP_PULL_UP_0P6KOHM 0
+#define QPNP_PIN_MPP_PULL_UP_OPEN 1
+#define QPNP_PIN_MPP_PULL_UP_10KOHM 2
+#define QPNP_PIN_MPP_PULL_UP_30KOHM 3
+#define QPNP_PIN_MPP_PULL_INVALID 4
+
+/* Out Strength (GPIO) - the amount of current supplied for an output GPIO */
+#define QPNP_PIN_OUT_STRENGTH_LOW 1
+#define QPNP_PIN_OUT_STRENGTH_MED 2
+#define QPNP_PIN_OUT_STRENGTH_HIGH 3
+#define QPNP_PIN_OUT_STRENGTH_INVALID 4
+
+/*
+ * 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
+
+/* Current Sink (MPP) Set the the amount of current to sync in mA. */
+#define QPNP_PIN_CS_OUT_5MA 0
+#define QPNP_PIN_CS_OUT_10MA 1
+#define QPNP_PIN_CS_OUT_15MA 2
+#define QPNP_PIN_CS_OUT_20MA 3
+#define QPNP_PIN_CS_OUT_25MA 4
+#define QPNP_PIN_CS_OUT_30MA 5
+#define QPNP_PIN_CS_OUT_35MA 6
+#define QPNP_PIN_CS_OUT_40MA 7
+
+/* 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
+
+#define QPNP_NUM_CTL_REGS 13
+
+/* QPNP_REG_MODE_CTL */
+#define QPNP_REG_OUT_SRC_SEL_SHIFT 0
+#define QPNP_REG_OUT_SRC_SEL_MASK 0xf
+#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
+
+/* Qualcomm specific pin configurations */
+#define QPNP_PINCONF_PARAM_AIN_CTRL (PIN_CONFIG_END + 1)
+#define QPNP_PINCONF_PARAM_AOUT_CTRL (PIN_CONFIG_END + 2)
+
+struct qpnp_pindesc {
+ u16 offset; /* address offset in SPMI device */
+ u32 index; /* offset from GPIO info base */
+ u8 type; /* peripheral hardware type */
+ u8 subtype; /* peripheral hardware subtype */
+ u8 major; /* digital major version */
+ u8 num_regs; /* control register count */
+ u8 cache[QPNP_NUM_CTL_REGS]; /* control register cache */
+};
+
+#define QPNP_REG_ADDR(qdesc, reg) ((qdesc)->offset + reg)
+#define QPNP_REG_CACHE(qdesc, reg) (&(qdesc)->cache[reg - QPNP_REG_MODE_CTL])
+
+struct qpnp_pingroups {
+ const char **pins;
+ unsigned npins;
+};
+
+struct qpnp_pinctrl {
+ struct device *dev;
+ struct regmap *map;
+ struct pinctrl_dev *ctrl;
+ struct gpio_chip chip;
+ struct pinctrl_desc desc;
+ struct qpnp_pindesc *pins;
+ struct pinctrl_gpio_range range;
+ struct qpnp_pingroups groups[QPNP_MUX_CNT];
+ const struct qpnp_pinctrl_info *info;
+};
+
+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[] = {
+ /* QPNP_PIN_AIN_AMUX_CH5...QPNP_PIN_AIN_AMUX_ABUS4 */
+ {"qcom,ain-ctrl", QPNP_PINCONF_PARAM_AIN_CTRL, 0},
+ /* QPNP_PIN_AOUT_1V25...QPNP_PIN_AOUT_ABUS4 */
+ {"qcom,aout-ctrl", QPNP_PINCONF_PARAM_AOUT_CTRL, 0},
+};
+
+static const char *const qpnp_functions_names[] = {
+ [QPNP_MUX_GPIO] = "gpio",
+ [QPNP_MUX_AIN] = "mpp-ain",
+ [QPNP_MUX_AOUT] = "mpp-aout",
+ [QPNP_MUX_CS] = "mpp-cs"
+};
+
+static inline struct qpnp_pindesc *qpnp_get_desc(struct qpnp_pinctrl *qctrl,
+ unsigned pin)
+{
+ if (pin > qctrl->info->npads) {
+ dev_warn(qctrl->dev, "invalid pin number %d", pin);
+ return NULL;
+ }
+
+ return &qctrl->pins[pin];
+}
+
+static inline u8 QPNP_GET(u8 *buff, int shift, int mask)
+{
+ return (*buff & mask) >> shift;
+}
+
+static inline void QPNP_SET(u8 *buff, int shift, int mask, int value)
+{
+ *buff &= ~mask;
+ *buff |= (value << shift) & mask;
+}
+
+static int qpnp_read_regs(struct qpnp_pinctrl *qctrl,
+ struct qpnp_pindesc *qdesc)
+{
+ int left = qdesc->num_regs;
+ u8 *buff = QPNP_REG_CACHE(qdesc, QPNP_REG_MODE_CTL);
+ u16 addr = QPNP_REG_ADDR(qdesc, QPNP_REG_MODE_CTL);
+ int ret;
+
+ while (left > 0) {
+ ret = regmap_bulk_read(qctrl->map, addr, buff,
+ left < 8 ? left : 8);
+ if (ret)
+ return ret;
+ left -= 8;
+ buff += 8;
+ addr += 8;
+ }
+
+ return 0;
+}
+
+static int qpnp_write_regs(struct qpnp_pinctrl *qctrl,
+ struct qpnp_pindesc *qdesc)
+{
+ int left = qdesc->num_regs;
+ u8 *buff = QPNP_REG_CACHE(qdesc, QPNP_REG_MODE_CTL);
+ u16 addr = QPNP_REG_ADDR(qdesc, QPNP_REG_MODE_CTL);
+ int ret;
+
+ while (left > 0) {
+ ret = regmap_bulk_write(qctrl->map, addr, buff,
+ left < 8 ? left : 8);
+ if (ret)
+ return ret;
+ left -= 8;
+ buff += 8;
+ addr += 8;
+ }
+
+ return 0;
+}
+
+/*
+ * Calculate the minimum number of registers that must be read / written
+ * in order to satisfy the full feature set of the given pin.
+ */
+static int qpnp_control_init(struct qpnp_pinctrl *qctrl,
+ struct qpnp_padinfo *qpad,
+ struct qpnp_pindesc *qdesc)
+{
+ if (qdesc->type == QPNP_GPIO_TYPE) {
+ switch (qdesc->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:
+ qdesc->num_regs = 7;
+
+ /* only GPIO is supported*/
+ qpad->funcs[QPNP_MUX_AIN] = QPNP_MUX_GPIO;
+ qpad->funcs[QPNP_MUX_AOUT] = QPNP_MUX_GPIO;
+ qpad->funcs[QPNP_MUX_CS] = QPNP_MUX_GPIO;
+
+ qctrl->groups[QPNP_MUX_GPIO].npins++;
+ break;
+ default:
+ dev_err(qctrl->dev, "invalid GPIO subtype 0x%x\n",
+ qdesc->subtype);
+ return -EINVAL;
+ }
+
+ } else if (qdesc->type == QPNP_MPP_TYPE) {
+ switch (qdesc->subtype) {
+ case QPNP_MPP_SUBTYPE_4CH_NO_SINK:
+ case QPNP_MPP_SUBTYPE_ULT_4CH_NO_SINK:
+ qdesc->num_regs = 12;
+
+ /* Current sink not supported*/
+ qpad->funcs[QPNP_MUX_CS] = QPNP_MUX_GPIO;
+
+ qctrl->groups[QPNP_MUX_GPIO].npins++;
+ qctrl->groups[QPNP_MUX_AIN].npins++;
+ qctrl->groups[QPNP_MUX_AOUT].npins++;
+ break;
+ case QPNP_MPP_SUBTYPE_4CH_NO_ANA_OUT:
+ case QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
+ qdesc->num_regs = 13;
+
+ /* Analog output not supported*/
+ qpad->funcs[QPNP_MUX_AOUT] = QPNP_MUX_GPIO;
+
+ qctrl->groups[QPNP_MUX_GPIO].npins++;
+ qctrl->groups[QPNP_MUX_AIN].npins++;
+ qctrl->groups[QPNP_MUX_CS].npins++;
+ break;
+ case QPNP_MPP_SUBTYPE_4CH_FULL_FUNC:
+ case QPNP_MPP_SUBTYPE_8CH_FULL_FUNC:
+ qdesc->num_regs = 13;
+
+ qctrl->groups[QPNP_MUX_GPIO].npins++;
+ qctrl->groups[QPNP_MUX_AIN].npins++;
+ qctrl->groups[QPNP_MUX_AOUT].npins++;
+ qctrl->groups[QPNP_MUX_CS].npins++;
+ break;
+ default:
+ dev_err(qctrl->dev, "invalid MPP subtype 0x%x\n",
+ qdesc->subtype);
+ return -EINVAL;
+ }
+ } else {
+ dev_err(qctrl->dev, "invalid type 0x%x\n", qdesc->type);
+ return -EINVAL;
+ }
+
+ return qpnp_read_regs(qctrl, qdesc);
+}
+
+static int qpnp_conv_to_pinattrib(struct qpnp_pindesc *qdesc, unsigned param,
+ unsigned val, struct qpnp_pinattrib attr[4])
+{
+ unsigned type, subtype;
+ int nattrs = 1;
+
+ type = qdesc->type;
+ subtype = qdesc->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_PIN_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_PIN_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_PIN_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_PIN_GPIO_PULL_NO;
+ else
+ attr[0].val = QPNP_PIN_MPP_PULL_UP_OPEN;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ if (type == QPNP_GPIO_TYPE) {
+ if (val >= QPNP_PIN_GPIO_PULL_INVALID)
+ return -EINVAL;
+ } else {
+ switch (val) {
+ case 0:
+ val = QPNP_PIN_MPP_PULL_UP_OPEN;
+ break;
+ case 600:
+ val = QPNP_PIN_MPP_PULL_UP_0P6KOHM;
+ break;
+ case 10000:
+ val = QPNP_PIN_MPP_PULL_UP_10KOHM;
+ break;
+ case 30000:
+ val = QPNP_PIN_MPP_PULL_UP_30KOHM;
+ break;
+ default:
+ return -EINVAL;
+ 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 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;
+ attr[0].val = QPNP_PIN_GPIO_PULL_DN;
+ 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_GPIO_TYPE) {
+ if (val >= QPNP_PIN_OUT_STRENGTH_INVALID || val == 0)
+ return -EINVAL;
+ attr[0].addr = QPNP_REG_DIG_OUT_CTL;
+ attr[0].shift = QPNP_REG_OUT_STRENGTH_SHIFT;
+ attr[0].mask = QPNP_REG_OUT_STRENGTH_MASK;
+ attr[0].val = val;
+ } else {
+ 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;
+ if (val >= QPNP_PIN_SEL_INVALID)
+ return -EINVAL;
+ if (type == QPNP_MPP_TYPE &&
+ (val == QPNP_PIN_SEL_FUNC_1 ||
+ val == QPNP_PIN_SEL_FUNC_2))
+ return -EINVAL;
+ attr[0].addr = QPNP_REG_MODE_CTL;
+ attr[0].shift = QPNP_REG_OUT_SRC_SEL_SHIFT;
+ attr[0].mask = QPNP_REG_OUT_SRC_SEL_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_PARAM_AOUT_CTRL:
+ if (type != QPNP_MPP_TYPE)
+ return -ENXIO;
+ if (val >= QPNP_PIN_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_PARAM_AIN_CTRL:
+ if (type != QPNP_MPP_TYPE)
+ return -ENXIO;
+ if (val >= QPNP_PIN_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;
+ default:
+ return -EINVAL;
+ }
+
+ return nattrs;
+}
+
+
+static int qpnp_conv_from_pinattrib(struct qpnp_pinctrl *qctrl,
+ struct qpnp_pindesc *qdesc,
+ unsigned param, unsigned *val)
+{
+ struct qpnp_pinattrib attr;
+ unsigned type, subtype, field;
+ unsigned int addr;
+ u8 *buff;
+
+ *val = 0;
+ type = qdesc->type;
+ subtype = qdesc->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_GPIO_TYPE) {
+ attr.addr = QPNP_REG_DIG_OUT_CTL;
+ attr.shift = QPNP_REG_OUT_STRENGTH_SHIFT;
+ attr.mask = QPNP_REG_OUT_STRENGTH_MASK;
+ } else {
+ 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_OUT_SRC_SEL_SHIFT;
+ attr.mask = QPNP_REG_OUT_SRC_SEL_MASK;
+ break;
+ case QPNP_PINCONF_PARAM_AOUT_CTRL:
+ 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_PARAM_AIN_CTRL:
+ 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;
+ default:
+ return -EINVAL;
+ }
+
+ addr = QPNP_REG_ADDR(qdesc, attr.addr);
+ buff = QPNP_REG_CACHE(qdesc, attr.addr);
+
+ field = QPNP_GET(buff, attr.shift, attr.mask);
+
+ switch (param) {
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ if (field == QPNP_PIN_OUT_BUF_CMOS)
+ *val = 1;
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ if (field == QPNP_PIN_OUT_BUF_OPEN_DRAIN_NMOS)
+ *val = 1;
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+ if (field == QPNP_PIN_OUT_BUF_OPEN_DRAIN_PMOS)
+ *val = 1;
+ break;
+ case PIN_CONFIG_BIAS_DISABLE:
+ if (type == QPNP_GPIO_TYPE) {
+ if (field == QPNP_PIN_GPIO_PULL_NO)
+ *val = 1;
+ } else {
+ if (field == QPNP_PIN_MPP_PULL_UP_OPEN)
+ *val = 1;
+ }
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ if (type == QPNP_GPIO_TYPE) {
+ *val = field;
+ } else {
+ switch (field) {
+ default:
+ case QPNP_PIN_MPP_PULL_UP_OPEN:
+ *val = 0;
+ break;
+ case QPNP_PIN_MPP_PULL_UP_0P6KOHM:
+ *val = 600;
+ break;
+ case QPNP_PIN_MPP_PULL_UP_10KOHM:
+ *val = 10000;
+ break;
+ case QPNP_PIN_MPP_PULL_UP_30KOHM:
+ *val = 30000;
+ break;
+ }
+ }
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ if (field == QPNP_PIN_GPIO_PULL_DN)
+ *val = 1;
+ break;
+ case PIN_CONFIG_POWER_SOURCE:
+ *val = field;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ if (type == QPNP_GPIO_TYPE)
+ *val = field;
+ else
+ *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_PARAM_AOUT_CTRL:
+ *val = field;
+ break;
+ case QPNP_PINCONF_PARAM_AIN_CTRL:
+ *val = field;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int qpnp_get_groups_cnt(struct pinctrl_dev *pctldev)
+{
+ struct qpnp_pinctrl *qpctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ /* Every PIN is a group */
+ return qpctrl->info->npads;
+}
+
+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->info->pads[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->info->desc[pin].number;
+ *num_pins = 1;
+ return 0;
+}
+
+static int qpnp_parse_dt_config(struct device *dev, struct device_node *np,
+ unsigned long **configs, unsigned int *nconfigs)
+{
+ struct qpnp_pinbindings *par;
+ unsigned long *cfg;
+ unsigned int ncfg = 0;
+ int ret;
+ int i;
+ u32 val;
+
+ if (!np)
+ return -EINVAL;
+
+ /* allocate a temporary array big enough to hold one of each option */
+ cfg = kcalloc(ARRAY_SIZE(qpnp_pinbindings), sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(qpnp_pinbindings); i++) {
+ par = &qpnp_pinbindings[i];
+ 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_info(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 all */
+ 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:
+ kfree(cfg);
+ return ret;
+}
+
+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 num_configs = 0;
+ struct property *prop;
+ const char *group;
+ int ret;
+
+ ret = qpnp_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 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_PIN;
+
+ 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_cnt,
+ .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_functions_names);
+}
+
+static const char *qpnp_get_function_name(struct pinctrl_dev *pctldev,
+ unsigned function)
+{
+ return qpnp_functions_names[function];
+}
+
+static int qpnp_get_function_groups(struct pinctrl_dev *pctldev,
+ unsigned function,
+ const char *const **groups,
+ unsigned *const num_groups)
+{
+ struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = qctrl->groups[function].pins;
+ *num_groups = qctrl->groups[function].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);
+ const struct qpnp_padinfo *qpad;
+ struct qpnp_pindesc *qdesc;
+ unsigned int addr, val;
+ int idx;
+ u8 *buff;
+
+ qpad = &qctrl->info->pads[pin];
+
+ for (idx = 0; idx < ARRAY_SIZE(qpad->funcs); idx++)
+ if (qpad->funcs[idx] == function)
+ break;
+
+ if (WARN_ON(idx == ARRAY_SIZE(qpad->funcs)))
+ return -EINVAL;
+
+ qdesc = qpnp_get_desc(qctrl, pin);
+ if (!qdesc)
+ return -EINVAL;
+
+ switch (function) {
+ case QPNP_MUX_GPIO:
+ val = QPNP_PIN_MODE_DIG_IN_OUT;
+ break;
+ case QPNP_MUX_AIN:
+ val = QPNP_PIN_MODE_AIN;
+ break;
+ case QPNP_MUX_AOUT:
+ val = QPNP_PIN_MODE_AOUT;
+ break;
+ case QPNP_MUX_CS:
+ val = QPNP_PIN_MODE_SINK;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ if (qdesc->type == QPNP_GPIO_TYPE &&
+ val >= QPNP_PIN_MODE_GPIO_INVALID) {
+ return -EINVAL;
+ } else if (qdesc->type == QPNP_MPP_TYPE) {
+ if (val >= QPNP_PIN_MODE_MPP_INVALID)
+ return -EINVAL;
+ if ((qdesc->subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT ||
+ qdesc->subtype == QPNP_MPP_SUBTYPE_ULT_4CH_NO_SINK) &&
+ (val == QPNP_PIN_MODE_BIDIR))
+ return -ENXIO;
+ }
+
+ addr = QPNP_REG_ADDR(qdesc, QPNP_REG_MODE_CTL);
+ buff = QPNP_REG_CACHE(qdesc, QPNP_REG_MODE_CTL);
+
+ QPNP_SET(buff, QPNP_REG_MODE_SEL_SHIFT, QPNP_REG_MODE_SEL_MASK, val);
+
+ addr = QPNP_REG_ADDR(qdesc, QPNP_REG_EN_CTL);
+ buff = QPNP_REG_CACHE(qdesc, QPNP_REG_EN_CTL);
+
+ QPNP_SET(buff, QPNP_REG_MASTER_EN_SHIFT, QPNP_REG_MASTER_EN_MASK, 1);
+
+ return qpnp_write_regs(qctrl, qdesc);
+}
+
+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_pindesc *qdesc;
+ u8 val, en_mask, *buff;
+ unsigned int addr;
+ int ret;
+
+ qdesc = qpnp_get_desc(qctrl, offset);
+ if (!qdesc)
+ return -ENODEV;
+
+ buff = QPNP_REG_CACHE(qdesc, QPNP_REG_MODE_CTL);
+
+ /* GPIO val is from RT status if input is enabled */
+ if ((*buff & QPNP_REG_MODE_SEL_MASK) == QPNP_PIN_MODE_DIG_IN) {
+
+ addr = QPNP_REG_ADDR(qdesc, QPNP_REG_STATUS1);
+ ret = regmap_bulk_read(qctrl->map, addr, &val, 1);
+
+ if (qdesc->type == QPNP_GPIO_TYPE && qdesc->major == 0)
+ en_mask = QPNP_REG_STATUS1_GPIO_EN_REV0_MASK;
+ else if (qdesc->type == QPNP_GPIO_TYPE &&
+ qdesc->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_OUT_SRC_SEL_MASK;
+ ret = ret >> QPNP_REG_OUT_SRC_SEL_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_pindesc *qdesc;
+ unsigned int addr;
+ u8 *buff;
+
+ qdesc = qpnp_get_desc(qctrl, offset);
+ if (!qdesc)
+ return;
+
+ addr = QPNP_REG_ADDR(qdesc, QPNP_REG_MODE_CTL);
+ buff = QPNP_REG_CACHE(qdesc, QPNP_REG_MODE_CTL);
+
+ QPNP_SET(buff, QPNP_REG_OUT_SRC_SEL_SHIFT,
+ QPNP_REG_OUT_SRC_SEL_MASK, value);
+
+ regmap_write(qctrl->map, addr, *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);
+ struct qpnp_pindesc *qdesc = qpnp_get_desc(qctrl, pin);
+ unsigned param = pinconf_to_config_param(*config);
+ unsigned arg;
+ int ret;
+
+ /* Convert pinconf values to register values */
+ ret = qpnp_conv_from_pinattrib(qctrl, qdesc, 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 num_configs)
+{
+ struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+ struct qpnp_pindesc *qdesc = qpnp_get_desc(qctrl, pin);
+ struct qpnp_pinattrib attr[4];
+ unsigned param;
+ unsigned arg;
+ int idx, ret, reg;
+ u8 *buff;
+
+ for (idx = 0; idx < num_configs; 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_pinattrib(qdesc, param, arg, attr);
+ if (ret < 0)
+ return -EINVAL;
+
+ for (reg = 0; reg < ret; reg++) {
+ buff = QPNP_REG_CACHE(qdesc, attr[reg].addr);
+ QPNP_SET(buff, attr[reg].mask, attr[reg].shift,
+ attr[reg].val);
+ }
+ }
+
+ return qpnp_write_regs(qctrl, qdesc);
+}
+
+static void qpnp_config_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned pin)
+{
+ struct qpnp_pinctrl *qctrl = pinctrl_dev_get_drvdata(pctldev);
+ struct qpnp_pindesc *qdesc;
+ bool is_digital = true;
+ bool is_input = false;
+ const char *mode = NULL, *name = NULL;
+ unsigned en, val;
+ u8 *buff;
+ int ret;
+
+ qdesc = qpnp_get_desc(qctrl, pin);
+ if (!qdesc)
+ return;
+
+ name = qctrl->info->pads[pin].name;
+
+ ret = qpnp_read_regs(qctrl, qdesc);
+ if (ret) {
+ seq_printf(s, " %-8s: read error %d", name, ret);
+ return;
+ }
+
+ buff = QPNP_REG_CACHE(qdesc, QPNP_REG_MODE_CTL);
+ val = QPNP_GET(buff, QPNP_REG_MODE_SEL_SHIFT, QPNP_REG_MODE_SEL_MASK);
+
+ buff = QPNP_REG_CACHE(qdesc, QPNP_REG_EN_CTL);
+ en = QPNP_GET(buff, QPNP_REG_MASTER_EN_SHIFT, QPNP_REG_MASTER_EN_MASK);
+
+ switch (val) {
+ case QPNP_PIN_MODE_DIG_IN:
+ is_input = true;
+ mode = "dig-in";
+ break;
+ case QPNP_PIN_MODE_DIG_OUT:
+ mode = "dig-out";
+ break;
+ case QPNP_PIN_MODE_DIG_IN_OUT:
+ is_input = true;
+ mode = "dig-io";
+ break;
+ case QPNP_PIN_MODE_BIDIR:
+ is_digital = false;
+ mode = "ana-io";
+ break;
+ case QPNP_PIN_MODE_AIN:
+ is_input = true;
+ is_digital = false;
+ mode = "ana-in";
+ break;
+ case QPNP_PIN_MODE_AOUT:
+ is_digital = false;
+ mode = "ana-out";
+ break;
+ case QPNP_PIN_MODE_SINK:
+ is_digital = false;
+ mode = "ana-sink";
+ break;
+ default:
+ return;
+ }
+
+ seq_printf(s, " %-8s: %-9s %s", name, mode, !en ? "high-Z" : "");
+}
+
+static const struct pinconf_ops qpnp_pinconf_ops = {
+ .pin_config_get = qpnp_config_get,
+ .pin_config_set = qpnp_config_set,
+ .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_pindesc *qdesc;
+
+ if (chip->of_gpio_n_cells < 2) {
+ dev_err(qctrl->dev, "of_gpio_n_cells < 2\n");
+ return -EINVAL;
+ }
+
+ qdesc = qpnp_get_desc(qctrl, gpio_desc->args[0]);
+ if (!qdesc)
+ return -EINVAL;
+
+ if (flags)
+ *flags = gpio_desc->args[1];
+
+ return qdesc->index;
+}
+
+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,
+ .dbg_show = qpnp_dbg_show,
+};
+
+static int qpnp_discover(struct qpnp_pinctrl *qctrl,
+ const struct qpnp_pinctrl_info *data)
+{
+ struct device *dev = qctrl->dev;
+ struct qpnp_pindesc *qdesc;
+ struct qpnp_padinfo *qpad;
+ int idx, ret, cnt, gios, ais, aos, css;
+ const char **pins;
+
+ for (qdesc = qctrl->pins, idx = 0; idx < data->npads; idx++, qdesc++) {
+
+ qpad = &data->pads[idx];
+ qdesc->offset = qpad->base;
+ qdesc->index = idx;
+
+ /* Read up to including QPNP_REG_SUBTYPE */
+ ret = regmap_bulk_read(qctrl->map, QPNP_REG_ADDR(qdesc, 0),
+ qdesc->cache, QPNP_REG_SUBTYPE + 1);
+ if (ret)
+ return ret;
+
+ qdesc->major = qdesc->cache[QPNP_REG_DIG_MAJOR_REV];
+ qdesc->type = qdesc->cache[QPNP_REG_TYPE];
+ qdesc->subtype = qdesc->cache[QPNP_REG_SUBTYPE];
+
+ /* Assume PIN support all functions */
+ qpad->funcs[QPNP_MUX_GPIO] = QPNP_MUX_GPIO;
+ qpad->funcs[QPNP_MUX_AIN] = QPNP_MUX_AIN;
+ qpad->funcs[QPNP_MUX_AOUT] = QPNP_MUX_AOUT;
+ qpad->funcs[QPNP_MUX_CS] = QPNP_MUX_CS;
+
+ ret = qpnp_control_init(qctrl, qpad, qdesc);
+ if (ret)
+ return ret;
+ }
+
+ for (idx = QPNP_MUX_GPIO; idx < QPNP_MUX_CNT; idx++) {
+ cnt = qctrl->groups[idx].npins;
+ if (cnt) {
+ pins = devm_kzalloc(dev, cnt * sizeof(pins),
+ GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+ qctrl->groups[idx].pins = pins;
+ }
+ }
+
+ gios = ais = aos = css = 0;
+ /* now scan through again and populate the lookup table */
+ for (qdesc = qctrl->pins, idx = 0; idx < data->npads; idx++, qdesc++) {
+ qpad = &data->pads[idx];
+ if (qpad->funcs[QPNP_MUX_GPIO] == QPNP_MUX_GPIO)
+ qctrl->groups[QPNP_MUX_GPIO].pins[gios++] = qpad->name;
+ if (qpad->funcs[QPNP_MUX_AIN] == QPNP_MUX_AIN)
+ qctrl->groups[QPNP_MUX_AIN].pins[ais++] = qpad->name;
+ if (qpad->funcs[QPNP_MUX_AOUT] == QPNP_MUX_AOUT)
+ qctrl->groups[QPNP_MUX_AOUT].pins[aos++] = qpad->name;
+ if (qpad->funcs[QPNP_MUX_CS] == QPNP_MUX_CS)
+ qctrl->groups[QPNP_MUX_CS].pins[css++] = qpad->name;
+ }
+
+ return 0;
+}
+
+int qpnp_pinctrl_probe(struct platform_device *pdev,
+ const struct qpnp_pinctrl_info *data)
+{
+ struct device *dev = &pdev->dev;
+ struct qpnp_pinctrl *qctrl;
+ int ret;
+
+ qctrl = devm_kzalloc(dev, sizeof(*qctrl), GFP_KERNEL);
+ if (!qctrl)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, qctrl);
+
+ qctrl->dev = &pdev->dev;
+ qctrl->map = dev_get_regmap(dev->parent, NULL);
+ qctrl->info = data;
+
+ qctrl->chip = qpnp_gpio_template;
+ qctrl->chip.base = -1;
+ qctrl->chip.ngpio = data->npads;
+ qctrl->chip.label = dev_name(dev);
+ qctrl->chip.of_gpio_n_cells = 2;
+ qctrl->chip.can_sleep = 1;
+
+ 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 = data->desc;
+ qctrl->desc.npins = data->npads;
+
+ qctrl->range.name = dev_name(dev);
+ qctrl->range.id = 0;
+ qctrl->range.base = 0;
+ qctrl->range.npins = data->npads;
+ qctrl->range.gc = &qctrl->chip;
+
+ qctrl->pins = devm_kzalloc(dev, sizeof(*qctrl->pins) * data->npads,
+ GFP_KERNEL);
+ if (!qctrl->pins)
+ return -ENOMEM;
+
+ ret = qpnp_discover(qctrl, data);
+ if (ret)
+ return ret;
+
+ ret = gpiochip_add(&qctrl->chip);
+ if (ret) {
+ dev_err(qctrl->dev, "can't add gpio chip\n");
+ return ret;
+ }
+
+ qctrl->ctrl = pinctrl_register(&qctrl->desc, dev, qctrl);
+ if (!qctrl->ctrl)
+ ret = -ENODEV;
+ else
+ pinctrl_add_gpio_range(qctrl->ctrl, &qctrl->range);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qpnp_pinctrl_probe);
+
+int qpnp_pinctrl_remove(struct platform_device *pdev)
+{
+ struct qpnp_pinctrl *qctrl = platform_get_drvdata(pdev);
+
+ pinctrl_unregister(qctrl->ctrl);
+
+ return gpiochip_remove(&qctrl->chip);
+}
+EXPORT_SYMBOL_GPL(qpnp_pinctrl_remove);
diff --git a/drivers/pinctrl/pinctrl-qpnp.h b/drivers/pinctrl/pinctrl-qpnp.h
new file mode 100644
index 0000000..13ffc77
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-qpnp.h
@@ -0,0 +1,41 @@
+/* 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.
+ */
+#ifndef __PINCTRL_QPNP_H__
+#define __PINCTRL_QPNP_H__
+
+struct platform_device;
+
+enum qpnp_functions {
+ QPNP_MUX_GPIO,
+ QPNP_MUX_AIN,
+ QPNP_MUX_AOUT,
+ QPNP_MUX_CS,
+ QPNP_MUX_CNT,
+};
+
+struct qpnp_padinfo {
+ unsigned base;
+ char name[8];
+ enum qpnp_functions funcs[QPNP_MUX_CNT];
+};
+
+struct qpnp_pinctrl_info {
+ unsigned npads;
+ struct qpnp_padinfo *pads;
+ struct pinctrl_pin_desc *desc;
+};
+
+int qpnp_pinctrl_probe(struct platform_device *pdev,
+ const struct qpnp_pinctrl_info *data);
+int qpnp_pinctrl_remove(struct platform_device *pdev);
+
+#endif
diff --git a/include/dt-bindings/pinctrl/pinctrl-qpnp.h b/include/dt-bindings/pinctrl/pinctrl-qpnp.h
new file mode 100644
index 0000000..ae61ed6
--- /dev/null
+++ b/include/dt-bindings/pinctrl/pinctrl-qpnp.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This header provides constants for Qualcomm QPNP pinctrl bindings.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DT_BINDINGS_PINCTRL_QPNP_H
+#define _DT_BINDINGS_PINCTRL_QPNP_H
+
+/*
+ * 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-source = <>"
+ */
+#define QPNP_PIN_VIN0 0
+#define QPNP_PIN_VIN1 1
+#define QPNP_PIN_VIN2 2
+#define QPNP_PIN_VIN3 3
+#define QPNP_PIN_VIN4 4
+#define QPNP_PIN_VIN5 5
+#define QPNP_PIN_VIN6 6
+#define QPNP_PIN_VIN7 7
+
+/*
+ * Source Select (GPIO) / Enable Select (MPP) - select alternate function for
+ * the pin. Certain pins can be paired (shorted) with each other. Some pins
+ * can act as alternate functions. In the context of GPIO, this acts as a
+ * source select. For MPPs, this is an enable select.
+ * To be used with "output-high = <>"
+ */
+#define QPNP_PIN_SEL_FUNC_CONSTANT 0
+#define QPNP_PIN_SEL_FUNC_PAIRED 1
+#define QPNP_PIN_SEL_FUNC_1 2
+#define QPNP_PIN_SEL_FUNC_2 3
+#define QPNP_PIN_SEL_DTEST1 4
+#define QPNP_PIN_SEL_DTEST2 5
+#define QPNP_PIN_SEL_DTEST3 6
+#define QPNP_PIN_SEL_DTEST4 7
+
+/*
+ * Analog Output (MPP) - Set the analog output reference.
+ * To be used with "qcom,aout-ctrl = <>"
+ */
+#define QPNP_PIN_AOUT_1V25 0
+#define QPNP_PIN_AOUT_0V625 1
+#define QPNP_PIN_AOUT_0V3125 2
+#define QPNP_PIN_AOUT_MPP 3
+#define QPNP_PIN_AOUT_ABUS1 4
+#define QPNP_PIN_AOUT_ABUS2 5
+#define QPNP_PIN_AOUT_ABUS3 6
+#define QPNP_PIN_AOUT_ABUS4 7
+
+/*
+ * Analog Input (MPP) - Set the source for analog input. /
+ * To be used with "qcom,ain-ctrl = <>"
+ */
+#define QPNP_PIN_AIN_CH5 0
+#define QPNP_PIN_AIN_CH6 1
+#define QPNP_PIN_AIN_CH7 2
+#define QPNP_PIN_AIN_CH8 3
+#define QPNP_PIN_AIN_ABUS1 4
+#define QPNP_PIN_AIN_ABUS2 5
+#define QPNP_PIN_AIN_ABUS3 6
+#define QPNP_PIN_AIN_ABUS4 7
+
+#endif
--
1.8.3.2

2014-07-07 15:13:11

by Ivan T. Ivanov

[permalink] [raw]
Subject: [PATCH 2/4] pinctrl: qcom: Add documentation for pinctrl-qpnp binding

From: "Ivan T. Ivanov" <[email protected]>

DT binding documentation for qcom,pm8941-pinctrl and
qcom,pm8841-pinctrl drivers.

Signed-off-by: Ivan T. Ivanov <[email protected]>
---
.../bindings/pinctrl/qcom,qpnp-pinctrl.txt | 78 ++++++++++++++++++++++
1 file changed, 78 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/qcom,qpnp-pinctrl.txt

diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,qpnp-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,qpnp-pinctrl.txt
new file mode 100644
index 0000000..0b24860
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,qpnp-pinctrl.txt
@@ -0,0 +1,78 @@
+Qualcomm Technologies (QTI) QPNP pinctrl controller
+
+Qualcomm Technologies (QTI) PMIC chips integrate SPMI based MPP (multiple
+purpose pin) and GPIO pin configuration hardware modules. These modules control
+the pin settings, including types/functions/directions/pulls/drive-strength/
+input/output, etc.
+
+They are two types of pins inside PMIC chips. Multi-Purpose Pin (MPP) and
+General Purpuse Pins (GPIO)
+
+MPP pins are supporting following functions:
+Digital Input, Digital Output, Analog Input, Analog Output and Current Sink
+
+GPIO pins are supporting following functions:
+Digital Input, Digital Output
+
+Required Properties:
+ - compatible: Should contain "qcom,pm8941-pinctrl" or "qcom,pm8841-pinctrl".
+ - reg: MPP's configuration registers map offset
+ GPIO's configuration registers map offset - optional
+ - gpio-controller: Marks the device node as a GPIO controller.
+ - #gpio-cells : Should be two.
+ The first cell is the gpio pin number and the
+ second cell is used for optional parameters.
+
+Please refer to ../gpio/gpio.txt for a general description of GPIO 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".
+
+Qualcomm's pin configuration nodes act as a container for an abitrary number of
+subnodes. Each of these subnodes represents some desired configuration for a
+pin, a group, or a list of pins or groups. This configuration can include the
+mux function to select on those pin(s)/group(s), and various pin configuration
+parameters, such as pull-up, drive strength, etc.
+
+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.
+
+Valid functions for MPPs are gpio, mpp-ain, mpp-aout, mpp-cs.
+Valid function for GPIOs is gpio.
+
+Valid names for PM8841 pins are mpp1-mpp4.
+Valid names for PM8941 pins are mpp1-mpp8 and gpio1-gpio36.
+
+The following generic properties as defined in pinctrl-bindings.txt are valid
+to specify in a GPIO pin configuration subnode:
+
+ pins, function, bias-disable, bias-pull-down, bias-pull,up, drive-strength.
+ bias-high-impedance, drive-push-pull, drive-open-drain, drive-open-source,
+ input-enable, input-disable, power-source, output-low, output-high
+
+The following generic properties as defined in pinctrl-bindings.txt are valid
+to specify in a MPP pin configuration subnode:
+
+ pins, function, bias-disable, bias-pull,up, drive-strength.
+ bias-high-impedance, input-enable, input-disable, power-source, output-low,
+ output-high,
+
+The following non-standard properties are valid to specify in a MPP pin
+configuration subnode:
+ qcom,ain-ctrl, qcom,aout-ctrl
+
+Example:
+
+ pm8941_pinctrl: pins@a000 {
+ compatible = "qcom,pm8941-pinctrl";
+ reg = <0xa000>, <0xc000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
--
1.8.3.2

2014-07-07 15:13:10

by Ivan T. Ivanov

[permalink] [raw]
Subject: [PATCH 3/4] pinctrl: qcom: Add PM8941 and PM8941 pinctrl drivers

From: "Ivan T. Ivanov" <[email protected]>

Add new pinctrl driver for GPIO and MPP subfunctions found in
Qualcomm PMIC chips.

Signed-off-by: Ivan T. Ivanov <[email protected]>
---
drivers/pinctrl/Kconfig | 17 +++++++
drivers/pinctrl/Makefile | 2 +
drivers/pinctrl/pinctrl-pm8841.c | 84 +++++++++++++++++++++++++++++++++++
drivers/pinctrl/pinctrl-pm8941.c | 96 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 199 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-pm8841.c
create mode 100644 drivers/pinctrl/pinctrl-pm8941.c

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 297c84d..914f43c 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -383,6 +383,23 @@ config PINCTRL_PALMAS
open drain configuration for the Palmas series devices like
TPS65913, TPS80036 etc.

+config PINCTRL_PM8841
+ tristate "Qualcomm PM8841 pin controller driver"
+ depends on OF
+ select PINCTRL_QPNP
+ help
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+ Qualcomm MPP subfunction block found in the Qualcomm PM8841 PMIC chip.
+
+config PINCTRL_PM8941
+ tristate "Qualcomm PM8941 pin controller driver"
+ depends on OF
+ select PINCTRL_QPNP
+ help
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+ Qualcomm MPP and GPIO subfunction block found in the Qualcomm PM8941
+ PMIC chip.
+
config PINCTRL_QPNP
bool
select PINMUX
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index bfbdba1..3d1fe38 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -47,6 +47,8 @@ obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o
obj-$(CONFIG_PINCTRL_DB8500) += pinctrl-nomadik-db8500.o
obj-$(CONFIG_PINCTRL_DB8540) += pinctrl-nomadik-db8540.o
obj-$(CONFIG_PINCTRL_PALMAS) += pinctrl-palmas.o
+obj-$(CONFIG_PINCTRL_PM8841) += pinctrl-pm8841.o
+obj-$(CONFIG_PINCTRL_PM8941) += pinctrl-pm8941.o
obj-$(CONFIG_PINCTRL_QPNP) += pinctrl-qpnp.o
obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
diff --git a/drivers/pinctrl/pinctrl-pm8841.c b/drivers/pinctrl/pinctrl-pm8841.c
new file mode 100644
index 0000000..0e46536
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-pm8841.c
@@ -0,0 +1,84 @@
+/*
+ * 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/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/platform_device.h>
+
+#include "pinctrl-qpnp.h"
+
+#define PM8841_MPP_CNT 4
+
+static int pm8841_pinctrl_probe(struct platform_device *qdev)
+{
+ struct device *dev = &qdev->dev;
+ struct qpnp_pinctrl_info *data;
+ struct pinctrl_pin_desc *desc;
+ struct qpnp_padinfo *pads;
+ struct resource *res;
+ int idx, number;
+
+ res = platform_get_resource(qdev, IORESOURCE_REG, 0);
+ if (!res)
+ return -ENXIO;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ pads = devm_kzalloc(dev, sizeof(*pads) * PM8841_MPP_CNT, GFP_KERNEL);
+ if (!pads)
+ return -ENOMEM;
+
+ desc = devm_kzalloc(dev, sizeof(*desc) * PM8841_MPP_CNT, GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ data->npads = PM8841_MPP_CNT;
+ data->pads = pads;
+ data->desc = desc;
+ number = 0;
+
+ for (idx = 0; idx < PM8841_MPP_CNT; idx++, pads++, desc++) {
+ snprintf(pads->name, ARRAY_SIZE(pads->name), "mpp%d", idx + 1);
+ pads->base = res->start + (idx * 0x100);
+ desc->number = number++;
+ desc->name = pads->name;
+ }
+
+ return qpnp_pinctrl_probe(qdev, data);
+}
+
+static const struct of_device_id pm8841_pinctrl_of_match[] = {
+ { .compatible = "qcom,pm8841-pinctrl", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pm8841_pinctrl_of_match);
+
+static struct platform_driver pm8841_pinctrl_driver = {
+ .driver = {
+ .name = "pm8841-pinctrl",
+ .owner = THIS_MODULE,
+ .of_match_table = pm8841_pinctrl_of_match,
+ },
+ .probe = pm8841_pinctrl_probe,
+ .remove = qpnp_pinctrl_remove,
+};
+module_platform_driver(pm8841_pinctrl_driver);
+
+MODULE_AUTHOR("Ivan T. Ivanov <[email protected]>");
+MODULE_DESCRIPTION("Qualcomm PM8841 pin control driver");
+MODULE_ALIAS("platform:pm8841-pinctrl");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/pinctrl-pm8941.c b/drivers/pinctrl/pinctrl-pm8941.c
new file mode 100644
index 0000000..c3817e8
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-pm8941.c
@@ -0,0 +1,96 @@
+/*
+ * 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/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/platform_device.h>
+
+#include "pinctrl-qpnp.h"
+
+#define PM8941_MPP_CNT 8
+#define PM8941_GPIO_CNT 36
+#define PM8941_PAD_CNT (PM8941_MPP_CNT + PM8941_GPIO_CNT)
+
+static int pm8941_pinctrl_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct qpnp_pinctrl_info *data;
+ struct pinctrl_pin_desc *desc;
+ struct qpnp_padinfo *pads;
+ struct resource *gpios, *mpps;
+ int idx, number;
+
+ mpps = platform_get_resource(pdev, IORESOURCE_REG, 0);
+ if (!mpps)
+ return -ENXIO;
+
+ gpios = platform_get_resource(pdev, IORESOURCE_REG, 1);
+ if (!gpios)
+ return -ENXIO;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ pads = devm_kzalloc(dev, sizeof(*pads) * PM8941_PAD_CNT, GFP_KERNEL);
+ if (!pads)
+ return -ENOMEM;
+
+ desc = devm_kzalloc(dev, sizeof(*desc) * PM8941_PAD_CNT, GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ data->npads = PM8941_PAD_CNT;
+ data->pads = pads;
+ data->desc = desc;
+ number = 0;
+
+ for (idx = 0; idx < PM8941_MPP_CNT; idx++, pads++, desc++) {
+ snprintf(pads->name, ARRAY_SIZE(pads->name), "mpp%d", idx + 1);
+ pads->base = mpps->start + (idx * 0x100);
+ desc->number = number++;
+ desc->name = pads->name;
+ }
+
+ for (idx = 0; idx < PM8941_GPIO_CNT; idx++, pads++, desc++) {
+ snprintf(pads->name, ARRAY_SIZE(pads->name), "gpio%d", idx + 1);
+ pads->base = gpios->start + (idx * 0x100);
+ desc->number = number++;
+ desc->name = pads->name;
+ }
+
+ return qpnp_pinctrl_probe(pdev, data);
+}
+
+static const struct of_device_id pm8941_pinctrl_of_match[] = {
+ { .compatible = "qcom,pm8941-pinctrl", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pm8941_pinctrl_of_match);
+
+static struct platform_driver pm8941_pinctrl_driver = {
+ .driver = {
+ .name = "pm8941-pinctrl",
+ .owner = THIS_MODULE,
+ .of_match_table = pm8941_pinctrl_of_match,
+ },
+ .probe = pm8941_pinctrl_probe,
+ .remove = qpnp_pinctrl_remove,
+};
+module_platform_driver(pm8941_pinctrl_driver);
+
+MODULE_AUTHOR("Ivan T. Ivanov <[email protected]>");
+MODULE_DESCRIPTION("Qualcomm PM8841 pin control driver");
+MODULE_ALIAS("platform:pm8941-pinctrl");
+MODULE_LICENSE("GPL v2");
--
1.8.3.2

2014-07-08 08:14:53

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH 1/4] pinctrl: qpnp: Qualcomm PMIC pin controller driver

On Mon, Jul 07, 2014 at 06:11:30PM +0300, Ivan T. Ivanov wrote:

> +struct qpnp_pindesc {
> + u16 offset; /* address offset in SPMI device */
> + u32 index; /* offset from GPIO info base */
> + u8 type; /* peripheral hardware type */
> + u8 subtype; /* peripheral hardware subtype */
> + u8 major; /* digital major version */
> + u8 num_regs; /* control register count */
> + u8 cache[QPNP_NUM_CTL_REGS]; /* control register cache */
> +};

The device uses a regmap, why do you also need to open code a register
cache here?


Attachments:
(No filename) (530.00 B)
signature.asc (836.00 B)
Digital signature
Download all attachments

2014-07-08 11:47:43

by Ivan T. Ivanov

[permalink] [raw]
Subject: Re: [PATCH 1/4] pinctrl: qpnp: Qualcomm PMIC pin controller driver

On Tue, 2014-07-08 at 09:52 +0200, Mark Brown wrote:
> On Mon, Jul 07, 2014 at 06:11:30PM +0300, Ivan T. Ivanov wrote:
>
> > +struct qpnp_pindesc {
> > + u16 offset; /* address offset in SPMI device */
> > + u32 index; /* offset from GPIO info base */
> > + u8 type; /* peripheral hardware type */
> > + u8 subtype; /* peripheral hardware subtype */
> > + u8 major; /* digital major version */
> > + u8 num_regs; /* control register count */
> > + u8 cache[QPNP_NUM_CTL_REGS]; /* control register cache */
> > +};
>
> The device uses a regmap, why do you also need to open code a register
> cache here?

Just leftover from downstream driver. Will remove it.

Regards,
Ivan

2014-07-09 09:43:03

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 0/4] New Qualcomm PMIC pin controller drivers

On Mon, Jul 7, 2014 at 5:11 PM, Ivan T. Ivanov <[email protected]> wrote:

> This set of patches adds pin control drivers for Multi-purpose
> pin (MPP) and General-purpose pin (GPIO) controllers found
> in Qualcomm 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.
>
> PMIC PM8941 have 8 MPP's and 36 GPIO's. PMIC PM8841 have 4 MPP's.
>
> Ivan T. Ivanov (4):
> pinctrl: qpnp: Qualcomm PMIC pin controller driver
> pinctrl: qcom: Add documentation for pinctrl-qpnp binding
> pinctrl: qcom: Add PM8941 and PM8941 pinctrl drivers
> ARM: dts: qcom: Add PM8941 and PM8841 pinctrl nodes

Oh, I just spent some 45 minutes reviewing an 8xxx pinctrl driver from
Björn Andersson
that *seems* to be doing exactly the same thing.

Now I have two drivers from people outside Qualcomm :-)

Some of my review comments on Björn's driver (like using SI units with the pin
config) are actually adressed in this patch set. The idea to split in subdrivers
per-ASIC may be good? I don't really know.

Can you two guys *PLEASE* join efforts and combine your drivers into one?

If we do this efficiently it should be possible to get one single real
nice driver
in the end.

If you start arguing about nittt gritty stuff like who's going to be author on
the commit I'll get really disappointed ;-)

Yours,
Linus Walleij

2014-07-09 11:13:31

by Ivan T. Ivanov

[permalink] [raw]
Subject: Re: [PATCH 0/4] New Qualcomm PMIC pin controller drivers

Hi,

On Wed, 2014-07-09 at 11:43 +0200, Linus Walleij wrote:
> On Mon, Jul 7, 2014 at 5:11 PM, Ivan T. Ivanov <[email protected]> wrote:
>
> > This set of patches adds pin control drivers for Multi-purpose
> > pin (MPP) and General-purpose pin (GPIO) controllers found
> > in Qualcomm 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.
> >
> > PMIC PM8941 have 8 MPP's and 36 GPIO's. PMIC PM8841 have 4 MPP's.
> >
> > Ivan T. Ivanov (4):
> > pinctrl: qpnp: Qualcomm PMIC pin controller driver
> > pinctrl: qcom: Add documentation for pinctrl-qpnp binding
> > pinctrl: qcom: Add PM8941 and PM8941 pinctrl drivers
> > ARM: dts: qcom: Add PM8941 and PM8841 pinctrl nodes
>
> Oh, I just spent some 45 minutes reviewing an 8xxx pinctrl driver from
> Björn Andersson
> that *seems* to be doing exactly the same thing.
>
> Now I have two drivers from people outside Qualcomm :-)
>
> Some of my review comments on Björn's driver (like using SI units with the pin
> config) are actually adressed in this patch set. The idea to split in subdrivers
> per-ASIC may be good? I don't really know.
>
> Can you two guys *PLEASE* join efforts and combine your drivers into one?

Not sure. Björn patches cover older PMIC chips, if not mistaken, mine
cover PMIC's used with APQ8074 and onward [1]. Main difference is
the bus which connects them to SoC, interrupts handling, runtime
pin type detection and register map.

Regards,
Ivan

[1] pm8019, pm8110, pm8226, pm8841, pm8916, pm8941, pm8994, pma8084,
pmd9635, pmd9635, pmi8962, pmi8994.

2014-07-09 11:44:04

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 0/4] New Qualcomm PMIC pin controller drivers

On Wed, Jul 9, 2014 at 1:13 PM, Ivan T. Ivanov <[email protected]> wrote:
> On Wed, 2014-07-09 at 11:43 +0200, Linus Walleij wrote:
>> On Mon, Jul 7, 2014 at 5:11 PM, Ivan T. Ivanov <[email protected]> wrote:
>>
>> > This set of patches adds pin control drivers for Multi-purpose
>> > pin (MPP) and General-purpose pin (GPIO) controllers found
>> > in Qualcomm 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.
>> >
>> > PMIC PM8941 have 8 MPP's and 36 GPIO's. PMIC PM8841 have 4 MPP's.
>> >
>> > Ivan T. Ivanov (4):
>> > pinctrl: qpnp: Qualcomm PMIC pin controller driver
>> > pinctrl: qcom: Add documentation for pinctrl-qpnp binding
>> > pinctrl: qcom: Add PM8941 and PM8941 pinctrl drivers
>> > ARM: dts: qcom: Add PM8941 and PM8841 pinctrl nodes
>>
>> Oh, I just spent some 45 minutes reviewing an 8xxx pinctrl driver from
>> Björn Andersson
>> that *seems* to be doing exactly the same thing.
>>
>> Now I have two drivers from people outside Qualcomm :-)
>>
>> Some of my review comments on Björn's driver (like using SI units with the pin
>> config) are actually adressed in this patch set. The idea to split in subdrivers
>> per-ASIC may be good? I don't really know.
>>
>> Can you two guys *PLEASE* join efforts and combine your drivers into one?
>
> Not sure. Björn patches cover older PMIC chips, if not mistaken, mine
> cover PMIC's used with APQ8074 and onward [1]. Main difference is
> the bus which connects them to SoC, interrupts handling, runtime
> pin type detection and register map.

Oh OK... then atleast there is something wrong with the naming of
Björns patches as they are 8xxx and your chips are also named
8xxx ...

Anyway I will create a patch to move the current msm driver down
to a folder named "qcom" and we can atleast collect these drivers
in a single place since the chipsets are related, OK?

Then I guess even if the chips are totally unrelated it'd be interesting
to have you two guys cross-review each other's drivers so the behaviour
is consistent across qualcomm platforms.

Yours,
Linus Walleij

2014-07-09 14:02:52

by Bjorn Andersson

[permalink] [raw]
Subject: Re: [PATCH 0/4] New Qualcomm PMIC pin controller drivers

On Wed, Jul 9, 2014 at 4:43 AM, Linus Walleij <[email protected]> wrote:
> On Wed, Jul 9, 2014 at 1:13 PM, Ivan T. Ivanov <[email protected]> wrote:
>> On Wed, 2014-07-09 at 11:43 +0200, Linus Walleij wrote:
>>> On Mon, Jul 7, 2014 at 5:11 PM, Ivan T. Ivanov <[email protected]> wrote:
>>> Oh, I just spent some 45 minutes reviewing an 8xxx pinctrl driver from
>>> Björn Andersson
>>> that *seems* to be doing exactly the same thing.
>>>

Sorry for not getting comments out on Ivan's patches before you got here.

>>> Now I have two drivers from people outside Qualcomm :-)
>>>

:)

>>> Some of my review comments on Björn's driver (like using SI units with the pin
>>> config) are actually adressed in this patch set. The idea to split in subdrivers
>>> per-ASIC may be good? I don't really know.
>>>
>>> Can you two guys *PLEASE* join efforts and combine your drivers into one?
>>

The drivers can't be merged; the hardware is more or less identical,
but the register map is completely different.

However, the device tree bindings are a different thing; as the
properties used to describe the hardware doesn't relate to how we
communicate with it I think we should be able to (and therefor should)
use the same documentation for the two (rather 7) chips.

>> Not sure. Björn patches cover older PMIC chips, if not mistaken, mine
>> cover PMIC's used with APQ8074 and onward [1]. Main difference is
>> the bus which connects them to SoC, interrupts handling, runtime
>> pin type detection and register map.
>

Correct Ivan; we do however share the same issues related to how to do
interrupt handling, units for properties and how to split/reuse
between gpio and mpp. Also we have solved the pins vs groups vs
functions slightly different, that should all be aligned I think.

> Oh OK... then atleast there is something wrong with the naming of
> Björns patches as they are 8xxx and your chips are also named
> 8xxx ...
>

So, the pm8xxx series of drivers refer to pm8018, pm8038, pm8058,
pm8917 and pm8921 which are used in platforms in 8660, 8960 and 8064
(among others).
The driver that Ivan posted is for the pm8841 and pm8941; these are
using spmi instead of ssbi which gives a larger addressing space and
allows for a less dense register map, so all the plumbing in those
drivers will be different.
The suggestion is to call the "new gen stuff" qpnp instead, but to me
that's just a setup for hitting the same problem in a few years...

Nontheless, the hardware guys have painted us into a corner where
pm8xxx refer to a series of very similar pmics and pm8x41 is not one
of those.

> Anyway I will create a patch to move the current msm driver down
> to a folder named "qcom" and we can atleast collect these drivers
> in a single place since the chipsets are related, OK?
>

+1

> Then I guess even if the chips are totally unrelated it'd be interesting
> to have you two guys cross-review each other's drivers so the behaviour
> is consistent across qualcomm platforms.
>

I hope we can meet somewhere in between, so that we and other
developers will be able to recognize the common element and structures
later on. Again, sorry for not finding the time to preempt you on
that.

Regards,
Bjorn

2014-07-10 13:39:48

by Ivan T. Ivanov

[permalink] [raw]
Subject: Re: [PATCH 0/4] New Qualcomm PMIC pin controller drivers

On Wed, 2014-07-09 at 07:02 -0700, Bjorn Andersson wrote:
> On Wed, Jul 9, 2014 at 4:43 AM, Linus Walleij <[email protected]> wrote:
> > On Wed, Jul 9, 2014 at 1:13 PM, Ivan T. Ivanov <[email protected]> wrote:
> >> On Wed, 2014-07-09 at 11:43 +0200, Linus Walleij wrote:
> >>> On Mon, Jul 7, 2014 at 5:11 PM, Ivan T. Ivanov <[email protected]> wrote:

<snip>

> However, the device tree bindings are a different thing; as the
> properties used to describe the hardware doesn't relate to how we
> communicate with it I think we should be able to (and therefor should)
> use the same documentation for the two (rather 7) chips.

Agreed.

>
> >> Not sure. Björn patches cover older PMIC chips, if not mistaken, mine
> >> cover PMIC's used with APQ8074 and onward [1]. Main difference is
> >> the bus which connects them to SoC, interrupts handling, runtime
> >> pin type detection and register map.
> >
>
> Correct Ivan; we do however share the same issues related to how to do
> interrupt handling,

Yep, but do we actually need to do interrupt handling in driver?
Interrupts are handled by parent device. GPIO client drivers could
use interrupt-controller registered by core driver?

> units for properties and how to split/reuse
> between gpio and mpp. Also we have solved the pins vs groups vs
> functions slightly different, that should all be aligned I think.

Sure.

<snip>

> > Then I guess even if the chips are totally unrelated it'd be interesting
> > to have you two guys cross-review each other's drivers so the behaviour
> > is consistent across qualcomm platforms.
> >
>
> I hope we can meet somewhere in between

Sure.

Regards,
Ivan

2014-07-12 23:51:50

by Bjorn Andersson

[permalink] [raw]
Subject: Re: [PATCH 0/4] New Qualcomm PMIC pin controller drivers

On Thu, Jul 10, 2014 at 6:39 AM, Ivan T. Ivanov <[email protected]> wrote:
> On Wed, 2014-07-09 at 07:02 -0700, Bjorn Andersson wrote:
[..]
>> Correct Ivan; we do however share the same issues related to how to do
>> interrupt handling,
>
> Yep, but do we actually need to do interrupt handling in driver?
> Interrupts are handled by parent device. GPIO client drivers could
> use interrupt-controller registered by core driver?
>

Among other things we have the volume and camera keys hooked to these
pins, using the gpio-keys driver; so that needs to be able to convert
a gpio to an irq.
But I don't believe we should turn this driver into an
interrupt-controller; as that wouldn't add any value...

Regards,
Bjorn

2014-07-14 15:01:25

by Ivan T. Ivanov

[permalink] [raw]
Subject: Re: [PATCH 0/4] New Qualcomm PMIC pin controller drivers

On Sat, 2014-07-12 at 16:51 -0700, Bjorn Andersson wrote:
> On Thu, Jul 10, 2014 at 6:39 AM, Ivan T. Ivanov <[email protected]> wrote:
> > On Wed, 2014-07-09 at 07:02 -0700, Bjorn Andersson wrote:
> [..]
> >> Correct Ivan; we do however share the same issues related to how to do
> >> interrupt handling,
> >
> > Yep, but do we actually need to do interrupt handling in driver?
> > Interrupts are handled by parent device. GPIO client drivers could
> > use interrupt-controller registered by core driver?
> >
>
> Among other things we have the volume and camera keys hooked to these
> pins, using the gpio-keys driver; so that needs to be able to convert
> a gpio to an irq.
> But I don't believe we should turn this driver into an
> interrupt-controller; as that wouldn't add any value...

I agree. But I would like to avoid specifying 44 "interrupts" in
DT files if they are known a priory in driver for specific chip.

Only drawback that I could see is that it will be little odd for
client drivers, which are using SPMI PMIC pinctrl driver, to specify
4 parameters required by interrupts property.

Regards,
Ivan