2013-06-21 21:53:08

by Hanumant Singh

[permalink] [raw]
Subject: [PATCH] pinctrl: msm: Add support for MSM TLMM pinmux

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

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

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

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

diff --git a/Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt
new file mode 100644
index 0000000..c76777b
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt
@@ -0,0 +1,187 @@
+MSM TLMM pinmux controller
+
+Qualcomm MSM integrates a GPIO and Pin mux/config hardware, (TOP Level Mode
+Multiplexper in short TLMM). It controls the input/output settings on the
+available pads/pins and also provides ability to multiplex and configure the
+output of various on-chip controllers onto these pads. The pins are also of
+different types, encapsulating different functions and having differing register
+semantics.
+
+Required Properties:
+- compatible: should be one of the following.
+ - "qcom,msm-tlmm-v3": for MSM with TLMM version 3.
+
+- reg: Base address of the pin controller hardware module and length of
+ the address space it occupies.
+
+- Pin types as child nodes: Pin types supported by a particular controller
+ instance are represented as child nodes of the controller node. Each
+ pin type node must contain following properties:
+
+Required Properties
+ - qcom,pin-type: identifies the pin type.
+ - qcom,pin-cells: number of cells in the pin type specifier.
+ - qcom,num-pins: number of pins of given type present on the MSM.
+
+- Pin groups as child nodes: The pin mux (selecting pin function
+ mode) and pin config (pull up/down, driver strength, direction) settings are
+ represented as child nodes of the pin-controller node. There is no limit on
+ the count of these child nodes.
+
+ Required Properties
+ -qcom,pins: phandle specifying pin type and a pin number.
+
+ Optional Properties
+ -qcom,pin-func: function setting for the pin group.
+
+ The child node should contain a list of pin(s) on which a particular pin
+ function selection or pin configuration (or both) have to applied. This
+ list of pins is specified using the property name "qcom,pins". There
+ should be atleast one pin specfied for this property and there is no upper
+ limit on the count of pins that can be specified. The pins are specified
+ using the pintype phandle and the pin number within that pintype.
+
+ The pin function selection that should be applied on the pins listed in the
+ child node is specified using the "qcom,pin-func" property. The value
+ of this property that should be applied to each of the pins listed in the
+ "qcom,pins" property, should be picked from the hardware manual of the SoC.
+ This property is optional in the child node if no specific function
+ selection is desired for the pins listed in the child node or if the pin is
+ to be used for bit bang. The value of this property is used as-is to program
+ the pin-controller
+
+ The pin group node must additionally have a pin configuration node as its own
+ child node. There can be more then one such configuration node for a pin group
+ node. There can be one or more configurations within the configuration
+ node. These configurations are applied to all pins mentoned above using the
+ "qcom,pins" property. These configurations are specific to the pintype of the
+ pins. The following pin configuration properties are supported by general
+ purpose pins.
+
+ - qcom,gp-pull: Pull up/down configuration.
+ - qcom,gp-drv: Drive strength configuration.
+ - qcom,gp-dir: Pull up/down configuration in power down mode.
+
+ The following pin configurations are properties are supported by SDC pins
+ - qcom,sdc1-clk-pull: Pull up/down configuration SDC1 clock pin.
+ - qcom,sdc1-clk-drv: Drive strength configuration for SDC1 clock pin.
+ - qcom,sdc1-cmd-pull: Pull up/down configuration for SDC1 command pin.
+ - qcom,sdc1-cmd-drv: Drive strength configuration for SDC1 command pin.
+ - qcom,sdc1-data-pull: Pull up/down configuration for SDC1 data pin.
+ - qcom,sdc1-data-drv: Drive strength configuration for SDC1 data pin.
+ - qcom,sdc2-clk-pull: Pull up/down configuration SDC2 clock pin.
+ - qcom,sdc2-clk-drv: Drive strength configuration for SDC2 clock pin.
+ - qcom,sdc2-cmd-pull: Pull up/down configuration for SDC2 command pin.
+ - qcom,sdc2-cmd-drv: Drive strength configuration for SDC2 command pin.
+ - qcom,sdc2-data-pull: Pull up/down configuration for SDC2 data pin.
+ - qcom,sdc2-data-drv: Drive strength configuration for SDC2 data pin.
+
+
+ The values specified by these config properties should be derived from the
+ hardware manual and these values are programmed as-is into the pin
+ pull up/down and driver strength register of the pin-controller.
+
+ NOTE: A pin group node should be formed for all pins that are going to have
+ the same function and configuration settings. If a subset of pins to be used
+ by a client require different function or configuration settings or both
+ then they should be modelled as a separate pin group node to be used by
+ the client.
+
+ The client nodes that require a particular pin function selection and/or
+ pin configuration should use the bindings listed in the "pinctrl-bindings txt"
+ file.
+
+Example 1: A pin-controller node with pin types
+
+ pinctrl@fd5110000 {
+ compatible = "qcom,msm-tlmm-v3";
+ reg = <0x11400000 0x4000>;
+
+ /* General purpose pin type */
+ gp: gp {
+ qcom,pin-type = "gp";
+ qcom,num-pins = <117>;
+ #qcom,pin-cells = <1>;
+ };
+ };
+
+Example 2: Spi pin entries within the pincontroller node
+ pinctrl@fd511000 {
+ ....
+ ..
+ spi-bus {
+ /*
+ * MOSI, MISO and CLK lines
+ * all sharing same function and config
+ * settings for each configuration node.
+ */
+ qcom,pins = <&gp 0>, <&gp 1>, <&gp 3>;
+ qcom,pin-func = <1>;
+
+ /* Active configuration of bus pins */
+ spi-bus-active: spi-bus-active {
+ qcom,gp-drv = <3>; /* 8 MA */
+ qcom,gp-pull = <0>; /* No PULL */
+ };
+ /* Sleep configuration of bus pins */
+ spi-bus-sleep: spi-bus-sleep {
+ qcom,gp-drv = <0>; /* 2 MA */
+ qcom,gp-pull = <0>; /* No PULL */
+ };
+ };
+
+ spi-cs {
+ /*
+ * Chip select for SPI
+ * different config
+ * settings as compared to bus pins.
+ */
+ qcom,pins = <&Gp 2>;
+ qcom,num-grp-pins = <1>;
+ qcom,pin-func = <1>;
+
+ /* Active configuration of cs pin */
+ spi-cs-active: spi-cs-active {
+ qcom,gp-drv = <2>; /* 8 MA */
+ qcom,gp-pull = <0>; /* No PULL */
+ };
+ /* Sleep configuration of cs pin */
+ spi-bus-sleep: spi-bus-sleep {
+ qcom,gp-drv = <0>; /* 2 MA */
+ qcom,gp-pull = <0>; /* No PULL */
+ };
+ };
+ };
+
+Example 3: A SPI client node that supports 'active' and 'sleep' states.
+
+ spi_0: spi@f9923000 { /* BLSP1 QUP1 */
+ compatible = "qcom,spi-qup-v2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "spi_physical", "spi_bam_physical";
+ reg = <0xf9923000 0x1000>,
+ <0xf9904000 0xF000>;
+ interrupt-names = "spi_irq", "spi_bam_irq";
+ interrupts = <0 95 0>, <0 238 0>;
+ spi-max-frequency = <19200000>;
+
+ /* pins used by spi controllers */
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&spi-bus-active &spi-cs-active>;
+ pinctrl-1 = <&spi-bus-sleep &spi-cs-sleep>;
+
+ qcom,infinite-mode = <0>;
+ qcom,use-bam;
+ qcom,ver-reg-exists;
+ qcom,bam-consumer-pipe-index = <12>;
+ qcom,bam-producer-pipe-index = <13>;
+ };
+
+Example 4: Set up the default pin state for spi controller.
+
+ static inline int msm_spi_request_pins{struct msm_spi *dd)
+ {
+ /* ... */
+ dd->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ }
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 8f66924..7cde411 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -132,6 +132,16 @@ config PINCTRL_IMX28
bool
select PINCTRL_MXS

