devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/9] Add some TZ1090 SoC infrastructure
@ 2013-05-24 16:21 James Hogan
  2013-05-24 16:21 ` [PATCH v2 2/9] metag: minimal TZ1090 (Comet) " James Hogan
                   ` (5 more replies)
  0 siblings, 6 replies; 18+ messages in thread
From: James Hogan @ 2013-05-24 16:21 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: James Hogan, linux-doc-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
	Thomas Gleixner, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ

The Toumaz Xenif TZ1090 SoC (AKA Comet) contains a 2-threaded
Linux-capable HTP (Meta 2), and is found in a number of development
boards and digital radios, such as the Minimorph Development Platform.

This patchset adds some core infrastructure for the TZ1090, including
drivers for the powerdown controller (PDC) irqchip, general/PDC pin
controllers, and general/PDC GPIO controllers. It's not yet enough to
get the UARTs going (no common clock framework) so JTAG/QEMU with DA TTY
is still needed to get a shell.

A QEMU with a Meta frontend and some TZ1090 emulation, and the SoC
Technical Reference Manual which documents the registers used by both
GPIO drivers and both pin control drivers can be downloaded from the
Minimorph SDK (registration of a Minimorph is currently required):
http://imgtec.com/meta_insider/metaflow-sdk.asp

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: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
Cc: Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>
Cc: Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Cc: Rob Herring <rob.herring-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>
Cc: Rob Landley <rob-VoJi6FS/r0vR7s880joybQ@public.gmane.org>
Cc: Thomas Gleixner <tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org>
Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org
Cc: linux-doc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org

James Hogan (9):
  metag: of_platform_populate from arch generic code
  metag: minimal TZ1090 (Comet) SoC infrastructure
  irq-imgpdc: add ImgTec PDC irqchip driver
  pinconf-generic: add drive strength to debugfs output
  pinconf-generic: add BIAS_BUS_HOLD pinconf
  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   |   41 +
 .../devicetree/bindings/gpio/gpio-tz1090.txt       |   83 +
 .../devicetree/bindings/metag/pdc-intc.txt         |  112 +
 .../bindings/pinctrl/img,tz1090-pdc-pinctrl.txt    |  128 ++
 .../bindings/pinctrl/img,tz1090-pinctrl.txt        |  223 ++
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 arch/metag/Kconfig.soc                             |   14 +
 arch/metag/Makefile                                |    2 +-
 arch/metag/boot/dts/Makefile                       |    2 +
 arch/metag/boot/dts/tz1090.dtsi                    |  106 +
 arch/metag/boot/dts/tz1090_generic.dts             |   10 +
 arch/metag/configs/tz1090_defconfig                |   42 +
 arch/metag/kernel/setup.c                          |    4 +
 drivers/gpio/Kconfig                               |   14 +
 drivers/gpio/Makefile                              |    2 +
 drivers/gpio/gpio-tz1090-pdc.c                     |  242 +++
 drivers/gpio/gpio-tz1090.c                         |  629 ++++++
 drivers/irqchip/Kconfig                            |    4 +
 drivers/irqchip/Makefile                           |    1 +
 drivers/irqchip/irq-imgpdc.c                       |  541 +++++
 drivers/pinctrl/Kconfig                            |   12 +
 drivers/pinctrl/Makefile                           |    2 +
 drivers/pinctrl/pinconf-generic.c                  |    2 +
 drivers/pinctrl/pinctrl-tz1090-pdc.c               | 1105 ++++++++++
 drivers/pinctrl/pinctrl-tz1090.c                   | 2150 ++++++++++++++++++++
 include/linux/pinctrl/pinconf-generic.h            |    6 +
 26 files changed, 5477 insertions(+), 1 deletion(-)
 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/metag/pdc-intc.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 arch/metag/boot/dts/tz1090.dtsi
 create mode 100644 arch/metag/boot/dts/tz1090_generic.dts
 create mode 100644 arch/metag/configs/tz1090_defconfig
 create mode 100644 drivers/gpio/gpio-tz1090-pdc.c
 create mode 100644 drivers/gpio/gpio-tz1090.c
 create mode 100644 drivers/irqchip/irq-imgpdc.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] 18+ messages in thread

* [PATCH v2 1/9] metag: of_platform_populate from arch generic code
       [not found] ` <1369412476-14245-1-git-send-email-james.hogan-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org>
@ 2013-05-24 16:21   ` James Hogan
  2013-06-13 12:44     ` James Hogan
  2013-05-24 16:21   ` [PATCH v2 6/9] pinctrl-tz1090: add TZ1090 pinctrl driver James Hogan
  1 sibling, 1 reply; 18+ messages in thread
From: James Hogan @ 2013-05-24 16:21 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, James Hogan, Rob Herring

If no init_machine callback is provided, call of_platform_populate()
instead. This allows a board/SoC that only needs to call
of_platform_populate to omit the callback altogether.

Signed-off-by: James Hogan <james.hogan-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org>
Cc: Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>
Cc: Rob Herring <rob.herring-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>
Cc: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org
---
 arch/metag/kernel/setup.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/metag/kernel/setup.c b/arch/metag/kernel/setup.c
index 4f5726f..e18cebb 100644
--- a/arch/metag/kernel/setup.c
+++ b/arch/metag/kernel/setup.c
@@ -20,6 +20,7 @@
 #include <linux/memblock.h>
 #include <linux/mm.h>
 #include <linux/of_fdt.h>
+#include <linux/of_platform.h>
 #include <linux/pfn.h>
 #include <linux/root_dev.h>
 #include <linux/sched.h>
@@ -424,6 +425,9 @@ static int __init customize_machine(void)
 	/* customizes platform devices, or adds new ones */
 	if (machine_desc->init_machine)
 		machine_desc->init_machine();
+	else
+		of_platform_populate(NULL, of_default_bus_match_table, NULL,
+				     NULL);
 	return 0;
 }
 arch_initcall(customize_machine);
-- 
1.8.1.2

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

* [PATCH v2 2/9] metag: minimal TZ1090 (Comet) SoC infrastructure
  2013-05-24 16:21 [PATCH v2 0/9] Add some TZ1090 SoC infrastructure James Hogan
@ 2013-05-24 16:21 ` James Hogan
  2013-06-13 12:44   ` James Hogan
  2013-05-24 16:21 ` [PATCH v2 3/9] irq-imgpdc: add ImgTec PDC irqchip driver James Hogan
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 18+ messages in thread
From: James Hogan @ 2013-05-24 16:21 UTC (permalink / raw)
  To: linux-kernel
  Cc: James Hogan, Grant Likely, Rob Herring, Rob Landley,
	Arnd Bergmann, linux-doc, devicetree-discuss

Add really minimal support for Toumaz Xenif TZ1090 SoC (A.K.A. Comet).
This consists of minimal build infrastructure, device tree files, and a
defconfig based on meta2_defconfig.

This SoC contains a 2-threaded HTP (Meta 2) as the main application
processor, and is found in a number of development boards and digital
radios, such as the Minimorph Development Platform.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: linux-doc@vger.kernel.org
Cc: devicetree-discuss@lists.ozlabs.org
---
Changes in v2:
 - removed arch/metag/soc/ directory

 .../devicetree/bindings/vendor-prefixes.txt        |  1 +
 arch/metag/Kconfig.soc                             |  9 +++++
 arch/metag/Makefile                                |  2 +-
 arch/metag/boot/dts/Makefile                       |  2 ++
 arch/metag/boot/dts/tz1090.dtsi                    | 29 +++++++++++++++
 arch/metag/boot/dts/tz1090_generic.dts             | 10 ++++++
 arch/metag/configs/tz1090_defconfig                | 42 ++++++++++++++++++++++
 7 files changed, 94 insertions(+), 1 deletion(-)
 create mode 100644 arch/metag/boot/dts/tz1090.dtsi
 create mode 100644 arch/metag/boot/dts/tz1090_generic.dts
 create mode 100644 arch/metag/configs/tz1090_defconfig

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 6931c43..be80992 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -57,6 +57,7 @@ snps 	Synopsys, Inc.
 st	STMicroelectronics
 ste	ST-Ericsson
 stericsson	ST-Ericsson
+toumaz	Toumaz
 ti	Texas Instruments
 toshiba	Toshiba Corporation
 via	VIA Technologies, Inc.
diff --git a/arch/metag/Kconfig.soc b/arch/metag/Kconfig.soc
index ec079cf..653b479 100644
--- a/arch/metag/Kconfig.soc
+++ b/arch/metag/Kconfig.soc
@@ -14,6 +14,15 @@ config META21_FPGA
 	help
 	  This is a Meta 2.1 FPGA bitstream, just a bare CPU.
 
+config SOC_TZ1090
+	bool "Toumaz Xenif TZ1090 SoC (Comet)"
+	select METAG_LNKGET_AROUND_CACHE
+	select METAG_META21
+	select METAG_SMP_WRITE_REORDERING
+	help
+	  This is a Toumaz Technology Xenif TZ1090 (A.K.A. Comet) SoC containing
+	  a 2-threaded HTP.
+
 endchoice
 
 menu "SoC configuration"
diff --git a/arch/metag/Makefile b/arch/metag/Makefile
index b566116..9739857 100644
--- a/arch/metag/Makefile
+++ b/arch/metag/Makefile
@@ -20,7 +20,7 @@ checkflags-$(CONFIG_METAG_META12)	+= -DMETAC_1_2
 checkflags-$(CONFIG_METAG_META21)	+= -DMETAC_2_1
 CHECKFLAGS				+= -D__metag__ $(checkflags-y)
 
-KBUILD_DEFCONFIG			:= meta2_defconfig
+KBUILD_DEFCONFIG			:= tz1090_defconfig
 
 sflags-$(CONFIG_METAG_META12)		+= -mmetac=1.2
 ifeq ($(CONFIG_METAG_META12),y)
diff --git a/arch/metag/boot/dts/Makefile b/arch/metag/boot/dts/Makefile
index dbd9521..72c1218 100644
--- a/arch/metag/boot/dts/Makefile
+++ b/arch/metag/boot/dts/Makefile
@@ -1,7 +1,9 @@
 dtb-y	+= skeleton.dtb
+dtb-y	+= tz1090_generic.dtb
 
 # Built-in dtb
 builtindtb-y				:= skeleton
+builtindtb-$(CONFIG_SOC_TZ1090)		:= tz1090_generic
 
 ifneq ($(CONFIG_METAG_BUILTIN_DTB_NAME),"")
 	builtindtb-y			:= $(patsubst "%",%,$(CONFIG_METAG_BUILTIN_DTB_NAME))
diff --git a/arch/metag/boot/dts/tz1090.dtsi b/arch/metag/boot/dts/tz1090.dtsi
new file mode 100644
index 0000000..ca057f0
--- /dev/null
+++ b/arch/metag/boot/dts/tz1090.dtsi
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 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/ "skeleton.dtsi"
+
+/ {
+	compatible = "toumaz,tz1090", "img,meta";
+
+	interrupt-parent = <&intc>;
+
+	intc: interrupt-controller {
+		compatible = "img,meta-intc";
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		num-banks = <2>;
+	};
+
+	soc {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+	};
+};
diff --git a/arch/metag/boot/dts/tz1090_generic.dts b/arch/metag/boot/dts/tz1090_generic.dts
new file mode 100644
index 0000000..aa826cb
--- /dev/null
+++ b/arch/metag/boot/dts/tz1090_generic.dts
@@ -0,0 +1,10 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+/dts-v1/;
+
+/include/ "tz1090.dtsi"
diff --git a/arch/metag/configs/tz1090_defconfig b/arch/metag/configs/tz1090_defconfig
new file mode 100644
index 0000000..9f9316a
--- /dev/null
+++ b/arch/metag/configs/tz1090_defconfig
@@ -0,0 +1,42 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_ELF_CORE is not set
+CONFIG_SLAB=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_MSDOS_PARTITION is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_SOC_TZ1090=y
+CONFIG_METAG_HALT_ON_PANIC=y
+# CONFIG_METAG_FPU is not set
+CONFIG_METAG_DA=y
+CONFIG_HZ_100=y
+CONFIG_DEVTMPFS=y
+# CONFIG_STANDALONE is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_FW_LOADER is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=1
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_INPUT is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_DA_TTY=y
+CONFIG_DA_CONSOLE=y
+# CONFIG_DEVKMEM is not set
+# CONFIG_HW_RANDOM is not set
+CONFIG_GPIOLIB=y
+# CONFIG_HWMON is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_DNOTIFY is not set
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_DEBUG_INFO=y
-- 
1.8.1.2

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

* [PATCH v2 3/9] irq-imgpdc: add ImgTec PDC irqchip driver
  2013-05-24 16:21 [PATCH v2 0/9] Add some TZ1090 SoC infrastructure James Hogan
  2013-05-24 16:21 ` [PATCH v2 2/9] metag: minimal TZ1090 (Comet) " James Hogan
