2013-04-23 14:36:56

by James Hogan

[permalink] [raw]
Subject: [PATCH 0/8] Add some TZ1090 SoC infrastructure

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

I aim to get whatever I can of this patchset into v3.10 (at least the
first two patches) so all review is very much appreciated.

Cc: Arnd Bergmann <[email protected]>
Cc: Thomas Gleixner <[email protected]>

James Hogan (8):
metag: of_platform_populate from arch generic code
metag: minimal TZ1090 (Comet) SoC infrastructure
irq-imgpdc: add ImgTec PDC irqchip driver
metag: tz1090: add <asm/soc-tz1090/gpio.h>
pinctrl-tz1090: add TZ1090 pinctrl driver
gpio-tz1090: add TZ1090 gpio driver
pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver
gpio-tz1090pdc: add TZ1090 PDC gpio driver

.../devicetree/bindings/gpio/gpio-tz1090-pdc.txt | 40 +
.../devicetree/bindings/gpio/gpio-tz1090.txt | 77 +
.../devicetree/bindings/metag/pdc-intc.txt | 112 ++
.../bindings/pinctrl/img,tz1090-pdc-pinctrl.txt | 125 ++
.../bindings/pinctrl/img,tz1090-pinctrl.txt | 214 +++
.../devicetree/bindings/vendor-prefixes.txt | 1 +
arch/metag/Kconfig.soc | 14 +
arch/metag/Makefile | 8 +-
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 | 41 +
arch/metag/include/asm/soc-tz1090/gpio.h | 121 ++
arch/metag/kernel/setup.c | 4 +
arch/metag/soc/tz1090/Makefile | 5 +
arch/metag/soc/tz1090/setup.c | 21 +
drivers/gpio/Kconfig | 14 +
drivers/gpio/Makefile | 2 +
drivers/gpio/gpio-tz1090-pdc.c | 237 +++
drivers/gpio/gpio-tz1090.c | 628 ++++++
drivers/irqchip/Kconfig | 4 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-imgpdc.c | 553 ++++++
drivers/pinctrl/Kconfig | 12 +
drivers/pinctrl/Makefile | 2 +
drivers/pinctrl/pinctrl-tz1090-pdc.c | 1114 +++++++++++
drivers/pinctrl/pinctrl-tz1090-pdc.h | 58 +
drivers/pinctrl/pinctrl-tz1090.c | 2018 ++++++++++++++++++++
drivers/pinctrl/pinctrl-tz1090.h | 58 +
29 files changed, 5601 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 arch/metag/include/asm/soc-tz1090/gpio.h
create mode 100644 arch/metag/soc/tz1090/Makefile
create mode 100644 arch/metag/soc/tz1090/setup.c
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-pdc.h
create mode 100644 drivers/pinctrl/pinctrl-tz1090.c
create mode 100644 drivers/pinctrl/pinctrl-tz1090.h

--
1.8.1.2


2013-04-23 14:34:33

by James Hogan

[permalink] [raw]
Subject: [PATCH 4/8] metag: tz1090: add <asm/soc-tz1090/gpio.h>

Add <asm/soc-tz1090/gpio.h> with definitions to number all the GPIOs
available on-chip. This is for use by the pinctrl and GPIO drivers.

Signed-off-by: James Hogan <[email protected]>
---
arch/metag/include/asm/soc-tz1090/gpio.h | 121 +++++++++++++++++++++++++++++++
1 file changed, 121 insertions(+)
create mode 100644 arch/metag/include/asm/soc-tz1090/gpio.h

diff --git a/arch/metag/include/asm/soc-tz1090/gpio.h b/arch/metag/include/asm/soc-tz1090/gpio.h
new file mode 100644
index 0000000..d19fe02
--- /dev/null
+++ b/arch/metag/include/asm/soc-tz1090/gpio.h
@@ -0,0 +1,121 @@
+#ifndef _TZ1090_GPIO_H_
+#define _TZ1090_GPIO_H_
+
+/* TZ1090 has 90 main GPIOs, in 3 banks of 30. */
+#define NR_BUILTIN_GPIO 90
+/* Plus 7 PDC GPIOs */
+#define NR_PDC_GPIO 7
+
+#define GPIO_0_BASE 0
+#define GPIO_0_PIN(x) (GPIO_0_BASE + (x))
+
+#define GPIO_1_BASE 30
+#define GPIO_1_PIN(x) (GPIO_1_BASE + (x))
+
+#define GPIO_2_BASE 60
+#define GPIO_2_PIN(x) (GPIO_2_BASE + (x))
+
+#define GPIO_PDC_BASE NR_BUILTIN_GPIO
+#define GPIO_PDC_PIN(x) (GPIO_PDC_BASE + (x))
+
+#define GPIO_SDIO_CLK GPIO_0_PIN(0)
+#define GPIO_SDIO_CMD GPIO_0_PIN(1)
+#define GPIO_SDIO_D0 GPIO_0_PIN(2)
+#define GPIO_SDIO_D1 GPIO_0_PIN(3)
+#define GPIO_SDIO_D2 GPIO_0_PIN(4)
+#define GPIO_SDIO_D3 GPIO_0_PIN(5)
+#define GPIO_SDH_CD GPIO_0_PIN(6)
+#define GPIO_SDH_WP GPIO_0_PIN(7)
+#define GPIO_SPI0_MCLK GPIO_0_PIN(8)
+#define GPIO_SPI0_CS0 GPIO_0_PIN(9)
+#define GPIO_SPI0_CS1 GPIO_0_PIN(10)
+#define GPIO_SPI0_CS2 GPIO_0_PIN(11)
+#define GPIO_SPI0_DOUT GPIO_0_PIN(12)
+#define GPIO_SPI0_DIN GPIO_0_PIN(13)
+#define GPIO_SPI1_MCLK GPIO_0_PIN(14)
+#define GPIO_SPI1_CS0 GPIO_0_PIN(15)
+#define GPIO_SPI1_CS1 GPIO_0_PIN(16)
+#define GPIO_SPI1_CS2 GPIO_0_PIN(17)
+#define GPIO_SPI1_DOUT GPIO_0_PIN(18)
+#define GPIO_SPI1_DIN GPIO_0_PIN(19)
+#define GPIO_UART0_RXD GPIO_0_PIN(20)
+#define GPIO_UART0_TXD GPIO_0_PIN(21)
+#define GPIO_UART0_CTS GPIO_0_PIN(22)
+#define GPIO_UART0_RTS GPIO_0_PIN(23)
+#define GPIO_UART1_RXD GPIO_0_PIN(24)
+#define GPIO_UART1_TXD GPIO_0_PIN(25)
+#define GPIO_SCB0_SDAT GPIO_0_PIN(26)
+#define GPIO_SCB0_SCLK GPIO_0_PIN(27)
+#define GPIO_SCB1_SDAT GPIO_0_PIN(28)
+#define GPIO_SCB1_SCLK GPIO_0_PIN(29)
+#define GPIO_SCB2_SDAT GPIO_1_PIN(0)
+#define GPIO_SCB2_SCLK GPIO_1_PIN(1)
+#define GPIO_I2S_MCLK GPIO_1_PIN(2)
+#define GPIO_I2S_BCLK_OUT GPIO_1_PIN(3)
+#define GPIO_I2S_LRCLK_OUT GPIO_1_PIN(4)
+#define GPIO_I2S_DOUT0 GPIO_1_PIN(5)
+#define GPIO_I2S_DOUT1 GPIO_1_PIN(6)
+#define GPIO_I2S_DOUT2 GPIO_1_PIN(7)
+#define GPIO_I2S_DIN GPIO_1_PIN(8)
+#define GPIO_PDM_A GPIO_1_PIN(9)
+#define GPIO_PDM_B GPIO_1_PIN(10)
+#define GPIO_PDM_C GPIO_1_PIN(11)
+#define GPIO_PDM_D GPIO_1_PIN(12)
+#define GPIO_TFT_RED0 GPIO_1_PIN(13)
+#define GPIO_TFT_RED1 GPIO_1_PIN(14)
+#define GPIO_TFT_RED2 GPIO_1_PIN(15)
+#define GPIO_TFT_RED3 GPIO_1_PIN(16)
+#define GPIO_TFT_RED4 GPIO_1_PIN(17)
+#define GPIO_TFT_RED5 GPIO_1_PIN(18)
+#define GPIO_TFT_RED6 GPIO_1_PIN(19)
+#define GPIO_TFT_RED7 GPIO_1_PIN(20)
+#define GPIO_TFT_GREEN0 GPIO_1_PIN(21)
+#define GPIO_TFT_GREEN1 GPIO_1_PIN(22)
+#define GPIO_TFT_GREEN2 GPIO_1_PIN(23)
+#define GPIO_TFT_GREEN3 GPIO_1_PIN(24)
+#define GPIO_TFT_GREEN4 GPIO_1_PIN(25)
+#define GPIO_TFT_GREEN5 GPIO_1_PIN(26)
+#define GPIO_TFT_GREEN6 GPIO_1_PIN(27)
+#define GPIO_TFT_GREEN7 GPIO_1_PIN(28)
+#define GPIO_TFT_BLUE0 GPIO_1_PIN(29)
+#define GPIO_TFT_BLUE1 GPIO_2_PIN(0)
+#define GPIO_TFT_BLUE2 GPIO_2_PIN(1)
+#define GPIO_TFT_BLUE3 GPIO_2_PIN(2)
+#define GPIO_TFT_BLUE4 GPIO_2_PIN(3)
+#define GPIO_TFT_BLUE5 GPIO_2_PIN(4)
+#define GPIO_TFT_BLUE6 GPIO_2_PIN(5)
+#define GPIO_TFT_BLUE7 GPIO_2_PIN(6)
+#define GPIO_TFT_VDDEN_GD GPIO_2_PIN(7)
+#define GPIO_TFT_PANELCLK GPIO_2_PIN(8)
+#define GPIO_TFT_BLANK_LS GPIO_2_PIN(9)
+#define GPIO_TFT_VSYNC_NS GPIO_2_PIN(10)
+#define GPIO_TFT_HSYNC_NR GPIO_2_PIN(11)
+#define GPIO_TFT_VD12ACB GPIO_2_PIN(12)
+#define GPIO_TFT_PWRSAVE GPIO_2_PIN(13)
+#define GPIO_TX_ON GPIO_2_PIN(14)
+#define GPIO_RX_ON GPIO_2_PIN(15)
+#define GPIO_PLL_ON GPIO_2_PIN(16)
+#define GPIO_PA_ON GPIO_2_PIN(17)
+#define GPIO_RX_HP GPIO_2_PIN(18)
+#define GPIO_GAIN0 GPIO_2_PIN(19)
+#define GPIO_GAIN1 GPIO_2_PIN(20)
+#define GPIO_GAIN2 GPIO_2_PIN(21)
+#define GPIO_GAIN3 GPIO_2_PIN(22)
+#define GPIO_GAIN4 GPIO_2_PIN(23)
+#define GPIO_GAIN5 GPIO_2_PIN(24)
+#define GPIO_GAIN6 GPIO_2_PIN(25)
+#define GPIO_GAIN7 GPIO_2_PIN(26)
+#define GPIO_ANT_SEL0 GPIO_2_PIN(27)
+#define GPIO_ANT_SEL1 GPIO_2_PIN(28)
+#define GPIO_SDH_CLK_IN GPIO_2_PIN(29)
+
+/* PDC GPIOs */
+#define GPIO_PDC_GPIO0 GPIO_PDC_PIN(0)
+#define GPIO_PDC_GPIO1 GPIO_PDC_PIN(1)
+#define GPIO_SYS_WAKE0 GPIO_PDC_PIN(2)
+#define GPIO_SYS_WAKE1 GPIO_PDC_PIN(3)
+#define GPIO_SYS_WAKE2 GPIO_PDC_PIN(4)
+#define GPIO_IR_DATA GPIO_PDC_PIN(5)
+#define GPIO_EXT_POWER GPIO_PDC_PIN(6)
+
+#endif
--
1.8.1.2

2013-04-23 14:34:40

by James Hogan