+config PINCTRL_MSM
+ depends on OF
+ bool
+ select PINMUX
+ select PINCONF
+
+config PINCTRL_MSM_TLMM_V3
+ bool
+ select PINCTRL_MSM
+
config PINCTRL_NOMADIK
bool "Nomadik pin controller driver"
depends on ARCH_U8500 || ARCH_NOMADIK
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 9bdaeb8..80469e4 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -26,6 +26,8 @@ obj-$(CONFIG_PINCTRL_FALCON) += pinctrl-falcon.o
obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o
obj-$(CONFIG_PINCTRL_IMX23) += pinctrl-imx23.o
obj-$(CONFIG_PINCTRL_IMX28) += pinctrl-imx28.o
+obj-$(CONFIG_PINCTRL_MSM) += pinctrl-msm.o
+obj-$(CONFIG_PINCTRL_MSM_TLMM_V3) += pinctrl-msm-tlmm-v3.o
obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o
obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o
obj-$(CONFIG_PINCTRL_DB8500) += pinctrl-nomadik-db8500.o
diff --git a/drivers/pinctrl/pinctrl-msm-tlmm-v3.c b/drivers/pinctrl/pinctrl-msm-tlmm-v3.c
new file mode 100644
index 0000000..8b979cf
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-msm-tlmm-v3.c
@@ -0,0 +1,345 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include "pinctrl-msm.h"
+
+/* GP PIN TYPE REG MASKS */
+#define TLMMV3_GP_DRV_SHFT 6
+#define TLMMV3_GP_DRV_MASK 0x7
+#define TLMMV3_GP_PULL_SHFT 0
+#define TLMMV3_GP_PULL_MASK 0x3
+#define TLMMV3_GP_DIR_SHFT 9
+#define TLMMV3_GP_DIR_MASK 1
+#define TLMMV3_GP_FUNC_SHFT 2
+#define TLMMV3_GP_FUNC_MASK 0xF
+/* SDC1 PIN TYPE REG MASKS */
+#define TLMMV3_SDC1_CLK_DRV_SHFT 6
+#define TLMMV3_SDC1_CLK_DRV_MASK 0x7
+#define TLMMV3_SDC1_DATA_DRV_SHFT 0
+#define TLMMV3_SDC1_DATA_DRV_MASK 0x7
+#define TLMMV3_SDC1_CMD_DRV_SHFT 3
+#define TLMMV3_SDC1_CMD_DRV_MASK 0x7
+#define TLMMV3_SDC1_CLK_PULL_SHFT 13
+#define TLMMV3_SDC1_CLK_PULL_MASK 0x3
+#define TLMMV3_SDC1_DATA_PULL_SHFT 9
+#define TLMMV3_SDC1_DATA_PULL_MASK 0x3
+#define TLMMV3_SDC1_CMD_PULL_SHFT 11
+#define TLMMV3_SDC1_CMD_PULL_MASK 0x3
+/* SDC2 PIN TYPE REG MASKS */
+#define TLMMV3_SDC2_CLK_DRV_SHFT 6
+#define TLMMV3_SDC2_CLK_DRV_MASK 0x7
+#define TLMMV3_SDC2_DATA_DRV_SHFT 0
+#define TLMMV3_SDC2_DATA_DRV_MASK 0x7
+#define TLMMV3_SDC2_CMD_DRV_SHFT 3
+#define TLMMV3_SDC2_CMD_DRV_MASK 0x7
+#define TLMMV3_SDC2_CLK_PULL_SHFT 14
+#define TLMMV3_SDC2_CLK_PULL_MASK 0x3
+#define TLMMV3_SDC2_DATA_PULL_SHFT 9
+#define TLMMV3_SDC2_DATA_PULL_MASK 0x3
+#define TLMMV3_SDC2_CMD_PULL_SHFT 11
+#define TLMMV3_SDC2_CMD_PULL_MASK 0x3
+
+#define TLMMV3_GP_INOUT_BIT 1
+#define TLMMV3_GP_OUT BIT(TLMMV3_GP_INOUT_BIT)
+#define TLMMV3_GP_IN 0
+
+#define MSM_TLMM_PACK_CFG(val, arg) ((val << 16) | arg)
+#define MSM_TLMM_UNPACK_CFG(conf) (conf >> 16)
+#define MSM_TLMM_UNPACK_ARG(conf) (conf & 0xffff)
+
+/* SDC Pin type register offsets */
+#define TLMMV3_SDC1_CFG(base) (base + 0x2044)
+#define TLMMV3_SDC2_CFG(base) (TLMMV3_SDC1_CFG(base) + 0x4)
+
+/* GP pin type register offsets */
+#define TLMMV3_GP_CFG(base, pin) (base + 0x1000 + 0x10 * (pin))
+#define TLMMV3_GP_INOUT(base, pin) (base + 0x1004 + 0x10 * (pin))
+
+enum msm_tlmm_gp_pincfg_param {
+ /* GP PIN TYPE CFG PARAMS */
+ MSM_TLMM_GP_DIR,
+ MSM_TLMM_GP_DRV,
+ MSM_TLMM_GP_PULL,
+ /* SDC1 PIN TYPE CFG PARAMS */
+ MSM_TLMM_SDC1_CLK_DRV,
+ MSM_TLMM_SDC1_CMD_DRV,
+ MSM_TLMM_SDC1_DATA_DRV,
+ MSM_TLMM_SDC1_CLK_PULL,
+ MSM_TLMM_SDC1_CMD_PULL,
+ MSM_TLMM_SDC1_DATA_PULL,
+ /* SDC2 PIN TYPE CFG PARAMS */
+ MSM_TLMM_SDC2_CLK_DRV,
+ MSM_TLMM_SDC2_CMD_DRV,
+ MSM_TLMM_SDC2_DATA_DRV,
+ MSM_TLMM_SDC2_CLK_PULL,
+ MSM_TLMM_SDC2_CMD_PULL,
+ MSM_TLMM_SDC2_DATA_PULL,
+};
+
+static struct msm_tlmm_cfg_params msm_tlmm_v3_gp_cfg_param[] = {
+ {
+ .name = "qcom,gp-dir",
+ .id = MSM_TLMM_GP_DIR,
+ },
+ {
+ .name = "qcom,gp-drv",
+ .id = MSM_TLMM_GP_DRV,
+ },
+ {
+ .name = "qcom,gp-pull",
+ .id = MSM_TLMM_GP_PULL,
+ },
+
+};
+
+static struct msm_tlmm_cfg_params msm_tlmm_v3_sdc_cfg_param[] = {
+ {
+ .name = "qcom,sdc1-clk-drv",
+ .id = MSM_TLMM_SDC1_CLK_DRV,
+ },
+ {
+ .name = "qcom,sdc1-clk-pull",
+ .id = MSM_TLMM_SDC1_CLK_PULL,
+ },
+ {
+ .name = "qcom,sdc1-cmd-drv",
+ .id = MSM_TLMM_SDC1_CMD_PULL,
+ },
+ {
+ .name = "qcom,sdc1-cmd-pull",
+ .id = MSM_TLMM_SDC1_CMD_PULL,
+ },
+ {
+ .name = "qcom,sdc1-data-pull",
+ .id = MSM_TLMM_SDC1_DATA_PULL,
+ },
+ {
+ .name = "qcom,sdc1-data-pull",
+ .id = MSM_TLMM_SDC1_DATA_PULL,
+ },
+ {
+ .name = "qcom,sdc2-clk-drv",
+ .id = MSM_TLMM_SDC2_CLK_DRV,
+ },
+ {
+ .name = "qcom,sdc2-clk-pull",
+ .id = MSM_TLMM_SDC2_CLK_PULL,
+ },
+ {
+ .name = "qcom,sdc2-cmd-drv",
+ .id = MSM_TLMM_SDC2_CMD_PULL,
+ },
+ {
+ .name = "qcom,sdc2-cmd-pull",
+ .id = MSM_TLMM_SDC2_CMD_PULL,
+ },
+ {
+ .name = "qcom,sdc2-data-pull",
+ .id = MSM_TLMM_SDC2_DATA_PULL,
+ },
+ {
+ .name = "qcom,sdc2-data-pull",
+ .id = MSM_TLMM_SDC2_DATA_PULL,
+ },
+};
+
+static int msm_tlmm_v3_sdc_cfg(uint pin_no, unsigned long *config,
+ void __iomem *cfg_reg,
+ bool write)
+{
+ unsigned int val, id, data;
+ u32 mask, shft;
+
+ id = MSM_TLMM_UNPACK_ARG(*config);
+ /* Get mask and shft values for this config type */
+ switch (id) {
+ case MSM_TLMM_SDC1_CLK_DRV:
+ mask = TLMMV3_SDC1_CLK_DRV_MASK;
+ shft = TLMMV3_SDC1_CLK_DRV_SHFT;
+ break;
+ case MSM_TLMM_SDC1_CLK_PULL:
+ mask = TLMMV3_SDC1_CLK_PULL_MASK;
+ shft = TLMMV3_SDC1_CLK_PULL_SHFT;
+ break;
+ case MSM_TLMM_SDC1_DATA_DRV:
+ mask = TLMMV3_SDC1_DATA_DRV_MASK;
+ shft = TLMMV3_SDC1_DATA_DRV_SHFT;
+ break;
+ case MSM_TLMM_SDC1_DATA_PULL:
+ mask = TLMMV3_SDC1_DATA_PULL_MASK;
+ shft = TLMMV3_SDC1_DATA_PULL_SHFT;
+ break;
+ case MSM_TLMM_SDC1_CMD_DRV:
+ mask = TLMMV3_SDC1_CMD_DRV_MASK;
+ shft = TLMMV3_SDC1_CMD_DRV_SHFT;
+ break;
+ case MSM_TLMM_SDC1_CMD_PULL:
+ mask = TLMMV3_SDC1_CMD_PULL_MASK;
+ shft = TLMMV3_SDC1_CMD_PULL_SHFT;
+ break;
+ case MSM_TLMM_SDC2_CLK_DRV:
+ cfg_reg = TLMMV3_SDC2_CFG(cfg_reg);
+ mask = TLMMV3_SDC2_CLK_DRV_MASK;
+ shft = TLMMV3_SDC2_CLK_DRV_SHFT;
+ break;
+ case MSM_TLMM_SDC2_CLK_PULL:
+ cfg_reg = TLMMV3_SDC2_CFG(cfg_reg);
+ mask = TLMMV3_SDC2_CLK_PULL_MASK;
+ shft = TLMMV3_SDC2_CLK_PULL_SHFT;
+ break;
+ case MSM_TLMM_SDC2_DATA_DRV:
+ cfg_reg = TLMMV3_SDC2_CFG(cfg_reg);
+ mask = TLMMV3_SDC2_DATA_DRV_MASK;
+ shft = TLMMV3_SDC2_DATA_DRV_SHFT;
+ break;
+ case MSM_TLMM_SDC2_DATA_PULL:
+ cfg_reg = TLMMV3_SDC2_CFG(cfg_reg);
+ mask = TLMMV3_SDC2_DATA_PULL_MASK;
+ shft = TLMMV3_SDC2_DATA_PULL_SHFT;
+ break;
+ case MSM_TLMM_SDC2_CMD_DRV:
+ cfg_reg = TLMMV3_SDC2_CFG(cfg_reg);
+ mask = TLMMV3_SDC2_CMD_DRV_MASK;
+ shft = TLMMV3_SDC2_CMD_DRV_SHFT;
+ break;
+ case MSM_TLMM_SDC2_CMD_PULL:
+ cfg_reg = TLMMV3_SDC2_CFG(cfg_reg);
+ mask = TLMMV3_SDC2_CMD_PULL_MASK;
+ shft = TLMMV3_SDC2_CMD_PULL_SHFT;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ val = readl_relaxed(cfg_reg);
+ if (write) {
+ data = MSM_TLMM_UNPACK_CFG(*config);
+ val &= ~(mask << shft);
+ val |= (data << shft);
+ writel_relaxed(val, cfg_reg);
+ } else {
+ val >>= shft;
+ val &= mask;
+ *config = MSM_TLMM_PACK_CFG(val, id);
+ }
+ return 0;
+}
+
+static void msm_tlmm_v3_sdc_set_reg_base(void __iomem **ptype_base,
+ void __iomem *tlmm_base)
+{
+ *ptype_base = tlmm_base + 0x2044;
+}
+
+static int msm_tlmm_v3_gp_cfg(uint pin_no, unsigned long *config,
+ void *reg_base, bool write)
+{
+ unsigned int val, id, data;
+ u32 mask, shft;
+ void __iomem *inout_reg = NULL;
+ void __iomem *cfg_reg = TLMMV3_GP_CFG(reg_base, pin_no);
+
+ id = MSM_TLMM_UNPACK_ARG(*config);
+ /* Get mask and shft values for this config type */
+ switch (id) {
+ case MSM_TLMM_GP_DRV:
+ mask = TLMMV3_GP_DRV_MASK;
+ shft = TLMMV3_GP_DRV_SHFT;
+ break;
+ case MSM_TLMM_GP_PULL:
+ mask = TLMMV3_GP_PULL_MASK;
+ shft = TLMMV3_GP_PULL_SHFT;
+ break;
+ case MSM_TLMM_GP_DIR:
+ mask = TLMMV3_GP_DIR_MASK;
+ shft = TLMMV3_GP_DIR_SHFT;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ val = readl_relaxed(cfg_reg);
+ if (write) {
+ data = MSM_TLMM_UNPACK_CFG(*config);
+ if (id == MSM_TLMM_GP_DIR) {
+ if (data == TLMMV3_GP_OUT) {
+ inout_reg = TLMMV3_GP_INOUT(reg_base, pin_no);
+ writel_relaxed(TLMMV3_GP_OUT, inout_reg);
+ }
+ if (data > TLMMV3_GP_IN)
+ data = TLMMV3_GP_OUT;
+ }
+ val &= ~(mask << shft);
+ val |= (data << shft);
+ writel_relaxed(val, cfg_reg);
+ } else {
+ val >>= shft;
+ val &= mask;
+ *config = MSM_TLMM_PACK_CFG(val, id);
+ }
+ return 0;
+}
+
+static void msm_tlmm_v3_gp_fn(uint pin_no, u32 func, void *reg_base,
+ bool enable)
+{
+ unsigned int val;
+ void __iomem *cfg_reg = TLMMV3_GP_CFG(reg_base, pin_no);
+ val = readl_relaxed(cfg_reg);
+ val &= ~(TLMMV3_GP_FUNC_MASK << TLMMV3_GP_FUNC_SHFT);
+ if (enable)
+ val |= (func << TLMMV3_GP_FUNC_SHFT);
+ writel_relaxed(val, cfg_reg);
+}
+
+static void msm_tlmm_v3_gp_set_reg_base(void __iomem **ptype_base,
+ void __iomem *tlmm_base)
+{
+ *ptype_base = tlmm_base;
+}
+
+static unsigned long msm_tlmm_v3_pack_cfg(u32 val,
+ struct msm_tlmm_cfg_params const
+ *cfg_param)
+{
+ return MSM_TLMM_PACK_CFG(val, cfg_param->id);
+}
+
+static struct msm_pintype_info tlmm_v3_pininfo[] = {
+ {
+ .prg_cfg = msm_tlmm_v3_gp_cfg,
+ .prg_func = msm_tlmm_v3_gp_fn,
+ .pack_cfg = msm_tlmm_v3_pack_cfg,
+ .set_reg_base = msm_tlmm_v3_gp_set_reg_base,
+ .tlmm_cfg_param = msm_tlmm_v3_gp_cfg_param,
+ .num_cfg_params = ARRAY_SIZE(msm_tlmm_v3_gp_cfg_param),
+ .reg_data = NULL,
+ .name = "gp",
+ },
+ {
+ .prg_cfg = msm_tlmm_v3_sdc_cfg,
+ .pack_cfg = msm_tlmm_v3_pack_cfg,
+ .set_reg_base = msm_tlmm_v3_sdc_set_reg_base,
+ .tlmm_cfg_param = msm_tlmm_v3_sdc_cfg_param,
+ .num_cfg_params = ARRAY_SIZE(msm_tlmm_v3_sdc_cfg_param),
+ .reg_data = NULL,
+ .name = "sdc",
+ }
+};
+
+struct msm_tlmm tlmm_v3_pintypes = {
+ .num_entries = ARRAY_SIZE(tlmm_v3_pininfo),
+ .pintype_info = tlmm_v3_pininfo,
+};
+
diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c
new file mode 100644
index 0000000..97b96e1
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-msm.c
@@ -0,0 +1,774 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include "core.h"
+#include "pinctrl-msm.h"
+
+/**
+ * struct msm_pinctrl_dd: represents the pinctrol driver data.
+ * @base: virtual base of TLMM.
+ * @irq: interrupt number for TLMM summary interrupt.
+ * @num_pins: Number of total pins present on TLMM.
+ * @msm_pindesc: list of descriptors for each pin.
+ * @num_pintypes: number of pintypes on TLMM.
+ * @msm_pintype: points to the representation of all pin types supported.
+ * @pctl: pin controller instance managed by the driver.
+ * @pctl_dev: pin controller descriptor registered with the pinctrl subsystem.
+ * @pin_grps: list of pin groups available to the driver.
+ * @num_grps: number of groups.
+ * @pmx_funcs:list of pin functions available to the driver
+ * @num_funcs: number of functions.
+ * @dev: pin contol device.
+ */
+struct msm_pinctrl_dd {
+ void __iomem *base;
+ int irq;
+ unsigned int num_pins;
+ struct msm_pindesc *msm_pindesc;
+ unsigned int num_pintypes;
+ struct msm_pintype_info *msm_pintype;
+ struct pinctrl_desc pctl;
+ struct pinctrl_dev *pctl_dev;
+ struct msm_pin_grps *pin_grps;
+ unsigned int num_grps;
+ struct msm_pmx_funcs *pmx_funcs;
+ unsigned int num_funcs;
+ struct device *dev;
+};
+
+static int msm_pmx_functions_count(struct pinctrl_dev *pctldev)
+{
+ struct msm_pinctrl_dd *dd;
+
+ dd = pinctrl_dev_get_drvdata(pctldev);
+ return dd->num_funcs;
+}
+
+/* return the name of the pin function specified */
+static const char *msm_pmux_get_fname(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct msm_pinctrl_dd *dd;
+
+ dd = pinctrl_dev_get_drvdata(pctldev);
+ return dd->pmx_funcs[selector].name;
+}
+
+/* return the groups associated for the specified function selector */
+static int msm_pmux_get_groups(struct pinctrl_dev *pctldev,
+ unsigned selector, const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct msm_pinctrl_dd *dd;
+
+ dd = pinctrl_dev_get_drvdata(pctldev);
+ *groups = dd->pmx_funcs[selector].gps;
+ *num_groups = dd->pmx_funcs[selector].num_grps;
+ return 0;
+}
+
+/* enable or disable a pinmux function */
+static void msm_pmux_prg_fn(struct pinctrl_dev *pctldev, unsigned selector,
+ unsigned group, bool enable)
+{
+ struct msm_pinctrl_dd *dd;
+ const unsigned int *pins;
+ struct msm_pindesc *pindesc;
+ struct msm_pintype_info *pintype;
+ unsigned int pin, cnt, func;
+
+ dd = pinctrl_dev_get_drvdata(pctldev);
+ pins = dd->pin_grps[group].pins;
+ pindesc = dd->msm_pindesc;
+
+ /*
+ * for each pin in the pin group selected, program the correspoding
+ * pin function number in the config register.
+ */
+ for (cnt = 0; cnt < dd->pin_grps[group].num_pins; cnt++) {
+ pin = pins[cnt];
+ /* Obtain the pin type for given pin */
+ pintype = pindesc[pin].pin_info;
+ /* Obtain the pin number within the pin type */
+ pin = pin - pintype->pin_start;
+ func = dd->pin_grps[group].func;
+ /* program the function value for the given pin type */
+ pintype->prg_func(pin, func, pintype->reg_data, enable);
+ }
+}
+
+/* enable a specified pinmux by writing to registers */
+static int msm_pmux_enable(struct pinctrl_dev *pctldev, unsigned selector,
+ unsigned group)
+{
+ msm_pmux_prg_fn(pctldev, selector, group, true);
+ return 0;
+}
+
+/* disable a specified pinmux by writing to registers */
+static void msm_pmux_disable(struct pinctrl_dev *pctldev,
+ unsigned selector, unsigned group)
+{
+ msm_pmux_prg_fn(pctldev, selector, group, false);
+}
+
+static struct pinmux_ops msm_pmxops = {
+ .get_functions_count = msm_pmx_functions_count,
+ .get_function_name = msm_pmux_get_fname,
+ .get_function_groups = msm_pmux_get_groups,
+ .enable = msm_pmux_enable,
+ .disable = msm_pmux_disable,
+};
+
+/* set or get the pin config settings for a specified pin */
+static int msm_pconf_prg(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long *config, bool rw)
+{
+ struct msm_pinctrl_dd *dd;
+ struct msm_pindesc *pindesc;
+ struct msm_pintype_info *pintype;
+
+ dd = pinctrl_dev_get_drvdata(pctldev);
+ pindesc = dd->msm_pindesc;
+ /* Get pin type for given pin */
+ pintype = pindesc[pin].pin_info;
+ /* Get pin offset from the pintype start pin number */
+ pin = pin - pintype->pin_start;
+ /* Program the config value for pin type */
+ return pintype->prg_cfg(pin, config, pintype->reg_data, rw);
+}
+
+/* set the pin config settings for a specified pin */
+static int msm_pconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long config)
+{
+ return msm_pconf_prg(pctldev, pin, &config, true);
+}
+
+/* get the pin config settings for a specified pin */
+static int msm_pconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long *config)
+{
+ return msm_pconf_prg(pctldev, pin, config, false);
+}
+
+/* set the pin config settings for a specified pin group */
+static int msm_pconf_group_set(struct pinctrl_dev *pctldev,
+ unsigned group, unsigned long config)
+{
+ struct msm_pinctrl_dd *dd;
+ const unsigned int *pins;
+ unsigned int cnt;
+
+ dd = pinctrl_dev_get_drvdata(pctldev);
+ pins = dd->pin_grps[group].pins;
+
+ for (cnt = 0; cnt < dd->pin_grps[group].num_pins; cnt++)
+ msm_pconf_set(pctldev, pins[cnt], config);
+
+ return 0;
+}
+
+/* get the pin config settings for a specified pin group */
+static int msm_pconf_group_get(struct pinctrl_dev *pctldev,
+ unsigned int group, unsigned long *config)
+{
+ struct msm_pinctrl_dd *dd;
+ const unsigned int *pins;
+
+ dd = pinctrl_dev_get_drvdata(pctldev);
+ pins = dd->pin_grps[group].pins;
+ msm_pconf_get(pctldev, pins[0], config);
+ return 0;
+}
+
+static struct pinconf_ops msm_pinconfops = {
+ .pin_config_get = msm_pconf_get,
+ .pin_config_set = msm_pconf_set,
+ .pin_config_group_get = msm_pconf_group_get,
+ .pin_config_group_set = msm_pconf_group_set,
+};
+
+/* check if the selector is a valid pin group selector */
+static int msm_get_grps_count(struct pinctrl_dev *pctldev)
+{
+ struct msm_pinctrl_dd *dd;
+
+ dd = pinctrl_dev_get_drvdata(pctldev);
+ return dd->num_grps;
+}
+
+/* return the name of the group selected by the group selector */
+static const char *msm_get_grps_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct msm_pinctrl_dd *dd;
+
+ dd = pinctrl_dev_get_drvdata(pctldev);
+ return dd->pin_grps[selector].name;
+}
+
+/* return the pin numbers associated with the specified group */
+static int msm_get_grps_pins(struct pinctrl_dev *pctldev,
+ unsigned selector, const unsigned **pins, unsigned *num_pins)
+{
+ struct msm_pinctrl_dd *dd;
+
+ dd = pinctrl_dev_get_drvdata(pctldev);
+ *pins = dd->pin_grps[selector].pins;
+ *num_pins = dd->pin_grps[selector].num_pins;
+ return 0;
+}
+
+static struct msm_pintype_info *msm_pgrp_to_pintype(struct device_node *nd,
+ struct msm_pinctrl_dd *dd)
+{
+ struct device_node *ptype_nd;
+ struct msm_pintype_info *pinfo = NULL;
+ int idx = 0;
+
+ /*Extract pin type node from parent node */
+ ptype_nd = of_parse_phandle(nd, "qcom,pins", 0);
+ /* find the pin type info for this pin type node */
+ for (idx = 0; idx < dd->num_pintypes; idx++) {
+ pinfo = &dd->msm_pintype[idx];
+ if (ptype_nd == pinfo->node) {
+ of_node_put(ptype_nd);
+ break;
+ }
+ }
+ return pinfo;
+}
+
+/* create pinctrl_map entries by parsing device tree nodes */
+static int msm_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *cfg_np, struct pinctrl_map **maps,
+ unsigned *nmaps)
+{
+ struct msm_pinctrl_dd *dd;
+ struct device_node *parent;
+ struct msm_pindesc *pindesc;
+ struct msm_pintype_info *pinfo;
+ struct msm_tlmm_cfg_params const *cfg_params;
+ struct pinctrl_map *map;
+ char *grp_name, *fn_name;
+ u32 val , num_cfg_params;
+ struct device *dev = pctldev->dev;
+ unsigned long *cfg = NULL;
+ int cfg_cnt = 0, map_cnt = 0, idx = 0, func_cnt = 0, ret = 0;
+
+ dd = pinctrl_dev_get_drvdata(pctldev);
+ pindesc = dd->msm_pindesc;
+ /* get parent node of config node */
+ parent = of_get_parent(cfg_np);
+ /*
+ * parent node contains pin grouping
+ * get pin type from pin grouping
+ */
+ pinfo = msm_pgrp_to_pintype(parent, dd);
+ /* check if there is a function associated with the parent pin group */
+ if (of_find_property(parent, "qcom,pin-func", NULL))
+ func_cnt++;
+ /*
+ * Count the config parameters specified in the node
+ * and supported by this pin type
+ */
+ cfg_params = pinfo->tlmm_cfg_param;
+ num_cfg_params = pinfo->num_cfg_params;
+
+ for (idx = 0; idx < num_cfg_params; idx++) {
+ if (!of_find_property(cfg_np, cfg_params[idx].name, NULL))
+ continue;
+ cfg_cnt++;
+ }
+ if (!cfg_cnt) {
+ dev_err(dd->dev, "no matching config specified\n");
+ of_node_put(parent);
+ return -EINVAL;
+ }
+ map_cnt = cfg_cnt + func_cnt;
+
+ /* Allocate memory for pin-map entries */
+ map = kzalloc(sizeof(*map) * map_cnt, GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+ *nmaps = 0;
+ /* Allocate memory to hold configs */
+ cfg = kzalloc(sizeof(*cfg) * cfg_cnt, GFP_KERNEL);
+ if (!cfg) {
+ ret = -ENOMEM;
+ goto cfg_err;
+ }
+
+ /* read cfg property values from cfg device tree node */
+ for (idx = 0, cfg_cnt = 0; idx < num_cfg_params; idx++) {
+ if (!of_find_property(cfg_np, cfg_params[idx].name, NULL))
+ continue;
+ of_property_read_u32(cfg_np, cfg_params[idx].name, &val);
+ cfg[cfg_cnt++] = pinfo->pack_cfg(val, &cfg_params[idx]);
+ }
+ grp_name = devm_kzalloc(dev, strlen(parent->name) + 1, GFP_KERNEL);
+ if (!grp_name) {
+ ret = -ENOMEM;
+ goto grp_err;
+ }
+ snprintf(grp_name, strlen(parent->name) + 1, "%s", parent->name);
+
+ /* create the config map entry */
+ map[*nmaps].data.configs.group_or_pin = grp_name;
+ map[*nmaps].data.configs.configs = cfg;
+ map[*nmaps].data.configs.num_configs = cfg_cnt;
+ map[*nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
+ *nmaps += 1;
+
+ /* If there is no function specified in device tree return */
+ if (func_cnt == 0) {
+ *maps = map;
+ goto no_func;
+ }
+ /* Get function mapping */
+ of_property_read_u32(parent, "qcom,pin-func", &val);
+ fn_name = devm_kzalloc(dev, strlen(grp_name) +
+ strlen("-func"),
+ GFP_KERNEL);
+ if (!fn_name) {
+ ret = -ENOMEM;
+ goto func_err;
+ }
+ snprintf(fn_name, strlen(grp_name) + strlen("-func") + 1, "%s%s",
+ parent->name, "-func");
+ map[*nmaps].data.mux.group = grp_name;
+ map[*nmaps].data.mux.function = fn_name;
+ map[*nmaps].type = PIN_MAP_TYPE_MUX_GROUP;
+ *nmaps += 1;
+ *maps = map;
+ of_node_put(parent);
+ return 0;
+
+func_err:
+ kfree(grp_name);
+grp_err:
+ kfree(map);
+cfg_err:
+ kfree(cfg);
+no_func:
+ of_node_put(parent);
+ return ret;
+}
+
+/* free the memory allocated to hold the pin-map table */
+static void msm_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ int idx;
+
+ for (idx = 0; idx < num_maps; idx++) {
+ if (map[idx].type == PIN_MAP_TYPE_CONFIGS_GROUP) {
+ kfree(map[idx].data.configs.group_or_pin);
+ kfree(map[idx].data.configs.configs);
+ } else if (map->type == PIN_MAP_TYPE_MUX_GROUP)
+ kfree(map[idx].data.mux.function);
+ };
+
+ kfree(map);
+}
+
+static struct pinctrl_ops msm_pctrlops = {
+ .get_groups_count = msm_get_grps_count,
+ .get_group_name = msm_get_grps_name,
+ .get_group_pins = msm_get_grps_pins,
+ .dt_node_to_map = msm_dt_node_to_map,
+ .dt_free_map = msm_dt_free_map,
+};
+
+static int msm_of_get_pin(struct device_node *np, int index,
+ struct msm_pinctrl_dd *dd, uint *pin)
+{
+ struct of_phandle_args pargs;
+ struct msm_pintype_info *pinfo;
+ int num_pintypes;
+ int ret, i;
+
+ ret = of_parse_phandle_with_args(np, "qcom,pins", "#qcom,pin-cells",
+ index, &pargs);
+ if (ret)
+ return ret;
+ pinfo = dd->msm_pintype;
+ num_pintypes = dd->num_pintypes;
+ for (i = 0; i < num_pintypes; i++) {
+ /* Find the matching pin type node */
+ if (pargs.np != pinfo->node)
+ continue;
+ /* Check if arg specified is in valid range for pin type */
+ if (pargs.args[0] > pinfo->num_pins) {
+ ret = -EINVAL;
+ goto out;
+ }
+ /*
+ * Pin number = index within pin type + start of pin numbers
+ * for this pin type
+ */
+ *pin = pargs.args[0] + pinfo->pin_start;
+ }
+out:
+ of_node_put(pargs.np);
+ return 0;
+}
+
+static int msm_pinctrl_dt_parse_pins(struct device_node *dev_node,
+ struct msm_pinctrl_dd *dd)
+{
+ struct device *dev;
+ struct device_node *pgrp_np;
+ struct msm_pin_grps *pin_grps, *curr_grp;
+ struct msm_pmx_funcs *pmx_funcs, *curr_func;
+ char *grp_name, *func_name;
+ int ret, i, grp_index = 0, func_index = 0;
+ uint pin = 0, *pins, num_grps = 0, num_pins = 0;
+ uint num_funcs = 0;
+ u32 func = 0;
+
+ dev = dd->dev;
+ for_each_child_of_node(dev_node, pgrp_np) {
+ if (!of_find_property(pgrp_np, "qcom,pins", NULL))
+ continue;
+ num_grps++;
+ }
+
+ for_each_child_of_node(dev_node, pgrp_np) {
+ if (!of_find_property(pgrp_np, "qcom,pin-func", NULL))
+ continue;
+ num_funcs++;
+ }
+
+ pin_grps = (struct msm_pin_grps *)devm_kzalloc(dd->dev,
+ sizeof(*pin_grps) * num_grps,
+ GFP_KERNEL);
+ if (!pin_grps) {
+ dev_err(dev, "Failed to allocate grp desc\n");
+ return -ENOMEM;
+ }
+ pmx_funcs = (struct msm_pmx_funcs *)devm_kzalloc(dd->dev,
+ sizeof(*pmx_funcs) * num_funcs,
+ GFP_KERNEL);
+ if (!pmx_funcs) {
+ dev_err(dev, "Failed to allocate grp desc\n");
+ return -ENOMEM;
+ }
+ /*
+ * Iterate over all child nodes, and for nodes containing pin lists
+ * populate corresponding pin group, and if provided, corresponding
+ * function
+ */
+ for_each_child_of_node(dev_node, pgrp_np) {
+ if (!of_find_property(pgrp_np, "qcom,pins", NULL))
+ continue;
+ curr_grp = pin_grps + grp_index;
+ /* Node name is group name */
+ grp_name = devm_kzalloc(dev, strlen(pgrp_np->name) + 1,
+ GFP_KERNEL);
+ if (!grp_name) {
+ dev_err(dev, "Unable to allocate group name\n");
+ return -ENOMEM;
+ }
+ snprintf(grp_name, strlen(pgrp_np->name) + 1, "%s",
+ pgrp_np->name);
+
+ num_pins = of_count_phandle_with_args(pgrp_np,
+ "qcom,pins",
+ "qcom,pin-cells");
+ if (IS_ERR_VALUE(num_pins)) {
+ dev_err(dev, "pin count not specified for groups %s\n",
+ grp_name);
+ return ret;
+ }
+ pins = devm_kzalloc(dd->dev, sizeof(unsigned int) * num_pins,
+ GFP_KERNEL);
+ if (!pins) {
+ dev_err(dev, "Unable to allocte pins for %s\n",
+ grp_name);
+ }
+ for (i = 0; i < num_pins; i++) {
+ ret = msm_of_get_pin(pgrp_np, i, dd, &pin);
+ if (ret) {
+ dev_err(dev, "Pin grp %s does not have pins\n",
+ grp_name);
+ return ret;
+ }
+ pins[i] = pin;
+ }
+ curr_grp->pins = pins;
+ curr_grp->num_pins = num_pins;
+ curr_grp->name = grp_name;
+ grp_index++;
+ /* Check if func specified */
+ if (!of_find_property(pgrp_np, "qcom,pin-func", NULL))
+ continue;
+ curr_func = pmx_funcs + func_index;
+ func_name = devm_kzalloc(dev, strlen(grp_name) +
+ strlen("-func") + 1,
+ GFP_KERNEL);
+ if (!func_name) {
+ dev_err(dev, "Cannot allocate func name for grp %s",
+ grp_name);
+ return -ENOMEM;
+ }
+ snprintf(func_name, strlen(grp_name)+strlen("-func") + 1,
+ "%s%s", grp_name, "-func");
+ curr_func->name = func_name;
+ curr_func->gps = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL);
+ if (!curr_func->gps) {
+ dev_err(dev, "failed to alloc memory for group list ");
+ return -ENOMEM;
+ }
+ of_property_read_u32(pgrp_np, "qcom,pin-func", &func);
+ curr_grp->func = func;
+ curr_func->gps[0] = grp_name;
+ curr_func->num_grps = 1;
+ func_index++;
+ }
+ dd->pin_grps = pin_grps;
+ dd->num_grps = num_grps;
+ dd->pmx_funcs = pmx_funcs;
+ dd->num_funcs = num_funcs;
+ return 0;
+}
+
+static void msm_populate_pindesc(struct msm_pintype_info *pinfo,
+ struct msm_pindesc *msm_pindesc)
+{
+ int i;
+ struct msm_pindesc *pindesc;
+
+ for (i = 0; i < pinfo->num_pins; i++) {
+ pindesc = &msm_pindesc[i + pinfo->pin_start];
+ pindesc->pin_info = pinfo;
+ snprintf(pindesc->name, sizeof(pindesc->name),
+ "%s-%d", pinfo->name, i);
+ }
+}
+
+static int msm_pinctrl_dt_parse_pintype(struct device_node *dev_node,
+ struct msm_pinctrl_dd *dd)
+{
+ struct device_node *pt_node;
+ struct msm_pindesc *msm_pindesc;
+ struct msm_pintype_info *pintype, *pinfo;
+ void __iomem **ptype_base;
+ u32 num_pins, pinfo_entries, curr_pins;
+ int i;
+ uint total_pins = 0;
+
+ for_each_child_of_node(dev_node, pt_node) {
+ if (!of_find_property(pt_node, "qcom,pin-type", NULL))
+ break;
+ else {
+ of_property_read_u32(pt_node, "qcom,num-pins",
+ &num_pins);
+ total_pins += num_pins;
+ }
+ }
+ dd->msm_pindesc = devm_kzalloc(dd->dev,
+ sizeof(struct msm_pindesc) *
+ total_pins, GFP_KERNEL);
+ if (!dd->msm_pindesc) {
+ dev_err(dd->dev, "Unable to allocate msm pindesc");
+ return -ENOMEM;
+ }
+ pinfo = dd->msm_pintype;
+ pinfo_entries = dd->num_pintypes;
+ dd->num_pins = total_pins;
+ msm_pindesc = dd->msm_pindesc;
+ curr_pins = 0;
+ /*
+ * Populate pin descriptor based on each pin type present in Device
+ * tree and supported by the driver
+ */
+ for_each_child_of_node(dev_node, pt_node) {
+ if (!of_find_property(pt_node, "qcom,pin-type", NULL))
+ break;
+ else {
+ for (i = 0; i < pinfo_entries; i++) {
+ pintype = &pinfo[i];
+ if (!of_property_match_string(pt_node,
+ "qcom,pin-type",
+ pintype->name)) {
+ of_property_read_u32(pt_node,
+ "qcom,num-pins",
+ &num_pins);
+ pintype->num_pins = num_pins;
+ pintype->pin_start = curr_pins;
+ pintype->pin_end = curr_pins +
+ num_pins;
+ of_node_get(pt_node);
+ pintype->node = pt_node;
+ ptype_base = &pintype->reg_data;
+ pintype->set_reg_base(ptype_base,
+ dd->base);
+ msm_populate_pindesc(pintype,
+ msm_pindesc);
+ curr_pins += num_pins;
+ }
+
+ }
+ }
+ }
+ return 0;
+}
+
+static const struct of_device_id msm_pinctrl_dt_match[] = {
+ { .compatible = "qcom,msm-tlmm-v3",
+ .data = &tlmm_v3_pintypes, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, msm_pinctrl_dt_match);
+
+static int msm_pinctrl_get_drvdata(struct msm_pinctrl_dd *dd,
+ struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ const struct msm_tlmm *tlmm_info;
+ int ret, i;
+ struct device_node *node = pdev->dev.of_node;
+
+ match = of_match_node(msm_pinctrl_dt_match, node);
+ if (IS_ERR(match))
+ return PTR_ERR(match);
+ tlmm_info = match->data;
+ dd->msm_pintype = tlmm_info->pintype_info;
+ dd->num_pintypes = tlmm_info->num_entries;
+ ret = msm_pinctrl_dt_parse_pintype(node, dd);
+ if (ret)
+ goto out;
+
+ ret = msm_pinctrl_dt_parse_pins(node, dd);
+ if (ret)
+ goto pin_err;
+
+pin_err:
+ for (i = 0; i < dd->num_pintypes; i++)
+ of_node_put(dd->msm_pintype[i].node);
+
+out:
+ return ret;
+}
+
+static int msm_register_pinctrl(struct msm_pinctrl_dd *dd)
+{
+ int i;
+ struct pinctrl_pin_desc *pindesc;
+ struct pinctrl_desc *ctrl_desc = &dd->pctl;
+
+ ctrl_desc->name = "msm-pinctrl";
+ ctrl_desc->owner = THIS_MODULE;
+ ctrl_desc->pmxops = &msm_pmxops;
+ ctrl_desc->confops = &msm_pinconfops;
+ ctrl_desc->pctlops = &msm_pctrlops;
+
+ pindesc = devm_kzalloc(dd->dev, sizeof(*pindesc) * dd->num_pins,
+ GFP_KERNEL);
+ if (!pindesc) {
+ dev_err(dd->dev, "Failed to allocate pinctrl pin desc\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < dd->num_pins; i++) {
+ pindesc[i].number = i;
+ pindesc[i].name = dd->msm_pindesc[i].name;
+ }
+ ctrl_desc->pins = pindesc;
+ ctrl_desc->npins = dd->num_pins;
+ dd->pctl_dev = pinctrl_register(ctrl_desc, dd->dev, dd);
+ if (!dd->pctl_dev) {
+ dev_err(dd->dev, "could not register pinctrl driver\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void msm_pinctrl_cleanup_dd(struct msm_pinctrl_dd *dd)
+{
+ int i;
+ for (i = 0; i < dd->num_pintypes; i++)
+ of_node_put(dd->msm_pintype[i].node);
+}
+
+static int msm_pinctrl_probe(struct platform_device *pdev)
+{
+ struct msm_pinctrl_dd *dd;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int ret;
+
+ dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+ if (!dd) {
+ dev_err(dev, "Alloction failed for driver data\n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dd->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dd->base))
+ return PTR_ERR(dd->base);
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res)
+ dd->irq = res->start;
+ dd->dev = dev;
+ ret = msm_pinctrl_get_drvdata(dd, pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "driver data not available\n");
+ return -EINVAL;
+ }
+ ret = msm_register_pinctrl(dd);
+ if (ret) {
+ msm_pinctrl_cleanup_dd(dd);
+ return ret;
+ }
+ platform_set_drvdata(pdev, dd);
+ return 0;
+}
+
+static struct platform_driver msm_pinctrl_driver = {
+ .probe = msm_pinctrl_probe,
+ .driver = {
+ .name = "msm-pinctrl",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(msm_pinctrl_dt_match),
+ },
+};
+
+static int __init msm_pinctrl_drv_register(void)
+{
+ return platform_driver_register(&msm_pinctrl_driver);
+}
+postcore_initcall(msm_pinctrl_drv_register);
+
+static void __exit msm_pinctrl_drv_unregister(void)
+{
+ platform_driver_unregister(&msm_pinctrl_driver);
+}
+module_exit(msm_pinctrl_drv_unregister);
+
+MODULE_AUTHOR("Hanumant Singh <[email protected]>");
+MODULE_LICENSE("GPLv2");
+
diff --git a/drivers/pinctrl/pinctrl-msm.h b/drivers/pinctrl/pinctrl-msm.h
new file mode 100644
index 0000000..7903a8e
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-msm.h
@@ -0,0 +1,112 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __PINCTRL_MSM_H__
+#define __PINCTRL_MSM_H__
+
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/machine.h>
+
+/**
+ * struct msm_pin_group: group of pins having the same pinmux function.
+ * @name: name of the pin group.
+ * @pins: the pins included in this group.
+ * @num_pins: number of pins included in this group.
+ * @func: the function number to be programmed when selected.
+ */
+struct msm_pin_grps {
+ const char *name;
+ unsigned int *pins;
+ unsigned num_pins;
+ u32 func;
+};
+
+/**
+ * struct msm_pmx_funcs: represent a pin function.
+ * @name: name of the pin function.
+ * @gps: one or more names of pin groups that provide this function.
+ * @num_grps: number of groups included in @groups.
+ */
+struct msm_pmx_funcs {
+ const char *name;
+ const char **gps;
+ unsigned num_grps;
+};
+
+/**
+ * struct msm_tlmm_cfgs: represent config properties of a pin type.
+ * @name: name of config.
+ * @id: id of the config.
+ */
+
+struct msm_tlmm_cfg_params {
+ const char *name;
+ unsigned int id;
+};
+
+/**
+ * struct msm_pintype_info: represent a pin type supported by the TLMM.
+ * @prg_cfg: helper to program a given config for a pintype.
+ * @prg_func: helper to program a given func for a pintype.
+ * @pack_cfg: helper to pack a parsed config as per a pintype.
+ * @set_reg_base: helper to set the register base address for a pintype.
+ * @reg_data: register base for a pintype.
+ * @tlmm_cfg_param: config parameters for pins of a given pintype.
+ * @name: name of pintype.
+ * @num_pins: number of pins of given pintype.
+ * @pin_start: starting pin number for the given pintype within pinctroller.
+ * @pin_end: ending pin number for the given pintype within pinctroller.
+ * @node: device node for the pintype.
+ */
+struct msm_pintype_info {
+ int (*prg_cfg)(uint pin_no, unsigned long *config, void *reg_data,
+ bool rw);
+ void (*prg_func)(uint pin_no, u32 func, void *reg_data, bool enable);
+ unsigned long (*pack_cfg)(u32 val,
+ struct msm_tlmm_cfg_params const *param);
+ void (*set_reg_base)(void __iomem **ptype_base,
+ void __iomem *tlmm_base);
+ void __iomem *reg_data;
+ struct msm_tlmm_cfg_params const *tlmm_cfg_param;
+ u32 num_cfg_params;
+ const char *name;
+ u32 num_pins;
+ int pin_start;
+ int pin_end;
+ struct device_node *node;
+};
+
+/**
+ * struct msm_tlmm: represents all the TLMM pintypes for a given TLMM version.
+ * @num_entries: number of pintypes.
+ * @pintype_info: descriptor for the pintypes. One for each present.
+ */
+struct msm_tlmm {
+ const uint num_entries;
+ struct msm_pintype_info *pintype_info;
+};
+
+/**
+ * struct msm_pindesc: descriptor for all pins maintained by pinctrl driver
+ * @pin_info: pintype for a given pin.
+ * @name: name of the pin.
+ */
+struct msm_pindesc {
+ struct msm_pintype_info *pin_info;
+ char name[20];
+};
+
+/* TLMM version specific data */
+extern struct msm_tlmm tlmm_v3_pintypes;
+#endif
+
--
1.8.2.1

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


2013-06-24 12:18:58

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH] pinctrl: msm: Add support for MSM TLMM pinmux

On Fri, Jun 21, 2013 at 11:52 PM, Hanumant Singh
<[email protected]> wrote:

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

This is really nice.

> + The pin group node must additionally have a pin configuration node as its own
> + child node. There can be more then one such configuration node for a pin group
> + node. There can be one or more configurations within the configuration
> + node. These configurations are applied to all pins mentoned above using the
> + "qcom,pins" property. These configurations are specific to the pintype of the
> + pins. The following pin configuration properties are supported by general
> + purpose pins.
> +
> + - qcom,gp-pull: Pull up/down configuration.
> + - qcom,gp-drv: Drive strength configuration.
> + - qcom,gp-dir: Pull up/down configuration in power down mode.

Rebase this to use the generic pin config mappings and parsing
code that can be found in the "devel" branch of the pinctrl tree.

> + The following pin configurations are properties are supported by SDC pins
> + - qcom,sdc1-clk-pull: Pull up/down configuration SDC1 clock pin.
> + - qcom,sdc1-clk-drv: Drive strength configuration for SDC1 clock pin.
> + - qcom,sdc1-cmd-pull: Pull up/down configuration for SDC1 command pin.
> + - qcom,sdc1-cmd-drv: Drive strength configuration for SDC1 command pin.
> + - qcom,sdc1-data-pull: Pull up/down configuration for SDC1 data pin.
> + - qcom,sdc1-data-drv: Drive strength configuration for SDC1 data pin.
> + - qcom,sdc2-clk-pull: Pull up/down configuration SDC2 clock pin.
> + - qcom,sdc2-clk-drv: Drive strength configuration for SDC2 clock pin.
> + - qcom,sdc2-cmd-pull: Pull up/down configuration for SDC2 command pin.
> + - qcom,sdc2-cmd-drv: Drive strength configuration for SDC2 command pin.
> + - qcom,sdc2-data-pull: Pull up/down configuration for SDC2 data pin.
> + - qcom,sdc2-data-drv: Drive strength configuration for SDC2 data pin.

I don't understand why each sdc thing needs its own definition
for everything. Please use the generic pin config bindings, call the
generic parser function and then reject if someone tries to config
something that is not supported.

> + spi-bus {
> + /*
> + * MOSI, MISO and CLK lines
> + * all sharing same function and config
> + * settings for each configuration node.
> + */
> + qcom,pins = <&gp 0>, <&gp 1>, <&gp 3>;
> + qcom,pin-func = <1>;
> +
> + /* Active configuration of bus pins */
> + spi-bus-active: spi-bus-active {
> + qcom,gp-drv = <3>; /* 8 MA */
> + qcom,gp-pull = <0>; /* No PULL */


This would be:

drive-strength = <8>;
bias-disable;

With the generic bindings I believe.

> +Example 4: Set up the default pin state for spi controller.
> +
> + static inline int msm_spi_request_pins{struct msm_spi *dd)
> + {
> + /* ... */
> + dd->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
> + }

Nowadays the device core will do this, so this is not any good
advice. (See commit ab78029ecc347debbd737f06688d788bd9d60c1d)

I will look closer at this when it uses generic pinconf and
generic pinconf DT bindings, let's target v3.12.

Yours,
Linus Walleij

2013-06-25 17:41:32

by Hanumant Singh

[permalink] [raw]
Subject: Re: [PATCH] pinctrl: msm: Add support for MSM TLMM pinmux

On 06/24/2013 05:18 AM, Linus Walleij wrote:

>> +
>> + - qcom,gp-pull: Pull up/down configuration.
>> + - qcom,gp-drv: Drive strength configuration.
>> + - qcom,gp-dir: Pull up/down configuration in power down mode.
>
> Rebase this to use the generic pin config mappings and parsing
> code that can be found in the "devel" branch of the pinctrl tree.
>
>> + The following pin configurations are properties are supported by SDC pins
>> + - qcom,sdc1-clk-pull: Pull up/down configuration SDC1 clock pin.
>> + - qcom,sdc1-clk-drv: Drive strength configuration for SDC1 clock pin.
>> + - qcom,sdc1-cmd-pull: Pull up/down configuration for SDC1 command pin.
>> + - qcom,sdc1-cmd-drv: Drive strength configuration for SDC1 command pin.
>> + - qcom,sdc1-data-pull: Pull up/down configuration for SDC1 data pin.
>> + - qcom,sdc1-data-drv: Drive strength configuration for SDC1 data pin.
>> + - qcom,sdc2-clk-pull: Pull up/down configuration SDC2 clock pin.
>> + - qcom,sdc2-clk-drv: Drive strength configuration for SDC2 clock pin.
>> + - qcom,sdc2-cmd-pull: Pull up/down configuration for SDC2 command pin.
>> + - qcom,sdc2-cmd-drv: Drive strength configuration for SDC2 command pin.
>> + - qcom,sdc2-data-pull: Pull up/down configuration for SDC2 data pin.
>> + - qcom,sdc2-data-drv: Drive strength configuration for SDC2 data pin.
>
> I don't understand why each sdc thing needs its own definition
> for everything. Please use the generic pin config bindings, call the
> generic parser function and then reject if someone tries to config
> something that is not supported.
>

The register semantics of SDC1 clk, command, and data lines, pull up and
drive strength attributes differ from SDC2 clk, command and data lines.

In general the TLMM v3 has more pin types then just the general/multi
purpose(gp) and SDC pin types above.
There are some pin types on the TLMM, whose config attributes do not
fall under the cattegories supported by generic pin config.
These attributes in some cases happen to be protocol specific.
Hence I would prefer to go with a custom config pack and unpack
implementation rather then the generic one.

>
>> +Example 4: Set up the default pin state for spi controller.
>> +
>> + static inline int msm_spi_request_pins{struct msm_spi *dd)
>> + {
>> + /* ... */
>> + dd->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
>> + }
>
> Nowadays the device core will do this, so this is not any good
> advice. (See commit ab78029ecc347debbd737f06688d788bd9d60c1d)

I will modify this.

>
> I will look closer at this when it uses generic pinconf and
> generic pinconf DT bindings, let's target v3.12.
>
> Yours,
> Linus Walleij
>


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

2013-06-27 08:26:29

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH] pinctrl: msm: Add support for MSM TLMM pinmux

On Tue, Jun 25, 2013 at 7:41 PM, hanumant <[email protected]> wrote:
> On 06/24/2013 05:18 AM, Linus Walleij wrote:

>>> + The following pin configurations are properties are supported by SDC pins
>>> + - qcom,sdc1-clk-pull: Pull up/down configuration SDC1 clock pin.
>>> + - qcom,sdc1-clk-drv: Drive strength configuration for SDC1 clock pin.
>>> + - qcom,sdc1-cmd-pull: Pull up/down configuration for SDC1 command pin.
>>> + - qcom,sdc1-cmd-drv: Drive strength configuration for SDC1 command pin.
>>> + - qcom,sdc1-data-pull: Pull up/down configuration for SDC1 data pin.
>>> + - qcom,sdc1-data-drv: Drive strength configuration for SDC1 data pin.
>>> + - qcom,sdc2-clk-pull: Pull up/down configuration SDC2 clock pin.
>>> + - qcom,sdc2-clk-drv: Drive strength configuration for SDC2 clock pin.
>>> + - qcom,sdc2-cmd-pull: Pull up/down configuration for SDC2 command pin.
>>> + - qcom,sdc2-cmd-drv: Drive strength configuration for SDC2 command pin.
>>> + - qcom,sdc2-data-pull: Pull up/down configuration for SDC2 data pin.
>>> + - qcom,sdc2-data-drv: Drive strength configuration for SDC2 data pin.
>>
>> I don't understand why each sdc thing needs its own definition
>> for everything. Please use the generic pin config bindings, call the
>> generic parser function and then reject if someone tries to config
>> something that is not supported.
>
> The register semantics of SDC1 clk, command, and data lines, pull up and
> drive strength attributes differ from SDC2 clk, command and data lines.

The register semantics does not have anything to do with the
representation in the device tree. The register semantics is a matter
for the driver, the device tree tells how to configure that driver,
the idea is not to name all species of config registers in the device
tree but to configure them, logically.

> In general the TLMM v3 has more pin types then just the general/multi
> purpose(gp) and SDC pin types above.
> There are some pin types on the TLMM, whose config attributes do not
> fall under the cattegories supported by generic pin config.

One does not exclude the other. Some aspect of a pin
may be configured using the generic bindings, some aspect
need vendor-specific extensions.

> These attributes in some cases happen to be protocol specific.
> Hence I would prefer to go with a custom config pack and unpack
> implementation rather then the generic one.

I disagree, but I'm open to negotiations :-)

Yours,
Linus Walleij

2013-06-27 15:17:24

by Hanumant Singh

[permalink] [raw]
Subject: Re: [PATCH] pinctrl: msm: Add support for MSM TLMM pinmux

On 06/27/2013 01:26 AM, Linus Walleij wrote:
> On Tue, Jun 25, 2013 at 7:41 PM, hanumant <[email protected]> wrote:
>> On 06/24/2013 05:18 AM, Linus Walleij wrote:
>
>>>> + The following pin configurations are properties are supported by SDC pins
>>>> + - qcom,sdc1-clk-pull: Pull up/down configuration SDC1 clock pin.
>>>> + - qcom,sdc1-clk-drv: Drive strength configuration for SDC1 clock pin.
>>>> + - qcom,sdc1-cmd-pull: Pull up/down configuration for SDC1 command pin.
>>>> + - qcom,sdc1-cmd-drv: Drive strength configuration for SDC1 command pin.
>>>> + - qcom,sdc1-data-pull: Pull up/down configuration for SDC1 data pin.
>>>> + - qcom,sdc1-data-drv: Drive strength configuration for SDC1 data pin.
>>>> + - qcom,sdc2-clk-pull: Pull up/down configuration SDC2 clock pin.
>>>> + - qcom,sdc2-clk-drv: Drive strength configuration for SDC2 clock pin.
>>>> + - qcom,sdc2-cmd-pull: Pull up/down configuration for SDC2 command pin.
>>>> + - qcom,sdc2-cmd-drv: Drive strength configuration for SDC2 command pin.
>>>> + - qcom,sdc2-data-pull: Pull up/down configuration for SDC2 data pin.
>>>> + - qcom,sdc2-data-drv: Drive strength configuration for SDC2 data pin.
>>>
>>> I don't understand why each sdc thing needs its own definition
>>> for everything. Please use the generic pin config bindings, call the
>>> generic parser function and then reject if someone tries to config
>>> something that is not supported.
>>
>> The register semantics of SDC1 clk, command, and data lines, pull up and
>> drive strength attributes differ from SDC2 clk, command and data lines.
>
> The register semantics does not have anything to do with the
> representation in the device tree. The register semantics is a matter
> for the driver, the device tree tells how to configure that driver,
> the idea is not to name all species of config registers in the device
> tree but to configure them, logically.

Fair enough. I think this is possible to do.
>
>> In general the TLMM v3 has more pin types then just the general/multi
>> purpose(gp) and SDC pin types above.
>> There are some pin types on the TLMM, whose config attributes do not
>> fall under the cattegories supported by generic pin config.
>
> One does not exclude the other. Some aspect of a pin
> may be configured using the generic bindings, some aspect
> need vendor-specific extensions.
>
>> These attributes in some cases happen to be protocol specific.
>> Hence I would prefer to go with a custom config pack and unpack
>> implementation rather then the generic one.
>
> I disagree, but I'm open to negotiations :-)

How does this sound?. If the attributes of a pin type can be defined
using the generic configs, I will use the generic configs and parser.
If the attributes are entirely protocol/MSM specific, I will use custom
configs and parsers. In terms of attributes of pins, all the pin types
on the TLMM fall into either one of those categories, but never in both.

Thanks
Hanumant

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