@ 2013-05-24 16:21 ` James Hogan
       [not found] ` <1369412476-14245-1-git-send-email-james.hogan-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org>
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 18+ messages in thread
From: James Hogan @ 2013-05-24 16:21 UTC (permalink / raw)
  To: linux-kernel
  Cc: James Hogan, Grant Likely, Rob Herring, Rob Landley,
	Thomas Gleixner, linux-doc, devicetree-discuss

Add irqchip driver for the ImgTec PowerDown Controller (PDC) as found in
the TZ1090. The PDC has a number of general system wakeup (SysWake)
interrupts (which would for example be connected to a power button or an
external peripheral), and a number of peripheral interrupts which can
also wake the system but are connected straight to specific low-power
peripherals (such as RTC or Infrared). It has a single interrupt output
for SysWakes, and individual interrupt outputs for each peripheral.

The driver demuxes the SysWake interrupt line, and passes the peripheral
interrupts straight through. It also handles the set_wake interrupt
operation to enable/disable the appropriate wake event bits.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-doc@vger.kernel.org
Cc: devicetree-discuss@lists.ozlabs.org
---
Changes in v2:
 - 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)

 .../devicetree/bindings/metag/pdc-intc.txt         | 112 +++++
 arch/metag/Kconfig.soc                             |   1 +
 arch/metag/boot/dts/tz1090.dtsi                    |  16 +
 drivers/irqchip/Kconfig                            |   4 +
 drivers/irqchip/Makefile                           |   1 +
 drivers/irqchip/irq-imgpdc.c                       | 541 +++++++++++++++++++++
 6 files changed, 675 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/metag/pdc-intc.txt
 create mode 100644 drivers/irqchip/irq-imgpdc.c

diff --git a/Documentation/devicetree/bindings/metag/pdc-intc.txt b/Documentation/devicetree/bindings/metag/pdc-intc.txt
new file mode 100644
index 0000000..1631d11
--- /dev/null
+++ b/Documentation/devicetree/bindings/metag/pdc-intc.txt
@@ -0,0 +1,112 @@
+* ImgTec Powerdown Controller (PDC) Interrupt Controller Binding
+
+This binding specifies what properties must be available in the device tree
+representation of a PDC IRQ controller. This has a number of input interrupt
+lines which can wake the system, and are passed on through output interrupt
+lines.
+
+Required properties:
+
+    - compatible: Specifies the compatibility list for the interrupt controller.
+      The type shall be <string> and the value shall include "img,pdc-intc".
+
+    - reg: Specifies the base PDC physical address(s) and size(s) of the
+      addressable register space. The type shall be <prop-encoded-array>.
+
+    - interrupt-controller: The presence of this property identifies the node
+      as an interrupt controller. No property value shall be defined.
+
+    - #interrupt-cells: Specifies the number of cells needed to encode an
+      interrupt source. The type shall be a <u32> and the value shall be 3.
+
+    - num-perips: Number of waking peripherals.
+
+    - num-syswakes: Number of SysWake inputs.
+
+    - interrupts: List of interrupt specifiers. The first specifier shall be the
+      shared SysWake interrupt, and remaining specifies shall be PDC peripheral
+      interrupts in order.
+
+* Interrupt Specifier Definition
+
+  Interrupt specifiers consists of 3 cells encoded as follows:
+
+    - <1st-cell>: The type of interrupt:
+                    0 = peripheral interrupt
+                    1 = SysWake interrupt
+
+    - <1st-cell>: The interrupt-number that identifies the interrupt source.
+
+    - <3rd-cell>: The level-sense information, encoded using the Linux interrupt
+                  flags as follows (only 4 valid for peripheral interrupts):
+                    1 = low-to-high edge triggered
+                    2 = high-to-low edge triggered
+                    3 = both edge triggered
+                    4 = active-high level-sensitive (required for perip irqs)
+                    8 = active-low level-sensitive
+
+* Examples
+
+Example 1:
+
+	/*
+	 * TZ1090 PDC block
+	 */
+	pdc: pdc@0x02006000 {
+		// This is an interrupt controller node.
+		interrupt-controller;
+
+		// Three cells to encode interrupt sources.
+		#interrupt-cells = <3>;
+
+		// Offset address of 0x02006000 and size of 0x1000.
+		reg = <0x02006000 0x1000>;
+
+		// Compatible with Meta hardware trigger block.
+		compatible = "img,pdc-intc";
+
+		// Three peripherals are connected.
+		num-perips = <3>;
+
+		// Four SysWakes are connected.
+		num-syswakes = <4>;
+
+		interrupts = <18 4 /* level */>, /* Syswakes */
+			     <30 4 /* level */>, /* Peripheral 0 (RTC) */
+			     <29 4 /* level */>, /* Peripheral 1 (IR) */
+			     <31 4 /* level */>; /* Peripheral 2 (WDT) */
+	};
+
+Example 2:
+
+	/*
+	 * An SoC peripheral that is wired through the PDC.
+	 */
+	rtc0 {
+		// The interrupt controller that this device is wired to.
+		interrupt-parent = <&pdc>;
+
+		// Interrupt source Peripheral 0
+		// Note that there are three cells as specified in the interrupt
+		// parent's '#interrupt-cells' property.
+		interrupts = <1   /* Peripheral */
+		              0   /* Peripheral 0 (RTC) */
+		              4>  /* IRQ_TYPE_LEVEL_HIGH */
+	};
+
+Example 3:
+
+	/*
+	 * An interrupt generating device that is wired to a SysWake pin.
+	 */
+	touchscreen0 {
+		// The interrupt controller that this device is wired to.
+		interrupt-parent = <&pdc>;
+
+		// Interrupt source SysWake 0 that is active-low level-sensitive
+		// Note that there are three cells as specified in the interrupt
+		// parent's '#interrupt-cells' property.
+		interrupts = <1 /* Syswake */
+			      0 /* SysWake0 */
+			      8 /* IRQ_TYPE_LEVEL_LOW */>;
+	};
diff --git a/arch/metag/Kconfig.soc b/arch/metag/Kconfig.soc
index 653b479..433befd 100644
--- a/arch/metag/Kconfig.soc
+++ b/arch/metag/Kconfig.soc
@@ -16,6 +16,7 @@ config META21_FPGA
 
 config SOC_TZ1090
 	bool "Toumaz Xenif TZ1090 SoC (Comet)"
+	select IMGPDC_IRQ
 	select METAG_LNKGET_AROUND_CACHE
 	select METAG_META21
 	select METAG_SMP_WRITE_REORDERING
diff --git a/arch/metag/boot/dts/tz1090.dtsi b/arch/metag/boot/dts/tz1090.dtsi
index ca057f0..12d671c 100644
--- a/arch/metag/boot/dts/tz1090.dtsi
+++ b/arch/metag/boot/dts/tz1090.dtsi
@@ -25,5 +25,21 @@
 		#address-cells = <1>;
 		#size-cells = <1>;
 		ranges;
+
+		pdc: pdc@0x02006000 {
+			interrupt-controller;
+			#interrupt-cells = <3>;
+
+			reg = <0x02006000 0x1000>;
+			compatible = "img,pdc-intc";
+
+			num-perips = <3>;
+			num-syswakes = <3>;
+
+			interrupts = <18 4 /* level */>, /* Syswakes */
+			             <30 4 /* level */>, /* Perip 0 (RTC) */
+			             <29 4 /* level */>, /* Perip 1 (IR) */
+			             <31 4 /* level */>; /* Perip 2 (WDT) */
+		};
 	};
 };
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 4a33351..2bfcf77 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -25,6 +25,10 @@ config ARM_VIC_NR
 	  The maximum number of VICs available in the system, for
 	  power management.
 
+config IMGPDC_IRQ
+	bool
+	select IRQ_DOMAIN
+
 config RENESAS_INTC_IRQPIN
 	bool
 	select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index cda4cb5..b24f3f5 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_ARCH_SUNXI)		+= irq-sun4i.o
 obj-$(CONFIG_ARCH_SPEAR3XX)		+= spear-shirq.o
 obj-$(CONFIG_ARM_GIC)			+= irq-gic.o
 obj-$(CONFIG_ARM_VIC)			+= irq-vic.o
+obj-$(CONFIG_IMGPDC_IRQ)		+= irq-imgpdc.o
 obj-$(CONFIG_SIRF_IRQ)			+= irq-sirfsoc.o
 obj-$(CONFIG_RENESAS_INTC_IRQPIN)	+= irq-renesas-intc-irqpin.o
 obj-$(CONFIG_RENESAS_IRQC)		+= irq-renesas-irqc.o