[permalink] [raw]
Subject: [PATCH 7/8] pinctrl-tz1090-pdc: add TZ1090 PDC pinctrl driver

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 <[email protected]>
Cc: Grant Likely <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: Rob Landley <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: [email protected]
---
.../bindings/pinctrl/img,tz1090-pdc-pinctrl.txt | 125 +++
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 | 1114 ++++++++++++++++++++
drivers/pinctrl/pinctrl-tz1090-pdc.h | 58 +
7 files changed, 1311 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt
create mode 100644 drivers/pinctrl/pinctrl-tz1090-pdc.c
create mode 100644 drivers/pinctrl/pinctrl-tz1090-pdc.h

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..308a619
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt
@@ -0,0 +1,125 @@
+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.
+- pull: Integer, representing the pull-down/up to apply to the pin(s).
+ 0: tri-state
+ 1: pull-up
+ 2: pull-down
+ 3: repeater
+- 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.
+ 0: 2mA
+ 1: 4mA
+ 2: 8mA
+ 4: 12mA
+- pos: 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 pull (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 pos.
+
+ pdc
+ pins: gpio0, gpio1, sys_wake0, sys_wake1, sys_wake2, ir_data,
+ ext_power.
+
+Example:
+
+ [email protected] {
+ #gpio-range-cells = <2>;
+ compatible = "img,tz1090-pdc-pinctrl";
+ reg = <0x02006500 0x100>;
+ };
+
+Example board file extract:
+
+ [email protected] {
+ syswake_default: syswakes {
+ syswake_cfg {
+ pins = "sys_wake0",
+ "sys_wake1",
+ "sys_wake2";
+ pull = <1>;
+ };
+ };
+ irmod_default: irmod {
+ gpio0_cfg {
+ pins = "gpio0";
+ function = "ir_mod_stable_out";
+ };
+ gpio1_cfg {
+ pins = "gpio1";
+ function = "ir_mod_power_out";
+ };
+ };
+ };
+
+ ir: [email protected] {
+ 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 be69c4f..1d8e2c2 100644
--- a/arch/metag/boot/dts/tz1090.dtsi
+++ b/arch/metag/boot/dts/tz1090.dtsi
@@ -48,6 +48,12 @@
reg = <0x02005800 0xe4>;
};

+ pdc_pinctrl: [email protected] {
+ #gpio-range-cells = <2>;
+ compatible = "img,tz1090-pdc-pinctrl";
+ reg = <0x02006500 0x100>;
+ };
+
gpios: [email protected] {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 5f8bfa8..44f1469 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -202,6 +202,12 @@ config PINCTRL_TZ1090
select PINMUX
select 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 1ae5596..e53b88e 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o
obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o
obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o
obj-$(CONFIG_PINCTRL_TZ1090) += pinctrl-tz1090.o
+obj-$(CONFIG_PINCTRL_TZ1090_PDC) += pinctrl-tz1090-pdc.o
obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o
obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o
obj-$(CONFIG_PINCTRL_SAMSUNG) += pinctrl-samsung.o
diff --git a/drivers/pinctrl/pinctrl-tz1090-pdc.c b/drivers/pinctrl/pinctrl-tz1090-pdc.c
new file mode 100644
index 0000000..f520634
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tz1090-pdc.c
@@ -0,0 +1,1114 @@
+/*
+ * 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/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/slab.h>
+
+#include <asm/global_lock.h>
+#include <asm/soc-tz1090/gpio.h>
+
+#include "core.h"
+#include "pinctrl-tz1090-pdc.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
+
+/**
+ * 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;
+ u32 bit:5;
+ u32 drv:1;
+};
+
+/*
+ * All PDC pins can be GPIOs. Define these first to match how the GPIO driver
+ * names/numbers its pins.
+ */
+#define _GPIO(offset) (GPIO_##offset - GPIO_PDC_GPIO0)
+
+#define TZ1090_PDC_PIN_GPIO0 _GPIO(PDC_GPIO0)
+#define TZ1090_PDC_PIN_GPIO1 _GPIO(PDC_GPIO1)
+#define TZ1090_PDC_PIN_SYS_WAKE0 _GPIO(SYS_WAKE0)
+#define TZ1090_PDC_PIN_SYS_WAKE1 _GPIO(SYS_WAKE1)
+#define TZ1090_PDC_PIN_SYS_WAKE2 _GPIO(SYS_WAKE2)
+#define TZ1090_PDC_PIN_IR_DATA _GPIO(IR_DATA)
+#define TZ1090_PDC_PIN_EXT_POWER _GPIO(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),
+};
+
+/* Pin group with mux control */
+#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 = 0, \
+ }
+
+#define DRV_PG(pg_name) \
+ { \
+ .name = #pg_name, \
+ .pins = pg_name##_pins, \
+ .npins = ARRAY_SIZE(pg_name##_pins), \
+ .drv = 1, \
+ }
+
+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);
+}
+
+static const struct cfg_param {
+ const char *property;
+ enum tz1090_pdc_pinconf_param param;
+} cfg_params[] = {
+ {"pull", TZ1090_PDC_PINCONF_PARAM_PULL},
+ {"schmitt", TZ1090_PDC_PINCONF_PARAM_SCHMITT},
+ {"slew-rate", TZ1090_PDC_PINCONF_PARAM_SLEW_RATE},
+ {"drive-strength", TZ1090_PDC_PINCONF_PARAM_DRIVE_STRENGTH},
+ {"pos", TZ1090_PDC_PINCONF_PARAM_POS},
+};
+
+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);
+ if (!ret) {
+ config = TZ1090_PDC_PINCONF_PACK(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 &= ~(1 << 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 |= 1 << 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 &= ~(1 << 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 |= 1 << 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 &= ~(1 << 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 tz1090_pdc_pinconf_param param,
+ bool report_err,
+ u32 *reg, u32 *width, u32 *mask, u32 *shift)
+{
+ /* Find information about parameter's register */
+ switch (param) {
+ case TZ1090_PDC_PINCONF_PARAM_PULL:
+ *reg = REG_GPIO_CONTROL2;
+ *shift = REG_GPIO_CONTROL2_PU_PD_S + pin*2;
+ *width = 2;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ /* Calculate field information */
+ *mask = ((1 << *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 tz1090_pdc_pinconf_param param =
+ TZ1090_PDC_PINCONF_UNPACK_PARAM(*config);
+ int ret;
+ u32 reg, width, mask, shift, val, arg;
+
+ /* Get register information */
+ ret = tz1090_pdc_pinconf_reg(pctldev, pin, param, true,
+ &reg, &width, &mask, &shift);
+ if (ret < 0)
+ return ret;
+
+ /* Extract field from register */
+ val = pmx_read(pmx, reg);
+ arg = (val & mask) >> shift;
+
+ /* And pack config */
+ *config = TZ1090_PDC_PINCONF_PACK(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 tz1090_pdc_pinconf_param param =
+ TZ1090_PDC_PINCONF_UNPACK_PARAM(config);
+ unsigned int arg;
+ int ret;
+ u32 reg, width, mask, shift, val;
+ 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);
+ if (ret < 0)
+ return ret;
+
+ /* Unpack argument and range check it */
+ arg = TZ1090_PDC_PINCONF_UNPACK_ARG(config);
+ if (arg >= (1 << width)) {
+ dev_dbg(pctldev->dev, "%s: arg %u larger than %u bits\n",
+ __func__, arg, width);
+ return -EINVAL;
+ }
+
+ /* Write register field */
+ __global_lock2(flags);
+ val = pmx_read(pmx, reg);
+ val &= ~mask;
+ val |= arg << shift;
+ pmx_write(pmx, val, reg);
+ __global_unlock2(flags);
+
+ return 0;
+}
+
+static int tz1090_pdc_pinconf_group_reg(struct pinctrl_dev *pctldev,
+ const struct tz1090_pdc_pingroup *g,
+ enum tz1090_pdc_pinconf_param param,
+ bool report_err, u32 *reg, u32 *width,
+ u32 *mask, u32 *shift)
+{
+ /* 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 -EINVAL;
+ }
+
+ /* Find information about drive parameter's register */
+ *reg = REG_GPIO_CONTROL2;
+ switch (param) {
+ case TZ1090_PDC_PINCONF_PARAM_SCHMITT:
+ *shift = REG_GPIO_CONTROL2_PDC_SCHMITT_S;
+ *width = 1;
+ break;
+ case TZ1090_PDC_PINCONF_PARAM_SLEW_RATE:
+ *shift = REG_GPIO_CONTROL2_PDC_SR_S;
+ *width = 1;
+ break;
+ case TZ1090_PDC_PINCONF_PARAM_DRIVE_STRENGTH:
+ *shift = REG_GPIO_CONTROL2_PDC_DR_S;
+ *width = 2;
+ break;
+ case TZ1090_PDC_PINCONF_PARAM_POS:
+ *shift = REG_GPIO_CONTROL2_PDC_POS_S;
+ *width = 1;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ /* Calculate field information */
+ *mask = ((1 << *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 tz1090_pdc_pinconf_param param =
+ TZ1090_PDC_PINCONF_UNPACK_PARAM(*config);
+ int ret;
+ u32 reg, width, mask, shift, val, arg;
+
+ /* We can't really get a per-pin configuration of a group. */
+ if (param < TZ1090_PDC_PINCONF_PARAM_GROUPED) {
+ dev_dbg(pctldev->dev,
+ "%s: Can't get per-pin param %u of group %s\n",
+ __func__, param, g->name);
+ return -EINVAL;
+ }
+
+ /* Get register information */
+ ret = tz1090_pdc_pinconf_group_reg(pctldev, g, param, true,
+ &reg, &width, &mask, &shift);
+ if (ret < 0)
+ return ret;
+
+ /* Extract field from register */
+ val = pmx_read(pmx, reg);
+ arg = (val & mask) >> shift;
+
+ /* And pack config */
+ *config = TZ1090_PDC_PINCONF_PACK(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 tz1090_pdc_pinconf_param param =
+ TZ1090_PDC_PINCONF_UNPACK_PARAM(config);
+ unsigned int arg;
+ const unsigned int *pit;
+ unsigned int i;
+ int ret;
+ u32 reg, width, mask, shift, val;
+ unsigned long flags;
+
+ dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n",
+ __func__, g->name, config);
+
+ /*
+ * If we're trying to set a per-pin configuration of a group, do the
+ * pins one by one. This is mainly as a convenience.
+ */
+ if (param < TZ1090_PDC_PINCONF_PARAM_GROUPED) {
+ 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;
+ }
+
+ /* Get register information */
+ ret = tz1090_pdc_pinconf_group_reg(pctldev, g, param, true,
+ &reg, &width, &mask, &shift);
+ if (ret < 0)
+ return ret;
+
+ /* Unpack argument and range check it */
+ arg = TZ1090_PDC_PINCONF_UNPACK_ARG(config);
+ if (arg >= (1 << width)) {
+ dev_dbg(pctldev->dev, "%s: arg %u larger than %u bits\n",
+ __func__, arg, width);
+ return -EINVAL;
+ }
+
+ /* Write register field */
+ __global_lock2(flags);
+ val = pmx_read(pmx, reg);
+ val &= ~mask;
+ val |= arg << shift;
+ pmx_write(pmx, val, reg);
+ __global_unlock2(flags);
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void tz1090_pdc_pinconf_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned pin)
+{
+ struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+ int i, ret;
+ u32 reg, width, mask, shift, val;
+
+ for (i = 0; i < ARRAY_SIZE(cfg_params); i++) {
+ /* Get register information */
+ ret = tz1090_pdc_pinconf_reg(pctldev, pin, cfg_params[i].param,
+ false, &reg, &width, &mask, &shift);
+ if (ret < 0)
+ continue;
+
+ /* Extract field from register */
+ val = pmx_read(pmx, reg);
+ val = (val & mask) >> shift;
+
+ seq_printf(s, "\n\t%s=%u",
+ cfg_params[i].property, val);
+ }
+}
+
+static const char *strip_prefix(const char *s)
+{
+ const char *comma = strchr(s, ',');
+ if (!comma)
+ return s;
+
+ return comma + 1;
+}
+
+static void tz1090_pdc_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s,
+ unsigned group)
+{
+ struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+ const struct tz1090_pdc_pingroup *g = &tz1090_pdc_groups[group];
+ int i, ret;
+ u32 reg, width, mask, shift, val;
+
+ for (i = 0; i < ARRAY_SIZE(cfg_params); i++) {
+ /* Get register information */
+ ret = tz1090_pdc_pinconf_group_reg(pctldev, g,
+ cfg_params[i].param, false,
+ &reg, &width, &mask, &shift);
+ if (ret < 0)
+ continue;
+
+ /* Extract field from register */
+ val = pmx_read(pmx, reg);
+ val = (val & mask) >> shift;
+
+ seq_printf(s, "\n\t%s=%u",
+ strip_prefix(cfg_params[i].property), val);
+ }
+}
+
+static void tz1090_pdc_pinconf_config_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s,
+ unsigned long config)
+{
+ enum tz1090_pdc_pinconf_param param =
+ TZ1090_PDC_PINCONF_UNPACK_PARAM(config);
+ u16 arg = TZ1090_PDC_PINCONF_UNPACK_ARG(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", strip_prefix(pname), arg);
+}
+#endif
+
+struct pinconf_ops tz1090_pdc_pinconf_ops = {
+ .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_dbg_show = tz1090_pdc_pinconf_dbg_show,
+ .pin_config_group_dbg_show = tz1090_pdc_pinconf_group_dbg_show,
+ .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 struct pinctrl_gpio_range tz1090_pdc_pinctrl_gpio_range = {
+ .name = "TZ1090-PDC GPIOs",
+ .base = GPIO_PDC_BASE,
+ .npins = NR_PDC_GPIO,
+};
+
+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;
+ }
+
+ pinctrl_add_gpio_range(pmx->pctl, &tz1090_pdc_pinctrl_gpio_range);
+
+ 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);
diff --git a/drivers/pinctrl/pinctrl-tz1090-pdc.h b/drivers/pinctrl/pinctrl-tz1090-pdc.h
new file mode 100644
index 0000000..b727072
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tz1090-pdc.h
@@ -0,0 +1,58 @@
+#ifndef __PINCTRL_TZ1090_PDC_H__
+#define __PINCTRL_TZ1090_PDC_H__
+
+enum tz1090_pdc_pinconf_param {
+ /* per-gpio parameters */
+ TZ1090_PDC_PINCONF_PARAM_PERGPIO = 0,
+
+ /* argument: tz1090_pdc_pinconf_pull */
+ TZ1090_PDC_PINCONF_PARAM_PULL,
+
+
+ /* grouped drive parameters */
+ TZ1090_PDC_PINCONF_PARAM_GROUPED,
+
+ /* argument: tz1090_pdc_pinconf_schmitt */
+ TZ1090_PDC_PINCONF_PARAM_SCHMITT,
+ /* argument: tz1090_pdc_pinconf_slew */
+ TZ1090_PDC_PINCONF_PARAM_SLEW_RATE,
+ /* argument: tz1090_pdc_pinconf_drive */
+ TZ1090_PDC_PINCONF_PARAM_DRIVE_STRENGTH,
+ /* argument: tz1090_pdc_pinconf_pos */
+ TZ1090_PDC_PINCONF_PARAM_POS,
+};
+
+enum tz1090_pdc_pinconf_pull {
+ TZ1090_PDC_PINCONF_PULL_TRISTATE,
+ TZ1090_PDC_PINCONF_PULL_UP,
+ TZ1090_PDC_PINCONF_PULL_DOWN,
+ TZ1090_PDC_PINCONF_PULL_REPEATER,
+};
+
+enum tz1090_pdc_pinconf_schmitt {
+ TZ1090_PDC_PINCONF_SCHMITT_OFF, /* no hysteresis */
+ TZ1090_PDC_PINCONF_SCHMITT_ON, /* schmitt trigger */
+};
+
+enum tz1090_pdc_pinconf_slew {
+ TZ1090_PDC_PINCONF_SLEW_SLOW, /* half frequency */
+ TZ1090_PDC_PINCONF_SLEW_FAST,
+};
+
+enum tz1090_pdc_pinconf_drive {
+ TZ1090_PDC_PINCONF_DRIVE_2mA,
+ TZ1090_PDC_PINCONF_DRIVE_4mA,
+ TZ1090_PDC_PINCONF_DRIVE_8mA,
+ TZ1090_PDC_PINCONF_DRIVE_12mA,
+};
+
+enum tz1090_pdc_pinconf_pos {
+ TZ1090_PDC_PINCONF_POS_DISABLE, /* weak pull-down disabled */
+ TZ1090_PDC_PINCONF_POS_INVALID, /* weak pull-down for invalid power */
+};
+
+#define TZ1090_PDC_PINCONF_PACK(_param_, _arg_) ((_param_) << 16 | (_arg_))
+#define TZ1090_PDC_PINCONF_UNPACK_PARAM(_conf_) ((_conf_) >> 16)
+#define TZ1090_PDC_PINCONF_UNPACK_ARG(_conf_) ((_conf_) & 0xffff)
+
+#endif /* __PINCTRL_TZ1090_PDC_H__ */
--
1.8.1.2

2013-04-23 14:34:39

by James Hogan

[permalink] [raw]
Subject: [PATCH 2/8] metag: minimal TZ1090 (Comet) SoC infrastructure

Add really minimal support for Toumaz Xenif TZ1090 SoC (A.K.A. Comet).
This consists of minimal build infrastructure, device tree files, a
machine definition, 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 <[email protected]>
Cc: Grant Likely <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: Rob Landley <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: [email protected]
---
.../devicetree/bindings/vendor-prefixes.txt | 1 +
arch/metag/Kconfig.soc | 9 +++++
arch/metag/Makefile | 8 ++++-
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 | 41 ++++++++++++++++++++++
arch/metag/soc/tz1090/Makefile | 5 +++
arch/metag/soc/tz1090/setup.c | 21 +++++++++++
9 files changed, 125 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
create mode 100644 arch/metag/soc/tz1090/Makefile
create mode 100644 arch/metag/soc/tz1090/setup.c

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 19e1ef7..4366623 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -54,6 +54,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..7f8cf4c 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)
@@ -46,6 +46,12 @@ core-y += arch/metag/boot/dts/
core-y += arch/metag/kernel/
core-y += arch/metag/mm/

+# SoCs
+socdir-$(CONFIG_SOC_TZ1090) += tz1090
+
+socdirs := $(filter-out ., $(patsubst %,%/,$(socdir-y)))
+core-y += $(addprefix arch/metag/soc/, $(socdirs))
+
libs-y += arch/metag/lib/
libs-y += arch/metag/tbx/

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..4794094
--- /dev/null
+++ b/arch/metag/configs/tz1090_defconfig
@@ -0,0 +1,41 @@
+# 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_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
diff --git a/arch/metag/soc/tz1090/Makefile b/arch/metag/soc/tz1090/Makefile
new file mode 100644
index 0000000..954dd76
--- /dev/null
+++ b/arch/metag/soc/tz1090/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for TZ1090 platform setup.
+#
+
+obj-y += setup.o
diff --git a/arch/metag/soc/tz1090/setup.c b/arch/metag/soc/tz1090/setup.c
new file mode 100644
index 0000000..fbd7074
--- /dev/null
+++ b/arch/metag/soc/tz1090/setup.c
@@ -0,0 +1,21 @@
+/*
+ * setup.c
+ *
+ * Copyright (C) 2009-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/init.h>
+#include <asm/mach/arch.h>
+
+static const char *tz1090_boards_compat[] __initdata = {
+ "toumaz,tz1090",
+ NULL,
+};
+
+MACHINE_START(TZ1090, "Generic TZ1090")
+ .dt_compat = tz1090_boards_compat,
+MACHINE_END
--
1.8.1.2

2013-04-23 14:34:37

by James Hogan

[permalink] [raw]
Subject: [PATCH 6/8] gpio-tz1090: add TZ1090 gpio driver

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 <[email protected]>
Cc: Grant Likely <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: Rob Landley <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: [email protected]
---
.../devicetree/bindings/gpio/gpio-tz1090.txt | 77 +++
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 | 628 +++++++++++++++++++++
6 files changed, 749 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..5e426df
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
@@ -0,0 +1,77 @@
+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]
+ [standard Linux gpio flags]>
+
+ Values for gpio specifier:
+ - GPIO number: a value in the range 0 to 29.
+ - GPIO flags: standard Linux GPIO flags as found in of_gpio.h
+
+ 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]
+ [standard Linux irq flags]>
+
+ Values for irq specifier:
+ - GPIO number: a value in the range 0 to 29
+ - IRQ flags: standard Linux IRQ flags for edge and level triggering
+
+
+
+Example:
+
+ gpios: [email protected] {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "img,tz1090-gpio";
+ reg = <0x02005800 0x90>;
+
+ /* bank 0 with an interrupt */
+ gpios0: [email protected] {
+ #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: [email protected] {
+ #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 ccbcbf1..be69c4f 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: [email protected] {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "img,tz1090-gpio";
+ reg = <0x02005800 0x90>;
+
+ gpios0: [email protected] {
+ gpio-controller;
+ interrupt-controller;
+ #gpio-cells = <2>;
+ #interrupt-cells = <2>;
+ reg = <0>;
+ interrupts = <13 4 /* level */>;
+ gpio-ranges = <&pinctrl 0 30>;
+ };
+ gpios1: [email protected] {
+ gpio-controller;
+ interrupt-controller;
+ #gpio-cells = <2>;
+ #interrupt-cells = <2>;
+ reg = <1>;
+ interrupts = <14 4 /* level */>;
+ gpio-ranges = <&pinctrl 30 30>;
+ };
+ gpios2: [email protected] {
+ gpio-controller;
+ interrupt-controller;
+ #gpio-cells = <2>;
+ #interrupt-cells = <2>;
+ reg = <2>;
+ interrupts = <15 4 /* level */>;
+ gpio-ranges = <&pinctrl 60 30>;
+ };
+ };
};
};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 93aaadf..922fefa 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -227,6 +227,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_VT8500
bool "VIA/Wondermedia SoC GPIO Support"
depends on ARCH_VT8500
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 22e07bc..fab78c4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -77,6 +77,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..023d572
--- /dev/null
+++ b/drivers/gpio/gpio-tz1090.c
@@ -0,0 +1,628 @@
+/*
+ * 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/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 void tz1090_gpio_write(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs, u32 data)
+{
+ iowrite32(data, bank->reg + reg_offs);
+}
+
+static 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 &= ~(0x1 << 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 |= 0x1 << 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 &= ~(0x1 << 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) & (0x1 << 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 &= ~(0x1 << offset);
+ value_p |= value_i & (0x1 << 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

2013-04-23 14:36:01

by James Hogan

[permalink] [raw]
Subject: [PATCH 5/8] pinctrl-tz1090: add TZ1090 pinctrl driver

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 <[email protected]>
Cc: Grant Likely <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: Rob Landley <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: [email protected]
---
.../bindings/pinctrl/img,tz1090-pinctrl.txt | 214 +++
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 | 2018 ++++++++++++++++++++
drivers/pinctrl/pinctrl-tz1090.h | 58 +
7 files changed, 2305 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt
create mode 100644 drivers/pinctrl/pinctrl-tz1090.c
create mode 100644 drivers/pinctrl/pinctrl-tz1090.h

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..4751795
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt
@@ -0,0 +1,214 @@
+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.
+
+The exception to that rule is the select config. The CR_PADS_GPIO_SELECT{0,1,2}
+registers select between the serial interface (where an internal peripheral such
+as the SPI block controls the pin) and the GPIO interface (where the pin can be
+controlled by the other GPIO registers). This defaults to the GPIO interface for
+all pins, so muxing a function onto a pin group will set all pins to the serial
+interface. This can be overridden by claiming one of the pins as a GPIO, or
+explicitly setting the select config to override this behaviour (see below).
+
+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.
+- select: Integer, force the pin(s) to the GPIO interface so they cannot be
+ controlled by internal peripherals.
+ 0: don't force (internal peripheral will have control of the pin(s) if
+ they're muxed and not selected for GPIO use).
+ 1: force gpio mode (internal peripheral will not control the pin(s))
+- pull: Integer, representing the pull-down/up to apply to the pin(s).
+ 0: tri-state
+ 1: pull-up
+ 2: pull-down
+ 3: repeater
+- 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.
+ 0: 2mA
+ 1: 4mA
+ 2: 8mA
+ 4: 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 select and pull (which can also be provided to any of the
+ groups below to set it for all gpio pins in that group).
+
+ They also all support the "default" function (if they cannot be individually
+ muxed, see pdm_d and spi1_cs2 mux groups below).
+
+ 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.
+
+ other pins:
+
+ These other pins are part of various pin groups below, but can't be
+ individually configured.
+
+ clk_out0, clk_out1, tck, tdi, tdo, tms, trst.
+
+ 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:
+
+ [email protected] {
+ #gpio-range-cells = <2>;
+ compatible = "img,tz1090-pinctrl";
+ reg = <0x02005800 0xe4>;
+ };
+
+Example board file extract:
+
+ [email protected] {
+ uart0_default: uart0 {
+ uart0_cfg {
+ pins = "uart0_rxd",
+ "uart0_txd";
+ function = "default";
+ };
+ };
+ tft_default: tft {
+ tft_cfg {
+ pins = "tft";
+ function = "tft";
+ };
+ };
+ };
+
+ [email protected] {
+ 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..ccbcbf1 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: [email protected] {
+ #gpio-range-cells = <2>;
+ compatible = "img,tz1090-pinctrl";
+ reg = <0x02005800 0xe4>;
+ };
};
};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 34f51d2..5f8bfa8 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -196,6 +196,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 PINCONF
+
config PINCTRL_U300
bool "U300 pin controller driver"
depends on ARCH_U300
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f82cc5b..1ae5596 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_PINCTRL_TEGRA) += pinctrl-tegra.o
obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o
obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o
obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o
+obj-$(CONFIG_PINCTRL_TZ1090) += pinctrl-tz1090.o
obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o
obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o
obj-$(CONFIG_PINCTRL_SAMSUNG) += pinctrl-samsung.o
diff --git a/drivers/pinctrl/pinctrl-tz1090.c b/drivers/pinctrl/pinctrl-tz1090.c
new file mode 100644
index 0000000..5ec36b8
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tz1090.c
@@ -0,0 +1,2018 @@
+/*
+ * 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/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/global_lock.h>
+#include <asm/soc-tz1090/gpio.h>
+
+#include "core.h"
+#include "pinctrl-tz1090.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
+
+/**
+ * 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;
+ u32 bit:5;
+ u32 width:5;
+};
+
+/**
+ * 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).
+ *
+ * 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;
+
+ u32 drv:1;
+ u32 slw_bit:5;
+};
+
+/*
+ * Most pins affected by the pinmux can also be GPIOs. Define these first.
+ * These must match how the GPIO driver names/numbers its pins.
+ */
+#define _GPIO(offset) (GPIO_##offset)
+
+#define TZ1090_PIN_SDIO_CLK _GPIO(SDIO_CLK)
+#define TZ1090_PIN_SDIO_CMD _GPIO(SDIO_CMD)
+#define TZ1090_PIN_SDIO_D0 _GPIO(SDIO_D0)
+#define TZ1090_PIN_SDIO_D1 _GPIO(SDIO_D1)
+#define TZ1090_PIN_SDIO_D2 _GPIO(SDIO_D2)
+#define TZ1090_PIN_SDIO_D3 _GPIO(SDIO_D3)
+#define TZ1090_PIN_SDH_CD _GPIO(SDH_CD)
+#define TZ1090_PIN_SDH_WP _GPIO(SDH_WP)
+#define TZ1090_PIN_SPI0_MCLK _GPIO(SPI0_MCLK)
+#define TZ1090_PIN_SPI0_CS0 _GPIO(SPI0_CS0)
+#define TZ1090_PIN_SPI0_CS1 _GPIO(SPI0_CS1)
+#define TZ1090_PIN_SPI0_CS2 _GPIO(SPI0_CS2)
+#define TZ1090_PIN_SPI0_DOUT _GPIO(SPI0_DOUT)
+#define TZ1090_PIN_SPI0_DIN _GPIO(SPI0_DIN)
+#define TZ1090_PIN_SPI1_MCLK _GPIO(SPI1_MCLK)
+#define TZ1090_PIN_SPI1_CS0 _GPIO(SPI1_CS0)
+#define TZ1090_PIN_SPI1_CS1 _GPIO(SPI1_CS1)
+#define TZ1090_PIN_SPI1_CS2 _GPIO(SPI1_CS2)
+#define TZ1090_PIN_SPI1_DOUT _GPIO(SPI1_DOUT)
+#define TZ1090_PIN_SPI1_DIN _GPIO(SPI1_DIN)
+#define TZ1090_PIN_UART0_RXD _GPIO(UART0_RXD)
+#define TZ1090_PIN_UART0_TXD _GPIO(UART0_TXD)
+#define TZ1090_PIN_UART0_CTS _GPIO(UART0_CTS)
+#define TZ1090_PIN_UART0_RTS _GPIO(UART0_RTS)
+#define TZ1090_PIN_UART1_RXD _GPIO(UART1_RXD)
+#define TZ1090_PIN_UART1_TXD _GPIO(UART1_TXD)
+#define TZ1090_PIN_SCB0_SDAT _GPIO(SCB0_SDAT)
+#define TZ1090_PIN_SCB0_SCLK _GPIO(SCB0_SCLK)
+#define TZ1090_PIN_SCB1_SDAT _GPIO(SCB1_SDAT)
+#define TZ1090_PIN_SCB1_SCLK _GPIO(SCB1_SCLK)
+#define TZ1090_PIN_SCB2_SDAT _GPIO(SCB2_SDAT)
+#define TZ1090_PIN_SCB2_SCLK _GPIO(SCB2_SCLK)
+#define TZ1090_PIN_I2S_MCLK _GPIO(I2S_MCLK)
+#define TZ1090_PIN_I2S_BCLK_OUT _GPIO(I2S_BCLK_OUT)
+#define TZ1090_PIN_I2S_LRCLK_OUT _GPIO(I2S_LRCLK_OUT)
+#define TZ1090_PIN_I2S_DOUT0 _GPIO(I2S_DOUT0)
+#define TZ1090_PIN_I2S_DOUT1 _GPIO(I2S_DOUT1)
+#define TZ1090_PIN_I2S_DOUT2 _GPIO(I2S_DOUT2)
+#define TZ1090_PIN_I2S_DIN _GPIO(I2S_DIN)
+#define TZ1090_PIN_PDM_A _GPIO(PDM_A)
+#define TZ1090_PIN_PDM_B _GPIO(PDM_B)
+#define TZ1090_PIN_PDM_C _GPIO(PDM_C)
+#define TZ1090_PIN_PDM_D _GPIO(PDM_D)
+#define TZ1090_PIN_TFT_RED0 _GPIO(TFT_RED0)
+#define TZ1090_PIN_TFT_RED1 _GPIO(TFT_RED1)
+#define TZ1090_PIN_TFT_RED2 _GPIO(TFT_RED2)
+#define TZ1090_PIN_TFT_RED3 _GPIO(TFT_RED3)
+#define TZ1090_PIN_TFT_RED4 _GPIO(TFT_RED4)
+#define TZ1090_PIN_TFT_RED5 _GPIO(TFT_RED5)
+#define TZ1090_PIN_TFT_RED6 _GPIO(TFT_RED6)
+#define TZ1090_PIN_TFT_RED7 _GPIO(TFT_RED7)
+#define TZ1090_PIN_TFT_GREEN0 _GPIO(TFT_GREEN0)
+#define TZ1090_PIN_TFT_GREEN1 _GPIO(TFT_GREEN1)
+#define TZ1090_PIN_TFT_GREEN2 _GPIO(TFT_GREEN2)
+#define TZ1090_PIN_TFT_GREEN3 _GPIO(TFT_GREEN3)
+#define TZ1090_PIN_TFT_GREEN4 _GPIO(TFT_GREEN4)
+#define TZ1090_PIN_TFT_GREEN5 _GPIO(TFT_GREEN5)
+#define TZ1090_PIN_TFT_GREEN6 _GPIO(TFT_GREEN6)
+#define TZ1090_PIN_TFT_GREEN7 _GPIO(TFT_GREEN7)
+#define TZ1090_PIN_TFT_BLUE0 _GPIO(TFT_BLUE0)
+#define TZ1090_PIN_TFT_BLUE1 _GPIO(TFT_BLUE1)
+#define TZ1090_PIN_TFT_BLUE2 _GPIO(TFT_BLUE2)
+#define TZ1090_PIN_TFT_BLUE3 _GPIO(TFT_BLUE3)
+#define TZ1090_PIN_TFT_BLUE4 _GPIO(TFT_BLUE4)
+#define TZ1090_PIN_TFT_BLUE5 _GPIO(TFT_BLUE5)
+#define TZ1090_PIN_TFT_BLUE6 _GPIO(TFT_BLUE6)
+#define TZ1090_PIN_TFT_BLUE7 _GPIO(TFT_BLUE7)
+#define TZ1090_PIN_TFT_VDDEN_GD _GPIO(TFT_VDDEN_GD)
+#define TZ1090_PIN_TFT_PANELCLK _GPIO(TFT_PANELCLK)
+#define TZ1090_PIN_TFT_BLANK_LS _GPIO(TFT_BLANK_LS)
+#define TZ1090_PIN_TFT_VSYNC_NS _GPIO(TFT_VSYNC_NS)
+#define TZ1090_PIN_TFT_HSYNC_NR _GPIO(TFT_HSYNC_NR)
+#define TZ1090_PIN_TFT_VD12ACB _GPIO(TFT_VD12ACB)
+#define TZ1090_PIN_TFT_PWRSAVE _GPIO(TFT_PWRSAVE)
+#define TZ1090_PIN_TX_ON _GPIO(TX_ON)
+#define TZ1090_PIN_RX_ON _GPIO(RX_ON)
+#define TZ1090_PIN_PLL_ON _GPIO(PLL_ON)
+#define TZ1090_PIN_PA_ON _GPIO(PA_ON)
+#define TZ1090_PIN_RX_HP _GPIO(RX_HP)
+#define TZ1090_PIN_GAIN0 _GPIO(GAIN0)
+#define TZ1090_PIN_GAIN1 _GPIO(GAIN1)
+#define TZ1090_PIN_GAIN2 _GPIO(GAIN2)
+#define TZ1090_PIN_GAIN3 _GPIO(GAIN3)
+#define TZ1090_PIN_GAIN4 _GPIO(GAIN4)
+#define TZ1090_PIN_GAIN5 _GPIO(GAIN5)
+#define TZ1090_PIN_GAIN6 _GPIO(GAIN6)
+#define TZ1090_PIN_GAIN7 _GPIO(GAIN7)
+#define TZ1090_PIN_ANT_SEL0 _GPIO(ANT_SEL0)
+#define TZ1090_PIN_ANT_SEL1 _GPIO(ANT_SEL1)
+#define TZ1090_PIN_SDH_CLK_IN _GPIO(SDH_CLK_IN)
+
+/* Non-GPIO pins follow */
+#define NUM_GPIOS NR_BUILTIN_GPIO
+#define _PIN(offset) (NUM_GPIOS + (offset))
+
+#define TZ1090_PIN_TCK _PIN(0)
+#define TZ1090_PIN_TRST _PIN(1)
+#define TZ1090_PIN_TDI _PIN(2)
+#define TZ1090_PIN_TDO _PIN(3)
+#define TZ1090_PIN_TMS _PIN(4)
+#define TZ1090_PIN_CLK_OUT0 _PIN(5)
+#define TZ1090_PIN_CLK_OUT1 _PIN(6)
+
+/* Pin names */
+
+static const struct pinctrl_pin_desc tz1090_pins[] = {
+ /* Normal GPIOs */
+ 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-GPIOs */
+ 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"),
+};
+
+/* Pin group pins */
+
+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,
+};
+
+/* Drive pin group pins */
+
+static const unsigned drive_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 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,
+};
+
+/* Mux functions */
+
+enum tz1090_mux {
+ /* internal placeholder */
+ TZ1090_MUX_NA = -1,
+ /* default function for an unmuxed pin */
+ TZ1090_MUX_DEFAULT,
+ /* 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,
+};
+
+/* Pin groups a function can be muxed to */
+
+static const char * const default_groups[] = {
+ /* non-muxing convenient gpio pingroups */
+ "uart",
+ "uart0",
+ "uart1",
+ "spi0",
+ "spi1",
+ "scb0",
+ "scb1",
+ "scb2",
+ "i2s",
+ /* pingroups of individual pins without a (non gpio select) mux */
+ "sdio_clk",
+ "sdio_cmd",
+ "sdio_d0",
+ "sdio_d1",
+ "sdio_d2",
+ "sdio_d3",
+ "sdh_cd",
+ "sdh_wp",
+ "spi0_mclk",
+ "spi0_cs0",
+ "spi0_cs1",
+ "spi0_cs2",
+ "spi0_dout",
+ "spi0_din",
+ "spi1_mclk",
+ "spi1_cs0",
+ "spi1_cs1",
+ /* spi1_cs2 can be muxed individually */
+ "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",
+ /* pdm_d can be muxed individually */
+ "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",
+ "tx_on",
+ "rx_on",
+ "pll_on",
+ "pa_on",
+ "rx_hp",
+ "gain0",
+ "gain1",
+ "gain2",
+ "gain3",
+ "gain4",
+ "gain5",
+ "gain6",
+ "gain7",
+ "ant_sel0",
+ "ant_sel1",
+ "sdh_clk_in",
+};
+
+static const char * const sdh_sdio_groups[] = {
+ "sdh",
+ "sdio",
+};
+
+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",
+};
+
+static const char * const tft_groups[] = {
+ "tft",
+};
+
+#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[] = {
+ /* MUX fn pingroups */
+ FUNCTION(DEFAULT, default, default),
+ 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 */
+
+#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 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)
+
+#define SUBMUX(f, submux) [(TZ1090_MUX_ ## f)] = &(submux ## _submux)
+
+/* Pin group with mux control */
+#define MUX_PG(pg_name, f0, f1, f2, f3, f4, \
+ mux_r, mux_b, mux_w, slw_b) \
+ { \
+ .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), \
+ .drv = ((slw_b) >= 0), \
+ .slw_bit = (slw_b), \
+ }
+
+#define SIMPLE_PG(pg_name) \
+ { \
+ .name = #pg_name, \
+ .pins = pg_name##_pins, \
+ .npins = ARRAY_SIZE(pg_name##_pins), \
+ }
+
+#define SIMPLE_DRV_PG(pg_name, slw_b) \
+ { \
+ .name = #pg_name, \
+ .pins = pg_name##_pins, \
+ .npins = ARRAY_SIZE(pg_name##_pins), \
+ .drv = 1, \
+ .slw_bit = (slw_b), \
+ }
+
+#define DRV_PG(pg_name, slw_b) \
+ { \
+ .name = "drive_"#pg_name, \
+ .pins = drive_##pg_name##_pins, \
+ .npins = ARRAY_SIZE(drive_##pg_name##_pins), \
+ .drv = 1, \
+ .slw_bit = (slw_b), \
+ }
+
+/* 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);
+
+static struct tz1090_muxdesc *tz1090_submux[] = {
+ SUBMUX(EXT_DAC, ext_dac),
+};
+
+static const struct tz1090_pingroup tz1090_groups[] = {
+ /* Muxing pin groups */
+ /* pg_name, f0, f1, f2, f3, f4, mux r/b/w, slw/schmitt/drv b */
+ MUX_PG(sdh, SDH, SDIO, NA, NA, NA, IF_CTL, 20, 2, -1),
+ MUX_PG(sdio, SDIO, SDH, NA, NA, NA, IF_CTL, 16, 2, -1),
+ MUX_PG(spi1_cs2, SPI1_CS2, USB_VBUS, NA, NA, NA, IF_CTL, 10, 2, -1),
+ MUX_PG(pdm_d, PDM_DAC, USB_VBUS, NA, NA, NA, IF_CTL, 8, 2, -1),
+ MUX_PG(afe, AFE, TS_OUT_0, NA, NA, NA, IF_CTL, 4, 2, 0 /* 0, 0 */),
+ MUX_PG(tft, TFT, EXT_DAC, TS_OUT_1, LCD_TRACE, PHY_RINGOSC, IF_CTL, 0, 3, 10 /* 10, 20 */),
+
+ /* Simple pin groups with drive control */
+ /* pg_name, slw/schmitt/drv b */
+ SIMPLE_DRV_PG(jtag, 11 /* 11, 22 */),
+ SIMPLE_DRV_PG(scb2, 9 /* 9, 18 */),
+ SIMPLE_DRV_PG(spi0, 7 /* 7, 14 */),
+ SIMPLE_DRV_PG(uart, 5 /* 5, 10 */),
+ SIMPLE_DRV_PG(scb1, 4 /* 4, 8 */),
+ SIMPLE_DRV_PG(spi1, 3 /* 3, 6 */),
+
+ /* Drive specific pin groups (drive_*) */
+ /* pg_name, slw/schmitt/drv b */
+ DRV_PG(sdio, 8 /* 8, 16 */), /* sdio_* + sdh_* */
+ DRV_PG(i2s, 6 /* 6, 12 */), /* i2s_* + clk_out1 */
+ DRV_PG(scb0, 2 /* 2, 4 */), /* scb0_* + pdm_{c,d} */
+ DRV_PG(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),
+
+ /* 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, select_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)
+ * @select_en: Pins that have been force seleced by pinconf (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];
+ u32 select_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);
+}
+
+static const struct cfg_param {
+ const char *property;
+ enum tz1090_pinconf_param param;
+} cfg_params[] = {
+ {"select", TZ1090_PINCONF_PARAM_SELECT},
+ {"pull", TZ1090_PINCONF_PARAM_PULL},
+ {"schmitt", TZ1090_PINCONF_PARAM_SCHMITT},
+ {"slew-rate", TZ1090_PINCONF_PARAM_SLEW_RATE},
+ {"drive-strength", TZ1090_PINCONF_PARAM_DRIVE_STRENGTH},
+};
+
+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);
+ if (!ret) {
+ config = TZ1090_PINCONF_PACK(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_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)
+{
+ *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 = !serial || gpio || force */
+ select = ((~pmx->pin_en[pmx_index] |
+ pmx->gpio_en[pmx_index] |
+ pmx->select_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 &= ~(1 << 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 &= ~(1 << 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_serial_select() - enable/disable serial interface for a pin
+ * @pmx: Pinmux data
+ * @pin: Pin number
+ * @gpio_select: 1 to enable serial interface (devices) when not GPIO,
+ * 0 to leave pin in GPIO mode
+ *
+ * Records that serial usage is enabled/disabled so that SELECT register can be
+ * set appropriately when GPIO is disabled.
+ */
+static void tz1090_pinctrl_serial_select(struct tz1090_pmx *pmx,
+ unsigned int pin,
+ unsigned int serial_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 serial is selected */
+ pin_en = pmx->pin_en[index];
+ pin_en &= ~(1 << shift);
+ pin_en |= (serial_select << shift);
+ pmx->pin_en[index] = pin_en;
+
+ /* update the select bit */
+ tz1090_pinctrl_select(pmx, pin);
+
+ spin_unlock(&pmx->lock);
+}
+
+/**
+ * tz1090_pinctrl_force_select() - enable/disable force gpio select for a pin
+ * @pmx: Pinmux data
+ * @pin: Pin number
+ * @gpio_select: 1 to force GPIO select,
+ * 0 to unforce GPIO select
+ *
+ * Records that the GPIO is force selected and serial interface shouldn't be
+ * enabled.
+ */
+static void tz1090_pinctrl_force_select(struct tz1090_pmx *pmx,
+ unsigned int pin,
+ unsigned int force_select)
+{
+ unsigned int index, shift;
+ u32 select_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 serial is selected */
+ select_en = pmx->select_en[index];
+ select_en &= ~(1 << shift);
+ select_en |= (force_select << shift);
+ pmx->select_en[index] = select_en;
+
+ /* update the select bit */
+ tz1090_pinctrl_select(pmx, pin);
+
+ spin_unlock(&pmx->lock);
+}
+
+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 = ((1 << 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;
+}
+
+static int tz1090_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function,
+ unsigned group)
+{
+ struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+ const struct tz1090_pingroup *grp;
+ int ret;
+ const unsigned int *pit;
+ unsigned int i, pin;
+
+ if (group >= ARRAY_SIZE(tz1090_groups)) {
+ pin = group - ARRAY_SIZE(tz1090_groups);
+ dev_dbg(pctldev->dev, "%s(func=%u (%s), pin=%u (%s))\n",
+ __func__,
+ function, tz1090_functions[function].name,
+ pin,
+ tz1090_pins[pin].name);
+
+ /* no muxing necessary */
+ if (function != TZ1090_MUX_DEFAULT)
+ return -EINVAL;
+ tz1090_pinctrl_serial_select(pmx, pin, 1);
+ return 0;
+ }
+
+ grp = &tz1090_groups[group];
+ dev_dbg(pctldev->dev, "%s(func=%u (%s), group=%u (%s))\n",
+ __func__,
+ function, tz1090_functions[function].name,
+ group, grp->name);
+
+ ret = tz1090_pinctrl_enable_mux(pmx, &grp->mux, function);
+ if (ret)
+ return ret;
+
+ /* set up each pin in group to serial interface */
+ for (i = 0, pit = grp->pins; i < grp->npins; ++i, ++pit)
+ tz1090_pinctrl_serial_select(pmx, *pit, 1);
+
+ return 0;
+}
+
+static void tz1090_pinctrl_disable(struct pinctrl_dev *pctldev,
+ unsigned function, unsigned group)
+{
+ struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+ const struct tz1090_pingroup *grp;
+ const unsigned int *pit;
+ unsigned int i, pin;
+
+ if (group >= ARRAY_SIZE(tz1090_groups)) {
+ pin = group - ARRAY_SIZE(tz1090_groups);
+ dev_dbg(pctldev->dev, "%s(func=%u (%s), pin=%u (%s))\n",
+ __func__,
+ function, tz1090_functions[function].name,
+ pin,
+ tz1090_pins[pin].name);
+
+ tz1090_pinctrl_serial_select(pmx, pin, 0);
+ return;
+ }
+
+ grp = &tz1090_groups[group];
+ dev_dbg(pctldev->dev, "%s(func=%u (%s), group=%u (%s))\n",
+ __func__,
+ function, tz1090_functions[function].name,
+ group, grp->name);
+
+ /* set up each pin in group to serial interface */
+ for (i = 0, pit = grp->pins; i < grp->npins; ++i, ++pit)
+ tz1090_pinctrl_serial_select(pmx, *pit, 0);
+}
+
+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;
+}
+
+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, 29}, /* 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 */
+};
+
+static int tz1090_pinconf_reg(struct pinctrl_dev *pctldev,
+ unsigned int pin,
+ enum tz1090_pinconf_param param,
+ bool report_err,
+ u32 *reg, u32 *width, u32 *mask, u32 *shift)
+{
+ struct tz1090_pinconf_pullup *pu;
+
+ /* Only core GPIO'able pins */
+ if (pin >= NUM_GPIOS)
+ return -EINVAL;
+
+ /* Find information about parameter's register */
+ switch (param) {
+ case TZ1090_PINCONF_PARAM_SELECT:
+ *reg = REG_PINCTRL_SELECT + 4*(pin / 30);
+ *shift = pin % 30;
+ *width = 1;
+ break;
+ case TZ1090_PINCONF_PARAM_PULL:
+ pu = &tz1090_pinconf_pullup[pin];
+ *reg = REG_PINCTRL_PU_PD + 4*pu->index;
+ *shift = pu->shift;
+ *width = 2;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ /* Calculate field information */
+ *mask = ((1 << *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 tz1090_pinconf_param param = TZ1090_PINCONF_UNPACK_PARAM(*config);
+ int ret;
+ u32 reg, width, mask, shift, val, arg;
+
+ /* Get register information */
+ ret = tz1090_pinconf_reg(pctldev, pin, param, true,
+ &reg, &width, &mask, &shift);
+ if (ret < 0)
+ return ret;
+
+ /* Extract field from register */
+ val = pmx_read(pmx, reg);
+ arg = (val & mask) >> shift;
+
+ /* And pack config */
+ *config = TZ1090_PINCONF_PACK(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 tz1090_pinconf_param param = TZ1090_PINCONF_UNPACK_PARAM(config);
+ unsigned int arg = TZ1090_PINCONF_UNPACK_ARG(config);
+ int ret;
+ u32 reg, width, mask, shift, val;
+ unsigned long flags;
+
+ dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n",
+ __func__, tz1090_pins[pin].name, config);
+
+ /* Select is a special case for writing */
+ if (param == TZ1090_PINCONF_PARAM_SELECT) {
+ if (arg > 1) {
+ dev_dbg(pctldev->dev, "%s: select larger than 1\n",
+ __func__);
+ return -EINVAL;
+ }
+ tz1090_pinctrl_force_select(pmx, pin, arg);
+ return 0;
+ }
+
+ /* Get register information */
+ ret = tz1090_pinconf_reg(pctldev, pin, param, true,
+ &reg, &width, &mask, &shift);
+ if (ret < 0)
+ return ret;
+
+ /* Unpack argument and range check it */
+ if (arg >= (1 << width)) {
+ dev_dbg(pctldev->dev, "%s: arg %u larger than %u bits\n",
+ __func__, arg, width);
+ return -EINVAL;
+ }
+
+ /* Write register field */
+ __global_lock2(flags);
+ val = pmx_read(pmx, reg);
+ val &= ~mask;
+ val |= arg << shift;
+ pmx_write(pmx, val, reg);
+ __global_unlock2(flags);
+
+ return 0;
+}
+
+static int tz1090_pinconf_group_reg(struct pinctrl_dev *pctldev,
+ const struct tz1090_pingroup *g,
+ enum tz1090_pinconf_param param,
+ bool report_err,
+ u32 *reg, u32 *width, u32 *mask, u32 *shift)
+{
+ /* 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 -EINVAL;
+ }
+
+ /* Find information about drive parameter's register */
+ switch (param) {
+ case TZ1090_PINCONF_PARAM_SCHMITT:
+ *reg = REG_PINCTRL_SCHMITT;
+ *width = 1;
+ break;
+ case TZ1090_PINCONF_PARAM_SLEW_RATE:
+ *reg = REG_PINCTRL_SR;
+ *width = 1;
+ break;
+ case TZ1090_PINCONF_PARAM_DRIVE_STRENGTH:
+ *reg = REG_PINCTRL_DR;
+ *width = 2;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ /* Calculate field information */
+ *shift = g->slw_bit * *width;
+ *mask = ((1 << *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 tz1090_pinconf_param param = TZ1090_PINCONF_UNPACK_PARAM(*config);
+ int ret;
+ unsigned int pin;
+ u32 reg, width, mask, shift, val, arg;
+
+ if (group >= ARRAY_SIZE(tz1090_groups)) {
+ pin = group - ARRAY_SIZE(tz1090_groups);
+ return tz1090_pinconf_get(pctldev, pin, config);
+ }
+
+ g = &tz1090_groups[group];
+
+ /* We can't really get a per-pin configuration of a group. */
+ if (param < TZ1090_PINCONF_PARAM_GROUPED) {
+ dev_dbg(pctldev->dev,
+ "%s: Can't get per-pin param %u of group %s\n",
+ __func__, param, g->name);
+ return -EINVAL;
+ }
+
+ /* Get register information */
+ ret = tz1090_pinconf_group_reg(pctldev, g, param, true,
+ &reg, &width, &mask, &shift);
+ if (ret < 0)
+ return ret;
+
+ /* Extract field from register */
+ val = pmx_read(pmx, reg);
+ arg = (val & mask) >> shift;
+
+ /* And pack config */
+ *config = TZ1090_PINCONF_PACK(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 tz1090_pinconf_param param = TZ1090_PINCONF_UNPACK_PARAM(config);
+ unsigned int arg, pin, i;
+ const unsigned int *pit;
+ int ret;
+ u32 reg, width, mask, shift, val;
+ unsigned long flags;
+
+ if (group >= ARRAY_SIZE(tz1090_groups)) {
+ pin = group - ARRAY_SIZE(tz1090_groups);
+ return tz1090_pinconf_set(pctldev, pin, config);
+ }
+
+ g = &tz1090_groups[group];
+ dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n",
+ __func__, g->name, config);
+
+ /*
+ * If we're trying to set a per-pin configuration of a group, do the
+ * pins one by one. This is mainly as a convenience.
+ */
+ if (param < TZ1090_PINCONF_PARAM_GROUPED) {
+ for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) {
+ ret = tz1090_pinconf_set(pctldev, *pit, config);
+ if (ret)
+ return ret;
+ }
+ return 0;
+ }
+
+ /* Get register information */
+ ret = tz1090_pinconf_group_reg(pctldev, g, param, true,
+ &reg, &width, &mask, &shift);
+ if (ret < 0)
+ return ret;
+
+ /* Unpack argument and range check it */
+ arg = TZ1090_PINCONF_UNPACK_ARG(config);
+ if (arg >= (1 << width)) {
+ dev_dbg(pctldev->dev, "%s: arg %u larger than %u bits\n",
+ __func__, arg, width);
+ return -EINVAL;
+ }
+
+ /* Write register field */
+ __global_lock2(flags);
+ val = pmx_read(pmx, reg);
+ val &= ~mask;
+ val |= arg << shift;
+ pmx_write(pmx, val, reg);
+ __global_unlock2(flags);
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void tz1090_pinconf_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned pin)
+{
+ struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+ int i, ret;
+ u32 reg, width, mask, shift, val;
+
+ for (i = 0; i < ARRAY_SIZE(cfg_params); i++) {
+ /* Get register information */
+ ret = tz1090_pinconf_reg(pctldev, pin, cfg_params[i].param,
+ false, &reg, &width, &mask, &shift);
+ if (ret < 0)
+ continue;
+
+ /* Extract field from register */
+ val = pmx_read(pmx, reg);
+ val = (val & mask) >> shift;
+
+ seq_printf(s, "\n\t%s=%u",
+ cfg_params[i].property, val);
+ }
+}
+
+static const char *strip_prefix(const char *s)
+{
+ const char *comma = strchr(s, ',');
+ if (!comma)
+ return s;
+
+ return comma + 1;
+}
+
+static void tz1090_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned group)
+{
+ struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+ const struct tz1090_pingroup *g;
+ int i, ret;
+ u32 reg, width, mask, shift, val;
+
+ /* don't show pin configs in group dbg */
+ if (group >= ARRAY_SIZE(tz1090_groups))
+ return;
+
+ g = &tz1090_groups[group];
+ for (i = 0; i < ARRAY_SIZE(cfg_params); i++) {
+ /* Get register information */
+ ret = tz1090_pinconf_group_reg(pctldev, g, cfg_params[i].param,
+ false, &reg, &width, &mask,
+ &shift);
+ if (ret < 0)
+ continue;
+
+ /* Extract field from register */
+ val = pmx_read(pmx, reg);
+ val = (val & mask) >> shift;
+
+ seq_printf(s, "\n\t%s=%u",
+ strip_prefix(cfg_params[i].property), val);
+ }
+}
+
+static void tz1090_pinconf_config_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s,
+ unsigned long config)
+{
+ enum tz1090_pinconf_param param = TZ1090_PINCONF_UNPACK_PARAM(config);
+ u16 arg = TZ1090_PINCONF_UNPACK_ARG(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", strip_prefix(pname), arg);
+}
+#endif
+
+struct pinconf_ops tz1090_pinconf_ops = {
+ .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_dbg_show = tz1090_pinconf_dbg_show,
+ .pin_config_group_dbg_show = tz1090_pinconf_group_dbg_show,
+ .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 struct pinctrl_gpio_range tz1090_pinctrl_gpio_range = {
+ .name = "TZ1090 GPIOs",
+ .base = GPIO_0_BASE,
+ .npins = NUM_GPIOS,
+};
+
+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;
+ }
+
+ pinctrl_add_gpio_range(pmx->pctl, &tz1090_pinctrl_gpio_range);
+
+ 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)
+{
+ 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);
diff --git a/drivers/pinctrl/pinctrl-tz1090.h b/drivers/pinctrl/pinctrl-tz1090.h
new file mode 100644
index 0000000..b2553b0
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tz1090.h
@@ -0,0 +1,58 @@
+#ifndef __PINCTRL_TZ1090_H__
+#define __PINCTRL_TZ1090_H__
+
+enum tz1090_pinconf_param {
+ /* per-gpio parameters */
+ TZ1090_PINCONF_PARAM_PERGPIO = 0,
+
+ /* argument: tz1090_pinconf_select */
+ TZ1090_PINCONF_PARAM_SELECT,
+ /* argument: tz1090_pinconf_pull */
+ TZ1090_PINCONF_PARAM_PULL,
+
+
+ /* grouped drive parameters */
+ TZ1090_PINCONF_PARAM_GROUPED,
+
+ /* argument: tz1090_pinconf_schmitt */
+ TZ1090_PINCONF_PARAM_SCHMITT,
+ /* argument: tz1090_pinconf_slew */
+ TZ1090_PINCONF_PARAM_SLEW_RATE,
+ /* argument: tz1090_pinconf_drive */
+ TZ1090_PINCONF_PARAM_DRIVE_STRENGTH,
+};
+
+enum tz1090_pinconf_select {
+ TZ1090_PINCONF_SELECT_SERIAL,
+ TZ1090_PINCONF_SELECT_GPIO,
+};
+
+enum tz1090_pinconf_pull {
+ TZ1090_PINCONF_PULL_TRISTATE,
+ TZ1090_PINCONF_PULL_UP,
+ TZ1090_PINCONF_PULL_DOWN,
+ TZ1090_PINCONF_PULL_REPEATER,
+};
+
+enum tz1090_pinconf_schmitt {
+ TZ1090_PINCONF_SCHMITT_OFF, /* no hysteresis */
+ TZ1090_PINCONF_SCHMITT_ON, /* schmitt trigger */
+};
+
+enum tz1090_pinconf_slew {
+ TZ1090_PINCONF_SLEW_SLOW, /* half frequency */
+ TZ1090_PINCONF_SLEW_FAST,
+};
+
+enum tz1090_pinconf_drive {
+ TZ1090_PINCONF_DRIVE_2mA,
+ TZ1090_PINCONF_DRIVE_4mA,
+ TZ1090_PINCONF_DRIVE_8mA,
+ TZ1090_PINCONF_DRIVE_12mA,
+};
+
+#define TZ1090_PINCONF_PACK(_param_, _arg_) ((_param_) << 16 | (_arg_))
+#define TZ1090_PINCONF_UNPACK_PARAM(_conf_) ((_conf_) >> 16)
+#define TZ1090_PINCONF_UNPACK_ARG(_conf_) ((_conf_) & 0xffff)
+
+#endif /* __PINCTRL_TZ1090_H__ */
--
1.8.1.2

2013-04-23 14:36:00

by James Hogan

[permalink] [raw]
Subject: [PATCH 3/8] irq-imgpdc: add ImgTec PDC irqchip driver

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 <[email protected]>
Cc: Grant Likely <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: Rob Landley <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: [email protected]
---
.../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 | 553 +++++++++++++++++++++
6 files changed, 687 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: [email protected] {
+ // 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: [email protected] {
+ 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 a350969..c5c5863 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 VERSATILE_FPGA_IRQ
bool
select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 98e3b87..79fa1d8 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -8,4 +8,5 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.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_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
diff --git a/drivers/irqchip/irq-imgpdc.c b/drivers/irqchip/irq-imgpdc.c
new file mode 100644
index 0000000..2060daf
--- /dev/null
+++ b/drivers/irqchip/irq-imgpdc.c
@@ -0,0 +1,553 @@
+/*
+ * IMG PowerDown Controller (PDC)
+ *
+ * Copyright 2010-2012 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.
+ * @lock: Lock to protect the PDC syswake registers.
+ */
+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;
+
+ 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);
+ unsigned int irq_route;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ irq_route = pdc_read(priv, PDC_IRQ_ROUTE);
+ irq_route &= ~(1 << data->hwirq);
+ pdc_write(priv, PDC_IRQ_ROUTE, irq_route);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void perip_irq_unmask(struct irq_data *data)
+{
+ struct pdc_intc_priv *priv = irqd_to_priv(data);
+ unsigned int irq_route;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ irq_route = pdc_read(priv, PDC_IRQ_ROUTE);
+ irq_route |= 1 << data->hwirq;
+ pdc_write(priv, PDC_IRQ_ROUTE, irq_route);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+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);
+ unsigned int irq_en;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ irq_en = pdc_read(priv, PDC_IRQ_ENABLE);
+ irq_en &= ~(PDC_IRQ_SYS0 << syswake);
+ pdc_write(priv, PDC_IRQ_ENABLE, irq_en);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+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);
+ unsigned int irq_en;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ irq_en = pdc_read(priv, PDC_IRQ_ENABLE);
+ irq_en |= PDC_IRQ_SYS0 << syswake;
+ pdc_write(priv, PDC_IRQ_ENABLE, irq_en);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+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;
+ unsigned long flags;
+
+ /* 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;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ /* 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);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ 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 long flags;
+ unsigned int irq_route;
+ unsigned int dst_irq;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ irq_route = pdc_read(priv, PDC_IRQ_ROUTE);
+ if (on)
+ irq_route |= mask;
+ else
+ irq_route &= ~mask;
+ pdc_write(priv, PDC_IRQ_ROUTE, irq_route);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ /* 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);
+
+ spin_lock(&priv->lock);
+ status = pdc_read(priv, PDC_IRQ_STATUS);
+ status &= pdc_read(priv, PDC_IRQ_ENABLE);
+ 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)
+{
+ unsigned int irq_route;
+ 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.
+ */
+ pdc_write(priv, PDC_IRQ_ENABLE, 0);
+
+ /*
+ * Enable routing of all syswakes
+ * Disable all wake sources
+ */
+ irq_route = ((PDC_IRQ_ROUTE_EXT_EN_SYS0 << priv->nr_syswakes) -
+ PDC_IRQ_ROUTE_EXT_EN_SYS0);
+ pdc_write(priv, PDC_IRQ_ROUTE, 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;
+ }
+ 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

2013-04-23 14:36:37

by James Hogan

[permalink] [raw]
Subject: [PATCH 8/8] gpio-tz1090pdc: add TZ1090 PDC gpio driver

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 <[email protected]>
Cc: Grant Likely <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: Rob Landley <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: [email protected]
---
.../devicetree/bindings/gpio/gpio-tz1090-pdc.txt | 40 ++++
arch/metag/boot/dts/tz1090.dtsi | 14 ++
drivers/gpio/Kconfig | 7 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-tz1090-pdc.c | 237 +++++++++++++++++++++
5 files changed, 299 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..5b63847
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-tz1090-pdc.txt
@@ -0,0 +1,40 @@
+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]
+ [standard Linux gpio flags]>
+
+ Values for gpio specifier:
+ - GPIO number: a value in the range 0 to 6.
+ - GPIO flags: standard Linux GPIO flags as found in of_gpio.h
+
+Optional properties:
+- gpio-ranges: Mapping to pin controller pins
+
+- interrupts: Individual syswake interrupts (other GPIOs cannot interrupt)
+
+
+Example:
+
+ pdc_gpios: [email protected] {
+ 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 1d8e2c2..82b43b1 100644
--- a/arch/metag/boot/dts/tz1090.dtsi
+++ b/arch/metag/boot/dts/tz1090.dtsi
@@ -88,5 +88,19 @@
gpio-ranges = <&pinctrl 60 30>;
};
};
+
+ pdc_gpios: [email protected] {
+ 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 7>;
+ };
};
};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 922fefa..56864d8 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -234,6 +234,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_VT8500
bool "VIA/Wondermedia SoC GPIO Support"
depends on ARCH_VT8500
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index fab78c4..d6312fc 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -78,6 +78,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..bbf10fb
--- /dev/null
+++ b/drivers/gpio/gpio-tz1090-pdc.c
@@ -0,0 +1,237 @@
+/*
+ * 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/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>
+#include <asm/soc-tz1090/gpio.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
+
+/* out of PDC gpios, only syswakes have irqs */
+#define NR_PDC_GPIO_IRQS 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[NR_PDC_GPIO_IRQS];
+};
+#define to_pdc(c) container_of(c, struct tz1090_pdc_gpio, chip)
+
+/* Register accesses into the PDC MMIO area */
+
+static void pdc_write(struct tz1090_pdc_gpio *priv, unsigned int reg_offs,
+ unsigned int data)
+{
+ writel(data, priv->reg + reg_offs);
+}
+
+static 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 |= 1 << 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 |= 1 << offset;
+ else
+ value &= ~(1 << offset);
+ pdc_write(priv, REG_SOC_GPIO_CONTROL0, value);
+ }
+
+ value = pdc_read(priv, REG_SOC_GPIO_CONTROL1);
+ value &= ~(1 << 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) & (1 << 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 |= 1 << offset;
+ else
+ value &= ~(1 << 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 = GPIO_PDC_PIN(offset) - GPIO_SYS_WAKE0;
+ int irq;
+
+ /* only syswakes have irqs */
+ if (syswake >= NR_PDC_GPIO_IRQS)
+ 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_PIN(0);
+ priv->chip.ngpio = NR_PDC_GPIO;
+
+ /* Map the syswake irqs */
+ for (i = 0; i < NR_PDC_GPIO_IRQS; ++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

2013-04-23 14:34:31

by James Hogan

[permalink] [raw]
Subject: [PATCH 1/8] metag: of_platform_populate from arch generic code

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 <[email protected]>
Cc: Grant Likely <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: Arnd Bergmann <[email protected]>
---
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

2013-04-23 15:09:58

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH 3/8] irq-imgpdc: add ImgTec PDC irqchip driver

On Tue, 23 Apr 2013, James Hogan wrote:
> +/**
> + * 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.
> + * @lock: Lock to protect the PDC syswake registers.
> + */
> +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;
> +
> + spinlock_t lock;

raw_spinlock_t please

> +/* 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);
> + unsigned int irq_route;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&priv->lock, flags);

This is always called with interrupts disabled.

> + irq_route = pdc_read(priv, PDC_IRQ_ROUTE);
> + irq_route &= ~(1 << data->hwirq);

Why not cache the mask value ?

> + pdc_write(priv, PDC_IRQ_ROUTE, irq_route);

> + spin_unlock_irqrestore(&priv->lock, flags);
> +}
> +
> +static void perip_irq_unmask(struct irq_data *data)
> +{
> + struct pdc_intc_priv *priv = irqd_to_priv(data);
> + unsigned int irq_route;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&priv->lock, flags);
> + irq_route = pdc_read(priv, PDC_IRQ_ROUTE);
> + irq_route |= 1 << data->hwirq;
> + pdc_write(priv, PDC_IRQ_ROUTE, irq_route);

This code is another slightly different copy of stuff which is
available in kernel/irq/generic-chip.c

Can we please stop the code duplication and reuse existing
infrastructure? Don't tell me it does not work for you. I sent out a
patch yesterday which makes the code suitable for irq domains.

> +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;
> + unsigned long flags;
> +
> + /* 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;
> + }
> +
> + spin_lock_irqsave(&priv->lock, flags);

All irqchip callbacks are called with interrupts disabled.

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

Whee. Aren't these numbers linear ?

> + /* 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);
> +
> + spin_lock(&priv->lock);
> + status = pdc_read(priv, PDC_IRQ_STATUS);
> + status &= pdc_read(priv, PDC_IRQ_ENABLE);
> + 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 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;
> + }
> + 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;

Leaks priv.

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

Leaks priv and mapping

> + }
> + if (val > SYS0_HWIRQ) {
> + dev_err(&pdev->dev, "num-perips (%u) out of range\n", val);
> + return -EINVAL;

Error handling is optional, right?

> +static int pdc_intc_remove(struct platform_device *pdev)
> +{
> + struct pdc_intc_priv *priv = platform_get_drvdata(pdev);
> +
> + irq_domain_remove(priv->domain);

And the rest of the resources is still there?

2013-04-23 15:25:40

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 2/8] metag: minimal TZ1090 (Comet) SoC infrastructure

On Tuesday 23 April 2013, James Hogan wrote:

> @@ -46,6 +46,12 @@ core-y += arch/metag/boot/dts/
> core-y += arch/metag/kernel/
> core-y += arch/metag/mm/
>
> +# SoCs
> +socdir-$(CONFIG_SOC_TZ1090) += tz1090
> +
> +socdirs := $(filter-out ., $(patsubst %,%/,$(socdir-y)))
> +core-y += $(addprefix arch/metag/soc/, $(socdirs))
> +

Does it actually make sense to have subdirectories per soc? I would
suggest you copy from arm64 rather from arm for the platform support and
do it as minimal as possible. Any code you need can go into a shared directory
as a start, and if you end up needing more of a hierarchical structure,
you can add that later. Hopefully we've come to the point now where almost
everything can live in drivers/* though.

> diff --git a/arch/metag/configs/tz1090_defconfig b/arch/metag/configs/tz1090_defconfig
> new file mode 100644
> index 0000000..4794094
> --- /dev/null
> +++ b/arch/metag/configs/tz1090_defconfig

Also, if this is compatible with your previous platform, I would recommend just
having a single defconfig that runs on all supported hardware. It's easy enough
for users to turn off the drivers and platforms they don't need.

> diff --git a/arch/metag/soc/tz1090/setup.c b/arch/metag/soc/tz1090/setup.c
> new file mode 100644
> index 0000000..fbd7074
> --- /dev/null
> +++ b/arch/metag/soc/tz1090/setup.c
> +
> +#include <linux/init.h>
> +#include <asm/mach/arch.h>
> +
> +static const char *tz1090_boards_compat[] __initdata = {
> + "toumaz,tz1090",
> + NULL,
> +};
> +
> +MACHINE_START(TZ1090, "Generic TZ1090")
> + .dt_compat = tz1090_boards_compat,
> +MACHINE_END

Have you looked at the patch I sent for default platform code on ARM?
The idea is to default to an empty machine descriptor if nothing matches
the root compatible entry of the DT. The same would work here to allow
you to run without any board code at all.

Arnd

2013-04-23 16:06:21

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH 2/8] metag: minimal TZ1090 (Comet) SoC infrastructure

Thanks for reviewing Arnd.

On 23/04/13 16:25, Arnd Bergmann wrote:
> On Tuesday 23 April 2013, James Hogan wrote:
>
>> @@ -46,6 +46,12 @@ core-y += arch/metag/boot/dts/
>> core-y += arch/metag/kernel/
>> core-y += arch/metag/mm/
>>
>> +# SoCs
>> +socdir-$(CONFIG_SOC_TZ1090) += tz1090
>> +
>> +socdirs := $(filter-out ., $(patsubst %,%/,$(socdir-y)))
>> +core-y += $(addprefix arch/metag/soc/, $(socdirs))
>> +
>
> Does it actually make sense to have subdirectories per soc? I would
> suggest you copy from arm64 rather from arm for the platform support and
> do it as minimal as possible. Any code you need can go into a shared directory
> as a start, and if you end up needing more of a hierarchical structure,
> you can add that later. Hopefully we've come to the point now where almost
> everything can live in drivers/* though.

Where is the shared directory for arm64 platforms? (arch/arm64 is
looking pretty bare).

It's certainly heading in that direction a lot. For this patchset I
could get away with dropping arch/metag/soc/*, and deal with anything
that really requires something like it later.

The machine callbacks I was planning on using in future patches are:
* init_time() for calling into the appropriate common clock driver from
time_init(), prior to setting up the timer so that the right frequency
can be reported based on the clock hierarchy specified in DT. I guess
this could be made more general, allowing any enabled clock component to
be initialised at this time.
* init_irq(), for dynamically detecting evaluation silicon and if so
telling the interrupt controller that there are no mask registers (easy
to drop tbh since nobody uses TZ1090 evaluation silicon any longer).
* probably something for setting up power management (suspend to ram /
standby and associated asm code), which would also be used by some
TZ1090 based boards requiring their own power management variations.

>
>> diff --git a/arch/metag/configs/tz1090_defconfig b/arch/metag/configs/tz1090_defconfig
>> new file mode 100644
>> index 0000000..4794094
>> --- /dev/null
>> +++ b/arch/metag/configs/tz1090_defconfig
>
> Also, if this is compatible with your previous platform, I would recommend just
> having a single defconfig that runs on all supported hardware. It's easy enough
> for users to turn off the drivers and platforms they don't need.

Unfortunately the selects in the SOC_TZ1090 are important as they enable
workarounds for hardware quirks which would have a performance impact on
newer cores without those quirks. At the moment (i.e. without doing
dynamic fixups of kernel code like I believe x86 does, and without an
updated compiler that can support fast jump labels), we can probably
only achieve single-SoC multi-board support in a given kernel, at least
when it comes to the TZ1090.

>
>> diff --git a/arch/metag/soc/tz1090/setup.c b/arch/metag/soc/tz1090/setup.c
>> new file mode 100644
>> index 0000000..fbd7074
>> --- /dev/null
>> +++ b/arch/metag/soc/tz1090/setup.c
>> +
>> +#include <linux/init.h>
>> +#include <asm/mach/arch.h>
>> +
>> +static const char *tz1090_boards_compat[] __initdata = {
>> + "toumaz,tz1090",
>> + NULL,
>> +};
>> +
>> +MACHINE_START(TZ1090, "Generic TZ1090")
>> + .dt_compat = tz1090_boards_compat,
>> +MACHINE_END
>
> Have you looked at the patch I sent for default platform code on ARM?
> The idea is to default to an empty machine descriptor if nothing matches
> the root compatible entry of the DT. The same would work here to allow
> you to run without any board code at all.

No I hadn't seen that. I'll look into it. This was meant as a stub to
later be extended, and at the moment without this we would fall back to
an almost identical definition in arch/metag/kernel/machines.c which
wouldn't do any harm at this stage.

Thanks
James

2013-04-24 09:14:16

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH 3/8] irq-imgpdc: add ImgTec PDC irqchip driver

Thanks for the review Thomas!

On 23/04/13 16:09, Thomas Gleixner wrote:
> On Tue, 23 Apr 2013, James Hogan wrote:
>> +/**
>> + * 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.
>> + * @lock: Lock to protect the PDC syswake registers.
>> + */
>> +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;
>> +
>> + spinlock_t lock;
>
> raw_spinlock_t please

Okay.

If I understand right, this would be because on RT, spinlock_t becomes a
mutex and won't work correctly with irqs actually disabled for the irq
callbacks below, is that right?

>> +static void perip_irq_mask(struct irq_data *data)
>> +{
>> + struct pdc_intc_priv *priv = irqd_to_priv(data);
>> + unsigned int irq_route;
>> + unsigned long flags;
>> +
>> + spin_lock_irqsave(&priv->lock, flags);
>
> This is always called with interrupts disabled.

Okay, I'll switch to raw_spin_lock

>> + irq_route = pdc_read(priv, PDC_IRQ_ROUTE);
>> + irq_route &= ~(1 << data->hwirq);
>
> Why not cache the mask value ?

Yes, caching PDC_IRQ_ROUTE should be possible since it should only be
used by this driver (hence the driver local spinlock).

>
>> + pdc_write(priv, PDC_IRQ_ROUTE, irq_route);
>
>> + spin_unlock_irqrestore(&priv->lock, flags);
>> +}
>> +
>> +static void perip_irq_unmask(struct irq_data *data)
>> +{
>> + struct pdc_intc_priv *priv = irqd_to_priv(data);
>> + unsigned int irq_route;
>> + unsigned long flags;
>> +
>> + spin_lock_irqsave(&priv->lock, flags);
>> + irq_route = pdc_read(priv, PDC_IRQ_ROUTE);
>> + irq_route |= 1 << data->hwirq;
>> + pdc_write(priv, PDC_IRQ_ROUTE, irq_route);
>
> This code is another slightly different copy of stuff which is
> available in kernel/irq/generic-chip.c
>
> Can we please stop the code duplication and reuse existing
> infrastructure? Don't tell me it does not work for you. I sent out a
> patch yesterday which makes the code suitable for irq domains.

I'll look into this. kernel/irq/generic-chip.c was added after this
driver was written.

>> +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;
>
> Whee. Aren't these numbers linear ?

Not necessarily as they're virtual irq numbers obtained via
platform_get_irq(), which come individually from device tree. Even their
hardware irq numbers aren't linear as they're not wired to their irqchip
in the same order:
> pdc: [email protected] {
> 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) */
> };

>> +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;
>> + }
>> + 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;
>
> Leaks priv.
>
>> + /* 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;
>
> Leaks priv and mapping
>
>> + }
>> + if (val > SYS0_HWIRQ) {
>> + dev_err(&pdev->dev, "num-perips (%u) out of range\n", val);
>> + return -EINVAL;
>
> Error handling is optional, right?
>
>> +static int pdc_intc_remove(struct platform_device *pdev)
>> +{
>> + struct pdc_intc_priv *priv = platform_get_drvdata(pdev);
>> +
>> + irq_domain_remove(priv->domain);
>
> And the rest of the resources is still there?

I was under the impression devm_kzalloc and devm_ioremap took care of
that in both the probe error case and the remove case:

> * devm_kzalloc - Resource-managed kzalloc
> * Managed kzalloc. Memory allocated with this function is
> * automatically freed on driver detach.

> * devm_ioremap - Managed ioremap()
> * Managed ioremap(). Map is automatically unmapped on driver detach.

I may have misunderstood the whole point of their existence though?

Thanks
James

2013-04-24 09:32:34

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH 3/8] irq-imgpdc: add ImgTec PDC irqchip driver

On Wed, 24 Apr 2013, James Hogan wrote:
> Thanks for the review Thomas!
>
> On 23/04/13 16:09, Thomas Gleixner wrote:
> > On Tue, 23 Apr 2013, James Hogan wrote:
>> + spinlock_t lock;
> >
> > raw_spinlock_t please
>
> Okay.
>
> If I understand right, this would be because on RT, spinlock_t becomes a
> mutex and won't work correctly with irqs actually disabled for the irq
> callbacks below, is that right?

Yep.

> I'll look into this. kernel/irq/generic-chip.c was added after this
> driver was written.

Fair enough.

> >> +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;
> >
> > Whee. Aren't these numbers linear ?
>
> Not necessarily as they're virtual irq numbers obtained via
> platform_get_irq(), which come individually from device tree. Even their
> hardware irq numbers aren't linear as they're not wired to their irqchip
> in the same order:
> > pdc: [email protected] {
> > 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) */
> > };

Interesting.

> >> +static int pdc_intc_remove(struct platform_device *pdev)
> >> +{
> >> + struct pdc_intc_priv *priv = platform_get_drvdata(pdev);
> >> +
> >> + irq_domain_remove(priv->domain);
> >
> > And the rest of the resources is still there?
>
> I was under the impression devm_kzalloc and devm_ioremap took care of
> that in both the probe error case and the remove case:

> > * devm_kzalloc - Resource-managed kzalloc
> > * Managed kzalloc. Memory allocated with this function is
> > * automatically freed on driver detach.
> >
> > * devm_ioremap - Managed ioremap()
> > * Managed ioremap(). Map is automatically unmapped on driver detach.
>
> I may have misunderstood the whole point of their existence though?

No, that was just me missing the devm_ in front of
kzalloc/ioremap. So you're good.

Thanks,

tglx

2013-04-24 13:27:10

by Catalin Marinas

[permalink] [raw]
Subject: Re: [PATCH 2/8] metag: minimal TZ1090 (Comet) SoC infrastructure

On 23 April 2013 17:06, James Hogan <[email protected]> wrote:
> On 23/04/13 16:25, Arnd Bergmann wrote:
>> On Tuesday 23 April 2013, James Hogan wrote:
>>
>>> @@ -46,6 +46,12 @@ core-y += arch/metag/boot/dts/
>>> core-y += arch/metag/kernel/
>>> core-y += arch/metag/mm/
>>>
>>> +# SoCs
>>> +socdir-$(CONFIG_SOC_TZ1090) += tz1090
>>> +
>>> +socdirs := $(filter-out ., $(patsubst %,%/,$(socdir-y)))
>>> +core-y += $(addprefix arch/metag/soc/, $(socdirs))
>>> +
>>
>> Does it actually make sense to have subdirectories per soc? I would
>> suggest you copy from arm64 rather from arm for the platform support and
>> do it as minimal as possible. Any code you need can go into a shared directory
>> as a start, and if you end up needing more of a hierarchical structure,
>> you can add that later. Hopefully we've come to the point now where almost
>> everything can live in drivers/* though.
>
> Where is the shared directory for arm64 platforms? (arch/arm64 is
> looking pretty bare).

There is no platform-specific code under arch/arm64/. SoC code is
split into various subsystems under drivers/ and it lives there
(including timers and irqchip). We have the SMP booting protocol but
there is no reason why SoCs can't use a common one (or two) specified
via DT (as it is the case of other SoC specific definitions).

> It's certainly heading in that direction a lot. For this patchset I
> could get away with dropping arch/metag/soc/*, and deal with anything
> that really requires something like it later.
>
> The machine callbacks I was planning on using in future patches are:
> * init_time() for calling into the appropriate common clock driver from
> time_init(), prior to setting up the timer so that the right frequency
> can be reported based on the clock hierarchy specified in DT. I guess
> this could be made more general, allowing any enabled clock component to
> be initialised at this time.

This is driven by DT on arm64, no need for platform callback (see
drivers/clocksource/arch_arm_timer.c).

> * init_irq(), for dynamically detecting evaluation silicon and if so
> telling the interrupt controller that there are no mask registers (easy
> to drop tbh since nobody uses TZ1090 evaluation silicon any longer).

Similarly, DT-driven (e.g. drivers/irqchip/irq-gic.c) with
irqchip_init() called from the arm64 init_IRQ().

> * probably something for setting up power management (suspend to ram /
> standby and associated asm code), which would also be used by some
> TZ1090 based boards requiring their own power management variations.

If you can separate the CPU-specific power management (like cache
flushing, MMU off) from the SoC part, you can place the SoC under
drivers/power/reset/. We currently moved the vexpress there, though it
is not a complete example for power management. We'll see when we get
more platforms.

--
Catalin

2013-04-24 14:51:53

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH 2/8] metag: minimal TZ1090 (Comet) SoC infrastructure

On 24/04/13 14:26, Catalin Marinas wrote:
> On 23 April 2013 17:06, James Hogan <[email protected]> wrote:
>> On 23/04/13 16:25, Arnd Bergmann wrote:
>>> On Tuesday 23 April 2013, James Hogan wrote:
>>>
>>>> @@ -46,6 +46,12 @@ core-y += arch/metag/boot/dts/
>>>> core-y += arch/metag/kernel/
>>>> core-y += arch/metag/mm/
>>>>
>>>> +# SoCs
>>>> +socdir-$(CONFIG_SOC_TZ1090) += tz1090
>>>> +
>>>> +socdirs := $(filter-out ., $(patsubst %,%/,$(socdir-y)))
>>>> +core-y += $(addprefix arch/metag/soc/, $(socdirs))
>>>> +
>>>
>>> Does it actually make sense to have subdirectories per soc? I would
>>> suggest you copy from arm64 rather from arm for the platform support and
>>> do it as minimal as possible. Any code you need can go into a shared directory
>>> as a start, and if you end up needing more of a hierarchical structure,
>>> you can add that later. Hopefully we've come to the point now where almost
>>> everything can live in drivers/* though.
>>
>> Where is the shared directory for arm64 platforms? (arch/arm64 is
>> looking pretty bare).
>
> There is no platform-specific code under arch/arm64/. SoC code is
> split into various subsystems under drivers/ and it lives there
> (including timers and irqchip). We have the SMP booting protocol but
> there is no reason why SoCs can't use a common one (or two) specified
> via DT (as it is the case of other SoC specific definitions).

Ah okay, thanks. That's what I've been aiming for as much as possible too.

>> It's certainly heading in that direction a lot. For this patchset I
>> could get away with dropping arch/metag/soc/*, and deal with anything
>> that really requires something like it later.
>>
>> The machine callbacks I was planning on using in future patches are:
>> * init_time() for calling into the appropriate common clock driver from
>> time_init(), prior to setting up the timer so that the right frequency
>> can be reported based on the clock hierarchy specified in DT. I guess
>> this could be made more general, allowing any enabled clock component to
>> be initialised at this time.
>
> This is driven by DT on arm64, no need for platform callback (see
> drivers/clocksource/arch_arm_timer.c).

Right. The problem is that the frequency of the core clock in TZ1090
(and hence the arch timer that is derived from it) isn't discoverable in
an arch generic way. I can do something similar to tegra (see
tegra_clocks_init()) to init the common clk stuff early and then do:

node = of_find_compatible_node(NULL, NULL, "img,meta");
clk_core = of_clk_get_by_name(node, "core");
rate = clk_get_rate(clk_core);

>From time_init prior to setting up the arch timer, but I need a platform
callback for that.

>
>> * init_irq(), for dynamically detecting evaluation silicon and if so
>> telling the interrupt controller that there are no mask registers (easy
>> to drop tbh since nobody uses TZ1090 evaluation silicon any longer).
>
> Similarly, DT-driven (e.g. drivers/irqchip/irq-gic.c) with
> irqchip_init() called from the arm64 init_IRQ().

I could put that info in the DT for the irqchip, but it would mean
another variation (I thought the whole point of DT was to handle the
cases that can't be detected automatically).

>
>> * probably something for setting up power management (suspend to ram /
>> standby and associated asm code), which would also be used by some
>> TZ1090 based boards requiring their own power management variations.
>
> If you can separate the CPU-specific power management (like cache
> flushing, MMU off) from the SoC part, you can place the SoC under
> drivers/power/reset/. We currently moved the vexpress there, though it
> is not a complete example for power management. We'll see when we get
> more platforms.

Cool, that looks like a good place for the power stuff.

Thanks
James

2013-04-25 11:25:42

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH 3/8] irq-imgpdc: add ImgTec PDC irqchip driver

Hi Thomas,

On 23/04/13 16:09, Thomas Gleixner wrote:
> On Tue, 23 Apr 2013, James Hogan wrote:
>> + pdc_write(priv, PDC_IRQ_ROUTE, irq_route);
>
>> + spin_unlock_irqrestore(&priv->lock, flags);
>> +}
>> +
>> +static void perip_irq_unmask(struct irq_data *data)
>> +{
>> + struct pdc_intc_priv *priv = irqd_to_priv(data);
>> + unsigned int irq_route;
>> + unsigned long flags;
>> +
>> + spin_lock_irqsave(&priv->lock, flags);
>> + irq_route = pdc_read(priv, PDC_IRQ_ROUTE);
>> + irq_route |= 1 << data->hwirq;
>> + pdc_write(priv, PDC_IRQ_ROUTE, irq_route);
>
> This code is another slightly different copy of stuff which is
> available in kernel/irq/generic-chip.c
>
> Can we please stop the code duplication and reuse existing
> infrastructure? Don't tell me it does not work for you. I sent out a
> patch yesterday which makes the code suitable for irq domains.

If you're referring to the one that adds the IRQ_GC_MASK_FROM_HWIRQ
flag, as far as I can tell this means I'd have to call
irq_setup_generic_chip on each individual irq (since virqs may not be
linear), or else resort to creating a legacy irqdomain (which I thought
was... well for legacy use).

It feels a bit convoluted, and wrong given that it adds itself to
gc_list. Did I misunderstand how you were expecting me to make use of
the generic chip functions with a linear irqdomain?

Given that your patch presumably isn't upstream yet anyway, is it
acceptable to go with the current version (other fixes applied) and
update it later if necessary?

Thanks
James

2013-04-25 15:21:34

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH 2/8] metag: minimal TZ1090 (Comet) SoC infrastructure

On 24/04/13 15:51, James Hogan wrote:
> On 24/04/13 14:26, Catalin Marinas wrote:
>> On 23 April 2013 17:06, James Hogan <[email protected]> wrote:
>>> It's certainly heading in that direction a lot. For this patchset I
>>> could get away with dropping arch/metag/soc/*, and deal with anything
>>> that really requires something like it later.
>>>
>>> The machine callbacks I was planning on using in future patches are:
>>> * init_time() for calling into the appropriate common clock driver from
>>> time_init(), prior to setting up the timer so that the right frequency
>>> can be reported based on the clock hierarchy specified in DT. I guess
>>> this could be made more general, allowing any enabled clock component to
>>> be initialised at this time.
>>
>> This is driven by DT on arm64, no need for platform callback (see
>> drivers/clocksource/arch_arm_timer.c).
>
> Right. The problem is that the frequency of the core clock in TZ1090
> (and hence the arch timer that is derived from it) isn't discoverable in
> an arch generic way. I can do something similar to tegra (see
> tegra_clocks_init()) to init the common clk stuff early and then do:
>
> node = of_find_compatible_node(NULL, NULL, "img,meta");
> clk_core = of_clk_get_by_name(node, "core");
> rate = clk_get_rate(clk_core);
>
> From time_init prior to setting up the arch timer, but I need a platform
> callback for that.

I take that back, I've just noticed commit
f2f6c2556dcc432e50003bc8fa4d62d95906f149 which makes clock setup
callbacks automatic using section cunningness. :-)

Cheers
James

2013-04-25 21:52:02

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 4/8] metag: tz1090: add <asm/soc-tz1090/gpio.h>

On Tue, Apr 23, 2013 at 4:33 PM, James Hogan <[email protected]> wrote:

> Add <asm/soc-tz1090/gpio.h> with definitions to number all the GPIOs
> available on-chip. This is for use by the pinctrl and GPIO drivers.
>
> Signed-off-by: James Hogan <[email protected]>

Usually the specific pins and names is a pecularity best contained
in the GPIO or pinctrl subsystem.

Please try to push this down into the driver, and only use a simple number
to access the GPIOs.

Yours,
Linus Walleij

2013-04-25 22:39:13

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 5/8] pinctrl-tz1090: add TZ1090 pinctrl driver

On Tue, Apr 23, 2013 at 4:33 PM, James Hogan <[email protected]> wrote:

> Add a pin control driver for the main pins on the TZ1090 SoC. This
> doesn't include the low-power pins as they're controlled separately via
> the Powerdown Controller (PDC) registers.
>
> Signed-off-by: James Hogan <[email protected]>
> Cc: Grant Likely <[email protected]>
> Cc: Rob Herring <[email protected]>
> Cc: Rob Landley <[email protected]>
> Cc: Linus Walleij <[email protected]>
> Cc: [email protected]

(...)
> +++ b/drivers/pinctrl/Kconfig
> @@ -196,6 +196,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 PINCONF

Why are you not using GENERIC_PINCONF?

It doesn't seem like this pin controller is using something
that isn't covered by that library.

This way you get rid of TZ1090_PINCONF_PACK() etc
and can use the standard packing.

> +#include <asm/soc-tz1090/gpio.h>

As mentioned I want the pin definintions from the arch to be in this
subsystem as well.

If the GPIO driver is also using the, then move the GPIO driver
into drivers/pinctrl, that is recommended in such cases.

> + * @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).
(...)
> + u32 drv:1;

So what about you use a bool for that?

> + u32 slw_bit:5;
> +};

(...)
> +/* Pin names */
> +
> +static const struct pinctrl_pin_desc tz1090_pins[] = {
> + /* Normal GPIOs */
> + PINCTRL_PIN(TZ1090_PIN_SDIO_CLK, "sdio_clk"),
> + PINCTRL_PIN(TZ1090_PIN_SDIO_CMD, "sdio_cmd"),

Are these actually the names from the datasheet? Usually these
have geographical names, like D1, A7... but just checking.

(...)
> +/* Sub muxes */

Can you describe here briefly what a sub mux is and how it is
deployed in this system? It's getting complicated at this point
so some help would be appreciated.

> +/* Pin group with mux control */
> +#define MUX_PG(pg_name, f0, f1, f2, f3, f4, \
> + mux_r, mux_b, mux_w, slw_b) \
> + { \
> + .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), \
> + .drv = ((slw_b) >= 0), \
> + .slw_bit = (slw_b), \
> + }
> +
> +#define SIMPLE_PG(pg_name) \
> + { \
> + .name = #pg_name, \
> + .pins = pg_name##_pins, \
> + .npins = ARRAY_SIZE(pg_name##_pins), \
> + }
> +
> +#define SIMPLE_DRV_PG(pg_name, slw_b) \
> + { \
> + .name = #pg_name, \
> + .pins = pg_name##_pins, \
> + .npins = ARRAY_SIZE(pg_name##_pins), \
> + .drv = 1, \
> + .slw_bit = (slw_b), \
> + }
> +
> +#define DRV_PG(pg_name, slw_b) \
> + { \
> + .name = "drive_"#pg_name, \
> + .pins = drive_##pg_name##_pins, \
> + .npins = ARRAY_SIZE(drive_##pg_name##_pins), \
> + .drv = 1, \
> + .slw_bit = (slw_b), \
> + }
> +
> +/* 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);

Again, this is not very easy to understand, so more commenting is warranted.
The macros may need individual documentation for being quite
hard to understand.

> +/**
> + * 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, select_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)
> + * @select_en: Pins that have been force seleced by pinconf (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];
> + u32 select_en[3];
> +};

This looks real nice! :-)

(...)
> +/* 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;
> +}

OK one way to solve it, that works...

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

At this point it is pretty important that the pin has a meaningful
name, that is why recycling the function names for pin names
is not good. "spi1_dout" doesn't make very much sense for a pin
that is now used as GPIO, so it better be named exactly like that.

That a pin named "D7" is sometimes used with function "spi1"
and sometimes with some conjured function like "GPIO17" makes
much more sense.

So please check pin names again.

(...)
> +static const struct cfg_param {
> + const char *property;
> + enum tz1090_pinconf_param param;
> +} cfg_params[] = {
> + {"select", TZ1090_PINCONF_PARAM_SELECT},
> + {"pull", TZ1090_PINCONF_PARAM_PULL},
> + {"schmitt", TZ1090_PINCONF_PARAM_SCHMITT},
> + {"slew-rate", TZ1090_PINCONF_PARAM_SLEW_RATE},
> + {"drive-strength", TZ1090_PINCONF_PARAM_DRIVE_STRENGTH},
> +};

Almost all exist in <linux/pinctrl/pinconf-generic.h>.

What is "select"? If you need another config parameter
we can just add it, but explain what it is and we can tell
if it fits.

> +/**
> + * tz1090_pinctrl_select() - update bit in SELECT register
> + * @pmx: Pinmux data
> + * @pin: Pin number (must be within GPIO range)
> + */

What is this SELECT thing now...

> +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 = !serial || gpio || force */
> + select = ((~pmx->pin_en[pmx_index] |
> + pmx->gpio_en[pmx_index] |
> + pmx->select_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 &= ~(1 << reg_shift);
> + val |= select << reg_shift;
> + pmx_write(pmx, val, reg);
> + __global_unlock2(flags);
> +}

What is this __global_lock2/__global_unlock2?

It doesn't look good :-/

I guess it's in one of the other patches. I'll figure it out...

(...)
> +/**
> + * tz1090_pinctrl_serial_select() - enable/disable serial interface for a pin
> + * @pmx: Pinmux data
> + * @pin: Pin number
> + * @gpio_select: 1 to enable serial interface (devices) when not GPIO,
> + * 0 to leave pin in GPIO mode
> + *
> + * Records that serial usage is enabled/disabled so that SELECT register can be
> + * set appropriately when GPIO is disabled.
> + */

Serial? Is that to be interpreted as something connected in series
with the pin so it means we mux in some other signal
than what the GPIO driver stage drives or so?

(...)
> +/**
> + * tz1090_pinctrl_force_select() - enable/disable force gpio select for a pin
> + * @pmx: Pinmux data
> + * @pin: Pin number
> + * @gpio_select: 1 to force GPIO select,
> + * 0 to unforce GPIO select
> + *
> + * Records that the GPIO is force selected and serial interface shouldn't be
> + * enabled.
> + */

What is the "force" in this? What is it forcing away?

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

Oh I start to get what the submux is now ... hm, haha quite odd!

> +static int tz1090_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function,
> + unsigned group)
> +{
> + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
> + const struct tz1090_pingroup *grp;
> + int ret;
> + const unsigned int *pit;
> + unsigned int i, pin;
> +
> + if (group >= ARRAY_SIZE(tz1090_groups)) {
> + pin = group - ARRAY_SIZE(tz1090_groups);
> + dev_dbg(pctldev->dev, "%s(func=%u (%s), pin=%u (%s))\n",
> + __func__,
> + function, tz1090_functions[function].name,
> + pin,
> + tz1090_pins[pin].name);
> +
> + /* no muxing necessary */
> + if (function != TZ1090_MUX_DEFAULT)
> + return -EINVAL;
> + tz1090_pinctrl_serial_select(pmx, pin, 1);
> + return 0;
> + }
> +
> + grp = &tz1090_groups[group];
> + dev_dbg(pctldev->dev, "%s(func=%u (%s), group=%u (%s))\n",
> + __func__,
> + function, tz1090_functions[function].name,
> + group, grp->name);
> +
> + ret = tz1090_pinctrl_enable_mux(pmx, &grp->mux, function);
> + if (ret)
> + return ret;
> +
> + /* set up each pin in group to serial interface */
> + for (i = 0, pit = grp->pins; i < grp->npins; ++i, ++pit)
> + tz1090_pinctrl_serial_select(pmx, *pit, 1);
> +
> + return 0;
> +}

OK now I sort of understand serial too ... so you program the
mux to shunt in a certain peripheral and then do serial selection
to connect the selected signal in series with the pin. Correct?

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

Usually if you define a unique group for every GPIO pin
(as you seem to have done) you do *NOT* define these two
functions. It will instead be handled by the usual groups,
check the code in pinmux.c.

(...)
> +static int tz1090_pinctrl_probe(struct platform_device *pdev)
(...)
> + pinctrl_add_gpio_range(pmx->pctl, &tz1090_pinctrl_gpio_range);

No. This is deprecated. Do this from the GPIO driver instead,
and since you're using device tree, use the range functionality
already present in drivers/gpio/gpiolib-of.c and the standard way
to represent ranges in device trees.

> +static int tz1090_pinctrl_remove(struct platform_device *pdev)
> +{
> + struct tz1090_pmx *pmx = platform_get_drvdata(pdev);
> +
> + pinctrl_unregister(pmx->pctl);
> +
> + return 0;
> +}

You forgot to remove the GPIO range. But whatever, because it's
going away.

> diff --git a/drivers/pinctrl/pinctrl-tz1090.h b/drivers/pinctrl/pinctrl-tz1090.h
> new file mode 100644
> index 0000000..b2553b0
> --- /dev/null
> +++ b/drivers/pinctrl/pinctrl-tz1090.h
> @@ -0,0 +1,58 @@
> +#ifndef __PINCTRL_TZ1090_H__
> +#define __PINCTRL_TZ1090_H__
> +
> +enum tz1090_pinconf_param {
> + /* per-gpio parameters */
> + TZ1090_PINCONF_PARAM_PERGPIO = 0,
> +
> + /* argument: tz1090_pinconf_select */
> + TZ1090_PINCONF_PARAM_SELECT,
> + /* argument: tz1090_pinconf_pull */
> + TZ1090_PINCONF_PARAM_PULL,
> +
> +
> + /* grouped drive parameters */
> + TZ1090_PINCONF_PARAM_GROUPED,
> +
> + /* argument: tz1090_pinconf_schmitt */
> + TZ1090_PINCONF_PARAM_SCHMITT,
> + /* argument: tz1090_pinconf_slew */
> + TZ1090_PINCONF_PARAM_SLEW_RATE,
> + /* argument: tz1090_pinconf_drive */
> + TZ1090_PINCONF_PARAM_DRIVE_STRENGTH,
> +};
> +
> +enum tz1090_pinconf_select {
> + TZ1090_PINCONF_SELECT_SERIAL,
> + TZ1090_PINCONF_SELECT_GPIO,
> +};
> +
> +enum tz1090_pinconf_pull {
> + TZ1090_PINCONF_PULL_TRISTATE,
> + TZ1090_PINCONF_PULL_UP,
> + TZ1090_PINCONF_PULL_DOWN,
> + TZ1090_PINCONF_PULL_REPEATER,
> +};

Compare:
* @PIN_CONFIG_BIAS_HIGH_IMPEDANCE: the pin will be set to a high impedance
* mode, also know as "third-state" (tristate) or "high-Z" or "floating".
* On output pins this effectively disconnects the pin, which is useful
* if for example some other pin is going to drive the signal connected
* to it for a while. Pins used for input are usually always high
* impedance.
* @PIN_CONFIG_BIAS_PULL_UP: the pin will be pulled up (usually with high
* impedance to VDD). If the argument is != 0 pull-up is enabled,
* if it is 0, pull-up is disabled.
* @PIN_CONFIG_BIAS_PULL_DOWN: the pin will be pulled down (usually with high
* impedance to GROUND). If the argument is != 0 pull-down is enabled,
* if it is 0, pull-down is disabled.

What is "repeater"?

> +enum tz1090_pinconf_schmitt {
> + TZ1090_PINCONF_SCHMITT_OFF, /* no hysteresis */
> + TZ1090_PINCONF_SCHMITT_ON, /* schmitt trigger */
> +};

Compare:
* @PIN_CONFIG_INPUT_SCHMITT_ENABLE: control schmitt-trigger mode on the pin.
* If the argument != 0, schmitt-trigger mode is enabled. If it's 0,
* schmitt-trigger mode is disabled.

> +enum tz1090_pinconf_slew {
> + TZ1090_PINCONF_SLEW_SLOW, /* half frequency */
> + TZ1090_PINCONF_SLEW_FAST,
> +};

Compare:
* @PIN_CONFIG_SLEW_RATE: if the pin can select slew rate, the argument to
* this parameter (on a custom format) tells the driver which alternative
* slew rate to use.

> +enum tz1090_pinconf_drive {
> + TZ1090_PINCONF_DRIVE_2mA,
> + TZ1090_PINCONF_DRIVE_4mA,
> + TZ1090_PINCONF_DRIVE_8mA,
> + TZ1090_PINCONF_DRIVE_12mA,
> +};

Compare:
* @PIN_CONFIG_DRIVE_STRENGTH: the pin will output the current passed as
* argument. The argument is in mA.

> +
> +#define TZ1090_PINCONF_PACK(_param_, _arg_) ((_param_) << 16 | (_arg_))
> +#define TZ1090_PINCONF_UNPACK_PARAM(_conf_) ((_conf_) >> 16)
> +#define TZ1090_PINCONF_UNPACK_ARG(_conf_) ((_conf_) & 0xffff)
> +
> +#endif /* __PINCTRL_TZ1090_H__ */


I suggest you get rid of all this. Use pinconf-generic, augment the
pinconf-generic.h file if need be.

Yours,
Linus Walleij

2013-04-25 23:01:04

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 6/8] gpio-tz1090: add TZ1090 gpio driver

On Tue, Apr 23, 2013 at 4:33 PM, James Hogan <[email protected]> wrote:

> Add a GPIO driver for the main GPIOs found in the TZ1090 (Comet) SoC.
> This doesn't include low-power GPIOs as they're controlled separately
> via the Powerdown Controller (PDC) registers.
>
> The driver is instantiated by device tree and supports interrupts for
> all GPIOs.
>
> Signed-off-by: James Hogan <[email protected]>
> Cc: Grant Likely <[email protected]>
> Cc: Rob Herring <[email protected]>
> Cc: Rob Landley <[email protected]>
> Cc: Linus Walleij <[email protected]>
> Cc: [email protected]

(...)
> + - #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]
> + [standard Linux gpio flags]>

So when someone using this device tree for Symbian or Windows
Mobile start to work, what does "standard Linux gpio flags" tell them?

> + Values for gpio specifier:
> + - GPIO number: a value in the range 0 to 29.
> + - GPIO flags: standard Linux GPIO flags as found in of_gpio.h

Dito. Linux-specifics are not generally allowed in device trees,
and if they are anyway used they shall be prefixed with "linux,"

> + Bank subnode optional properties:
> + - gpio-ranges: Mapping to pin controller pins

Here you seem to use DT GPIO ranges, yet the pinctrl driver registers
some GPIO range, care to explain how that fits together?

> + - #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]
> + [standard Linux irq flags]>
> +
> + Values for irq specifier:
> + - GPIO number: a value in the range 0 to 29
> + - IRQ flags: standard Linux IRQ flags for edge and level triggering

Same comments.

(...)

+#include <asm/global_lock.h>

What on earth is that. I can only fear it. I don't like the
looks of that thing.

(...)
> +/* Convenience register accessors */
> +static void tz1090_gpio_write(struct tz1090_gpio_bank *bank,
> + unsigned int reg_offs, u32 data)
> +{
> + iowrite32(data, bank->reg + reg_offs);
> +}
> +
> +static u32 tz1090_gpio_read(struct tz1090_gpio_bank *bank,
> + unsigned int reg_offs)
> +{
> + return ioread32(bank->reg + reg_offs);
> +}

The pinctrl driver included the keyword "inline" for these so
this should be consistent and do that too.

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

This global lock scares me.

+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 &= ~(0x1 << offset);

I usually do this:

#include <linux/bitops.h>

value &= ~BIT(offset);

+ tz1090_gpio_write(bank, reg_offs, value);
+}

> +/* 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 |= 0x1 << offset;

I usually do this:

#include <linux/bitops.h>

value |= BIT(offset);

> +/* 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)

If val is used as it is, make it a bool.

> +{
> + u32 value;
> +
> + value = tz1090_gpio_read(bank, reg_offs);
> + value &= ~(0x1 << offset);
> + value |= !!val << offset;

You're claming val to [0,1] obviously it's a bool.

> + tz1090_gpio_write(bank, reg_offs, 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;
> +}

This is nice, it just glues smoothly into pinctrl here.

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

And here.

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

This is also very nice and handling the toggling edge in
a working way.

Overall looking very nice, just needs som polishing, and I'm way
scared about that global lock.

Yours,
Linus Walleij

2013-04-26 09:22:47

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH 6/8] gpio-tz1090: add TZ1090 gpio driver

Hi Linus,

Thanks for reviewing these patches.

On 26/04/13 00:01, Linus Walleij wrote:
> On Tue, Apr 23, 2013 at 4:33 PM, James Hogan <[email protected]> wrote:
>
>> Add a GPIO driver for the main GPIOs found in the TZ1090 (Comet) SoC.
>> This doesn't include low-power GPIOs as they're controlled separately
>> via the Powerdown Controller (PDC) registers.
>>
>> The driver is instantiated by device tree and supports interrupts for
>> all GPIOs.
>>
>> Signed-off-by: James Hogan <[email protected]>
>> Cc: Grant Likely <[email protected]>
>> Cc: Rob Herring <[email protected]>
>> Cc: Rob Landley <[email protected]>
>> Cc: Linus Walleij <[email protected]>
>> Cc: [email protected]
>
> (...)
>> + - #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]
>> + [standard Linux gpio flags]>
>
> So when someone using this device tree for Symbian or Windows
> Mobile start to work, what does "standard Linux gpio flags" tell them?

well this is what of_gpio.h says:
> /*
> * This is Linux-specific flags. By default controllers' and Linux' mapping
> * match, but GPIO controllers are free to translate their own flags to
> * Linux-specific in their .xlate callback. Though, 1:1 mapping is recommended.
> */
> enum of_gpio_flags {
> OF_GPIO_ACTIVE_LOW = 0x1,
> };

So do you mean that the bindings docs should just explicitly state those
flag values rather than referring to Linux?

>> + Bank subnode optional properties:
>> + - gpio-ranges: Mapping to pin controller pins
>
> Here you seem to use DT GPIO ranges, yet the pinctrl driver registers
> some GPIO range, care to explain how that fits together?

I'll look into this a bit deeper. I suspect the one in the pinctrl
driver shouldn't be there.

> +#include <asm/global_lock.h>
>
> What on earth is that. I can only fear it. I don't like the
> looks of that thing.

Yes, it's not particularly pleasant.

The TZ1090 is designed for digital radios. It has a 2-threaded Meta HTP
as the CPU (which might typically run Linux on one thread and an RTOS
doing audio decode and driving the audio hardware on the other), and 2
UCCPs (these are Radio Processing Units each containing a small Meta
MTX, which will typically interface to a tuner chip and do things like
DAB/FM radio or WiFi).

So basically a bunch of global registers (e.g. pinctrl and gpio) are
shared between all 3 cores (up to 4 OSes). The __global_lock2 should do
all that is required to ensure exclusive access to the register (as long
as the other OSes do something similar when they access the same
registers). This is one of the reasons why there are 3 gpio banks with
separate interrupts, and each bank's interrupt is optional in this driver.

>
> (...)
>> +/* Convenience register accessors */
>> +static void tz1090_gpio_write(struct tz1090_gpio_bank *bank,
>> + unsigned int reg_offs, u32 data)
>> +{
>> + iowrite32(data, bank->reg + reg_offs);
>> +}
>> +
>> +static u32 tz1090_gpio_read(struct tz1090_gpio_bank *bank,
>> + unsigned int reg_offs)
>> +{
>> + return ioread32(bank->reg + reg_offs);
>> +}
>
> The pinctrl driver included the keyword "inline" for these so
> this should be consistent and do that too.

Okay

> +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 &= ~(0x1 << offset);
>
> I usually do this:
>
> #include <linux/bitops.h>
>
> value &= ~BIT(offset);

Good idea.

>> +/* 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)
>
> If val is used as it is, make it a bool.
>
>> +{
>> + u32 value;
>> +
>> + value = tz1090_gpio_read(bank, reg_offs);
>> + value &= ~(0x1 << offset);
>> + value |= !!val << offset;
>
> You're claming val to [0,1] obviously it's a bool.

It's often simply passing on things like the int output_value from the
direction_output and set callbacks. Is it necessary to confuse things by
making it a bool and then turning it back into an int to write into the
register?

> This is also very nice and handling the toggling edge in
> a working way.

Credit goes to some engineers from PURE for the toggling magic :-)

> Overall looking very nice, just needs som polishing, and I'm way
> scared about that global lock.

Thanks again for taking the time to review.

Cheers
James

2013-04-26 11:54:46

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH 5/8] pinctrl-tz1090: add TZ1090 pinctrl driver

Hi Linus,

On 25/04/13 23:39, Linus Walleij wrote:
> On Tue, Apr 23, 2013 at 4:33 PM, James Hogan <[email protected]> wrote:
>
>> Add a pin control driver for the main pins on the TZ1090 SoC. This
>> doesn't include the low-power pins as they're controlled separately via
>> the Powerdown Controller (PDC) registers.
>>
>> Signed-off-by: James Hogan <[email protected]>
>> Cc: Grant Likely <[email protected]>
>> Cc: Rob Herring <[email protected]>
>> Cc: Rob Landley <[email protected]>
>> Cc: Linus Walleij <[email protected]>
>> Cc: [email protected]
>
> (...)
>> +++ b/drivers/pinctrl/Kconfig
>> @@ -196,6 +196,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 PINCONF
>
> Why are you not using GENERIC_PINCONF?
>
> It doesn't seem like this pin controller is using something
> that isn't covered by that library.
>
> This way you get rid of TZ1090_PINCONF_PACK() etc
> and can use the standard packing.

Cool, I wasn't actually aware of that, I'll look into it.

>> +#include <asm/soc-tz1090/gpio.h>
>
> As mentioned I want the pin definintions from the arch to be in this
> subsystem as well.
>
> If the GPIO driver is also using the, then move the GPIO driver
> into drivers/pinctrl, that is recommended in such cases.

okay, I'll sort that out.

>> + * @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).
> (...)
>> + u32 drv:1;
>
> So what about you use a bool for that?

okay (I'll probably convert all the bitfields to normal types. since the
submux was added they don't actually offer any advantage).

>
>> + u32 slw_bit:5;
>> +};
>
> (...)
>> +/* Pin names */
>> +
>> +static const struct pinctrl_pin_desc tz1090_pins[] = {
>> + /* Normal GPIOs */
>> + PINCTRL_PIN(TZ1090_PIN_SDIO_CLK, "sdio_clk"),
>> + PINCTRL_PIN(TZ1090_PIN_SDIO_CMD, "sdio_cmd"),
>
> Are these actually the names from the datasheet? Usually these
> have geographical names, like D1, A7... but just checking.

Yes, these are the ball names in the datasheet, also the names of the
corresponding gpios, and used on board schematics to refer to the SoC
connections, although the corresponding ball indexes are also provided
in one place in $letter$number form. I suspect it's done this way
because many of the pins can only mux to a single peripheral (or gpio).

>
> (...)
>> +/* Sub muxes */
>
> Can you describe here briefly what a sub mux is and how it is
> deployed in this system? It's getting complicated at this point
> so some help would be appreciated.

Sure. The TZ1090 doesn't have a lot of muxing capabilities (about 10
bits devoted to muxing if you ignore debug signals and gpios), but the
largest one (the TFT pins) has a 2 level mux. Here are the register fields:

CR_PADS_TFT_CTL
TFT pad mux control
0 - TFT display
1 - Ext DAC
2 - TS Out 1
3 - Meta2 Trace
4 - Phyway IF

CR_PADS_AFE_DIR_CTL
Control how the TFT pad direction is driven when CR_PADS_TFT_CTL = 1
0 - Pads are output to DAC
1 - not aqadc_stb
2 - iqdac_stb

so the submux is an internal thing that allows the tft pins to appear to
have all the above functions, and if any of the last 3 are chosen the
first level mux is set to 1.

>
>> +/* Pin group with mux control */
>> +#define MUX_PG(pg_name, f0, f1, f2, f3, f4, \
>> + mux_r, mux_b, mux_w, slw_b) \
>> + { \
>> + .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), \
>> + .drv = ((slw_b) >= 0), \
>> + .slw_bit = (slw_b), \
>> + }
>> +
>> +#define SIMPLE_PG(pg_name) \
>> + { \
>> + .name = #pg_name, \
>> + .pins = pg_name##_pins, \
>> + .npins = ARRAY_SIZE(pg_name##_pins), \
>> + }
>> +
>> +#define SIMPLE_DRV_PG(pg_name, slw_b) \
>> + { \
>> + .name = #pg_name, \
>> + .pins = pg_name##_pins, \
>> + .npins = ARRAY_SIZE(pg_name##_pins), \
>> + .drv = 1, \
>> + .slw_bit = (slw_b), \
>> + }
>> +
>> +#define DRV_PG(pg_name, slw_b) \
>> + { \
>> + .name = "drive_"#pg_name, \
>> + .pins = drive_##pg_name##_pins, \
>> + .npins = ARRAY_SIZE(drive_##pg_name##_pins), \
>> + .drv = 1, \
>> + .slw_bit = (slw_b), \
>> + }
>> +
>> +/* 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);
>
> Again, this is not very easy to understand, so more commenting is warranted.
> The macros may need individual documentation for being quite
> hard to understand.

Okay

>
>> +/**
>> + * 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, select_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)
>> + * @select_en: Pins that have been force seleced by pinconf (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];
>> + u32 select_en[3];
>> +};
>
> This looks real nice! :-)

Thanks. Is the behaviour w.r.t. gpios correct/acceptable btw?...
The pins default to "gpio mode" (SELECT=1, where the normal SoC
peripheral cannot mess with it), so these last 3 fields basically allow
a pin to be put in gpio mode iff:

* gpio_en: gpio is requested by gpio driver

* OR !pin_en: pin hasn't had a specific mux function enabled (where each
pin can be individually muxed to "default" to take it out of gpio mode).

* OR select_en: pin is force selected (pinconf select=1) in DT e.g. pin
is part of a mux but isn't wired up to the normal thing, e.g. a TFT pin
that's wired to a button.

I.e. the gpio driver can always take control of a pin even if it's been
muxed (e.g. allowing a driver to do a gpio bitbang bus reset).

>
> (...)
>> +/* 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;
>> +}
>
> OK one way to solve it, that works...
>
>> +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;
>
> At this point it is pretty important that the pin has a meaningful
> name, that is why recycling the function names for pin names
> is not good. "spi1_dout" doesn't make very much sense for a pin
> that is now used as GPIO, so it better be named exactly like that.

I think if the pin's actual name is "spi1_dout", and board schematics
always refer to that pin name, e.g. having an LED connected to
"spi1_dout", then it does make sense for the pin (and pingroup
containing only that pin) to be referred to by that name, whether it's
in gpio mode or doing it's normal job.

> That a pin named "D7" is sometimes used with function "spi1"
> and sometimes with some conjured function like "GPIO17" makes
> much more sense.

Are you suggesting having a function for each GPIO number that is
enabled by muxing? I'm not sure that would work given a configuration
like this, where a pin would have 2 functions:
"spi1" pin group (containing "spi1_dout"), mux to function "spi1"
"spi1_dout" pin, mux to function "GPIO17"

Handling this with pinconf (select=1) works though.

>
> So please check pin names again.
>
> (...)
>> +static const struct cfg_param {
>> + const char *property;
>> + enum tz1090_pinconf_param param;
>> +} cfg_params[] = {
>> + {"select", TZ1090_PINCONF_PARAM_SELECT},
>> + {"pull", TZ1090_PINCONF_PARAM_PULL},
>> + {"schmitt", TZ1090_PINCONF_PARAM_SCHMITT},
>> + {"slew-rate", TZ1090_PINCONF_PARAM_SLEW_RATE},
>> + {"drive-strength", TZ1090_PINCONF_PARAM_DRIVE_STRENGTH},
>> +};
>
> Almost all exist in <linux/pinctrl/pinconf-generic.h>.
>
> What is "select"? If you need another config parameter
> we can just add it, but explain what it is and we can tell
> if it fits.

select refers to the registers CR_PADS_GPIO_SELECT{0,1,2} with
descriptions like this:
Reset values: 0x3fffffc0, 0x3fffffff, 0x3fffffff
29:0 CR_PADS_GPIO_SEL0 GPIO select (1 bit per GPIO pin)
0 = serial interface
1 = GPIO interface
[29] SCB1_SCLK
(etc)

PARAM_GPIO may be a better name (select seems to have just stuck from
when the original gpio driver was written 3 years ago), although it
should be noted that the gpio system still has to enable it too, so it's
really about taking it out of the "serial interface" so that the
connected SoC peripheral cannot mess with it (1) by default (2) if it's
not connected to what the peripheral would expect, e.g. controlling
board power!

>> +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 = !serial || gpio || force */
>> + select = ((~pmx->pin_en[pmx_index] |
>> + pmx->gpio_en[pmx_index] |
>> + pmx->select_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 &= ~(1 << reg_shift);
>> + val |= select << reg_shift;
>> + pmx_write(pmx, val, reg);
>> + __global_unlock2(flags);
>> +}
>
> What is this __global_lock2/__global_unlock2?
>
> It doesn't look good :-/
>
> I guess it's in one of the other patches. I'll figure it out...

I forgot to mention in other email, it's an architecture thing (see
arch/metag/include/asm/global_lock.h).

>
> (...)
>> +/**
>> + * tz1090_pinctrl_serial_select() - enable/disable serial interface for a pin
>> + * @pmx: Pinmux data
>> + * @pin: Pin number
>> + * @gpio_select: 1 to enable serial interface (devices) when not GPIO,
>> + * 0 to leave pin in GPIO mode
>> + *
>> + * Records that serial usage is enabled/disabled so that SELECT register can be
>> + * set appropriately when GPIO is disabled.
>> + */
>
> Serial? Is that to be interpreted as something connected in series
> with the pin so it means we mux in some other signal
> than what the GPIO driver stage drives or so?

Something like that. It's from the CR_PADS_GPIO_SELECT0 register
description where each bit can be:
"0 = serial interface" (connect pin to the named peripheral signal such
as SCB1's SCLK (SCB is I2C controller))
"1 = GPIO interface" (other gpio registers control it).

I've confused more than once over why it's named so confusingly several
times (it sounds more like it's referring to uart than just "some SoC
peripheral").

>
> (...)
>> +/**
>> + * tz1090_pinctrl_force_select() - enable/disable force gpio select for a pin
>> + * @pmx: Pinmux data
>> + * @pin: Pin number
>> + * @gpio_select: 1 to force GPIO select,
>> + * 0 to unforce GPIO select
>> + *
>> + * Records that the GPIO is force selected and serial interface shouldn't be
>> + * enabled.
>> + */
>
> What is the "force" in this? What is it forcing away?

It's sort of the opposite of tz1090_pinctrl_serial_select and overrides
it. E.g. as per the gpio behaviour described above, you might have:
"spi1" pin group (containing "spi1_dout"), mux to function "spi1", which
automatically calls tz1090_pinctrl_serial_select() for each pin.
"spi1_dout" pinconf select=1, which calls tz1090_pinctrl_force_select().

We want the pin to be in gpio mode as long as that select=1
configuration is in place, or the spi controller may mess with it.

The alternative is to explicitly need to pinconf select=0 on any used
pins. Enabling the function "default" on them instead seemed much nicer
and told the pinctrl system that the pin was in use.

>
>> +static int tz1090_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function,
>> + unsigned group)
>> +{
>> + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
>> + const struct tz1090_pingroup *grp;
>> + int ret;
>> + const unsigned int *pit;
>> + unsigned int i, pin;
>> +
>> + if (group >= ARRAY_SIZE(tz1090_groups)) {
>> + pin = group - ARRAY_SIZE(tz1090_groups);
>> + dev_dbg(pctldev->dev, "%s(func=%u (%s), pin=%u (%s))\n",
>> + __func__,
>> + function, tz1090_functions[function].name,
>> + pin,
>> + tz1090_pins[pin].name);
>> +
>> + /* no muxing necessary */
>> + if (function != TZ1090_MUX_DEFAULT)
>> + return -EINVAL;
>> + tz1090_pinctrl_serial_select(pmx, pin, 1);
>> + return 0;
>> + }
>> +
>> + grp = &tz1090_groups[group];
>> + dev_dbg(pctldev->dev, "%s(func=%u (%s), group=%u (%s))\n",
>> + __func__,
>> + function, tz1090_functions[function].name,
>> + group, grp->name);
>> +
>> + ret = tz1090_pinctrl_enable_mux(pmx, &grp->mux, function);
>> + if (ret)
>> + return ret;
>> +
>> + /* set up each pin in group to serial interface */
>> + for (i = 0, pit = grp->pins; i < grp->npins; ++i, ++pit)
>> + tz1090_pinctrl_serial_select(pmx, *pit, 1);
>> +
>> + return 0;
>> +}
>
> OK now I sort of understand serial too ... so you program the
> mux to shunt in a certain peripheral and then do serial selection
> to connect the selected signal in series with the pin. Correct?

Yes, that's how I understand it

>
>> +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;
>> +}
>> +
>> +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);
>> +}
>
> Usually if you define a unique group for every GPIO pin
> (as you seem to have done) you do *NOT* define these two
> functions. It will instead be handled by the usual groups,
> check the code in pinmux.c.
>
> (...)
>> +static int tz1090_pinctrl_probe(struct platform_device *pdev)
> (...)
>> + pinctrl_add_gpio_range(pmx->pctl, &tz1090_pinctrl_gpio_range);
>
> No. This is deprecated. Do this from the GPIO driver instead,
> and since you're using device tree, use the range functionality
> already present in drivers/gpio/gpiolib-of.c and the standard way
> to represent ranges in device trees.

Okay.

>> diff --git a/drivers/pinctrl/pinctrl-tz1090.h b/drivers/pinctrl/pinctrl-tz1090.h
>> new file mode 100644
>> index 0000000..b2553b0
>> --- /dev/null
>> +++ b/drivers/pinctrl/pinctrl-tz1090.h
>> @@ -0,0 +1,58 @@
>> +#ifndef __PINCTRL_TZ1090_H__
>> +#define __PINCTRL_TZ1090_H__
>> +
>> +enum tz1090_pinconf_param {
>> + /* per-gpio parameters */
>> + TZ1090_PINCONF_PARAM_PERGPIO = 0,
>> +
>> + /* argument: tz1090_pinconf_select */
>> + TZ1090_PINCONF_PARAM_SELECT,
>> + /* argument: tz1090_pinconf_pull */
>> + TZ1090_PINCONF_PARAM_PULL,
>> +
>> +
>> + /* grouped drive parameters */
>> + TZ1090_PINCONF_PARAM_GROUPED,
>> +
>> + /* argument: tz1090_pinconf_schmitt */
>> + TZ1090_PINCONF_PARAM_SCHMITT,
>> + /* argument: tz1090_pinconf_slew */
>> + TZ1090_PINCONF_PARAM_SLEW_RATE,
>> + /* argument: tz1090_pinconf_drive */
>> + TZ1090_PINCONF_PARAM_DRIVE_STRENGTH,
>> +};
>> +
>> +enum tz1090_pinconf_select {
>> + TZ1090_PINCONF_SELECT_SERIAL,
>> + TZ1090_PINCONF_SELECT_GPIO,
>> +};
>> +
>> +enum tz1090_pinconf_pull {
>> + TZ1090_PINCONF_PULL_TRISTATE,
>> + TZ1090_PINCONF_PULL_UP,
>> + TZ1090_PINCONF_PULL_DOWN,
>> + TZ1090_PINCONF_PULL_REPEATER,
>> +};
>
> Compare:
> * @PIN_CONFIG_BIAS_HIGH_IMPEDANCE: the pin will be set to a high impedance
> * mode, also know as "third-state" (tristate) or "high-Z" or "floating".
> * On output pins this effectively disconnects the pin, which is useful
> * if for example some other pin is going to drive the signal connected
> * to it for a while. Pins used for input are usually always high
> * impedance.
> * @PIN_CONFIG_BIAS_PULL_UP: the pin will be pulled up (usually with high
> * impedance to VDD). If the argument is != 0 pull-up is enabled,
> * if it is 0, pull-up is disabled.
> * @PIN_CONFIG_BIAS_PULL_DOWN: the pin will be pulled down (usually with high
> * impedance to GROUND). If the argument is != 0 pull-down is enabled,
> * if it is 0, pull-down is disabled.
>
> What is "repeater"?

This is described in the pads datasheet as "Repeater (Bus keeper)", also
known as a bus holder, which means that it keeps the last value driven
on a tri-state bus, so if the other end pulls it high and then goes
tristate it stays high, and same for low.

>
>> +enum tz1090_pinconf_schmitt {
>> + TZ1090_PINCONF_SCHMITT_OFF, /* no hysteresis */
>> + TZ1090_PINCONF_SCHMITT_ON, /* schmitt trigger */
>> +};
>
> Compare:
> * @PIN_CONFIG_INPUT_SCHMITT_ENABLE: control schmitt-trigger mode on the pin.
> * If the argument != 0, schmitt-trigger mode is enabled. If it's 0,
> * schmitt-trigger mode is disabled.
>
>> +enum tz1090_pinconf_slew {
>> + TZ1090_PINCONF_SLEW_SLOW, /* half frequency */
>> + TZ1090_PINCONF_SLEW_FAST,
>> +};
>
> Compare:
> * @PIN_CONFIG_SLEW_RATE: if the pin can select slew rate, the argument to
> * this parameter (on a custom format) tells the driver which alternative
> * slew rate to use.
>
>> +enum tz1090_pinconf_drive {
>> + TZ1090_PINCONF_DRIVE_2mA,
>> + TZ1090_PINCONF_DRIVE_4mA,
>> + TZ1090_PINCONF_DRIVE_8mA,
>> + TZ1090_PINCONF_DRIVE_12mA,
>> +};
>
> Compare:
> * @PIN_CONFIG_DRIVE_STRENGTH: the pin will output the current passed as
> * argument. The argument is in mA.
>
>> +
>> +#define TZ1090_PINCONF_PACK(_param_, _arg_) ((_param_) << 16 | (_arg_))
>> +#define TZ1090_PINCONF_UNPACK_PARAM(_conf_) ((_conf_) >> 16)
>> +#define TZ1090_PINCONF_UNPACK_ARG(_conf_) ((_conf_) & 0xffff)
>> +
>> +#endif /* __PINCTRL_TZ1090_H__ */
>
>
> I suggest you get rid of all this. Use pinconf-generic, augment the
> pinconf-generic.h file if need be.

Awesome, thanks so much for the review comments!

Cheers
James

2013-05-03 08:49:44

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 6/8] gpio-tz1090: add TZ1090 gpio driver

