linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/4] Add TZ1090 pinctrl/gpio drivers
@ 2013-06-20  9:26 James Hogan
  2013-06-20  9:26 ` [PATCH v3 1/4] pinctrl-tz1090: add TZ1090 pinctrl driver James Hogan
                   ` (3 more replies)
  0 siblings, 4 replies; 12+ messages in thread
From: James Hogan @ 2013-06-20  9:26 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-kernel, James Hogan, Grant Likely, Rob Herring,
	Rob Landley, devicetree-discuss, linux-doc

This patchset adds GPIO and pin controller drivers for the TZ1090, for
both the general GPIOs/pins, and the low power (PDC) GPIOs/pins.

LinusW: Please consider taking these for v3.11. The pinctrl ones need to
go via your pinctrl tree due to the dependence on the new pinconf
bindings.

Changes in v3:
 - separated from irq-imgpdc and removed arch/metag changes to allow
   these patches to go upstream separately via the pinctrl[/gpio] trees
   (particularly the pinctrl drivers depend on the new pinconf DT
   bindings).
 - some s/unsigned/unsigned int/.
 - some s/unsigned int/bool/ and use of BIT().
 - pinctrl-tz1090*: switch to generic pinconfig DT bindings and
   pinconf_generic_dump_config.
 - pinctrl-tz1090*: use tz1090, prefix for pins and function in DT
   bindings.
 - pinctrl-tz1090*: make internal functions static.
 - pinctrl-tz1090*: move initcall from postcore to arch.
 - gpio-tz1090*: refer to <dt-bindings/gpio/gpio.h> and
   <dt-bindings/interrupt-controller/irq.h> flags in bindings.
 - gpio-tz1090*: move initcall from postcore to subsys.
 - gpio-tz1090: add REG_ prefix to some constants for consistency.
 - gpio-tz1090: add comment to explain tz1090_gpio_irq_next_edge
   cunningness.

Changes in v2:

There've been plenty of changes since v1 (thanks for all the feedback!)
so I think it's time for v2. I believe the only feedback not yet
addressed is changing irq-imgpdc driver to use generic irqchip.

 - rebased on v3.10-rc2
 - removed arch/metag/soc/ directory
 - removed arch/metag/include/asm/soc-tz1090/gpio.h
 - irq-imgpdc: use cached versions of irq_en and irq_route registers
 - irq-imgpdc: switch to using raw_spinlock
 - irq-imgpdc: (not had time to switch to generic irqchip yet)
 - add drive strength to generic pinconf debugfs output (patch 4)
 - add BIAS_BUS_HOLD generic pinconf (patch 5)
 - pinctrl-tz1090: switched to generic pinconf (a previous patch adds
   the BIAS_BUS_HOLD generic pinconf)
 - pinctrl-tz1090: changed device tree bindings to more closely match
   generic pinconf (separate tristate, pull-up, pull-down, bus-hold
   flags instead of pull=<X>, drive-strength measured in mA)
 - pinctrl-tz1090: removed "select" pinconf. Instead pins in a pin group
   have their own individual pin groups and can be individually muxed.
   Different pins in same pin mux group muxed to different functions is
   disallowed. Pin switched to peripheral mode when mux enabled.
 - pinctrl-tz1090: make use of BIT() from linux/bitops.h
 - pinctrl-tz1090: add a whole bunch of comments
 - gpio-tz1090: remove references to Linux flags in dt bindings
 - gpio-tz1090: make use of BIT() from linux/bitops.h
 - gpio-tz1090: make register accessors inline to match pinctrl
 - gpio-tz1090: update gpio-ranges to use 3 cells after recent ABI
   breakage
 - pinctrl-tz1090-pdc: switched to generic pinconf (a previous patch
   adds the BIAS_BUS_HOLD generic pinconf)
 - pinctrl-tz1090-pdc: changed device tree bindings to more closely
   match generic pinconf (separate tristate, pull-up, pull-down,
   bus-hold flags instead of pull=<X>, drive-strength measured in mA)
 - pinctrl-tz1090-pdc: make use of BIT() from linux/bitops.h
 - pinctrl-tz1090-pdc: add a whole bunch of comments
 - gpio-tz1090-pdc: remove references to Linux flags in dt bindings
 - gpio-tz1090-pdc: make use of BIT() from linux/bitops.h
 - gpio-tz1090-pdc: make register accessors inline to match pinctrl
 - gpio-tz1090-pdc: update gpio-ranges to use 3 cells after recent ABI
   breakage

Cc: Grant Likely <grant.likely@linaro.org>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: devicetree-discuss@lists.ozlabs.org
Cc: linux-doc@vger.kernel.org

James Hogan (4):
  pinctrl-tz1090: add TZ1090 pinctrl driver
  gpio-tz1090: add TZ1090 gpio driver
  pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver
  gpio-tz1090-pdc: add TZ1090 PDC gpio driver

 .../devicetree/bindings/gpio/gpio-tz1090-pdc.txt   |   43 +
 .../devicetree/bindings/gpio/gpio-tz1090.txt       |   87 +
 .../bindings/pinctrl/img,tz1090-pdc-pinctrl.txt    |  130 ++
 .../bindings/pinctrl/img,tz1090-pinctrl.txt        |  232 +++
 drivers/gpio/Kconfig                               |   14 +
 drivers/gpio/Makefile                              |    2 +
 drivers/gpio/gpio-tz1090-pdc.c                     |  243 +++
 drivers/gpio/gpio-tz1090.c                         |  633 ++++++
 drivers/pinctrl/Kconfig                            |   12 +
 drivers/pinctrl/Makefile                           |    2 +
 drivers/pinctrl/pinctrl-tz1090-pdc.c               | 1029 ++++++++++
 drivers/pinctrl/pinctrl-tz1090.c                   | 2077 ++++++++++++++++++++
 12 files changed, 4504 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/gpio-tz1090-pdc.txt
 create mode 100644 Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
 create mode 100644 Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt
 create mode 100644 Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt
 create mode 100644 drivers/gpio/gpio-tz1090-pdc.c
 create mode 100644 drivers/gpio/gpio-tz1090.c
 create mode 100644 drivers/pinctrl/pinctrl-tz1090-pdc.c
 create mode 100644 drivers/pinctrl/pinctrl-tz1090.c

-- 
1.8.1.2



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

* [PATCH v3 1/4] pinctrl-tz1090: add TZ1090 pinctrl driver
  2013-06-20  9:26 [PATCH v3 0/4] Add TZ1090 pinctrl/gpio drivers James Hogan