diff --git a/drivers/irqchip/irq-imgpdc.c b/drivers/irqchip/irq-imgpdc.c
new file mode 100644
index 0000000..0190d2e
--- /dev/null
+++ b/drivers/irqchip/irq-imgpdc.c
@@ -0,0 +1,541 @@
+/*
+ * IMG PowerDown Controller (PDC)
+ *
+ * Copyright 2010-2013 Imagination Technologies Ltd.
+ *
+ * Exposes the syswake and PDC peripheral wake interrupts to the system.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/* PDC interrupt register numbers */
+
+#define PDC_IRQ_STATUS			0x310
+#define PDC_IRQ_ENABLE			0x314
+#define PDC_IRQ_CLEAR			0x318
+#define PDC_IRQ_ROUTE			0x31c
+#define PDC_SYS_WAKE_BASE		0x330
+#define PDC_SYS_WAKE_STRIDE		0x8
+#define PDC_SYS_WAKE_CONFIG_BASE	0x334
+#define PDC_SYS_WAKE_CONFIG_STRIDE	0x8
+
+/* PDC interrupt register field masks */
+
+#define PDC_IRQ_SYS3			0x08
+#define PDC_IRQ_SYS2			0x04
+#define PDC_IRQ_SYS1			0x02
+#define PDC_IRQ_SYS0			0x01
+#define PDC_IRQ_ROUTE_WU_EN_SYS3	0x08000000
+#define PDC_IRQ_ROUTE_WU_EN_SYS2	0x04000000
+#define PDC_IRQ_ROUTE_WU_EN_SYS1	0x02000000
+#define PDC_IRQ_ROUTE_WU_EN_SYS0	0x01000000
+#define PDC_IRQ_ROUTE_WU_EN_WD		0x00040000
+#define PDC_IRQ_ROUTE_WU_EN_IR		0x00020000
+#define PDC_IRQ_ROUTE_WU_EN_RTC		0x00010000
+#define PDC_IRQ_ROUTE_EXT_EN_SYS3	0x00000800
+#define PDC_IRQ_ROUTE_EXT_EN_SYS2	0x00000400
+#define PDC_IRQ_ROUTE_EXT_EN_SYS1	0x00000200
+#define PDC_IRQ_ROUTE_EXT_EN_SYS0	0x00000100
+#define PDC_IRQ_ROUTE_EXT_EN_WD		0x00000004
+#define PDC_IRQ_ROUTE_EXT_EN_IR		0x00000002
+#define PDC_IRQ_ROUTE_EXT_EN_RTC	0x00000001
+#define PDC_SYS_WAKE_RESET		0x00000010
+#define PDC_SYS_WAKE_INT_MODE		0x0000000e
+#define PDC_SYS_WAKE_INT_MODE_SHIFT	1
+#define PDC_SYS_WAKE_PIN_VAL		0x00000001
+
+/* PDC interrupt constants */
+
+#define PDC_SYS_WAKE_INT_LOW		0x0
+#define PDC_SYS_WAKE_INT_HIGH		0x1
+#define PDC_SYS_WAKE_INT_DOWN		0x2
+#define PDC_SYS_WAKE_INT_UP		0x3
+#define PDC_SYS_WAKE_INT_CHANGE		0x6
+#define PDC_SYS_WAKE_INT_NONE		0x4
+
+/**
+ * struct pdc_intc_priv - private pdc interrupt data.
+ * @nr_perips:		Number of peripheral interrupt signals.
+ * @nr_syswakes:	Number of syswake signals.
+ * @perip_irqs:		List of peripheral IRQ numbers handled.
+ * @syswake_irq:	Shared PDC syswake IRQ number.
+ * @domain:		IRQ domain for PDC peripheral and syswake IRQs.
+ * @pdc_base:		Base of PDC registers.
+ * @irq_en:		Cached version of PDC_IRQ_ENABLE register.
+ * @irq_route:		Cached version of PDC_IRQ_ROUTE register.
+ * @lock:		Lock to protect the PDC syswake registers and the cached
+ *			values of those registers in this struct.
+ */
+struct pdc_intc_priv {
+	unsigned int		nr_perips;
+	unsigned int		nr_syswakes;
+	unsigned int		*perip_irqs;
+	unsigned int		syswake_irq;
+	struct irq_domain	*domain;
+	void __iomem		*pdc_base;
+
+	u32			irq_en;
+	u32			irq_route;
+	raw_spinlock_t		lock;
+};
+
+static void pdc_write(struct pdc_intc_priv *priv, unsigned int reg_offs,
+		      unsigned int data)
+{
+	iowrite32(data, priv->pdc_base + reg_offs);
+}
+
+static unsigned int pdc_read(struct pdc_intc_priv *priv,
+			     unsigned int reg_offs)
+{
+	return ioread32(priv->pdc_base + reg_offs);
+}
+
+/* Generic IRQ callbacks */
+
+#define SYS0_HWIRQ	8
+
+static unsigned int hwirq_is_syswake(irq_hw_number_t hw)
+{
+	return hw >= SYS0_HWIRQ;
+}
+
+static unsigned int hwirq_to_syswake(irq_hw_number_t hw)
+{
+	return hw - SYS0_HWIRQ;
+}
+
+static irq_hw_number_t syswake_to_hwirq(unsigned int syswake)
+{
+	return SYS0_HWIRQ + syswake;
+}
+
+static struct pdc_intc_priv *irqd_to_priv(struct irq_data *data)
+{
+	return (struct pdc_intc_priv *)data->domain->host_data;
+}
+
+static void perip_irq_mask(struct irq_data *data)
+{
+	struct pdc_intc_priv *priv = irqd_to_priv(data);
+
+	raw_spin_lock(&priv->lock);
+	priv->irq_route &= ~(1 << data->hwirq);
+	pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
+	raw_spin_unlock(&priv->lock);
+}
+
+static void perip_irq_unmask(struct irq_data *data)
+{
+	struct pdc_intc_priv *priv = irqd_to_priv(data);
+
+	raw_spin_lock(&priv->lock);
+	priv->irq_route |= 1 << data->hwirq;
+	pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
+	raw_spin_unlock(&priv->lock);
+}
+
+static void syswake_irq_ack(struct irq_data *data)
+{
+	struct pdc_intc_priv *priv = irqd_to_priv(data);
+	unsigned int syswake = hwirq_to_syswake(data->hwirq);
+
+	pdc_write(priv, PDC_IRQ_CLEAR, 1 << syswake);
+}
+
+static void syswake_irq_mask(struct irq_data *data)
+{
+	struct pdc_intc_priv *priv = irqd_to_priv(data);
+	unsigned int syswake = hwirq_to_syswake(data->hwirq);
+
+	raw_spin_lock(&priv->lock);
+	priv->irq_en &= ~(PDC_IRQ_SYS0 << syswake);
+	pdc_write(priv, PDC_IRQ_ENABLE, priv->irq_en);
+	raw_spin_unlock(&priv->lock);
+}
+
+static void syswake_irq_unmask(struct irq_data *data)
+{
+	struct pdc_intc_priv *priv = irqd_to_priv(data);
+	unsigned int syswake = hwirq_to_syswake(data->hwirq);
+
+	raw_spin_lock(&priv->lock);
+	priv->irq_en |= PDC_IRQ_SYS0 << syswake;
+	pdc_write(priv, PDC_IRQ_ENABLE, priv->irq_en);
+	raw_spin_unlock(&priv->lock);
+}
+
+static int syswake_irq_set_type(struct irq_data *data, unsigned int flow_type)
+{
+	struct pdc_intc_priv *priv = irqd_to_priv(data);
+	unsigned int syswake = hwirq_to_syswake(data->hwirq);
+	unsigned int irq_mode;
+	unsigned int soc_sys_wake_regoff, soc_sys_wake;
+
+	/* translate to syswake IRQ mode */
+	switch (flow_type) {
+	case IRQ_TYPE_EDGE_BOTH:
+		irq_mode = PDC_SYS_WAKE_INT_CHANGE;
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		irq_mode = PDC_SYS_WAKE_INT_UP;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		irq_mode = PDC_SYS_WAKE_INT_DOWN;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		irq_mode = PDC_SYS_WAKE_INT_HIGH;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		irq_mode = PDC_SYS_WAKE_INT_LOW;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	raw_spin_lock(&priv->lock);
+
+	/* set the IRQ mode */
+	soc_sys_wake_regoff = PDC_SYS_WAKE_BASE + syswake*PDC_SYS_WAKE_STRIDE;
+	soc_sys_wake = pdc_read(priv, soc_sys_wake_regoff);
+	soc_sys_wake &= ~PDC_SYS_WAKE_INT_MODE;
+	soc_sys_wake |= irq_mode << PDC_SYS_WAKE_INT_MODE_SHIFT;
+	pdc_write(priv, soc_sys_wake_regoff, soc_sys_wake);
+
+	/* and update the handler */
+	if (flow_type & IRQ_TYPE_LEVEL_MASK)
+		__irq_set_handler_locked(data->irq, handle_level_irq);
+	else
+		__irq_set_handler_locked(data->irq, handle_edge_irq);
+
+	raw_spin_unlock(&priv->lock);
+
+	return 0;
+}
+
+/* applies to both peripheral and syswake interrupts */
+static int pdc_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+	struct pdc_intc_priv *priv = irqd_to_priv(data);
+	irq_hw_number_t hw = data->hwirq;
+	unsigned int mask = (1 << 16) << hw;
+	unsigned int dst_irq;
+
+	raw_spin_lock(&priv->lock);
+	if (on)
+		priv->irq_route |= mask;
+	else
+		priv->irq_route &= ~mask;
+	pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
+	raw_spin_unlock(&priv->lock);
+
+	/* control the destination IRQ wakeup too for standby mode */
+	if (hwirq_is_syswake(hw))
+		dst_irq = priv->syswake_irq;
+	else
+		dst_irq = priv->perip_irqs[hw];
+	irq_set_irq_wake(dst_irq, on);
+
+	return 0;
+}
+
+/* peripheral virtual interrupt functions */
+static struct irq_chip perip_irq_chip = {
+	.irq_mask	= perip_irq_mask,
+	.irq_unmask	= perip_irq_unmask,
+	.irq_set_wake	= pdc_irq_set_wake,
+	/* for standby we use the peripheral IRQ */
+	.flags		= IRQCHIP_MASK_ON_SUSPEND,
+};
+
+/* syswake virtual interrupt functions */
+static struct irq_chip syswake_irq_chip = {
+	.irq_ack	= syswake_irq_ack,
+	.irq_mask	= syswake_irq_mask,
+	.irq_unmask	= syswake_irq_unmask,
+	.irq_set_type	= syswake_irq_set_type,
+	.irq_set_wake	= pdc_irq_set_wake,
+	/* for standby we use the shared IRQ */
+	.flags		= IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static int pdc_intc_irq_map(struct irq_domain *d, unsigned int irq,
+			    irq_hw_number_t hw)
+{
+	if (hwirq_is_syswake(hw))
+		irq_set_chip_and_handler(irq, &syswake_irq_chip,
+					 handle_edge_irq);
+	else
+		irq_set_chip_and_handler(irq, &perip_irq_chip,
+					 handle_level_irq);
+	return 0;
+}
+
+/* translate interrupt identifier to hwirq number */
+static int pdc_intc_irq_xlate(struct irq_domain *d,
+			      struct device_node *ctrlr,
+			      const u32 *intspec,
+			      unsigned int intsize,
+			      irq_hw_number_t *out_hwirq,
+			      unsigned int *out_type)
+{
+	struct pdc_intc_priv *priv = d->host_data;
+
+	if (WARN_ON(intsize < 2))
+		return -EINVAL;
+
+	/* 0 indicates peripheral interrupt */
+	if (intspec[0] == 0) {
+		if (WARN_ON(intspec[1] >= priv->nr_perips))
+			return -EINVAL;
+		*out_hwirq = intspec[1];
+		return 0;
+	}
+
+	/* invalid? */
+	if (WARN_ON(intspec[0] != 1))
+		return -EINVAL;
+
+	/* 1 indictes syswake interrupt */
+	if (WARN_ON(intsize < 3))
+		return -EINVAL;
+	if (WARN_ON(intspec[1] >= priv->nr_syswakes))
+		return -EINVAL;
+	*out_hwirq = syswake_to_hwirq(intspec[1]);
+	*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+	return 0;
+}
+
+static const struct irq_domain_ops pdc_intc_irq_domain_ops = {
+	.map	= pdc_intc_irq_map,
+	.xlate	= pdc_intc_irq_xlate,
+};
+
+static void pdc_intc_perip_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct pdc_intc_priv *priv;
+	unsigned int i, irq_no;
+
+	priv = (struct pdc_intc_priv *)irq_desc_get_handler_data(desc);
+
+	/* find the peripheral number */
+	for (i = 0; i < priv->nr_perips; ++i)
+		if (irq == priv->perip_irqs[i])
+			goto found;
+
+	/* should never get here */
+	return;
+found:
+
+	/* pass on the interrupt */
+	irq_no = irq_linear_revmap(priv->domain, i);
+	generic_handle_irq(irq_no);
+}
+
+static void pdc_intc_syswake_isr(unsigned int irq, struct irq_desc *desc)
+{
+	struct pdc_intc_priv *priv;
+	unsigned int syswake, irq_no;
+	unsigned int status;
+
+	priv = (struct pdc_intc_priv *)irq_desc_get_handler_data(desc);
+
+	raw_spin_lock(&priv->lock);
+	status = pdc_read(priv, PDC_IRQ_STATUS) & priv->irq_en;
+	raw_spin_unlock(&priv->lock);
+
+	status &= (1 << priv->nr_syswakes) - 1;
+
+	for (syswake = 0; status; status >>= 1, ++syswake) {
+		/* Has this sys_wake triggered? */
+		if (!(status & 1))
+			continue;
+
+		irq_no = irq_linear_revmap(priv->domain,
+					   syswake_to_hwirq(syswake));
+		generic_handle_irq(irq_no);
+	}
+}
+
+static void pdc_intc_setup(struct pdc_intc_priv *priv)
+{
+	int i;
+	unsigned int soc_sys_wake_regoff;
+	unsigned int soc_sys_wake;
+
+	/*
+	 * Mask all syswake interrupts before routing, or we could receive an
+	 * interrupt before we're ready to handle it.
+	 */
+	priv->irq_en = 0;
+	pdc_write(priv, PDC_IRQ_ENABLE, priv->irq_en);
+
+	/*
+	 * Enable routing of all syswakes
+	 * Disable all wake sources
+	 */
+	priv->irq_route = ((PDC_IRQ_ROUTE_EXT_EN_SYS0 << priv->nr_syswakes) -
+				PDC_IRQ_ROUTE_EXT_EN_SYS0);
+	pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
+
+	/* Initialise syswake IRQ */
+	for (i = 0; i < priv->nr_syswakes; ++i) {
+		/* set the IRQ mode to rising edge */
+		soc_sys_wake_regoff = PDC_SYS_WAKE_BASE + i*PDC_SYS_WAKE_STRIDE;
+		soc_sys_wake = PDC_SYS_WAKE_INT_UP
+				<< PDC_SYS_WAKE_INT_MODE_SHIFT;
+		pdc_write(priv, soc_sys_wake_regoff, soc_sys_wake);
+	}
+}
+
+static int pdc_intc_probe(struct platform_device *pdev)
+{
+	struct pdc_intc_priv *priv;
+	struct device_node *node = pdev->dev.of_node;
+	struct resource *res_regs;
+	unsigned int i;
+	int irq, ret;
+	u32 val;
+
+	if (!node)
+		return -ENOENT;
+
+	/* Get registers */
+	res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res_regs == NULL) {
+		dev_err(&pdev->dev, "cannot find registers resource\n");
+		return -ENOENT;
+	}
+
+	/* Allocate driver data */
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&pdev->dev, "cannot allocate device data\n");
+		return -ENOMEM;
+	}
+	raw_spin_lock_init(&priv->lock);
+	platform_set_drvdata(pdev, priv);
+
+	/* Ioremap the registers */
+	priv->pdc_base = devm_ioremap(&pdev->dev, res_regs->start,
+				      res_regs->end - res_regs->start);
+	if (!priv->pdc_base)
+		return -EIO;
+
+	/* Get number of peripherals */
+	ret = of_property_read_u32(node, "num-perips", &val);
+	if (ret) {
+		dev_err(&pdev->dev, "No num-perips node property found\n");
+		return -EINVAL;
+	}
+	if (val > SYS0_HWIRQ) {
+		dev_err(&pdev->dev, "num-perips (%u) out of range\n", val);
+		return -EINVAL;
+	}
+	priv->nr_perips = val;
+
+	/* Get number of syswakes */
+	ret = of_property_read_u32(node, "num-syswakes", &val);
+	if (ret) {
+		dev_err(&pdev->dev, "No num-syswakes node property found\n");
+		return -EINVAL;
+	}
+	if (val > SYS0_HWIRQ) {
+		dev_err(&pdev->dev, "num-syswakes (%u) out of range\n", val);
+		return -EINVAL;
+	}
+	priv->nr_syswakes = val;
+
+	/* Get peripheral IRQ numbers */
+	priv->perip_irqs = devm_kzalloc(&pdev->dev, 4 * priv->nr_perips,
+					GFP_KERNEL);
+	if (!priv->perip_irqs) {
+		dev_err(&pdev->dev, "cannot allocate perip IRQ list\n");
+		return -ENOMEM;
+	}
+	for (i = 0; i < priv->nr_perips; ++i) {
+		irq = platform_get_irq(pdev, 1 + i);
+		if (irq < 0) {
+			dev_err(&pdev->dev, "cannot find perip IRQ #%u\n", i);
+			return irq;
+		}
+		priv->perip_irqs[i] = irq;
+	}
+	/* check if too many were provided */
+	if (platform_get_irq(pdev, 1 + i) >= 0) {
+		dev_err(&pdev->dev, "surplus perip IRQs detected\n");
+		return -EINVAL;
+	}
+
+	/* Get syswake IRQ number */
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "cannot find syswake IRQ\n");
+		return irq;
+	}
+	priv->syswake_irq = irq;
+
+	/* Set up an IRQ domain */
+	priv->domain = irq_domain_add_linear(node, 16, &pdc_intc_irq_domain_ops,
+					     priv);
+	if (unlikely(!priv->domain)) {
+		dev_err(&pdev->dev, "cannot add IRQ domain\n");
+		return -ENOMEM;
+	}
+
+	/* Set up the hardware to enable interrupt routing */
+	pdc_intc_setup(priv);
+
+	/* Setup chained handlers for the peripheral IRQs */
+	for (i = 0; i < priv->nr_perips; ++i) {
+		irq = priv->perip_irqs[i];
+		irq_set_handler_data(irq, priv);
+		irq_set_chained_handler(irq, pdc_intc_perip_isr);
+	}
+
+	/* Setup chained handler for the syswake IRQ */
+	irq_set_handler_data(priv->syswake_irq, priv);
+	irq_set_chained_handler(priv->syswake_irq, pdc_intc_syswake_isr);
+
+	dev_info(&pdev->dev,
+		 "PDC IRQ controller initialised (%u perip IRQs, %u syswake IRQs)\n",
+		 priv->nr_perips,
+		 priv->nr_syswakes);
+
+	return 0;
+}
+
+static int pdc_intc_remove(struct platform_device *pdev)
+{
+	struct pdc_intc_priv *priv = platform_get_drvdata(pdev);
+
+	irq_domain_remove(priv->domain);
+	return 0;
+}
+
+static const struct of_device_id pdc_intc_match[] = {
+	{ .compatible = "img,pdc-intc" },
+	{}
+};
+
+static struct platform_driver pdc_intc_driver = {
+	.driver = {
+		.name		= "pdc-intc",
+		.of_match_table	= pdc_intc_match,
+	},
+	.probe = pdc_intc_probe,
+	.remove = pdc_intc_remove,
+};
+
+static int __init pdc_intc_init(void)
+{
+	return platform_driver_register(&pdc_intc_driver);
+}
+core_initcall(pdc_intc_init);
-- 
1.8.1.2

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