On Fri, Apr 26, 2013 at 11:22 AM, James Hogan <[email protected]> wrote:
>> On Tue, Apr 23, 2013 at 4:33 PM, James Hogan <[email protected]> wrote:

>> So when someone using this device tree for Symbian or Windows
>> Mobile start to work, what does "standard Linux gpio flags" tell them?
>
> well this is what of_gpio.h says:
>> /*
>> * This is Linux-specific flags. By default controllers' and Linux' mapping
>> * match, but GPIO controllers are free to translate their own flags to
>> * Linux-specific in their .xlate callback. Though, 1:1 mapping is recommended.
>> */
>> enum of_gpio_flags {
>> OF_GPIO_ACTIVE_LOW = 0x1,
>> };
>
> So do you mean that the bindings docs should just explicitly state those
> flag values rather than referring to Linux?

Yes, without mentioning Linux.

"setting bit 0 means active low".

> So basically a bunch of global registers (e.g. pinctrl and gpio) are
> shared between all 3 cores (up to 4 OSes). The __global_lock2 should do
> all that is required to ensure exclusive access to the register (as long
> as the other OSes do something similar when they access the same
> registers). This is one of the reasons why there are 3 gpio banks with
> separate interrupts, and each bank's interrupt is optional in this driver.

OK I get it ...