@ 2013-06-20  9:26 ` James Hogan
  2013-06-24 15:04   ` Linus Walleij
  2013-06-20  9:26 ` [PATCH v3 2/4] gpio-tz1090: add TZ1090 gpio driver James Hogan
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 12+ messages in thread
From: James Hogan @ 2013-06-20  9:26 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-kernel, James Hogan, Grant Likely, Rob Herring,
	Rob Landley, linux-doc, devicetree-discuss

Add a pin control driver for the main pins on the TZ1090 SoC. This
doesn't include the low-power pins as they're controlled separately via
the Powerdown Controller (PDC) registers.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: linux-doc@vger.kernel.org
Cc: devicetree-discuss@lists.ozlabs.org
---
Changes in v3:
 - separated from irq-imgpdc and removed arch/metag changes to allow
   these patches to go upstream separately via the pinctrl[/gpio] trees
   (particularly the pinctrl drivers depend on the new pinconf DT
   bindings).
 - some s/unsigned/unsigned int/.
 - some s/unsigned int/bool/ and use of BIT().
 - pinctrl-tz1090*: switch to generic pinconfig DT bindings and
   pinconf_generic_dump_config.
 - pinctrl-tz1090*: use tz1090, prefix for pins and function in DT
   bindings.
 - pinctrl-tz1090*: make internal functions static.
 - pinctrl-tz1090*: move initcall from postcore to arch.

Changes in v2:
 - pinctrl-tz1090: switched to generic pinconf (a previous patch adds
   the BIAS_BUS_HOLD generic pinconf)
 - pinctrl-tz1090: changed device tree bindings to more closely match
   generic pinconf (separate tristate, pull-up, pull-down, bus-hold
   flags instead of pull=<X>, drive-strength measured in mA)
 - pinctrl-tz1090: removed "select" pinconf. Instead pins in a pin group
   have their own individual pin groups and can be individually muxed.
   Different pins in same pin mux group muxed to different functions is
   disallowed. Pin switched to peripheral mode when mux enabled.
 - pinctrl-tz1090: make use of BIT() from linux/bitops.h
 - pinctrl-tz1090: add a whole bunch of comments

 .../bindings/pinctrl/img,tz1090-pinctrl.txt        |  232 +++
 drivers/pinctrl/Kconfig                            |    6 +
 drivers/pinctrl/Makefile                           |    1 +
 drivers/pinctrl/pinctrl-tz1090.c                   | 2077 ++++++++++++++++++++
 4 files changed, 2316 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt
 create mode 100644 drivers/pinctrl/pinctrl-tz1090.c

diff --git a/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt
new file mode 100644
index 0000000..39bfd9c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt
@@ -0,0 +1,232 @@
+ImgTec TZ1090 pin controller
+
+Required properties:
+- compatible: "img,tz1090-pinctrl"
+- reg: Should contain the register physical address and length of the pad
+  configuration registers (CR_PADS_* and CR_IF_CTL0).
+
+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".
+
+TZ1090'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. For this reason, even seemingly boolean
+values are actually tristates in this binding: unspecified, off, or on.
+Unspecified is represented as an absent property, and off/on are represented as
+integer values 0 and 1.
+
+Required subnode-properties:
+- tz1090,pins : An array of strings. Each string contains the name of a pin or
+  group. Valid values for these names are listed below.
+
+Optional subnode-properties:
+- tz1090,function: A string containing the name of the function to mux to the
+  pin or group. Valid values for function names are listed below, including
+  which pingroups can be muxed to them.
+- supported generic pinconfig properties (for further details see
+  Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt):
+  - bias-disable
+  - bias-high-impedance
+  - bias-bus-hold
+  - bias-pull-up
+  - bias-pull-down
+  - input-schmitt-enable
+  - input-schmitt-disable
+  - slew-rate: Integer, control slew rate of pins.
+      0: slow (half frequency)
+      1: fast
+  - drive-strength: Integer, control drive strength of pins in mA.
+      2: 2mA
+      4: 4mA
+      8: 8mA
+      12: 12mA
+
+
+Note that many of these properties are only valid for certain specific pins
+or groups. See the TZ1090 TRM for complete details regarding which groups
+support which functionality. The Linux pinctrl driver may also be a useful
+reference.
+
+Valid values for pin and group names are:
+
+  gpio pins:
+
+    These all support bias-high-impediance, bias-pull-up, bias-pull-down, and
+    bias-bus-hold (which can also be provided to any of the groups below to set
+    it for all pins in that group).
+
+    They also all support the some form of muxing. Any pins which are contained
+    in one of the mux groups (see below) can be muxed only to the functions
+    supported by the mux group. All other pins can be muxed to the "perip"
+    function which which enables them with their intended peripheral.
+
+    Different pins in the same mux group cannot be muxed to different functions,
+    however it is possible to mux only a subset of the pins in a mux group to a
+    particular function and leave the remaining pins unmuxed. This is useful if
+    the board connects certain pins in a group to other devices to be controlled
+    by GPIO, and you don't want the usual peripheral to have any control of the
+    pin.
+
+    ant_sel0, ant_sel1, gain0, gain1, gain2, gain3, gain4, gain5, gain6, gain7,
+    i2s_bclk_out, i2s_din, i2s_dout0, i2s_dout1, i2s_dout2, i2s_lrclk_out,
+    i2s_mclk, pa_on, pdm_a, pdm_b, pdm_c, pdm_d, pll_on, rx_hp, rx_on,
+    scb0_sclk, scb0_sdat, scb1_sclk, scb1_sdat, scb2_sclk, scb2_sdat, sdh_cd,
+    sdh_clk_in, sdh_wp, sdio_clk, sdio_cmd, sdio_d0, sdio_d1, sdio_d2, sdio_d3,
+    spi0_cs0, spi0_cs1, spi0_cs2, spi0_din, spi0_dout, spi0_mclk, spi1_cs0,
+    spi1_cs1, spi1_cs2, spi1_din, spi1_dout, spi1_mclk, tft_blank_ls, tft_blue0,
+    tft_blue1, tft_blue2, tft_blue3, tft_blue4, tft_blue5, tft_blue6, tft_blue7,
+    tft_green0, tft_green1, tft_green2, tft_green3, tft_green4, tft_green5,
+    tft_green6, tft_green7, tft_hsync_nr, tft_panelclk, tft_pwrsave, tft_red0,
+    tft_red1, tft_red2, tft_red3, tft_red4, tft_red5, tft_red6, tft_red7,
+    tft_vd12acb, tft_vdden_gd, tft_vsync_ns, tx_on, uart0_cts, uart0_rts,
+    uart0_rxd, uart0_txd, uart1_rxd, uart1_txd.
+
+        bias-high-impediance:  supported.
+        bias-pull-up:          supported.
+        bias-pull-down:        supported.
+        bias-bus-hold:         supported.
+        function:              perip or those supported by pin's mux group.
+
+  other pins:
+
+    These other pins are part of various pin groups below, but can't be
+    controlled as GPIOs. They do however support bias-high-impediance,
+    bias-pull-up, bias-pull-down, and bias-bus-hold (which can also be provided
+    to any of the groups below to set it for all pins in that group).
+
+    clk_out0, clk_out1, tck, tdi, tdo, tms, trst.
+
+        bias-high-impediance:  supported.
+        bias-pull-up:          supported.
+        bias-pull-down:        supported.
+        bias-bus-hold:         supported.
+
+  mux groups:
+
+    These all support function, and some support drive configs.
+
+    afe
+        pins:                  tx_on, rx_on, pll_on, pa_on, rx_hp, ant_sel0,
+                               ant_sel1, gain0, gain1, gain2, gain3, gain4,
+                               gain5, gain6, gain7.
+        function:              afe, ts_out_0.
+        input-schmitt-enable:  supported.
+        input-schmitt-disable: supported.
+        slew-rate:             supported.
+        drive-strength:        supported.
+    pdm_d
+        pins:                  pdm_d.
+        function:              pdm_dac, usb_vbus.
+    sdh
+        pins:                  sdh_cd, sdh_wp, sdh_clk_in.
+        function:              sdh, sdio.
+    sdio
+        pins:                  sdio_clk, sdio_cmd, sdio_d0, sdio_d1, sdio_d2,
+                               sdio_d3.
+        function:              sdio, sdh.
+    spi1_cs2
+        pins:                  spi1_cs2.
+        function:              spi1_cs2, usb_vbus.
+    tft
+        pins:                  tft_red0, tft_red1, tft_red2, tft_red3,
+                               tft_red4, tft_red5, tft_red6, tft_red7,
+                               tft_green0, tft_green1, tft_green2, tft_green3,
+                               tft_green4, tft_green5, tft_green6, tft_green7,
+                               tft_blue0, tft_blue1, tft_blue2, tft_blue3,
+                               tft_blue4, tft_blue5, tft_blue6, tft_blue7,
+                               tft_vdden_gd, tft_panelclk, tft_blank_ls,
+                               tft_vsync_ns, tft_hsync_nr, tft_vd12acb,
+                               tft_pwrsave.
+        function:              tft, ext_dac, not_iqadc_stb, iqdac_stb, ts_out_1,
+                               lcd_trace, phy_ringosc.
+        input-schmitt-enable:  supported.
+        input-schmitt-disable: supported.
+        slew-rate:             supported.
+        drive-strength:        supported.
+
+  drive groups:
+
+    These all support input-schmitt-enable, input-schmitt-disable, slew-rate,
+    and drive-strength.
+
+    jtag
+        pins:   tck, trst, tdi, tdo, tms.
+    scb1
+        pins:   scb1_sdat, scb1_sclk.
+    scb2
+        pins:   scb2_sdat, scb2_sclk.
+    spi0
+        pins:   spi0_mclk, spi0_cs0, spi0_cs1, spi0_cs2, spi0_dout, spi0_din.
+    spi1
+        pins:   spi1_mclk, spi1_cs0, spi1_cs1, spi1_cs2, spi1_dout, spi1_din.
+    uart
+        pins:   uart0_txd, uart0_rxd, uart0_rts, uart0_cts,
+                uart1_txd, uart1_rxd.
+    drive_i2s
+        pins:   clk_out1, i2s_din, i2s_dout0, i2s_dout1, i2s_dout2,
+                i2s_lrclk_out, i2s_bclk_out, i2s_mclk.
+    drive_pdm
+        pins:   clk_out0, pdm_b, pdm_a.
+    drive_scb0
+        pins:   scb0_sclk, scb0_sdat, pdm_d, pdm_c.
+    drive_sdio
+        pins:   sdio_clk, sdio_cmd, sdio_d0, sdio_d1, sdio_d2, sdio_d3,
+                sdh_wp, sdh_cd, sdh_clk_in.
+
+  convenience groups:
+
+    These are just convenient groupings of pins and don't support any drive
+    configs.
+
+    uart0
+        pins:   uart0_cts, uart0_rts, uart0_rxd, uart0_txd.
+    uart1
+        pins:   uart1_rxd, uart1_txd.
+    scb0
+        pins:   scb0_sclk, scb0_sdat.
+    i2s
+        pins:   i2s_bclk_out, i2s_din, i2s_dout0, i2s_dout1, i2s_dout2,
+                i2s_lrclk_out, i2s_mclk.
+
+Example:
+
+	pinctrl: pinctrl@02005800 {
+		#gpio-range-cells = <3>;
+		compatible = "img,tz1090-pinctrl";
+		reg = <0x02005800 0xe4>;
+	};
+
+Example board file extract:
+
+	&pinctrl {
+		uart0_default: uart0 {
+			uart0_cfg {
+				tz1090,pins =	"uart0_rxd",
+						"uart0_txd";
+				tz1090,function = "perip";
+			};
+		};
+		tft_default: tft {
+			tft_cfg {
+				tz1090,pins =	"tft";
+				tz1090,function = "tft";
+			};
+		};
+	};
+
+	uart@02004b00 {
+		pinctrl-names = "default";
+		pinctrl-0 = <&uart0_default>;
+	};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index e01976f..acdaa08 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -212,6 +212,12 @@ config PINCTRL_TEGRA114
 	bool
 	select PINCTRL_TEGRA
 
+config PINCTRL_TZ1090
+	bool "Toumaz Xenif TZ1090 pin control driver"
+	depends on SOC_TZ1090
+	select PINMUX
+	select GENERIC_PINCONF
+
 config PINCTRL_U300
 	bool "U300 pin controller driver"
 	depends on ARCH_U300
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 9031afd..37ff29e 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_PINCTRL_TEGRA)	+= pinctrl-tegra.o
 obj-$(CONFIG_PINCTRL_TEGRA20)	+= pinctrl-tegra20.o
 obj-$(CONFIG_PINCTRL_TEGRA30)	+= pinctrl-tegra30.o
 obj-$(CONFIG_PINCTRL_TEGRA114)	+= pinctrl-tegra114.o
+obj-$(CONFIG_PINCTRL_TZ1090)	+= pinctrl-tz1090.o
 obj-$(CONFIG_PINCTRL_U300)	+= pinctrl-u300.o
 obj-$(CONFIG_PINCTRL_COH901)	+= pinctrl-coh901.o
 obj-$(CONFIG_PINCTRL_SAMSUNG)	+= pinctrl-samsung.o
diff --git a/drivers/pinctrl/pinctrl-tz1090.c b/drivers/pinctrl/pinctrl-tz1090.c
new file mode 100644
index 0000000..02ff3a2
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tz1090.c
@@ -0,0 +1,2077 @@
+/*
+ * Pinctrl driver for the Toumaz Xenif TZ1090 SoC
+ *
+ * Copyright (c) 2013, Imagination Technologies Ltd.
+ *
+ * Derived from Tegra code:
+ * Copyright (c) 2011-2012, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Derived from code:
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 NVIDIA Corporation
+ * Copyright (C) 2009-2011 ST-Ericsson AB
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/*
+ * The registers may be shared with other threads/cores, so we need to use the
+ * metag global lock2 for atomicity.
+ */
+#include <asm/global_lock.h>
+
+#include "core.h"
+#include "pinconf.h"
+
+/* Register offsets from bank base address */
+#define REG_PINCTRL_SELECT	0x10
+#define REG_PINCTRL_SCHMITT	0x90
+#define REG_PINCTRL_PU_PD	0xa0
+#define REG_PINCTRL_SR		0xc0
+#define REG_PINCTRL_DR		0xd0
+#define REG_PINCTRL_IF_CTL	0xe0
+
+/* REG_PINCTRL_PU_PD field values */
+#define REG_PU_PD_TRISTATE	0
+#define REG_PU_PD_UP		1
+#define REG_PU_PD_DOWN		2
+#define REG_PU_PD_REPEATER	3
+
+/* REG_PINCTRL_DR field values */
+#define REG_DR_2mA		0
+#define REG_DR_4mA		1
+#define REG_DR_8mA		2
+#define REG_DR_12mA		3
+
+/**
+ * struct tz1090_function - TZ1090 pinctrl mux function
+ * @name:	The name of the function, exported to pinctrl core.
+ * @groups:	An array of pin groups that may select this function.
+ * @ngroups:	The number of entries in @groups.
+ */
+struct tz1090_function {
+	const char		*name;
+	const char * const	*groups;
+	unsigned int		ngroups;
+};
+
+/**
+ * struct tz1090_muxdesc - TZ1090 individual mux description
+ * @funcs:	Function for each mux value.
+ * @reg:	Mux register offset. 0 if unsupported.
+ * @bit:	Mux register bit. 0 if unsupported.
+ * @width:	Mux field width. 0 if unsupported.
+ *
+ * A representation of a group of signals (possibly just one signal) in the
+ * TZ1090 which can be muxed to a set of functions or sub muxes.
+ */
+struct tz1090_muxdesc {
+	int	funcs[5];
+	u16	reg;
+	u8	bit;
+	u8	width;
+};
+
+/**
+ * struct tz1090_pingroup - TZ1090 pin group
+ * @name:	Name of pin group.
+ * @pins:	Array of pin numbers in this pin group.
+ * @npins:	Number of pins in this pin group.
+ * @mux:	Top level mux.
+ * @drv:	Drive control supported, 0 if unsupported.
+ *		This means Schmitt, Slew, and Drive strength.
+ * @slw_bit:	Slew register bit. 0 if unsupported.
+ *		The same bit is used for Schmitt, and Drive (*2).
+ * @func:	Currently muxed function.
+ * @func_count:	Number of pins using current mux function.
+ *
+ * A representation of a group of pins (possibly just one pin) in the TZ1090
+ * pin controller. Each group allows some parameter or parameters to be
+ * configured. The most common is mux function selection.
+ */
+struct tz1090_pingroup {
+	const char		*name;
+	const unsigned int	*pins;
+	unsigned int		npins;
+	struct tz1090_muxdesc	mux;
+
+	bool			drv;
+	u8			slw_bit;
+
+	int			func;
+	unsigned int		func_count;
+};
+
+/*
+ * Most pins affected by the pinmux can also be GPIOs. Define these first.
+ * These must match how the GPIO driver names/numbers its pins.
+ */
+
+enum tz1090_pin {
+	/* GPIO pins */
+	TZ1090_PIN_SDIO_CLK,
+	TZ1090_PIN_SDIO_CMD,
+	TZ1090_PIN_SDIO_D0,
+	TZ1090_PIN_SDIO_D1,
+	TZ1090_PIN_SDIO_D2,
+	TZ1090_PIN_SDIO_D3,
+	TZ1090_PIN_SDH_CD,
+	TZ1090_PIN_SDH_WP,
+	TZ1090_PIN_SPI0_MCLK,
+	TZ1090_PIN_SPI0_CS0,
+	TZ1090_PIN_SPI0_CS1,
+	TZ1090_PIN_SPI0_CS2,
+	TZ1090_PIN_SPI0_DOUT,
+	TZ1090_PIN_SPI0_DIN,
+	TZ1090_PIN_SPI1_MCLK,
+	TZ1090_PIN_SPI1_CS0,
+	TZ1090_PIN_SPI1_CS1,
+	TZ1090_PIN_SPI1_CS2,
+	TZ1090_PIN_SPI1_DOUT,
+	TZ1090_PIN_SPI1_DIN,
+	TZ1090_PIN_UART0_RXD,
+	TZ1090_PIN_UART0_TXD,
+	TZ1090_PIN_UART0_CTS,
+	TZ1090_PIN_UART0_RTS,
+	TZ1090_PIN_UART1_RXD,
+	TZ1090_PIN_UART1_TXD,
+	TZ1090_PIN_SCB0_SDAT,
+	TZ1090_PIN_SCB0_SCLK,
+	TZ1090_PIN_SCB1_SDAT,
+	TZ1090_PIN_SCB1_SCLK,
+	TZ1090_PIN_SCB2_SDAT,
+	TZ1090_PIN_SCB2_SCLK,
+	TZ1090_PIN_I2S_MCLK,
+	TZ1090_PIN_I2S_BCLK_OUT,
+	TZ1090_PIN_I2S_LRCLK_OUT,
+	TZ1090_PIN_I2S_DOUT0,
+	TZ1090_PIN_I2S_DOUT1,
+	TZ1090_PIN_I2S_DOUT2,
+	TZ1090_PIN_I2S_DIN,
+	TZ1090_PIN_PDM_A,
+	TZ1090_PIN_PDM_B,
+	TZ1090_PIN_PDM_C,
+	TZ1090_PIN_PDM_D,
+	TZ1090_PIN_TFT_RED0,
+	TZ1090_PIN_TFT_RED1,
+	TZ1090_PIN_TFT_RED2,
+	TZ1090_PIN_TFT_RED3,
+	TZ1090_PIN_TFT_RED4,
+	TZ1090_PIN_TFT_RED5,
+	TZ1090_PIN_TFT_RED6,
+	TZ1090_PIN_TFT_RED7,
+	TZ1090_PIN_TFT_GREEN0,
+	TZ1090_PIN_TFT_GREEN1,
+	TZ1090_PIN_TFT_GREEN2,
+	TZ1090_PIN_TFT_GREEN3,
+	TZ1090_PIN_TFT_GREEN4,
+	TZ1090_PIN_TFT_GREEN5,
+	TZ1090_PIN_TFT_GREEN6,
+	TZ1090_PIN_TFT_GREEN7,
+	TZ1090_PIN_TFT_BLUE0,
+	TZ1090_PIN_TFT_BLUE1,
+	TZ1090_PIN_TFT_BLUE2,
+	TZ1090_PIN_TFT_BLUE3,
+	TZ1090_PIN_TFT_BLUE4,
+	TZ1090_PIN_TFT_BLUE5,
+	TZ1090_PIN_TFT_BLUE6,
+	TZ1090_PIN_TFT_BLUE7,
+	TZ1090_PIN_TFT_VDDEN_GD,
+	TZ1090_PIN_TFT_PANELCLK,
+	TZ1090_PIN_TFT_BLANK_LS,
+	TZ1090_PIN_TFT_VSYNC_NS,
+	TZ1090_PIN_TFT_HSYNC_NR,
+	TZ1090_PIN_TFT_VD12ACB,
+	TZ1090_PIN_TFT_PWRSAVE,
+	TZ1090_PIN_TX_ON,
+	TZ1090_PIN_RX_ON,
+	TZ1090_PIN_PLL_ON,
+	TZ1090_PIN_PA_ON,
+	TZ1090_PIN_RX_HP,
+	TZ1090_PIN_GAIN0,
+	TZ1090_PIN_GAIN1,
+	TZ1090_PIN_GAIN2,
+	TZ1090_PIN_GAIN3,
+	TZ1090_PIN_GAIN4,
+	TZ1090_PIN_GAIN5,
+	TZ1090_PIN_GAIN6,
+	TZ1090_PIN_GAIN7,
+	TZ1090_PIN_ANT_SEL0,
+	TZ1090_PIN_ANT_SEL1,
+	TZ1090_PIN_SDH_CLK_IN,
+
+	/* Non-GPIO pins */
+	TZ1090_PIN_TCK,
+	TZ1090_PIN_TRST,
+	TZ1090_PIN_TDI,
+	TZ1090_PIN_TDO,
+	TZ1090_PIN_TMS,
+	TZ1090_PIN_CLK_OUT0,
+	TZ1090_PIN_CLK_OUT1,
+
+	NUM_GPIOS = TZ1090_PIN_TCK,
+};
+
+/* Pin names */
+
+static const struct pinctrl_pin_desc tz1090_pins[] = {
+	/* GPIO pins */
+	PINCTRL_PIN(TZ1090_PIN_SDIO_CLK,	"sdio_clk"),
+	PINCTRL_PIN(TZ1090_PIN_SDIO_CMD,	"sdio_cmd"),
+	PINCTRL_PIN(TZ1090_PIN_SDIO_D0,		"sdio_d0"),
+	PINCTRL_PIN(TZ1090_PIN_SDIO_D1,		"sdio_d1"),
+	PINCTRL_PIN(TZ1090_PIN_SDIO_D2,		"sdio_d2"),
+	PINCTRL_PIN(TZ1090_PIN_SDIO_D3,		"sdio_d3"),
+	PINCTRL_PIN(TZ1090_PIN_SDH_CD,		"sdh_cd"),
+	PINCTRL_PIN(TZ1090_PIN_SDH_WP,		"sdh_wp"),
+	PINCTRL_PIN(TZ1090_PIN_SPI0_MCLK,	"spi0_mclk"),
+	PINCTRL_PIN(TZ1090_PIN_SPI0_CS0,	"spi0_cs0"),
+	PINCTRL_PIN(TZ1090_PIN_SPI0_CS1,	"spi0_cs1"),
+	PINCTRL_PIN(TZ1090_PIN_SPI0_CS2,	"spi0_cs2"),
+	PINCTRL_PIN(TZ1090_PIN_SPI0_DOUT,	"spi0_dout"),
+	PINCTRL_PIN(TZ1090_PIN_SPI0_DIN,	"spi0_din"),
+	PINCTRL_PIN(TZ1090_PIN_SPI1_MCLK,	"spi1_mclk"),
+	PINCTRL_PIN(TZ1090_PIN_SPI1_CS0,	"spi1_cs0"),
+	PINCTRL_PIN(TZ1090_PIN_SPI1_CS1,	"spi1_cs1"),
+	PINCTRL_PIN(TZ1090_PIN_SPI1_CS2,	"spi1_cs2"),
+	PINCTRL_PIN(TZ1090_PIN_SPI1_DOUT,	"spi1_dout"),
+	PINCTRL_PIN(TZ1090_PIN_SPI1_DIN,	"spi1_din"),
+	PINCTRL_PIN(TZ1090_PIN_UART0_RXD,	"uart0_rxd"),
+	PINCTRL_PIN(TZ1090_PIN_UART0_TXD,	"uart0_txd"),
+	PINCTRL_PIN(TZ1090_PIN_UART0_CTS,	"uart0_cts"),
+	PINCTRL_PIN(TZ1090_PIN_UART0_RTS,	"uart0_rts"),
+	PINCTRL_PIN(TZ1090_PIN_UART1_RXD,	"uart1_rxd"),
+	PINCTRL_PIN(TZ1090_PIN_UART1_TXD,	"uart1_txd"),
+	PINCTRL_PIN(TZ1090_PIN_SCB0_SDAT,	"scb0_sdat"),
+	PINCTRL_PIN(TZ1090_PIN_SCB0_SCLK,	"scb0_sclk"),
+	PINCTRL_PIN(TZ1090_PIN_SCB1_SDAT,	"scb1_sdat"),
+	PINCTRL_PIN(TZ1090_PIN_SCB1_SCLK,	"scb1_sclk"),
+	PINCTRL_PIN(TZ1090_PIN_SCB2_SDAT,	"scb2_sdat"),
+	PINCTRL_PIN(TZ1090_PIN_SCB2_SCLK,	"scb2_sclk"),
+	PINCTRL_PIN(TZ1090_PIN_I2S_MCLK,	"i2s_mclk"),
+	PINCTRL_PIN(TZ1090_PIN_I2S_BCLK_OUT,	"i2s_bclk_out"),
+	PINCTRL_PIN(TZ1090_PIN_I2S_LRCLK_OUT,	"i2s_lrclk_out"),
+	PINCTRL_PIN(TZ1090_PIN_I2S_DOUT0,	"i2s_dout0"),
+	PINCTRL_PIN(TZ1090_PIN_I2S_DOUT1,	"i2s_dout1"),
+	PINCTRL_PIN(TZ1090_PIN_I2S_DOUT2,	"i2s_dout2"),
+	PINCTRL_PIN(TZ1090_PIN_I2S_DIN,		"i2s_din"),
+	PINCTRL_PIN(TZ1090_PIN_PDM_A,		"pdm_a"),
+	PINCTRL_PIN(TZ1090_PIN_PDM_B,		"pdm_b"),
+	PINCTRL_PIN(TZ1090_PIN_PDM_C,		"pdm_c"),
+	PINCTRL_PIN(TZ1090_PIN_PDM_D,		"pdm_d"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_RED0,	"tft_red0"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_RED1,	"tft_red1"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_RED2,	"tft_red2"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_RED3,	"tft_red3"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_RED4,	"tft_red4"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_RED5,	"tft_red5"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_RED6,	"tft_red6"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_RED7,	"tft_red7"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_GREEN0,	"tft_green0"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_GREEN1,	"tft_green1"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_GREEN2,	"tft_green2"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_GREEN3,	"tft_green3"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_GREEN4,	"tft_green4"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_GREEN5,	"tft_green5"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_GREEN6,	"tft_green6"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_GREEN7,	"tft_green7"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_BLUE0,	"tft_blue0"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_BLUE1,	"tft_blue1"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_BLUE2,	"tft_blue2"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_BLUE3,	"tft_blue3"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_BLUE4,	"tft_blue4"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_BLUE5,	"tft_blue5"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_BLUE6,	"tft_blue6"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_BLUE7,	"tft_blue7"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_VDDEN_GD,	"tft_vdden_gd"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_PANELCLK,	"tft_panelclk"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_BLANK_LS,	"tft_blank_ls"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_VSYNC_NS,	"tft_vsync_ns"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_HSYNC_NR,	"tft_hsync_nr"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_VD12ACB,	"tft_vd12acb"),
+	PINCTRL_PIN(TZ1090_PIN_TFT_PWRSAVE,	"tft_pwrsave"),
+	PINCTRL_PIN(TZ1090_PIN_TX_ON,		"tx_on"),
+	PINCTRL_PIN(TZ1090_PIN_RX_ON,		"rx_on"),
+	PINCTRL_PIN(TZ1090_PIN_PLL_ON,		"pll_on"),
+	PINCTRL_PIN(TZ1090_PIN_PA_ON,		"pa_on"),
+	PINCTRL_PIN(TZ1090_PIN_RX_HP,		"rx_hp"),
+	PINCTRL_PIN(TZ1090_PIN_GAIN0,		"gain0"),
+	PINCTRL_PIN(TZ1090_PIN_GAIN1,		"gain1"),
+	PINCTRL_PIN(TZ1090_PIN_GAIN2,		"gain2"),
+	PINCTRL_PIN(TZ1090_PIN_GAIN3,		"gain3"),
+	PINCTRL_PIN(TZ1090_PIN_GAIN4,		"gain4"),
+	PINCTRL_PIN(TZ1090_PIN_GAIN5,		"gain5"),
+	PINCTRL_PIN(TZ1090_PIN_GAIN6,		"gain6"),
+	PINCTRL_PIN(TZ1090_PIN_GAIN7,		"gain7"),
+	PINCTRL_PIN(TZ1090_PIN_ANT_SEL0,	"ant_sel0"),
+	PINCTRL_PIN(TZ1090_PIN_ANT_SEL1,	"ant_sel1"),
+	PINCTRL_PIN(TZ1090_PIN_SDH_CLK_IN,	"sdh_clk_in"),
+
+	/* Non-GPIO pins */
+	PINCTRL_PIN(TZ1090_PIN_TCK,		"tck"),
+	PINCTRL_PIN(TZ1090_PIN_TRST,		"trst"),
+	PINCTRL_PIN(TZ1090_PIN_TDI,		"tdi"),
+	PINCTRL_PIN(TZ1090_PIN_TDO,		"tdo"),
+	PINCTRL_PIN(TZ1090_PIN_TMS,		"tms"),
+	PINCTRL_PIN(TZ1090_PIN_CLK_OUT0,	"clk_out0"),
+	PINCTRL_PIN(TZ1090_PIN_CLK_OUT1,	"clk_out1"),
+};
+
+/* Pins in each pin group */
+
+static const unsigned int spi1_cs2_pins[] = {
+	TZ1090_PIN_SPI1_CS2,
+};
+
+static const unsigned int pdm_d_pins[] = {
+	TZ1090_PIN_PDM_D,
+};
+
+static const unsigned int tft_pins[] = {
+	TZ1090_PIN_TFT_RED0,
+	TZ1090_PIN_TFT_RED1,
+	TZ1090_PIN_TFT_RED2,
+	TZ1090_PIN_TFT_RED3,
+	TZ1090_PIN_TFT_RED4,
+	TZ1090_PIN_TFT_RED5,
+	TZ1090_PIN_TFT_RED6,
+	TZ1090_PIN_TFT_RED7,
+	TZ1090_PIN_TFT_GREEN0,
+	TZ1090_PIN_TFT_GREEN1,
+	TZ1090_PIN_TFT_GREEN2,
+	TZ1090_PIN_TFT_GREEN3,
+	TZ1090_PIN_TFT_GREEN4,
+	TZ1090_PIN_TFT_GREEN5,
+	TZ1090_PIN_TFT_GREEN6,
+	TZ1090_PIN_TFT_GREEN7,
+	TZ1090_PIN_TFT_BLUE0,
+	TZ1090_PIN_TFT_BLUE1,
+	TZ1090_PIN_TFT_BLUE2,
+	TZ1090_PIN_TFT_BLUE3,
+	TZ1090_PIN_TFT_BLUE4,
+	TZ1090_PIN_TFT_BLUE5,
+	TZ1090_PIN_TFT_BLUE6,
+	TZ1090_PIN_TFT_BLUE7,
+	TZ1090_PIN_TFT_VDDEN_GD,
+	TZ1090_PIN_TFT_PANELCLK,
+	TZ1090_PIN_TFT_BLANK_LS,
+	TZ1090_PIN_TFT_VSYNC_NS,
+	TZ1090_PIN_TFT_HSYNC_NR,
+	TZ1090_PIN_TFT_VD12ACB,
+	TZ1090_PIN_TFT_PWRSAVE,
+};
+
+static const unsigned int afe_pins[] = {
+	TZ1090_PIN_TX_ON,
+	TZ1090_PIN_RX_ON,
+	TZ1090_PIN_PLL_ON,
+	TZ1090_PIN_PA_ON,
+	TZ1090_PIN_RX_HP,
+	TZ1090_PIN_ANT_SEL0,
+	TZ1090_PIN_ANT_SEL1,
+	TZ1090_PIN_GAIN0,
+	TZ1090_PIN_GAIN1,
+	TZ1090_PIN_GAIN2,
+	TZ1090_PIN_GAIN3,
+	TZ1090_PIN_GAIN4,
+	TZ1090_PIN_GAIN5,
+	TZ1090_PIN_GAIN6,
+	TZ1090_PIN_GAIN7,
+};
+
+static const unsigned int sdio_pins[] = {
+	TZ1090_PIN_SDIO_CLK,
+	TZ1090_PIN_SDIO_CMD,
+	TZ1090_PIN_SDIO_D0,
+	TZ1090_PIN_SDIO_D1,
+	TZ1090_PIN_SDIO_D2,
+	TZ1090_PIN_SDIO_D3,
+};
+
+static const unsigned int sdh_pins[] = {
+	TZ1090_PIN_SDH_CD,
+	TZ1090_PIN_SDH_WP,
+	TZ1090_PIN_SDH_CLK_IN,
+};
+
+static const unsigned int spi0_pins[] = {
+	TZ1090_PIN_SPI0_MCLK,
+	TZ1090_PIN_SPI0_CS0,
+	TZ1090_PIN_SPI0_CS1,
+	TZ1090_PIN_SPI0_CS2,
+	TZ1090_PIN_SPI0_DOUT,
+	TZ1090_PIN_SPI0_DIN,
+};
+
+static const unsigned int spi1_pins[] = {
+	TZ1090_PIN_SPI1_MCLK,
+	TZ1090_PIN_SPI1_CS0,
+	TZ1090_PIN_SPI1_CS1,
+	TZ1090_PIN_SPI1_CS2,
+	TZ1090_PIN_SPI1_DOUT,
+	TZ1090_PIN_SPI1_DIN,
+};
+
+static const unsigned int uart0_pins[] = {
+	TZ1090_PIN_UART0_RTS,
+	TZ1090_PIN_UART0_CTS,
+	TZ1090_PIN_UART0_TXD,
+	TZ1090_PIN_UART0_RXD,
+};
+
+static const unsigned int uart1_pins[] = {
+	TZ1090_PIN_UART1_TXD,
+	TZ1090_PIN_UART1_RXD,
+};
+
+static const unsigned int uart_pins[] = {
+	TZ1090_PIN_UART1_TXD,
+	TZ1090_PIN_UART1_RXD,
+	TZ1090_PIN_UART0_RTS,
+	TZ1090_PIN_UART0_CTS,
+	TZ1090_PIN_UART0_TXD,
+	TZ1090_PIN_UART0_RXD,
+};
+
+static const unsigned int scb0_pins[] = {
+	TZ1090_PIN_SCB0_SDAT,
+	TZ1090_PIN_SCB0_SCLK,
+};
+
+static const unsigned int scb1_pins[] = {
+	TZ1090_PIN_SCB1_SDAT,
+	TZ1090_PIN_SCB1_SCLK,
+};
+
+static const unsigned int scb2_pins[] = {
+	TZ1090_PIN_SCB2_SDAT,
+	TZ1090_PIN_SCB2_SCLK,
+};
+
+static const unsigned int i2s_pins[] = {
+	TZ1090_PIN_I2S_MCLK,
+	TZ1090_PIN_I2S_BCLK_OUT,
+	TZ1090_PIN_I2S_LRCLK_OUT,
+	TZ1090_PIN_I2S_DOUT0,
+	TZ1090_PIN_I2S_DOUT1,
+	TZ1090_PIN_I2S_DOUT2,
+	TZ1090_PIN_I2S_DIN,
+};
+
+static const unsigned int jtag_pins[] = {
+	TZ1090_PIN_TCK,
+	TZ1090_PIN_TRST,
+	TZ1090_PIN_TDI,
+	TZ1090_PIN_TDO,
+	TZ1090_PIN_TMS,
+};
+
+/* Pins in each drive pin group */
+
+static const unsigned int drive_sdio_pins[] = {
+	TZ1090_PIN_SDIO_CLK,
+	TZ1090_PIN_SDIO_CMD,
+	TZ1090_PIN_SDIO_D0,
+	TZ1090_PIN_SDIO_D1,
+	TZ1090_PIN_SDIO_D2,
+	TZ1090_PIN_SDIO_D3,
+	TZ1090_PIN_SDH_WP,
+	TZ1090_PIN_SDH_CD,
+	TZ1090_PIN_SDH_CLK_IN,
+};
+
+static const unsigned int drive_i2s_pins[] = {
+	TZ1090_PIN_CLK_OUT1,
+	TZ1090_PIN_I2S_DIN,
+	TZ1090_PIN_I2S_DOUT0,
+	TZ1090_PIN_I2S_DOUT1,
+	TZ1090_PIN_I2S_DOUT2,
+	TZ1090_PIN_I2S_LRCLK_OUT,
+	TZ1090_PIN_I2S_BCLK_OUT,
+	TZ1090_PIN_I2S_MCLK,
+};
+
+static const unsigned int drive_scb0_pins[] = {
+	TZ1090_PIN_SCB0_SCLK,
+	TZ1090_PIN_SCB0_SDAT,
+	TZ1090_PIN_PDM_D,
+	TZ1090_PIN_PDM_C,
+};
+
+static const unsigned int drive_pdm_pins[] = {
+	TZ1090_PIN_CLK_OUT0,
+	TZ1090_PIN_PDM_B,
+	TZ1090_PIN_PDM_A,
+};
+
+/* Pin groups each function can be muxed to */
+
+/*
+ * The magic "perip" function allows otherwise non-muxing pins to be enabled in
+ * peripheral mode.
+ */
+static const char * const perip_groups[] = {
+	/* non-muxing convenient gpio pingroups */
+	"uart",
+	"uart0",
+	"uart1",
+	"spi0",
+	"spi1",
+	"scb0",
+	"scb1",
+	"scb2",
+	"i2s",
+	/* individual pins not part of a pin mux group */
+	"spi0_mclk",
+	"spi0_cs0",
+	"spi0_cs1",
+	"spi0_cs2",
+	"spi0_dout",
+	"spi0_din",
+	"spi1_mclk",
+	"spi1_cs0",
+	"spi1_cs1",
+	"spi1_dout",
+	"spi1_din",
+	"uart0_rxd",
+	"uart0_txd",
+	"uart0_cts",
+	"uart0_rts",
+	"uart1_rxd",
+	"uart1_txd",
+	"scb0_sdat",
+	"scb0_sclk",
+	"scb1_sdat",
+	"scb1_sclk",
+	"scb2_sdat",
+	"scb2_sclk",
+	"i2s_mclk",
+	"i2s_bclk_out",
+	"i2s_lrclk_out",
+	"i2s_dout0",
+	"i2s_dout1",
+	"i2s_dout2",
+	"i2s_din",
+	"pdm_a",
+	"pdm_b",
+	"pdm_c",
+};
+
+static const char * const sdh_sdio_groups[] = {
+	"sdh",
+	"sdio",
+	/* sdh pins */
+	"sdh_cd",
+	"sdh_wp",
+	"sdh_clk_in",
+	/* sdio pins */
+	"sdio_clk",
+	"sdio_cmd",
+	"sdio_d0",
+	"sdio_d1",
+	"sdio_d2",
+	"sdio_d3",
+};
+
+static const char * const spi1_cs2_groups[] = {
+	"spi1_cs2",
+};
+
+static const char * const pdm_dac_groups[] = {
+	"pdm_d",
+};
+
+static const char * const usb_vbus_groups[] = {
+	"spi1_cs2",
+	"pdm_d",
+};
+
+static const char * const afe_groups[] = {
+	"afe",
+	/* afe pins */
+	"tx_on",
+	"rx_on",
+	"pll_on",
+	"pa_on",
+	"rx_hp",
+	"ant_sel0",
+	"ant_sel1",
+	"gain0",
+	"gain1",
+	"gain2",
+	"gain3",
+	"gain4",
+	"gain5",
+	"gain6",
+	"gain7",
+};
+
+static const char * const tft_groups[] = {
+	"tft",
+	/* tft pins */
+	"tft_red0",
+	"tft_red1",
+	"tft_red2",
+	"tft_red3",
+	"tft_red4",
+	"tft_red5",
+	"tft_red6",
+	"tft_red7",
+	"tft_green0",
+	"tft_green1",
+	"tft_green2",
+	"tft_green3",
+	"tft_green4",
+	"tft_green5",
+	"tft_green6",
+	"tft_green7",
+	"tft_blue0",
+	"tft_blue1",
+	"tft_blue2",
+	"tft_blue3",
+	"tft_blue4",
+	"tft_blue5",
+	"tft_blue6",
+	"tft_blue7",
+	"tft_vdden_gd",
+	"tft_panelclk",
+	"tft_blank_ls",
+	"tft_vsync_ns",
+	"tft_hsync_nr",
+	"tft_vd12acb",
+	"tft_pwrsave",
+};
+
+/* Mux functions that can be used by a mux */
+
+enum tz1090_mux {
+	/* internal placeholder */
+	TZ1090_MUX_NA = -1,
+	/* magic per-non-muxing-GPIO-pin peripheral mode mux */
+	TZ1090_MUX_PERIP,
+	/* SDH/SDIO mux */
+	TZ1090_MUX_SDH,
+	TZ1090_MUX_SDIO,
+	/* USB_VBUS muxes */
+	TZ1090_MUX_SPI1_CS2,
+	TZ1090_MUX_PDM_DAC,
+	TZ1090_MUX_USB_VBUS,
+	/* AFE mux */
+	TZ1090_MUX_AFE,
+	TZ1090_MUX_TS_OUT_0,
+	/* EXT_DAC mux */
+	TZ1090_MUX_DAC,
+	TZ1090_MUX_NOT_IQADC_STB,
+	TZ1090_MUX_IQDAC_STB,
+	/* TFT mux */
+	TZ1090_MUX_TFT,
+	TZ1090_MUX_EXT_DAC,
+	TZ1090_MUX_TS_OUT_1,
+	TZ1090_MUX_LCD_TRACE,
+	TZ1090_MUX_PHY_RINGOSC,
+};
+
+#define FUNCTION(mux, fname, group)			\
+	[(TZ1090_MUX_ ## mux)] = {			\
+		.name = #fname,				\
+		.groups = group##_groups,		\
+		.ngroups = ARRAY_SIZE(group##_groups),	\
+	}
+/* For intermediate functions with submuxes */
+#define NULL_FUNCTION(mux, fname)			\
+	[(TZ1090_MUX_ ## mux)] = {			\
+		.name = #fname,				\
+	}
+
+/* Must correlate with enum tz1090_mux */
+static const struct tz1090_function tz1090_functions[] = {
+	/*	 FUNCTION	function name	pingroups */
+	FUNCTION(PERIP,		perip,		perip),
+	FUNCTION(SDH,		sdh,		sdh_sdio),
+	FUNCTION(SDIO,		sdio,		sdh_sdio),
+	FUNCTION(SPI1_CS2,	spi1_cs2,	spi1_cs2),
+	FUNCTION(PDM_DAC,	pdm_dac,	pdm_dac),
+	FUNCTION(USB_VBUS,	usb_vbus,	usb_vbus),
+	FUNCTION(AFE,		afe,		afe),
+	FUNCTION(TS_OUT_0,	ts_out_0,	afe),
+	FUNCTION(DAC,		ext_dac,	tft),
+	FUNCTION(NOT_IQADC_STB,	not_iqadc_stb,	tft),
+	FUNCTION(IQDAC_STB,	iqdac_stb,	tft),
+	FUNCTION(TFT,		tft,		tft),
+	NULL_FUNCTION(EXT_DAC,	_ext_dac),
+	FUNCTION(TS_OUT_1,	ts_out_1,	tft),
+	FUNCTION(LCD_TRACE,	lcd_trace,	tft),
+	FUNCTION(PHY_RINGOSC,	phy_ringosc,	tft),
+};
+
+/* Sub muxes */
+
+/**
+ * MUX() - Initialise a mux description.
+ * @f0:		Function 0 (TZ1090_MUX_ is prepended, NA for none)
+ * @f1:		Function 1 (TZ1090_MUX_ is prepended, NA for none)
+ * @f2:		Function 2 (TZ1090_MUX_ is prepended, NA for none)
+ * @f3:		Function 3 (TZ1090_MUX_ is prepended, NA for none)
+ * @f4:		Function 4 (TZ1090_MUX_ is prepended, NA for none)
+ * @mux_r:	Mux register (REG_PINCTRL_ is prepended)
+ * @mux_b:	Bit number in register that the mux field begins
+ * @mux_w:	Width of mux field in register
+ */
+#define MUX(f0, f1, f2, f3, f4, mux_r, mux_b, mux_w)		\
+	{							\
+		.funcs = {					\
+			TZ1090_MUX_ ## f0,			\
+			TZ1090_MUX_ ## f1,			\
+			TZ1090_MUX_ ## f2,			\
+			TZ1090_MUX_ ## f3,			\
+			TZ1090_MUX_ ## f4,			\
+		},						\
+		.reg = (REG_PINCTRL_ ## mux_r),			\
+		.bit = (mux_b),					\
+		.width = (mux_w),				\
+	}
+
+/**
+ * DEFINE_SUBMUX() - Defines a submux description separate from a pin group.
+ * @mux:	Mux name (_submux is appended)
+ * @f0:		Function 0 (TZ1090_MUX_ is prepended, NA for none)
+ * @f1:		Function 1 (TZ1090_MUX_ is prepended, NA for none)
+ * @f2:		Function 2 (TZ1090_MUX_ is prepended, NA for none)
+ * @f3:		Function 3 (TZ1090_MUX_ is prepended, NA for none)
+ * @f4:		Function 4 (TZ1090_MUX_ is prepended, NA for none)
+ * @mux_r:	Mux register (REG_PINCTRL_ is prepended)
+ * @mux_b:	Bit number in register that the mux field begins
+ * @mux_w:	Width of mux field in register
+ *
+ * A sub mux is a nested mux that can be bound to a magic function number used
+ * by another mux description. For example value 4 of the top level mux might
+ * correspond to a function which has a submux pointed to in tz1090_submux[].
+ * The outer mux can then take on any function in the top level mux or the
+ * submux, and if a submux function is chosen both muxes are updated to route
+ * the signal from the submux.
+ *
+ * The submux can be defined with DEFINE_SUBMUX and pointed to from
+ * tz1090_submux[] using SUBMUX.
+ */
+#define DEFINE_SUBMUX(mux, f0, f1, f2, f3, f4, mux_r, mux_b, mux_w)	\
+	static struct tz1090_muxdesc mux ## _submux =			\
+		MUX(f0, f1, f2, f3, f4, mux_r, mux_b, mux_w)
+
+/**
+ * SUBMUX() - Link a submux to a function number.
+ * @f:		Function name (TZ1090_MUX_ is prepended)
+ * @submux:	Submux name (_submux is appended)
+ *
+ * For use in tz1090_submux[] initialisation to link an intermediate function
+ * number to a particular submux description. It indicates that when the
+ * function is chosen the signal is connected to the submux.
+ */
+#define SUBMUX(f, submux)	[(TZ1090_MUX_ ## f)] = &(submux ## _submux)
+
+/**
+ * MUX_PG() - Initialise a pin group with mux control
+ * @pg_name:	Pin group name (stringified, _pins appended to get pins array)
+ * @f0:		Function 0 (TZ1090_MUX_ is prepended, NA for none)
+ * @f1:		Function 1 (TZ1090_MUX_ is prepended, NA for none)
+ * @f2:		Function 2 (TZ1090_MUX_ is prepended, NA for none)
+ * @f3:		Function 3 (TZ1090_MUX_ is prepended, NA for none)
+ * @f4:		Function 4 (TZ1090_MUX_ is prepended, NA for none)
+ * @mux_r:	Mux register (REG_PINCTRL_ is prepended)
+ * @mux_b:	Bit number in register that the mux field begins
+ * @mux_w:	Width of mux field in register
+ */
+#define MUX_PG(pg_name, f0, f1, f2, f3, f4,			\
+	       mux_r, mux_b, mux_w)				\
+	{							\
+		.name = #pg_name,				\
+		.pins = pg_name##_pins,				\
+		.npins = ARRAY_SIZE(pg_name##_pins),		\
+		.mux = MUX(f0, f1, f2, f3, f4,			\
+			   mux_r, mux_b, mux_w),		\
+	}
+
+/**
+ * SIMPLE_PG() - Initialise a simple convenience pin group
+ * @pg_name:	Pin group name (stringified, _pins appended to get pins array)
+ *
+ * A simple pin group is simply used for binding pins together so they can be
+ * referred to by a single name instead of having to list every pin
+ * individually.
+ */
+#define SIMPLE_PG(pg_name)					\
+	{							\
+		.name = #pg_name,				\
+		.pins = pg_name##_pins,				\
+		.npins = ARRAY_SIZE(pg_name##_pins),		\
+	}
+
+/**
+ * DRV_PG() - Initialise a pin group with drive control
+ * @pg_name:	Pin group name (stringified, _pins appended to get pins array)
+ * @slw_b:	Slew register bit.
+ *		The same bit is used for Schmitt, and Drive (*2).
+ */
+#define DRV_PG(pg_name, slw_b)					\
+	{							\
+		.name = #pg_name,				\
+		.pins = pg_name##_pins,				\
+		.npins = ARRAY_SIZE(pg_name##_pins),		\
+		.drv = true,					\
+		.slw_bit = (slw_b),				\
+	}
+
+/*
+ * Define main muxing pin groups
+ */
+
+/* submuxes */
+
+/*            name     f0,  f1,            f2,        f3, f4, mux r/b/w */
+DEFINE_SUBMUX(ext_dac, DAC, NOT_IQADC_STB, IQDAC_STB, NA, NA, IF_CTL, 6, 2);
+
+/* bind submuxes to internal functions */
+static struct tz1090_muxdesc *tz1090_submux[] = {
+	SUBMUX(EXT_DAC, ext_dac),
+};
+
+/*
+ * These are the pin mux groups. Pin muxing can be enabled and disabled for each
+ * pin individually so these groups are internal. The mapping of pins to pin mux
+ * group is below (tz1090_mux_pins).
+ */
+static struct tz1090_pingroup tz1090_mux_groups[] = {
+	/* Muxing pin groups */
+	/*     pg_name,  f0,       f1,       f2,       f3,        f4,          mux r/b/w */
+	MUX_PG(sdh,      SDH,      SDIO,     NA,       NA,        NA,          IF_CTL, 20, 2),
+	MUX_PG(sdio,     SDIO,     SDH,      NA,       NA,        NA,          IF_CTL, 16, 2),
+	MUX_PG(spi1_cs2, SPI1_CS2, USB_VBUS, NA,       NA,        NA,          IF_CTL, 10, 2),
+	MUX_PG(pdm_d,    PDM_DAC,  USB_VBUS, NA,       NA,        NA,          IF_CTL,  8, 2),
+	MUX_PG(afe,      AFE,      TS_OUT_0, NA,       NA,        NA,          IF_CTL,  4, 2),
+	MUX_PG(tft,      TFT,      EXT_DAC,  TS_OUT_1, LCD_TRACE, PHY_RINGOSC, IF_CTL,  0, 3),
+};
+
+/*
+ * This is the mapping from GPIO pins to pin mux groups in tz1090_mux_groups[].
+ * Pins which aren't muxable to multiple peripherals are set to
+ * TZ1090_MUX_GROUP_MAX to enable the "perip" function to enable/disable
+ * peripheral control of the pin.
+ *
+ * This array is initialised in tz1090_init_mux_pins().
+ */
+static u8 tz1090_mux_pins[NUM_GPIOS];
+
+/* TZ1090_MUX_GROUP_MAX is used in tz1090_mux_pins[] for non-muxing pins */
+#define TZ1090_MUX_GROUP_MAX ARRAY_SIZE(tz1090_mux_groups)
+
+/**
+ * tz1090_init_mux_pins() - Initialise GPIO pin to mux group mapping.
+ *
+ * Initialises the tz1090_mux_pins[] array to be the inverse of the pin lists in
+ * each pin mux group in tz1090_mux_groups[].
+ *
+ * It is assumed that no pin mux groups overlap (share pins).
+ */
+static void __init tz1090_init_mux_pins(void)
+{
+	unsigned int g, p;
+	const struct tz1090_pingroup *grp;
+	const unsigned int *pin;
+
+	for (p = 0; p < NUM_GPIOS; ++p)
+		tz1090_mux_pins[p] = TZ1090_MUX_GROUP_MAX;
+
+	grp = tz1090_mux_groups;
+	for (g = 0, grp = tz1090_mux_groups;
+	     g < ARRAY_SIZE(tz1090_mux_groups); ++g, ++grp)
+		for (pin = grp->pins, p = 0; p < grp->npins; ++p, ++pin)
+			tz1090_mux_pins[*pin] = g;
+}
+
+/*
+ * These are the externally visible pin groups. Some of them allow group control
+ * of drive configuration. Some are just simple convenience pingroups. All the
+ * internal pin mux groups in tz1090_mux_groups[] are mirrored here with the
+ * same pins.
+ * Pseudo pin groups follow in the group numbers after this array for each GPIO
+ * pin. Any group used for muxing must have all pins belonging to the same pin
+ * mux group.
+ */
+static struct tz1090_pingroup tz1090_groups[] = {
+	/* Pin groups with drive control (with no out of place pins) */
+	/*     pg_name,		slw/schmitt/drv b */
+	DRV_PG(jtag,		11 /* 11, 22 */),
+	DRV_PG(tft,		10 /* 10, 20 */),
+	DRV_PG(scb2,		9  /*  9, 18 */),
+	DRV_PG(spi0,		7  /*  7, 14 */),
+	DRV_PG(uart,		5  /*  5, 10 */),
+	DRV_PG(scb1,		4  /*  4,  8 */),
+	DRV_PG(spi1,		3  /*  3,  6 */),
+	DRV_PG(afe,		0  /*  0,  0 */),
+
+	/*
+	 * Drive specific pin groups (with odd combinations of pins which makes
+	 * the pin group naming somewhat arbitrary)
+	 */
+	/*     pg_name,		slw/schmitt/drv b */
+	DRV_PG(drive_sdio,	8  /*  8, 16 */), /* sdio_* + sdh_* */
+	DRV_PG(drive_i2s,	6  /*  6, 12 */), /* i2s_* + clk_out1 */
+	DRV_PG(drive_scb0,	2  /*  2,  4 */), /* scb0_* + pdm_{c,d} */
+	DRV_PG(drive_pdm,	1  /*  1,  2 */), /* pdm_{a,b} + clk_out0 */
+
+	/* Convenience pin groups */
+	/*        pg_name */
+	SIMPLE_PG(uart0),
+	SIMPLE_PG(uart1),
+	SIMPLE_PG(scb0),
+	SIMPLE_PG(i2s),
+	SIMPLE_PG(sdh),
+	SIMPLE_PG(sdio),
+
+	/* pseudo-pingroups for each GPIO pin follow */
+};
+
+/**
+ * struct tz1090_pmx - Private pinctrl data
+ * @dev:	Platform device
+ * @pctl:	Pin control device
+ * @regs:	Register region
+ * @lock:	Lock protecting coherency of pin_en, gpio_en, and SELECT regs
+ * @pin_en:	Pins that have been enabled (32 pins packed into each element)
+ * @gpio_en:	GPIOs that have been enabled (32 pins packed into each element)
+ */
+struct tz1090_pmx {
+	struct device		*dev;
+	struct pinctrl_dev	*pctl;
+	void __iomem		*regs;
+	spinlock_t		lock;
+	u32			pin_en[3];
+	u32			gpio_en[3];
+};
+
+static inline u32 pmx_read(struct tz1090_pmx *pmx, u32 reg)
+{
+	return ioread32(pmx->regs + reg);
+}
+
+static inline void pmx_write(struct tz1090_pmx *pmx, u32 val, u32 reg)
+{
+	iowrite32(val, pmx->regs + reg);
+}
+
+/*
+ * Pin control operations
+ */
+
+/* each GPIO pin has it's own pseudo pingroup containing only itself */
+
+static int tz1090_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(tz1090_groups) + NUM_GPIOS;
+}
+
+static const char *tz1090_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
+						 unsigned int group)
+{
+	if (group < ARRAY_SIZE(tz1090_groups)) {
+		/* normal pingroup */
+		return tz1090_groups[group].name;
+	} else {
+		/* individual gpio pin pseudo-pingroup */
+		unsigned int pin = group - ARRAY_SIZE(tz1090_groups);
+		return tz1090_pins[pin].name;
+	}
+}
+
+static int tz1090_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+					 unsigned int group,
+					 const unsigned int **pins,
+					 unsigned int *num_pins)
+{
+	if (group < ARRAY_SIZE(tz1090_groups)) {
+		/* normal pingroup */
+		*pins = tz1090_groups[group].pins;
+		*num_pins = tz1090_groups[group].npins;
+	} else {
+		/* individual gpio pin pseudo-pingroup */
+		unsigned int pin = group - ARRAY_SIZE(tz1090_groups);
+		*pins = &tz1090_pins[pin].number;
+		*num_pins = 1;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void tz1090_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev,
+					struct seq_file *s,
+					unsigned int offset)
+{
+	seq_printf(s, " %s", dev_name(pctldev->dev));
+}
+#endif
+
+static int reserve_map(struct device *dev, struct pinctrl_map **map,
+		       unsigned int *reserved_maps, unsigned int *num_maps,
+		       unsigned int reserve)
+{
+	unsigned int old_num = *reserved_maps;
+	unsigned int new_num = *num_maps + reserve;
+	struct pinctrl_map *new_map;
+
+	if (old_num >= new_num)
+		return 0;
+
+	new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL);
+	if (!new_map) {
+		dev_err(dev, "krealloc(map) failed\n");
+		return -ENOMEM;
+	}
+
+	memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map));
+
+	*map = new_map;
+	*reserved_maps = new_num;
+
+	return 0;
+}
+
+static int add_map_mux(struct pinctrl_map **map, unsigned int *reserved_maps,
+		       unsigned int *num_maps, const char *group,
+		       const char *function)
+{
+	if (WARN_ON(*num_maps == *reserved_maps))
+		return -ENOSPC;
+
+	(*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP;
+	(*map)[*num_maps].data.mux.group = group;
+	(*map)[*num_maps].data.mux.function = function;
+	(*num_maps)++;
+
+	return 0;
+}
+
+static int add_map_configs(struct device *dev,
+			   struct pinctrl_map **map,
+			   unsigned int *reserved_maps, unsigned int *num_maps,
+			   const char *group, unsigned long *configs,
+			   unsigned int num_configs)
+{
+	unsigned long *dup_configs;
+
+	if (WARN_ON(*num_maps == *reserved_maps))
+		return -ENOSPC;
+
+	dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs),
+			      GFP_KERNEL);
+	if (!dup_configs) {
+		dev_err(dev, "kmemdup(configs) failed\n");
+		return -ENOMEM;
+	}
+
+	(*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
+	(*map)[*num_maps].data.configs.group_or_pin = group;
+	(*map)[*num_maps].data.configs.configs = dup_configs;
+	(*map)[*num_maps].data.configs.num_configs = num_configs;
+	(*num_maps)++;
+
+	return 0;
+}
+
+static void tz1090_pinctrl_dt_free_map(struct pinctrl_dev *pctldev,
+				       struct pinctrl_map *map,
+				       unsigned int num_maps)
+{
+	int i;
+
+	for (i = 0; i < num_maps; i++)
+		if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
+			kfree(map[i].data.configs.configs);
+
+	kfree(map);
+}
+
+static int tz1090_pinctrl_dt_subnode_to_map(struct device *dev,
+					    struct device_node *np,
+					    struct pinctrl_map **map,
+					    unsigned int *reserved_maps,
+					    unsigned int *num_maps)
+{
+	int ret;
+	const char *function;
+	unsigned long *configs = NULL;
+	unsigned int num_configs = 0;
+	unsigned int reserve;
+	struct property *prop;
+	const char *group;
+
+	ret = of_property_read_string(np, "tz1090,function", &function);
+	if (ret < 0) {
+		/* EINVAL=missing, which is fine since it's optional */
+		if (ret != -EINVAL)
+			dev_err(dev, "could not parse property function\n");
+		function = NULL;
+	}
+
+	ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs);
+	if (ret)
+		return ret;
+
+	reserve = 0;
+	if (function != NULL)
+		reserve++;
+	if (num_configs)
+		reserve++;
+	ret = of_property_count_strings(np, "tz1090,pins");
+	if (ret < 0) {
+		dev_err(dev, "could not parse property pins\n");
+		goto exit;
+	}
+	reserve *= ret;
+
+	ret = reserve_map(dev, map, reserved_maps, num_maps, reserve);
+	if (ret < 0)
+		goto exit;
+
+	of_property_for_each_string(np, "tz1090,pins", prop, group) {
+		if (function) {
+			ret = add_map_mux(map, reserved_maps, num_maps,
+					  group, function);
+			if (ret < 0)
+				goto exit;
+		}
+
+		if (num_configs) {
+			ret = add_map_configs(dev, map, reserved_maps,
+					      num_maps, group, configs,
+					      num_configs);
+			if (ret < 0)
+				goto exit;
+		}
+	}
+
+	ret = 0;
+
+exit:
+	kfree(configs);
+	return ret;
+}
+
+static int tz1090_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
+					 struct device_node *np_config,
+					 struct pinctrl_map **map,
+					 unsigned int *num_maps)
+{
+	unsigned int reserved_maps;
+	struct device_node *np;
+	int ret;
+
+	reserved_maps = 0;
+	*map = NULL;
+	*num_maps = 0;
+
+	for_each_child_of_node(np_config, np) {
+		ret = tz1090_pinctrl_dt_subnode_to_map(pctldev->dev, np, map,
+						       &reserved_maps,
+						       num_maps);
+		if (ret < 0) {
+			tz1090_pinctrl_dt_free_map(pctldev, *map, *num_maps);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static struct pinctrl_ops tz1090_pinctrl_ops = {
+	.get_groups_count	= tz1090_pinctrl_get_groups_count,
+	.get_group_name		= tz1090_pinctrl_get_group_name,
+	.get_group_pins		= tz1090_pinctrl_get_group_pins,
+#ifdef CONFIG_DEBUG_FS
+	.pin_dbg_show		= tz1090_pinctrl_pin_dbg_show,
+#endif
+	.dt_node_to_map		= tz1090_pinctrl_dt_node_to_map,
+	.dt_free_map		= tz1090_pinctrl_dt_free_map,
+};
+
+/*
+ * Pin mux operations
+ */
+
+static int tz1090_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(tz1090_functions);
+}
+
+static const char *tz1090_pinctrl_get_func_name(struct pinctrl_dev *pctldev,
+						unsigned int function)
+{
+	return tz1090_functions[function].name;
+}
+
+static int tz1090_pinctrl_get_func_groups(struct pinctrl_dev *pctldev,
+					  unsigned int function,
+					  const char * const **groups,
+					  unsigned int * const num_groups)
+{
+	/* pingroup functions */
+	*groups = tz1090_functions[function].groups;
+	*num_groups = tz1090_functions[function].ngroups;
+	return 0;
+}
+
+/**
+ * tz1090_pinctrl_select() - update bit in SELECT register
+ * @pmx:		Pinmux data
+ * @pin:		Pin number (must be within GPIO range)
+ */
+static void tz1090_pinctrl_select(struct tz1090_pmx *pmx,
+				  unsigned int pin)
+{
+	u32 reg, reg_shift, select, val;
+	unsigned int pmx_index, pmx_shift;
+	unsigned long flags;
+
+	/* uses base 32 instead of base 30 */
+	pmx_index = pin >> 5;
+	pmx_shift = pin & 0x1f;
+
+	/* select = !perip || gpio */
+	select = ((~pmx->pin_en[pmx_index] |
+		   pmx->gpio_en[pmx_index]) >> pmx_shift) & 1;
+
+	/* find register and bit offset (base 30) */
+	reg = REG_PINCTRL_SELECT + 4*(pin / 30);
+	reg_shift = pin % 30;
+
+	/* modify gpio select bit */
+	__global_lock2(flags);
+	val = pmx_read(pmx, reg);
+	val &= ~BIT(reg_shift);
+	val |= select << reg_shift;
+	pmx_write(pmx, val, reg);
+	__global_unlock2(flags);
+}
+
+/**
+ * tz1090_pinctrl_gpio_select() - enable/disable GPIO usage for a pin
+ * @pmx:		Pinmux data
+ * @pin:		Pin number
+ * @gpio_select:	true to enable pin as GPIO,
+ *			false to leave control to whatever function is enabled
+ *
+ * Records that GPIO usage is enabled/disabled so that enabling a function
+ * doesn't override the SELECT register bit.
+ */
+static void tz1090_pinctrl_gpio_select(struct tz1090_pmx *pmx,
+				       unsigned int pin,
+				       bool gpio_select)
+{
+	unsigned int index, shift;
+	u32 gpio_en;
+
+	if (pin >= NUM_GPIOS)
+		return;
+
+	/* uses base 32 instead of base 30 */
+	index = pin >> 5;
+	shift = pin & 0x1f;
+
+	spin_lock(&pmx->lock);
+
+	/* keep a record whether gpio is selected */
+	gpio_en = pmx->gpio_en[index];
+	gpio_en &= ~BIT(shift);
+	if (gpio_select)
+		gpio_en |= BIT(shift);
+	pmx->gpio_en[index] = gpio_en;
+
+	/* update the select bit */
+	tz1090_pinctrl_select(pmx, pin);
+
+	spin_unlock(&pmx->lock);
+}
+
+/**
+ * tz1090_pinctrl_perip_select() - enable/disable peripheral interface for a pin
+ * @pmx:		Pinmux data
+ * @pin:		Pin number
+ * @perip_select:	true to enable peripheral interface when not GPIO,
+ *			false to leave pin in GPIO mode
+ *
+ * Records that peripheral usage is enabled/disabled so that SELECT register can
+ * be set appropriately when GPIO is disabled.
+ */
+static void tz1090_pinctrl_perip_select(struct tz1090_pmx *pmx,
+					unsigned int pin,
+					bool perip_select)
+{
+	unsigned int index, shift;
+	u32 pin_en;
+
+	if (pin >= NUM_GPIOS)
+		return;
+
+	/* uses base 32 instead of base 30 */
+	index = pin >> 5;
+	shift = pin & 0x1f;
+
+	spin_lock(&pmx->lock);
+
+	/* keep a record whether peripheral is selected */
+	pin_en = pmx->pin_en[index];
+	pin_en &= ~BIT(shift);
+	if (perip_select)
+		pin_en |= BIT(shift);
+	pmx->pin_en[index] = pin_en;
+
+	/* update the select bit */
+	tz1090_pinctrl_select(pmx, pin);
+
+	spin_unlock(&pmx->lock);
+}
+
+/**
+ * tz1090_pinctrl_enable_mux() - Switch a pin mux group to a function.
+ * @pmx:		Pinmux data
+ * @desc:		Pinmux description
+ * @function:		Function to switch to
+ *
+ * Enable a particular function on a pin mux group. Since pin mux descriptions
+ * are nested this function is recursive.
+ */
+static int tz1090_pinctrl_enable_mux(struct tz1090_pmx *pmx,
+				     const struct tz1090_muxdesc *desc,
+				     unsigned int function)
+{
+	const int *fit;
+	unsigned long flags;
+	int mux;
+	unsigned int func, ret;
+	u32 reg, mask;
+
+	/* find the mux value for this function, searching recursively */
+	for (mux = 0, fit = desc->funcs;
+	     mux < ARRAY_SIZE(desc->funcs); ++mux, ++fit) {
+		func = *fit;
+		if (func == function)
+			goto found_mux;
+
+		/* maybe it's a sub-mux */
+		if (func < ARRAY_SIZE(tz1090_submux) && tz1090_submux[func]) {
+			ret = tz1090_pinctrl_enable_mux(pmx,
+							tz1090_submux[func],
+							function);
+			if (!ret)
+				goto found_mux;
+		}
+	}
+
+	return -EINVAL;
+found_mux:
+
+	/* Set up the mux */
+	if (desc->width) {
+		mask = (BIT(desc->width) - 1) << desc->bit;
+		__global_lock2(flags);
+		reg = pmx_read(pmx, desc->reg);
+		reg &= ~mask;
+		reg |= (mux << desc->bit) & mask;
+		pmx_write(pmx, reg, desc->reg);
+		__global_unlock2(flags);
+	}
+
+	return 0;
+}
+
+/**
+ * tz1090_pinctrl_enable() - Enable a function on a pin group.
+ * @pctldev:		Pin control data
+ * @function:		Function index to enable
+ * @group:		Group index to enable
+ *
+ * Enable a particular function on a group of pins. The per GPIO pin pseudo pin
+ * groups can be used (in which case the pin will be enabled in peripheral mode
+ * and if it belongs to a pin mux group the mux will be switched if it isn't
+ * already in use. Some convenience pin groups can also be used in which case
+ * the effect is the same as enabling the function on each individual pin in the
+ * group.
+ */
+static int tz1090_pinctrl_enable(struct pinctrl_dev *pctldev,
+				 unsigned int function, unsigned int group)
+{
+	struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	struct tz1090_pingroup *grp;
+	int ret;
+	unsigned int pin_num, mux_group, i, npins;
+	const unsigned int *pins;
+
+	/* group of pins? */
+	if (group < ARRAY_SIZE(tz1090_groups)) {
+		grp = &tz1090_groups[group];
+		npins = grp->npins;
+		pins = grp->pins;
+		/*
+		 * All pins in the group must belong to the same mux group,
+		 * which allows us to just use the mux group of the first pin.
+		 * By explicitly listing permitted pingroups for each function
+		 * the pinmux core should ensure this is always the case.
+		 */
+	} else {
+		pin_num = group - ARRAY_SIZE(tz1090_groups);
+		npins = 1;
+		pins = &pin_num;
+	}
+	mux_group = tz1090_mux_pins[*pins];
+
+	/* no mux group, but can still be individually muxed to peripheral */
+	if (mux_group >= TZ1090_MUX_GROUP_MAX) {
+		if (function == TZ1090_MUX_PERIP)
+			goto mux_pins;
+		return -EINVAL;
+	}
+
+	/* mux group already set to a different function? */
+	grp = &tz1090_mux_groups[mux_group];
+	if (grp->func_count && grp->func != function) {
+		dev_err(pctldev->dev,
+			"%s: can't mux pin(s) to '%s', group already muxed to '%s'\n",
+			__func__, tz1090_functions[function].name,
+			tz1090_functions[grp->func].name);
+		return -EBUSY;
+	}
+
+	dev_dbg(pctldev->dev, "%s: muxing %u pin(s) in '%s' to '%s'\n",
+		__func__, npins, grp->name, tz1090_functions[function].name);
+
+	/* if first pin in mux group to be enabled, enable the group mux */
+	if (!grp->func_count) {
+		grp->func = function;
+		ret = tz1090_pinctrl_enable_mux(pmx, &grp->mux, function);
+		if (ret)
+			return ret;
+	}
+	/* add pins to ref count and mux individually to peripheral */
+	grp->func_count += npins;
+mux_pins:
+	for (i = 0; i < npins; ++i)
+		tz1090_pinctrl_perip_select(pmx, pins[i], true);
+
+	return 0;
+}
+
+/**
+ * tz1090_pinctrl_disable() - Disable a function on a pin group.
+ * @pctldev:		Pin control data
+ * @function:		Function index to disable
+ * @group:		Group index to disable
+ *
+ * Disable a particular function on a group of pins. The per GPIO pin pseudo pin
+ * groups can be used (in which case the pin will be taken out of peripheral
+ * mode. Some convenience pin groups can also be used in which case the effect
+ * is the same as enabling the function on each individual pin in the group.
+ */
+static void tz1090_pinctrl_disable(struct pinctrl_dev *pctldev,
+				   unsigned int function, unsigned int group)
+{
+	struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	struct tz1090_pingroup *grp;
+	unsigned int pin_num, mux_group, i, npins;
+	const unsigned int *pins;
+
+	/* group of pins? */
+	if (group < ARRAY_SIZE(tz1090_groups)) {
+		grp = &tz1090_groups[group];
+		npins = grp->npins;
+		pins = grp->pins;
+		/*
+		 * All pins in the group must belong to the same mux group,
+		 * which allows us to just use the mux group of the first pin.
+		 * By explicitly listing permitted pingroups for each function
+		 * the pinmux core should ensure this is always the case.
+		 */
+	} else {
+		pin_num = group - ARRAY_SIZE(tz1090_groups);
+		npins = 1;
+		pins = &pin_num;
+	}
+	mux_group = tz1090_mux_pins[*pins];
+
+	/* no mux group, but can still be individually muxed to peripheral */
+	if (mux_group >= TZ1090_MUX_GROUP_MAX) {
+		if (function == TZ1090_MUX_PERIP)
+			goto unmux_pins;
+		return;
+	}
+
+	/* mux group already set to a different function? */
+	grp = &tz1090_mux_groups[mux_group];
+	dev_dbg(pctldev->dev, "%s: unmuxing %u pin(s) in '%s' from '%s'\n",
+		__func__, npins, grp->name, tz1090_functions[function].name);
+
+	/* subtract pins from ref count and unmux individually */
+	WARN_ON(grp->func_count < npins);
+	grp->func_count -= npins;
+unmux_pins:
+	for (i = 0; i < npins; ++i)
+		tz1090_pinctrl_perip_select(pmx, pins[i], false);
+}
+
+/**
+ * tz1090_pinctrl_gpio_request_enable() - Put pin in GPIO mode.
+ * @pctldev:		Pin control data
+ * @range:		GPIO range
+ * @pin:		Pin number
+ *
+ * Puts a particular pin into GPIO mode, disabling peripheral control until it's
+ * disabled again.
+ */
+static int tz1090_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev,
+					      struct pinctrl_gpio_range *range,
+					      unsigned int pin)
+{
+	struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	tz1090_pinctrl_gpio_select(pmx, pin, true);
+	return 0;
+}
+
+/**
+ * tz1090_pinctrl_gpio_disable_free() - Take pin out of GPIO mode.
+ * @pctldev:		Pin control data
+ * @range:		GPIO range
+ * @pin:		Pin number
+ *
+ * Take a particular pin out of GPIO mode. If the pin is enabled for a
+ * peripheral it will return to peripheral mode.
+ */
+static void tz1090_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev,
+					     struct pinctrl_gpio_range *range,
+					     unsigned int pin)
+{
+	struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	tz1090_pinctrl_gpio_select(pmx, pin, false);
+}
+
+static struct pinmux_ops tz1090_pinmux_ops = {
+	.get_functions_count	= tz1090_pinctrl_get_funcs_count,
+	.get_function_name	= tz1090_pinctrl_get_func_name,
+	.get_function_groups	= tz1090_pinctrl_get_func_groups,
+	.enable			= tz1090_pinctrl_enable,
+	.disable		= tz1090_pinctrl_disable,
+	.gpio_request_enable	= tz1090_pinctrl_gpio_request_enable,
+	.gpio_disable_free	= tz1090_pinctrl_gpio_disable_free,
+};
+
+/*
+ * Pin config operations
+ */
+
+struct tz1090_pinconf_pullup {
+	unsigned char index;
+	unsigned char shift;
+};
+
+/* The mapping of pin to pull up/down register index and shift */
+static struct tz1090_pinconf_pullup tz1090_pinconf_pullup[] = {
+	{5, 22}, /*  0 - TZ1090_PIN_SDIO_CLK */
+	{0, 14}, /*  1 - TZ1090_PIN_SDIO_CMD */
+	{0,  6}, /*  2 - TZ1090_PIN_SDIO_D0 */
+	{0,  8}, /*  3 - TZ1090_PIN_SDIO_D1 */
+	{0, 10}, /*  4 - TZ1090_PIN_SDIO_D2 */
+	{0, 12}, /*  5 - TZ1090_PIN_SDIO_D3 */
+	{0,  2}, /*  6 - TZ1090_PIN_SDH_CD */
+	{0,  4}, /*  7 - TZ1090_PIN_SDH_WP */
+	{0, 16}, /*  8 - TZ1090_PIN_SPI0_MCLK */
+	{0, 18}, /*  9 - TZ1090_PIN_SPI0_CS0 */
+	{0, 20}, /* 10 - TZ1090_PIN_SPI0_CS1 */
+	{0, 22}, /* 11 - TZ1090_PIN_SPI0_CS2 */
+	{0, 24}, /* 12 - TZ1090_PIN_SPI0_DOUT */
+	{0, 26}, /* 13 - TZ1090_PIN_SPI0_DIN */
+	{0, 28}, /* 14 - TZ1090_PIN_SPI1_MCLK */
+	{0, 30}, /* 15 - TZ1090_PIN_SPI1_CS0 */
+	{1,  0}, /* 16 - TZ1090_PIN_SPI1_CS1 */
+	{1,  2}, /* 17 - TZ1090_PIN_SPI1_CS2 */
+	{1,  4}, /* 18 - TZ1090_PIN_SPI1_DOUT */
+	{1,  6}, /* 19 - TZ1090_PIN_SPI1_DIN */
+	{1,  8}, /* 20 - TZ1090_PIN_UART0_RXD */
+	{1, 10}, /* 21 - TZ1090_PIN_UART0_TXD */
+	{1, 12}, /* 22 - TZ1090_PIN_UART0_CTS */
+	{1, 14}, /* 23 - TZ1090_PIN_UART0_RTS */
+	{1, 16}, /* 24 - TZ1090_PIN_UART1_RXD */
+	{1, 18}, /* 25 - TZ1090_PIN_UART1_TXD */
+	{1, 20}, /* 26 - TZ1090_PIN_SCB0_SDAT */
+	{1, 22}, /* 27 - TZ1090_PIN_SCB0_SCLK */
+	{1, 24}, /* 28 - TZ1090_PIN_SCB1_SDAT */
+	{1, 26}, /* 29 - TZ1090_PIN_SCB1_SCLK */
+
+	{1, 28}, /* 30 - TZ1090_PIN_SCB2_SDAT */
+	{1, 30}, /* 31 - TZ1090_PIN_SCB2_SCLK */
+	{2,  0}, /* 32 - TZ1090_PIN_I2S_MCLK */
+	{2,  2}, /* 33 - TZ1090_PIN_I2S_BCLK_OUT */
+	{2,  4}, /* 34 - TZ1090_PIN_I2S_LRCLK_OUT */
+	{2,  6}, /* 35 - TZ1090_PIN_I2S_DOUT0 */
+	{2,  8}, /* 36 - TZ1090_PIN_I2S_DOUT1 */
+	{2, 10}, /* 37 - TZ1090_PIN_I2S_DOUT2 */
+	{2, 12}, /* 38 - TZ1090_PIN_I2S_DIN */
+	{4, 12}, /* 39 - TZ1090_PIN_PDM_A */
+	{4, 14}, /* 40 - TZ1090_PIN_PDM_B */
+	{4, 18}, /* 41 - TZ1090_PIN_PDM_C */
+	{4, 20}, /* 42 - TZ1090_PIN_PDM_D */
+	{2, 14}, /* 43 - TZ1090_PIN_TFT_RED0 */
+	{2, 16}, /* 44 - TZ1090_PIN_TFT_RED1 */
+	{2, 18}, /* 45 - TZ1090_PIN_TFT_RED2 */
+	{2, 20}, /* 46 - TZ1090_PIN_TFT_RED3 */
+	{2, 22}, /* 47 - TZ1090_PIN_TFT_RED4 */
+	{2, 24}, /* 48 - TZ1090_PIN_TFT_RED5 */
+	{2, 26}, /* 49 - TZ1090_PIN_TFT_RED6 */
+	{2, 28}, /* 50 - TZ1090_PIN_TFT_RED7 */
+	{2, 30}, /* 51 - TZ1090_PIN_TFT_GREEN0 */
+	{3,  0}, /* 52 - TZ1090_PIN_TFT_GREEN1 */
+	{3,  2}, /* 53 - TZ1090_PIN_TFT_GREEN2 */
+	{3,  4}, /* 54 - TZ1090_PIN_TFT_GREEN3 */
+	{3,  6}, /* 55 - TZ1090_PIN_TFT_GREEN4 */
+	{3,  8}, /* 56 - TZ1090_PIN_TFT_GREEN5 */
+	{3, 10}, /* 57 - TZ1090_PIN_TFT_GREEN6 */
+	{3, 12}, /* 58 - TZ1090_PIN_TFT_GREEN7 */
+	{3, 14}, /* 59 - TZ1090_PIN_TFT_BLUE0 */
+
+	{3, 16}, /* 60 - TZ1090_PIN_TFT_BLUE1 */
+	{3, 18}, /* 61 - TZ1090_PIN_TFT_BLUE2 */
+	{3, 20}, /* 62 - TZ1090_PIN_TFT_BLUE3 */
+	{3, 22}, /* 63 - TZ1090_PIN_TFT_BLUE4 */
+	{3, 24}, /* 64 - TZ1090_PIN_TFT_BLUE5 */
+	{3, 26}, /* 65 - TZ1090_PIN_TFT_BLUE6 */
+	{3, 28}, /* 66 - TZ1090_PIN_TFT_BLUE7 */
+	{3, 30}, /* 67 - TZ1090_PIN_TFT_VDDEN_GD */
+	{4,  0}, /* 68 - TZ1090_PIN_TFT_PANELCLK */
+	{4,  2}, /* 69 - TZ1090_PIN_TFT_BLANK_LS */
+	{4,  4}, /* 70 - TZ1090_PIN_TFT_VSYNC_NS */
+	{4,  6}, /* 71 - TZ1090_PIN_TFT_HSYNC_NR */
+	{4,  8}, /* 72 - TZ1090_PIN_TFT_VD12ACB */
+	{4, 10}, /* 73 - TZ1090_PIN_TFT_PWRSAVE */
+	{4, 24}, /* 74 - TZ1090_PIN_TX_ON */
+	{4, 26}, /* 75 - TZ1090_PIN_RX_ON */
+	{4, 28}, /* 76 - TZ1090_PIN_PLL_ON */
+	{4, 30}, /* 77 - TZ1090_PIN_PA_ON */
+	{5,  0}, /* 78 - TZ1090_PIN_RX_HP */
+	{5,  6}, /* 79 - TZ1090_PIN_GAIN0 */
+	{5,  8}, /* 80 - TZ1090_PIN_GAIN1 */
+	{5, 10}, /* 81 - TZ1090_PIN_GAIN2 */
+	{5, 12}, /* 82 - TZ1090_PIN_GAIN3 */
+	{5, 14}, /* 83 - TZ1090_PIN_GAIN4 */
+	{5, 16}, /* 84 - TZ1090_PIN_GAIN5 */
+	{5, 18}, /* 85 - TZ1090_PIN_GAIN6 */
+	{5, 20}, /* 86 - TZ1090_PIN_GAIN7 */
+	{5,  2}, /* 87 - TZ1090_PIN_ANT_SEL0 */
+	{5,  4}, /* 88 - TZ1090_PIN_ANT_SEL1 */
+	{0,  0}, /* 89 - TZ1090_PIN_SDH_CLK_IN */
+
+	{5, 24}, /* 90 - TZ1090_PIN_TCK */
+	{5, 26}, /* 91 - TZ1090_PIN_TRST */
+	{5, 28}, /* 92 - TZ1090_PIN_TDI */
+	{5, 30}, /* 93 - TZ1090_PIN_TDO */
+	{6,  0}, /* 94 - TZ1090_PIN_TMS */
+	{4, 16}, /* 95 - TZ1090_PIN_CLK_OUT0 */
+	{4, 22}, /* 96 - TZ1090_PIN_CLK_OUT1 */
+};
+
+static int tz1090_pinconf_reg(struct pinctrl_dev *pctldev,
+			      unsigned int pin,
+			      enum pin_config_param param,
+			      bool report_err,
+			      u32 *reg, u32 *width, u32 *mask, u32 *shift,
+			      u32 *val)
+{
+	struct tz1090_pinconf_pullup *pu;
+
+	/* All supported pins have controllable input bias */
+	switch (param) {
+	case PIN_CONFIG_BIAS_DISABLE:
+	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+		*val = REG_PU_PD_TRISTATE;
+		break;
+	case PIN_CONFIG_BIAS_PULL_UP:
+		*val = REG_PU_PD_UP;
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		*val = REG_PU_PD_DOWN;
+		break;
+	case PIN_CONFIG_BIAS_BUS_HOLD:
+		*val = REG_PU_PD_REPEATER;
+		break;
+	default:
+		return -ENOTSUPP;
+	};
+
+	/* Only input bias parameters supported */
+	pu = &tz1090_pinconf_pullup[pin];
+	*reg = REG_PINCTRL_PU_PD + 4*pu->index;
+	*shift = pu->shift;
+	*width = 2;
+
+	/* Calculate field information */
+	*mask = (BIT(*width) - 1) << *shift;
+
+	return 0;
+}
+
+static int tz1090_pinconf_get(struct pinctrl_dev *pctldev,
+			      unsigned int pin, unsigned long *config)
+{
+	struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param param = pinconf_to_config_param(*config);
+	int ret;
+	u32 reg, width, mask, shift, val, tmp, arg;
+
+	/* Get register information */
+	ret = tz1090_pinconf_reg(pctldev, pin, param, true,
+				 &reg, &width, &mask, &shift, &val);
+	if (ret < 0)
+		return ret;
+
+	/* Extract field from register */
+	tmp = pmx_read(pmx, reg);
+	arg = ((tmp & mask) >> shift) == val;
+
+	/* Config not active */
+	if (!arg)
+		return -EINVAL;
+
+	/* And pack config */
+	*config = pinconf_to_config_packed(param, arg);
+
+	return 0;
+}
+
+static int tz1090_pinconf_set(struct pinctrl_dev *pctldev,
+			      unsigned int pin, unsigned long config)
+{
+	struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param param = pinconf_to_config_param(config);
+	unsigned int arg = pinconf_to_config_argument(config);
+	int ret;
+	u32 reg, width, mask, shift, val, tmp;
+	unsigned long flags;
+
+	dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n",
+		__func__, tz1090_pins[pin].name, config);
+
+	/* Get register information */
+	ret = tz1090_pinconf_reg(pctldev, pin, param, true,
+				 &reg, &width, &mask, &shift, &val);
+	if (ret < 0)
+		return ret;
+
+	/* Unpack argument and range check it */
+	if (arg > 1) {
+		dev_dbg(pctldev->dev, "%s: arg %u out of range\n",
+			__func__, arg);
+		return -EINVAL;
+	}
+
+	/* Write register field */
+	__global_lock2(flags);
+	tmp = pmx_read(pmx, reg);
+	tmp &= ~mask;
+	if (arg)
+		tmp |= val << shift;
+	pmx_write(pmx, tmp, reg);
+	__global_unlock2(flags);
+
+	return 0;
+}
+
+static const int tz1090_boolean_map[] = {
+	[0]		= -EINVAL,
+	[1]		= 1,
+};
+
+static const int tz1090_dr_map[] = {
+	[REG_DR_2mA]	= 2,
+	[REG_DR_4mA]	= 4,
+	[REG_DR_8mA]	= 8,
+	[REG_DR_12mA]	= 12,
+};
+
+static int tz1090_pinconf_group_reg(struct pinctrl_dev *pctldev,
+				    const struct tz1090_pingroup *g,
+				    enum pin_config_param param,
+				    bool report_err,
+				    u32 *reg, u32 *width, u32 *mask, u32 *shift,
+				    const int **map)
+{
+	/* Drive configuration applies in groups, but not to all groups. */
+	if (!g->drv) {
+		if (report_err)
+			dev_dbg(pctldev->dev,
+				"%s: group %s has no drive control\n",
+				__func__, g->name);
+		return -ENOTSUPP;
+	}
+
+	/* Find information about drive parameter's register */
+	switch (param) {
+	case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+		*reg = REG_PINCTRL_SCHMITT;
+		*width = 1;
+		*map = tz1090_boolean_map;
+		break;
+	case PIN_CONFIG_SLEW_RATE:
+		*reg = REG_PINCTRL_SR;
+		*width = 1;
+		*map = tz1090_boolean_map;
+		break;
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		*reg = REG_PINCTRL_DR;
+		*width = 2;
+		*map = tz1090_dr_map;
+		break;
+	default:
+		return -ENOTSUPP;
+	};
+
+	/* Calculate field information */
+	*shift = g->slw_bit * *width;
+	*mask = (BIT(*width) - 1) << *shift;
+
+	return 0;
+}
+
+static int tz1090_pinconf_group_get(struct pinctrl_dev *pctldev,
+				    unsigned int group,
+				    unsigned long *config)
+{
+	struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	const struct tz1090_pingroup *g;
+	enum pin_config_param param = pinconf_to_config_param(*config);
+	int ret, arg;
+	unsigned int pin;
+	u32 reg, width, mask, shift, val;
+	const int *map;
+
+	if (group >= ARRAY_SIZE(tz1090_groups)) {
+		pin = group - ARRAY_SIZE(tz1090_groups);
+		return tz1090_pinconf_get(pctldev, pin, config);
+	}
+
+	g = &tz1090_groups[group];
+	if (g->npins == 1) {
+		pin = g->pins[0];
+		ret = tz1090_pinconf_get(pctldev, pin, config);
+		if (ret != -ENOTSUPP)
+			return ret;
+	}
+
+	/* Get register information */
+	ret = tz1090_pinconf_group_reg(pctldev, g, param, true,
+				       &reg, &width, &mask, &shift, &map);
+	if (ret < 0)
+		return ret;
+
+	/* Extract field from register */
+	val = pmx_read(pmx, reg);
+	arg = map[(val & mask) >> shift];
+	if (arg < 0)
+		return arg;
+
+	/* And pack config */
+	*config = pinconf_to_config_packed(param, arg);
+
+	return 0;
+}
+
+static int tz1090_pinconf_group_set(struct pinctrl_dev *pctldev,
+				    unsigned int group, unsigned long config)
+{
+	struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	const struct tz1090_pingroup *g;
+	enum pin_config_param param = pinconf_to_config_param(config);
+	unsigned int arg, pin, i;
+	const unsigned int *pit;
+	int ret;
+	u32 reg, width, mask, shift, val;
+	unsigned long flags;
+	const int *map;
+
+	if (group >= ARRAY_SIZE(tz1090_groups)) {
+		pin = group - ARRAY_SIZE(tz1090_groups);
+		return tz1090_pinconf_set(pctldev, pin, config);
+	}
+
+	g = &tz1090_groups[group];
+	if (g->npins == 1) {
+		pin = g->pins[0];
+		ret = tz1090_pinconf_set(pctldev, pin, config);
+		if (ret != -ENOTSUPP)
+			return ret;
+	}
+
+	dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n",
+		__func__, g->name, config);
+
+	/* Get register information */
+	ret = tz1090_pinconf_group_reg(pctldev, g, param, true,
+				       &reg, &width, &mask, &shift, &map);
+	if (ret < 0) {
+		/*
+		 * Maybe we're trying to set a per-pin configuration of a group,
+		 * so do the pins one by one. This is mainly as a convenience.
+		 */
+		for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) {
+			ret = tz1090_pinconf_set(pctldev, *pit, config);
+			if (ret)
+				return ret;
+		}
+		return 0;
+	}
+
+	/* Unpack argument and map it to register value */
+	arg = pinconf_to_config_argument(config);
+	for (i = 0; i < BIT(width); ++i) {
+		if (map[i] == arg || (map[i] == -EINVAL && !arg)) {
+			/* Write register field */
+			__global_lock2(flags);
+			val = pmx_read(pmx, reg);
+			val &= ~mask;
+			val |= i << shift;
+			pmx_write(pmx, val, reg);
+			__global_unlock2(flags);
+			return 0;
+		}
+	}
+
+	dev_dbg(pctldev->dev, "%s: arg %u not supported\n",
+		__func__, arg);
+	return -EINVAL;
+}
+
+static struct pinconf_ops tz1090_pinconf_ops = {
+	.is_generic			= true,
+	.pin_config_get			= tz1090_pinconf_get,
+	.pin_config_set			= tz1090_pinconf_set,
+	.pin_config_group_get		= tz1090_pinconf_group_get,
+	.pin_config_group_set		= tz1090_pinconf_group_set,
+	.pin_config_config_dbg_show	= pinconf_generic_dump_config,
+};
+
+/*
+ * Pin control driver setup
+ */
+
+static struct pinctrl_desc tz1090_pinctrl_desc = {
+	.pctlops	= &tz1090_pinctrl_ops,
+	.pmxops		= &tz1090_pinmux_ops,
+	.confops	= &tz1090_pinconf_ops,
+	.owner		= THIS_MODULE,
+};
+
+static int tz1090_pinctrl_probe(struct platform_device *pdev)
+{
+	struct tz1090_pmx *pmx;
+	struct resource *res;
+
+	pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL);
+	if (!pmx) {
+		dev_err(&pdev->dev, "Can't alloc tz1090_pmx\n");
+		return -ENOMEM;
+	}
+	pmx->dev = &pdev->dev;
+	spin_lock_init(&pmx->lock);
+
+	tz1090_pinctrl_desc.name = dev_name(&pdev->dev);
+	tz1090_pinctrl_desc.pins = tz1090_pins;
+	tz1090_pinctrl_desc.npins = ARRAY_SIZE(tz1090_pins);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Missing MEM resource\n");
+		return -ENODEV;
+	}
+
+	if (!devm_request_mem_region(&pdev->dev, res->start,
+				     resource_size(res),
+				     dev_name(&pdev->dev))) {
+		dev_err(&pdev->dev,
+			"Couldn't request MEM resource\n");
+		return -ENODEV;
+	}
+
+	pmx->regs = devm_ioremap(&pdev->dev, res->start,
+				 resource_size(res));
+	if (!pmx->regs) {
+		dev_err(&pdev->dev, "Couldn't ioremap regs\n");
+		return -ENODEV;
+	}
+
+	pmx->pctl = pinctrl_register(&tz1090_pinctrl_desc, &pdev->dev, pmx);
+	if (!pmx->pctl) {
+		dev_err(&pdev->dev, "Couldn't register pinctrl driver\n");
+		return -ENODEV;
+	}
+
+	platform_set_drvdata(pdev, pmx);
+
+	dev_info(&pdev->dev, "TZ1090 pinctrl driver initialised\n");
+
+	return 0;
+}
+
+static int tz1090_pinctrl_remove(struct platform_device *pdev)
+{
+	struct tz1090_pmx *pmx = platform_get_drvdata(pdev);
+
+	pinctrl_unregister(pmx->pctl);
+
+	return 0;
+}
+
+static struct of_device_id tz1090_pinctrl_of_match[] = {
+	{ .compatible = "img,tz1090-pinctrl", },
+	{ },
+};
+
+static struct platform_driver tz1090_pinctrl_driver = {
+	.driver = {
+		.name		= "tz1090-pinctrl",
+		.owner		= THIS_MODULE,
+		.of_match_table	= tz1090_pinctrl_of_match,
+	},
+	.probe	= tz1090_pinctrl_probe,
+	.remove	= tz1090_pinctrl_remove,
+};
+
+static int __init tz1090_pinctrl_init(void)
+{
+	tz1090_init_mux_pins();
+	return platform_driver_register(&tz1090_pinctrl_driver);
+}
+arch_initcall(tz1090_pinctrl_init);
+
+static void __exit tz1090_pinctrl_exit(void)
+{
+	platform_driver_unregister(&tz1090_pinctrl_driver);
+}
+module_exit(tz1090_pinctrl_exit);
+
+MODULE_AUTHOR("Imagination Technologies Ltd.");
+MODULE_DESCRIPTION("Toumaz Xenif TZ1090 pinctrl driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, tz1090_pinctrl_of_match);
-- 
1.8.1.2



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

* [PATCH v3 2/4] gpio-tz1090: add TZ1090 gpio driver
  2013-06-20  9:26 [PATCH v3 0/4] Add TZ1090 pinctrl/gpio drivers James Hogan
  2013-06-20  9:26 ` [PATCH v3 1/4] pinctrl-tz1090: add TZ1090 pinctrl driver James Hogan