* [PATCH v2 6/9] pinctrl-tz1090: add TZ1090 pinctrl driver
       [not found] ` <1369412476-14245-1-git-send-email-james.hogan-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org>
  2013-05-24 16:21   ` [PATCH v2 1/9] metag: of_platform_populate from arch generic code James Hogan
@ 2013-05-24 16:21   ` James Hogan
  2013-05-28  6:48     ` Linus Walleij
  1 sibling, 1 reply; 18+ messages in thread
From: James Hogan @ 2013-05-24 16:21 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: James Hogan, linux-doc-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ

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-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org>
Cc: Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>
Cc: Rob Herring <rob.herring-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>
Cc: Rob Landley <rob-VoJi6FS/r0vR7s880joybQ@public.gmane.org>
Cc: Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Cc: linux-doc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org
---
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        |  223 ++
 arch/metag/Kconfig.soc                             |    2 +
 arch/metag/boot/dts/tz1090.dtsi                    |    6 +
 drivers/pinctrl/Kconfig                            |    6 +
 drivers/pinctrl/Makefile                           |    1 +
 drivers/pinctrl/pinctrl-tz1090.c                   | 2150 ++++++++++++++++++++
 6 files changed, 2388 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..4dd41a1
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt
@@ -0,0 +1,223 @@
+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:
+- 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:
+- 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.
+- tristate: Flag, put pin into high impedance state.
+- pull-up: Flag, pull pin high.
+- pull-down: Flag, pull pin low.
+- bus-hold: Flag, weak latch last value on tristate bus.
+- schmitt: Integer, enable or disable Schmitt trigger mode for the pins.
+    0: no hysteresis
+    1: schmitt trigger
+- 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 tristate, pull-up, pull-down, and 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.
+
+        tristate:       supported.
+        pull-up:        supported.
+        pull-down:      supported.
+        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 tristate, pull-up, pull-down,
+    and 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.
+
+        tristate:       supported.
+        pull-up:        supported.
+        pull-down:      supported.
+        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.
+        schmitt:        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.
+        schmitt:        supported.
+        slew-rate:      supported.
+        drive-strength: supported.
+
+  drive groups:
+
+    These all support schmitt, 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 {
+				pins =	"uart0_rxd",
+					"uart0_txd";
+				function = "perip";
+			};
+		};
+		tft_default: tft {
+			tft_cfg {
+				pins =	"tft";
+				function = "tft";
+			};
+		};
+	};
+
+	uart@02004b00 {
+		pinctrl-names = "default";
+		pinctrl-0 = <&uart0_default>;
+	};
diff --git a/arch/metag/Kconfig.soc b/arch/metag/Kconfig.soc
index 433befd..944dc30 100644
--- a/arch/metag/Kconfig.soc
+++ b/arch/metag/Kconfig.soc
@@ -20,6 +20,8 @@ config SOC_TZ1090
 	select METAG_LNKGET_AROUND_CACHE
 	select METAG_META21
 	select METAG_SMP_WRITE_REORDERING
+	select PINCTRL
+	select PINCTRL_TZ1090
 	help
 	  This is a Toumaz Technology Xenif TZ1090 (A.K.A. Comet) SoC containing
 	  a 2-threaded HTP.
diff --git a/arch/metag/boot/dts/tz1090.dtsi b/arch/metag/boot/dts/tz1090.dtsi
index 12d671c..e3f117c 100644
--- a/arch/metag/boot/dts/tz1090.dtsi
+++ b/arch/metag/boot/dts/tz1090.dtsi
@@ -41,5 +41,11 @@
 			             <29 4 /* level */>, /* Perip 1 (IR) */
 			             <31 4 /* level */>; /* Perip 2 (WDT) */
 		};
+
+		pinctrl: pinctrl@02005800 {
+			#gpio-range-cells = <3>;
+			compatible = "img,tz1090-pinctrl";
+			reg = <0x02005800 0xe4>;
+		};
 	};
 };
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 8f66924..d9ae8e8 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -186,6 +186,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 9bdaeb8..7514305 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -37,6 +37,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..d03e2ac
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tz1090.c
@@ -0,0 +1,2150 @@
+/*
+ * 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"
+
+/* 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		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 spi1_cs2_pins[] = {
+	TZ1090_PIN_SPI1_CS2,
+};
+
+static const unsigned pdm_d_pins[] = {
+	TZ1090_PIN_PDM_D,
+};
+
+static const unsigned 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 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 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 sdh_pins[] = {
+	TZ1090_PIN_SDH_CD,
+	TZ1090_PIN_SDH_WP,
+	TZ1090_PIN_SDH_CLK_IN,
+};
+
+static const unsigned 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 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 uart0_pins[] = {
+	TZ1090_PIN_UART0_RTS,
+	TZ1090_PIN_UART0_CTS,
+	TZ1090_PIN_UART0_TXD,
+	TZ1090_PIN_UART0_RXD,
+};
+
+static const unsigned uart1_pins[] = {
+	TZ1090_PIN_UART1_TXD,
+	TZ1090_PIN_UART1_RXD,
+};
+
+static const unsigned 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 scb0_pins[] = {
+	TZ1090_PIN_SCB0_SDAT,
+	TZ1090_PIN_SCB0_SCLK,
+};
+
+static const unsigned scb1_pins[] = {
+	TZ1090_PIN_SCB1_SDAT,
+	TZ1090_PIN_SCB1_SCLK,
+};
+
+static const unsigned scb2_pins[] = {
+	TZ1090_PIN_SCB2_SDAT,
+	TZ1090_PIN_SCB2_SCLK,
+};
+
+static const unsigned 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 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 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 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 drive_scb0_pins[] = {
+	TZ1090_PIN_SCB0_SCLK,
+	TZ1090_PIN_SCB0_SDAT,
+	TZ1090_PIN_PDM_D,
+	TZ1090_PIN_PDM_C,
+};
+
+static const unsigned 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 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 group,
+					 const unsigned **pins,
+					 unsigned *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 offset)
+{
+	seq_printf(s, " %s", dev_name(pctldev->dev));
+}
+#endif
+
+static int reserve_map(struct device *dev, struct pinctrl_map **map,
+		       unsigned *reserved_maps, unsigned *num_maps,
+		       unsigned reserve)
+{
+	unsigned old_num = *reserved_maps;
+	unsigned 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 *reserved_maps,
+		       unsigned *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 *reserved_maps, unsigned *num_maps,
+			   const char *group, unsigned long *configs,
+			   unsigned 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 int add_config(struct device *dev, unsigned long **configs,
+		      unsigned *num_configs, unsigned long config)
+{
+	unsigned old_num = *num_configs;
+	unsigned new_num = old_num + 1;
+	unsigned long *new_configs;
+
+	new_configs = krealloc(*configs, sizeof(*new_configs) * new_num,
+			       GFP_KERNEL);
+	if (!new_configs) {
+		dev_err(dev, "krealloc(configs) failed\n");
+		return -ENOMEM;
+	}
+
+	new_configs[old_num] = config;
+
+	*configs = new_configs;
+	*num_configs = new_num;
+
+	return 0;
+}
+
+void tz1090_pinctrl_dt_free_map(struct pinctrl_dev *pctldev,
+				struct pinctrl_map *map, unsigned 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);
+}
+
+/* Describes pinconf properties/flags available from device tree */
+static const struct cfg_param {
+	const char *property;
+	enum pin_config_param param;
+	bool flag;
+} cfg_params[] = {
+	{"tristate",		PIN_CONFIG_BIAS_HIGH_IMPEDANCE,		true},
+	{"pull-up",		PIN_CONFIG_BIAS_PULL_UP,		true},
+	{"pull-down",		PIN_CONFIG_BIAS_PULL_DOWN,		true},
+	{"bus-hold",		PIN_CONFIG_BIAS_BUS_HOLD,		true},
+	{"schmitt",		PIN_CONFIG_INPUT_SCHMITT_ENABLE,	true},
+	{"slew-rate",		PIN_CONFIG_SLEW_RATE,			false},
+	{"drive-strength",	PIN_CONFIG_DRIVE_STRENGTH,		false},
+};
+
+int tz1090_pinctrl_dt_subnode_to_map(struct device *dev,
+				     struct device_node *np,
+				     struct pinctrl_map **map,
+				     unsigned *reserved_maps,
+				     unsigned *num_maps)
+{
+	int ret, i;
+	const char *function;
+	u32 val;
+	unsigned long config;
+	unsigned long *configs = NULL;
+	unsigned num_configs = 0;
+	unsigned reserve;
+	struct property *prop;
+	const char *group;
+
+	ret = of_property_read_string(np, "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;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(cfg_params); i++) {
+		ret = of_property_read_u32(np, cfg_params[i].property, &val);
+		/* flags don't have to have a value */
+		if (ret == -EOVERFLOW && cfg_params[i].flag) {
+			val = 1;
+			ret = 0;
+		}
+		if (!ret) {
+			config = pinconf_to_config_packed(cfg_params[i].param,
+							  val);
+			ret = add_config(dev, &configs, &num_configs, config);
+			if (ret < 0)
+				goto exit;
+		/* EINVAL=missing, which is fine since it's optional */
+		} else if (ret != -EINVAL) {
+			dev_err(dev, "could not parse property %s (%d)\n",
+				cfg_params[i].property, ret);
+		}
+	}
+
+	reserve = 0;
+	if (function != NULL)
+		reserve++;
+	if (num_configs)
+		reserve++;
+	ret = of_property_count_strings(np, "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, "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;
+}
+
+int tz1090_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
+				  struct device_node *np_config,
+				  struct pinctrl_map **map, unsigned *num_maps)
+{
+	unsigned 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 function)
+{
+	return tz1090_functions[function].name;
+}
+
+static int tz1090_pinctrl_get_func_groups(struct pinctrl_dev *pctldev,
+					  unsigned function,
+					  const char * const **groups,
+					  unsigned * 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:	1 to enable pin as GPIO,
+ *			0 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,
+				       unsigned int 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);
+	gpio_en |= (gpio_select << 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
+ * @gpio_select:	1 to enable peripheral interface when not GPIO,
+ *			0 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,
+					unsigned int 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);
+	pin_en |= (perip_select << 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 function,
+				 unsigned 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], 1);
+
+	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 function, unsigned 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], 0);
+}
+
+/**
+ * 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, 1);
+	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, 0);
+}
+
+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 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 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 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 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;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void tz1090_pinconf_config_dbg_show(struct pinctrl_dev *pctldev,
+					   struct seq_file *s,
+					   unsigned long config)
+{
+	enum pin_config_param param = pinconf_to_config_param(config);
+	u16 arg = pinconf_to_config_argument(config);
+	const char *pname = "unknown";
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cfg_params); i++) {
+		if (cfg_params[i].param == param) {
+			pname = cfg_params[i].property;
+			break;
+		}
+	}
+
+	seq_printf(s, "%s=%d", pname, arg);
+}
+#endif
+
+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,
+#ifdef CONFIG_DEBUG_FS
+	.pin_config_config_dbg_show	= tz1090_pinconf_config_dbg_show,
+#endif
+};
+
+/*
+ * 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);
+}
+postcore_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] 18+ messages in thread

* [PATCH v2 7/9] gpio-tz1090: add TZ1090 gpio driver
  2013-05-24 16:21 [PATCH v2 0/9] Add some TZ1090 SoC infrastructure James Hogan
                   ` (2 preceding siblings ...)
       [not found] ` <1369412476-14245-1-git-send-email-james.hogan-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org>
@ 2013-05-24 16:21 ` James Hogan
  2013-05-29 15:32   ` Linus Walleij
  2013-05-24 16:21 ` [PATCH v2 8/9] pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver James Hogan
  2013-05-24 16:21 ` [PATCH v2 9/9] gpio-tz1090-pdc: add TZ1090 PDC gpio driver James Hogan
  5 siblings, 1 reply; 18+ messages in thread
From: James Hogan @ 2013-05-24 16:21 UTC (permalink / raw)
  To: linux-kernel
  Cc: James Hogan, Grant Likely, Rob Herring, Rob Landley,
	Linus Walleij, 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@secretlab.ca>
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 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       |  83 +++
 arch/metag/Kconfig.soc                             |   1 +
 arch/metag/boot/dts/tz1090.dtsi                    |  35 ++
 drivers/gpio/Kconfig                               |   7 +
 drivers/gpio/Makefile                              |   1 +
 drivers/gpio/gpio-tz1090.c                         | 629 +++++++++++++++++++++
 6 files changed, 756 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..6fd98be
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
@@ -0,0 +1,83 @@
+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:
+        1: 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
+        1: trigger on rising edge
+        2: trigger on falling edge
+        3: trigger on both rising and falling edges
+        4: trigger when high
+        8: trigger when 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 4 /* level */>;
+			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/arch/metag/Kconfig.soc b/arch/metag/Kconfig.soc
index 944dc30..d61d687 100644
--- a/arch/metag/Kconfig.soc
+++ b/arch/metag/Kconfig.soc
@@ -16,6 +16,7 @@ config META21_FPGA
 
 config SOC_TZ1090
 	bool "Toumaz Xenif TZ1090 SoC (Comet)"