I think this platform will never ever work with single zImage
though, that seems very unlikely given these constraints.

Well you will have to fight this out with the ARM SoC maintainers
anyway. If they are OK with it I will live with it.

(CC ARM SoC for this.)

>>> +/* 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)
>>
>> If val is used as it is, make it a bool.
>>
>>> +{
>>> + u32 value;
>>> +
>>> + value = tz1090_gpio_read(bank, reg_offs);
>>> + value &= ~(0x1 << offset);
>>> + value |= !!val << offset;
>>
>> You're claming val to [0,1] obviously it's a bool.
>
> It's often simply passing on things like the int output_value from the
> direction_output and set callbacks. Is it necessary to confuse things by
> making it a bool and then turning it back into an int to write into the
> register?

Nah no big deal. Keep it like this if it's convenient.
Mainly pointing it out...

Yours,
Linus Walleij

2013-05-03 09:09:29

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH 6/8] gpio-tz1090: add TZ1090 gpio driver

Hi Linus,

On 03/05/13 09:49, Linus Walleij wrote:
> On Fri, Apr 26, 2013 at 11:22 AM, James Hogan <[email protected]> wrote:
>> So basically a bunch of global registers (e.g. pinctrl and gpio) are
>> shared between all 3 cores (up to 4 OSes). The __global_lock2 should do
>> all that is required to ensure exclusive access to the register (as long
>> as the other OSes do something similar when they access the same
>> registers). This is one of the reasons why there are 3 gpio banks with
>> separate interrupts, and each bank's interrupt is optional in this driver.
>
> OK I get it ...
>
> I think this platform will never ever work with single zImage
> though, that seems very unlikely given these constraints.
>
> Well you will have to fight this out with the ARM SoC maintainers
> anyway. If they are OK with it I will live with it.
>
> (CC ARM SoC for this.)

Sorry, I wasn't very clear. This driver runs on a Meta core (i.e.
arch/metag) not an ARM core, and <asm/global_lock.h> is an architecture
thing (LOCK{0,1,2} are instructions).

Cheers
James

2013-05-03 09:13:16

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 5/8] pinctrl-tz1090: add TZ1090 pinctrl driver

On Fri, Apr 26, 2013 at 1:54 PM, James Hogan <[email protected]> wrote:
> On 25/04/13 23:39, Linus Walleij wrote:
>> On Tue, Apr 23, 2013 at 4:33 PM, James Hogan <[email protected]> wrote:
>> (...)
>>> +/* Pin names */
>>> +
>>> +static const struct pinctrl_pin_desc tz1090_pins[] = {
>>> + /* Normal GPIOs */
>>> + PINCTRL_PIN(TZ1090_PIN_SDIO_CLK, "sdio_clk"),
>>> + PINCTRL_PIN(TZ1090_PIN_SDIO_CMD, "sdio_cmd"),
>>
>> Are these actually the names from the datasheet? Usually these
>> have geographical names, like D1, A7... but just checking.
>
> Yes, these are the ball names in the datasheet, also the names of the
> corresponding gpios, and used on board schematics to refer to the SoC
> connections, although the corresponding ball indexes are also provided
> in one place in $letter$number form. I suspect it's done this way
> because many of the pins can only mux to a single peripheral (or gpio).

OK I'll live with it...

>> (...)
>>> +/* Sub muxes */
>>
>> Can you describe here briefly what a sub mux is and how it is
>> deployed in this system? It's getting complicated at this point
>> so some help would be appreciated.
>
> Sure. The TZ1090 doesn't have a lot of muxing capabilities (about 10
> bits devoted to muxing if you ignore debug signals and gpios), but the
> largest one (the TFT pins) has a 2 level mux. Here are the register fields:
>
> CR_PADS_TFT_CTL
> TFT pad mux control
> 0 - TFT display
> 1 - Ext DAC
> 2 - TS Out 1
> 3 - Meta2 Trace
> 4 - Phyway IF
>
> CR_PADS_AFE_DIR_CTL
> Control how the TFT pad direction is driven when CR_PADS_TFT_CTL = 1
> 0 - Pads are output to DAC
> 1 - not aqadc_stb
> 2 - iqdac_stb
>
> so the submux is an internal thing that allows the tft pins to appear to
> have all the above functions, and if any of the last 3 are chosen the
> first level mux is set to 1.