@ 2013-06-20  9:26 ` James Hogan
  2013-06-24 13:34   ` Grant Likely
  2013-06-20  9:26 ` [PATCH v3 3/4] pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver James Hogan
  2013-06-20  9:26 ` [PATCH v3 4/4] gpio-tz1090-pdc: add TZ1090 PDC gpio driver James Hogan
  3 siblings, 1 reply; 12+ messages in thread
From: James Hogan @ 2013-06-20  9:26 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-kernel, James Hogan, Grant Likely, Rob Herring,
	Rob Landley, linux-doc, devicetree-discuss

Add a GPIO driver for the main GPIOs found in the TZ1090 (Comet) SoC.
This doesn't include low-power GPIOs as they're controlled separately
via the Powerdown Controller (PDC) registers.

The driver is instantiated by device tree and supports interrupts for
all GPIOs.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: linux-doc@vger.kernel.org
Cc: devicetree-discuss@lists.ozlabs.org
---
Changes in v3:
 - separated from irq-imgpdc and removed arch/metag changes to allow
   these patches to go upstream separately via the pinctrl[/gpio] trees
   (particularly the pinctrl drivers depend on the new pinconf DT
   bindings).
 - some s/unsigned/unsigned int/.
 - some s/unsigned int/bool/ and use of BIT().
 - gpio-tz1090*: refer to <dt-bindings/gpio/gpio.h> and
   <dt-bindings/interrupt-controller/irq.h> flags in bindings.
 - gpio-tz1090*: move initcall from postcore to subsys.
 - gpio-tz1090: add REG_ prefix to some constants for consistency.
 - gpio-tz1090: add comment to explain tz1090_gpio_irq_next_edge
   cunningness.