+	select ARCH_WANT_OPTIONAL_GPIOLIB
 	select IMGPDC_IRQ
 	select METAG_LNKGET_AROUND_CACHE
 	select METAG_META21
diff --git a/arch/metag/boot/dts/tz1090.dtsi b/arch/metag/boot/dts/tz1090.dtsi
index e3f117c..fdd382d 100644
--- a/arch/metag/boot/dts/tz1090.dtsi
+++ b/arch/metag/boot/dts/tz1090.dtsi
@@ -47,5 +47,40 @@
 			compatible = "img,tz1090-pinctrl";
 			reg = <0x02005800 0xe4>;
 		};
+
+		gpios: gpios@02005800 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "img,tz1090-gpio";
+			reg = <0x02005800 0x90>;
+
+			gpios0: bank@0 {
+				gpio-controller;
+				interrupt-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				reg = <0>;
+				interrupts = <13 4 /* level */>;
+				gpio-ranges = <&pinctrl 0 0 30>;
+			};
+			gpios1: bank@1 {
+				gpio-controller;
+				interrupt-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				reg = <1>;
+				interrupts = <14 4 /* level */>;
+				gpio-ranges = <&pinctrl 0 30 30>;
+			};
+			gpios2: bank@2 {
+				gpio-controller;
+				interrupt-controller;
+				#gpio-cells = <2>;
+				#interrupt-cells = <2>;
+				reg = <2>;
+				interrupts = <15 4 /* level */>;
+				gpio-ranges = <&pinctrl 0 60 30>;
+			};
+		};
 	};
 };
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 87d5670..49e4f67 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..7f4b521
--- /dev/null
+++ b/drivers/gpio/gpio-tz1090.c
@@ -0,0 +1,629 @@
+/*
+ * 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 GPIO_POLARITY_LOW	0
+#define GPIO_POLARITY_HIGH	1
+
+/* REG_GPIO_IRQ_TYPE */
+#define GPIO_LEVEL_TRIGGERED	0
+#define GPIO_EDGE_TRIGGERED	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,
+					int val)
+{
+	u32 value;
+
+	value = tz1090_gpio_read(bank, reg_offs);
+	value &= ~BIT(offset);
+	value |= !!val << 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,
+				int 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 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 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 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 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 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 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 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, unsigned int 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;
+
+	__global_lock2(lstat);
+	/* irq_polarity[offset] = !input[offset] */
+	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, 0);
+}
+
+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, 1);
+}
+
+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, 1);
+	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 = GPIO_EDGE_TRIGGERED;
+		polarity = GPIO_POLARITY_LOW;
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		type = GPIO_EDGE_TRIGGERED;
+		polarity = GPIO_POLARITY_HIGH;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		type = GPIO_EDGE_TRIGGERED;
+		polarity = GPIO_POLARITY_LOW;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		type = GPIO_LEVEL_TRIGGERED;
+		polarity = GPIO_POLARITY_HIGH;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		type = GPIO_LEVEL_TRIGGERED;
+		polarity = GPIO_POLARITY_LOW;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	tz1090_gpio_irq_type(bank, data->hwirq, type);
+	if (type == GPIO_LEVEL_TRIGGERED)
+		__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);
+}
+postcore_initcall(tz1090_gpio_init);
-- 
1.8.1.2

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

* [PATCH v2 8/9] pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver
  2013-05-24 16:21 [PATCH v2 0/9] Add some TZ1090 SoC infrastructure James Hogan
                   ` (3 preceding siblings ...)
  2013-05-24 16:21 ` [PATCH v2 7/9] gpio-tz1090: add TZ1090 gpio driver James Hogan