OK! Please put that whole block above in a

/*
* Multi-line comment in the driver so it will be obvious information
* to the poor souls that will later have to understand the code.
*/

>>> +/**
>>> + * 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, select_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)
>>> + * @select_en: Pins that have been force seleced by pinconf (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];
>>> + u32 select_en[3];
>>> +};
>>
>> This looks real nice! :-)
>
> Thanks. Is the behaviour w.r.t. gpios correct/acceptable btw?...
> The pins default to "gpio mode" (SELECT=1, where the normal SoC
> peripheral cannot mess with it), so these last 3 fields basically allow
> a pin to be put in gpio mode iff:
>
> * gpio_en: gpio is requested by gpio driver
>
> * OR !pin_en: pin hasn't had a specific mux function enabled (where each
> pin can be individually muxed to "default" to take it out of gpio mode).
>
> * OR select_en: pin is force selected (pinconf select=1) in DT e.g. pin
> is part of a mux but isn't wired up to the normal thing, e.g. a TFT pin
> that's wired to a button.
>
> I.e. the gpio driver can always take control of a pin even if it's been
> muxed (e.g. allowing a driver to do a gpio bitbang bus reset).

Yes this is the case with most combined drivers.

(Maybe the framework isn't always as helpful as it could be,
improvements welcome.)

>>> +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;
>>
>> At this point it is pretty important that the pin has a meaningful
>> name, that is why recycling the function names for pin names
>> is not good. "spi1_dout" doesn't make very much sense for a pin
>> that is now used as GPIO, so it better be named exactly like that.
>
> I think if the pin's actual name is "spi1_dout", and board schematics
> always refer to that pin name, e.g. having an LED connected to
> "spi1_dout", then it does make sense for the pin (and pingroup
> containing only that pin) to be referred to by that name, whether it's
> in gpio mode or doing it's normal job.

Yes this is OK just checking.

>> That a pin named "D7" is sometimes used with function "spi1"
>> and sometimes with some conjured function like "GPIO17" makes
>> much more sense.
>
> Are you suggesting having a function for each GPIO number that is
> enabled by muxing?

No, in your case the current scheme which matches to the
actual pin names is much better.

>> (...)
>>> +static const struct cfg_param {
>>> + const char *property;
>>> + enum tz1090_pinconf_param param;
>>> +} cfg_params[] = {
>>> + {"select", TZ1090_PINCONF_PARAM_SELECT},
>>> + {"pull", TZ1090_PINCONF_PARAM_PULL},
>>> + {"schmitt", TZ1090_PINCONF_PARAM_SCHMITT},
>>> + {"slew-rate", TZ1090_PINCONF_PARAM_SLEW_RATE},
>>> + {"drive-strength", TZ1090_PINCONF_PARAM_DRIVE_STRENGTH},
>>> +};
>>
>> Almost all exist in <linux/pinctrl/pinconf-generic.h>.
>>
>> What is "select"? If you need another config parameter
>> we can just add it, but explain what it is and we can tell
>> if it fits.
>
> select refers to the registers CR_PADS_GPIO_SELECT{0,1,2} with
> descriptions like this:
> Reset values: 0x3fffffc0, 0x3fffffff, 0x3fffffff
> 29:0 CR_PADS_GPIO_SEL0 GPIO select (1 bit per GPIO pin)
> 0 = serial interface
> 1 = GPIO interface
> [29] SCB1_SCLK
> (etc)
>
> PARAM_GPIO may be a better name (select seems to have just stuck from
> when the original gpio driver was written 3 years ago), although it
> should be noted that the gpio system still has to enable it too, so it's
> really about taking it out of the "serial interface" so that the
> connected SoC peripheral cannot mess with it (1) by default (2) if it's
> not connected to what the peripheral would expect, e.g. controlling
> board power!

The GPIO select should not be visible to the outside like this,
as it is a particular bit that should only be set on request from the
GPIO framework.

If what you need is to set the pin into "GPIO mode" to drive it
to some default state then from pinconf-generic.h you should use
one of the existing defines like PIN_CONFIG_OUTPUT
to actively drive it to high or low as default, or
PIN_CONFIG_BIAS_HIGH_IMPEDANCE for some default
GPIO input mode.

Read the new section named "GPIO mode pitfalls" in
Documentation/pinctrl.txt

>> (...)
>>> +/**
>>> + * tz1090_pinctrl_serial_select() - enable/disable serial interface for a pin
>>> + * @pmx: Pinmux data
>>> + * @pin: Pin number
>>> + * @gpio_select: 1 to enable serial interface (devices) when not GPIO,
>>> + * 0 to leave pin in GPIO mode
>>> + *
>>> + * Records that serial usage is enabled/disabled so that SELECT register can be
>>> + * set appropriately when GPIO is disabled.
>>> + */
>>
>> Serial? Is that to be interpreted as something connected in series
>> with the pin so it means we mux in some other signal
>> than what the GPIO driver stage drives or so?
>
> Something like that. It's from the CR_PADS_GPIO_SELECT0 register
> description where each bit can be:
> "0 = serial interface" (connect pin to the named peripheral signal such
> as SCB1's SCLK (SCB is I2C controller))
> "1 = GPIO interface" (other gpio registers control it).
>
> I've confused more than once over why it's named so confusingly several
> times (it sounds more like it's referring to uart than just "some SoC
> peripheral").

OK I get it, write some smallish blurb about the above in the kerneldoc
right there.

Consider renaming this function like tz1090_pinctrl_mux_pin_to_peripheral()
which sort of tells what it does.

>> (...)
>>> +/**
>>> + * tz1090_pinctrl_force_select() - enable/disable force gpio select for a pin
>>> + * @pmx: Pinmux data
>>> + * @pin: Pin number
>>> + * @gpio_select: 1 to force GPIO select,
>>> + * 0 to unforce GPIO select
>>> + *
>>> + * Records that the GPIO is force selected and serial interface shouldn't be
>>> + * enabled.
>>> + */
>>
>> What is the "force" in this? What is it forcing away?
>
> It's sort of the opposite of tz1090_pinctrl_serial_select and overrides
> it. E.g. as per the gpio behaviour described above, you might have:
> "spi1" pin group (containing "spi1_dout"), mux to function "spi1", which
> automatically calls tz1090_pinctrl_serial_select() for each pin.
> "spi1_dout" pinconf select=1, which calls tz1090_pinctrl_force_select().
>
> We want the pin to be in gpio mode as long as that select=1
> configuration is in place, or the spi controller may mess with it.
>
> The alternative is to explicitly need to pinconf select=0 on any used
> pins. Enabling the function "default" on them instead seemed much nicer
> and told the pinctrl system that the pin was in use.

By the same standards as above consider renaming this function
to tz1090_pinctrl_mux_pin_to_gpio().

>>> +static int tz1090_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function,
>>> + unsigned group)
>>> +{
>>> + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
>>> + const struct tz1090_pingroup *grp;
>>> + int ret;
>>> + const unsigned int *pit;
>>> + unsigned int i, pin;
>>> +
>>> + if (group >= ARRAY_SIZE(tz1090_groups)) {
>>> + pin = group - ARRAY_SIZE(tz1090_groups);
>>> + dev_dbg(pctldev->dev, "%s(func=%u (%s), pin=%u (%s))\n",
>>> + __func__,
>>> + function, tz1090_functions[function].name,
>>> + pin,
>>> + tz1090_pins[pin].name);
>>> +
>>> + /* no muxing necessary */
>>> + if (function != TZ1090_MUX_DEFAULT)
>>> + return -EINVAL;
>>> + tz1090_pinctrl_serial_select(pmx, pin, 1);
>>> + return 0;
>>> + }
>>> +
>>> + grp = &tz1090_groups[group];
>>> + dev_dbg(pctldev->dev, "%s(func=%u (%s), group=%u (%s))\n",
>>> + __func__,
>>> + function, tz1090_functions[function].name,
>>> + group, grp->name);
>>> +
>>> + ret = tz1090_pinctrl_enable_mux(pmx, &grp->mux, function);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + /* set up each pin in group to serial interface */
>>> + for (i = 0, pit = grp->pins; i < grp->npins; ++i, ++pit)
>>> + tz1090_pinctrl_serial_select(pmx, *pit, 1);
>>> +
>>> + return 0;
>>> +}
>>
>> OK now I sort of understand serial too ... so you program the
>> mux to shunt in a certain peripheral and then do serial selection
>> to connect the selected signal in series with the pin. Correct?
>
> Yes, that's how I understand it

By renaming the functions per above, everything becomes much
clearer and less obscure.

>>> +enum tz1090_pinconf_pull {
>>> + TZ1090_PINCONF_PULL_TRISTATE,
>>> + TZ1090_PINCONF_PULL_UP,
>>> + TZ1090_PINCONF_PULL_DOWN,
>>> + TZ1090_PINCONF_PULL_REPEATER,
>>> +};
>>
>> Compare:
>> * @PIN_CONFIG_BIAS_HIGH_IMPEDANCE: the pin will be set to a high impedance
>> * mode, also know as "third-state" (tristate) or "high-Z" or "floating".
>> * On output pins this effectively disconnects the pin, which is useful
>> * if for example some other pin is going to drive the signal connected
>> * to it for a while. Pins used for input are usually always high
>> * impedance.
>> * @PIN_CONFIG_BIAS_PULL_UP: the pin will be pulled up (usually with high
>> * impedance to VDD). If the argument is != 0 pull-up is enabled,
>> * if it is 0, pull-up is disabled.
>> * @PIN_CONFIG_BIAS_PULL_DOWN: the pin will be pulled down (usually with high
>> * impedance to GROUND). If the argument is != 0 pull-down is enabled,
>> * if it is 0, pull-down is disabled.
>>
>> What is "repeater"?
>
> This is described in the pads datasheet as "Repeater (Bus keeper)", also
> known as a bus holder, which means that it keeps the last value driven
> on a tri-state bus, so if the other end pulls it high and then goes
> tristate it stays high, and same for low.

Hm that's a pretty cool thing! I suggest you add a new #define
in very general terms describing the above to pinconf-generic.h
and use that.

Something like PIN_CONFIG_BIAS_REPEATER right below
HIGH_IMPEDANCE, as it is closely related and obviously
a biasing option.

Yours,
Linus Walleij

2013-05-03 12:23:52

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH 5/8] pinctrl-tz1090: add TZ1090 pinctrl driver