Changes in v2:
 - gpio-tz1090: remove references to Linux flags in dt bindings
 - gpio-tz1090: make use of BIT() from linux/bitops.h
 - gpio-tz1090: make register accessors inline to match pinctrl
 - gpio-tz1090: update gpio-ranges to use 3 cells after recent ABI
   breakage

 .../devicetree/bindings/gpio/gpio-tz1090.txt       |  87 +++
 drivers/gpio/Kconfig                               |   7 +
 drivers/gpio/Makefile                              |   1 +
 drivers/gpio/gpio-tz1090.c                         | 633 +++++++++++++++++++++
 4 files changed, 728 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
 create mode 100644 drivers/gpio/gpio-tz1090.c

diff --git a/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt b/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
new file mode 100644
index 0000000..e017d4b
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
@@ -0,0 +1,87 @@
+ImgTec TZ1090 GPIO Controller
+
+Required properties:
+- compatible: Compatible property value should be "img,tz1090-gpio>".
+
+- reg: Physical base address of the controller and length of memory mapped
+  region.
+
+- #address-cells: Should be 1 (for bank subnodes)
+
+- #size-cells: Should be 0 (for bank subnodes)
+
+- Each bank of GPIOs should have a subnode to represent it.
+
+  Bank subnode required properties:
+  - reg: Index of bank in the range 0 to 2.
+
+  - gpio-controller: Specifies that the node is a gpio controller.
+
+  - #gpio-cells: Should be 2. The syntax of the gpio specifier used by client
+    nodes should have the following values.
+       <[phandle of the gpio controller node]
+        [gpio number within the gpio bank]
+        [gpio flags]>
+
+    Values for gpio specifier:
+    - GPIO number: a value in the range 0 to 29.
+    - GPIO flags: bit field of flags, as defined in <dt-bindings/gpio/gpio.h>.
+      Only the following flags are supported:
+        GPIO_ACTIVE_HIGH
+        GPIO_ACTIVE_LOW
+
+  Bank subnode optional properties:
+  - gpio-ranges: Mapping to pin controller pins
+
+  - interrupts: Interrupt for the entire bank
+
+  - interrupt-controller: Specifies that the node is an interrupt controller
+
+  - #interrupt-cells: Should be 2. The syntax of the interrupt specifier used by
+    client nodes should have the following values.
+       <[phandle of the interurupt controller]
+        [gpio number within the gpio bank]
+        [irq flags]>
+
+    Values for irq specifier:
+    - GPIO number: a value in the range 0 to 29
+    - IRQ flags: value to describe edge and level triggering, as defined in
+      <dt-bindings/interrupt-controller/irq.h>. Only the following flags are
+      supported:
+        IRQ_TYPE_EDGE_RISING
+        IRQ_TYPE_EDGE_FALLING
+        IRQ_TYPE_EDGE_BOTH
+        IRQ_TYPE_LEVEL_HIGH
+        IRQ_TYPE_LEVEL_LOW
+
+
+
+Example:
+
+	gpios: gpio-controller@02005800 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "img,tz1090-gpio";
+		reg = <0x02005800 0x90>;
+
+		/* bank 0 with an interrupt */
+		gpios0: bank@0 {
+			#gpio-cells = <2>;
+			#interrupt-cells = <2>;
+			reg = <0>;
+			interrupts = <13 IRQ_TYPE_LEVEL_HIGH>;
+			gpio-controller;
+			gpio-ranges = <&pinctrl 0 30>;
+			interrupt-controller;
+		};
+
+		/* bank 2 without interrupt */
+		gpios2: bank@2 {
+			#gpio-cells = <2>;
+			reg = <2>;
+			gpio-controller;
+			gpio-ranges = <&pinctrl 60 30>;
+		};
+	};
+
+
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 573c449..ee27c2e 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -232,6 +232,13 @@ config GPIO_TS5500
 	  blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
 	  LCD port.
 
+config GPIO_TZ1090
+	bool "Toumaz Xenif TZ1090 GPIO support"
+	depends on SOC_TZ1090
+	default y
+	help
+	  Say yes here to support Toumaz Xenif TZ1090 GPIOs.
+
 config GPIO_XILINX
 	bool "Xilinx GPIO support"
 	depends on PPC_OF || MICROBLAZE
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 0cb2d65..37bdc1e 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_GPIO_TPS65912)	+= gpio-tps65912.o
 obj-$(CONFIG_GPIO_TS5500)	+= gpio-ts5500.o
 obj-$(CONFIG_GPIO_TWL4030)	+= gpio-twl4030.o
 obj-$(CONFIG_GPIO_TWL6040)	+= gpio-twl6040.o
+obj-$(CONFIG_GPIO_TZ1090)	+= gpio-tz1090.o
 obj-$(CONFIG_GPIO_UCB1400)	+= gpio-ucb1400.o
 obj-$(CONFIG_GPIO_VIPERBOARD)	+= gpio-viperboard.o
 obj-$(CONFIG_GPIO_VR41XX)	+= gpio-vr41xx.o