@ 2013-05-24 16:21 ` James Hogan
  2013-05-29 15:36   ` Linus Walleij
  2013-05-24 16:21 ` [PATCH v2 9/9] gpio-tz1090-pdc: add TZ1090 PDC gpio driver James Hogan
  5 siblings, 1 reply; 18+ messages in thread
From: James Hogan @ 2013-05-24 16:21 UTC (permalink / raw)
  To: linux-kernel
  Cc: James Hogan, Grant Likely, Rob Herring, Rob Landley,
	Linus Walleij, 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@secretlab.ca>
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 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    |  128 +++
 arch/metag/Kconfig.soc                             |    1 +
 arch/metag/boot/dts/tz1090.dtsi                    |    6 +
 drivers/pinctrl/Kconfig                            |    6 +
 drivers/pinctrl/Makefile                           |    1 +
 drivers/pinctrl/pinctrl-tz1090-pdc.c               | 1105 ++++++++++++++++++++
 6 files changed, 1247 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..b899b1f
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt
@@ -0,0 +1,128 @@
+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:
+- 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:
+- 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.
+- tristate: Flag, put pin into high impedance state.
+- pull-up: Flag, pull pin high.
+- pull-down: Flag: pull pin low.
+- bus-hold: Flag, weak latch last value on tristate bus.
+- schmitt: Integer, enable or disable Schmitt trigger mode for the pins.
+    0: no hysteresis
+    1: schmitt trigger
+- 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
+- power-on-start: Integer, power on start value of pins
+    0: weak pull-down disabled
+    1: weak pull-down for invalid power
+
+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 tristate, pull-up, pull-down, and 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 schmitt, slew-rate, drive-strength, and power-on-start.
+
+    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 {
+				pins =	"sys_wake0",
+					"sys_wake1",
+					"sys_wake2";
+				pull-up;
+			};
+		};
+		irmod_default: irmod {
+			gpio0_cfg {
+				pins =	"gpio0";
+				function = "ir_mod_stable_out";
+			};
+			gpio1_cfg {
+				pins =	"gpio1";
+				function = "ir_mod_power_out";
+			};
+		};
+	};
+
+	ir: ir@02006200 {
+		pinctrl-names = "default";
+		pinctrl-0 = <&irmod_default>;
+	};
diff --git a/arch/metag/Kconfig.soc b/arch/metag/Kconfig.soc
index d61d687..973640f 100644
--- a/arch/metag/Kconfig.soc
+++ b/arch/metag/Kconfig.soc
@@ -23,6 +23,7 @@ config SOC_TZ1090
 	select METAG_SMP_WRITE_REORDERING
 	select PINCTRL
 	select PINCTRL_TZ1090
+	select PINCTRL_TZ1090_PDC
 	help
 	  This is a Toumaz Technology Xenif TZ1090 (A.K.A. Comet) SoC containing
 	  a 2-threaded HTP.
diff --git a/arch/metag/boot/dts/tz1090.dtsi b/arch/metag/boot/dts/tz1090.dtsi
index fdd382d..e2e61a8 100644
--- a/arch/metag/boot/dts/tz1090.dtsi
+++ b/arch/metag/boot/dts/tz1090.dtsi
@@ -48,6 +48,12 @@
 			reg = <0x02005800 0xe4>;
 		};
 
+		pdc_pinctrl: pinctrl@02006500 {
+			#gpio-range-cells = <3>;
+			compatible = "img,tz1090-pdc-pinctrl";
+			reg = <0x02006500 0x100>;
+		};
+
 		gpios: gpios@02005800 {
 			#address-cells = <1>;
 			#size-cells = <0>;
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index d9ae8e8..f1b9d27 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -192,6 +192,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 7514305..8aae378 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -38,6 +38,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..7fc74b9
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tz1090-pdc.c
@@ -0,0 +1,1105 @@
+/*
+ * 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"
+
+/* 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		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 gpio0_pins[] = {
+	TZ1090_PDC_PIN_GPIO0,
+};
+
+static const unsigned gpio1_pins[] = {
+	TZ1090_PDC_PIN_GPIO1,
+};
+
+static const unsigned 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 group)
+{
+	return tz1090_pdc_groups[group].name;
+}
+
+static int tz1090_pdc_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+					     unsigned group,
+					     const unsigned **pins,
+					     unsigned *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 offset)
+{
+	seq_printf(s, " %s", dev_name(pctldev->dev));
+}
+#endif
+
+static int reserve_map(struct device *dev, struct pinctrl_map **map,
+		       unsigned *reserved_maps, unsigned *num_maps,
+		       unsigned reserve)
+{
+	unsigned old_num = *reserved_maps;
+	unsigned 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 *reserved_maps,
+		       unsigned *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 *reserved_maps, unsigned *num_maps,
+			   const char *group, unsigned long *configs,
+			   unsigned 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 int add_config(struct device *dev, unsigned long **configs,
+		      unsigned *num_configs, unsigned long config)
+{
+	unsigned old_num = *num_configs;
+	unsigned new_num = old_num + 1;
+	unsigned long *new_configs;
+
+	new_configs = krealloc(*configs, sizeof(*new_configs) * new_num,
+			       GFP_KERNEL);
+	if (!new_configs) {
+		dev_err(dev, "krealloc(configs) failed\n");
+		return -ENOMEM;
+	}
+
+	new_configs[old_num] = config;
+
+	*configs = new_configs;
+	*num_configs = new_num;
+
+	return 0;
+}
+
+void tz1090_pdc_pinctrl_dt_free_map(struct pinctrl_dev *pctldev,
+				    struct pinctrl_map *map, unsigned 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);
+}
+
+/* Describes pinconf properties/flags available from device tree */
+static const struct cfg_param {
+	const char *property;
+	enum pin_config_param param;
+	bool flag;
+} cfg_params[] = {
+	{"tristate",		PIN_CONFIG_BIAS_HIGH_IMPEDANCE,		true},
+	{"pull-up",		PIN_CONFIG_BIAS_PULL_UP,		true},
+	{"pull-down",		PIN_CONFIG_BIAS_PULL_DOWN,		true},
+	{"bus-hold",		PIN_CONFIG_BIAS_BUS_HOLD,		true},
+	{"power-on-start",	PIN_CONFIG_LOW_POWER_MODE,		true},
+	{"schmitt",		PIN_CONFIG_INPUT_SCHMITT_ENABLE,	true},
+	{"slew-rate",		PIN_CONFIG_SLEW_RATE,			false},
+	{"drive-strength",	PIN_CONFIG_DRIVE_STRENGTH,		false},
+
+};
+
+int tz1090_pdc_pinctrl_dt_subnode_to_map(struct device *dev,
+					 struct device_node *np,
+					 struct pinctrl_map **map,
+					 unsigned *reserved_maps,
+					 unsigned *num_maps)
+{
+	int ret, i;
+	const char *function;
+	u32 val;
+	unsigned long config;
+	unsigned long *configs = NULL;
+	unsigned num_configs = 0;
+	unsigned reserve;
+	struct property *prop;
+	const char *group;
+
+	ret = of_property_read_string(np, "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;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(cfg_params); i++) {
+		ret = of_property_read_u32(np, cfg_params[i].property, &val);
+		/* flags don't have to have a value */
+		if (ret == -EOVERFLOW && cfg_params[i].flag) {
+			val = 1;
+			ret = 0;
+		}
+		if (!ret) {
+			config = pinconf_to_config_packed(cfg_params[i].param,
+							  val);
+			ret = add_config(dev, &configs, &num_configs, config);
+			if (ret < 0)
+				goto exit;
+		/* EINVAL=missing, which is fine since it's optional */
+		} else if (ret != -EINVAL) {
+			dev_err(dev, "could not parse property %s\n",
+				cfg_params[i].property);
+		}
+	}
+
+	reserve = 0;
+	if (function != NULL)
+		reserve++;
+	if (num_configs)
+		reserve++;
+	ret = of_property_count_strings(np, "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, "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;
+}
+
+int tz1090_pdc_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
+				      struct device_node *np_config,
+				      struct pinctrl_map **map,
+				      unsigned *num_maps)
+{
+	unsigned 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 function)
+{
+	return tz1090_pdc_functions[function].name;
+}
+
+static int tz1090_pdc_pinctrl_get_func_groups(struct pinctrl_dev *pctldev,
+					      unsigned function,
+					      const char * const **groups,
+					      unsigned * 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 function, unsigned 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 function, unsigned 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 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 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 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 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;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void tz1090_pdc_pinconf_config_dbg_show(struct pinctrl_dev *pctldev,
+					       struct seq_file *s,
+					       unsigned long config)
+{
+	enum pin_config_param param = pinconf_to_config_param(config);
+	u16 arg = pinconf_to_config_argument(config);
+	const char *pname = "unknown";
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cfg_params); i++) {
+		if (cfg_params[i].param == param) {
+			pname = cfg_params[i].property;
+			break;
+		}
+	}
+
+	seq_printf(s, "%s=%d", pname, arg);
+}
+#endif
+
+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,
+#ifdef CONFIG_DEBUG_FS
+	.pin_config_config_dbg_show	= tz1090_pdc_pinconf_config_dbg_show,
+#endif
+};
+
+/*
+ * 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);
+}
+postcore_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] 18+ messages in thread

* [PATCH v2 9/9] gpio-tz1090-pdc: add TZ1090 PDC gpio driver
  2013-05-24 16:21 [PATCH v2 0/9] Add some TZ1090 SoC infrastructure James Hogan
                   ` (4 preceding siblings ...)
  2013-05-24 16:21 ` [PATCH v2 8/9] pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver James Hogan
@ 2013-05-24 16:21 ` James Hogan
  2013-05-29 15:38   ` Linus Walleij
  5 siblings, 1 reply; 18+ messages in thread
From: James Hogan @ 2013-05-24 16:21 UTC (permalink / raw)
  To: linux-kernel
  Cc: James Hogan, Grant Likely, Rob Herring, Rob Landley,
	Linus Walleij, 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@secretlab.ca>
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 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   |  41 ++++
 arch/metag/boot/dts/tz1090.dtsi                    |  14 ++
 drivers/gpio/Kconfig                               |   7 +
 drivers/gpio/Makefile                              |   1 +
 drivers/gpio/gpio-tz1090-pdc.c                     | 242 +++++++++++++++++++++
 5 files changed, 305 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..a0e5eeb
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-tz1090-pdc.txt
@@ -0,0 +1,41 @@
+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:
+      1: 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 =	<1 0 0>,	/* Syswake 0 */
+				<1 1 0>,	/* Syswake 1 */
+				<1 2 0>;	/* Syswake 2 */
+	};
diff --git a/arch/metag/boot/dts/tz1090.dtsi b/arch/metag/boot/dts/tz1090.dtsi
index e2e61a8..4975e10 100644
--- a/arch/metag/boot/dts/tz1090.dtsi
+++ b/arch/metag/boot/dts/tz1090.dtsi
@@ -88,5 +88,19 @@
 				gpio-ranges = <&pinctrl 0 60 30>;
 			};
 		};
+
+		pdc_gpios: gpios@02006500 {
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			compatible = "img,tz1090-pdc-gpio";
+			reg = <0x02006500 0x100>;
+
+			interrupt-parent = <&pdc>;
+			interrupts =	<1 0 0>,	/* Syswake 0 */
+					<1 1 0>,	/* Syswake 1 */
+					<1 2 0>;	/* Syswake 2 */
+			gpio-ranges = <&pdc_pinctrl 0 0 7>;
+		};
 	};
 };
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 49e4f67..5cfae04 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..85fed6a
--- /dev/null
+++ b/drivers/gpio/gpio-tz1090-pdc.c
@@ -0,0 +1,242 @@
+/*
+ * 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 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 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 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 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 offset)
+{
+	return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void tz1090_pdc_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+	pinctrl_free_gpio(chip->base + offset);
+}
+
+static int tz1090_pdc_gpio_to_irq(struct gpio_chip *chip, unsigned 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);
+}
+postcore_initcall(tz1090_pdc_gpio_init);
-- 
1.8.1.2



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

* Re: [PATCH v2 6/9] pinctrl-tz1090: add TZ1090 pinctrl driver
  2013-05-24 16:21   ` [PATCH v2 6/9] pinctrl-tz1090: add TZ1090 pinctrl driver James Hogan
@ 2013-05-28  6:48     ` Linus Walleij
  0 siblings, 0 replies; 18+ messages in thread
From: Linus Walleij @ 2013-05-28  6:48 UTC (permalink / raw)
  To: James Hogan, Laurent Pinchart
  Cc: linux-kernel, Grant Likely, Rob Herring, Rob Landley, linux-doc,
	devicetree-discuss

Hi James, I want to route this patch by Laurent Pinchart, as he's also
adding device tree support for a SoC using generic pinconf.

Creating new bindings for every pin controller defining the same thing
and implementing the same OF parsers is not sane.

We need to:

- Define generic pinconf bindings
- Implement a common parser for these in drivers/pinctrl/pinconf-generic.c

I'll merge your patches to <linux/pinctrl/pinctrl-generic.h> as a
baseline.

On Fri, May 24, 2013 at 6:21 PM, James Hogan <james.hogan@imgtec.com> wrote:

(...)

> +Optional subnode-properties:
> +- 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.
> +- tristate: Flag, put pin into high impedance state.
> +- pull-up: Flag, pull pin high.
> +- pull-down: Flag, pull pin low.
> +- bus-hold: Flag, weak latch last value on tristate bus.
> +- schmitt: Integer, enable or disable Schmitt trigger mode for the pins.
> +    0: no hysteresis
> +    1: schmitt trigger
> +- 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

So if you want to use these names they shall be made generic.

Else you have to prefix everything with "tz1090,<property>"

> +/* Describes pinconf properties/flags available from device tree */
> +static const struct cfg_param {
> +       const char *property;
> +       enum pin_config_param param;
> +       bool flag;
> +} cfg_params[] = {
> +       {"tristate",            PIN_CONFIG_BIAS_HIGH_IMPEDANCE,         true},
> +       {"pull-up",             PIN_CONFIG_BIAS_PULL_UP,                true},
> +       {"pull-down",           PIN_CONFIG_BIAS_PULL_DOWN,              true},
> +       {"bus-hold",            PIN_CONFIG_BIAS_BUS_HOLD,               true},
> +       {"schmitt",             PIN_CONFIG_INPUT_SCHMITT_ENABLE,        true},
> +       {"slew-rate",           PIN_CONFIG_SLEW_RATE,                   false},
> +       {"drive-strength",      PIN_CONFIG_DRIVE_STRENGTH,              false},
> +};
> +
> +int tz1090_pinctrl_dt_subnode_to_map(struct device *dev,
> +                                    struct device_node *np,
> +                                    struct pinctrl_map **map,
> +                                    unsigned *reserved_maps,
> +                                    unsigned *num_maps)
> +{
> +       int ret, i;
> +       const char *function;
> +       u32 val;
> +       unsigned long config;
> +       unsigned long *configs = NULL;
> +       unsigned num_configs = 0;
> +       unsigned reserve;
> +       struct property *prop;
> +       const char *group;
> +
> +       ret = of_property_read_string(np, "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;
> +       }
> +
> +       for (i = 0; i < ARRAY_SIZE(cfg_params); i++) {
> +               ret = of_property_read_u32(np, cfg_params[i].property, &val);
> +               /* flags don't have to have a value */
> +               if (ret == -EOVERFLOW && cfg_params[i].flag) {
> +                       val = 1;
> +                       ret = 0;
> +               }
> +               if (!ret) {
> +                       config = pinconf_to_config_packed(cfg_params[i].param,
> +                                                         val);
> +                       ret = add_config(dev, &configs, &num_configs, config);
> +                       if (ret < 0)
> +                               goto exit;
> +               /* EINVAL=missing, which is fine since it's optional */
> +               } else if (ret != -EINVAL) {
> +                       dev_err(dev, "could not parse property %s (%d)\n",
> +                               cfg_params[i].property, ret);
> +               }
> +       }
> +
> +       reserve = 0;
> +       if (function != NULL)
> +               reserve++;
> +       if (num_configs)
> +               reserve++;
> +       ret = of_property_count_strings(np, "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, "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;
> +}

This is looking good. Can you split out the pin config mapping in
some way and put that into pinconf-generic.c?

Yours,
Linus Walleij

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

* Re: [PATCH v2 7/9] gpio-tz1090: add TZ1090 gpio driver
  2013-05-24 16:21 ` [PATCH v2 7/9] gpio-tz1090: add TZ1090 gpio driver James Hogan
@ 2013-05-29 15:32   ` Linus Walleij
  2013-05-29 16:06     ` James Hogan
  0 siblings, 1 reply; 18+ messages in thread
From: Linus Walleij @ 2013-05-29 15:32 UTC (permalink / raw)
  To: James Hogan
  Cc: linux-kernel, Grant Likely, Rob Herring, Rob Landley, linux-doc,
	devicetree-discuss

On Fri, May 24, 2013 at 6:21 PM, 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.

(...)

This is looking much better.