Hi Linus,

On 03/05/13 10:13, Linus Walleij wrote:
> On Fri, Apr 26, 2013 at 1:54 PM, James Hogan <[email protected]> wrote:
>> On 25/04/13 23:39, Linus Walleij wrote:
>>> On Tue, Apr 23, 2013 at 4:33 PM, James Hogan <[email protected]> wrote:
>>>> +static const struct cfg_param {
>>>> + const char *property;
>>>> + enum tz1090_pinconf_param param;
>>>> +} cfg_params[] = {
>>>> + {"select", TZ1090_PINCONF_PARAM_SELECT},
>>>> + {"pull", TZ1090_PINCONF_PARAM_PULL},
>>>> + {"schmitt", TZ1090_PINCONF_PARAM_SCHMITT},
>>>> + {"slew-rate", TZ1090_PINCONF_PARAM_SLEW_RATE},
>>>> + {"drive-strength", TZ1090_PINCONF_PARAM_DRIVE_STRENGTH},
>>>> +};
>>>
>>> Almost all exist in <linux/pinctrl/pinconf-generic.h>.
>>>
>>> What is "select"? If you need another config parameter
>>> we can just add it, but explain what it is and we can tell
>>> if it fits.
>>
>> select refers to the registers CR_PADS_GPIO_SELECT{0,1,2} with
>> descriptions like this:
>> Reset values: 0x3fffffc0, 0x3fffffff, 0x3fffffff
>> 29:0 CR_PADS_GPIO_SEL0 GPIO select (1 bit per GPIO pin)
>> 0 = serial interface
>> 1 = GPIO interface
>> [29] SCB1_SCLK
>> (etc)
>>
>> PARAM_GPIO may be a better name (select seems to have just stuck from
>> when the original gpio driver was written 3 years ago), although it
>> should be noted that the gpio system still has to enable it too, so it's
>> really about taking it out of the "serial interface" so that the
>> connected SoC peripheral cannot mess with it (1) by default (2) if it's
>> not connected to what the peripheral would expect, e.g. controlling
>> board power!
>
> The GPIO select should not be visible to the outside like this,
> as it is a particular bit that should only be set on request from the
> GPIO framework.
>
> If what you need is to set the pin into "GPIO mode" to drive it
> to some default state then from pinconf-generic.h you should use
> one of the existing defines like PIN_CONFIG_OUTPUT
> to actively drive it to high or low as default, or
> PIN_CONFIG_BIAS_HIGH_IMPEDANCE for some default
> GPIO input mode.
>
> Read the new section named "GPIO mode pitfalls" in
> Documentation/pinctrl.txt