diff --git a/drivers/gpio/gpio-tz1090.c b/drivers/gpio/gpio-tz1090.c
new file mode 100644
index 0000000..099a9ef
--- /dev/null
+++ b/drivers/gpio/gpio-tz1090.c
@@ -0,0 +1,633 @@
+/*
+ * Toumaz Xenif TZ1090 GPIO handling.
+ *
+ * Copyright (C) 2008-2013 Imagination Technologies Ltd.
+ *
+ *  Based on ARM PXA code and others.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <asm/global_lock.h>
+
+/* Register offsets from bank base address */
+#define REG_GPIO_DIR		0x00
+#define REG_GPIO_IRQ_PLRT	0x20
+#define REG_GPIO_IRQ_TYPE	0x30
+#define REG_GPIO_IRQ_EN		0x40
+#define REG_GPIO_IRQ_STS	0x50
+#define REG_GPIO_BIT_EN		0x60
+#define REG_GPIO_DIN		0x70
+#define REG_GPIO_DOUT		0x80
+
+/* REG_GPIO_IRQ_PLRT */
+#define REG_GPIO_IRQ_PLRT_LOW	0
+#define REG_GPIO_IRQ_PLRT_HIGH	1
+
+/* REG_GPIO_IRQ_TYPE */
+#define REG_GPIO_IRQ_TYPE_LEVEL	0
+#define REG_GPIO_IRQ_TYPE_EDGE	1
+
+/**
+ * struct tz1090_gpio_bank - GPIO bank private data
+ * @chip:	Generic GPIO chip for GPIO bank
+ * @domain:	IRQ domain for GPIO bank (may be NULL)
+ * @reg:	Base of registers, offset for this GPIO bank
+ * @irq:	IRQ number for GPIO bank
+ * @label:	Debug GPIO bank label, used for storage of chip->label
+ *
+ * This is the main private data for a GPIO bank. It encapsulates a gpio_chip,
+ * and the callbacks for the gpio_chip can access the private data with the
+ * to_bank() macro below.
+ */
+struct tz1090_gpio_bank {
+	struct gpio_chip chip;
+	struct irq_domain *domain;
+	void __iomem *reg;
+	int irq;
+	char label[16];
+};
+#define to_bank(c)	container_of(c, struct tz1090_gpio_bank, chip)
+
+/**
+ * struct tz1090_gpio - Overall GPIO device private data
+ * @dev:	Device (from platform device)
+ * @reg:	Base of GPIO registers
+ *
+ * Represents the overall GPIO device. This structure is actually only
+ * temporary, and used during init.
+ */
+struct tz1090_gpio {
+	struct device *dev;
+	void __iomem *reg;
+};
+
+/**
+ * struct tz1090_gpio_bank_info - Temporary registration info for GPIO bank
+ * @priv:	Overall GPIO device private data
+ * @node:	Device tree node specific to this GPIO bank
+ * @index:	Index of bank in range 0-2
+ */
+struct tz1090_gpio_bank_info {
+	struct tz1090_gpio *priv;
+	struct device_node *node;
+	unsigned int index;
+};
+
+/* Convenience register accessors */
+static inline void tz1090_gpio_write(struct tz1090_gpio_bank *bank,
+			      unsigned int reg_offs, u32 data)
+{
+	iowrite32(data, bank->reg + reg_offs);
+}
+
+static inline u32 tz1090_gpio_read(struct tz1090_gpio_bank *bank,
+			    unsigned int reg_offs)
+{
+	return ioread32(bank->reg + reg_offs);
+}
+
+/* caller must hold LOCK2 */
+static inline void _tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
+					  unsigned int reg_offs,
+					  unsigned int offset)
+{
+	u32 value;
+
+	value = tz1090_gpio_read(bank, reg_offs);
+	value &= ~BIT(offset);
+	tz1090_gpio_write(bank, reg_offs, value);
+}
+
+static void tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
+				  unsigned int reg_offs,
+				  unsigned int offset)
+{
+	int lstat;
+
+	__global_lock2(lstat);
+	_tz1090_gpio_clear_bit(bank, reg_offs, offset);
+	__global_unlock2(lstat);
+}
+
+/* caller must hold LOCK2 */
+static inline void _tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
+					unsigned int reg_offs,
+					unsigned int offset)
+{
+	u32 value;
+
+	value = tz1090_gpio_read(bank, reg_offs);
+	value |= BIT(offset);
+	tz1090_gpio_write(bank, reg_offs, value);
+}
+
+static void tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
+				unsigned int reg_offs,
+				unsigned int offset)
+{
+	int lstat;
+
+	__global_lock2(lstat);
+	_tz1090_gpio_set_bit(bank, reg_offs, offset);
+	__global_unlock2(lstat);
+}
+
+/* caller must hold LOCK2 */
+static inline void _tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
+					unsigned int reg_offs,
+					unsigned int offset,
+					bool val)
+{
+	u32 value;
+
+	value = tz1090_gpio_read(bank, reg_offs);
+	value &= ~BIT(offset);
+	if (val)
+		value |= BIT(offset);
+	tz1090_gpio_write(bank, reg_offs, value);
+}
+
+static void tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
+				unsigned int reg_offs,
+				unsigned int offset,
+				bool val)
+{
+	int lstat;
+
+	__global_lock2(lstat);
+	_tz1090_gpio_mod_bit(bank, reg_offs, offset, val);
+	__global_unlock2(lstat);
+}
+
+static inline int tz1090_gpio_read_bit(struct tz1090_gpio_bank *bank,
+				       unsigned int reg_offs,
+				       unsigned int offset)
+{
+	return tz1090_gpio_read(bank, reg_offs) & BIT(offset);
+}
+
+/* GPIO chip callbacks */
+
+static int tz1090_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned int offset)
+{
+	struct tz1090_gpio_bank *bank = to_bank(chip);
+	tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
+
+	return 0;
+}
+
+static int tz1090_gpio_direction_output(struct gpio_chip *chip,
+					unsigned int offset, int output_value)
+{
+	struct tz1090_gpio_bank *bank = to_bank(chip);
+	int lstat;
+
+	__global_lock2(lstat);
+	_tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
+	_tz1090_gpio_clear_bit(bank, REG_GPIO_DIR, offset);
+	__global_unlock2(lstat);
+
+	return 0;
+}
+
+/*
+ * Return GPIO level
+ */
+static int tz1090_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+	struct tz1090_gpio_bank *bank = to_bank(chip);
+
+	return tz1090_gpio_read_bit(bank, REG_GPIO_DIN, offset);
+}
+
+/*
+ * Set output GPIO level
+ */
+static void tz1090_gpio_set(struct gpio_chip *chip, unsigned int offset,
+			    int output_value)
+{
+	struct tz1090_gpio_bank *bank = to_bank(chip);
+
+	tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
+}
+
+static int tz1090_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+	struct tz1090_gpio_bank *bank = to_bank(chip);
+	int ret;
+
+	ret = pinctrl_request_gpio(chip->base + offset);
+	if (ret)
+		return ret;
+
+	tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
+	tz1090_gpio_set_bit(bank, REG_GPIO_BIT_EN, offset);
+
+	return 0;
+}
+
+static void tz1090_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+	struct tz1090_gpio_bank *bank = to_bank(chip);
+
+	pinctrl_free_gpio(chip->base + offset);
+
+	tz1090_gpio_clear_bit(bank, REG_GPIO_BIT_EN, offset);
+}
+
+static int tz1090_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+	struct tz1090_gpio_bank *bank = to_bank(chip);
+
+	if (!bank->domain)
+		return -EINVAL;
+
+	return irq_create_mapping(bank->domain, offset);
+}
+
+/* IRQ chip handlers */
+
+/* Get TZ1090 GPIO chip from irq data provided to generic IRQ callbacks */
+static inline struct tz1090_gpio_bank *irqd_to_gpio_bank(struct irq_data *data)
+{
+	return (struct tz1090_gpio_bank *)data->domain->host_data;
+}
+
+static void tz1090_gpio_irq_clear(struct tz1090_gpio_bank *bank,
+				  unsigned int offset)
+{
+	tz1090_gpio_clear_bit(bank, REG_GPIO_IRQ_STS, offset);
+}
+
+static void tz1090_gpio_irq_enable(struct tz1090_gpio_bank *bank,
+				   unsigned int offset, bool enable)
+{
+	tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_EN, offset, enable);
+}
+
+static void tz1090_gpio_irq_polarity(struct tz1090_gpio_bank *bank,
+				     unsigned int offset, unsigned int polarity)
+{
+	tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_PLRT, offset, polarity);
+}
+
+static int tz1090_gpio_valid_handler(struct irq_desc *desc)
+{
+	return desc->handle_irq == handle_level_irq ||
+		desc->handle_irq == handle_edge_irq;
+}
+
+static void tz1090_gpio_irq_type(struct tz1090_gpio_bank *bank,
+				 unsigned int offset, unsigned int type)
+{
+	tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_TYPE, offset, type);
+}
+
+/* set polarity to trigger on next edge, whether rising or falling */
+static void tz1090_gpio_irq_next_edge(struct tz1090_gpio_bank *bank,
+				      unsigned int offset)
+{
+	unsigned int value_p, value_i;
+	int lstat;
+
+	/*
+	 * Set the GPIO's interrupt polarity to the opposite of the current
+	 * input value so that the next edge triggers an interrupt.
+	 */
+	__global_lock2(lstat);
+	value_i = ~tz1090_gpio_read(bank, REG_GPIO_DIN);
+	value_p = tz1090_gpio_read(bank, REG_GPIO_IRQ_PLRT);
+	value_p &= ~BIT(offset);
+	value_p |= value_i & BIT(offset);
+	tz1090_gpio_write(bank, REG_GPIO_IRQ_PLRT, value_p);
+	__global_unlock2(lstat);
+}
+
+static void gpio_ack_irq(struct irq_data *data)
+{
+	struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+
+	tz1090_gpio_irq_clear(bank, data->hwirq);
+}
+
+static void gpio_mask_irq(struct irq_data *data)
+{
+	struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+
+	tz1090_gpio_irq_enable(bank, data->hwirq, false);
+}
+
+static void gpio_unmask_irq(struct irq_data *data)
+{
+	struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+
+	tz1090_gpio_irq_enable(bank, data->hwirq, true);
+}
+
+static unsigned int gpio_startup_irq(struct irq_data *data)
+{
+	struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+	irq_hw_number_t hw = data->hwirq;
+	struct irq_desc *desc = irq_to_desc(data->irq);
+
+	/*
+	 * This warning indicates that the type of the irq hasn't been set
+	 * before enabling the irq. This would normally be done by passing some
+	 * trigger flags to request_irq().
+	 */
+	WARN(!tz1090_gpio_valid_handler(desc),
+		"irq type not set before enabling gpio irq %d", data->irq);
+
+	tz1090_gpio_irq_clear(bank, hw);
+	tz1090_gpio_irq_enable(bank, hw, true);
+	return 0;
+}
+
+static int gpio_set_irq_type(struct irq_data *data, unsigned int flow_type)
+{
+	struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+	unsigned int type;
+	unsigned int polarity;
+
+	switch (flow_type) {
+	case IRQ_TYPE_EDGE_BOTH:
+		type = REG_GPIO_IRQ_TYPE_EDGE;
+		polarity = REG_GPIO_IRQ_PLRT_LOW;
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		type = REG_GPIO_IRQ_TYPE_EDGE;
+		polarity = REG_GPIO_IRQ_PLRT_HIGH;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		type = REG_GPIO_IRQ_TYPE_EDGE;
+		polarity = REG_GPIO_IRQ_PLRT_LOW;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		type = REG_GPIO_IRQ_TYPE_LEVEL;
+		polarity = REG_GPIO_IRQ_PLRT_HIGH;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		type = REG_GPIO_IRQ_TYPE_LEVEL;
+		polarity = REG_GPIO_IRQ_PLRT_LOW;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	tz1090_gpio_irq_type(bank, data->hwirq, type);
+	if (type == REG_GPIO_IRQ_TYPE_LEVEL)
+		__irq_set_handler_locked(data->irq, handle_level_irq);
+	else
+		__irq_set_handler_locked(data->irq, handle_edge_irq);
+
+	if (flow_type == IRQ_TYPE_EDGE_BOTH)
+		tz1090_gpio_irq_next_edge(bank, data->hwirq);
+	else
+		tz1090_gpio_irq_polarity(bank, data->hwirq, polarity);
+
+	return 0;
+}
+
+#ifdef CONFIG_SUSPEND
+static int gpio_set_irq_wake(struct irq_data *data, unsigned int on)
+{
+	struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+
+#ifdef CONFIG_PM_DEBUG
+	pr_info("irq_wake irq%d state:%d\n", data->irq, on);
+#endif
+
+	/* wake on gpio block interrupt */
+	return irq_set_irq_wake(bank->irq, on);
+}
+#else
+#define gpio_set_irq_wake NULL
+#endif
+
+/* gpio virtual interrupt functions */
+static struct irq_chip gpio_irq_chip = {
+	.irq_startup	= gpio_startup_irq,
+	.irq_ack	= gpio_ack_irq,
+	.irq_mask	= gpio_mask_irq,
+	.irq_unmask	= gpio_unmask_irq,
+	.irq_set_type	= gpio_set_irq_type,
+	.irq_set_wake	= gpio_set_irq_wake,
+	.flags		= IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static void tz1090_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	irq_hw_number_t hw;
+	unsigned int irq_stat, irq_no;
+	struct tz1090_gpio_bank *bank;
+	struct irq_desc *child_desc;
+
+	bank = (struct tz1090_gpio_bank *)irq_desc_get_handler_data(desc);
+	irq_stat = tz1090_gpio_read(bank, REG_GPIO_DIR) &
+		   tz1090_gpio_read(bank, REG_GPIO_IRQ_STS) &
+		   tz1090_gpio_read(bank, REG_GPIO_IRQ_EN) &
+		   0x3FFFFFFF; /* 30 bits only */
+
+	for (hw = 0; irq_stat; irq_stat >>= 1, ++hw) {
+		if (!(irq_stat & 1))
+			continue;
+
+		irq_no = irq_linear_revmap(bank->domain, hw);
+		child_desc = irq_to_desc(irq_no);
+
+		/* Toggle edge for pin with both edges triggering enabled */
+		if (irqd_get_trigger_type(&child_desc->irq_data)
+				== IRQ_TYPE_EDGE_BOTH)
+			tz1090_gpio_irq_next_edge(bank, hw);
+
+		BUG_ON(!tz1090_gpio_valid_handler(child_desc));
+		generic_handle_irq_desc(irq_no, child_desc);
+	}
+}
+
+static int tz1090_gpio_irq_map(struct irq_domain *d, unsigned int irq,
+			       irq_hw_number_t hw)
+{
+	irq_set_chip(irq, &gpio_irq_chip);
+	return 0;
+}
+
+static const struct irq_domain_ops tz1090_gpio_irq_domain_ops = {
+	.map	= tz1090_gpio_irq_map,
+	.xlate	= irq_domain_xlate_twocell,
+};
+
+static int tz1090_gpio_bank_probe(struct tz1090_gpio_bank_info *info)
+{
+	struct device_node *np = info->node;
+	struct device *dev = info->priv->dev;
+	struct tz1090_gpio_bank *bank;
+
+	bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
+	if (!bank) {
+		dev_err(dev, "unable to allocate driver data\n");
+		return -ENOMEM;
+	}
+
+	/* Offset the main registers to the first register in this bank */
+	bank->reg = info->priv->reg + info->index * 4;
+
+	/* Set up GPIO chip */
+	snprintf(bank->label, sizeof(bank->label), "tz1090-gpio-%u",
+		 info->index);
+	bank->chip.label		= bank->label;
+	bank->chip.dev			= dev;
+	bank->chip.direction_input	= tz1090_gpio_direction_input;
+	bank->chip.direction_output	= tz1090_gpio_direction_output;
+	bank->chip.get			= tz1090_gpio_get;
+	bank->chip.set			= tz1090_gpio_set;
+	bank->chip.free			= tz1090_gpio_free;
+	bank->chip.request		= tz1090_gpio_request;
+	bank->chip.to_irq		= tz1090_gpio_to_irq;
+	bank->chip.of_node		= np;
+
+	/* GPIO numbering from 0 */
+	bank->chip.base			= info->index * 30;
+	bank->chip.ngpio		= 30;
+
+	/* Add the GPIO bank */
+	gpiochip_add(&bank->chip);
+
+	/* Get the GPIO bank IRQ if provided */
+	bank->irq = irq_of_parse_and_map(np, 0);
+
+	/* The interrupt is optional (it may be used by another core on chip) */
+	if (bank->irq < 0) {
+		dev_info(dev, "IRQ not provided for bank %u, IRQs disabled\n",
+			 info->index);
+		return 0;
+	}
+
+	dev_info(dev, "Setting up IRQs for GPIO bank %u\n",
+		 info->index);
+
+	/*
+	 * Initialise all interrupts to disabled so we don't get
+	 * spurious ones on a dirty boot and hit the BUG_ON in the
+	 * handler.
+	 */
+	tz1090_gpio_write(bank, REG_GPIO_IRQ_EN, 0);
+
+	/* Add a virtual IRQ for each GPIO */
+	bank->domain = irq_domain_add_linear(np,
+					     bank->chip.ngpio,
+					     &tz1090_gpio_irq_domain_ops,
+					     bank);
+
+	/* Setup chained handler for this GPIO bank */
+	irq_set_handler_data(bank->irq, bank);
+	irq_set_chained_handler(bank->irq, tz1090_gpio_irq_handler);
+
+	return 0;
+}
+
+static void tz1090_gpio_register_banks(struct tz1090_gpio *priv)
+{
+	struct device_node *np = priv->dev->of_node;
+	struct device_node *node;
+
+	for_each_available_child_of_node(np, node) {
+		struct tz1090_gpio_bank_info info;
+		const __be32 *addr;
+		int len, ret;
+
+		addr = of_get_property(node, "reg", &len);
+		if (!addr || (len < sizeof(int))) {
+			dev_err(priv->dev, "invalid reg on %s\n",
+				node->full_name);
+			continue;
+		}
+
+		info.index = be32_to_cpup(addr);
+		if (info.index >= 3) {
+			dev_err(priv->dev, "index %u in %s out of range\n",
+				info.index, node->full_name);
+			continue;
+		}
+		info.node = of_node_get(node);
+		info.priv = priv;
+
+		ret = tz1090_gpio_bank_probe(&info);
+		if (ret) {
+			dev_err(priv->dev, "failure registering %s\n",
+				node->full_name);
+			of_node_put(node);
+			continue;
+		}
+	}
+}
+
+static int tz1090_gpio_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct resource *res_regs;
+	struct tz1090_gpio priv;
+
+	if (!np) {
+		dev_err(&pdev->dev, "must be instantiated via devicetree\n");
+		return -ENOENT;
+	}
+
+	res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res_regs) {
+		dev_err(&pdev->dev, "cannot find registers resource\n");
+		return -ENOENT;
+	}
+
+	priv.dev = &pdev->dev;
+
+	/* Ioremap the registers */
+	priv.reg = devm_ioremap(&pdev->dev, res_regs->start,
+				 res_regs->end - res_regs->start);
+	if (!priv.reg) {
+		dev_err(&pdev->dev, "unable to ioremap registers\n");
+		return -ENOMEM;
+	}
+
+	/* Look for banks */
+	tz1090_gpio_register_banks(&priv);
+
+	return 0;
+}
+
+static struct of_device_id tz1090_gpio_of_match[] = {
+	{ .compatible = "img,tz1090-gpio" },
+	{ },
+};
+
+static struct platform_driver tz1090_gpio_driver = {
+	.driver = {
+		.name		= "tz1090-gpio",
+		.owner		= THIS_MODULE,
+		.of_match_table	= tz1090_gpio_of_match,
+	},
+	.probe		= tz1090_gpio_probe,
+};
+
+static int __init tz1090_gpio_init(void)
+{
+	return platform_driver_register(&tz1090_gpio_driver);
+}
+subsys_initcall(tz1090_gpio_init);
-- 
1.8.1.2



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

* [PATCH v3 3/4] pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver
  2013-06-20  9:26 [PATCH v3 0/4] Add TZ1090 pinctrl/gpio drivers James Hogan
  2013-06-20  9:26 ` [PATCH v3 1/4] pinctrl-tz1090: add TZ1090 pinctrl driver James Hogan
  2013-06-20  9:26 ` [PATCH v3 2/4] gpio-tz1090: add TZ1090 gpio driver James Hogan