However I have some more improvement comments, due to new
knowledge. I am sorry about the moving target but it's not my fault...

Basically you should adopt the new style of referencing
flags using the preprocessor.

The examples can be found in the linux-next tree.

> +++ b/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
(...)
> +  - #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:
> +        1: active low

Reference flags from
#include <dt-bindings/gpio/gpio.h>

Then you can use the named constants:
GPIO_ACTIVE_HIGH, GPIO_ACTIVE_LOW

(...)
> +  - #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
> +        1: trigger on rising edge
> +        2: trigger on falling edge
> +        3: trigger on both rising and falling edges
> +        4: trigger when high
> +        8: trigger when low

This goes here as well.
#include <dt-bindings/interrupt-controller/irq.h>

Then you can use:
IRQ_TYPE_NONE. IRQ_TYPE_EDGE_RISING (etc)

> +       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 4 /* level */>;

And it will look like:
interrupts = <13 IRQ_TYPE_LEVEL_HIGH>;

Which is way easier to understand and you no longer
need to insert comments to explain things.

(...)
> +++ b/drivers/gpio/gpio-tz1090.c

(...)
> +/* REG_GPIO_IRQ_PLRT */
> +#define GPIO_POLARITY_LOW      0
> +#define GPIO_POLARITY_HIGH     1
> +
> +/* REG_GPIO_IRQ_TYPE */
> +#define GPIO_LEVEL_TRIGGERED   0
> +#define GPIO_EDGE_TRIGGERED    1

Why does the comment start with REG_* but not the actual
definition?

(...)
> +/* caller must hold LOCK2 */
> +static inline void _tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
> +                                       unsigned int reg_offs,
> +                                       unsigned int offset,
> +                                       int val)
> +{
> +       u32 value;
> +
> +       value = tz1090_gpio_read(bank, reg_offs);
> +       value &= ~BIT(offset);
> +       value |= !!val << offset;

I can't parse that last line, it is equivalent to writing:

if (val)
    value |= BIT(offset);

Which I think is easier to understand.


> +/* 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;
> +
> +       __global_lock2(lstat);
> +       /* irq_polarity[offset] = !input[offset] */

This comments probably need to be a bit more verbose, like explain
to readers what is happening here.

> +       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);
> +}

This looks like it'll work though, nice!

(...)
> +static struct of_device_id tz1090_gpio_of_match[] = {
> +       { .compatible = "img,tz1090-gpio" },
> +       { },
> +};

(...)
> +postcore_initcall(tz1090_gpio_init);

Is it really necessary to have this so early?

Apart from these remarks it is looking very good.

Yours,
Linus Walleij

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

* Re: [PATCH v2 8/9] pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver
  2013-05-24 16:21 ` [PATCH v2 8/9] pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver James Hogan
@ 2013-05-29 15:36   ` Linus Walleij
  0 siblings, 0 replies; 18+ messages in thread
From: Linus Walleij @ 2013-05-29 15:36 UTC (permalink / raw)
  To: James Hogan
  Cc: linux-kernel, Grant Likely, Rob Herring, Rob Landley, linux-doc,
	devicetree-discuss

On Fri, May 24, 2013 at 6:21 PM, James Hogan <james.hogan@imgtec.com> wrote:

> +++ b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt

> +Optional subnode-properties:
> +- 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.
> +- tristate: Flag, put pin into high impedance state.
> +- pull-up: Flag, pull pin high.
> +- pull-down: Flag: pull pin low.
> +- bus-hold: Flag, weak latch last value on tristate bus.
> +- schmitt: Integer, enable or disable Schmitt trigger mode for the pins.
> +    0: no hysteresis
> +    1: schmitt trigger
> +- 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
> +- power-on-start: Integer, power on start value of pins
> +    0: weak pull-down disabled
> +    1: weak pull-down for invalid power

Like on the first pinctrl patch, this needs to be genercized and moved to
the core, you're even adding code duplication between two drivers here,
so we really need to do something about it.

(...)
> +/* Describes pinconf properties/flags available from device tree */
> +static const struct cfg_param {
> +       const char *property;
> +       enum pin_config_param param;
> +       bool flag;
> +} cfg_params[] = {
> +       {"tristate",            PIN_CONFIG_BIAS_HIGH_IMPEDANCE,         true},
> +       {"pull-up",             PIN_CONFIG_BIAS_PULL_UP,                true},
> +       {"pull-down",           PIN_CONFIG_BIAS_PULL_DOWN,              true},
> +       {"bus-hold",            PIN_CONFIG_BIAS_BUS_HOLD,               true},
> +       {"power-on-start",      PIN_CONFIG_LOW_POWER_MODE,              true},
> +       {"schmitt",             PIN_CONFIG_INPUT_SCHMITT_ENABLE,        true},
> +       {"slew-rate",           PIN_CONFIG_SLEW_RATE,                   false},
> +       {"drive-strength",      PIN_CONFIG_DRIVE_STRENGTH,              false},
> +
> +};

Like this is looking very familiar.

Apart from that is it looking nice.

Yours,
Linus Walleij

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

* Re: [PATCH v2 9/9] gpio-tz1090-pdc: add TZ1090 PDC gpio driver
  2013-05-24 16:21 ` [PATCH v2 9/9] gpio-tz1090-pdc: add TZ1090 PDC gpio driver James Hogan
@ 2013-05-29 15:38   ` Linus Walleij
  2013-05-29 16:12     ` James Hogan
  0 siblings, 1 reply; 18+ messages in thread
From: Linus Walleij @ 2013-05-29 15:38 UTC (permalink / raw)
  To: James Hogan
  Cc: linux-kernel, Grant Likely, Rob Herring, Rob Landley, linux-doc,
	devicetree-discuss

On Fri, May 24, 2013 at 6:21 PM, James Hogan <james.hogan@imgtec.com> wrote:

> Add a GPIO driver for the low-power Powerdown Controller GPIOs in the
> TZ1090 SoC.
(...)
> +  Values for gpio specifier:
> +  - GPIO number: a value in the range 0 to 6.
> +  - GPIO flags: bit field of flags:
> +      1: active low

Use #include <dt-bindings/gpio/gpio.h>
for flags and symbolic names.

> +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 =    <1 0 0>,        /* Syswake 0 */
> +                               <1 1 0>,        /* Syswake 1 */
> +                               <1 2 0>;        /* Syswake 2 */
> +       };

Interrupt flags seem to be undocumented here.

Apart from this it's looking nice.

Yours,
Linus Walleij

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

* Re: [PATCH v2 7/9] gpio-tz1090: add TZ1090 gpio driver
  2013-05-29 15:32   ` Linus Walleij
@ 2013-05-29 16:06     ` James Hogan
  2013-05-29 19:03       ` Andy Shevchenko
  0 siblings, 1 reply; 18+ messages in thread
From: James Hogan @ 2013-05-29 16:06 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-kernel, Grant Likely, Rob Herring, Rob Landley, linux-doc,
	devicetree-discuss

On 29/05/13 16:32, Linus Walleij wrote:
> On Fri, May 24, 2013 at 6:21 PM, 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.
> 
> (...)
> 
> This is looking much better.
> 
> However I have some more improvement comments, due to new
> knowledge. I am sorry about the moving target but it's not my fault...

No worries :-)

> And it will look like:
> interrupts = <13 IRQ_TYPE_LEVEL_HIGH>;
> 
> Which is way easier to understand and you no longer
> need to insert comments to explain things.

Very nice. I like this. Thanks.

>> +/* REG_GPIO_IRQ_PLRT */
>> +#define GPIO_POLARITY_LOW      0
>> +#define GPIO_POLARITY_HIGH     1
>> +
>> +/* REG_GPIO_IRQ_TYPE */
>> +#define GPIO_LEVEL_TRIGGERED   0
>> +#define GPIO_EDGE_TRIGGERED    1
> 
> Why does the comment start with REG_* but not the actual
> definition?

Legacy reasons (those constants were originally in that
<asm/soc-tz1090/gpio.h> header). I'll clean up the naming.

> 
> (...)
>> +/* caller must hold LOCK2 */
>> +static inline void _tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
>> +                                       unsigned int reg_offs,
>> +                                       unsigned int offset,
>> +                                       int val)
>> +{
>> +       u32 value;
>> +
>> +       value = tz1090_gpio_read(bank, reg_offs);
>> +       value &= ~BIT(offset);
>> +       value |= !!val << offset;
> 
> I can't parse that last line, it is equivalent to writing:
> 
> if (val)
>     value |= BIT(offset);
> 
> Which I think is easier to understand.

Apparently I was demonstrating how premature optimisation is the root of
all evil (as disassembling it testifies) :-). I'll stop doing this.

> 
> 
>> +/* 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;
>> +
>> +       __global_lock2(lstat);
>> +       /* irq_polarity[offset] = !input[offset] */
> 
> This comments probably need to be a bit more verbose, like explain
> to readers what is happening here.

Okay

>> +postcore_initcall(tz1090_gpio_init);
> 
> Is it really necessary to have this so early?

It was necessary when I wanted GPIO setup to precede platform code that
hadn't been converted to DT yet but messed with GPIOs. For upstream I
think I can change it to subsys_initcall to match the majority of other
gpio drivers.

(for the record, in drivers/gpio/*.c:
      1 core_initcall
      1 device_initcall
      1 late_initcall
      2 pure_initcall
      3 arch_initcall
     14 postcore_initcall
     29 subsys_initcall)

> Apart from these remarks it is looking very good.

Thanks for reviewing.

Cheers
James


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

* Re: [PATCH v2 9/9] gpio-tz1090-pdc: add TZ1090 PDC gpio driver
  2013-05-29 15:38   ` Linus Walleij
@ 2013-05-29 16:12     ` James Hogan
  2013-05-31 12:17       ` James Hogan
  0 siblings, 1 reply; 18+ messages in thread
From: James Hogan @ 2013-05-29 16:12 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-kernel, Grant Likely, Rob Herring, Rob Landley, linux-doc,
	devicetree-discuss

On 29/05/13 16:38, Linus Walleij wrote:
> On Fri, May 24, 2013 at 6:21 PM, James Hogan <james.hogan@imgtec.com> wrote:
>> +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 =    <1 0 0>,        /* Syswake 0 */
>> +                               <1 1 0>,        /* Syswake 1 */
>> +                               <1 2 0>;        /* Syswake 2 */
>> +       };
> 
> Interrupt flags seem to be undocumented here.

Hmmm yes, good spot.

Thanks
James


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

* Re: [PATCH v2 7/9] gpio-tz1090: add TZ1090 gpio driver
  2013-05-29 16:06     ` James Hogan
@ 2013-05-29 19:03       ` Andy Shevchenko
  0 siblings, 0 replies; 18+ messages in thread
From: Andy Shevchenko @ 2013-05-29 19:03 UTC (permalink / raw)
  To: James Hogan
  Cc: Linus Walleij, linux-kernel, Grant Likely, Rob Herring,
	Rob Landley, linux-doc, devicetree-discuss

On Wed, May 29, 2013 at 7:06 PM, James Hogan <james.hogan@imgtec.com> wrote:
> (for the record, in drivers/gpio/*.c:
>       1 core_initcall
>       1 device_initcall

I think this one should go to the subsys_initicall (I didn't check,
but I recall that is gpio-langwell driver).

>       1 late_initcall
>       2 pure_initcall
>       3 arch_initcall
>      14 postcore_initcall
>      29 subsys_initcall)


--
With Best Regards,
Andy Shevchenko

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

* Re: [PATCH v2 9/9] gpio-tz1090-pdc: add TZ1090 PDC gpio driver
  2013-05-29 16:12     ` James Hogan
@ 2013-05-31 12:17       ` James Hogan
  0 siblings, 0 replies; 18+ messages in thread
From: James Hogan @ 2013-05-31 12:17 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-kernel, Grant Likely, Rob Herring, Rob Landley, linux-doc,
	devicetree-discuss

On 29/05/13 17:12, James Hogan wrote:
> On 29/05/13 16:38, Linus Walleij wrote:
>> On Fri, May 24, 2013 at 6:21 PM, James Hogan <james.hogan@imgtec.com> wrote:
>>> +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 =    <1 0 0>,        /* Syswake 0 */
>>> +                               <1 1 0>,        /* Syswake 1 */
>>> +                               <1 2 0>;        /* Syswake 2 */
>>> +       };
>>
>> Interrupt flags seem to be undocumented here.
> 
> Hmmm yes, good spot.

Looking again at this, the driver doesn't expose any new interrupts.
These 3 interrupts are simply passed on by the gpio chip's to_irq()
callback. The flags in the example depend entirely on the interrupt
parent, in this case the PDC irq driver in patch 2, so they don't need
documenting here.