Thanks, that was interesting. I've had a think about this (and done some
experiments with a multimeter), and the problem is these generic
pinconfs already have meanings which don't match what the SELECT
register does. For example, having a pin be tristate and not controlled
by the peripheral, and having it tristate as far as the gpio hardware is
concerned (e.g. no pull-up) but still controlled by the peripheral, are
two very different things that need disambiguation.

I think what it comes down to as far as pinctrl is concerned is that the
SELECT registers enable/disable peripheral muxes on a per-pin basis.
Therefore perhaps it makes best sense to just have a custom/generic
pinconf PIN_CONFIG_PERIPHERAL_ENABLE/"peripheral=<1>;" to enable
peripheral muxing in the first place (sets SELECT=0), and require it to
be set on all individual pins that need it (i.e. don't automatically set
SELECT=0 on all pins in a group when the mux is enabled). What do you think?

Thanks
James

2013-05-03 13:03:16

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 5/8] pinctrl-tz1090: add TZ1090 pinctrl driver

On Fri, May 3, 2013 at 2:23 PM, James Hogan <[email protected]> wrote:
> [Me]
>> If what you need is to set the pin into "GPIO mode" to drive it
>> to some default state then from pinconf-generic.h you should use
>> one of the existing defines like PIN_CONFIG_OUTPUT
>> to actively drive it to high or low as default, or
>> PIN_CONFIG_BIAS_HIGH_IMPEDANCE for some default
>> GPIO input mode.
>>
>> Read the new section named "GPIO mode pitfalls" in
>> Documentation/pinctrl.txt
>
> Thanks, that was interesting. I've had a think about this (and done some
> experiments with a multimeter), and the problem is these generic
> pinconfs already have meanings which don't match what the SELECT
> register does. For example, having a pin be tristate and not controlled
> by the peripheral, and having it tristate as far as the gpio hardware is
> concerned (e.g. no pull-up) but still controlled by the peripheral, are
> two very different things that need disambiguation.