@ 2013-06-20  9:26 ` James Hogan
  2013-06-24 15:08   ` Linus Walleij
  2013-06-20  9:26 ` [PATCH v3 4/4] gpio-tz1090-pdc: add TZ1090 PDC gpio driver James Hogan
  3 siblings, 1 reply; 12+ messages in thread
From: James Hogan @ 2013-06-20  9:26 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-kernel, James Hogan, Grant Likely, Rob Herring,
	Rob Landley, linux-doc, devicetree-discuss

Add a pin control driver for the TZ1090's low power pins via the
powerdown controller SOC_GPIO_CONTROL registers.

These pins have individually controlled pull-up, and group controlled
schmitt, slew-rate, drive-strength, and power-on-start (pos).

The pdc_gpio0 and pdc_gpio1 pins can also be muxed onto the
ir_mod_stable_out and ir_mod_power_out functions respectively. If no
function is set they remain in GPIO mode. These muxes can be overridden
by requesting them as GPIOs.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: linux-doc@vger.kernel.org
Cc: devicetree-discuss@lists.ozlabs.org
---
Changes in v3:
 - separated from irq-imgpdc and removed arch/metag changes to allow
   these patches to go upstream separately via the pinctrl[/gpio] trees
   (particularly the pinctrl drivers depend on the new pinconf DT
   bindings).
 - some s/unsigned/unsigned int/.
 - pinctrl-tz1090*: switch to generic pinconfig DT bindings and
   pinconf_generic_dump_config.
 - pinctrl-tz1090*: use tz1090, prefix for pins and function in DT
   bindings.
 - pinctrl-tz1090*: make internal functions static.
 - pinctrl-tz1090*: move initcall from postcore to arch.

Changes in v2:
 - pinctrl-tz1090-pdc: switched to generic pinconf (a previous patch
   adds the BIAS_BUS_HOLD generic pinconf)
 - pinctrl-tz1090-pdc: changed device tree bindings to more closely
   match generic pinconf (separate tristate, pull-up, pull-down,
   bus-hold flags instead of pull=<X>, drive-strength measured in mA)
 - pinctrl-tz1090-pdc: make use of BIT() from linux/bitops.h
 - pinctrl-tz1090-pdc: add a whole bunch of comments

 .../bindings/pinctrl/img,tz1090-pdc-pinctrl.txt    |  130 +++
 drivers/pinctrl/Kconfig                            |    6 +
 drivers/pinctrl/Makefile                           |    1 +
 drivers/pinctrl/pinctrl-tz1090-pdc.c               | 1029 ++++++++++++++++++++
 4 files changed, 1166 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt
 create mode 100644 drivers/pinctrl/pinctrl-tz1090-pdc.c

diff --git a/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt
new file mode 100644
index 0000000..9f7a85b
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt
@@ -0,0 +1,130 @@
+ImgTec TZ1090 PDC pin controller
+
+Required properties:
+- compatible: "img,tz1090-pdc-pinctrl"
+- reg: Should contain the register physical address and length of the
+  SOC_GPIO_CONTROL registers in the PDC register region.
+
+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".
+
+TZ1090-PDC'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. For this reason, even seemingly boolean
+values are actually tristates in this binding: unspecified, off, or on.
+Unspecified is represented as an absent property, and off/on are represented as
+integer values 0 and 1.
+
+Required subnode-properties:
+- tz1090,pins : An array of strings. Each string contains the name of a pin or
+  group. Valid values for these names are listed below.
+
+Optional subnode-properties:
+- tz1090,function: A string containing the name of the function to mux to the
+  pin or group. Valid values for function names are listed below, including
+  which pingroups can be muxed to them.
+- supported generic pinconfig properties (for further details see
+  Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt):
+  - bias-disable
+  - bias-high-impedance
+  - bias-bus-hold
+  - bias-pull-up
+  - bias-pull-down
+  - input-schmitt-enable
+  - input-schmitt-disable
+  - slew-rate: Integer, control slew rate of pins.
+      0: slow (half frequency)
+      1: fast
+  - drive-strength: Integer, control drive strength of pins in mA.
+      2: 2mA
+      4: 4mA
+      8: 8mA
+      12: 12mA
+  - low-power-enable: Flag, power-on-start weak pull-down for invalid power.
+  - low-power-disable: Flag, power-on-start weak pull-down disabled.
+
+Note that many of these properties are only valid for certain specific pins
+or groups. See the TZ1090 TRM for complete details regarding which groups
+support which functionality. The Linux pinctrl driver may also be a useful
+reference.
+
+Valid values for pin and group names are:
+
+  pins:
+
+    These all support bias-high-impediance, bias-pull-up, bias-pull-down, and
+    bias-bus-hold (which can also be provided to any of the groups below to set
+    it for all gpio pins in that group).
+
+    gpio0, gpio1, sys_wake0, sys_wake1, sys_wake2, ir_data, ext_power.
+
+  mux groups:
+
+    These all support function.
+
+    gpio0
+        pins:       gpio0.
+        function:   ir_mod_stable_out.
+    gpio1
+        pins:       gpio1.
+        function:   ir_mod_power_out.
+
+  drive groups:
+
+    These support input-schmitt-enable, input-schmitt-disable, slew-rate,
+    drive-strength, low-power-enable, and low-power-disable.
+
+    pdc
+        pins:   gpio0, gpio1, sys_wake0, sys_wake1, sys_wake2, ir_data,
+                ext_power.
+
+Example:
+
+	pinctrl_pdc: pinctrl@02006500 {
+		#gpio-range-cells = <3>;
+		compatible = "img,tz1090-pdc-pinctrl";
+		reg = <0x02006500 0x100>;
+	};
+
+Example board file extracts:
+
+	&pinctrl_pdc {
+		pinctrl-names = "default";
+		pinctrl-0 = <&syswake_default>;
+
+		syswake_default: syswakes {
+			syswake_cfg {
+				tz1090,pins =	"sys_wake0",
+						"sys_wake1",
+						"sys_wake2";
+				pull-up;
+			};
+		};
+		irmod_default: irmod {
+			gpio0_cfg {
+				tz1090,pins =	"gpio0";
+				tz1090,function = "ir_mod_stable_out";
+			};
+			gpio1_cfg {
+				tz1090,pins =	"gpio1";
+				tz1090,function = "ir_mod_power_out";
+			};
+		};
+	};
+
+	ir: ir@02006200 {
+		pinctrl-names = "default";
+		pinctrl-0 = <&irmod_default>;
+	};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index acdaa08..74ec834 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -218,6 +218,12 @@ config PINCTRL_TZ1090
 	select PINMUX
 	select GENERIC_PINCONF
 
+config PINCTRL_TZ1090_PDC
+	bool "Toumaz Xenif TZ1090 PDC pin control driver"
+	depends on SOC_TZ1090
+	select PINMUX
+	select PINCONF
+
 config PINCTRL_U300
 	bool "U300 pin controller driver"
 	depends on ARCH_U300
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 37ff29e..cf699a5 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_PINCTRL_TEGRA20)	+= pinctrl-tegra20.o
 obj-$(CONFIG_PINCTRL_TEGRA30)	+= pinctrl-tegra30.o
 obj-$(CONFIG_PINCTRL_TEGRA114)	+= pinctrl-tegra114.o
 obj-$(CONFIG_PINCTRL_TZ1090)	+= pinctrl-tz1090.o
+obj-$(CONFIG_PINCTRL_TZ1090_PDC)	+= pinctrl-tz1090-pdc.o
 obj-$(CONFIG_PINCTRL_U300)	+= pinctrl-u300.o
 obj-$(CONFIG_PINCTRL_COH901)	+= pinctrl-coh901.o
 obj-$(CONFIG_PINCTRL_SAMSUNG)	+= pinctrl-samsung.o
diff --git a/drivers/pinctrl/pinctrl-tz1090-pdc.c b/drivers/pinctrl/pinctrl-tz1090-pdc.c
new file mode 100644
index 0000000..12e4808
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tz1090-pdc.c
@@ -0,0 +1,1029 @@
+/*
+ * Pinctrl driver for the Toumaz Xenif TZ1090 PowerDown Controller pins
+ *
+ * Copyright (c) 2013, Imagination Technologies Ltd.
+ *
+ * Derived from Tegra code:
+ * Copyright (c) 2011-2012, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Derived from code:
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 NVIDIA Corporation
+ * Copyright (C) 2009-2011 ST-Ericsson AB
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/slab.h>
+
+/*
+ * The registers may be shared with other threads/cores, so we need to use the
+ * metag global lock2 for atomicity.
+ */
+#include <asm/global_lock.h>
+
+#include "core.h"
+#include "pinconf.h"
+
+/* Register offsets from bank base address */
+#define REG_GPIO_CONTROL0	0x00
+#define REG_GPIO_CONTROL2	0x08
+
+/* Register field information */
+#define REG_GPIO_CONTROL2_PU_PD_S	16
+#define REG_GPIO_CONTROL2_PDC_POS_S	 4
+#define REG_GPIO_CONTROL2_PDC_DR_S	 2
+#define REG_GPIO_CONTROL2_PDC_SR_S	 1
+#define REG_GPIO_CONTROL2_PDC_SCHMITT_S	 0
+
+/* PU_PD field values */
+#define REG_PU_PD_TRISTATE	0
+#define REG_PU_PD_UP		1
+#define REG_PU_PD_DOWN		2
+#define REG_PU_PD_REPEATER	3
+
+/* DR field values */
+#define REG_DR_2mA		0
+#define REG_DR_4mA		1
+#define REG_DR_8mA		2
+#define REG_DR_12mA		3
+
+/**
+ * struct tz1090_pdc_function - TZ1090 PDC pinctrl mux function
+ * @name:	The name of the function, exported to pinctrl core.
+ * @groups:	An array of pin groups that may select this function.
+ * @ngroups:	The number of entries in @groups.
+ */
+struct tz1090_pdc_function {
+	const char		*name;
+	const char * const	*groups;
+	unsigned int		ngroups;
+};
+
+/**
+ * struct tz1090_pdc_pingroup - TZ1090 PDC pin group
+ * @name:	Name of pin group.
+ * @pins:	Array of pin numbers in this pin group.
+ * @npins:	Number of pins in this pin group.
+ * @func:	Function enabled by the mux.
+ * @reg:	Mux register offset.
+ * @bit:	Mux register bit.
+ * @drv:	Drive control supported, otherwise it's a mux.
+ *		This means Schmitt, Slew, and Drive strength.
+ *
+ * A representation of a group of pins (possibly just one pin) in the TZ1090
+ * PDC pin controller. Each group allows some parameter or parameters to be
+ * configured. The most common is mux function selection.
+ */
+struct tz1090_pdc_pingroup {
+	const char		*name;
+	const unsigned int	*pins;
+	unsigned int		npins;
+	int			func;
+	u16			reg;
+	u8			bit;
+	bool			drv;
+};
+
+/*
+ * All PDC pins can be GPIOs. Define these first to match how the GPIO driver
+ * names/numbers its pins.
+ */
+
+enum tz1090_pdc_pin {
+	TZ1090_PDC_PIN_GPIO0,
+	TZ1090_PDC_PIN_GPIO1,
+	TZ1090_PDC_PIN_SYS_WAKE0,
+	TZ1090_PDC_PIN_SYS_WAKE1,
+	TZ1090_PDC_PIN_SYS_WAKE2,
+	TZ1090_PDC_PIN_IR_DATA,
+	TZ1090_PDC_PIN_EXT_POWER,
+};
+
+/* Pin names */
+
+static const struct pinctrl_pin_desc tz1090_pdc_pins[] = {
+	/* PDC GPIOs */
+	PINCTRL_PIN(TZ1090_PDC_PIN_GPIO0,	"gpio0"),
+	PINCTRL_PIN(TZ1090_PDC_PIN_GPIO1,	"gpio1"),
+	PINCTRL_PIN(TZ1090_PDC_PIN_SYS_WAKE0,	"sys_wake0"),
+	PINCTRL_PIN(TZ1090_PDC_PIN_SYS_WAKE1,	"sys_wake1"),
+	PINCTRL_PIN(TZ1090_PDC_PIN_SYS_WAKE2,	"sys_wake2"),
+	PINCTRL_PIN(TZ1090_PDC_PIN_IR_DATA,	"ir_data"),
+	PINCTRL_PIN(TZ1090_PDC_PIN_EXT_POWER,	"ext_power"),
+};
+
+/* Pin group pins */
+
+static const unsigned int gpio0_pins[] = {
+	TZ1090_PDC_PIN_GPIO0,
+};
+
+static const unsigned int gpio1_pins[] = {
+	TZ1090_PDC_PIN_GPIO1,
+};
+
+static const unsigned int pdc_pins[] = {
+	TZ1090_PDC_PIN_GPIO0,
+	TZ1090_PDC_PIN_GPIO1,
+	TZ1090_PDC_PIN_SYS_WAKE0,
+	TZ1090_PDC_PIN_SYS_WAKE1,
+	TZ1090_PDC_PIN_SYS_WAKE2,
+	TZ1090_PDC_PIN_IR_DATA,
+	TZ1090_PDC_PIN_EXT_POWER,
+};
+
+/* Mux functions */
+
+enum tz1090_pdc_mux {
+	/* PDC_GPIO0 mux */
+	TZ1090_PDC_MUX_IR_MOD_STABLE_OUT,
+	/* PDC_GPIO1 mux */
+	TZ1090_PDC_MUX_IR_MOD_POWER_OUT,
+};
+
+/* Pin groups a function can be muxed to */
+
+static const char * const gpio0_groups[] = {
+	"gpio0",
+};
+
+static const char * const gpio1_groups[] = {
+	"gpio1",
+};
+
+#define FUNCTION(mux, fname, group)			\
+	[(TZ1090_PDC_MUX_ ## mux)] = {			\
+		.name = #fname,				\
+		.groups = group##_groups,		\
+		.ngroups = ARRAY_SIZE(group##_groups),	\
+	}
+
+/* Must correlate with enum tz1090_pdc_mux */
+static const struct tz1090_pdc_function tz1090_pdc_functions[] = {
+	/*	 MUX			fn			pingroups */
+	FUNCTION(IR_MOD_STABLE_OUT,	ir_mod_stable_out,	gpio0),
+	FUNCTION(IR_MOD_POWER_OUT,	ir_mod_power_out,	gpio1),
+};
+
+/**
+ * MUX_PG() - Initialise a pin group with mux control
+ * @pg_name:	Pin group name (stringified, _pins appended to get pins array)
+ * @f0:		Function 0 (TZ1090_PDC_MUX_ is prepended)
+ * @mux_r:	Mux register (REG_PINCTRL_ is prepended)
+ * @mux_b:	Bit number in register of mux field
+ */
+#define MUX_PG(pg_name, f0, mux_r, mux_b)			\
+	{							\
+		.name = #pg_name,				\
+		.pins = pg_name##_pins,				\
+		.npins = ARRAY_SIZE(pg_name##_pins),		\
+		.func = TZ1090_PDC_MUX_ ## f0,			\
+		.reg = (REG_ ## mux_r),				\
+		.bit = (mux_b),					\
+	}
+
+/**
+ * DRV_PG() - Initialise a pin group with drive control
+ * @pg_name:	Pin group name (stringified, _pins appended to get pins array)
+ */
+#define DRV_PG(pg_name)				\
+	{							\
+		.name = #pg_name,				\
+		.pins = pg_name##_pins,				\
+		.npins = ARRAY_SIZE(pg_name##_pins),		\
+		.drv = true,					\
+	}
+
+static const struct tz1090_pdc_pingroup tz1090_pdc_groups[] = {
+	/* Muxing pin groups */
+	/*     pg_name, f0,                 mux register,  mux bit */
+	MUX_PG(gpio0,   IR_MOD_STABLE_OUT,  GPIO_CONTROL0, 7),
+	MUX_PG(gpio1,   IR_MOD_POWER_OUT,   GPIO_CONTROL0, 6),
+
+	/* Drive pin groups */
+	/*     pg_name */
+	DRV_PG(pdc),
+};
+
+/**
+ * struct tz1090_pdc_pmx - Private pinctrl data
+ * @dev:	Platform device
+ * @pctl:	Pin control device
+ * @regs:	Register region
+ * @lock:	Lock protecting coherency of mux_en and gpio_en
+ * @mux_en:	Muxes that have been enabled
+ * @gpio_en:	Muxable GPIOs that have been enabled
+ */
+struct tz1090_pdc_pmx {
+	struct device		*dev;
+	struct pinctrl_dev	*pctl;
+	void __iomem		*regs;
+	spinlock_t		lock;
+	u32			mux_en;
+	u32			gpio_en;
+};
+
+static inline u32 pmx_read(struct tz1090_pdc_pmx *pmx, u32 reg)
+{
+	return ioread32(pmx->regs + reg);
+}
+
+static inline void pmx_write(struct tz1090_pdc_pmx *pmx, u32 val, u32 reg)
+{
+	iowrite32(val, pmx->regs + reg);
+}
+
+/*
+ * Pin control operations
+ */
+
+static int tz1090_pdc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(tz1090_pdc_groups);
+}
+
+static const char *tz1090_pdc_pinctrl_get_group_name(struct pinctrl_dev *pctl,
+						     unsigned int group)
+{
+	return tz1090_pdc_groups[group].name;
+}
+
+static int tz1090_pdc_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+					     unsigned int group,
+					     const unsigned int **pins,
+					     unsigned int *num_pins)
+{
+	*pins = tz1090_pdc_groups[group].pins;
+	*num_pins = tz1090_pdc_groups[group].npins;
+
+	return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void tz1090_pdc_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev,
+					    struct seq_file *s,
+					    unsigned int offset)
+{
+	seq_printf(s, " %s", dev_name(pctldev->dev));
+}
+#endif
+
+static int reserve_map(struct device *dev, struct pinctrl_map **map,
+		       unsigned int *reserved_maps, unsigned int *num_maps,
+		       unsigned int reserve)
+{
+	unsigned int old_num = *reserved_maps;
+	unsigned int new_num = *num_maps + reserve;
+	struct pinctrl_map *new_map;
+
+	if (old_num >= new_num)
+		return 0;
+
+	new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL);
+	if (!new_map) {
+		dev_err(dev, "krealloc(map) failed\n");
+		return -ENOMEM;
+	}
+
+	memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map));
+
+	*map = new_map;
+	*reserved_maps = new_num;
+
+	return 0;
+}
+
+static int add_map_mux(struct pinctrl_map **map, unsigned int *reserved_maps,
+		       unsigned int *num_maps, const char *group,
+		       const char *function)
+{
+	if (WARN_ON(*num_maps == *reserved_maps))
+		return -ENOSPC;
+
+	(*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP;
+	(*map)[*num_maps].data.mux.group = group;
+	(*map)[*num_maps].data.mux.function = function;
+	(*num_maps)++;
+
+	return 0;
+}
+
+/**
+ * get_group_selector() - returns the group selector for a group
+ * @pin_group: the pin group to look up
+ *
+ * This is the same as pinctrl_get_group_selector except it doesn't produce an
+ * error message if the group isn't found or debug messages.
+ */
+static int get_group_selector(const char *pin_group)
+{
+	unsigned int group;
+
+	for (group = 0; group < ARRAY_SIZE(tz1090_pdc_groups); ++group)
+		if (!strcmp(tz1090_pdc_groups[group].name, pin_group))
+			return group;
+
+	return -EINVAL;
+}
+
+static int add_map_configs(struct device *dev,
+			   struct pinctrl_map **map,
+			   unsigned int *reserved_maps, unsigned int *num_maps,
+			   const char *group, unsigned long *configs,
+			   unsigned int num_configs)
+{
+	unsigned long *dup_configs;
+	enum pinctrl_map_type type;
+
+	if (WARN_ON(*num_maps == *reserved_maps))
+		return -ENOSPC;
+
+	dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs),
+			      GFP_KERNEL);
+	if (!dup_configs) {
+		dev_err(dev, "kmemdup(configs) failed\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * We support both pins and pin groups, but we need to figure out which
+	 * one we have.
+	 */
+	if (get_group_selector(group) >= 0)
+		type = PIN_MAP_TYPE_CONFIGS_GROUP;
+	else
+		type = PIN_MAP_TYPE_CONFIGS_PIN;
+	(*map)[*num_maps].type = type;
+	(*map)[*num_maps].data.configs.group_or_pin = group;
+	(*map)[*num_maps].data.configs.configs = dup_configs;
+	(*map)[*num_maps].data.configs.num_configs = num_configs;
+	(*num_maps)++;
+
+	return 0;
+}
+
+static void tz1090_pdc_pinctrl_dt_free_map(struct pinctrl_dev *pctldev,
+					   struct pinctrl_map *map,
+					   unsigned int num_maps)
+{
+	int i;
+
+	for (i = 0; i < num_maps; i++)
+		if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
+			kfree(map[i].data.configs.configs);
+
+	kfree(map);
+}
+
+static int tz1090_pdc_pinctrl_dt_subnode_to_map(struct device *dev,
+						struct device_node *np,
+						struct pinctrl_map **map,
+						unsigned int *reserved_maps,
+						unsigned int *num_maps)
+{
+	int ret;
+	const char *function;
+	unsigned long *configs = NULL;
+	unsigned int num_configs = 0;
+	unsigned int reserve;
+	struct property *prop;
+	const char *group;
+
+	ret = of_property_read_string(np, "tz1090,function", &function);
+	if (ret < 0) {
+		/* EINVAL=missing, which is fine since it's optional */
+		if (ret != -EINVAL)
+			dev_err(dev,
+				"could not parse property function\n");
+		function = NULL;
+	}
+
+	ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs);
+	if (ret)
+		return ret;
+
+	reserve = 0;
+	if (function != NULL)
+		reserve++;
+	if (num_configs)
+		reserve++;
+	ret = of_property_count_strings(np, "tz1090,pins");
+	if (ret < 0) {
+		dev_err(dev, "could not parse property pins\n");
+		goto exit;
+	}
+	reserve *= ret;
+
+	ret = reserve_map(dev, map, reserved_maps, num_maps, reserve);
+	if (ret < 0)
+		goto exit;
+
+	of_property_for_each_string(np, "tz1090,pins", prop, group) {
+		if (function) {
+			ret = add_map_mux(map, reserved_maps, num_maps,
+					  group, function);
+			if (ret < 0)
+				goto exit;
+		}
+
+		if (num_configs) {
+			ret = add_map_configs(dev, map, reserved_maps,
+					      num_maps, group, configs,
+					      num_configs);
+			if (ret < 0)
+				goto exit;
+		}
+	}
+
+	ret = 0;
+
+exit:
+	kfree(configs);
+	return ret;
+}
+
+static int tz1090_pdc_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
+					     struct device_node *np_config,
+					     struct pinctrl_map **map,
+					     unsigned int *num_maps)
+{
+	unsigned int reserved_maps;
+	struct device_node *np;
+	int ret;
+
+	reserved_maps = 0;
+	*map = NULL;
+	*num_maps = 0;
+
+	for_each_child_of_node(np_config, np) {
+		ret = tz1090_pdc_pinctrl_dt_subnode_to_map(pctldev->dev, np,
+							   map, &reserved_maps,
+							   num_maps);
+		if (ret < 0) {
+			tz1090_pdc_pinctrl_dt_free_map(pctldev, *map,
+						       *num_maps);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static struct pinctrl_ops tz1090_pdc_pinctrl_ops = {
+	.get_groups_count	= tz1090_pdc_pinctrl_get_groups_count,
+	.get_group_name		= tz1090_pdc_pinctrl_get_group_name,
+	.get_group_pins		= tz1090_pdc_pinctrl_get_group_pins,
+#ifdef CONFIG_DEBUG_FS
+	.pin_dbg_show		= tz1090_pdc_pinctrl_pin_dbg_show,
+#endif
+	.dt_node_to_map		= tz1090_pdc_pinctrl_dt_node_to_map,
+	.dt_free_map		= tz1090_pdc_pinctrl_dt_free_map,
+};
+
+/*
+ * Pin mux operations
+ */
+
+static int tz1090_pdc_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(tz1090_pdc_functions);
+}
+
+static const char *tz1090_pdc_pinctrl_get_func_name(struct pinctrl_dev *pctldev,
+						    unsigned int function)
+{
+	return tz1090_pdc_functions[function].name;
+}
+
+static int tz1090_pdc_pinctrl_get_func_groups(struct pinctrl_dev *pctldev,
+					      unsigned int function,
+					      const char * const **groups,
+					      unsigned int * const num_groups)
+{
+	*groups = tz1090_pdc_functions[function].groups;
+	*num_groups = tz1090_pdc_functions[function].ngroups;
+
+	return 0;
+}
+
+/**
+ * tz1090_pdc_pinctrl_mux() - update mux bit
+ * @pmx:		Pinmux data
+ * @grp:		Pin mux group
+ */
+static void tz1090_pdc_pinctrl_mux(struct tz1090_pdc_pmx *pmx,
+				   const struct tz1090_pdc_pingroup *grp)
+{
+	u32 reg, select;
+	unsigned int pin_shift = grp->pins[0];
+	unsigned long flags;
+
+	/* select = mux && !gpio */
+	select = ((pmx->mux_en & ~pmx->gpio_en) >> pin_shift) & 1;
+
+	/* set up the mux */
+	__global_lock2(flags);
+	reg = pmx_read(pmx, grp->reg);
+	reg &= ~BIT(grp->bit);
+	reg |= select << grp->bit;
+	pmx_write(pmx, reg, grp->reg);
+	__global_unlock2(flags);
+}
+
+static int tz1090_pdc_pinctrl_enable(struct pinctrl_dev *pctldev,
+				     unsigned int function, unsigned int group)
+{
+	struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	const struct tz1090_pdc_pingroup *grp = &tz1090_pdc_groups[group];
+
+	dev_dbg(pctldev->dev, "%s(func=%u (%s), group=%u (%s))\n",
+		__func__,
+		function, tz1090_pdc_functions[function].name,
+		group, tz1090_pdc_groups[group].name);
+
+	/* is it even a mux? */
+	if (grp->drv)
+		return -EINVAL;
+
+	/* does this group even control the function? */
+	if (function != grp->func)
+		return -EINVAL;
+
+	/* record the pin being muxed and update mux bit */
+	spin_lock(&pmx->lock);
+	pmx->mux_en |= BIT(grp->pins[0]);
+	tz1090_pdc_pinctrl_mux(pmx, grp);
+	spin_unlock(&pmx->lock);
+	return 0;
+}
+
+static void tz1090_pdc_pinctrl_disable(struct pinctrl_dev *pctldev,
+				       unsigned int function,
+				       unsigned int group)
+{
+	struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	const struct tz1090_pdc_pingroup *grp = &tz1090_pdc_groups[group];
+
+	dev_dbg(pctldev->dev, "%s(func=%u (%s), group=%u (%s))\n",
+		__func__,
+		function, tz1090_pdc_functions[function].name,
+		group, tz1090_pdc_groups[group].name);
+
+	/* is it even a mux? */
+	if (grp->drv)
+		return;
+
+	/* does this group even control the function? */
+	if (function != grp->func)
+		return;
+
+	/* record the pin being unmuxed and update mux bit */
+	spin_lock(&pmx->lock);
+	pmx->mux_en &= ~BIT(grp->pins[0]);
+	tz1090_pdc_pinctrl_mux(pmx, grp);
+	spin_unlock(&pmx->lock);
+}
+
+static const struct tz1090_pdc_pingroup *find_mux_group(
+						struct tz1090_pdc_pmx *pmx,
+						unsigned int pin)
+{
+	const struct tz1090_pdc_pingroup *grp;
+	unsigned int group;
+
+	grp = tz1090_pdc_groups;
+	for (group = 0; group < ARRAY_SIZE(tz1090_pdc_groups); ++group, ++grp) {
+		/* only match muxes */
+		if (grp->drv)
+			continue;
+
+		/* with a matching pin */
+		if (grp->pins[0] == pin)
+			return grp;
+	}
+
+	return NULL;
+}
+
+static int tz1090_pdc_pinctrl_gpio_request_enable(
+					struct pinctrl_dev *pctldev,
+					struct pinctrl_gpio_range *range,
+					unsigned int pin)
+{
+	struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	const struct tz1090_pdc_pingroup *grp = find_mux_group(pmx, pin);
+
+	if (grp) {
+		/* record the pin in GPIO use and update mux bit */
+		spin_lock(&pmx->lock);
+		pmx->gpio_en |= BIT(pin);
+		tz1090_pdc_pinctrl_mux(pmx, grp);
+		spin_unlock(&pmx->lock);
+	}
+	return 0;
+}
+
+static void tz1090_pdc_pinctrl_gpio_disable_free(
+					struct pinctrl_dev *pctldev,
+					struct pinctrl_gpio_range *range,
+					unsigned int pin)
+{
+	struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	const struct tz1090_pdc_pingroup *grp = find_mux_group(pmx, pin);
+
+	if (grp) {
+		/* record the pin not in GPIO use and update mux bit */
+		spin_lock(&pmx->lock);
+		pmx->gpio_en &= ~BIT(pin);
+		tz1090_pdc_pinctrl_mux(pmx, grp);
+		spin_unlock(&pmx->lock);
+	}
+}
+
+static struct pinmux_ops tz1090_pdc_pinmux_ops = {
+	.get_functions_count	= tz1090_pdc_pinctrl_get_funcs_count,
+	.get_function_name	= tz1090_pdc_pinctrl_get_func_name,
+	.get_function_groups	= tz1090_pdc_pinctrl_get_func_groups,
+	.enable			= tz1090_pdc_pinctrl_enable,
+	.disable		= tz1090_pdc_pinctrl_disable,
+	.gpio_request_enable	= tz1090_pdc_pinctrl_gpio_request_enable,
+	.gpio_disable_free	= tz1090_pdc_pinctrl_gpio_disable_free,
+};
+
+/*
+ * Pin config operations
+ */
+
+static int tz1090_pdc_pinconf_reg(struct pinctrl_dev *pctldev,
+				  unsigned int pin,
+				  enum pin_config_param param,
+				  bool report_err,
+				  u32 *reg, u32 *width, u32 *mask, u32 *shift,
+				  u32 *val)
+{
+	/* Find information about parameter's register */
+	switch (param) {
+	case PIN_CONFIG_BIAS_DISABLE:
+	case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+		*val = REG_PU_PD_TRISTATE;
+		break;
+	case PIN_CONFIG_BIAS_PULL_UP:
+		*val = REG_PU_PD_UP;
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		*val = REG_PU_PD_DOWN;
+		break;
+	case PIN_CONFIG_BIAS_BUS_HOLD:
+		*val = REG_PU_PD_REPEATER;
+		break;
+	default:
+		return -ENOTSUPP;
+	};
+
+	/* Only input bias parameters supported */
+	*reg = REG_GPIO_CONTROL2;
+	*shift = REG_GPIO_CONTROL2_PU_PD_S + pin*2;
+	*width = 2;
+
+	/* Calculate field information */
+	*mask = (BIT(*width) - 1) << *shift;
+
+	return 0;
+}
+
+static int tz1090_pdc_pinconf_get(struct pinctrl_dev *pctldev,
+				  unsigned int pin, unsigned long *config)
+{
+	struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param param = pinconf_to_config_param(*config);
+	int ret;
+	u32 reg, width, mask, shift, val, tmp, arg;
+
+	/* Get register information */
+	ret = tz1090_pdc_pinconf_reg(pctldev, pin, param, true,
+				     &reg, &width, &mask, &shift, &val);
+	if (ret < 0)
+		return ret;
+
+	/* Extract field from register */
+	tmp = pmx_read(pmx, reg);
+	arg = ((tmp & mask) >> shift) == val;
+
+	/* Config not active */
+	if (!arg)
+		return -EINVAL;
+
+	/* And pack config */
+	*config = pinconf_to_config_packed(param, arg);
+
+	return 0;
+}
+
+static int tz1090_pdc_pinconf_set(struct pinctrl_dev *pctldev,
+				  unsigned int pin, unsigned long config)
+{
+	struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param param = pinconf_to_config_param(config);
+	unsigned int arg = pinconf_to_config_argument(config);
+	int ret;
+	u32 reg, width, mask, shift, val, tmp;
+	unsigned long flags;
+
+	dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n",
+		__func__, tz1090_pdc_pins[pin].name, config);
+
+	/* Get register information */
+	ret = tz1090_pdc_pinconf_reg(pctldev, pin, param, true,
+				     &reg, &width, &mask, &shift, &val);
+	if (ret < 0)
+		return ret;
+
+	/* Unpack argument and range check it */
+	if (arg > 1) {
+		dev_dbg(pctldev->dev, "%s: arg %u out of range\n",
+			__func__, arg);
+		return -EINVAL;
+	}
+
+	/* Write register field */
+	__global_lock2(flags);
+	tmp = pmx_read(pmx, reg);
+	tmp &= ~mask;
+	if (arg)
+		tmp |= val << shift;
+	pmx_write(pmx, tmp, reg);
+	__global_unlock2(flags);
+
+	return 0;
+}
+
+static const int tz1090_pdc_boolean_map[] = {
+	[0]		= -EINVAL,
+	[1]		= 1,
+};
+
+static const int tz1090_pdc_dr_map[] = {
+	[REG_DR_2mA]	= 2,
+	[REG_DR_4mA]	= 4,
+	[REG_DR_8mA]	= 8,
+	[REG_DR_12mA]	= 12,
+};
+
+static int tz1090_pdc_pinconf_group_reg(struct pinctrl_dev *pctldev,
+					const struct tz1090_pdc_pingroup *g,
+					enum pin_config_param param,
+					bool report_err, u32 *reg, u32 *width,
+					u32 *mask, u32 *shift, const int **map)
+{
+	/* Drive configuration applies in groups, but not to all groups. */
+	if (!g->drv) {
+		if (report_err)
+			dev_dbg(pctldev->dev,
+				"%s: group %s has no drive control\n",
+				__func__, g->name);
+		return -ENOTSUPP;
+	}
+
+	/* Find information about drive parameter's register */
+	*reg = REG_GPIO_CONTROL2;
+	switch (param) {
+	case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+		*shift = REG_GPIO_CONTROL2_PDC_SCHMITT_S;
+		*width = 1;
+		*map = tz1090_pdc_boolean_map;
+		break;
+	case PIN_CONFIG_SLEW_RATE:
+		*shift = REG_GPIO_CONTROL2_PDC_SR_S;
+		*width = 1;
+		*map = tz1090_pdc_boolean_map;
+		break;
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		*shift = REG_GPIO_CONTROL2_PDC_DR_S;
+		*width = 2;
+		*map = tz1090_pdc_dr_map;
+		break;
+	case PIN_CONFIG_LOW_POWER_MODE:
+		*shift = REG_GPIO_CONTROL2_PDC_POS_S;
+		*width = 1;
+		*map = tz1090_pdc_boolean_map;
+		break;
+	default:
+		return -ENOTSUPP;
+	};
+
+	/* Calculate field information */
+	*mask = (BIT(*width) - 1) << *shift;
+
+	return 0;
+}
+
+static int tz1090_pdc_pinconf_group_get(struct pinctrl_dev *pctldev,
+					unsigned int group,
+					unsigned long *config)
+{
+	struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	const struct tz1090_pdc_pingroup *g = &tz1090_pdc_groups[group];
+	enum pin_config_param param = pinconf_to_config_param(*config);
+	int ret, arg;
+	u32 reg, width, mask, shift, val;
+	const int *map;
+
+	/* Get register information */
+	ret = tz1090_pdc_pinconf_group_reg(pctldev, g, param, true,
+					   &reg, &width, &mask, &shift, &map);
+	if (ret < 0)
+		return ret;
+
+	/* Extract field from register */
+	val = pmx_read(pmx, reg);
+	arg = map[(val & mask) >> shift];
+	if (arg < 0)
+		return arg;
+
+	/* And pack config */
+	*config = pinconf_to_config_packed(param, arg);
+
+	return 0;
+}
+
+static int tz1090_pdc_pinconf_group_set(struct pinctrl_dev *pctldev,
+					unsigned int group,
+					unsigned long config)
+{
+	struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	const struct tz1090_pdc_pingroup *g = &tz1090_pdc_groups[group];
+	enum pin_config_param param = pinconf_to_config_param(config);
+	const unsigned int *pit;
+	unsigned int i;
+	int ret, arg;
+	u32 reg, width, mask, shift, val;
+	unsigned long flags;
+	const int *map;
+
+	dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n",
+		__func__, g->name, config);
+
+	/* Get register information */
+	ret = tz1090_pdc_pinconf_group_reg(pctldev, g, param, true,
+					   &reg, &width, &mask, &shift, &map);
+	if (ret < 0) {
+		/*
+		 * Maybe we're trying to set a per-pin configuration of a group,
+		 * so do the pins one by one. This is mainly as a convenience.
+		 */
+		for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) {
+			ret = tz1090_pdc_pinconf_set(pctldev, *pit, config);
+			if (ret)
+				return ret;
+		}
+		return 0;
+	}
+
+	/* Unpack argument and map it to register value */
+	arg = pinconf_to_config_argument(config);
+	for (i = 0; i < BIT(width); ++i) {
+		if (map[i] == arg || (map[i] == -EINVAL && !arg)) {
+			/* Write register field */
+			__global_lock2(flags);
+			val = pmx_read(pmx, reg);
+			val &= ~mask;
+			val |= i << shift;
+			pmx_write(pmx, val, reg);
+			__global_unlock2(flags);
+			return 0;
+		}
+	}
+
+	dev_dbg(pctldev->dev, "%s: arg %u not supported\n",
+		__func__, arg);
+	return 0;
+}
+
+static struct pinconf_ops tz1090_pdc_pinconf_ops = {
+	.is_generic			= true,
+	.pin_config_get			= tz1090_pdc_pinconf_get,
+	.pin_config_set			= tz1090_pdc_pinconf_set,
+	.pin_config_group_get		= tz1090_pdc_pinconf_group_get,
+	.pin_config_group_set		= tz1090_pdc_pinconf_group_set,
+	.pin_config_config_dbg_show	= pinconf_generic_dump_config,
+};
+
+/*
+ * Pin control driver setup
+ */
+
+static struct pinctrl_desc tz1090_pdc_pinctrl_desc = {
+	.pctlops	= &tz1090_pdc_pinctrl_ops,
+	.pmxops		= &tz1090_pdc_pinmux_ops,
+	.confops	= &tz1090_pdc_pinconf_ops,
+	.owner		= THIS_MODULE,
+};
+
+static int tz1090_pdc_pinctrl_probe(struct platform_device *pdev)
+{
+	struct tz1090_pdc_pmx *pmx;
+	struct resource *res;
+
+	pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL);
+	if (!pmx) {
+		dev_err(&pdev->dev, "Can't alloc tz1090_pdc_pmx\n");
+		return -ENOMEM;
+	}
+	pmx->dev = &pdev->dev;
+	spin_lock_init(&pmx->lock);
+
+	tz1090_pdc_pinctrl_desc.name = dev_name(&pdev->dev);
+	tz1090_pdc_pinctrl_desc.pins = tz1090_pdc_pins;
+	tz1090_pdc_pinctrl_desc.npins = ARRAY_SIZE(tz1090_pdc_pins);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Missing MEM resource\n");
+		return -ENODEV;
+	}
+
+	if (!devm_request_mem_region(&pdev->dev, res->start,
+				     resource_size(res),
+				     dev_name(&pdev->dev))) {
+		dev_err(&pdev->dev,
+			"Couldn't request MEM resource\n");
+		return -ENODEV;
+	}
+
+	pmx->regs = devm_ioremap(&pdev->dev, res->start,
+				 resource_size(res));
+	if (!pmx->regs) {
+		dev_err(&pdev->dev, "Couldn't ioremap regs\n");
+		return -ENODEV;
+	}
+
+	pmx->pctl = pinctrl_register(&tz1090_pdc_pinctrl_desc, &pdev->dev, pmx);
+	if (!pmx->pctl) {
+		dev_err(&pdev->dev, "Couldn't register pinctrl driver\n");
+		return -ENODEV;
+	}
+
+	platform_set_drvdata(pdev, pmx);
+
+	dev_info(&pdev->dev, "TZ1090 PDC pinctrl driver initialised\n");
+
+	return 0;
+}
+
+static int tz1090_pdc_pinctrl_remove(struct platform_device *pdev)
+{
+	struct tz1090_pdc_pmx *pmx = platform_get_drvdata(pdev);
+
+	pinctrl_unregister(pmx->pctl);
+
+	return 0;
+}
+
+static struct of_device_id tz1090_pdc_pinctrl_of_match[] = {
+	{ .compatible = "img,tz1090-pdc-pinctrl", },
+	{ },
+};
+
+static struct platform_driver tz1090_pdc_pinctrl_driver = {
+	.driver = {
+		.name		= "tz1090-pdc-pinctrl",
+		.owner		= THIS_MODULE,
+		.of_match_table	= tz1090_pdc_pinctrl_of_match,
+	},
+	.probe	= tz1090_pdc_pinctrl_probe,
+	.remove	= tz1090_pdc_pinctrl_remove,
+};
+
+static int __init tz1090_pdc_pinctrl_init(void)
+{
+	return platform_driver_register(&tz1090_pdc_pinctrl_driver);
+}
+arch_initcall(tz1090_pdc_pinctrl_init);
+
+static void __exit tz1090_pdc_pinctrl_exit(void)
+{
+	platform_driver_unregister(&tz1090_pdc_pinctrl_driver);
+}
+module_exit(tz1090_pdc_pinctrl_exit);
+
+MODULE_AUTHOR("Imagination Technologies Ltd.");
+MODULE_DESCRIPTION("Toumaz Xenif TZ1090 PDC pinctrl driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, tz1090_pdc_pinctrl_of_match);
-- 
1.8.1.2



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

* [PATCH v3 4/4] gpio-tz1090-pdc: add TZ1090 PDC gpio driver
  2013-06-20  9:26 [PATCH v3 0/4] Add TZ1090 pinctrl/gpio drivers James Hogan
                   ` (2 preceding siblings ...)
  2013-06-20  9:26 ` [PATCH v3 3/4] pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver James Hogan
@ 2013-06-20  9:26 ` James Hogan
  2013-06-24 15:11   ` Linus Walleij
  3 siblings, 1 reply; 12+ messages in thread
From: James Hogan @ 2013-06-20  9:26 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-kernel, James Hogan, Grant Likely, Rob Herring,
	Rob Landley, linux-doc, devicetree-discuss

Add a GPIO driver for the low-power Powerdown Controller GPIOs in the
TZ1090 SoC.

The driver is instantiated by device tree and supports interrupts for
the SysWake GPIOs only.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: linux-doc@vger.kernel.org
Cc: devicetree-discuss@lists.ozlabs.org
---
Changes in v3:
 - separated from irq-imgpdc and removed arch/metag changes to allow
   these patches to go upstream separately via the pinctrl[/gpio] trees
   (particularly the pinctrl drivers depend on the new pinconf DT
   bindings).
 - some s/unsigned/unsigned int/.
 - gpio-tz1090*: refer to <dt-bindings/gpio/gpio.h> and
   <dt-bindings/interrupt-controller/irq.h> flags in bindings.
 - gpio-tz1090*: move initcall from postcore to subsys.

Changes in v2:
 - gpio-tz1090-pdc: remove references to Linux flags in dt bindings
 - gpio-tz1090-pdc: make use of BIT() from linux/bitops.h
 - gpio-tz1090-pdc: make register accessors inline to match pinctrl
 - gpio-tz1090-pdc: update gpio-ranges to use 3 cells after recent ABI
   breakage

 .../devicetree/bindings/gpio/gpio-tz1090-pdc.txt   |  43 ++++
 drivers/gpio/Kconfig                               |   7 +
 drivers/gpio/Makefile                              |   1 +
 drivers/gpio/gpio-tz1090-pdc.c                     | 243 +++++++++++++++++++++
 4 files changed, 294 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/gpio-tz1090-pdc.txt
 create mode 100644 drivers/gpio/gpio-tz1090-pdc.c

diff --git a/Documentation/devicetree/bindings/gpio/gpio-tz1090-pdc.txt b/Documentation/devicetree/bindings/gpio/gpio-tz1090-pdc.txt
new file mode 100644
index 0000000..704a277
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-tz1090-pdc.txt
@@ -0,0 +1,43 @@
+ImgTec TZ1090 PDC GPIO Controller
+
+Required properties:
+- compatible: Compatible property value should be "img,tz1090-pdc-gpio>".
+
+- reg: Physical base address of the controller and length of memory mapped
+  region. This starts at and cover the SOC_GPIO_CONTROL registers.
+
+- gpio-controller: Specifies that the node is a gpio controller.
+
+- #gpio-cells: Should be 2. The syntax of the gpio specifier used by client
+  nodes should have the following values.
+     <[phandle of the gpio controller node]
+      [PDC gpio number]
+      [gpio flags]>
+
+  Values for gpio specifier:
+  - GPIO number: a value in the range 0 to 6.
+  - GPIO flags: bit field of flags, as defined in <dt-bindings/gpio/gpio.h>.
+    Only the following flags are supported:
+      GPIO_ACTIVE_HIGH
+      GPIO_ACTIVE_LOW
+
+Optional properties:
+- gpio-ranges: Mapping to pin controller pins
+
+- interrupts: Individual syswake interrupts (other GPIOs cannot interrupt)
+
+
+Example:
+
+	pdc_gpios: gpio-controller@02006500 {
+		gpio-controller;
+		#gpio-cells = <2>;
+
+		compatible = "img,tz1090-pdc-gpio";
+		reg = <0x02006500 0x100>;
+
+		interrupt-parent = <&pdc>;
+		interrupts =	<8  IRQ_TYPE_NONE>,	/* Syswake 0 */
+				<9  IRQ_TYPE_NONE>,	/* Syswake 1 */
+				<10 IRQ_TYPE_NONE>;	/* Syswake 2 */
+	};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index ee27c2e..08dbfbe 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -239,6 +239,13 @@ config GPIO_TZ1090
 	help
 	  Say yes here to support Toumaz Xenif TZ1090 GPIOs.
 
+config GPIO_TZ1090_PDC
+	bool "Toumaz Xenif TZ1090 PDC GPIO support"
+	depends on SOC_TZ1090
+	default y
+	help
+	  Say yes here to support Toumaz Xenif TZ1090 PDC GPIOs.
+
 config GPIO_XILINX
 	bool "Xilinx GPIO support"
 	depends on PPC_OF || MICROBLAZE
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 37bdc1e..44341c7 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -80,6 +80,7 @@ obj-$(CONFIG_GPIO_TS5500)	+= gpio-ts5500.o
 obj-$(CONFIG_GPIO_TWL4030)	+= gpio-twl4030.o
 obj-$(CONFIG_GPIO_TWL6040)	+= gpio-twl6040.o
 obj-$(CONFIG_GPIO_TZ1090)	+= gpio-tz1090.o
+obj-$(CONFIG_GPIO_TZ1090_PDC)	+= gpio-tz1090-pdc.o
 obj-$(CONFIG_GPIO_UCB1400)	+= gpio-ucb1400.o
 obj-$(CONFIG_GPIO_VIPERBOARD)	+= gpio-viperboard.o
 obj-$(CONFIG_GPIO_VR41XX)	+= gpio-vr41xx.o
diff --git a/drivers/gpio/gpio-tz1090-pdc.c b/drivers/gpio/gpio-tz1090-pdc.c
new file mode 100644
index 0000000..f512da2
--- /dev/null
+++ b/drivers/gpio/gpio-tz1090-pdc.c
@@ -0,0 +1,243 @@
+/*
+ * Toumaz Xenif TZ1090 PDC GPIO handling.
+ *
+ * Copyright (C) 2012-2013 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <asm/global_lock.h>
+
+/* Register offsets from SOC_GPIO_CONTROL0 */
+#define REG_SOC_GPIO_CONTROL0	0x00
+#define REG_SOC_GPIO_CONTROL1	0x04
+#define REG_SOC_GPIO_CONTROL2	0x08
+#define REG_SOC_GPIO_CONTROL3	0x0c
+#define REG_SOC_GPIO_STATUS	0x80
+
+/* PDC GPIOs go after normal GPIOs */
+#define GPIO_PDC_BASE		90
+#define GPIO_PDC_NGPIO		7
+
+/* Out of PDC gpios, only syswakes have irqs */
+#define GPIO_PDC_IRQ_FIRST	2
+#define GPIO_PDC_NIRQ		3
+
+/**
+ * struct tz1090_pdc_gpio - GPIO bank private data
+ * @chip:	Generic GPIO chip for GPIO bank
+ * @reg:	Base of registers, offset for this GPIO bank
+ * @irq:	IRQ numbers for Syswake GPIOs
+ *
+ * This is the main private data for the PDC GPIO driver. It encapsulates a
+ * gpio_chip, and the callbacks for the gpio_chip can access the private data
+ * with the to_pdc() macro below.
+ */
+struct tz1090_pdc_gpio {
+	struct gpio_chip chip;
+	void __iomem *reg;
+	int irq[GPIO_PDC_NIRQ];
+};
+#define to_pdc(c)	container_of(c, struct tz1090_pdc_gpio, chip)
+
+/* Register accesses into the PDC MMIO area */
+
+static inline void pdc_write(struct tz1090_pdc_gpio *priv, unsigned int reg_offs,
+		      unsigned int data)
+{
+	writel(data, priv->reg + reg_offs);
+}
+
+static inline unsigned int pdc_read(struct tz1090_pdc_gpio *priv,
+			     unsigned int reg_offs)
+{
+	return readl(priv->reg + reg_offs);
+}
+
+/* Generic GPIO interface */
+
+static int tz1090_pdc_gpio_direction_input(struct gpio_chip *chip,
+					   unsigned int offset)
+{
+	struct tz1090_pdc_gpio *priv = to_pdc(chip);
+	u32 value;
+	int lstat;
+
+	__global_lock2(lstat);
+	value = pdc_read(priv, REG_SOC_GPIO_CONTROL1);
+	value |= BIT(offset);
+	pdc_write(priv, REG_SOC_GPIO_CONTROL1, value);
+	__global_unlock2(lstat);
+
+	return 0;
+}
+
+static int tz1090_pdc_gpio_direction_output(struct gpio_chip *chip,
+					    unsigned int offset,
+					    int output_value)
+{
+	struct tz1090_pdc_gpio *priv = to_pdc(chip);
+	u32 value;
+	int lstat;
+
+	__global_lock2(lstat);
+	/* EXT_POWER doesn't seem to have an output value bit */
+	if (offset < 6) {
+		value = pdc_read(priv, REG_SOC_GPIO_CONTROL0);
+		if (output_value)
+			value |= BIT(offset);
+		else
+			value &= ~BIT(offset);
+		pdc_write(priv, REG_SOC_GPIO_CONTROL0, value);
+	}
+
+	value = pdc_read(priv, REG_SOC_GPIO_CONTROL1);
+	value &= ~BIT(offset);
+	pdc_write(priv, REG_SOC_GPIO_CONTROL1, value);
+	__global_unlock2(lstat);
+
+	return 0;
+}
+
+static int tz1090_pdc_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+	struct tz1090_pdc_gpio *priv = to_pdc(chip);
+	return pdc_read(priv, REG_SOC_GPIO_STATUS) & BIT(offset);
+}
+
+static void tz1090_pdc_gpio_set(struct gpio_chip *chip, unsigned int offset,
+				int output_value)
+{
+	struct tz1090_pdc_gpio *priv = to_pdc(chip);
+	u32 value;
+	int lstat;
+
+	/* EXT_POWER doesn't seem to have an output value bit */
+	if (offset >= 6)
+		return;
+
+	__global_lock2(lstat);
+	value = pdc_read(priv, REG_SOC_GPIO_CONTROL0);
+	if (output_value)
+		value |= BIT(offset);
+	else
+		value &= ~BIT(offset);
+	pdc_write(priv, REG_SOC_GPIO_CONTROL0, value);
+	__global_unlock2(lstat);
+}
+
+static int tz1090_pdc_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+	return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void tz1090_pdc_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+	pinctrl_free_gpio(chip->base + offset);
+}
+
+static int tz1090_pdc_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+	struct tz1090_pdc_gpio *priv = to_pdc(chip);
+	unsigned int syswake = offset - GPIO_PDC_IRQ_FIRST;
+	int irq;
+
+	/* only syswakes have irqs */
+	if (syswake >= GPIO_PDC_NIRQ)
+		return -EINVAL;
+
+	irq = priv->irq[syswake];
+	if (!irq)
+		return -EINVAL;
+
+	return irq;
+}
+
+static int tz1090_pdc_gpio_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct resource *res_regs;
+	struct tz1090_pdc_gpio *priv;
+	unsigned int i;
+
+	if (!np) {
+		dev_err(&pdev->dev, "must be instantiated via devicetree\n");
+		return -ENOENT;
+	}
+
+	res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res_regs) {
+		dev_err(&pdev->dev, "cannot find registers resource\n");
+		return -ENOENT;
+	}
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&pdev->dev, "unable to allocate driver data\n");
+		return -ENOMEM;
+	}
+
+	/* Ioremap the registers */
+	priv->reg = devm_ioremap(&pdev->dev, res_regs->start,
+				 res_regs->end - res_regs->start);
+	if (!priv->reg) {
+		dev_err(&pdev->dev, "unable to ioremap registers\n");
+		return -ENOMEM;
+	}
+
+	/* Set up GPIO chip */
+	priv->chip.label		= "tz1090-pdc-gpio";
+	priv->chip.dev			= &pdev->dev;
+	priv->chip.direction_input	= tz1090_pdc_gpio_direction_input;
+	priv->chip.direction_output	= tz1090_pdc_gpio_direction_output;
+	priv->chip.get			= tz1090_pdc_gpio_get;
+	priv->chip.set			= tz1090_pdc_gpio_set;
+	priv->chip.free			= tz1090_pdc_gpio_free;
+	priv->chip.request		= tz1090_pdc_gpio_request;
+	priv->chip.to_irq		= tz1090_pdc_gpio_to_irq;
+	priv->chip.of_node		= np;
+
+	/* GPIO numbering */
+	priv->chip.base			= GPIO_PDC_BASE;
+	priv->chip.ngpio		= GPIO_PDC_NGPIO;
+
+	/* Map the syswake irqs */
+	for (i = 0; i < GPIO_PDC_NIRQ; ++i)
+		priv->irq[i] = irq_of_parse_and_map(np, i);
+
+	/* Add the GPIO bank */
+	gpiochip_add(&priv->chip);
+
+	return 0;
+}
+
+static struct of_device_id tz1090_pdc_gpio_of_match[] = {
+	{ .compatible = "img,tz1090-pdc-gpio" },
+	{ },
+};
+
+static struct platform_driver tz1090_pdc_gpio_driver = {
+	.driver = {
+		.name		= "tz1090-pdc-gpio",
+		.owner		= THIS_MODULE,
+		.of_match_table	= tz1090_pdc_gpio_of_match,
+	},
+	.probe		= tz1090_pdc_gpio_probe,
+};
+
+static int __init tz1090_pdc_gpio_init(void)
+{
+	return platform_driver_register(&tz1090_pdc_gpio_driver);
+}
+subsys_initcall(tz1090_pdc_gpio_init);
-- 
1.8.1.2



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

* Re: [PATCH v3 2/4] gpio-tz1090: add TZ1090 gpio driver
  2013-06-20  9:26 ` [PATCH v3 2/4] gpio-tz1090: add TZ1090 gpio driver James Hogan