Cheers
James


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

* Re: [PATCH v2 1/9] metag: of_platform_populate from arch generic code
  2013-05-24 16:21   ` [PATCH v2 1/9] metag: of_platform_populate from arch generic code James Hogan
@ 2013-06-13 12:44     ` James Hogan
  0 siblings, 0 replies; 18+ messages in thread
From: James Hogan @ 2013-06-13 12:44 UTC (permalink / raw)
  To: James Hogan
  Cc: linux-kernel, Grant Likely, Rob Herring, Arnd Bergmann,
	devicetree-discuss

On 24/05/13 17:21, James Hogan wrote:
> If no init_machine callback is provided, call of_platform_populate()
> instead. This allows a board/SoC that only needs to call
> of_platform_populate to omit the callback altogether.
> 
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> Cc: Grant Likely <grant.likely@secretlab.ca>
> Cc: Rob Herring <rob.herring@calxeda.com>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: devicetree-discuss@lists.ozlabs.org

I've applied this patch for v3.11

Cheers
James

> ---
>  arch/metag/kernel/setup.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/arch/metag/kernel/setup.c b/arch/metag/kernel/setup.c
> index 4f5726f..e18cebb 100644
> --- a/arch/metag/kernel/setup.c
> +++ b/arch/metag/kernel/setup.c
> @@ -20,6 +20,7 @@
>  #include <linux/memblock.h>
>  #include <linux/mm.h>
>  #include <linux/of_fdt.h>
> +#include <linux/of_platform.h>
>  #include <linux/pfn.h>
>  #include <linux/root_dev.h>
>  #include <linux/sched.h>
> @@ -424,6 +425,9 @@ static int __init customize_machine(void)
>  	/* customizes platform devices, or adds new ones */
>  	if (machine_desc->init_machine)
>  		machine_desc->init_machine();
> +	else
> +		of_platform_populate(NULL, of_default_bus_match_table, NULL,
> +				     NULL);
>  	return 0;
>  }
>  arch_initcall(customize_machine);
> 

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

* Re: [PATCH v2 2/9] metag: minimal TZ1090 (Comet) SoC infrastructure
  2013-05-24 16:21 ` [PATCH v2 2/9] metag: minimal TZ1090 (Comet) " James Hogan
@ 2013-06-13 12:44   ` James Hogan
  0 siblings, 0 replies; 18+ messages in thread
From: James Hogan @ 2013-06-13 12:44 UTC (permalink / raw)
  To: James Hogan
  Cc: linux-kernel, Grant Likely, Rob Herring, Rob Landley,
	Arnd Bergmann, linux-doc, devicetree-discuss

On 24/05/13 17:21, James Hogan wrote:
> Add really minimal support for Toumaz Xenif TZ1090 SoC (A.K.A. Comet).
> This consists of minimal build infrastructure, device tree files, and a
> defconfig based on meta2_defconfig.
> 
> This SoC contains a 2-threaded HTP (Meta 2) as the main application
> processor, and is found in a number of development boards and digital
> radios, such as the Minimorph Development Platform.
> 
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> Cc: Grant Likely <grant.likely@secretlab.ca>
> Cc: Rob Herring <rob.herring@calxeda.com>
> Cc: Rob Landley <rob@landley.net>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: linux-doc@vger.kernel.org
> Cc: devicetree-discuss@lists.ozlabs.org
> ---
> Changes in v2:
>  - removed arch/metag/soc/ directory

I've applied this patch for v3.11

Cheers
James

> 
>  .../devicetree/bindings/vendor-prefixes.txt        |  1 +
>  arch/metag/Kconfig.soc                             |  9 +++++
>  arch/metag/Makefile                                |  2 +-
>  arch/metag/boot/dts/Makefile                       |  2 ++
>  arch/metag/boot/dts/tz1090.dtsi                    | 29 +++++++++++++++
>  arch/metag/boot/dts/tz1090_generic.dts             | 10 ++++++
>  arch/metag/configs/tz1090_defconfig                | 42 ++++++++++++++++++++++
>  7 files changed, 94 insertions(+), 1 deletion(-)
>  create mode 100644 arch/metag/boot/dts/tz1090.dtsi
>  create mode 100644 arch/metag/boot/dts/tz1090_generic.dts
>  create mode 100644 arch/metag/configs/tz1090_defconfig
> 
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index 6931c43..be80992 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -57,6 +57,7 @@ snps 	Synopsys, Inc.
>  st	STMicroelectronics
>  ste	ST-Ericsson
>  stericsson	ST-Ericsson
> +toumaz	Toumaz
>  ti	Texas Instruments
>  toshiba	Toshiba Corporation
>  via	VIA Technologies, Inc.
> diff --git a/arch/metag/Kconfig.soc b/arch/metag/Kconfig.soc
> index ec079cf..653b479 100644
> --- a/arch/metag/Kconfig.soc
> +++ b/arch/metag/Kconfig.soc
> @@ -14,6 +14,15 @@ config META21_FPGA
>  	help
>  	  This is a Meta 2.1 FPGA bitstream, just a bare CPU.
>  
> +config SOC_TZ1090
> +	bool "Toumaz Xenif TZ1090 SoC (Comet)"
> +	select METAG_LNKGET_AROUND_CACHE
> +	select METAG_META21
> +	select METAG_SMP_WRITE_REORDERING
> +	help
> +	  This is a Toumaz Technology Xenif TZ1090 (A.K.A. Comet) SoC containing
> +	  a 2-threaded HTP.
> +
>  endchoice
>  
>  menu "SoC configuration"
> diff --git a/arch/metag/Makefile b/arch/metag/Makefile
> index b566116..9739857 100644
> --- a/arch/metag/Makefile
> +++ b/arch/metag/Makefile
> @@ -20,7 +20,7 @@ checkflags-$(CONFIG_METAG_META12)	+= -DMETAC_1_2
>  checkflags-$(CONFIG_METAG_META21)	+= -DMETAC_2_1
>  CHECKFLAGS				+= -D__metag__ $(checkflags-y)
>  
> -KBUILD_DEFCONFIG			:= meta2_defconfig
> +KBUILD_DEFCONFIG			:= tz1090_defconfig
>  
>  sflags-$(CONFIG_METAG_META12)		+= -mmetac=1.2
>  ifeq ($(CONFIG_METAG_META12),y)
> diff --git a/arch/metag/boot/dts/Makefile b/arch/metag/boot/dts/Makefile
> index dbd9521..72c1218 100644
> --- a/arch/metag/boot/dts/Makefile
> +++ b/arch/metag/boot/dts/Makefile
> @@ -1,7 +1,9 @@
>  dtb-y	+= skeleton.dtb
> +dtb-y	+= tz1090_generic.dtb
>  
>  # Built-in dtb
>  builtindtb-y				:= skeleton
> +builtindtb-$(CONFIG_SOC_TZ1090)		:= tz1090_generic
>  
>  ifneq ($(CONFIG_METAG_BUILTIN_DTB_NAME),"")
>  	builtindtb-y			:= $(patsubst "%",%,$(CONFIG_METAG_BUILTIN_DTB_NAME))
> diff --git a/arch/metag/boot/dts/tz1090.dtsi b/arch/metag/boot/dts/tz1090.dtsi
> new file mode 100644
> index 0000000..ca057f0
> --- /dev/null
> +++ b/arch/metag/boot/dts/tz1090.dtsi
> @@ -0,0 +1,29 @@
> +/*
> + * Copyright (C) 2012 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/ "skeleton.dtsi"
> +
> +/ {
> +	compatible = "toumaz,tz1090", "img,meta";
> +
> +	interrupt-parent = <&intc>;
> +
> +	intc: interrupt-controller {
> +		compatible = "img,meta-intc";
> +		interrupt-controller;
> +		#interrupt-cells = <2>;
> +		num-banks = <2>;
> +	};
> +
> +	soc {
> +		compatible = "simple-bus";
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		ranges;
> +	};
> +};
> diff --git a/arch/metag/boot/dts/tz1090_generic.dts b/arch/metag/boot/dts/tz1090_generic.dts
> new file mode 100644
> index 0000000..aa826cb
> --- /dev/null
> +++ b/arch/metag/boot/dts/tz1090_generic.dts
> @@ -0,0 +1,10 @@
> +/*
> + * Copyright (C) 2012 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.
> + */
> +/dts-v1/;
> +
> +/include/ "tz1090.dtsi"
> diff --git a/arch/metag/configs/tz1090_defconfig b/arch/metag/configs/tz1090_defconfig
> new file mode 100644
> index 0000000..9f9316a
> --- /dev/null
> +++ b/arch/metag/configs/tz1090_defconfig
> @@ -0,0 +1,42 @@
> +# CONFIG_LOCALVERSION_AUTO is not set
> +# CONFIG_SWAP is not set
> +CONFIG_SYSVIPC=y
> +CONFIG_SYSFS_DEPRECATED=y
> +CONFIG_SYSFS_DEPRECATED_V2=y
> +CONFIG_KALLSYMS_ALL=y
> +# CONFIG_ELF_CORE is not set
> +CONFIG_SLAB=y
> +# CONFIG_BLK_DEV_BSG is not set
> +CONFIG_PARTITION_ADVANCED=y
> +# CONFIG_MSDOS_PARTITION is not set
> +# CONFIG_IOSCHED_DEADLINE is not set
> +# CONFIG_IOSCHED_CFQ is not set
> +CONFIG_FLATMEM_MANUAL=y
> +CONFIG_SOC_TZ1090=y
> +CONFIG_METAG_HALT_ON_PANIC=y
> +# CONFIG_METAG_FPU is not set
> +CONFIG_METAG_DA=y
> +CONFIG_HZ_100=y
> +CONFIG_DEVTMPFS=y
> +# CONFIG_STANDALONE is not set
> +# CONFIG_PREVENT_FIRMWARE_BUILD is not set
> +# CONFIG_FW_LOADER is not set
> +CONFIG_BLK_DEV_RAM=y
> +CONFIG_BLK_DEV_RAM_COUNT=1
> +CONFIG_BLK_DEV_RAM_SIZE=16384
> +# CONFIG_INPUT is not set
> +# CONFIG_SERIO is not set
> +# CONFIG_VT is not set
> +# CONFIG_LEGACY_PTYS is not set
> +CONFIG_DA_TTY=y
> +CONFIG_DA_CONSOLE=y
> +# CONFIG_DEVKMEM is not set
> +# CONFIG_HW_RANDOM is not set
> +CONFIG_GPIOLIB=y
> +# CONFIG_HWMON is not set
> +# CONFIG_USB_SUPPORT is not set
> +# CONFIG_DNOTIFY is not set
> +CONFIG_TMPFS=y
> +# CONFIG_MISC_FILESYSTEMS is not set
> +# CONFIG_SCHED_DEBUG is not set
> +CONFIG_DEBUG_INFO=y
> 

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

end of thread, other threads:[~2013-06-13 12:44 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-05-24 16:21 [PATCH v2 0/9] Add some TZ1090 SoC infrastructure James Hogan
2013-05-24 16:21 ` [PATCH v2 2/9] metag: minimal TZ1090 (Comet) " James Hogan
2013-06-13 12:44   ` James Hogan
2013-05-24 16:21 ` [PATCH v2 3/9] irq-imgpdc: add ImgTec PDC irqchip driver James Hogan
     [not found] ` <1369412476-14245-1-git-send-email-james.hogan-1AXoQHu6uovQT0dZR+AlfA@public.gmane.org>
2013-05-24 16:21   ` [PATCH v2 1/9] metag: of_platform_populate from arch generic code James Hogan
2013-06-13 12:44     ` James Hogan
2013-05-24 16:21   ` [PATCH v2 6/9] pinctrl-tz1090: add TZ1090 pinctrl driver James Hogan
2013-05-28  6:48     ` Linus Walleij
2013-05-24 16:21 ` [PATCH v2 7/9] gpio-tz1090: add TZ1090 gpio driver James Hogan
2013-05-29 15:32   ` Linus Walleij
2013-05-29 16:06     ` James Hogan
2013-05-29 19:03       ` Andy Shevchenko
2013-05-24 16:21 ` [PATCH v2 8/9] pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver James Hogan
2013-05-29 15:36   ` Linus Walleij
2013-05-24 16:21 ` [PATCH v2 9/9] gpio-tz1090-pdc: add TZ1090 PDC gpio driver James Hogan
2013-05-29 15:38   ` Linus Walleij
2013-05-29 16:12     ` James Hogan
2013-05-31 12:17       ` James Hogan

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).