I don't know if that is necessary.

While I do recognize that it is possible that we need to put
pins into "GPIO mode", i.e. drive them actively low or high,
as PIN_CONFIG_OUTPUT does, I'm not convinced that
pin config should handle the case where a signal is passed
through from a peripheral.

I think that for every pin that is put to use for a peripheral
you must anyway at some point call .enable() on the
struct pinmux_ops of the pin controller.

Thus this part of the problem (poking that "select" bit)
should be handled by the pinmux part of the driver.

The pinconf part does not need to know about it.

The idea is definately not for the pin config to act
as a "backend" for pin muxing, rather to partition
the problem into two independent parts.

Yours,
Linus Walleij

2013-05-03 15:06:15

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH 5/8] pinctrl-tz1090: add TZ1090 pinctrl driver

Hi Linus,

On 03/05/13 14:03, Linus Walleij wrote:
> On Fri, May 3, 2013 at 2:23 PM, James Hogan <[email protected]> wrote:
>> [Me]
>>> If what you need is to set the pin into "GPIO mode" to drive it
>>> to some default state then from pinconf-generic.h you should use
>>> one of the existing defines like PIN_CONFIG_OUTPUT
>>> to actively drive it to high or low as default, or
>>> PIN_CONFIG_BIAS_HIGH_IMPEDANCE for some default
>>> GPIO input mode.
>>>
>>> Read the new section named "GPIO mode pitfalls" in
>>> Documentation/pinctrl.txt
>>
>> Thanks, that was interesting. I've had a think about this (and done some
>> experiments with a multimeter), and the problem is these generic
>> pinconfs already have meanings which don't match what the SELECT
>> register does. For example, having a pin be tristate and not controlled
>> by the peripheral, and having it tristate as far as the gpio hardware is
>> concerned (e.g. no pull-up) but still controlled by the peripheral, are
>> two very different things that need disambiguation.
>
> I don't know if that is necessary.
>
> While I do recognize that it is possible that we need to put
> pins into "GPIO mode", i.e. drive them actively low or high,
> as PIN_CONFIG_OUTPUT does, I'm not convinced that
> pin config should handle the case where a signal is passed
> through from a peripheral.
>
> I think that for every pin that is put to use for a peripheral
> you must anyway at some point call .enable() on the
> struct pinmux_ops of the pin controller.
>
> Thus this part of the problem (poking that "select" bit)
> should be handled by the pinmux part of the driver.
>
> The pinconf part does not need to know about it.

Okay, so how would you recommend handling the case of a pin in a muxing
pingroup that shouldn't be put into peripheral mode?

E.g. imagine an 18bit display is wired to the (24bit) tft pins (which
are muxed as a group to "tft" function), and the least significant tft
pins are used as GPIOs to control something like board power supplies.

Without using pinconf I think the muxing pingroups would have to overlap
like below (is that acceptable?):

pingroup "tft_pins"
pins: "red0"..."red7"
functions: "tft", "lcd"

pingroup "red0"
pins: "red0"
functions: "peripheral" (OR "none")
...
pingroup "red7"
pins: "red7"
functions: "peripheral" (OR "none")

and then do something like this?

map {
tft_mux {
pins = "tft_pins";
function = "tft";
/* mux tft pins to tft panel interface */
};
tft_pins {
pins = "red7", "red6", "red5", "red4", "red2";
function = "peripheral";
/* mux pins to peripherals */
};
};

or maybe this:

map {
tft_mux {
pins = "tft_pins";
function = "tft";
/* auto sets individual pins to peripheral */
};
tft_pins {
pins = "red1", "red0";
function = "none";
/* set individual pins to !peripheral */
};
};

Cheers
James

2013-05-14 11:52:36

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 5/8] pinctrl-tz1090: add TZ1090 pinctrl driver

On Fri, May 3, 2013 at 5:06 PM, James Hogan <[email protected]> wrote:
> [me]
>> Thus this part of the problem (poking that "select" bit)
>> should be handled by the pinmux part of the driver.
>>
>> The pinconf part does not need to know about it.
>
> Okay, so how would you recommend handling the case of a pin in a muxing
> pingroup that shouldn't be put into peripheral mode?
>
> E.g. imagine an 18bit display is wired to the (24bit) tft pins (which
> are muxed as a group to "tft" function), and the least significant tft
> pins are used as GPIOs to control something like board power supplies.
>
> Without using pinconf I think the muxing pingroups would have to overlap
> like below (is that acceptable?):

I don't know if I understand your example correctly but are you after
this part of the documentation from Documentation/pinctrl.txt:

Pinmux conventions
==================
(...)
It is possible to map several groups to the same combination of device,
pin controller and function. This is for cases where a certain function on
a certain pin controller may use different sets of pins in different
configurations.

Yours,
Linus Walleij

2013-05-14 12:22:38

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH 5/8] pinctrl-tz1090: add TZ1090 pinctrl driver

On 14/05/13 12:52, Linus Walleij wrote:
> On Fri, May 3, 2013 at 5:06 PM, James Hogan <[email protected]> wrote:
>> [me]
>>> Thus this part of the problem (poking that "select" bit)
>>> should be handled by the pinmux part of the driver.
>>>
>>> The pinconf part does not need to know about it.
>>
>> Okay, so how would you recommend handling the case of a pin in a muxing
>> pingroup that shouldn't be put into peripheral mode?
>>
>> E.g. imagine an 18bit display is wired to the (24bit) tft pins (which
>> are muxed as a group to "tft" function), and the least significant tft
>> pins are used as GPIOs to control something like board power supplies.
>>
>> Without using pinconf I think the muxing pingroups would have to overlap
>> like below (is that acceptable?):
>
> I don't know if I understand your example correctly but are you after
> this part of the documentation from Documentation/pinctrl.txt:
>
> Pinmux conventions
> ==================
> (...)
> It is possible to map several groups to the same combination of device,
> pin controller and function. This is for cases where a certain function on
> a certain pin controller may use different sets of pins in different
> configurations.

I think that's the other way around, i.e. that's talking about mapping
several pingroups to the same function. The next paragraph is closer to
the problem:

Documentation/pinctrl.txt
> - PINS for a certain FUNCTION using a certain PIN GROUP on a certain
> PIN CONTROLLER are provided on a first-come first-serve basis, so if some
> other device mux setting or GPIO pin request has already taken your physical
> pin, you will be denied the use of it. To get (activate) a new setting, the
> old one has to be put (deactivated) first.

In my example the tft pingroup contains all the tft pins, but I might
want a particular pin inside that pingroup to never be controlled by the
mux (using the per-pin mux disable (SELECT) bits).

So if I use pinmux I'd have something like:
* "tft" pingroup maps to "tft" function
* "tft_green0" pingroup (containing individual pin inside "tft"
pingroup) maps to "none" function to disable muxing (or the inverse,
with each pin in use mapping to "periph" to enable muxing).
in which case the pin has multiple muxes "controlling" it (even though
they're sort of nested). The above paragraph seems to condemn this
arrangement.

Or using pinconf I'd have something like:
* "tft" pingroup maps to "tft" function
* "tft_green0" pin (in "tft" pingroup) has pinconf "no-periph" (or the
inverse, with each one in use having pinconf "periph")

Or modifying the pinctrl subsystem maybe something like:
* [optional: particular named pins in] "tft" pingroup map to "tft" function
that way enabling a mux doesn't necessarily apply to all pins in the
group, and drivers for hardware that doesn't support partial enabling of
the mux can reject it (I might experiment with this if I get the time).

Does that make more sense?

Cheers
James

2013-05-15 19:07:51

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 5/8] pinctrl-tz1090: add TZ1090 pinctrl driver

On Tue, May 14, 2013 at 2:22 PM, James Hogan <[email protected]> wrote:

> I think that's the other way around, i.e. that's talking about mapping
> several pingroups to the same function. The next paragraph is closer to
> the problem:
>
> Documentation/pinctrl.txt
>> - PINS for a certain FUNCTION using a certain PIN GROUP on a certain
>> PIN CONTROLLER are provided on a first-come first-serve basis, so if some
>> other device mux setting or GPIO pin request has already taken your physical
>> pin, you will be denied the use of it. To get (activate) a new setting, the
>> old one has to be put (deactivated) first.
>
> In my example the tft pingroup contains all the tft pins, but I might
> want a particular pin inside that pingroup to never be controlled by the
> mux (using the per-pin mux disable (SELECT) bits).
>
> So if I use pinmux I'd have something like:
> * "tft" pingroup maps to "tft" function
> * "tft_green0" pingroup (containing individual pin inside "tft"
> pingroup) maps to "none" function to disable muxing (or the inverse,
> with each pin in use mapping to "periph" to enable muxing).
> in which case the pin has multiple muxes "controlling" it (even though
> they're sort of nested). The above paragraph seems to condemn this
> arrangement.

So if this usecase is what you want to support, why don't you just
exclude that one pin from the "tft" group and put it into another
group?

If you want it activated anyway as part of some state e.g.
"default", you can just specify multiple groups for that state.

> Or using pinconf I'd have something like:
> * "tft" pingroup maps to "tft" function
> * "tft_green0" pin (in "tft" pingroup) has pinconf "no-periph" (or the
> inverse, with each one in use having pinconf "periph")

If you want some pinconf set up for this one pin as part
of a configured state that's OK even if the pin isn't muxed
in as part of this state. muxing and config are orthogonal.

> Or modifying the pinctrl subsystem maybe something like:
> * [optional: particular named pins in] "tft" pingroup map to "tft" function
> that way enabling a mux doesn't necessarily apply to all pins in the
> group, and drivers for hardware that doesn't support partial enabling of
> the mux can reject it (I might experiment with this if I get the time).

No thanks, this will be hopeless to understand, the pinctrl
subsystem is already really hard for outsiders to understand,
let us not raise the bar even more...

Yours,
Linus Walleij

2013-05-15 19:09:19

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 6/8] gpio-tz1090: add TZ1090 gpio driver

On Fri, May 3, 2013 at 11:09 AM, James Hogan <[email protected]> wrote:
> Hi Linus,
> On 03/05/13 09:49, Linus Walleij wrote:
>> On Fri, Apr 26, 2013 at 11:22 AM, James Hogan <[email protected]> wrote:
>>> So basically a bunch of global registers (e.g. pinctrl and gpio) are
>>> shared between all 3 cores (up to 4 OSes). The __global_lock2 should do
>>> all that is required to ensure exclusive access to the register (as long
>>> as the other OSes do something similar when they access the same
>>> registers). This is one of the reasons why there are 3 gpio banks with
>>> separate interrupts, and each bank's interrupt is optional in this driver.
>>
>> OK I get it ...
>>
>> I think this platform will never ever work with single zImage
>> though, that seems very unlikely given these constraints.
>>
>> Well you will have to fight this out with the ARM SoC maintainers
>> anyway. If they are OK with it I will live with it.
>>
>> (CC ARM SoC for this.)
>
> Sorry, I wasn't very clear. This driver runs on a Meta core (i.e.
> arch/metag) not an ARM core, and <asm/global_lock.h> is an architecture
> thing (LOCK{0,1,2} are instructions).

Aha sorry it's my fault, I'm some ARM-head so I just think the whole
world is. Neat arch!

Yours,
Linus Walleij

2013-05-16 09:12:50

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH 5/8] pinctrl-tz1090: add TZ1090 pinctrl driver

Hi Linus,

On 15/05/13 20:07, Linus Walleij wrote:
> On Tue, May 14, 2013 at 2:22 PM, James Hogan <[email protected]> wrote:
>
>> I think that's the other way around, i.e. that's talking about mapping
>> several pingroups to the same function. The next paragraph is closer to
>> the problem:
>>
>> Documentation/pinctrl.txt
>>> - PINS for a certain FUNCTION using a certain PIN GROUP on a certain
>>> PIN CONTROLLER are provided on a first-come first-serve basis, so if some
>>> other device mux setting or GPIO pin request has already taken your physical
>>> pin, you will be denied the use of it. To get (activate) a new setting, the
>>> old one has to be put (deactivated) first.
>>
>> In my example the tft pingroup contains all the tft pins, but I might
>> want a particular pin inside that pingroup to never be controlled by the
>> mux (using the per-pin mux disable (SELECT) bits).
>>
>> So if I use pinmux I'd have something like:
>> * "tft" pingroup maps to "tft" function
>> * "tft_green0" pingroup (containing individual pin inside "tft"
>> pingroup) maps to "none" function to disable muxing (or the inverse,
>> with each pin in use mapping to "periph" to enable muxing).
>> in which case the pin has multiple muxes "controlling" it (even though
>> they're sort of nested). The above paragraph seems to condemn this
>> arrangement.
>
> So if this usecase is what you want to support, why don't you just
> exclude that one pin from the "tft" group and put it into another
> group?

It's a very board specific thing (that was just one example). To
generalise your suggestion would make all muxing pingroups contain no
pins (which is perhaps accurate since the thing that's muxed by the
group is a set of internal signals that may be muxed out to pins via the
SELECT bits).

Cheers
James

2013-05-17 06:47:08

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 5/8] pinctrl-tz1090: add TZ1090 pinctrl driver

On Thu, May 16, 2013 at 11:12 AM, James Hogan <[email protected]> wrote:
> Hi Linus,
> On 15/05/13 20:07, Linus Walleij wrote:
>> On Tue, May 14, 2013 at 2:22 PM, James Hogan <[email protected]> wrote:
>>
>>> I think that's the other way around, i.e. that's talking about mapping
>>> several pingroups to the same function. The next paragraph is closer to
>>> the problem:
>>>
>>> Documentation/pinctrl.txt
>>>> - PINS for a certain FUNCTION using a certain PIN GROUP on a certain
>>>> PIN CONTROLLER are provided on a first-come first-serve basis, so if some
>>>> other device mux setting or GPIO pin request has already taken your physical
>>>> pin, you will be denied the use of it. To get (activate) a new setting, the
>>>> old one has to be put (deactivated) first.
>>>
>>> In my example the tft pingroup contains all the tft pins, but I might
>>> want a particular pin inside that pingroup to never be controlled by the
>>> mux (using the per-pin mux disable (SELECT) bits).
>>>
>>> So if I use pinmux I'd have something like:
>>> * "tft" pingroup maps to "tft" function
>>> * "tft_green0" pingroup (containing individual pin inside "tft"
>>> pingroup) maps to "none" function to disable muxing (or the inverse,
>>> with each pin in use mapping to "periph" to enable muxing).
>>> in which case the pin has multiple muxes "controlling" it (even though
>>> they're sort of nested). The above paragraph seems to condemn this
>>> arrangement.
>>
>> So if this usecase is what you want to support, why don't you just
>> exclude that one pin from the "tft" group and put it into another
>> group?
>
> It's a very board specific thing (that was just one example). To
> generalise your suggestion would make all muxing pingroups contain no
> pins (which is perhaps accurate since the thing that's muxed by the
> group is a set of internal signals that may be muxed out to pins via the
> SELECT bits).

Well there are different ways to skin this cat.

Stephen usually says that if each pin can be muxed individually
(such as if they each have a unique control register switching that
very pin between different devices) then each pin should have its own
pin group.

Then you accumulate these groups into a state, there may be many
of them. One group per pin is perfectly legal.

Myself I've been soft on the issue. I think that is the hardware
engineers designing the pin muxing have had certain usecases in
mind, or (especially) if there are hardware restricts such that
the mux from a system point of view can just be set if it is set
on all pins in succession, we can define a group of pins for the
usecase.

You're making it sound like the one-group-per-pin is a more
viable approach in your case and that will work, but myself I
usually try to go over the usecases from a HW point of view and
think which groups make sense to combine, and come up with
a small set of groups that I combine with a function to get the
right settings for a device.

Please choose the solution you think fits your system best.

However avoid falling into the combinatorial trap, pinctrl
is not about listing all the possible *theoretical* combinations
of pins in groups, that is mathematics. We're doing practical
solutions here.

Yours,
Linus Walleij