@ 2013-06-24 13:34   ` Grant Likely
  2013-06-24 14:48     ` James Hogan
  0 siblings, 1 reply; 12+ messages in thread
From: Grant Likely @ 2013-06-24 13:34 UTC (permalink / raw)
  To: James Hogan, Linus Walleij
  Cc: linux-kernel, James Hogan, Rob Herring, Rob Landley, linux-doc,
	devicetree-discuss

On Thu, 20 Jun 2013 10:26:28 +0100, James Hogan <james.hogan@imgtec.com> wrote:
> Add a GPIO driver for the main GPIOs found in the TZ1090 (Comet) SoC.
> This doesn't include low-power GPIOs as they're controlled separately
> via the Powerdown Controller (PDC) registers.
> 
> The driver is instantiated by device tree and supports interrupts for
> all GPIOs.
> 
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> Cc: Grant Likely <grant.likely@linaro.org>
> Cc: Rob Herring <rob.herring@calxeda.com>
> Cc: Rob Landley <rob@landley.net>
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Cc: linux-doc@vger.kernel.org
> Cc: devicetree-discuss@lists.ozlabs.org
> ---
> Changes in v3:
>  - separated from irq-imgpdc and removed arch/metag changes to allow
>    these patches to go upstream separately via the pinctrl[/gpio] trees
>    (particularly the pinctrl drivers depend on the new pinconf DT
>    bindings).
>  - some s/unsigned/unsigned int/.
>  - some s/unsigned int/bool/ and use of BIT().
>  - gpio-tz1090*: refer to <dt-bindings/gpio/gpio.h> and
>    <dt-bindings/interrupt-controller/irq.h> flags in bindings.
>  - gpio-tz1090*: move initcall from postcore to subsys.
>  - gpio-tz1090: add REG_ prefix to some constants for consistency.
>  - gpio-tz1090: add comment to explain tz1090_gpio_irq_next_edge
>    cunningness.
> 
> Changes in v2:
>  - gpio-tz1090: remove references to Linux flags in dt bindings
>  - gpio-tz1090: make use of BIT() from linux/bitops.h
>  - gpio-tz1090: make register accessors inline to match pinctrl
>  - gpio-tz1090: update gpio-ranges to use 3 cells after recent ABI
>    breakage
> 
>  .../devicetree/bindings/gpio/gpio-tz1090.txt       |  87 +++
>  drivers/gpio/Kconfig                               |   7 +
>  drivers/gpio/Makefile                              |   1 +
>  drivers/gpio/gpio-tz1090.c                         | 633 +++++++++++++++++++++
>  4 files changed, 728 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
>  create mode 100644 drivers/gpio/gpio-tz1090.c
> 
> diff --git a/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt b/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
> new file mode 100644
> index 0000000..e017d4b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
> @@ -0,0 +1,87 @@
> +ImgTec TZ1090 GPIO Controller
> +
> +Required properties:
> +- compatible: Compatible property value should be "img,tz1090-gpio>".

typo at end of line

> +
> +- reg: Physical base address of the controller and length of memory mapped
> +  region.
> +
> +- #address-cells: Should be 1 (for bank subnodes)
> +
> +- #size-cells: Should be 0 (for bank subnodes)
> +
> +- Each bank of GPIOs should have a subnode to represent it.
> +
> +  Bank subnode required properties:
> +  - reg: Index of bank in the range 0 to 2.
> +
> +  - gpio-controller: Specifies that the node is a gpio controller.
> +
> +  - #gpio-cells: Should be 2. The syntax of the gpio specifier used by client
> +    nodes should have the following values.
> +       <[phandle of the gpio controller node]
> +        [gpio number within the gpio bank]
> +        [gpio flags]>
> +
> +    Values for gpio specifier:
> +    - GPIO number: a value in the range 0 to 29.
> +    - GPIO flags: bit field of flags, as defined in <dt-bindings/gpio/gpio.h>.
> +      Only the following flags are supported:
> +        GPIO_ACTIVE_HIGH
> +        GPIO_ACTIVE_LOW
> +
> +  Bank subnode optional properties:
> +  - gpio-ranges: Mapping to pin controller pins

This is specific to this binding. To avoid namespace colisions, add a
"img," prefix to the property name.

> +
> +  - interrupts: Interrupt for the entire bank
> +
> +  - interrupt-controller: Specifies that the node is an interrupt controller
> +
> +  - #interrupt-cells: Should be 2. The syntax of the interrupt specifier used by
> +    client nodes should have the following values.
> +       <[phandle of the interurupt controller]
> +        [gpio number within the gpio bank]
> +        [irq flags]>
> +
> +    Values for irq specifier:
> +    - GPIO number: a value in the range 0 to 29
> +    - IRQ flags: value to describe edge and level triggering, as defined in
> +      <dt-bindings/interrupt-controller/irq.h>. Only the following flags are
> +      supported:
> +        IRQ_TYPE_EDGE_RISING
> +        IRQ_TYPE_EDGE_FALLING
> +        IRQ_TYPE_EDGE_BOTH
> +        IRQ_TYPE_LEVEL_HIGH
> +        IRQ_TYPE_LEVEL_LOW
> +
> +
> +
> +Example:
> +
> +	gpios: gpio-controller@02005800 {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		compatible = "img,tz1090-gpio";
> +		reg = <0x02005800 0x90>;
> +
> +		/* bank 0 with an interrupt */
> +		gpios0: bank@0 {
> +			#gpio-cells = <2>;
> +			#interrupt-cells = <2>;
> +			reg = <0>;
> +			interrupts = <13 IRQ_TYPE_LEVEL_HIGH>;
> +			gpio-controller;
> +			gpio-ranges = <&pinctrl 0 30>;
> +			interrupt-controller;
> +		};
> +
> +		/* bank 2 without interrupt */
> +		gpios2: bank@2 {
> +			#gpio-cells = <2>;
> +			reg = <2>;
> +			gpio-controller;
> +			gpio-ranges = <&pinctrl 60 30>;
> +		};
> +	};
> +
> +
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 573c449..ee27c2e 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -232,6 +232,13 @@ config GPIO_TS5500
>  	  blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
>  	  LCD port.
>  
> +config GPIO_TZ1090
> +	bool "Toumaz Xenif TZ1090 GPIO support"
> +	depends on SOC_TZ1090
> +	default y
> +	help
> +	  Say yes here to support Toumaz Xenif TZ1090 GPIOs.
> +
>  config GPIO_XILINX
>  	bool "Xilinx GPIO support"
>  	depends on PPC_OF || MICROBLAZE
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 0cb2d65..37bdc1e 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -79,6 +79,7 @@ obj-$(CONFIG_GPIO_TPS65912)	+= gpio-tps65912.o
>  obj-$(CONFIG_GPIO_TS5500)	+= gpio-ts5500.o
>  obj-$(CONFIG_GPIO_TWL4030)	+= gpio-twl4030.o
>  obj-$(CONFIG_GPIO_TWL6040)	+= gpio-twl6040.o
> +obj-$(CONFIG_GPIO_TZ1090)	+= gpio-tz1090.o
>  obj-$(CONFIG_GPIO_UCB1400)	+= gpio-ucb1400.o
>  obj-$(CONFIG_GPIO_VIPERBOARD)	+= gpio-viperboard.o
>  obj-$(CONFIG_GPIO_VR41XX)	+= gpio-vr41xx.o
> diff --git a/drivers/gpio/gpio-tz1090.c b/drivers/gpio/gpio-tz1090.c
> new file mode 100644
> index 0000000..099a9ef
> --- /dev/null
> +++ b/drivers/gpio/gpio-tz1090.c
> @@ -0,0 +1,633 @@
> +/*
> + * Toumaz Xenif TZ1090 GPIO handling.
> + *
> + * Copyright (C) 2008-2013 Imagination Technologies Ltd.
> + *
> + *  Based on ARM PXA code and others.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/export.h>
> +#include <linux/gpio.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/kernel.h>
> +#include <linux/of_irq.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/syscore_ops.h>
> +#include <asm/global_lock.h>
> +
> +/* Register offsets from bank base address */
> +#define REG_GPIO_DIR		0x00
> +#define REG_GPIO_IRQ_PLRT	0x20
> +#define REG_GPIO_IRQ_TYPE	0x30
> +#define REG_GPIO_IRQ_EN		0x40
> +#define REG_GPIO_IRQ_STS	0x50
> +#define REG_GPIO_BIT_EN		0x60
> +#define REG_GPIO_DIN		0x70
> +#define REG_GPIO_DOUT		0x80
> +
> +/* REG_GPIO_IRQ_PLRT */
> +#define REG_GPIO_IRQ_PLRT_LOW	0
> +#define REG_GPIO_IRQ_PLRT_HIGH	1
> +
> +/* REG_GPIO_IRQ_TYPE */
> +#define REG_GPIO_IRQ_TYPE_LEVEL	0
> +#define REG_GPIO_IRQ_TYPE_EDGE	1
> +
> +/**
> + * struct tz1090_gpio_bank - GPIO bank private data
> + * @chip:	Generic GPIO chip for GPIO bank
> + * @domain:	IRQ domain for GPIO bank (may be NULL)
> + * @reg:	Base of registers, offset for this GPIO bank
> + * @irq:	IRQ number for GPIO bank
> + * @label:	Debug GPIO bank label, used for storage of chip->label
> + *
> + * This is the main private data for a GPIO bank. It encapsulates a gpio_chip,
> + * and the callbacks for the gpio_chip can access the private data with the
> + * to_bank() macro below.
> + */
> +struct tz1090_gpio_bank {
> +	struct gpio_chip chip;
> +	struct irq_domain *domain;
> +	void __iomem *reg;
> +	int irq;
> +	char label[16];
> +};
> +#define to_bank(c)	container_of(c, struct tz1090_gpio_bank, chip)
> +
> +/**
> + * struct tz1090_gpio - Overall GPIO device private data
> + * @dev:	Device (from platform device)
> + * @reg:	Base of GPIO registers
> + *
> + * Represents the overall GPIO device. This structure is actually only
> + * temporary, and used during init.
> + */
> +struct tz1090_gpio {
> +	struct device *dev;
> +	void __iomem *reg;
> +};
> +
> +/**
> + * struct tz1090_gpio_bank_info - Temporary registration info for GPIO bank
> + * @priv:	Overall GPIO device private data
> + * @node:	Device tree node specific to this GPIO bank
> + * @index:	Index of bank in range 0-2
> + */
> +struct tz1090_gpio_bank_info {
> +	struct tz1090_gpio *priv;
> +	struct device_node *node;
> +	unsigned int index;
> +};
> +
> +/* Convenience register accessors */
> +static inline void tz1090_gpio_write(struct tz1090_gpio_bank *bank,
> +			      unsigned int reg_offs, u32 data)
> +{
> +	iowrite32(data, bank->reg + reg_offs);
> +}
> +
> +static inline u32 tz1090_gpio_read(struct tz1090_gpio_bank *bank,
> +			    unsigned int reg_offs)
> +{
> +	return ioread32(bank->reg + reg_offs);
> +}
> +
> +/* caller must hold LOCK2 */
> +static inline void _tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
> +					  unsigned int reg_offs,
> +					  unsigned int offset)
> +{
> +	u32 value;
> +
> +	value = tz1090_gpio_read(bank, reg_offs);
> +	value &= ~BIT(offset);
> +	tz1090_gpio_write(bank, reg_offs, value);
> +}
> +
> +static void tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
> +				  unsigned int reg_offs,
> +				  unsigned int offset)
> +{
> +	int lstat;
> +
> +	__global_lock2(lstat);
> +	_tz1090_gpio_clear_bit(bank, reg_offs, offset);
> +	__global_unlock2(lstat);
> +}
> +
> +/* caller must hold LOCK2 */
> +static inline void _tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
> +					unsigned int reg_offs,
> +					unsigned int offset)
> +{
> +	u32 value;
> +
> +	value = tz1090_gpio_read(bank, reg_offs);
> +	value |= BIT(offset);
> +	tz1090_gpio_write(bank, reg_offs, value);
> +}
> +
> +static void tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
> +				unsigned int reg_offs,
> +				unsigned int offset)
> +{
> +	int lstat;
> +
> +	__global_lock2(lstat);
> +	_tz1090_gpio_set_bit(bank, reg_offs, offset);
> +	__global_unlock2(lstat);
> +}
> +
> +/* caller must hold LOCK2 */
> +static inline void _tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
> +					unsigned int reg_offs,
> +					unsigned int offset,
> +					bool val)
> +{
> +	u32 value;
> +
> +	value = tz1090_gpio_read(bank, reg_offs);
> +	value &= ~BIT(offset);
> +	if (val)
> +		value |= BIT(offset);
> +	tz1090_gpio_write(bank, reg_offs, value);
> +}
> +
> +static void tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
> +				unsigned int reg_offs,
> +				unsigned int offset,
> +				bool val)
> +{
> +	int lstat;
> +
> +	__global_lock2(lstat);
> +	_tz1090_gpio_mod_bit(bank, reg_offs, offset, val);
> +	__global_unlock2(lstat);
> +}
> +
> +static inline int tz1090_gpio_read_bit(struct tz1090_gpio_bank *bank,
> +				       unsigned int reg_offs,
> +				       unsigned int offset)
> +{
> +	return tz1090_gpio_read(bank, reg_offs) & BIT(offset);
> +}
> +
> +/* GPIO chip callbacks */
> +
> +static int tz1090_gpio_direction_input(struct gpio_chip *chip,
> +				       unsigned int offset)
> +{
> +	struct tz1090_gpio_bank *bank = to_bank(chip);
> +	tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
> +
> +	return 0;
> +}
> +
> +static int tz1090_gpio_direction_output(struct gpio_chip *chip,
> +					unsigned int offset, int output_value)
> +{
> +	struct tz1090_gpio_bank *bank = to_bank(chip);
> +	int lstat;
> +
> +	__global_lock2(lstat);
> +	_tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
> +	_tz1090_gpio_clear_bit(bank, REG_GPIO_DIR, offset);
> +	__global_unlock2(lstat);
> +
> +	return 0;
> +}
> +
> +/*
> + * Return GPIO level
> + */
> +static int tz1090_gpio_get(struct gpio_chip *chip, unsigned int offset)
> +{
> +	struct tz1090_gpio_bank *bank = to_bank(chip);
> +
> +	return tz1090_gpio_read_bit(bank, REG_GPIO_DIN, offset);
> +}
> +
> +/*
> + * Set output GPIO level
> + */
> +static void tz1090_gpio_set(struct gpio_chip *chip, unsigned int offset,
> +			    int output_value)
> +{
> +	struct tz1090_gpio_bank *bank = to_bank(chip);
> +
> +	tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
> +}
> +
> +static int tz1090_gpio_request(struct gpio_chip *chip, unsigned int offset)
> +{
> +	struct tz1090_gpio_bank *bank = to_bank(chip);
> +	int ret;
> +
> +	ret = pinctrl_request_gpio(chip->base + offset);
> +	if (ret)
> +		return ret;
> +
> +	tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
> +	tz1090_gpio_set_bit(bank, REG_GPIO_BIT_EN, offset);
> +
> +	return 0;
> +}

Is it possible to use the gpio-generic.c hooks for manipulating the
gpio bits?

> +
> +static void tz1090_gpio_free(struct gpio_chip *chip, unsigned int offset)
> +{
> +	struct tz1090_gpio_bank *bank = to_bank(chip);
> +
> +	pinctrl_free_gpio(chip->base + offset);
> +
> +	tz1090_gpio_clear_bit(bank, REG_GPIO_BIT_EN, offset);
> +}
> +
> +static int tz1090_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
> +{
> +	struct tz1090_gpio_bank *bank = to_bank(chip);
> +
> +	if (!bank->domain)
> +		return -EINVAL;
> +
> +	return irq_create_mapping(bank->domain, offset);
> +}
> +
> +/* IRQ chip handlers */
> +
> +/* Get TZ1090 GPIO chip from irq data provided to generic IRQ callbacks */
> +static inline struct tz1090_gpio_bank *irqd_to_gpio_bank(struct irq_data *data)
> +{
> +	return (struct tz1090_gpio_bank *)data->domain->host_data;
> +}
> +
> +static void tz1090_gpio_irq_clear(struct tz1090_gpio_bank *bank,
> +				  unsigned int offset)
> +{
> +	tz1090_gpio_clear_bit(bank, REG_GPIO_IRQ_STS, offset);
> +}
> +
> +static void tz1090_gpio_irq_enable(struct tz1090_gpio_bank *bank,
> +				   unsigned int offset, bool enable)
> +{
> +	tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_EN, offset, enable);
> +}
> +
> +static void tz1090_gpio_irq_polarity(struct tz1090_gpio_bank *bank,
> +				     unsigned int offset, unsigned int polarity)
> +{
> +	tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_PLRT, offset, polarity);
> +}
> +
> +static int tz1090_gpio_valid_handler(struct irq_desc *desc)
> +{
> +	return desc->handle_irq == handle_level_irq ||
> +		desc->handle_irq == handle_edge_irq;
> +}
> +
> +static void tz1090_gpio_irq_type(struct tz1090_gpio_bank *bank,
> +				 unsigned int offset, unsigned int type)
> +{
> +	tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_TYPE, offset, type);
> +}
> +
> +/* set polarity to trigger on next edge, whether rising or falling */
> +static void tz1090_gpio_irq_next_edge(struct tz1090_gpio_bank *bank,
> +				      unsigned int offset)
> +{
> +	unsigned int value_p, value_i;
> +	int lstat;
> +
> +	/*
> +	 * Set the GPIO's interrupt polarity to the opposite of the current
> +	 * input value so that the next edge triggers an interrupt.
> +	 */
> +	__global_lock2(lstat);
> +	value_i = ~tz1090_gpio_read(bank, REG_GPIO_DIN);
> +	value_p = tz1090_gpio_read(bank, REG_GPIO_IRQ_PLRT);
> +	value_p &= ~BIT(offset);
> +	value_p |= value_i & BIT(offset);
> +	tz1090_gpio_write(bank, REG_GPIO_IRQ_PLRT, value_p);
> +	__global_unlock2(lstat);
> +}
> +
> +static void gpio_ack_irq(struct irq_data *data)
> +{
> +	struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
> +
> +	tz1090_gpio_irq_clear(bank, data->hwirq);
> +}
> +
> +static void gpio_mask_irq(struct irq_data *data)
> +{
> +	struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
> +
> +	tz1090_gpio_irq_enable(bank, data->hwirq, false);
> +}
> +
> +static void gpio_unmask_irq(struct irq_data *data)
> +{
> +	struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
> +
> +	tz1090_gpio_irq_enable(bank, data->hwirq, true);
> +}

Similarly, can this driver use the generic irq chip to eliminate the
above hooks?

[...]
> +
> +static void tz1090_gpio_register_banks(struct tz1090_gpio *priv)
> +{
> +	struct device_node *np = priv->dev->of_node;
> +	struct device_node *node;
> +
> +	for_each_available_child_of_node(np, node) {
> +		struct tz1090_gpio_bank_info info;
> +		const __be32 *addr;
> +		int len, ret;
> +
> +		addr = of_get_property(node, "reg", &len);
> +		if (!addr || (len < sizeof(int))) {
> +			dev_err(priv->dev, "invalid reg on %s\n",
> +				node->full_name);
> +			continue;
> +		}

Use of_property_read_u32(). It's safer and does the be32 conversion for you.

> +
> +		info.index = be32_to_cpup(addr);
> +		if (info.index >= 3) {
> +			dev_err(priv->dev, "index %u in %s out of range\n",
> +				info.index, node->full_name);
> +			continue;
> +		}
> +		info.node = of_node_get(node);
> +		info.priv = priv;
> +
> +		ret = tz1090_gpio_bank_probe(&info);
> +		if (ret) {
> +			dev_err(priv->dev, "failure registering %s\n",
> +				node->full_name);
> +			of_node_put(node);
> +			continue;
> +		}
> +	}
> +}
> +
> +static int tz1090_gpio_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct resource *res_regs;
> +	struct tz1090_gpio priv;
> +
> +	if (!np) {
> +		dev_err(&pdev->dev, "must be instantiated via devicetree\n");
> +		return -ENOENT;
> +	}
> +
> +	res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res_regs) {
> +		dev_err(&pdev->dev, "cannot find registers resource\n");
> +		return -ENOENT;
> +	}
> +
> +	priv.dev = &pdev->dev;
> +
> +	/* Ioremap the registers */
> +	priv.reg = devm_ioremap(&pdev->dev, res_regs->start,
> +				 res_regs->end - res_regs->start);
> +	if (!priv.reg) {
> +		dev_err(&pdev->dev, "unable to ioremap registers\n");
> +		return -ENOMEM;
> +	}
> +
> +	/* Look for banks */
> +	tz1090_gpio_register_banks(&priv);
> +
> +	return 0;
> +}
> +
> +static struct of_device_id tz1090_gpio_of_match[] = {
> +	{ .compatible = "img,tz1090-gpio" },
> +	{ },
> +};
> +
> +static struct platform_driver tz1090_gpio_driver = {
> +	.driver = {
> +		.name		= "tz1090-gpio",
> +		.owner		= THIS_MODULE,
> +		.of_match_table	= tz1090_gpio_of_match,
> +	},
> +	.probe		= tz1090_gpio_probe,
> +};
> +
> +static int __init tz1090_gpio_init(void)
> +{
> +	return platform_driver_register(&tz1090_gpio_driver);
> +}
> +subsys_initcall(tz1090_gpio_init);
> -- 
> 1.8.1.2
> 
> 

-- 
email sent from notmuch.vim plugin

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

* Re: [PATCH v3 2/4] gpio-tz1090: add TZ1090 gpio driver
  2013-06-24 13:34   ` Grant Likely
@ 2013-06-24 14:48     ` James Hogan
  2013-06-24 15:36       ` James Hogan
  0 siblings, 1 reply; 12+ messages in thread
From: James Hogan @ 2013-06-24 14:48 UTC (permalink / raw)
  To: Grant Likely
  Cc: Linus Walleij, linux-kernel, Rob Herring, Rob Landley, linux-doc,
	devicetree-discuss

On 24/06/13 14:34, Grant Likely wrote:
> On Thu, 20 Jun 2013 10:26:28 +0100, James Hogan <james.hogan@imgtec.com> wrote:
>> diff --git a/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt b/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
>> new file mode 100644
>> index 0000000..e017d4b
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
>> @@ -0,0 +1,87 @@
>> +ImgTec TZ1090 GPIO Controller
>> +
>> +Required properties:
>> +- compatible: Compatible property value should be "img,tz1090-gpio>".
> 
> typo at end of line

Yes, I'll fix in gpio-tz1090-pdc driver bindings too

>> +  Bank subnode optional properties:
>> +  - gpio-ranges: Mapping to pin controller pins
> 
> This is specific to this binding. To avoid namespace colisions, add a
> "img," prefix to the property name.

This property is described in
Documentation/devicetree/bindings/gpio/gpio.txt... (and my examples are
out of date from when the gpio offset cell was added in v3.10). I'll add
a reference to that Document.

>> +/* GPIO chip callbacks */
>> +
>> +static int tz1090_gpio_direction_input(struct gpio_chip *chip,
>> +				       unsigned int offset)
>> +{
>> +	struct tz1090_gpio_bank *bank = to_bank(chip);
>> +	tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
>> +
>> +	return 0;
>> +}
>> +
>> +static int tz1090_gpio_direction_output(struct gpio_chip *chip,
>> +					unsigned int offset, int output_value)
>> +{
>> +	struct tz1090_gpio_bank *bank = to_bank(chip);
>> +	int lstat;
>> +
>> +	__global_lock2(lstat);
>> +	_tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
>> +	_tz1090_gpio_clear_bit(bank, REG_GPIO_DIR, offset);
>> +	__global_unlock2(lstat);
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * Return GPIO level
>> + */
>> +static int tz1090_gpio_get(struct gpio_chip *chip, unsigned int offset)
>> +{
>> +	struct tz1090_gpio_bank *bank = to_bank(chip);
>> +
>> +	return tz1090_gpio_read_bit(bank, REG_GPIO_DIN, offset);
>> +}
>> +
>> +/*
>> + * Set output GPIO level
>> + */
>> +static void tz1090_gpio_set(struct gpio_chip *chip, unsigned int offset,
>> +			    int output_value)
>> +{
>> +	struct tz1090_gpio_bank *bank = to_bank(chip);
>> +
>> +	tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
>> +}
>> +
>> +static int tz1090_gpio_request(struct gpio_chip *chip, unsigned int offset)
>> +{
>> +	struct tz1090_gpio_bank *bank = to_bank(chip);
>> +	int ret;
>> +
>> +	ret = pinctrl_request_gpio(chip->base + offset);
>> +	if (ret)
>> +		return ret;
>> +
>> +	tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
>> +	tz1090_gpio_set_bit(bank, REG_GPIO_BIT_EN, offset);
>> +
>> +	return 0;
>> +}
> 
> Is it possible to use the gpio-generic.c hooks for manipulating the
> gpio bits?

Due to the unfortunate necessity to use the __global_lock2 functions
(for atomic accesses between different non-linux threads/cores) I don't
think this is possible.

>> +/* IRQ chip handlers */
>> +
>> +/* Get TZ1090 GPIO chip from irq data provided to generic IRQ callbacks */
>> +static inline struct tz1090_gpio_bank *irqd_to_gpio_bank(struct irq_data *data)
>> +{
>> +	return (struct tz1090_gpio_bank *)data->domain->host_data;
>> +}
>> +
>> +static void tz1090_gpio_irq_clear(struct tz1090_gpio_bank *bank,
>> +				  unsigned int offset)
>> +{
>> +	tz1090_gpio_clear_bit(bank, REG_GPIO_IRQ_STS, offset);
>> +}
>> +
>> +static void tz1090_gpio_irq_enable(struct tz1090_gpio_bank *bank,
>> +				   unsigned int offset, bool enable)
>> +{
>> +	tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_EN, offset, enable);
>> +}
>> +
>> +static void tz1090_gpio_irq_polarity(struct tz1090_gpio_bank *bank,
>> +				     unsigned int offset, unsigned int polarity)
>> +{
>> +	tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_PLRT, offset, polarity);
>> +}
>> +
>> +static int tz1090_gpio_valid_handler(struct irq_desc *desc)
>> +{
>> +	return desc->handle_irq == handle_level_irq ||
>> +		desc->handle_irq == handle_edge_irq;
>> +}
>> +
>> +static void tz1090_gpio_irq_type(struct tz1090_gpio_bank *bank,
>> +				 unsigned int offset, unsigned int type)
>> +{
>> +	tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_TYPE, offset, type);
>> +}
>> +
>> +/* set polarity to trigger on next edge, whether rising or falling */
>> +static void tz1090_gpio_irq_next_edge(struct tz1090_gpio_bank *bank,
>> +				      unsigned int offset)
>> +{
>> +	unsigned int value_p, value_i;
>> +	int lstat;
>> +
>> +	/*
>> +	 * Set the GPIO's interrupt polarity to the opposite of the current
>> +	 * input value so that the next edge triggers an interrupt.
>> +	 */
>> +	__global_lock2(lstat);
>> +	value_i = ~tz1090_gpio_read(bank, REG_GPIO_DIN);
>> +	value_p = tz1090_gpio_read(bank, REG_GPIO_IRQ_PLRT);
>> +	value_p &= ~BIT(offset);
>> +	value_p |= value_i & BIT(offset);
>> +	tz1090_gpio_write(bank, REG_GPIO_IRQ_PLRT, value_p);
>> +	__global_unlock2(lstat);
>> +}
>> +
>> +static void gpio_ack_irq(struct irq_data *data)
>> +{
>> +	struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
>> +
>> +	tz1090_gpio_irq_clear(bank, data->hwirq);
>> +}
>> +
>> +static void gpio_mask_irq(struct irq_data *data)
>> +{
>> +	struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
>> +
>> +	tz1090_gpio_irq_enable(bank, data->hwirq, false);
>> +}
>> +
>> +static void gpio_unmask_irq(struct irq_data *data)
>> +{
>> +	struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
>> +
>> +	tz1090_gpio_irq_enable(bank, data->hwirq, true);
>> +}
> 
> Similarly, can this driver use the generic irq chip to eliminate the
> above hooks?

hmm, I could probably get away with it for irq callbacks since a bank's
IRQ cannot be shared with non-Linux threads/cores.

> 
> [...]
>> +
>> +static void tz1090_gpio_register_banks(struct tz1090_gpio *priv)
>> +{
>> +	struct device_node *np = priv->dev->of_node;
>> +	struct device_node *node;
>> +
>> +	for_each_available_child_of_node(np, node) {
>> +		struct tz1090_gpio_bank_info info;
>> +		const __be32 *addr;
>> +		int len, ret;
>> +
>> +		addr = of_get_property(node, "reg", &len);
>> +		if (!addr || (len < sizeof(int))) {
>> +			dev_err(priv->dev, "invalid reg on %s\n",
>> +				node->full_name);
>> +			continue;
>> +		}
> 
> Use of_property_read_u32(). It's safer and does the be32 conversion for you.

will do.

Thanks for the review.

Cheers
James


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

* Re: [PATCH v3 1/4] pinctrl-tz1090: add TZ1090 pinctrl driver
  2013-06-20  9:26 ` [PATCH v3 1/4] pinctrl-tz1090: add TZ1090 pinctrl driver James Hogan
@ 2013-06-24 15:04   ` Linus Walleij
  2013-06-24 15:38     ` James Hogan
  0 siblings, 1 reply; 12+ messages in thread
From: Linus Walleij @ 2013-06-24 15:04 UTC (permalink / raw)
  To: James Hogan
  Cc: linux-kernel, Grant Likely, Rob Herring, Rob Landley, linux-doc,
	devicetree-discuss

On Thu, Jun 20, 2013 at 11:26 AM, James Hogan <james.hogan@imgtec.com> wrote:

> Add a pin control driver for the main pins on the TZ1090 SoC. This
> doesn't include the low-power pins as they're controlled separately via
> the Powerdown Controller (PDC) registers.
>
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> Cc: Grant Likely <grant.likely@linaro.org>
> Cc: Rob Herring <rob.herring@calxeda.com>
> Cc: Rob Landley <rob@landley.net>
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Cc: linux-doc@vger.kernel.org
> Cc: devicetree-discuss@lists.ozlabs.org
> ---
> Changes in v3:
>  - separated from irq-imgpdc and removed arch/metag changes to allow
>    these patches to go upstream separately via the pinctrl[/gpio] trees
>    (particularly the pinctrl drivers depend on the new pinconf DT
>    bindings).
>  - some s/unsigned/unsigned int/.
>  - some s/unsigned int/bool/ and use of BIT().
>  - pinctrl-tz1090*: switch to generic pinconfig DT bindings and
>    pinconf_generic_dump_config.
>  - pinctrl-tz1090*: use tz1090, prefix for pins and function in DT
>    bindings.
>  - pinctrl-tz1090*: make internal functions static.
>  - pinctrl-tz1090*: move initcall from postcore to arch.

Thanks James, patch applied.

Also thanks for helping out in getting the generic pinconf
in place for some three different drivers for this merge window,
this was very good for pinctrl at large.

Yours,
Linus Walleij

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

* Re: [PATCH v3 3/4] pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver
  2013-06-20  9:26 ` [PATCH v3 3/4] pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver James Hogan
@ 2013-06-24 15:08   ` Linus Walleij
  0 siblings, 0 replies; 12+ messages in thread
From: Linus Walleij @ 2013-06-24 15:08 UTC (permalink / raw)
  To: James Hogan
  Cc: linux-kernel, Grant Likely, Rob Herring, Rob Landley, linux-doc,
	devicetree-discuss

On Thu, Jun 20, 2013 at 11:26 AM, James Hogan <james.hogan@imgtec.com> wrote:

> Add a pin control driver for the TZ1090's low power pins via the
> powerdown controller SOC_GPIO_CONTROL registers.
>
> These pins have individually controlled pull-up, and group controlled
> schmitt, slew-rate, drive-strength, and power-on-start (pos).
>
> The pdc_gpio0 and pdc_gpio1 pins can also be muxed onto the
> ir_mod_stable_out and ir_mod_power_out functions respectively. If no
> function is set they remain in GPIO mode. These muxes can be overridden
> by requesting them as GPIOs.
>
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> Cc: Grant Likely <grant.likely@linaro.org>
> Cc: Rob Herring <rob.herring@calxeda.com>
> Cc: Rob Landley <rob@landley.net>
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Cc: linux-doc@vger.kernel.org
> Cc: devicetree-discuss@lists.ozlabs.org
> ---
> Changes in v3:
>  - separated from irq-imgpdc and removed arch/metag changes to allow
>    these patches to go upstream separately via the pinctrl[/gpio] trees
>    (particularly the pinctrl drivers depend on the new pinconf DT
>    bindings).
>  - some s/unsigned/unsigned int/.
>  - pinctrl-tz1090*: switch to generic pinconfig DT bindings and
>    pinconf_generic_dump_config.
>  - pinctrl-tz1090*: use tz1090, prefix for pins and function in DT
>    bindings.
>  - pinctrl-tz1090*: make internal functions static.
>  - pinctrl-tz1090*: move initcall from postcore to arch.

Thanks, patch applied as well.

Yours,
Linus Walleij

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

* Re: [PATCH v3 4/4] gpio-tz1090-pdc: add TZ1090 PDC gpio driver
  2013-06-20  9:26 ` [PATCH v3 4/4] gpio-tz1090-pdc: add TZ1090 PDC gpio driver James Hogan
@ 2013-06-24 15:11   ` Linus Walleij
  0 siblings, 0 replies; 12+ messages in thread
From: Linus Walleij @ 2013-06-24 15:11 UTC (permalink / raw)
  To: James Hogan
  Cc: linux-kernel, Grant Likely, Rob Herring, Rob Landley, linux-doc,
	devicetree-discuss

On Thu, Jun 20, 2013 at 11:26 AM, James Hogan <james.hogan@imgtec.com> wrote:

> Add a GPIO driver for the low-power Powerdown Controller GPIOs in the
> TZ1090 SoC.
>
> The driver is instantiated by device tree and supports interrupts for
> the SysWake GPIOs only.
>
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> Cc: Grant Likely <grant.likely@linaro.org>
> Cc: Rob Herring <rob.herring@calxeda.com>
> Cc: Rob Landley <rob@landley.net>
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Cc: linux-doc@vger.kernel.org
> Cc: devicetree-discuss@lists.ozlabs.org
> ---
> Changes in v3:
>  - separated from irq-imgpdc and removed arch/metag changes to allow
>    these patches to go upstream separately via the pinctrl[/gpio] trees
>    (particularly the pinctrl drivers depend on the new pinconf DT
>    bindings).
>  - some s/unsigned/unsigned int/.
>  - gpio-tz1090*: refer to <dt-bindings/gpio/gpio.h> and
>    <dt-bindings/interrupt-controller/irq.h> flags in bindings.
>  - gpio-tz1090*: move initcall from postcore to subsys.

I'll be happy to apply the GPIO patches to the pinctrl tree as well
once you fixed the comments on patch 2 from Grant.

Yours,
Linus Walleij

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

* Re: [PATCH v3 2/4] gpio-tz1090: add TZ1090 gpio driver
  2013-06-24 14:48     ` James Hogan
@ 2013-06-24 15:36       ` James Hogan
  0 siblings, 0 replies; 12+ messages in thread
From: James Hogan @ 2013-06-24 15:36 UTC (permalink / raw)
  To: Grant Likely, Linus Walleij
  Cc: linux-kernel, Rob Herring, Rob Landley, linux-doc, devicetree-discuss

On 24/06/13 15:48, James Hogan wrote:
> On 24/06/13 14:34, Grant Likely wrote:
>> Similarly, can this driver use the generic irq chip to eliminate the
>> above hooks?
> 
> hmm, I could probably get away with it for irq callbacks since a bank's
> IRQ cannot be shared with non-Linux threads/cores.

I just remembered, the commits that make the generic irqchip work with
linear irq domains are in tip/irq/core so this won't work on
pinctrl/devel or gpio/next branches.

How would you like that handled? I'm happy to write a patch to convert
to generic irqchip ready to be applied later (e.g. for v3.12).

Cheers
James


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

* Re: [PATCH v3 1/4] pinctrl-tz1090: add TZ1090 pinctrl driver
  2013-06-24 15:04   ` Linus Walleij
@ 2013-06-24 15:38     ` James Hogan
  0 siblings, 0 replies; 12+ messages in thread
From: James Hogan @ 2013-06-24 15:38 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-kernel, Grant Likely, Rob Herring, Rob Landley, linux-doc,
	devicetree-discuss

On 24/06/13 16:04, Linus Walleij wrote:
> On Thu, Jun 20, 2013 at 11:26 AM, James Hogan <james.hogan@imgtec.com> wrote:
> 
>> Add a pin control driver for the main pins on the TZ1090 SoC. This
>> doesn't include the low-power pins as they're controlled separately via
>> the Powerdown Controller (PDC) registers.
>>
>> Signed-off-by: James Hogan <james.hogan@imgtec.com>
>> Cc: Grant Likely <grant.likely@linaro.org>
>> Cc: Rob Herring <rob.herring@calxeda.com>
>> Cc: Rob Landley <rob@landley.net>
>> Cc: Linus Walleij <linus.walleij@linaro.org>
>> Cc: linux-doc@vger.kernel.org
>> Cc: devicetree-discuss@lists.ozlabs.org
>> ---
>> Changes in v3:
>>  - separated from irq-imgpdc and removed arch/metag changes to allow
>>    these patches to go upstream separately via the pinctrl[/gpio] trees
>>    (particularly the pinctrl drivers depend on the new pinconf DT
>>    bindings).
>>  - some s/unsigned/unsigned int/.
>>  - some s/unsigned int/bool/ and use of BIT().
>>  - pinctrl-tz1090*: switch to generic pinconfig DT bindings and
>>    pinconf_generic_dump_config.
>>  - pinctrl-tz1090*: use tz1090, prefix for pins and function in DT
>>    bindings.
>>  - pinctrl-tz1090*: make internal functions static.
>>  - pinctrl-tz1090*: move initcall from postcore to arch.
> 
> Thanks James, patch applied.
> 
> Also thanks for helping out in getting the generic pinconf
> in place for some three different drivers for this merge window,
> this was very good for pinctrl at large.

Thanks Linus, and no problem!

Cheers
James


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

end of thread, other threads:[~2013-06-24 15:39 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-06-20  9:26 [PATCH v3 0/4] Add TZ1090 pinctrl/gpio drivers James Hogan
2013-06-20  9:26 ` [PATCH v3 1/4] pinctrl-tz1090: add TZ1090 pinctrl driver James Hogan
2013-06-24 15:04   ` Linus Walleij
2013-06-24 15:38     ` James Hogan
2013-06-20  9:26 ` [PATCH v3 2/4] gpio-tz1090: add TZ1090 gpio driver James Hogan
2013-06-24 13:34   ` Grant Likely
2013-06-24 14:48     ` James Hogan
2013-06-24 15:36       ` James Hogan
2013-06-20  9:26 ` [PATCH v3 3/4] pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver James Hogan
2013-06-24 15:08   ` Linus Walleij
2013-06-20  9:26 ` [PATCH v3 4/4] gpio-tz1090-pdc: add TZ1090 PDC gpio driver James Hogan
2013-06-24 15:11   ` Linus Walleij

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