2013-04-10 15:45:56

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 1/2] pinmux: Add TB10x pinmux driver

The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
Used to control the pinmux and is a prerequisite for the GPIO driver.

Signed-off-by: Christian Ruppert <[email protected]>
Signed-off-by: Pierrick Hascoet <[email protected]>
---
.../bindings/pinctrl/abilis,tb10x-iomux.txt | 92 +++
drivers/pinctrl/Kconfig | 4 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-tb10x.c | 794 ++++++++++++++++++++
include/linux/pinctrl/pinctrl-tb10x.h | 46 ++
5 files changed, 937 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
create mode 100644 drivers/pinctrl/pinctrl-tb10x.c
create mode 100644 include/linux/pinctrl/pinctrl-tb10x.h

diff --git a/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
new file mode 100644
index 0000000..9979169
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
@@ -0,0 +1,92 @@
+Abilis Systems TB10x pin controller
+===================================
+
+Required properties
+-------------------
+
+- #address-cells: should be <1>.
+- #size-cells: should be <1>;
+- compatible: should be "abilis,tb10x-iomux";
+- reg: should contain the physical address and size of the pin controller's
+ register range.
+
+
+Port definitions
+----------------
+
+Ports are defined (and referenced) by sub-nodes of the pin controller. Every
+sub-node defines exactly one port (i.e. a set of pins). Ports are defined by
+their names in the pin controller driver.
+
+Required port definition subnode properties:
+ - pingrp: should be set to the name of the port's pin group.
+
+The following pin groups are available:
+ - GPIO ports: gpioa_pins, gpiob_pins, gpioc_pins, gpiod_pins, gpioe_pins,
+ gpiof_pins, gpiog_pins, gpioh_pins, gpioi_pins, gpioj_pins,
+ gpiok_pins, gpiol_pins, gpiom_pins, gpion_pins
+ - Serial TS input ports: mis0_pins, mis1_pins, mis2_pins, mis3_pins,
+ mis4_pins, mis5_pins, mis6_pins, mis7_pins
+ - Parallel TS input ports: mip1_pins, mip3_pins, mip5_pins, mip7_pins
+ - Serial TS output ports: mos0_pins, mos1_pins, mos2_pins, mos3_pins
+ - Parallel TS output port: mop_pins
+ - CI+ port: ciplus_pins
+ - CableCard (Mcard) port: mcard_pins
+ - Smart card ports: stc0_pins, stc1_pins
+ - UART ports: uart0_pins, uart1_pins
+ - SPI ports: spi1_pins, spi3_pins
+ - JTAG: jtag_pins
+
+All other ports of the chip are not multiplexed and thus not managed by this
+driver.
+
+
+Pinmux driver control
+---------------------
+
+In case a driver requires a port to be mapped, the corresponding node should
+be compatible with "abilis,simple-pinctrl" and define a pinctrl state named
+"abilis,simple-default" pointing to the port's respective node inside the pin
+controller. Drivers managing pin controller states internally can be
+configured normally as described in the pinctrl-bindings.txt.
+
+The pinmux driver is connected to the TB10x GPIO driver in a way that a GPIO
+node managing a GPIO port can point to the respective pinmux subnode using
+the gpio-pins property
+
+
+Example
+-------
+
+iomux: iomux@FF10601c {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "abilis,tb10x-iomux";
+ reg = <0xFF10601c 0x4>;
+ pctl_gpio_a: pctl-gpio-a {
+ pingrp = "gpioa_pins";
+ };
+ pctl_uart0: pctl-uart0 {
+ pingrp = "uart0_pins";
+ };
+};
+uart@FF100000 {
+ compatible = "snps,dw-apb-uart",
+ "abilis,simple-pinctrl";
+ reg = <0xFF100000 0x100>;
+ clock-frequency = <166666666>;
+ interrupts = <25 1>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ pinctrl-names = "abilis,simple-default"; /* <<<<<<<< */
+ pinctrl-0 = <&pctl_uart0>; /* <<<<<<<< */
+};
+gpioa: gpio@FF140000 {
+ compatible = "abilis,tb10x-gpio";
+ reg = <0xFF140000 0x1000>;
+ gpio-controller;
+ #gpio-cells = <1>;
+ gpio-count = <3>;
+ gpio-base = <0>;
+ gpio-pins = <&pctl_gpio_a>; /* <<<<<<<< */
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 34f51d2..b5b6682 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -235,6 +235,10 @@ config PINCTRL_XWAY
depends on SOC_TYPE_XWAY
depends on PINCTRL_LANTIQ

+config PINCTRL_TB10X
+ bool
+ depends on ARC_PLAT_TB10X
+
endmenu

endif
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f82cc5b..b68d614 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_PINCTRL_EXYNOS) += pinctrl-exynos.o
obj-$(CONFIG_PINCTRL_EXYNOS5440) += pinctrl-exynos5440.o
obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o
obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o
+obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o

obj-$(CONFIG_PLAT_ORION) += mvebu/
obj-$(CONFIG_ARCH_SHMOBILE) += sh-pfc/
diff --git a/drivers/pinctrl/pinctrl-tb10x.c b/drivers/pinctrl/pinctrl-tb10x.c
new file mode 100644
index 0000000..52a1299
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tb10x.c
@@ -0,0 +1,794 @@
+/*
+ * Abilis Systems TB10x pin control driver
+ *
+ * Copyright (C) Abilis Systems 2012
+ *
+ * Author: Christian Ruppert <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/stringify.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/pinctrl/pinctrl-tb10x.h>
+
+#define TB10X_IOMUX_COMPATIBLE ("abilis,tb10x-iomux")
+
+static const struct pinctrl_pin_desc tb10x_pins[] = {
+ /* Port 1 */
+ PINCTRL_PIN(0 + 0, "MICLK_S0"),
+ PINCTRL_PIN(0 + 1, "MISTRT_S0"),
+ PINCTRL_PIN(0 + 2, "MIVAL_S0"),
+ PINCTRL_PIN(0 + 3, "MDI_S0"),
+ PINCTRL_PIN(0 + 4, "GPIOA0"),
+ PINCTRL_PIN(0 + 5, "GPIOA1"),
+ PINCTRL_PIN(0 + 6, "GPIOA2"),
+ PINCTRL_PIN(0 + 7, "MDI_S1"),
+ PINCTRL_PIN(0 + 8, "MIVAL_S1"),
+ PINCTRL_PIN(0 + 9, "MISTRT_S1"),
+ PINCTRL_PIN(0 + 10, "MICLK_S1"),
+ /* Port 2 */
+ PINCTRL_PIN(16 + 0, "MICLK_S2"),
+ PINCTRL_PIN(16 + 1, "MISTRT_S2"),
+ PINCTRL_PIN(16 + 2, "MIVAL_S2"),
+ PINCTRL_PIN(16 + 3, "MDI_S2"),
+ PINCTRL_PIN(16 + 4, "GPIOC0"),
+ PINCTRL_PIN(16 + 5, "GPIOC1"),
+ PINCTRL_PIN(16 + 6, "GPIOC2"),
+ PINCTRL_PIN(16 + 7, "MDI_S3"),
+ PINCTRL_PIN(16 + 8, "MIVAL_S3"),
+ PINCTRL_PIN(16 + 9, "MISTRT_S3"),
+ PINCTRL_PIN(16 + 10, "MICLK_S3"),
+ /* Port 3 */
+ PINCTRL_PIN(32 + 0, "MICLK_S4"),
+ PINCTRL_PIN(32 + 1, "MISTRT_S4"),
+ PINCTRL_PIN(32 + 2, "MIVAL_S4"),
+ PINCTRL_PIN(32 + 3, "MDI_S4"),
+ PINCTRL_PIN(32 + 4, "GPIOE0"),
+ PINCTRL_PIN(32 + 5, "GPIOE1"),
+ PINCTRL_PIN(32 + 6, "GPIOE2"),
+ PINCTRL_PIN(32 + 7, "MDI_S5"),
+ PINCTRL_PIN(32 + 8, "MIVAL_S5"),
+ PINCTRL_PIN(32 + 9, "MISTRT_S5"),
+ PINCTRL_PIN(32 + 10, "MICLK_S5"),
+ /* Port 4 */
+ PINCTRL_PIN(48 + 0, "MICLK_S6"),
+ PINCTRL_PIN(48 + 1, "MISTRT_S6"),
+ PINCTRL_PIN(48 + 2, "MIVAL_S6"),
+ PINCTRL_PIN(48 + 3, "MDI_S6"),
+ PINCTRL_PIN(48 + 4, "GPIOG0"),
+ PINCTRL_PIN(48 + 5, "GPIOG1"),
+ PINCTRL_PIN(48 + 6, "GPIOG2"),
+ PINCTRL_PIN(48 + 7, "MDI_S7"),
+ PINCTRL_PIN(48 + 8, "MIVAL_S7"),
+ PINCTRL_PIN(48 + 9, "MISTRT_S7"),
+ PINCTRL_PIN(48 + 10, "MICLK_S7"),
+ /* Port 6 */
+ PINCTRL_PIN(64 + 0, "T_MOSTRT_S0"),
+ PINCTRL_PIN(64 + 1, "T_MOVAL_S0"),
+ PINCTRL_PIN(64 + 2, "T_MDO_S0"),
+ PINCTRL_PIN(64 + 3, "T_MOSTRT_S1"),
+ PINCTRL_PIN(64 + 4, "T_MOVAL_S1"),
+ PINCTRL_PIN(64 + 5, "T_MDO_S1"),
+ PINCTRL_PIN(64 + 6, "T_MOSTRT_S2"),
+ PINCTRL_PIN(64 + 7, "T_MOVAL_S2"),
+ PINCTRL_PIN(64 + 8, "T_MDO_S2"),
+ PINCTRL_PIN(64 + 9, "T_MOSTRT_S3"),
+ /* Port 7 */
+ PINCTRL_PIN(80 + 0, "UART0_TXD"),
+ PINCTRL_PIN(80 + 1, "UART0_RXD"),
+ PINCTRL_PIN(80 + 2, "UART0_CTS"),
+ PINCTRL_PIN(80 + 3, "UART0_RTS"),
+ PINCTRL_PIN(80 + 4, "UART1_TXD"),
+ PINCTRL_PIN(80 + 5, "UART1_RXD"),
+ PINCTRL_PIN(80 + 6, "UART1_CTS"),
+ PINCTRL_PIN(80 + 7, "UART1_RTS"),
+ /* Port 8 */
+ PINCTRL_PIN(96 + 0, "SPI3_CLK"),
+ PINCTRL_PIN(96 + 1, "SPI3_MISO"),
+ PINCTRL_PIN(96 + 2, "SPI3_MOSI"),
+ PINCTRL_PIN(96 + 3, "SPI3_SSN"),
+ /* Port 9 */
+ PINCTRL_PIN(112 + 0, "SPI1_CLK"),
+ PINCTRL_PIN(112 + 1, "SPI1_MISO"),
+ PINCTRL_PIN(112 + 2, "SPI1_MOSI"),
+ PINCTRL_PIN(112 + 3, "SPI1_SSN0"),
+ PINCTRL_PIN(112 + 4, "SPI1_SSN1"),
+ /* Port 5 */
+ PINCTRL_PIN(128 + 0, "PC_CE1N"),
+ PINCTRL_PIN(128 + 1, "PC_CE2N"),
+ PINCTRL_PIN(128 + 2, "PC_REGN"),
+ PINCTRL_PIN(128 + 3, "PC_INPACKN"),
+ PINCTRL_PIN(128 + 4, "PC_OEN"),
+ PINCTRL_PIN(128 + 5, "PC_WEN"),
+ PINCTRL_PIN(128 + 6, "PC_IORDN"),
+ PINCTRL_PIN(128 + 7, "PC_IOWRN"),
+ PINCTRL_PIN(128 + 8, "PC_RDYIRQN"),
+ PINCTRL_PIN(128 + 9, "PC_WAITN"),
+ PINCTRL_PIN(128 + 10, "PC_A0"),
+ PINCTRL_PIN(128 + 11, "PC_A1"),
+ PINCTRL_PIN(128 + 12, "PC_A2"),
+ PINCTRL_PIN(128 + 13, "PC_A3"),
+ PINCTRL_PIN(128 + 14, "PC_A4"),
+ PINCTRL_PIN(128 + 15, "PC_A5"),
+ PINCTRL_PIN(128 + 16, "PC_A6"),
+ PINCTRL_PIN(128 + 17, "PC_A7"),
+ PINCTRL_PIN(128 + 18, "PC_A8"),
+ PINCTRL_PIN(128 + 19, "PC_A9"),
+ PINCTRL_PIN(128 + 20, "PC_A10"),
+ PINCTRL_PIN(128 + 21, "PC_A11"),
+ PINCTRL_PIN(128 + 22, "PC_A12"),
+ PINCTRL_PIN(128 + 23, "PC_A13"),
+ PINCTRL_PIN(128 + 24, "PC_A14"),
+ PINCTRL_PIN(128 + 25, "PC_D0"),
+ PINCTRL_PIN(128 + 26, "PC_D1"),
+ PINCTRL_PIN(128 + 27, "PC_D2"),
+ PINCTRL_PIN(128 + 28, "PC_D3"),
+ PINCTRL_PIN(128 + 29, "PC_D4"),
+ PINCTRL_PIN(128 + 30, "PC_D5"),
+ PINCTRL_PIN(128 + 31, "PC_D6"),
+ PINCTRL_PIN(128 + 32, "PC_D7"),
+ PINCTRL_PIN(128 + 33, "PC_MOSTRT"),
+ PINCTRL_PIN(128 + 34, "PC_MOVAL"),
+ PINCTRL_PIN(128 + 35, "PC_MDO0"),
+ PINCTRL_PIN(128 + 36, "PC_MDO1"),
+ PINCTRL_PIN(128 + 37, "PC_MDO2"),
+ PINCTRL_PIN(128 + 38, "PC_MDO3"),
+ PINCTRL_PIN(128 + 39, "PC_MDO4"),
+ PINCTRL_PIN(128 + 40, "PC_MDO5"),
+ PINCTRL_PIN(128 + 41, "PC_MDO6"),
+ PINCTRL_PIN(128 + 42, "PC_MDO7"),
+ PINCTRL_PIN(128 + 43, "PC_MISTRT"),
+ PINCTRL_PIN(128 + 44, "PC_MIVAL"),
+ PINCTRL_PIN(128 + 45, "PC_MDI0"),
+ PINCTRL_PIN(128 + 46, "PC_MDI1"),
+ PINCTRL_PIN(128 + 47, "PC_MDI2"),
+ PINCTRL_PIN(128 + 48, "PC_MDI3"),
+ PINCTRL_PIN(128 + 49, "PC_MDI4"),
+ PINCTRL_PIN(128 + 50, "PC_MDI5"),
+ PINCTRL_PIN(128 + 51, "PC_MDI6"),
+ PINCTRL_PIN(128 + 52, "PC_MDI7"),
+ PINCTRL_PIN(128 + 53, "PC_MICLK"),
+ /* Unmuxed GPIOs */
+ PINCTRL_PIN(256 + 0, "GPIOB0"),
+ PINCTRL_PIN(256 + 1, "GPIOB1"),
+
+ PINCTRL_PIN(256 + 2, "GPIOD0"),
+ PINCTRL_PIN(256 + 3, "GPIOD1"),
+
+ PINCTRL_PIN(256 + 4, "GPIOF0"),
+ PINCTRL_PIN(256 + 5, "GPIOF1"),
+
+ PINCTRL_PIN(256 + 6, "GPIOH0"),
+ PINCTRL_PIN(256 + 7, "GPIOH1"),
+
+ PINCTRL_PIN(256 + 8, "GPIOI0"),
+ PINCTRL_PIN(256 + 9, "GPIOI1"),
+ PINCTRL_PIN(256 + 10, "GPIOI2"),
+ PINCTRL_PIN(256 + 11, "GPIOI3"),
+ PINCTRL_PIN(256 + 12, "GPIOI4"),
+ PINCTRL_PIN(256 + 13, "GPIOI5"),
+ PINCTRL_PIN(256 + 14, "GPIOI6"),
+ PINCTRL_PIN(256 + 15, "GPIOI7"),
+ PINCTRL_PIN(256 + 16, "GPIOI8"),
+ PINCTRL_PIN(256 + 17, "GPIOI9"),
+ PINCTRL_PIN(256 + 18, "GPIOI10"),
+ PINCTRL_PIN(256 + 19, "GPIOI11"),
+
+ PINCTRL_PIN(256 + 20, "GPION0"),
+ PINCTRL_PIN(256 + 21, "GPION1"),
+ PINCTRL_PIN(256 + 22, "GPION2"),
+ PINCTRL_PIN(256 + 23, "GPION3"),
+#define MAX_PIN (256 + 24)
+ PINCTRL_PIN(MAX_PIN, "GPION4"),
+};
+
+
+/* Port 1 */
+static const unsigned mis0_pins[] = { 0, 1, 2, 3};
+static const unsigned gpioa_pins[] = { 4, 5, 6};
+static const unsigned mis1_pins[] = { 7, 8, 9, 10};
+static const unsigned mip1_pins[] = { 0, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10};
+
+/* Port 2 */
+static const unsigned mis2_pins[] = {16, 17, 18, 19};
+static const unsigned gpioc_pins[] = {20, 21, 22};
+static const unsigned mis3_pins[] = {23, 24, 25, 26};
+static const unsigned mip3_pins[] = {16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26};
+
+/* Port 3 */
+static const unsigned mis4_pins[] = {32, 33, 34, 35};
+static const unsigned gpioe_pins[] = {36, 37, 38};
+static const unsigned mis5_pins[] = {39, 40, 41, 42};
+static const unsigned mip5_pins[] = {32, 33, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42};
+
+/* Port 4 */
+static const unsigned mis6_pins[] = {48, 49, 50, 51};
+static const unsigned gpiog_pins[] = {52, 53, 54};
+static const unsigned mis7_pins[] = {55, 56, 57, 58};
+static const unsigned mip7_pins[] = {48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, 58};
+
+/* Port 6 */
+static const unsigned mop_pins[] = {64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73};
+static const unsigned mos0_pins[] = {64, 65, 66};
+static const unsigned mos1_pins[] = {67, 68, 69};
+static const unsigned mos2_pins[] = {70, 71, 72};
+static const unsigned mos3_pins[] = {73};
+
+/* Port 7 */
+static const unsigned uart0_pins[] = {80, 81, 82, 83};
+static const unsigned uart1_pins[] = {84, 85, 86, 87};
+static const unsigned gpiol_pins[] = {80, 81, 82, 83};
+static const unsigned gpiom_pins[] = {84, 85, 86, 87};
+
+/* Port 8 */
+static const unsigned spi3_pins[] = {96, 97, 98, 99};
+static const unsigned jtag_pins[] = {96, 97, 98, 99};
+
+/* Port 9 */
+static const unsigned spi1_pins[] = {112, 113, 114, 115, 116};
+static const unsigned gpion_pins[] = {112, 113, 114, 115, 116};
+
+/* Port 5 */
+static const unsigned gpioj_pins[] = {128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141,
+ 142, 143, 144, 145, 146, 147, 148,
+ 149, 150, 151, 152, 153, 154, 155,
+ 156, 157, 158, 159};
+static const unsigned gpiok_pins[] = {160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 179, 180, 181};
+static const unsigned ciplus_pins[] = {128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156, 157, 158,
+ 159, 160, 161, 162, 163, 164, 165, 166,
+ 167, 168, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 179, 180, 181};
+static const unsigned mcard_pins[] = {131, 138, 139, 140, 150, 151, 161,
+ 163, 164, 165, 166, 167, 168, 169, 170,
+ 171, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181};
+static const unsigned stc0_pins[] = {162, 163, 164, 165, 166, 167, 168};
+static const unsigned stc1_pins[] = {153, 154, 155, 156, 157, 158, 172};
+
+/* Unmuxed GPIOs */
+static const unsigned gpiob_pins[] = {256, 257};
+static const unsigned gpiod_pins[] = {258, 259};
+static const unsigned gpiof_pins[] = {260, 261};
+static const unsigned gpioh_pins[] = {262, 263};
+static const unsigned gpioi_pins[] = {264, 265, 266, 267, 268, 269, 270,
+ 271, 272, 273, 274, 275};
+
+
+struct tb10x_pinfuncgrp {
+ const char *name;
+ const unsigned int *pins;
+ const unsigned int pincnt;
+ const int port;
+ const unsigned int mode;
+ const int isgpio;
+ struct tb10x_pinctrl_state *pctl;
+};
+#define DEFPINFUNCGRP(NAME, PORT, MODE, ISGPIO) { \
+ .name = __stringify(NAME), \
+ .pins = NAME, .pincnt = ARRAY_SIZE(NAME), \
+ .port = (PORT), .mode = (MODE), .pctl = NULL, \
+ .isgpio = (ISGPIO), \
+ }
+static struct tb10x_pinfuncgrp tb10x_pingroups[] = {
+ DEFPINFUNCGRP(mis0_pins, 0, 0, 0),
+ DEFPINFUNCGRP(gpioa_pins, 0, 0, 1),
+ DEFPINFUNCGRP(mis1_pins, 0, 0, 0),
+ DEFPINFUNCGRP(mip1_pins, 0, 1, 0),
+ DEFPINFUNCGRP(mis2_pins, 1, 0, 0),
+ DEFPINFUNCGRP(gpioc_pins, 1, 0, 1),
+ DEFPINFUNCGRP(mis3_pins, 1, 0, 0),
+ DEFPINFUNCGRP(mip3_pins, 1, 1, 0),
+ DEFPINFUNCGRP(mis4_pins, 2, 0, 0),
+ DEFPINFUNCGRP(gpioe_pins, 2, 0, 1),
+ DEFPINFUNCGRP(mis5_pins, 2, 0, 0),
+ DEFPINFUNCGRP(mip5_pins, 2, 1, 0),
+ DEFPINFUNCGRP(mis6_pins, 3, 0, 0),
+ DEFPINFUNCGRP(gpiog_pins, 3, 0, 1),
+ DEFPINFUNCGRP(mis7_pins, 3, 0, 0),
+ DEFPINFUNCGRP(mip7_pins, 3, 1, 0),
+ DEFPINFUNCGRP(gpioj_pins, 4, 0, 1),
+ DEFPINFUNCGRP(gpiok_pins, 4, 0, 1),
+ DEFPINFUNCGRP(ciplus_pins, 4, 1, 0),
+ DEFPINFUNCGRP(mcard_pins, 4, 2, 0),
+ DEFPINFUNCGRP(stc0_pins, 4, 3, 0),
+ DEFPINFUNCGRP(stc1_pins, 4, 3, 0),
+ DEFPINFUNCGRP(mop_pins, 5, 0, 0),
+ DEFPINFUNCGRP(mos0_pins, 5, 1, 0),
+ DEFPINFUNCGRP(mos1_pins, 5, 1, 0),
+ DEFPINFUNCGRP(mos2_pins, 5, 1, 0),
+ DEFPINFUNCGRP(mos3_pins, 5, 1, 0),
+ DEFPINFUNCGRP(uart0_pins, 6, 0, 0),
+ DEFPINFUNCGRP(uart1_pins, 6, 0, 0),
+ DEFPINFUNCGRP(gpiol_pins, 6, 1, 1),
+ DEFPINFUNCGRP(gpiom_pins, 6, 1, 1),
+ DEFPINFUNCGRP(spi3_pins, 7, 0, 0),
+ DEFPINFUNCGRP(jtag_pins, 7, 1, 0),
+ DEFPINFUNCGRP(spi1_pins, 8, 0, 0),
+ DEFPINFUNCGRP(gpion_pins, 8, 1, 1),
+ DEFPINFUNCGRP(gpiob_pins, -1, 0, 1),
+ DEFPINFUNCGRP(gpiod_pins, -1, 0, 1),
+ DEFPINFUNCGRP(gpiof_pins, -1, 0, 1),
+ DEFPINFUNCGRP(gpioh_pins, -1, 0, 1),
+ DEFPINFUNCGRP(gpioi_pins, -1, 0, 1),
+};
+#undef DEFPINFUNCGRP
+
+struct tb10x_of_pinfunc {
+ const char *name;
+ const char *group;
+};
+
+#define TB10X_PORTS (9)
+
+struct tb10x_pinctrl_state {
+ struct pinctrl_dev *pctl;
+ void *iobase;
+ const struct tb10x_pinfuncgrp *pingroups;
+ unsigned int pinfuncgrpcnt;
+ struct tb10x_of_pinfunc *pinfuncs;
+ unsigned int pinfuncnt;
+ struct mutex mutex;
+ struct {
+ unsigned int mode;
+ unsigned int count;
+ } ports[TB10X_PORTS];
+ DECLARE_BITMAP(gpios, MAX_PIN + 1);
+};
+
+static struct tb10x_pinfuncgrp *tb10x_get_pinfuncgrp(const char *name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tb10x_pingroups); i++)
+ if (!strcmp(name, tb10x_pingroups[i].name))
+ return &tb10x_pingroups[i];
+
+ return ERR_PTR(-ENODEV);
+}
+
+struct tb10x_pinfuncgrp *tb10x_prepare_gpio_range(struct device_node *dn,
+ struct pinctrl_gpio_range *gr)
+{
+ const char *name;
+ int ret;
+ struct tb10x_pinfuncgrp *pfg;
+
+ ret = of_property_read_string(dn, "pingrp", &name);
+ if (ret)
+ return ERR_PTR(ret);
+
+ pfg = tb10x_get_pinfuncgrp(name);
+
+ if (!IS_ERR(pfg)) {
+ if (!pfg->isgpio)
+ return ERR_PTR(-EINVAL);
+
+ if (!pfg->pctl)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ gr->pin_base = pfg->pins[0];
+ gr->npins = pfg->pincnt;
+ }
+
+ return pfg;
+}
+EXPORT_SYMBOL(tb10x_prepare_gpio_range);
+
+void tb10x_setup_gpio_range(struct tb10x_pinfuncgrp *pfg,
+ struct pinctrl_gpio_range *gr)
+{
+ pinctrl_add_gpio_range(pfg->pctl->pctl, gr);
+}
+EXPORT_SYMBOL(tb10x_setup_gpio_range);
+
+static inline void tb10x_pinctrl_set_config(struct tb10x_pinctrl_state *state,
+ unsigned int port, unsigned int mode)
+{
+ u32 pcfg;
+
+ if (state->ports[port].count)
+ return;
+
+ state->ports[port].mode = mode;
+
+ pcfg = ioread32(state->iobase) & ~(0x3 << (2 * port));
+ iowrite32(pcfg | ((mode & 0x3) << (2 * port)), state->iobase);
+}
+
+static inline unsigned int tb10x_pinctrl_get_config(
+ struct tb10x_pinctrl_state *state,
+ unsigned int port)
+{
+ return (ioread32(state->iobase) >> (2 * port)) & 0x3;
+}
+
+static int tb10x_get_groups_count(struct pinctrl_dev *pctl)
+{
+ struct tb10x_pinctrl_state *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncgrpcnt;
+}
+
+static const char *tb10x_get_group_name(struct pinctrl_dev *pctl, unsigned n)
+{
+ struct tb10x_pinctrl_state *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pingroups[n].name;
+}
+
+static int tb10x_get_group_pins(struct pinctrl_dev *pctl, unsigned n,
+ unsigned const **pins,
+ unsigned * const num_pins)
+{
+ struct tb10x_pinctrl_state *state = pinctrl_dev_get_drvdata(pctl);
+ *pins = state->pingroups[n].pins;
+ *num_pins = state->pingroups[n].pincnt;
+ return 0;
+}
+
+static int tb10x_dt_node_to_map(struct pinctrl_dev *pctl,
+ struct device_node *np_config,
+ struct pinctrl_map **map, unsigned *num_maps)
+{
+ struct pinctrl_map *m;
+ const char *string;
+
+ if (of_property_read_string(np_config, "pingrp", &string)) {
+ pr_err("%s: No pingrp property in device tree.\n",
+ np_config->full_name);
+ return -EINVAL;
+ }
+
+ m = kzalloc(sizeof(struct pinctrl_map), GFP_KERNEL);
+ if (!m)
+ return -ENOMEM;
+
+ m->type = PIN_MAP_TYPE_MUX_GROUP;
+ m->data.mux.group = string;
+ m->data.mux.function = np_config->name;
+ *map = m;
+ *num_maps = 1;
+ return 0;
+}
+
+static void tb10x_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ kfree(map);
+}
+
+static struct pinctrl_ops tb10x_pinctrl_ops = {
+ .get_groups_count = tb10x_get_groups_count,
+ .get_group_name = tb10x_get_group_name,
+ .get_group_pins = tb10x_get_group_pins,
+ .dt_node_to_map = tb10x_dt_node_to_map,
+ .dt_free_map = tb10x_dt_free_map,
+};
+
+static int tb10x_get_functions_count(struct pinctrl_dev *pctl)
+{
+ struct tb10x_pinctrl_state *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncnt;
+}
+
+static const char *tb10x_get_function_name(struct pinctrl_dev *pctl,
+ unsigned n)
+{
+ struct tb10x_pinctrl_state *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncs[n].name;
+}
+
+static int tb10x_get_function_groups(struct pinctrl_dev *pctl,
+ unsigned n, const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct tb10x_pinctrl_state *state = pinctrl_dev_get_drvdata(pctl);
+ *groups = &state->pinfuncs[n].group;
+ *num_groups = 1;
+ return 0;
+}
+
+static int tb10x_gpio_request_enable(struct pinctrl_dev *pctl,
+ struct pinctrl_gpio_range *range,
+ unsigned pin)
+{
+ struct tb10x_pinctrl_state *state = pinctrl_dev_get_drvdata(pctl);
+ int muxport = -1;
+ int muxmode = -1;
+ int i;
+
+ mutex_lock(&state->mutex);
+
+ for (i = 0; i < ARRAY_SIZE(tb10x_pingroups); i++) {
+ struct tb10x_pinfuncgrp *pfg = &tb10x_pingroups[i];
+ unsigned int port = pfg->port;
+ unsigned int mode = pfg->mode;
+ int j;
+
+ if (port < 0)
+ continue;
+
+ if (pfg->isgpio) {
+ muxport = port;
+ muxmode = mode;
+ continue;
+ }
+
+ for (j = 0; j < pfg->pincnt; j++) {
+ if (pin == pfg->pins[j]) {
+ if (state->ports[port].count
+ && (state->ports[port].mode == mode)) {
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+ break;
+ }
+ }
+ }
+
+ set_bit(pin, state->gpios);
+
+ /* don't need to check for count == 0 because set_config is a NOP
+ * if count != 0
+ */
+ if (muxport >= 0)
+ tb10x_pinctrl_set_config(state, muxport, muxmode);
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static void tb10x_gpio_disable_free(struct pinctrl_dev *pctl,
+ struct pinctrl_gpio_range *range,
+ unsigned pin)
+{
+ struct tb10x_pinctrl_state *state = pinctrl_dev_get_drvdata(pctl);
+
+ mutex_lock(&state->mutex);
+
+ clear_bit(pin, state->gpios);
+
+ mutex_unlock(&state->mutex);
+}
+
+static int tb10x_pctl_enable(struct pinctrl_dev *pctl,
+ unsigned func_selector, unsigned group_selector)
+{
+ struct tb10x_pinctrl_state *state = pinctrl_dev_get_drvdata(pctl);
+ const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector];
+ int i;
+
+ if (grp->port < 0)
+ return 0;
+
+ mutex_lock(&state->mutex);
+
+ if (state->ports[grp->port].count
+ && (state->ports[grp->port].mode != grp->mode)) {
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+
+ for (i = 0; i < grp->pincnt; i++)
+ if (test_bit(grp->pins[i], state->gpios)) {
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+
+ tb10x_pinctrl_set_config(state, grp->port, grp->mode);
+
+ state->ports[grp->port].count++;
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static void tb10x_pctl_disable(struct pinctrl_dev *pctl,
+ unsigned func_selector, unsigned group_selector)
+{
+ struct tb10x_pinctrl_state *state = pinctrl_dev_get_drvdata(pctl);
+ const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector];
+
+ if (grp->port < 0)
+ return;
+
+ mutex_lock(&state->mutex);
+
+ state->ports[grp->port].count--;
+
+ mutex_unlock(&state->mutex);
+}
+
+static struct pinmux_ops tb10x_pinmux_ops = {
+ .get_functions_count = tb10x_get_functions_count,
+ .get_function_name = tb10x_get_function_name,
+ .get_function_groups = tb10x_get_function_groups,
+ .gpio_request_enable = tb10x_gpio_request_enable,
+ .gpio_disable_free = tb10x_gpio_disable_free,
+ .enable = tb10x_pctl_enable,
+ .disable = tb10x_pctl_disable,
+};
+
+static struct pinctrl_desc tb10x_pindesc = {
+ .name = "TB10x",
+ .pins = tb10x_pins,
+ .npins = ARRAY_SIZE(tb10x_pins),
+ .owner = THIS_MODULE,
+ .pctlops = &tb10x_pinctrl_ops,
+ .pmxops = &tb10x_pinmux_ops,
+};
+
+static int tb10x_pinctrl_probe(struct platform_device *pdev)
+{
+ int ret = -EINVAL;
+ struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct device_node *of_node = pdev->dev.of_node;
+ struct device_node *child;
+ struct tb10x_pinctrl_state *state;
+ int i;
+
+ if (!of_node) {
+ dev_err(&pdev->dev, "No device tree node found.\n");
+ return -EINVAL;
+ }
+
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource defined.\n");
+ return -EINVAL;
+ }
+
+ state = kzalloc(sizeof(struct tb10x_pinctrl_state) +
+ of_get_child_count(of_node)
+ * sizeof(struct tb10x_of_pinfunc),
+ GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, state);
+ state->pinfuncs = (struct tb10x_of_pinfunc *)(state + 1);
+ mutex_init(&state->mutex);
+
+ if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+ dev_err(&pdev->dev, "Request register region failed.\n");
+ ret = -EBUSY;
+ goto request_mem_fail;
+ }
+
+ state->iobase = ioremap(mem->start, resource_size(mem));
+ if (!state->iobase) {
+ dev_err(&pdev->dev, "Request register region failed.\n");
+ ret = -EBUSY;
+ goto ioremap_fail;
+ }
+
+ state->pingroups = tb10x_pingroups;
+ state->pinfuncgrpcnt = ARRAY_SIZE(tb10x_pingroups);
+
+ for (i = 0; i < TB10X_PORTS; i++)
+ state->ports[i].mode = tb10x_pinctrl_get_config(state, i);
+
+ for_each_child_of_node(of_node, child) {
+ const char *name;
+
+ if (!of_property_read_string(child, "pingrp", &name)) {
+ struct tb10x_pinfuncgrp *pfg;
+
+ pfg = tb10x_get_pinfuncgrp(name);
+
+ if (IS_ERR(pfg) || pfg->pctl) {
+ dev_err(&pdev->dev,
+ "Could not allocate pin group %s.\n",
+ name);
+ continue;
+ }
+ pfg->pctl = state;
+ state->pinfuncs[state->pinfuncnt].name = child->name;
+ state->pinfuncs[state->pinfuncnt].group = name;
+ state->pinfuncnt++;
+ }
+ }
+
+ state->pctl = pinctrl_register(&tb10x_pindesc, &pdev->dev, state);
+ if (IS_ERR(state->pctl)) {
+ dev_err(&pdev->dev, "could not register TB10x pin driver\n");
+ ret = PTR_ERR(state->pctl);
+ goto pinctrl_reg_fail;
+ }
+
+ return 0;
+
+pinctrl_reg_fail:
+ iounmap(state->iobase);
+ioremap_fail:
+ release_region(mem->start, resource_size(mem));
+request_mem_fail:
+ mutex_destroy(&state->mutex);
+ kfree(state);
+ return ret;
+}
+
+static int tb10x_pinctrl_remove(struct platform_device *pdev)
+{
+ struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct tb10x_pinctrl_state *state = platform_get_drvdata(pdev);
+ int i;
+
+ pinctrl_unregister(state->pctl);
+
+ for (i = 0; i < ARRAY_SIZE(tb10x_pingroups); i++)
+ if (tb10x_pingroups[i].pctl == state)
+ tb10x_pingroups[i].pctl = NULL;
+
+ iounmap(state->iobase);
+
+ release_region(mem->start, resource_size(mem));
+
+ mutex_destroy(&state->mutex);
+
+ kfree(state);
+
+ return 0;
+}
+
+static const struct of_device_id tb10x_pinctrl_dt_ids[] = {
+ { .compatible = TB10X_IOMUX_COMPATIBLE },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tb10x_pinctrl_dt_ids);
+
+static struct platform_driver tb10x_pinctrl_pdrv = {
+ .probe = tb10x_pinctrl_probe,
+ .remove = tb10x_pinctrl_remove,
+ .driver = {
+ .name = "tb10x_pinctrl",
+ .of_match_table = of_match_ptr(tb10x_pinctrl_dt_ids),
+ .owner = THIS_MODULE
+ }
+};
+
+static int __init tb10x_iopinctrl_init(void)
+{
+ return platform_driver_register(&tb10x_pinctrl_pdrv);
+}
+
+static void __exit tb10x_iopinctrl_exit(void)
+{
+ platform_driver_unregister(&tb10x_pinctrl_pdrv);
+}
+
+MODULE_AUTHOR("Christian Ruppert <[email protected]>");
+MODULE_LICENSE("GPL");
+module_init(tb10x_iopinctrl_init);
+module_exit(tb10x_iopinctrl_exit);
diff --git a/include/linux/pinctrl/pinctrl-tb10x.h b/include/linux/pinctrl/pinctrl-tb10x.h
new file mode 100644
index 0000000..310541b
--- /dev/null
+++ b/include/linux/pinctrl/pinctrl-tb10x.h
@@ -0,0 +1,46 @@
+/*
+ * Abilis Systems TB10x pin control driver
+ *
+ * Copyright (C) Abilis Systems 2013
+ *
+ * Author: Christian Ruppert <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PINCTRL_TB10X_H
+#define PINCTRL_TB10X_H
+
+struct tb10x_pinfuncgrp; /* Opaque structure */
+
+#ifdef CONFIG_PINCTRL_TB10X
+struct tb10x_pinfuncgrp *tb10x_prepare_gpio_range(struct device_node *dn,
+ struct pinctrl_gpio_range *gr);
+
+void tb10x_setup_gpio_range(struct tb10x_pinfuncgrp *pfg,
+ struct pinctrl_gpio_range *gr);
+#else
+/*static inline struct tb10x_pinfuncgrp *tb10x_prepare_gpio_range(
+ struct device_node *dn, struct pinctrl_gpio_range *gr)
+{
+ return -ENXIO;
+}
+
+static inline void tb10x_setup_gpio_range(struct tb10x_pinfuncgrp *pfg,
+ struct pinctrl_gpio_range *gr)
+{
+}*/
+#endif
+
+#endif
--
1.7.1


2013-04-10 15:46:07

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 2/2] GPIO: Add TB10x GPIO driver

The GPIO driver for the Abilis Systems TB10x series of SOCs based on ARC700
CPUs. It supports GPIO control and GPIO interrupt generation. This driver
works in conjunction with the TB10x pinctrl driver.

Signed-off-by: Sascha Leuenberger <[email protected]>
Signed-off-by: Christian Ruppert <[email protected]>
---
.../devicetree/bindings/gpio/abilis,tb10x-gpio.txt | 34 ++
drivers/gpio/Kconfig | 4 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-tb10x.c | 445 ++++++++++++++++++++
4 files changed, 484 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
create mode 100644 drivers/gpio/gpio-tb10x.c

diff --git a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
new file mode 100644
index 0000000..d6eb650
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
@@ -0,0 +1,34 @@
+* Abilis TB10x GPIO controller
+
+Required Properties:
+- compatible: Should be "abilis,tb10x-gpio"
+- reg: Address and length of the register set for the device
+- gpio-controller: Marks the device node as a gpio controller.
+- #gpio-cells: Should be <1>;
+- gpio-base: GPIO pin index of the first GPOI managed by this controller.
+- gpio-pins: Pointer to the pin configuration node inside an
+ "abilis,tb10x-iomux"-compatible pin controller this GPIO controller is
+ managing.
+
+Optional Properties:
+- interrupt-controller: Marks the device node as an interrupt controller.
+- #interrupt-cells: Should be <1>.
+- interrupts: Defines the interrupt line connecting this GPIO controller to
+ its parent interrupt controller.
+- interrupt-parent: Defines the parent interrupt controller.
+
+Example:
+
+gpioa: gpio@FF140000 {
+ compatible = "abilis,tb10x-gpio";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&tb10x_ictl>;
+ interrupts = <27 1>;
+ reg = <0xFF140000 0x1000>;
+ gpio-controller;
+ #gpio-cells = <1>;
+ gpio-count = <3>;
+ gpio-base = <0>;
+ gpio-pins = <&pctl_gpio_a>;
+};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 93aaadf..7357dd5 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -309,6 +309,10 @@ config GPIO_LYNXPOINT
driver for GPIO functionality on Intel Lynxpoint PCH chipset
Requires ACPI device enumeration code to set up a platform device.

+config GPIO_TB10X
+ bool
+ depends on PINCTRL_TB10X
+
comment "I2C GPIO expanders:"

config GPIO_ARIZONA
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 22e07bc..09b0cee 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o
+obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c
new file mode 100644
index 0000000..b78389b
--- /dev/null
+++ b/drivers/gpio/gpio-tb10x.c
@@ -0,0 +1,445 @@
+/* Abilis Systems MODULE DESCRIPTION
+ *
+ * Copyright (C) Abilis Systems 2013
+ *
+ * Authors: Sascha Leuenberger <[email protected]>
+ * Christian Ruppert <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/spinlock.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinctrl-tb10x.h>
+
+#define TB10X_GPIO_DIR_IN (0x00000000)
+#define TB10X_GPIO_DIR_OUT (0x00000001)
+#define OFFSET_TO_REG_DDR (0x00)
+#define OFFSET_TO_REG_DATA (0x04)
+#define OFFSET_TO_REG_INT_EN (0x08)
+#define OFFSET_TO_REG_CHANGE (0x0C)
+#define OFFSET_TO_REG_WRMASK (0x10)
+#define OFFSET_TO_REG_INT_TYPE (0x14)
+
+struct tb10x_gpio {
+ spinlock_t spinlock;
+ void __iomem *regs;
+ struct irq_domain *domain;
+ int irq;
+ struct gpio_chip gc;
+ struct pinctrl_gpio_range gr;
+};
+
+static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs)
+{
+ return ioread32(gpio->regs + offs);
+}
+
+static inline void tb10x_reg_write(struct tb10x_gpio *gpio, unsigned int offs,
+ u32 val)
+{
+ iowrite32(val, gpio->regs + offs);
+}
+
+static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs,
+ u32 mask, u32 val)
+{
+ u32 r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio->spinlock, flags);
+ r = tb10x_reg_read(gpio, offs);
+ r = (r & ~mask) | (val & mask);
+ tb10x_reg_write(gpio, offs, r);
+ spin_unlock_irqrestore(&gpio->spinlock, flags);
+}
+
+static inline struct tb10x_gpio *to_tb10x_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct tb10x_gpio, gc);
+}
+
+static int tb10x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = 1 << offset;
+ int val = TB10X_GPIO_DIR_IN << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
+
+ return 0;
+}
+
+static int tb10x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int val;
+
+ val = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_DATA);
+
+ if (val & 1 << offset)
+ return 1;
+ else
+ return 0;
+}
+
+static void tb10x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = 1 << offset;
+ int val = value << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DATA, mask, val);
+}
+
+static int tb10x_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = 1 << offset;
+ int val = TB10X_GPIO_DIR_OUT << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
+
+ return 0;
+}
+
+static int tb10x_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void tb10x_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+}
+
+static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ return irq_create_mapping(tb10x_gpio->domain, offset);
+}
+
+#ifdef CONFIG_OF_GPIO
+static int tb10x_gpio_xlate(struct gpio_chip *chip,
+ const struct of_phandle_args *gpiospec, u32 *flags)
+{
+ return gpiospec->args[0];
+}
+#endif
+
+static void tb10x_gpio_irq_ack(struct irq_data *data)
+{
+ struct tb10x_gpio *tb10x_gpio = irq_data_get_irq_chip_data(data);
+ unsigned int hwirq = irqd_to_hwirq(data);
+ tb10x_reg_write(tb10x_gpio, OFFSET_TO_REG_CHANGE, 0x1 << hwirq);
+}
+
+static void tb10x_gpio_irq_unmask(struct irq_data *data)
+{
+ struct tb10x_gpio *tb10x_gpio = irq_data_get_irq_chip_data(data);
+ unsigned int hwirq = irqd_to_hwirq(data);
+ u32 m = 0x1 << hwirq;
+ u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);
+ tb10x_reg_write(tb10x_gpio, OFFSET_TO_REG_CHANGE, m);
+ tb10x_reg_write(tb10x_gpio, OFFSET_TO_REG_INT_EN, r | m);
+}
+
+static void tb10x_gpio_irq_mask(struct irq_data *data)
+{
+ struct tb10x_gpio *tb10x_gpio = irq_data_get_irq_chip_data(data);
+ unsigned int hwirq = irqd_to_hwirq(data);
+ u32 m = 0x1 << hwirq;
+ u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);
+ tb10x_reg_write(tb10x_gpio, OFFSET_TO_REG_INT_EN, r & ~m);
+}
+
+static int tb10x_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ if ((type & IRQF_TRIGGER_MASK) != IRQ_TYPE_EDGE_BOTH) {
+ pr_err("Cannot assign multiple trigger modes to GPIO IRQ.\n");
+ return -EINVAL;
+ }
+
+ irqd_set_trigger_type(data, type);
+
+ return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip irq_tb10x_gpio_chip = {
+ .name = "TB10x-GPIO",
+ .irq_ack = tb10x_gpio_irq_ack,
+ .irq_mask = tb10x_gpio_irq_mask,
+ .irq_unmask = tb10x_gpio_irq_unmask,
+ .irq_set_type = tb10x_gpio_irq_set_type,
+};
+
+static int tb10x_gpio_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_data(irq, d->host_data);
+ irq_set_chip_and_handler(irq, &irq_tb10x_gpio_chip, handle_level_irq);
+
+ return 0;
+}
+
+static struct irq_domain_ops irq_tb10x_gpio_domain_ops = {
+ .map = tb10x_gpio_irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static irqreturn_t tb10x_gpio_irq_cascade(int irq, void *data)
+{
+ struct tb10x_gpio *tb10x_gpio = data;
+ u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_CHANGE);
+ u32 m = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);
+ int i;
+ for (i = 0; i < 32; i++)
+ if (r & m & (1L << i))
+ generic_handle_irq(irq_find_mapping(tb10x_gpio->domain,
+ i));
+ return IRQ_HANDLED;
+}
+
+static int tb10x_gpio_probe(struct platform_device *pdev)
+{
+ struct tb10x_gpio *tb10x_gpio;
+ struct resource *mem;
+ struct device_node *dn = pdev->dev.of_node;
+ int ret = -EBUSY;
+ u32 base;
+ u32 phandle;
+ struct device_node *pstate;
+ struct pinctrl *pctl;
+ struct tb10x_pinfuncgrp *pfg;
+
+ if (!dn)
+ return -EINVAL;
+
+ if (of_property_read_u32(dn, "gpio-base", &base))
+ return -EINVAL;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource defined.\n");
+ return -EINVAL;
+ }
+
+ tb10x_gpio = kzalloc(sizeof(*tb10x_gpio), GFP_KERNEL);
+ if (tb10x_gpio == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&tb10x_gpio->spinlock);
+
+ if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+ dev_err(&pdev->dev, "Request reg region failed.\n");
+ goto fail_reg_req;
+ }
+
+ tb10x_gpio->regs = ioremap(mem->start, resource_size(mem));
+ if (!tb10x_gpio->regs) {
+ dev_err(&pdev->dev, "Could not remap reg space.\n");
+ goto fail_ioremap;
+ }
+
+ tb10x_gpio->gc.label = of_node_full_name(dn);
+ tb10x_gpio->gc.dev = &pdev->dev;
+ tb10x_gpio->gc.owner = THIS_MODULE;
+ tb10x_gpio->gc.direction_input = tb10x_gpio_direction_in;
+ tb10x_gpio->gc.get = tb10x_gpio_get;
+ tb10x_gpio->gc.direction_output = tb10x_gpio_direction_out;
+ tb10x_gpio->gc.set = tb10x_gpio_set;
+ tb10x_gpio->gc.request = tb10x_gpio_request;
+ tb10x_gpio->gc.free = tb10x_gpio_free;
+#ifdef CONFIG_OF_GPIO
+ tb10x_gpio->gc.of_xlate = tb10x_gpio_xlate;
+ tb10x_gpio->gc.of_gpio_n_cells = 1;
+#endif
+
+ tb10x_gpio->gc.can_sleep = 0;
+
+ tb10x_gpio->gc.base = base;
+
+ if (of_find_property(dn, "pinctrl-0", NULL)) {
+ int idx, len;
+ const __be32 *prop;
+
+ idx = of_property_match_string(dn, "pinctrl-names",
+ PINCTRL_STATE_DEFAULT);
+ if (idx < 0) {
+ ret = idx;
+ goto fail_pinctrl_setup;
+ }
+
+ prop = of_get_property(dn, "pinctrl-0", &len);
+ if (IS_ERR(prop)) {
+ ret = PTR_ERR(prop);
+ goto fail_pinctrl_setup;
+ }
+
+ if (len <= idx) {
+ ret = -EINVAL;
+ goto fail_pinctrl_setup;
+ }
+ phandle = be32_to_cpu(prop[idx]);
+
+ pctl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pctl)) {
+ ret = PTR_ERR(pctl);
+ dev_err(&pdev->dev, "Could not set up pinctrl: %d\n",
+ ret);
+ goto fail_pinctrl_setup;
+ }
+ } else {
+ ret = of_property_read_u32(dn, "gpio-pins", &phandle);
+ if (ret)
+ goto fail_pinctrl_setup;
+ }
+
+ pstate = of_find_node_by_phandle(phandle);
+ if (!pstate) {
+ ret = -EINVAL;
+ goto fail_pinctrl_setup;
+ }
+ of_node_put(pstate);
+
+ pfg = tb10x_prepare_gpio_range(pstate, &tb10x_gpio->gr);
+ if (IS_ERR(pfg)) {
+ ret = PTR_ERR(pfg);
+ goto fail_pinctrl_setup;
+ }
+
+ tb10x_gpio->gc.ngpio = tb10x_gpio->gr.npins;
+
+ tb10x_gpio->gr.name = of_node_full_name(dn);
+ tb10x_gpio->gr.id = 0;
+ tb10x_gpio->gr.base = base;
+ tb10x_gpio->gr.gc = &tb10x_gpio->gc;
+
+ ret = gpiochip_add(&tb10x_gpio->gc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not add gpiochip.\n");
+ goto fail_gpiochip_registration;
+ }
+
+ tb10x_setup_gpio_range(pfg, &tb10x_gpio->gr);
+
+ platform_set_drvdata(pdev, tb10x_gpio);
+
+ if (of_find_property(dn, "interrupt-controller", NULL)) {
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "No interrupt specified.\n");
+ goto fail_get_irq;
+ }
+ tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
+ tb10x_gpio->irq = ret;
+ ret = request_irq(ret, tb10x_gpio_irq_cascade,
+ IRQF_TRIGGER_FALLING | IRQF_SHARED,
+ dev_name(&pdev->dev), tb10x_gpio);
+ if (ret != 0)
+ goto fail_request_irq;
+
+ tb10x_gpio->domain = irq_domain_add_linear(dn,
+ tb10x_gpio->gr.npins,
+ &irq_tb10x_gpio_domain_ops,
+ tb10x_gpio);
+ if (!tb10x_gpio->domain) {
+ ret = -ENOMEM;
+ goto fail_irq_domain;
+ }
+ }
+
+ return 0;
+
+fail_irq_domain:
+ free_irq(tb10x_gpio->irq, tb10x_gpio);
+fail_request_irq:
+fail_get_irq:
+ ret = gpiochip_remove(&tb10x_gpio->gc);
+fail_gpiochip_registration:
+fail_pinctrl_setup:
+ iounmap(tb10x_gpio->regs);
+fail_ioremap:
+ release_mem_region(mem->start, resource_size(mem));
+fail_reg_req:
+ kfree(tb10x_gpio);
+
+ return ret;
+}
+
+static int __exit tb10x_gpio_remove(struct platform_device *pdev)
+{
+ struct tb10x_gpio *tb10x_gpio = platform_get_drvdata(pdev);
+ struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int ret;
+
+ if (tb10x_gpio->gc.to_irq) {
+ irq_domain_remove(tb10x_gpio->domain);
+ free_irq(tb10x_gpio->irq, tb10x_gpio);
+ }
+ ret = gpiochip_remove(&tb10x_gpio->gc);
+ if (ret)
+ return ret;
+
+ iounmap(tb10x_gpio->regs);
+ release_mem_region(mem->start, resource_size(mem));
+ kfree(tb10x_gpio);
+
+ return 0;
+}
+
+static const struct of_device_id tb10x_gpio_dt_ids[] = {
+ { .compatible = "abilis,tb10x-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tb10x_gpio_dt_ids);
+
+static struct platform_driver tb10x_gpio_driver = {
+ .probe = tb10x_gpio_probe,
+ .remove = tb10x_gpio_remove,
+ .driver = {
+ .name = "tb10x-gpio",
+ .of_match_table = of_match_ptr(tb10x_gpio_dt_ids),
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init ab_gpio_init(void)
+{
+ return platform_driver_register(&tb10x_gpio_driver);
+}
+
+static void __exit ab_gpio_exit(void)
+{
+ platform_driver_unregister(&tb10x_gpio_driver);
+}
+
+module_init(ab_gpio_init);
+module_exit(ab_gpio_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("tb10x gpio.");
+MODULE_VERSION("0.0.1");
--
1.7.1

2013-04-17 14:48:58

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Wed, Apr 10, 2013 at 5:45 PM, Christian Ruppert
<[email protected]> wrote:

> The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
> Used to control the pinmux and is a prerequisite for the GPIO driver.
>
> Signed-off-by: Christian Ruppert <[email protected]>
> Signed-off-by: Pierrick Hascoet <[email protected]>

Please include Stephen Warren on cc for checking DT bindings for these
things, I feel a bit uncertain about such things time to time.

(...)
> +++ b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
(...)
> +In case a driver requires a port to be mapped, the corresponding node should
> +be compatible with "abilis,simple-pinctrl" and define a pinctrl state named
> +"abilis,simple-default" pointing to the port's respective node inside the pin
> +controller.

Oh um.... I don't understand this.

> + Drivers managing pin controller states internally can be
> +configured normally as described in the pinctrl-bindings.txt.

I don't understand this either.

Do you mean this is opposed to using hogs or something?

Have your familiarized yourself with commit
ab78029ecc347debbd737f06688d788bd9d60c1d
"drivers/pinctrl: grab default handles from device core"
?

> +The pinmux driver is connected to the TB10x GPIO driver in a way that a GPIO
> +node managing a GPIO port can point to the respective pinmux subnode using
> +the gpio-pins property

NAK - this sounds like you're reinventing the GPIO ranges.

See:
Documentation/devicetree/bindings/gpio/gpio.txt, section
2.1) gpio-controller and pinctrl subsystem


> +Example
> +-------
> +
> +iomux: iomux@FF10601c {
> + #address-cells = <1>;
> + #size-cells = <1>;
> + compatible = "abilis,tb10x-iomux";
> + reg = <0xFF10601c 0x4>;
> + pctl_gpio_a: pctl-gpio-a {
> + pingrp = "gpioa_pins";
> + };
> + pctl_uart0: pctl-uart0 {
> + pingrp = "uart0_pins";
> + };
> +};
> +uart@FF100000 {
> + compatible = "snps,dw-apb-uart",
> + "abilis,simple-pinctrl";

Why do you need this compatible property,
abilis,simple-pinctrl? And what is simple about it?

> + reg = <0xFF100000 0x100>;
> + clock-frequency = <166666666>;
> + interrupts = <25 1>;
> + reg-shift = <2>;
> + reg-io-width = <4>;
> + pinctrl-names = "abilis,simple-default"; /* <<<<<<<< */
> + pinctrl-0 = <&pctl_uart0>; /* <<<<<<<< */

I suggest you just put the name of the states into pinctrl-names
pinctrl-names = "default";

> +gpioa: gpio@FF140000 {
> + compatible = "abilis,tb10x-gpio";
> + reg = <0xFF140000 0x1000>;
> + gpio-controller;
> + #gpio-cells = <1>;
> + gpio-count = <3>;
> + gpio-base = <0>;
> + gpio-pins = <&pctl_gpio_a>; /* <<<<<<<< */

And why doesn't this need a name?

(...)
> diff --git a/drivers/pinctrl/pinctrl-tb10x.c b/drivers/pinctrl/pinctrl-tb10x.c

> +#include <linux/stringify.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/machine.h>
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/slab.h>
> +#include <linux/pinctrl/pinctrl-tb10x.h>

No, put that into <linux/platform_data/pinctrl-tb10x.h>

> +static const struct pinctrl_pin_desc tb10x_pins[] = {
> + /* Port 1 */
> + PINCTRL_PIN(0 + 0, "MICLK_S0"),
> + PINCTRL_PIN(0 + 1, "MISTRT_S0"),
> + PINCTRL_PIN(0 + 2, "MIVAL_S0"),
> + PINCTRL_PIN(0 + 3, "MDI_S0"),
> + PINCTRL_PIN(0 + 4, "GPIOA0"),
> + PINCTRL_PIN(0 + 5, "GPIOA1"),
> + PINCTRL_PIN(0 + 6, "GPIOA2"),
> + PINCTRL_PIN(0 + 7, "MDI_S1"),
> + PINCTRL_PIN(0 + 8, "MIVAL_S1"),
> + PINCTRL_PIN(0 + 9, "MISTRT_S1"),
> + PINCTRL_PIN(0 + 10, "MICLK_S1"),

If you're using an offset like that in every such macro,
use a #define for the offset, like


#define TB10X_PORT0 0
#define TB10X_PORT1 16

Then

PINCTRL_PIN(TB10X_PORT0 + 0, "MICLK_S0"),

start to mean something when reading it.

(...)
> +/* Port 1 */
> +static const unsigned mis0_pins[] = { 0, 1, 2, 3};
> +static const unsigned gpioa_pins[] = { 4, 5, 6};
> +static const unsigned mis1_pins[] = { 7, 8, 9, 10};
> +static const unsigned mip1_pins[] = { 0, 1, 2, 3, 4, 5,
> + 6, 7, 8, 9, 10};

Likewise you should use the same #defines here I guess.

static const unsigned mis0_pins[] = { TB10X_PORT0 + 0, TB10X_PORT0 + 1,
TB10X_PORT0 +
2, TB10X_PORT0 + 3};

An alternative is to just drop the offset everywhere above, but it needs
to be consistent. Do it one way: either offsets everywhere or just
plain numbers everywhere, not a mixture like now.

(...)
> +struct tb10x_pinctrl_state {

Usually I just call such structs "tb10x", it's quite clear
from the code that it stores the state.

> + struct pinctrl_dev *pctl;
> + void *iobase;

Just "base" is more common.

> + const struct tb10x_pinfuncgrp *pingroups;
> + unsigned int pinfuncgrpcnt;
> + struct tb10x_of_pinfunc *pinfuncs;
> + unsigned int pinfuncnt;
> + struct mutex mutex;
> + struct {
> + unsigned int mode;
> + unsigned int count;
> + } ports[TB10X_PORTS];

Why is this struct anonymous? Can't you just declare it outside this struct?

> + DECLARE_BITMAP(gpios, MAX_PIN + 1);
> +};

This struct needs kerneldoc added to it so we understand each member.

> +struct tb10x_pinfuncgrp *tb10x_prepare_gpio_range(struct device_node *dn,
> + struct pinctrl_gpio_range *gr)
> +{
> + const char *name;
> + int ret;
> + struct tb10x_pinfuncgrp *pfg;
> +
> + ret = of_property_read_string(dn, "pingrp", &name);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + pfg = tb10x_get_pinfuncgrp(name);
> +
> + if (!IS_ERR(pfg)) {
> + if (!pfg->isgpio)
> + return ERR_PTR(-EINVAL);
> +
> + if (!pfg->pctl)
> + return ERR_PTR(-EPROBE_DEFER);
> +
> + gr->pin_base = pfg->pins[0];
> + gr->npins = pfg->pincnt;
> + }
> +
> + return pfg;
> +}
> +EXPORT_SYMBOL(tb10x_prepare_gpio_range);

No thanks, use the ranges we have already defined and implemented
in drivers/gpio/gpiolib-of.c, function of_gpiochip_add_pin_range

And you need none of the funky special way to register ranges.

> +static inline void tb10x_pinctrl_set_config(struct tb10x_pinctrl_state *state,
> + unsigned int port, unsigned int mode)
> +{
> + u32 pcfg;
> +
> + if (state->ports[port].count)
> + return;
> +
> + state->ports[port].mode = mode;
> +
> + pcfg = ioread32(state->iobase) & ~(0x3 << (2 * port));
> + iowrite32(pcfg | ((mode & 0x3) << (2 * port)), state->iobase);
> +}

What is 0x3 above? 2*port? Please use #define for the
magic constant 3 and explain with a comment why you do *=2.

(...)
> +static int tb10x_gpio_request_enable(struct pinctrl_dev *pctl,
> + struct pinctrl_gpio_range *range,
> + unsigned pin)
> +{
> + struct tb10x_pinctrl_state *state = pinctrl_dev_get_drvdata(pctl);
> + int muxport = -1;
> + int muxmode = -1;
> + int i;
> +
> + mutex_lock(&state->mutex);
> +
> + for (i = 0; i < ARRAY_SIZE(tb10x_pingroups); i++) {
> + struct tb10x_pinfuncgrp *pfg = &tb10x_pingroups[i];
> + unsigned int port = pfg->port;
> + unsigned int mode = pfg->mode;
> + int j;
> +
> + if (port < 0)
> + continue;
> +
> + if (pfg->isgpio) {
> + muxport = port;
> + muxmode = mode;
> + continue;
> + }
> +
> + for (j = 0; j < pfg->pincnt; j++) {
> + if (pin == pfg->pins[j]) {
> + if (state->ports[port].count
> + && (state->ports[port].mode == mode)) {
> + mutex_unlock(&state->mutex);
> + return -EBUSY;
> + }
> + break;
> + }
> + }
> + }

This looks complicated.

I suggest trying to use:

struct pinctrl_gpio_range *range =
pinctrl_find_gpio_range_from_pin(pctldev, pin);

Then use the range to figure out which GPIO to hit.

(...)
> +static void tb10x_pctl_disable(struct pinctrl_dev *pctl,
> + unsigned func_selector, unsigned group_selector)
> +{
> + struct tb10x_pinctrl_state *state = pinctrl_dev_get_drvdata(pctl);
> + const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector];
> +
> + if (grp->port < 0)
> + return;
> +
> + mutex_lock(&state->mutex);
> +
> + state->ports[grp->port].count--;
> +
> + mutex_unlock(&state->mutex);
> +}


Keeping refcounts like that looks complicated.

> +static int tb10x_pinctrl_probe(struct platform_device *pdev)
> +{
> + int ret = -EINVAL;
> + struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + struct device_node *of_node = pdev->dev.of_node;
> + struct device_node *child;
> + struct tb10x_pinctrl_state *state;
> + int i;
> +
> + if (!of_node) {
> + dev_err(&pdev->dev, "No device tree node found.\n");
> + return -EINVAL;
> + }
> +
> + if (!mem) {
> + dev_err(&pdev->dev, "No memory resource defined.\n");
> + return -EINVAL;
> + }
> +
> + state = kzalloc(sizeof(struct tb10x_pinctrl_state) +

Use devm_kzalloc().

> + of_get_child_count(of_node)
> + * sizeof(struct tb10x_of_pinfunc),
> + GFP_KERNEL);
> + if (!state)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, state);
> + state->pinfuncs = (struct tb10x_of_pinfunc *)(state + 1);
> + mutex_init(&state->mutex);
> +
> + if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {

Use devm_ioremap_resource().

> + dev_err(&pdev->dev, "Request register region failed.\n");
> + ret = -EBUSY;
> + goto request_mem_fail;
> + }
> +
> + state->iobase = ioremap(mem->start, resource_size(mem));
> + if (!state->iobase) {
> + dev_err(&pdev->dev, "Request register region failed.\n");
> + ret = -EBUSY;
> + goto ioremap_fail;
> + }

And that will take care of this too.

> + state->pingroups = tb10x_pingroups;
> + state->pinfuncgrpcnt = ARRAY_SIZE(tb10x_pingroups);

Basically I don't like this because it looks like your driver is trying
to duplicate the job of the core: to keep track of groups and their
counts using the callback methods.

> + state->pctl = pinctrl_register(&tb10x_pindesc, &pdev->dev, state);
> + if (IS_ERR(state->pctl)) {
> + dev_err(&pdev->dev, "could not register TB10x pin driver\n");
> + ret = PTR_ERR(state->pctl);
> + goto pinctrl_reg_fail;
> + }
> +
> + return 0;
> +
> +pinctrl_reg_fail:
> + iounmap(state->iobase);
> +ioremap_fail:
> + release_region(mem->start, resource_size(mem));
> +request_mem_fail:
> + mutex_destroy(&state->mutex);
> + kfree(state);
> + return ret;


With devm_* allocators all of this goes away and you can
just return -ERRCODE; wherever there is a problem.

> +}
> +
> +static int tb10x_pinctrl_remove(struct platform_device *pdev)
> +{
> + struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + struct tb10x_pinctrl_state *state = platform_get_drvdata(pdev);
> + int i;
> +
> + pinctrl_unregister(state->pctl);
> +
> + for (i = 0; i < ARRAY_SIZE(tb10x_pingroups); i++)
> + if (tb10x_pingroups[i].pctl == state)
> + tb10x_pingroups[i].pctl = NULL;

I don't understand what this cleanup is doing.

> +
> + iounmap(state->iobase);
> +
> + release_region(mem->start, resource_size(mem));
> +
> + mutex_destroy(&state->mutex);
> +
> + kfree(state);

The above goes away with devm_* allocators.

> +static const struct of_device_id tb10x_pinctrl_dt_ids[] = {
> + { .compatible = TB10X_IOMUX_COMPATIBLE },

Can you just inline the compat string here instead of using a #define?

Yours,
Linus Walleij

2013-04-17 15:13:22

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 2/2] GPIO: Add TB10x GPIO driver

On Wed, Apr 10, 2013 at 5:45 PM, Christian Ruppert
<[email protected]> wrote:

> The GPIO driver for the Abilis Systems TB10x series of SOCs based on ARC700
> CPUs. It supports GPIO control and GPIO interrupt generation. This driver
> works in conjunction with the TB10x pinctrl driver.
>
> Signed-off-by: Sascha Leuenberger <[email protected]>
> Signed-off-by: Christian Ruppert <[email protected]>

(...)
> +++ b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
> @@ -0,0 +1,34 @@
> +* Abilis TB10x GPIO controller
> +
> +Required Properties:
> +- compatible: Should be "abilis,tb10x-gpio"
> +- reg: Address and length of the register set for the device
> +- gpio-controller: Marks the device node as a gpio controller.
> +- #gpio-cells: Should be <1>;
> +- gpio-base: GPIO pin index of the first GPOI managed by this controller.

No, this is a Linux-specific number, it shall not be encoded into the
device tree at all. (Search the archive for previous discussions on this.)

If you use standard GPIO ranges this will fix itself, and you'll see how
nicely it all fits together. You do not need to know the global numbers
at all.

> +- gpio-pins: Pointer to the pin configuration node inside an
> + "abilis,tb10x-iomux"-compatible pin controller this GPIO controller is
> + managing.

No thanks, use the existing GPIO ranges and it will be much easier.

(...)
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 93aaadf..7357dd5 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -309,6 +309,10 @@ config GPIO_LYNXPOINT
> driver for GPIO functionality on Intel Lynxpoint PCH chipset
> Requires ACPI device enumeration code to set up a platform device.
>
> +config GPIO_TB10X
> + bool
> + depends on PINCTRL_TB10X

Can't you just select it instead?

You probably want to select OF_GPIO too.

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

> +#define TB10X_GPIO_DIR_IN (0x00000000)
> +#define TB10X_GPIO_DIR_OUT (0x00000001)
> +#define OFFSET_TO_REG_DDR (0x00)
> +#define OFFSET_TO_REG_DATA (0x04)
> +#define OFFSET_TO_REG_INT_EN (0x08)
> +#define OFFSET_TO_REG_CHANGE (0x0C)
> +#define OFFSET_TO_REG_WRMASK (0x10)
> +#define OFFSET_TO_REG_INT_TYPE (0x14)
> +
> +struct tb10x_gpio {
> + spinlock_t spinlock;
> + void __iomem *regs;

If the other driver calls this "base" maybe you can
use the same name here?

> + struct irq_domain *domain;
> + int irq;
> + struct gpio_chip gc;
> + struct pinctrl_gpio_range gr;
> +};

Please add some brief kerneldoc to this struct.

(...)
> +static int tb10x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
> +{
> + struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
> + int mask = 1 << offset;

Everywhere you have code like that, please do it like this:

#include <linux/bitops.h>

int mask = BIT(offset);

> + int val = TB10X_GPIO_DIR_IN << offset;
> +
> + tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
> +
> + return 0;
> +}

(...)
> +static int tb10x_gpio_request(struct gpio_chip *chip, unsigned offset)
> +{
> + return pinctrl_request_gpio(chip->base + offset);
> +}
> +
> +static void tb10x_gpio_free(struct gpio_chip *chip, unsigned offset)
> +{
> + pinctrl_free_gpio(chip->base + offset);
> +}

This is good!

> +static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
> +{
> + struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);

Newline here

> + return irq_create_mapping(tb10x_gpio->domain, offset);
> +}

Very nice with this irqdomain and all.

> +#ifdef CONFIG_OF_GPIO
> +static int tb10x_gpio_xlate(struct gpio_chip *chip,
> + const struct of_phandle_args *gpiospec, u32 *flags)
> +{
> + return gpiospec->args[0];
> +}
> +#endif

Why do you need to #ifdef this?

Just select OF_GPIO in the Kconfig.

> +static void tb10x_gpio_irq_ack(struct irq_data *data)
> +{
> + struct tb10x_gpio *tb10x_gpio = irq_data_get_irq_chip_data(data);
> + unsigned int hwirq = irqd_to_hwirq(data);

Newline.

> + tb10x_reg_write(tb10x_gpio, OFFSET_TO_REG_CHANGE, 0x1 << hwirq);
> +}
> +
> +static void tb10x_gpio_irq_unmask(struct irq_data *data)
> +{
> + struct tb10x_gpio *tb10x_gpio = irq_data_get_irq_chip_data(data);
> + unsigned int hwirq = irqd_to_hwirq(data);
> + u32 m = 0x1 << hwirq;
> + u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);

Newline.

> + tb10x_reg_write(tb10x_gpio, OFFSET_TO_REG_CHANGE, m);
> + tb10x_reg_write(tb10x_gpio, OFFSET_TO_REG_INT_EN, r | m);
> +}
> +
> +static void tb10x_gpio_irq_mask(struct irq_data *data)
> +{
> + struct tb10x_gpio *tb10x_gpio = irq_data_get_irq_chip_data(data);
> + unsigned int hwirq = irqd_to_hwirq(data);
> + u32 m = 0x1 << hwirq;

Here too: u32 m = BIT(hwirq);

> + u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);

Newline.

> + tb10x_reg_write(tb10x_gpio, OFFSET_TO_REG_INT_EN, r & ~m);
> +}

> +static int tb10x_gpio_irq_set_type(struct irq_data *data, unsigned int type)
> +{
> + if ((type & IRQF_TRIGGER_MASK) != IRQ_TYPE_EDGE_BOTH) {
> + pr_err("Cannot assign multiple trigger modes to GPIO IRQ.\n");

But... the error message says the opposite of what the if() expression
is testing...? You test that both edges are requested.

(...)
> +static irqreturn_t tb10x_gpio_irq_cascade(int irq, void *data)
> +{
> + struct tb10x_gpio *tb10x_gpio = data;
> + u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_CHANGE);
> + u32 m = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);
> + int i;
> + for (i = 0; i < 32; i++)

Use something like:

int offset;
for_each_set_bit(offset, &m, 32) {}...

> + if (r & m & (1L << i))
> + generic_handle_irq(irq_find_mapping(tb10x_gpio->domain,
> + i));
> + return IRQ_HANDLED;
> +}

(...)
> +static int tb10x_gpio_probe(struct platform_device *pdev)
> +{
> + struct tb10x_gpio *tb10x_gpio;
> + struct resource *mem;
> + struct device_node *dn = pdev->dev.of_node;
> + int ret = -EBUSY;
> + u32 base;
> + u32 phandle;
> + struct device_node *pstate;
> + struct pinctrl *pctl;
> + struct tb10x_pinfuncgrp *pfg;
> +
> + if (!dn)
> + return -EINVAL;
> +
> + if (of_property_read_u32(dn, "gpio-base", &base))
> + return -EINVAL;

Remove, as explained above.

> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem) {
> + dev_err(&pdev->dev, "No memory resource defined.\n");
> + return -EINVAL;
> + }
> +
> + tb10x_gpio = kzalloc(sizeof(*tb10x_gpio), GFP_KERNEL);

Use devm_kzalloc().

> + if (tb10x_gpio == NULL)
> + return -ENOMEM;
> +
> + spin_lock_init(&tb10x_gpio->spinlock);
> +
> + if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
> + dev_err(&pdev->dev, "Request reg region failed.\n");
> + goto fail_reg_req;
> + }
> +
> + tb10x_gpio->regs = ioremap(mem->start, resource_size(mem));
> + if (!tb10x_gpio->regs) {
> + dev_err(&pdev->dev, "Could not remap reg space.\n");
> + goto fail_ioremap;
> + }

Replace with devm_ioremap_resource().

> + tb10x_gpio->gc.label = of_node_full_name(dn);
> + tb10x_gpio->gc.dev = &pdev->dev;
> + tb10x_gpio->gc.owner = THIS_MODULE;
> + tb10x_gpio->gc.direction_input = tb10x_gpio_direction_in;
> + tb10x_gpio->gc.get = tb10x_gpio_get;
> + tb10x_gpio->gc.direction_output = tb10x_gpio_direction_out;
> + tb10x_gpio->gc.set = tb10x_gpio_set;
> + tb10x_gpio->gc.request = tb10x_gpio_request;
> + tb10x_gpio->gc.free = tb10x_gpio_free;
> +#ifdef CONFIG_OF_GPIO
> + tb10x_gpio->gc.of_xlate = tb10x_gpio_xlate;
> + tb10x_gpio->gc.of_gpio_n_cells = 1;
> +#endif

select OF_GPIO in Kconfig and skip #ifdef:s.

> +
> + tb10x_gpio->gc.can_sleep = 0;
> +
> + tb10x_gpio->gc.base = base;
> +
> + if (of_find_property(dn, "pinctrl-0", NULL)) {
> + int idx, len;
> + const __be32 *prop;
> +
> + idx = of_property_match_string(dn, "pinctrl-names",
> + PINCTRL_STATE_DEFAULT);
> + if (idx < 0) {
> + ret = idx;
> + goto fail_pinctrl_setup;
> + }
> +
> + prop = of_get_property(dn, "pinctrl-0", &len);
> + if (IS_ERR(prop)) {
> + ret = PTR_ERR(prop);
> + goto fail_pinctrl_setup;
> + }
> +
> + if (len <= idx) {
> + ret = -EINVAL;
> + goto fail_pinctrl_setup;
> + }
> + phandle = be32_to_cpu(prop[idx]);
> +
> + pctl = devm_pinctrl_get_select_default(&pdev->dev);
> + if (IS_ERR(pctl)) {
> + ret = PTR_ERR(pctl);
> + dev_err(&pdev->dev, "Could not set up pinctrl: %d\n",
> + ret);
> + goto fail_pinctrl_setup;
> + }
> + } else {
> + ret = of_property_read_u32(dn, "gpio-pins", &phandle);
> + if (ret)
> + goto fail_pinctrl_setup;
> + }

No, this looks like a mishmash of pinctrl and GPIO responsibilities.

This kind of stuff should be done in the pinctrl driver, and it seems
redundant too, you are likely looking for exactly what commit
ab78029ecc347debbd737f06688d788bd9d60c1d
"drivers/pinctrl: grab default handles from device core"
is already doing for each and every driver if you just name your
states "default" instead.

> + pstate = of_find_node_by_phandle(phandle);
> + if (!pstate) {
> + ret = -EINVAL;
> + goto fail_pinctrl_setup;
> + }
> + of_node_put(pstate);
> +
> + pfg = tb10x_prepare_gpio_range(pstate, &tb10x_gpio->gr);
> + if (IS_ERR(pfg)) {
> + ret = PTR_ERR(pfg);
> + goto fail_pinctrl_setup;
> + }

Again use the standard OF range mapping in the core gpiolib.

No cross-calling to the pinctrl driver please.

None of this code should be needed.

> + ret = gpiochip_add(&tb10x_gpio->gc);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Could not add gpiochip.\n");
> + goto fail_gpiochip_registration;
> + }
> +
> + tb10x_setup_gpio_range(pfg, &tb10x_gpio->gr);

Again you either use the gpiolib OF core range creation.

> + if (of_find_property(dn, "interrupt-controller", NULL)) {
> + ret = platform_get_irq(pdev, 0);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "No interrupt specified.\n");
> + goto fail_get_irq;
> + }
> + tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
> + tb10x_gpio->irq = ret;
> + ret = request_irq(ret, tb10x_gpio_irq_cascade,

Use devm_request_irq().

> + IRQF_TRIGGER_FALLING | IRQF_SHARED,
> + dev_name(&pdev->dev), tb10x_gpio);
> + if (ret != 0)
> + goto fail_request_irq;
> +
> + tb10x_gpio->domain = irq_domain_add_linear(dn,
> + tb10x_gpio->gr.npins,
> + &irq_tb10x_gpio_domain_ops,
> + tb10x_gpio);
> + if (!tb10x_gpio->domain) {
> + ret = -ENOMEM;
> + goto fail_irq_domain;
> + }
> + }

This part looks nice!

> +fail_irq_domain:
> + free_irq(tb10x_gpio->irq, tb10x_gpio);
> +fail_request_irq:
> +fail_get_irq:
> + ret = gpiochip_remove(&tb10x_gpio->gc);
> +fail_gpiochip_registration:
> +fail_pinctrl_setup:
> + iounmap(tb10x_gpio->regs);
> +fail_ioremap:
> + release_mem_region(mem->start, resource_size(mem));
> +fail_reg_req:
> + kfree(tb10x_gpio);

Cuts down cleanups after introducing devm_* managed resources.

> +static int __exit tb10x_gpio_remove(struct platform_device *pdev)
> +{
> + struct tb10x_gpio *tb10x_gpio = platform_get_drvdata(pdev);
> + struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + int ret;
> +
> + if (tb10x_gpio->gc.to_irq) {
> + irq_domain_remove(tb10x_gpio->domain);
> + free_irq(tb10x_gpio->irq, tb10x_gpio);
> + }
> + ret = gpiochip_remove(&tb10x_gpio->gc);
> + if (ret)
> + return ret;
> +
> + iounmap(tb10x_gpio->regs);
> + release_mem_region(mem->start, resource_size(mem));
> + kfree(tb10x_gpio);

Dito.

> +
> + return 0;
> +}
> +
> +static const struct of_device_id tb10x_gpio_dt_ids[] = {
> + { .compatible = "abilis,tb10x-gpio" },

Hm here you're not using a #define for the compatible string, nice!

Yours,
Linus Walleij

2013-04-17 18:32:06

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 04/10/2013 09:45 AM, Christian Ruppert wrote:
> The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
> Used to control the pinmux and is a prerequisite for the GPIO driver.

Linus already did a review of this, but I have a few extra comments:

> diff --git a/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt

> +Abilis Systems TB10x pin controller
> +===================================
> +
> +Required properties
> +-------------------
> +
> +- #address-cells: should be <1>.
> +- #size-cells: should be <1>;

What are those two used for? They would normally only be required if the
child nodes of the pinctrl node contained "reg" properties. But, they
don't in the examples later in this file.

> +Ports are defined (and referenced) by sub-nodes of the pin controller. Every
> +sub-node defines exactly one port (i.e. a set of pins). Ports are defined by
> +their names in the pin controller driver.

I'm not really sure what the implication here is. Does this mean that
the driver isn't expected to contain tables which define which
pins/groups/functions exist, but rather gets those lists/tables from the
DT? I prefer not to do that, although it is acceptable to write the
binding/driver this way.


So there seems to be something huge missing here; the only property
defined here for the child nodes is "pingrp". In the examples, there's
nothing else used. I don't see how this binding allows the actual
desired mux functions to be specified. All other pinctrl DT bindings
have the child nodes contain something along the lines of:

* A list of pins/groups to apply the settings to.
* A property defining the mux function to select on those pins/groups.
* Other properties defining configuration for those pins/groups, such as
pull-up/down, drive-type, drive-strength, etc.

All that seems missing here. Surely it's required?

Perhaps this "abilis,simple-default" thing is intended to represent some
specific default configuration? If so, I don't think that's the right
way to go. Also, the DT binding should be as complete as possible from
the start, rather than planning to define/implement part of it now and
then keep adding to it later. This all implies that some extra
properties really should be defined here.

2013-04-17 18:37:41

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/2] GPIO: Add TB10x GPIO driver

On 04/10/2013 09:45 AM, Christian Ruppert wrote:
> The GPIO driver for the Abilis Systems TB10x series of SOCs based on ARC700
> CPUs. It supports GPIO control and GPIO interrupt generation. This driver
> works in conjunction with the TB10x pinctrl driver.

I have a couple comments beyond what Linus already said,

> diff --git a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt

> +Required Properties:
> +- compatible: Should be "abilis,tb10x-gpio"
> +- reg: Address and length of the register set for the device
> +- gpio-controller: Marks the device node as a gpio controller.
> +- #gpio-cells: Should be <1>;

I think this value should be at least two. The extra cell should be used
to represent flags. Bit 0 of that cell is customarily used to represent
"active low", which is information for SW to use to interpret the port,
so it doesn't matter if your HW supports that or not. See for example:

Documentation/devicetree/bindings/gpio/nvidia,tegra20-gpio.txt

- #gpio-cells : Should be two. The first cell is the pin number and the
second cell is used to specify optional parameters:
- bit 0 specifies polarity (0 for normal, 1 for inverted)

> +Optional Properties:
> +- interrupt-controller: Marks the device node as an interrupt controller.
> +- #interrupt-cells: Should be <1>.

Equally here, I imagine you want 2 here not 1, so that IRQ flags can be
defined. For the customary values in the second cell, see:
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt's
"two cells" description. Of course, if your HW can't configure this,
then ignore this comment.

> +Example:
> +
> +gpioa: gpio@FF140000 {
> + compatible = "abilis,tb10x-gpio";
> + interrupt-controller;
> + #interrupt-cells = <1>;
> + interrupt-parent = <&tb10x_ictl>;
> + interrupts = <27 1>;
> + reg = <0xFF140000 0x1000>;
> + gpio-controller;
> + #gpio-cells = <1>;
> + gpio-count = <3>;

The gpio-count property isn't defined above.

> + gpio-base = <0>;
> + gpio-pins = <&pctl_gpio_a>;
> +};

Finally, Documentation/devicetree/bindings/vendor-prefixes.txt should be
updated to define the abilis vendor prefix.

2013-04-18 09:03:46

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

Dear Stephen and Linus,

I am responding to this message because it touches the core issue I was
wondering about when integrating pinctrl with GPIO. I think all
unrelated comments in your other messages are valid and will be
addressed in the next iteration of both drivers.

On Wed, Apr 17, 2013 at 12:32:00PM -0600, Stephen Warren wrote:
> On 04/10/2013 09:45 AM, Christian Ruppert wrote:
> > The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
> > Used to control the pinmux and is a prerequisite for the GPIO driver.
>
> Linus already did a review of this, but I have a few extra comments:
>
> > diff --git a/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
>
> > +Abilis Systems TB10x pin controller
> > +===================================
> > +
> > +Required properties
> > +-------------------
> > +
> > +- #address-cells: should be <1>.
> > +- #size-cells: should be <1>;
>
> What are those two used for? They would normally only be required if the
> child nodes of the pinctrl node contained "reg" properties. But, they
> don't in the examples later in this file.

Agree, this seems to be a mistake. The properties will be removed.

> > +Ports are defined (and referenced) by sub-nodes of the pin controller. Every
> > +sub-node defines exactly one port (i.e. a set of pins). Ports are defined by
> > +their names in the pin controller driver.
>
> I'm not really sure what the implication here is. Does this mean that
> the driver isn't expected to contain tables which define which
> pins/groups/functions exist, but rather gets those lists/tables from the
> DT? I prefer not to do that, although it is acceptable to write the
> binding/driver this way.

We have the entire pin data base implemented in the pinctrl driver. This
includes if a pin can or cannot be used as GPIO and which range it
belongs to. Making this information redundant in both GPIO and pinctrl
drivers is a bad idea IMHO. Thus, the pin controller's sub nodes are
merely defined to have phandles so that we can use pinctrl core
functionality. The reasoning is the following:

We would like to avoid the use of Linux pin numbers in the device tree.
Customers are used to physical pin numbers and exposing the logical
Linux-internal numbering scheme through the device tree would generate
quite some confusion. There are two reasons why we cannot align the two:
- Not all products supported by these drivers have the same pin outs.
- GPIO ranges must be consecutive in the Linux pin numbering space
which is generally not the case in physical pin numbering.

If I understand Documentation/devicetree/bindings/gpio/gpio.txt
correctly, the "standard" gpio-ranges definition does use Linux logical
pin numbers, however. This disqualifies gpio-ranges from being used in
our device tree. I haven't found a clean way around this dilemma, thus
the current implementation duplicating parts of the core system and
cross-calling pinctrl from GPIO.

I am well aware of the problem but I haven't found any documentation,
core functions or examples which solve this. The most elegant way would
be some core functionality allowing to define GPIO ranges by directly
querying the pin data base (or to preregister GPIO ranges in the pin
controller to which GPIO drivers can then "attach"). Is there something
like this I have missed?

As a side note, the same argument does not apply to gpio-base, btw, for
two reasons:
- Our customer documentation does not talk about a global GPIO
numbering scheme. In our products, GPIOs are organised in banks and
there is no risk of confusion.
- The Linux-internal GPIO numbering is already exposed to customers
through the /sys/class/gpio interface.
That said, if we solve the cross-calling issue I can still move it to
the pin data base in the next patch set since you are right that there
is no real reason to make it "user-configurable" through DT.

> So there seems to be something huge missing here; the only property
> defined here for the child nodes is "pingrp". In the examples, there's
> nothing else used. I don't see how this binding allows the actual
> desired mux functions to be specified. All other pinctrl DT bindings
> have the child nodes contain something along the lines of:
>
> * A list of pins/groups to apply the settings to.

This is what the pingrp string refers to. We really don't want to expose
Linux internal pin numbers!

> * A property defining the mux function to select on those pins/groups.

The muxing possibilities between the individual pin groups are part of
the pin database in the driver.

> * Other properties defining configuration for those pins/groups, such as
> pull-up/down, drive-type, drive-strength, etc.

These are not configurable in the TB10x series of chips.

> All that seems missing here. Surely it's required?
>
> Perhaps this "abilis,simple-default" thing is intended to represent some
> specific default configuration? If so, I don't think that's the right
> way to go. Also, the DT binding should be as complete as possible from
> the start, rather than planning to define/implement part of it now and
> then keep adding to it later. This all implies that some extra
> properties really should be defined here.

The abilis,simple-default thing is simply there because I missed commit
ab78029ecc347debbd737f06688d788bd9d60c1d and followed
Documentation/pinctrl.txt a bit too closely. ("So if possible, handle
the pin control in platform code or some other place where you have
access to all the affected struct device * pointers.") Currently, our
platform code iterates through nodes compatible with
abilis,simple-pinctrl and sets up the pin controller accordingly. I'll
happily remove this mechanism.

Best regards and thanks for your valuable feedback,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-04-26 07:47:18

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Thu, Apr 18, 2013 at 11:03 AM, Christian Ruppert
<[email protected]> wrote:

> We would like to avoid the use of Linux pin numbers in the device tree.
> Customers are used to physical pin numbers and exposing the logical
> Linux-internal numbering scheme through the device tree would generate
> quite some confusion. There are two reasons why we cannot align the two:
> - Not all products supported by these drivers have the same pin outs.
> - GPIO ranges must be consecutive in the Linux pin numbering space
> which is generally not the case in physical pin numbering.

If you are talking about the pin numbers really, i.e. the number we
put into

struct pinctrl_pin_desc {
unsigned number; <- this
const char *name;
};

Then are you aware that this is a sparse number space?

I.e. you can choose whatever number you want. You do not have
to offset numbers from zero or anything like that. You just
need to make sure the numbers do not overlap (no two pins
have the same number).

So if this is what you want to achieve, just use the same number
as in your datasheet in the pin number -> problem solved.

This may lead to some offsetting in your driver etc, and some
struggle do to that. Inspect drivers/pinctrl/pinctrl-abx500.c
for example. Here the datasheet offsets the pins from 1 rather
than from zero, so Patrice had to struggle when cross-mapping
these numbers, but it can surely be done.

> If I understand Documentation/devicetree/bindings/gpio/gpio.txt
> correctly, the "standard" gpio-ranges definition does use Linux logical
> pin numbers, however.

There is no requirement that pin numbers be different from the
datasheet "logical". It is OK to have a 1-1 mapping.

> This disqualifies gpio-ranges from being used in
> our device tree. I haven't found a clean way around this dilemma, thus
> the current implementation duplicating parts of the core system and
> cross-calling pinctrl from GPIO.

I think I have described a solution to this.

> I am well aware of the problem but I haven't found any documentation,
> core functions or examples which solve this. The most elegant way would
> be some core functionality allowing to define GPIO ranges by directly
> querying the pin data base (or to preregister GPIO ranges in the pin
> controller to which GPIO drivers can then "attach"). Is there something
> like this I have missed?

The current preferred pattern is to have the GPIO portions register
the ranges referring to the pin controller pin numbers.

This is because it enables us to only use controller-local
number spaces in both cases.

> As a side note, the same argument does not apply to gpio-base, btw, for
> two reasons:
> - Our customer documentation does not talk about a global GPIO
> numbering scheme. In our products, GPIOs are organised in banks and
> there is no risk of confusion.

This is the local numbering used when registering ranges from
the GPIOlib side referred to above.

> - The Linux-internal GPIO numbering is already exposed to customers
> through the /sys/class/gpio interface.

Yeah this sucks but we have to live with it forever.

> That said, if we solve the cross-calling issue I can still move it to
> the pin data base in the next patch set since you are right that there
> is no real reason to make it "user-configurable" through DT.

I don't understand this.

(Questions answered I think ...)

>> Perhaps this "abilis,simple-default" thing is intended to represent some
>> specific default configuration? If so, I don't think that's the right
>> way to go. Also, the DT binding should be as complete as possible from
>> the start, rather than planning to define/implement part of it now and
>> then keep adding to it later. This all implies that some extra
>> properties really should be defined here.
>
> The abilis,simple-default thing is simply there because I missed commit
> ab78029ecc347debbd737f06688d788bd9d60c1d and followed
> Documentation/pinctrl.txt a bit too closely. ("So if possible, handle
> the pin control in platform code or some other place where you have
> access to all the affected struct device * pointers.") Currently, our
> platform code iterates through nodes compatible with
> abilis,simple-pinctrl and sets up the pin controller accordingly. I'll
> happily remove this mechanism.

Thanks!

Yours,
Linus Walleij

2013-04-29 16:18:21

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

Hello Linus,

On Fri, Apr 26, 2013 at 09:47:16AM +0200, Linus Walleij wrote:
> On Thu, Apr 18, 2013 at 11:03 AM, Christian Ruppert
> <[email protected]> wrote:
>
> > We would like to avoid the use of Linux pin numbers in the device tree.
> > Customers are used to physical pin numbers and exposing the logical
> > Linux-internal numbering scheme through the device tree would generate
> > quite some confusion. There are two reasons why we cannot align the two:
> > - Not all products supported by these drivers have the same pin outs.
> > - GPIO ranges must be consecutive in the Linux pin numbering space
> > which is generally not the case in physical pin numbering.
>
> If you are talking about the pin numbers really, i.e. the number we
> put into
>
> struct pinctrl_pin_desc {
> unsigned number; <- this
> const char *name;
> };
>
> Then are you aware that this is a sparse number space?
>
> I.e. you can choose whatever number you want. You do not have
> to offset numbers from zero or anything like that. You just
> need to make sure the numbers do not overlap (no two pins
> have the same number).
>
> So if this is what you want to achieve, just use the same number
> as in your datasheet in the pin number -> problem solved.

The problem is that we must support several products which are basically
different packaging options of the same chip (or simplified versions
thereof). Not all of those products will have the same number of pins
and as a consequence, data sheet pin numbering will also be different.
The port names are going to remain the same for all products, however.
Some of the ports are just not going to be physically available in some
or the products. Sorry if that wasn't clear from my previous mail.

> This may lead to some offsetting in your driver etc, and some
> struggle do to that. Inspect drivers/pinctrl/pinctrl-abx500.c
> for example. Here the datasheet offsets the pins from 1 rather
> than from zero, so Patrice had to struggle when cross-mapping
> these numbers, but it can surely be done.

This driver seems to avoid many of our problems by integrating both GPIO
and pinctrl driver in the same module. It would not be very difficult to
integrate our separate TB10x GPiO and pinctrl drivers in the same way:
The cross-calling would disappear and the GPIO-base specification could
also easily be removed from device tree. We would still use the named
pingroups scheme in device tree instead of Linux internal pin numbering,
however.
Is this the way to go? I was a bit reluctant from doing this since I'm
not a big fan of huge mega-drivers. Maybe in this particular case such a
driver may be justified?

>[...]
> > I am well aware of the problem but I haven't found any documentation,
> > core functions or examples which solve this. The most elegant way would
> > be some core functionality allowing to define GPIO ranges by directly
> > querying the pin data base (or to preregister GPIO ranges in the pin
> > controller to which GPIO drivers can then "attach"). Is there something
> > like this I have missed?
>
> The current preferred pattern is to have the GPIO portions register
> the ranges referring to the pin controller pin numbers.
>
> This is because it enables us to only use controller-local
> number spaces in both cases.

Agree. The only place in the TB10x drivers where non-local number space
is required is when preparing the GPIO range. I am wondering if some
generic interface in the framework could help us avoid the driver
cross-calls.

> > As a side note, the same argument does not apply to gpio-base, btw, for
> > two reasons:
> > - Our customer documentation does not talk about a global GPIO
> > numbering scheme. In our products, GPIOs are organised in banks and
> > there is no risk of confusion.
>
> This is the local numbering used when registering ranges from
> the GPIOlib side referred to above.
>
> > - The Linux-internal GPIO numbering is already exposed to customers
> > through the /sys/class/gpio interface.
>
> Yeah this sucks but we have to live with it forever.
>
> > That said, if we solve the cross-calling issue I can still move it to
> > the pin data base in the next patch set since you are right that there
> > is no real reason to make it "user-configurable" through DT.
>
> I don't understand this.

Actually, I am thinking of removing the need of specifying gpio-base in
the device tree and assigning a constant gpio-base to each functional
GPIO pin group in pinctrl-tb10x.c. This would also enforce some
coherence in the GPIO numbering between different product variants which
is probably a very good thing. Again, the issue here is that the pin
data base is in the pinctrl driver and the GPIO range is registered in
the GPIO driver but if I integrate the drivers into the same module with
a shared pin data base this issue will disappear.

> >> Perhaps this "abilis,simple-default" thing is intended to represent some
> >> specific default configuration? If so, I don't think that's the right
> >> way to go. Also, the DT binding should be as complete as possible from
> >> the start, rather than planning to define/implement part of it now and
> >> then keep adding to it later. This all implies that some extra
> >> properties really should be defined here.
> >
> > The abilis,simple-default thing is simply there because I missed commit
> > ab78029ecc347debbd737f06688d788bd9d60c1d and followed
> > Documentation/pinctrl.txt a bit too closely. ("So if possible, handle
> > the pin control in platform code or some other place where you have
> > access to all the affected struct device * pointers.") Currently, our
> > platform code iterates through nodes compatible with
> > abilis,simple-pinctrl and sets up the pin controller accordingly. I'll
> > happily remove this mechanism.

Thanks a lot for your guidance.

Best regards,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-05-02 18:50:09

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 04/29/2013 10:17 AM, Christian Ruppert wrote:
> Hello Linus,
>
> On Fri, Apr 26, 2013 at 09:47:16AM +0200, Linus Walleij wrote:
>> On Thu, Apr 18, 2013 at 11:03 AM, Christian Ruppert
>> <[email protected]> wrote:
>>
>>> We would like to avoid the use of Linux pin numbers in the device tree.
>>> Customers are used to physical pin numbers and exposing the logical
>>> Linux-internal numbering scheme through the device tree would generate
>>> quite some confusion. There are two reasons why we cannot align the two:
>>> - Not all products supported by these drivers have the same pin outs.
>>> - GPIO ranges must be consecutive in the Linux pin numbering space
>>> which is generally not the case in physical pin numbering.
>>
>> If you are talking about the pin numbers really, i.e. the number we
>> put into
>>
>> struct pinctrl_pin_desc {
>> unsigned number; <- this
>> const char *name;
>> };
>>
>> Then are you aware that this is a sparse number space?
>>
>> I.e. you can choose whatever number you want. You do not have
>> to offset numbers from zero or anything like that. You just
>> need to make sure the numbers do not overlap (no two pins
>> have the same number).
>>
>> So if this is what you want to achieve, just use the same number
>> as in your datasheet in the pin number -> problem solved.
>
> The problem is that we must support several products which are basically
> different packaging options of the same chip (or simplified versions
> thereof). Not all of those products will have the same number of pins
> and as a consequence, data sheet pin numbering will also be different.
> The port names are going to remain the same for all products, however.
> Some of the ports are just not going to be physically available in some
> or the products. Sorry if that wasn't clear from my previous mail.

It sounds like you can use the exact same numbering scheme for all the
different packaging options; it's just that some of the pin numbers
simply won't exist on some of the packaging options, so while defined by
the DT binding, it simply won't make sense to use them?

Certainly, Tegra20 has two packaging options, and the pinctrl driver for
it has zero knowledge of this. Perhaps we're just lucky though. I guess
the Tegra TRM doesn't define any "Pin number" (just "pin name") for the
pins, so there's no possibility of the same "number" meaning different
things in the two packages, so perhaps we're just getting lucky here.

2013-05-02 18:52:40

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 04/18/2013 03:03 AM, Christian Ruppert wrote:
> Dear Stephen and Linus,
>
> I am responding to this message because it touches the core issue I was
> wondering about when integrating pinctrl with GPIO. I think all
> unrelated comments in your other messages are valid and will be
> addressed in the next iteration of both drivers.
>
> On Wed, Apr 17, 2013 at 12:32:00PM -0600, Stephen Warren wrote:
>> On 04/10/2013 09:45 AM, Christian Ruppert wrote:
>>> The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
>>> Used to control the pinmux and is a prerequisite for the GPIO driver.
>>
>> Linus already did a review of this, but I have a few extra comments:
>>
>>> diff --git a/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
...
>> * A list of pins/groups to apply the settings to.
>
> This is what the pingrp string refers to. We really don't want to expose
> Linux internal pin numbers!

I wasn't suggesting a particular naming/numbering scheme for the pins,
but simply that the document has to state what that naming/numbering
scheme is, so that people know how to fill in the DT. If the
naming/numbering scheme exactly matches some HW manual, then it should
be enough to say e.g. "see section X in Foo manual", to avoid
duplicating the information. If not, then the valid values should be
listed in the binding document.

2013-05-03 18:03:29

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Thu, May 2, 2013 at 8:49 PM, Stephen Warren <[email protected]> wrote:
> On 04/29/2013 10:17 AM, Christian Ruppert wrote:
>>>
>>> So if this is what you want to achieve, just use the same number
>>> as in your datasheet in the pin number -> problem solved.
>>
>> The problem is that we must support several products which are basically
>> different packaging options of the same chip (or simplified versions
>> thereof). Not all of those products will have the same number of pins
>> and as a consequence, data sheet pin numbering will also be different.
>> The port names are going to remain the same for all products, however.
>> Some of the ports are just not going to be physically available in some
>> or the products. Sorry if that wasn't clear from my previous mail.
>
> It sounds like you can use the exact same numbering scheme for all the
> different packaging options; it's just that some of the pin numbers
> simply won't exist on some of the packaging options, so while defined by
> the DT binding, it simply won't make sense to use them?
>
> Certainly, Tegra20 has two packaging options, and the pinctrl driver for
> it has zero knowledge of this. Perhaps we're just lucky though. I guess
> the Tegra TRM doesn't define any "Pin number" (just "pin name") for the
> pins, so there's no possibility of the same "number" meaning different
> things in the two packages, so perhaps we're just getting lucky here.

I am certain it must be possible to come up with something reasonable
here, especially since this is using the device tree.

In U300 we had something like 4 different packaging types, all with
different names on the pins, however I could avoid the entire
issue by using pad numbers instead, i.e. the numbers of the pads/fingers
on the silicon die inside the chip.
(Documentation/pinctrl.txt contains hints on this.)

It seems like your situation is similar.

And since you work for Abilis I assume you can access such low-level
documentation and come up with a numbering scheme based on
something that does not vary with packaging.

And if you can't, and since you're using device tree, come up with
a per-packainging pin numbering, put it into a special .dtsi file that
you layer over the core SoC .dtsi file just to get these numbers,
and then use the apropriate packaging .dtsi in yout eventual
machine .dts file.

Yours,
Linus Walleij

2013-05-08 16:41:57

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Fri, May 03, 2013 at 08:03:27PM +0200, Linus Walleij wrote:
> On Thu, May 2, 2013 at 8:49 PM, Stephen Warren <[email protected]> wrote:
> > On 04/29/2013 10:17 AM, Christian Ruppert wrote:
> >>>
> >>> So if this is what you want to achieve, just use the same number
> >>> as in your datasheet in the pin number -> problem solved.
> >>
> >> The problem is that we must support several products which are basically
> >> different packaging options of the same chip (or simplified versions
> >> thereof). Not all of those products will have the same number of pins
> >> and as a consequence, data sheet pin numbering will also be different.
> >> The port names are going to remain the same for all products, however.
> >> Some of the ports are just not going to be physically available in some
> >> or the products. Sorry if that wasn't clear from my previous mail.
> >
> > It sounds like you can use the exact same numbering scheme for all the
> > different packaging options; it's just that some of the pin numbers
> > simply won't exist on some of the packaging options, so while defined by
> > the DT binding, it simply won't make sense to use them?
> >
> > Certainly, Tegra20 has two packaging options, and the pinctrl driver for
> > it has zero knowledge of this. Perhaps we're just lucky though. I guess
> > the Tegra TRM doesn't define any "Pin number" (just "pin name") for the
> > pins, so there's no possibility of the same "number" meaning different
> > things in the two packages, so perhaps we're just getting lucky here.
>
> I am certain it must be possible to come up with something reasonable
> here, especially since this is using the device tree.
>
> In U300 we had something like 4 different packaging types, all with
> different names on the pins, however I could avoid the entire
> issue by using pad numbers instead, i.e. the numbers of the pads/fingers
> on the silicon die inside the chip.
> (Documentation/pinctrl.txt contains hints on this.)
>
> It seems like your situation is similar.
>
> And since you work for Abilis I assume you can access such low-level
> documentation and come up with a numbering scheme based on
> something that does not vary with packaging.

Yes but unluckily the decision which numbers to expose to customers is
not an engineering decision, thus the difficulty with everything but
physical pin numbers.

> And if you can't, and since you're using device tree, come up with
> a per-packainging pin numbering, put it into a special .dtsi file that
> you layer over the core SoC .dtsi file just to get these numbers,
> and then use the apropriate packaging .dtsi in yout eventual
> machine .dts file.

What do you think about the following modification to the pinctrl/GPIO
frameworks instead (not yet a formal patch, more a request for comment
to illustrate what I mean. If you agree, I will clean it up and submit a
proper patch after discussion).

It adds a dt_gpiorange_xlate function to the pinctrl callbacks which
defaults to the conventional behaviour using kernel logical pin numbers.
However, pin controllers which provide more complex mechanisms can
define #gpio-range-cells and provide this callback in order to keep
Linux pin numbering inside the kernel.

One could either use the phandles to pin controller sub nodes as done in
the original gpio-tb10x patch or e.g. define numbered pin groups inside
the pin controller. This is the mechanism used in many other device tree
bindings (interrupt controllers, GPIOs etc) to separate kernel internal
numbering schemes from the device tree.

It would also allow removing the module cross-calling between the tb10x
GPIO and pinctrl drivers by migrating most of the code from
tb10x_prepare_gpio_range to that callback and removing
tb10x_setup_gpio_range alltogether.

Greetings,
Christian

---
drivers/gpio/gpiolib-of.c | 61 +++++++++++++++++++++++++++++++++------
drivers/pinctrl/devicetree.c | 31 ++++++++++++++++++++
include/linux/of_gpio.h | 1 +
include/linux/pinctrl/pinctrl.h | 18 +++++++++++
4 files changed, 102 insertions(+), 9 deletions(-)

diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 5150df6..10df33b 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -183,10 +183,57 @@ err0:
EXPORT_SYMBOL(of_mm_gpiochip_add);

#ifdef CONFIG_PINCTRL
+static inline int of_gpiochip_parse_xlate(struct device_node *np, int index,
+ u32 *pin_offset, u32 *npins,
+ struct pinctrl_dev **pctldevret)
+{
+ int ret;
+ struct of_phandle_args pinspec;
+ struct pinctrl_dev *pctldev;
+
+ ret = of_parse_phandle_with_args(np, "gpio-ranges",
+ "#gpio-range-cells", index, &pinspec);
+ if (ret)
+ return ret;
+
+ pctldev = of_pinctrl_get(pinspec.np);
+ if (!pctldev)
+ return -EINVAL;
+
+ if (pctldevret)
+ *pctldevret = pctldev;
+
+ ret = of_pinctrl_gpiorange_xlate(pctldev, pinspec.np,
+ pinspec.args,
+ pinspec.args_count,
+ pin_offset, npins);
+ return ret;
+}
+
+int of_gpiochip_get_npins(struct device_node *np)
+{
+ int pincnt = 0;
+ int index;
+
+ if (!np)
+ return -EINVAL;
+
+ for (index = 0;; index++) {
+ u32 pin_offset, npins;
+
+ if (of_gpiochip_parse_xlate(np, index,
+ &pin_offset, &npins, NULL))
+ break;
+
+ pincnt += npins;
+ }
+
+ return pincnt;
+}
+
static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
{
struct device_node *np = chip->of_node;
- struct of_phandle_args pinspec;
struct pinctrl_dev *pctldev;
int index = 0, ret;

@@ -194,13 +241,10 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
return;

for (;; index++) {
- ret = of_parse_phandle_with_args(np, "gpio-ranges",
- "#gpio-range-cells", index, &pinspec);
- if (ret)
- break;
+ u32 pin_offset, npins;

- pctldev = of_pinctrl_get(pinspec.np);
- if (!pctldev)
+ if (of_gpiochip_parse_xlate(np, index,
+ &pin_offset, &npins, &pctldev))
break;

/*
@@ -217,8 +261,7 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
ret = gpiochip_add_pin_range(chip,
pinctrl_dev_get_devname(pctldev),
0, /* offset in gpiochip */
- pinspec.args[0],
- pinspec.args[1]);
+ pin_offset, npins);

if (ret)
break;
diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c
index fd40a11..4a11c49 100644
--- a/drivers/pinctrl/devicetree.c
+++ b/drivers/pinctrl/devicetree.c
@@ -117,6 +117,37 @@ struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
return pctldev;
}

+static int default_dt_gpiorange_xlate(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ u32 *rangespec, int rangespecsize,
+ u32 *pin_offset, u32 *npins)
+{
+ if (rangespecsize != 2)
+ return -EINVAL;
+
+ *pin_offset = rangespec[0];
+ *npins = rangespec[1];
+ return 0;
+}
+
+int of_pinctrl_gpiorange_xlate(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ u32 *rangespec, int rangespecsize,
+ u32 *pin_offset, u32 *npins)
+{
+ struct pinctrl_ops *ops = pctldev->desc->pctlops;
+ int ret;
+
+ if (ops->dt_gpiorange_xlate)
+ ret = ops->dt_gpiorange_xlate(pctldev, np, rangespec,
+ rangespecsize, pin_offset, npins);
+ else
+ ret = default_dt_gpiorange_xlate(pctldev, np, rangespec,
+ rangespecsize, pin_offset, npins);
+
+ return ret;
+}
+
static int dt_to_map_one_config(struct pinctrl *p, const char *statename,
struct device_node *np_config)
{
diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h
index a83dc6f..486999a 100644
--- a/include/linux/of_gpio.h
+++ b/include/linux/of_gpio.h
@@ -53,6 +53,7 @@ extern int of_get_named_gpio_flags(struct device_node *np,
extern int of_mm_gpiochip_add(struct device_node *np,
struct of_mm_gpio_chip *mm_gc);

+extern int of_gpiochip_get_npins(struct device_node *np);
extern void of_gpiochip_add(struct gpio_chip *gc);
extern void of_gpiochip_remove(struct gpio_chip *gc);
extern int of_gpio_simple_xlate(struct gpio_chip *gc,
diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 778804d..78beeb8 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -81,6 +81,8 @@ struct pinctrl_gpio_range {
* allocated members of the mapping table entries themselves. This
* function is optional, and may be omitted for pinctrl drivers that do
* not support device tree.
+ * @dt_gpiorange_xlate: translate device tree gpio-ranges specification to
+ * pin_offset and npins arguments for gpiochip_add_pin_range.
*/
struct pinctrl_ops {
int (*get_groups_count) (struct pinctrl_dev *pctldev);
@@ -97,6 +99,10 @@ struct pinctrl_ops {
struct pinctrl_map **map, unsigned *num_maps);
void (*dt_free_map) (struct pinctrl_dev *pctldev,
struct pinctrl_map *map, unsigned num_maps);
+ int (*dt_gpiorange_xlate) (struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ u32 *rangespec, int rangespecsize,
+ u32 *pin_offset, u32 *npins);
};

/**
@@ -145,12 +151,24 @@ pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,

#ifdef CONFIG_OF
extern struct pinctrl_dev *of_pinctrl_get(struct device_node *np);
+extern int of_pinctrl_gpiorange_xlate(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ u32 *rangespec, int rangespecsize,
+ u32 *pin_offset, u32 *npins);
#else
static inline
struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
{
return NULL;
}
+static inline
+int of_pinctrl_gpiorange_xlate(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ u32 *rangespec, int rangespecsize,
+ u32 *pin_offset, u32 *npins)
+{
+ return 0;
+}
#endif /* CONFIG_OF */

extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev);
--


--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-05-08 20:01:59

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 05/08/2013 10:41 AM, Christian Ruppert wrote:
...
> What do you think about the following modification to the pinctrl/GPIO
> frameworks instead (not yet a formal patch, more a request for comment
> to illustrate what I mean. If you agree, I will clean it up and submit a
> proper patch after discussion).
>
> It adds a dt_gpiorange_xlate function to the pinctrl callbacks which
> defaults to the conventional behaviour using kernel logical pin numbers.
> However, pin controllers which provide more complex mechanisms can
> define #gpio-range-cells and provide this callback in order to keep
> Linux pin numbering inside the kernel.

Can you provide an example of the DT content, and explain exactly what
this patch does with it; what effect it has on the existing GPIO or
pinctrl code?

2013-05-10 08:26:10

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Wed, May 08, 2013 at 02:01:53PM -0600, Stephen Warren wrote:
> On 05/08/2013 10:41 AM, Christian Ruppert wrote:
> ...
> > What do you think about the following modification to the pinctrl/GPIO
> > frameworks instead (not yet a formal patch, more a request for comment
> > to illustrate what I mean. If you agree, I will clean it up and submit a
> > proper patch after discussion).
> >
> > It adds a dt_gpiorange_xlate function to the pinctrl callbacks which
> > defaults to the conventional behaviour using kernel logical pin numbers.
> > However, pin controllers which provide more complex mechanisms can
> > define #gpio-range-cells and provide this callback in order to keep
> > Linux pin numbering inside the kernel.
>
> Can you provide an example of the DT content, and explain exactly what
> this patch does with it; what effect it has on the existing GPIO or
> pinctrl code?

The patch does not change the default behaviour of the kernel: In case
no dt_gpiorange_xlate callback is defined for a given driver (e.g. for
pre-existing drivers), the default function simply interprets the first
argument as Linux pin number and the second as pin count, same as now.
New drivers can use the callback to translate device specific pin
references to Linux pin numbers (in the idea of of_xlate in the GPIO
framework or xlate in the irqchip framework).

In the case of TB10x, I was thinking of something in the lines of

iomux: iomux@FF10601c {
#gpio-range-cells = <1>; /* one cell used for gpiorange phandle */
compatible = "abilis,tb10x-iomux";
reg = <0xFF10601c 0x4>;
pctl_gpio_a: pctl-gpio-a { /* define phandle to GPIOA I/O function */
pingrp = "gpioa_pins";
};
pctl_gpio_b: pctl-gpio-b { /* define phandle to GPIOB I/O function */
pingrp = "gpiob_pins";
};
pctl_uart0: pctl-uart0 { /* define phandle to UART0 I/O function */
pingrp = "uart0_pins";
};
};
uart@FF100000 {
compatible = "snps,dw-apb-uart";
reg = <0xFF100000 0x100>;
clock-frequency = <166666666>;
interrupts = <25 1>;
reg-shift = <2>;
reg-io-width = <4>;
pinctrl-names = "default"; /* For non-GPIO modules, request I/O */
pinctrl-0 = <&pctl_uart0>; /* functions normally */
};
gpioa: gpio@FF140000 {
compatible = "abilis,tb10x-gpio";
reg = <0xFF140000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&iomux &pctl_gpio_a>; /* (*1) */
};

gpioa: gpio@FF140000 {
compatible = "abilis,tb10x-gpio";
reg = <0xFF140000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
pinctrl-names = "default" /* here the GPIO controller requests */
pinctrl-0 = <&pctl_gpio_b>; /* the entire GPIOB port explicitly */
gpio-ranges = <&iomux &pctl_gpio_b>; /* (*2) */
};

(*1) TB100 GPIO ranges are defined as a phandle to the I/O function
which provides all pins of a given GPIO port. This function is not
necessarily requested from pinctrl and GPIO ports may overlap with
other functions. The pin controller knows about this and provides
whatever GPIO pin is available after mapping other requested
functions.
(*2) Here, the entire GPIOB port is explicitly requested by the GPIO
module, i.e. all pins of the port are made available as GPIOs.

The xlate function in pinctrl-tb10x.c would look something like this:

static int tb10x_dt_gpiorange_xlate(struct pinctrl_dev *pctldev,
struct device_node *np,
u32 *rangespec, int rangespecsize,
u32 *pin_offset, u32 *npins)
{
const char *name;
int ret;
struct tb10x_pinfuncgrp *pfg;
struct device_node *dn;

if (rangespecsize != 1)
return -EINVAL;

dn = of_find_node_by_phandle(rangespec[0]);
if (!dn)
return -EINVAL;
of_node_put(dn);

ret = of_property_read_string(dn, "pingrp", &name);
if (ret)
return ret;

pfg = tb10x_get_pinfuncgrp(name);
if (IS_ERR(pfg))
return PTR_ERR(pfg);

if (!pfg->isgpio)
return -EINVAL;

*pin_offset = pfg->pins[0];
*npins = pfg->pincnt;

return 0;
}

Other drivers may use physical pin numbers, assign integer identifiers
to each GPIO bank or use some other numbering scheme documented in
Documentation/devicetree/bindings.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-05-14 12:29:49

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Fri, May 10, 2013 at 10:25 AM, Christian Ruppert
<[email protected]> wrote:

>> > What do you think about the following modification to the pinctrl/GPIO
>> > frameworks instead (not yet a formal patch, more a request for comment
>> > to illustrate what I mean. If you agree, I will clean it up and submit a
>> > proper patch after discussion).
>> >
>> > It adds a dt_gpiorange_xlate function to the pinctrl callbacks which
>> > defaults to the conventional behaviour using kernel logical pin numbers.
>> > However, pin controllers which provide more complex mechanisms can
>> > define #gpio-range-cells and provide this callback in order to keep
>> > Linux pin numbering inside the kernel.
(...)
> The patch does not change the default behaviour of the kernel: In case
> no dt_gpiorange_xlate callback is defined for a given driver (e.g. for
> pre-existing drivers), the default function simply interprets the first
> argument as Linux pin number and the second as pin count, same as now.
> New drivers can use the callback to translate device specific pin
> references to Linux pin numbers (in the idea of of_xlate in the GPIO
> framework or xlate in the irqchip framework).

I like the concept.

However I am totally opposed to the idea of making this something
device tree-exclusive.

Look at for example
drivers/pinctrl/pinctrl-abx500.c:

static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
unsigned gpio)
{
(...)
/* on ABx5xx, there is no GPIO0, so adjust the offset */
unsigned offset = gpio - 1;

As you can see, this driver, which does not use device tree,
is working around the same problem. Here the problem is that
the pins are numbered starting at 1 instead of 0, a very trivial
numberspace shuffleing.

I'd be open to this approach if you:

- Make it generic for all pinctrl drivers, i.e. add the translation
to the core so it does not just apply to devices using device tree.

- Augment the pinctrl-abx500.c driver to show how this simplifies
that driver. (Does not need to be perfect, I'll help out finalizing it
for sure.)

- Then add DT-specific wrapper using this core feature.

This way the problem will be solved for everybody, including
ACPI when they sooner or later come back with the same issue.

Yours,
Linus Walleij

2013-05-15 09:42:21

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Tue, May 14, 2013 at 02:29:46PM +0200, Linus Walleij wrote:
> On Fri, May 10, 2013 at 10:25 AM, Christian Ruppert
> <[email protected]> wrote:
>
> >> > What do you think about the following modification to the pinctrl/GPIO
> >> > frameworks instead (not yet a formal patch, more a request for comment
> >> > to illustrate what I mean. If you agree, I will clean it up and submit a
> >> > proper patch after discussion).
> >> >
> >> > It adds a dt_gpiorange_xlate function to the pinctrl callbacks which
> >> > defaults to the conventional behaviour using kernel logical pin numbers.
> >> > However, pin controllers which provide more complex mechanisms can
> >> > define #gpio-range-cells and provide this callback in order to keep
> >> > Linux pin numbering inside the kernel.
> (...)
> > The patch does not change the default behaviour of the kernel: In case
> > no dt_gpiorange_xlate callback is defined for a given driver (e.g. for
> > pre-existing drivers), the default function simply interprets the first
> > argument as Linux pin number and the second as pin count, same as now.
> > New drivers can use the callback to translate device specific pin
> > references to Linux pin numbers (in the idea of of_xlate in the GPIO
> > framework or xlate in the irqchip framework).
>
> I like the concept.
>
> However I am totally opposed to the idea of making this something
> device tree-exclusive.
>
> Look at for example
> drivers/pinctrl/pinctrl-abx500.c:
>
> static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
> unsigned gpio)
> {
> (...)
> /* on ABx5xx, there is no GPIO0, so adjust the offset */
> unsigned offset = gpio - 1;
>
> As you can see, this driver, which does not use device tree,
> is working around the same problem. Here the problem is that
> the pins are numbered starting at 1 instead of 0, a very trivial
> numberspace shuffleing.

Let me see if I understand this right:
In ABx5xx, the pin numbering is offset by 1 wrt. GPIO numbering?

> I'd be open to this approach if you:
>
> - Make it generic for all pinctrl drivers, i.e. add the translation
> to the core so it does not just apply to devices using device tree.

Do you mean what would be required here is a generic way to translate
pin numbers to GPIO numbers in the pin controller?

This translation is currently achieved by the gpio_range mechanism.
Internals of gpio_range leak into the pinctrl drivers and are bypassed
in many cases. E.g. pinctrl-abx500.c uses an internal reimplementation
of gpio ranges and the offset by one instead of the information in the
pinctrl_gpio_range structure.
Mapping pin numbers to GPIO numbers in the pin controller would have the
following advantages:
- It makes GPIO numbering well defined which is clearly an advantage
for the /sys/class/gpio interface: GPIO numbering is now controlled
by the pinctrl driver author and no longer needs to be kernel
internal.
- The device tree/acpi issue is solved since a GPIO controller could
now define its range in GPIO number space (which becomes public)
rather than kernel-internal pin number space. At least for our
products, GPIO numbering generally doesn't change between different
variants of the same chip and the /sys/class/gpio customer
documentation would apply to device also.
- The custom logic inside many pinctrl drivers would be confined in one
translation function the driver provides instead of being spread out
all over the driver.
- "Sparse GPIO ranges" are easy to implement if required by the
platform/driver.

> - Augment the pinctrl-abx500.c driver to show how this simplifies
> that driver. (Does not need to be perfect, I'll help out finalizing it
> for sure.)

The issue is that such a change is quite fundamental, all pinctrl
drivers would have to be upgraded (not just pinctrl-abx500.c) and struct
pinctrl_gpio_range would have to be removed from the gpio_request_enable
callback and friends in favour of some generic translation mechanism.

I am also afraid that the custom logic in many drivers could only be
rewritten with the help of the respective driver's author.

> - Then add DT-specific wrapper using this core feature.

GPIO ranges definitions in DT would probably be no longer be compatible
with previous definitions since the number space would change from pin
numbers to GPIO numbers. I don't think this is acceptable.

> This way the problem will be solved for everybody, including
> ACPI when they sooner or later come back with the same issue.

Given the issues highlighted above I'm not sure if I understand your
proposal correctly. Although I see the advantages, I wonder if the
migration to such a system is feasible in practise.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-05-16 00:12:43

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 05/10/2013 02:25 AM, Christian Ruppert wrote:
> On Wed, May 08, 2013 at 02:01:53PM -0600, Stephen Warren wrote:
>> On 05/08/2013 10:41 AM, Christian Ruppert wrote:
>> ...
>>> What do you think about the following modification to the pinctrl/GPIO
>>> frameworks instead (not yet a formal patch, more a request for comment
>>> to illustrate what I mean. If you agree, I will clean it up and submit a
>>> proper patch after discussion).
>>>
>>> It adds a dt_gpiorange_xlate function to the pinctrl callbacks which
>>> defaults to the conventional behaviour using kernel logical pin numbers.
>>> However, pin controllers which provide more complex mechanisms can
>>> define #gpio-range-cells and provide this callback in order to keep
>>> Linux pin numbering inside the kernel.
>>
>> Can you provide an example of the DT content, and explain exactly what
>> this patch does with it; what effect it has on the existing GPIO or
>> pinctrl code?
>
> The patch does not change the default behaviour of the kernel: In case
> no dt_gpiorange_xlate callback is defined for a given driver (e.g. for
> pre-existing drivers), the default function simply interprets the first
> argument as Linux pin number and the second as pin count, same as now.
> New drivers can use the callback to translate device specific pin
> references to Linux pin numbers (in the idea of of_xlate in the GPIO
> framework or xlate in the irqchip framework).
>
> In the case of TB10x, I was thinking of something in the lines of
>
> iomux: iomux@FF10601c {
> #gpio-range-cells = <1>; /* one cell used for gpiorange phandle */
> compatible = "abilis,tb10x-iomux";
> reg = <0xFF10601c 0x4>;
> pctl_gpio_a: pctl-gpio-a { /* define phandle to GPIOA I/O function */
> pingrp = "gpioa_pins";
> };

These aren't phandles at all; they're just plain old nodes and properties.

> pctl_gpio_b: pctl-gpio-b { /* define phandle to GPIOB I/O function */
> pingrp = "gpiob_pins";
> };
> pctl_uart0: pctl-uart0 { /* define phandle to UART0 I/O function */
> pingrp = "uart0_pins";
> };
> };
> uart@FF100000 {
> compatible = "snps,dw-apb-uart";
> reg = <0xFF100000 0x100>;
> clock-frequency = <166666666>;
> interrupts = <25 1>;
> reg-shift = <2>;
> reg-io-width = <4>;
> pinctrl-names = "default"; /* For non-GPIO modules, request I/O */
> pinctrl-0 = <&pctl_uart0>; /* functions normally */
> };
> gpioa: gpio@FF140000 {
> compatible = "abilis,tb10x-gpio";
> reg = <0xFF140000 0x1000>;
> gpio-controller;
> #gpio-cells = <2>;
> gpio-ranges = <&iomux &pctl_gpio_a>; /* (*1) */

I guess you had to make the pctl_gpio_a a DT node, because to reference
it by phandle, it has to be a node; a phandle can't point at a property.

> };
>
> gpioa: gpio@FF140000 {
> compatible = "abilis,tb10x-gpio";
> reg = <0xFF140000 0x1000>;
> gpio-controller;
> #gpio-cells = <2>;
> pinctrl-names = "default" /* here the GPIO controller requests */
> pinctrl-0 = <&pctl_gpio_b>; /* the entire GPIOB port explicitly */
> gpio-ranges = <&iomux &pctl_gpio_b>; /* (*2) */
> };
>
> (*1) TB100 GPIO ranges are defined as a phandle to the I/O function
> which provides all pins of a given GPIO port. This function is not
> necessarily requested from pinctrl and GPIO ports may overlap with
> other functions. The pin controller knows about this and provides
> whatever GPIO pin is available after mapping other requested
> functions.
> (*2) Here, the entire GPIOB port is explicitly requested by the GPIO
> module, i.e. all pins of the port are made available as GPIOs.

So I think all you're looking for is a way in DT to represent GPIO
ranges? I don't think that should be by string name, but rather numbers:

(actually, doesn't pinctrl-simple already have this?)

The GPIO driver exposes N GPIOs, probably numbered 0..n-1, but I guess
it can number them in whatever sparse way it wants.

The pinctrl driver exposes N pins, again probably numbered 0..n-1, but I
guess it can number them in whatever sparse way it wants.

You want a way to represent the mapping between the various GPIO HW
modules' internal GPIO IDs and the various pinctrl HW modules' pin IDs.
That mapping should be just by integer GPIO/pin ID, I think. What about
the following:

iomux: iomux@FF10601c {
...
};
gpio@FF140000 {
gpio-ranges = <GPIO_BASE SIZE &iomux IOMUX_PIN_BASE>;
};

where:

GPIO_BASE: GPIO ID (within gpioa, not global) for the start of the range
covered by this entry.

SIZE: Number of GPIO IDs (and pinctrl pin IDs) covered by this entry.

&iomux: The pin controller related to this range.

IOMUX_PIN_BASE: The pinctrl pin ID (within the iomux pin controller, not
global) for the start of the range covered by this entry.

You could easily have more complex setups, with multiple disjoint ranges:

iomuxa: iomux@1000 {
...
};
iomuxb: iomux@2000 {
...
};
gpio@8000 {
gpio-ranges =
<0 16 &iomuxa 0>,
/* disjoint here */
<32 16 &iomuxb 0>,
<48 5 &iomuxa 16>,
};
gpio@9000 {
gpio-ranges = <GPIO_BASE SIZE &iomux IOMUX_PIN_BASE>;
gpio-ranges =
<1 16 &iomuxb 16>,
/* iomuxa's pin IDs are disjoint, hence not 21 here */
<32 8 &iomuxa 32>,
};

The final version of this may require e.g. #gpio-range-pin-cells in the
iomux nodes, and #gpio-range-gpio-cells in the GPIO nodes to aid
automated parsing.

That way, these properties simply end up being parsed into standard and
already extant pinctrl_add_gpio_range() calls, or the other similar API.

2013-05-20 08:03:28

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Wed, May 15, 2013 at 11:41 AM, Christian Ruppert
<[email protected]> wrote:
> On Tue, May 14, 2013 at 02:29:46PM +0200, Linus Walleij wrote:

>> Look at for example
>> drivers/pinctrl/pinctrl-abx500.c:
>>
>> static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
>> unsigned gpio)
>> {
>> (...)
>> /* on ABx5xx, there is no GPIO0, so adjust the offset */
>> unsigned offset = gpio - 1;
>>
>> As you can see, this driver, which does not use device tree,
>> is working around the same problem. Here the problem is that
>> the pins are numbered starting at 1 instead of 0, a very trivial
>> numberspace shuffleing.
>
> Let me see if I understand this right:
> In ABx5xx, the pin numbering is offset by 1 wrt. GPIO numbering?

No, sorry that the code is strange... The variable is named "gpio"
because that is the numbering used in the data sheet.

Think of "gpio" as "the number in the data sheet".

Then offset is calculated to start from 0.

>> I'd be open to this approach if you:
>>
>> - Make it generic for all pinctrl drivers, i.e. add the translation
>> to the core so it does not just apply to devices using device tree.
>
> Do you mean what would be required here is a generic way to translate
> pin numbers to GPIO numbers in the pin controller?

No that is already done by the GPIO ranges.

I thought this discussion was about shuffleing around/translating
the numberings of the pins themselves.

> This translation is currently achieved by the gpio_range mechanism.

Yep.

> Internals of gpio_range leak into the pinctrl drivers and are bypassed
> in many cases. E.g. pinctrl-abx500.c uses an internal reimplementation
> of gpio ranges and the offset by one instead of the information in the
> pinctrl_gpio_range structure.

It has abx500_pinrange to associate some information with certain
ranges of pins. It has nothing to do with GPIO ranges if you look
closer at it.

The offset by one is achieved not by this range type, but by hard-coding
a subtraction with one at every entry point, as illustrated. This
is orthogonal to all range concepts.

> Mapping pin numbers to GPIO numbers in the pin controller would have the
> following advantages:
> - It makes GPIO numbering well defined which is clearly an advantage
> for the /sys/class/gpio interface: GPIO numbering is now controlled
> by the pinctrl driver author and no longer needs to be kernel
> internal.
> - The device tree/acpi issue is solved since a GPIO controller could
> now define its range in GPIO number space (which becomes public)
> rather than kernel-internal pin number space. At least for our
> products, GPIO numbering generally doesn't change between different
> variants of the same chip and the /sys/class/gpio customer
> documentation would apply to device also.

This is getting very confused so I can't follow it.

For the GPIO and pin control subsystems there exist three things:

- Local offsets on the GPIO controller, such as if there is a
GPIO controller for 32 lines represented by 32 bits, offset
0 .. 31.

- The global GPIO number space, which is a big array with
some roof, where the GPIO numbers are shoehorned in,
trying not to collide.

- Local offsets on the pin controller, which work the same way
as GPIO local offsets.

I think you are talking about something completely different
here, and that might be the numbering scheme used in the
data sheet, or device tree is this correct? Please call that
the data sheet or device tree numbering system in that
case, or it will be very confusing for me. To me all
"GPIO numbers" are pure kernel concepts.

If you mean that you try to map the global GPIO number space
1:1 on top of what your datasheet has, just *don't do that*.
Because we want to get rid of this global GPIO numberspace.
Alexandre is already working hard on this!

Can you please try to be very specific on what is
going on here?

> - The custom logic inside many pinctrl drivers would be confined in one
> translation function the driver provides instead of being spread out
> all over the driver.
> - "Sparse GPIO ranges" are easy to implement if required by the
> platform/driver.

This looks good.

>> - Augment the pinctrl-abx500.c driver to show how this simplifies
>> that driver. (Does not need to be perfect, I'll help out finalizing it
>> for sure.)
>
> The issue is that such a change is quite fundamental, all pinctrl
> drivers would have to be upgraded (not just pinctrl-abx500.c) and struct
> pinctrl_gpio_range would have to be removed from the gpio_request_enable
> callback and friends in favour of some generic translation mechanism.

Doing large refactorings is a normal part of kernel life.
See: Documentation/stable_api_nonsense.txt

> I am also afraid that the custom logic in many drivers could only be
> rewritten with the help of the respective driver's author.

If you're patching their drivers they are obliged to help out
by reviewing and testing, that is how we work. If they don't
review and test patches to their own code, they obviously do
not care if we break it either. All testing in linux-next and the
release cycle is about being able to change things if need be.

>> - Then add DT-specific wrapper using this core feature.
>
> GPIO ranges definitions in DT would probably be no longer be compatible
> with previous definitions since the number space would change from pin
> numbers to GPIO numbers. I don't think this is acceptable.

I can't see why you're saying this. Please elaborate.

What I hear is that DT need a translation mechanism, and
what I say is that OK, implement that in the core then, the
semantic effect will be the same.

>> This way the problem will be solved for everybody, including
>> ACPI when they sooner or later come back with the same issue.
>
> Given the issues highlighted above I'm not sure if I understand your
> proposal correctly. Although I see the advantages, I wonder if the
> migration to such a system is feasible in practise.

We need to iterate this discussion a few turns until I actually
understand your problem I think.

Yours,
Linus Walleij

2013-05-20 08:10:37

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Thu, May 16, 2013 at 2:12 AM, Stephen Warren <[email protected]> wrote:
> On 05/10/2013 02:25 AM, Christian Ruppert wrote:

>> (*1) TB100 GPIO ranges are defined as a phandle to the I/O function
>> which provides all pins of a given GPIO port. This function is not
>> necessarily requested from pinctrl and GPIO ports may overlap with
>> other functions. The pin controller knows about this and provides
>> whatever GPIO pin is available after mapping other requested
>> functions.
>> (*2) Here, the entire GPIOB port is explicitly requested by the GPIO
>> module, i.e. all pins of the port are made available as GPIOs.
>
> So I think all you're looking for is a way in DT to represent GPIO
> ranges? I don't think that should be by string name, but rather numbers:
>
> (actually, doesn't pinctrl-simple already have this?)

Now I'm ever more confused ... we already have this :-)

It's not even pinctrl-simple-centric it is completely generic.
The code is in drivers/gpio/gpiolib-of.c.

It was written by Shiraz Hashin and Haojian Zhuang.
At the time I augmented the core code quite a bit to make
a good fit.

This is from:
Documentation/devicetree/bindings/gpio/gpio.txt

2.1) gpio-controller and pinctrl subsystem
------------------------------------------

gpio-controller on a SOC might be tightly coupled with the pinctrl
subsystem, in the sense that the pins can be used by other functions
together with optional gpio feature.

While the pin allocation is totally managed by the pin ctrl subsystem,
gpio (under gpiolib) is still maintained by gpio drivers. It may happen
that different pin ranges in a SoC is managed by different gpio drivers.

This makes it logical to let gpio drivers announce their pin ranges to
the pin ctrl subsystem and call 'pinctrl_request_gpio' in order to
request the corresponding pin before any gpio usage.

For this, the gpio controller can use a pinctrl phandle and pins to
announce the pinrange to the pin ctrl subsystem. For example,

qe_pio_e: gpio-controller@1460 {
#gpio-cells = <2>;
compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
reg = <0x1460 0x18>;
gpio-controller;
gpio-ranges = <&pinctrl1 0 20 10>, <&pinctrl2 10 50 20>;

}

where,
&pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.

Next values specify the base pin and number of pins for the range
handled by 'qe_pio_e' gpio. In the given example from base pin 20 to
pin 29 under pinctrl1 with gpio offset 0 and pin 50 to pin 69 under
pinctrl2 with gpio offset 10 is handled by this gpio controller.

The pinctrl node must have "#gpio-range-cells" property to show number of
arguments to pass with phandle from gpio controllers node.

Yours,
Linus Walleij

2013-05-22 09:50:39

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Mon, May 20, 2013 at 10:03:24AM +0200, Linus Walleij wrote:
> On Wed, May 15, 2013 at 11:41 AM, Christian Ruppert
> <[email protected]> wrote:
> > On Tue, May 14, 2013 at 02:29:46PM +0200, Linus Walleij wrote:
>
> >> Look at for example
> >> drivers/pinctrl/pinctrl-abx500.c:
> >>
> >> static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
> >> unsigned gpio)
> >> {
> >> (...)
> >> /* on ABx5xx, there is no GPIO0, so adjust the offset */
> >> unsigned offset = gpio - 1;
> >>
> >> As you can see, this driver, which does not use device tree,
> >> is working around the same problem. Here the problem is that
> >> the pins are numbered starting at 1 instead of 0, a very trivial
> >> numberspace shuffleing.
> >
> > Let me see if I understand this right:
> > In ABx5xx, the pin numbering is offset by 1 wrt. GPIO numbering?
>
> No, sorry that the code is strange... The variable is named "gpio"
> because that is the numbering used in the data sheet.
>
> Think of "gpio" as "the number in the data sheet".
>
> Then offset is calculated to start from 0.

OK, seems like I thoroughly misunderstood what you were saying and drew
some wrong conclusions from that. In order to avoid confusion I'll
delete everything related to this misunderstanding below.

> [...]
> If you mean that you try to map the global GPIO number space
> 1:1 on top of what your datasheet has, just *don't do that*.
> Because we want to get rid of this global GPIO numberspace.
> Alexandre is already working hard on this!

Thanks for the hint. Defining a global GPIO number space in the data
sheet was one of our ideas but I agree it would be preferable to do away
with this completely and rather deal with GPIOs on a bank level.

> > - The custom logic inside many pinctrl drivers would be confined in one
> > translation function the driver provides instead of being spread out
> > all over the driver.
> > - "Sparse GPIO ranges" are easy to implement if required by the
> > platform/driver.
>
> This looks good.

I think these advantages could be maintained through two alternative
methods:
1) Extend the GPIO ranges mechanism to allow for pin groups in addition
to contiguous pin ranges. This would be my preferred method since no
pin numbers are exposed to the user at all (neither kernel internal
nor physical). All pin numbering remains inside the driver. This
would be a clean version of what I did in the original patch.
2) Add a physical-to-Linux-internal pin number translation callback to
the pinmux driver core interface. Map 1-to-1 if this callback is not
defined to maintain backwards compatibility. I don't like this
solution as much as 1) for the following reasons:
. It adds yet another number space to confuse things even more.
. Managing contiguous ranges becomes more difficult because ranges
contiguous in one number space (physical pins, Linux pins, GPIOs)
are not necessarily contiguous in the others, requiring more
complex translation mechanisms.
. Some chips have matrix like number schemes (A1, A2, ..., B1, B1,
...) etc. which cannot be directly addressed with this and would
need to introduce yet another layer of indirection.
. In cases where different package options need to be supported, a
translation table is required for each inside the driver. For
modern SOCs, each of these tables can easily be a few kB in size
and I don't know if we want to add that amount of static data to
the kernel, esp. because all but one of the tables are unused.

I don't know ACPI and one of the two solutions might not be applicable
there.

> [...]
> We need to iterate this discussion a few turns until I actually
> understand your problem I think.

I agree. Let me resume my high level constraints again:
- Align customer visible interfaces (such as device tree and
sys/class/gpio) with our hardware documentation.
- Documentation can be augmented (e.g. a global GPIO number space
_could_ be added) but fundamentals (such as physical pin numbering,
our GPIO bank structure) must not be changed.
- Several distinct naming schemes for the same thing (such as pins) are
only allowed if it is obvious to which naming scheme a given name
belongs. E.g. pins may have both a name (string) and a number (and
they actually have) but it is not allowed to have two different
numbers designating the same pin.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-05-22 14:28:56

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Mon, May 20, 2013 at 10:10:33AM +0200, Linus Walleij wrote:
> On Thu, May 16, 2013 at 2:12 AM, Stephen Warren <[email protected]> wrote:
> > On 05/10/2013 02:25 AM, Christian Ruppert wrote:
>
> >> (*1) TB100 GPIO ranges are defined as a phandle to the I/O function
> >> which provides all pins of a given GPIO port. This function is not
> >> necessarily requested from pinctrl and GPIO ports may overlap with
> >> other functions. The pin controller knows about this and provides
> >> whatever GPIO pin is available after mapping other requested
> >> functions.
> >> (*2) Here, the entire GPIOB port is explicitly requested by the GPIO
> >> module, i.e. all pins of the port are made available as GPIOs.
> >
> > So I think all you're looking for is a way in DT to represent GPIO
> > ranges? I don't think that should be by string name, but rather numbers:
> >
> > (actually, doesn't pinctrl-simple already have this?)
>
> Now I'm ever more confused ... we already have this :-)
>
> It's not even pinctrl-simple-centric it is completely generic.
> The code is in drivers/gpio/gpiolib-of.c.
>
> It was written by Shiraz Hashin and Haojian Zhuang.
> At the time I augmented the core code quite a bit to make
> a good fit.

I agree. Unluckily, it uses pinctrl-internal pin numbering which we
would have to make coherent with the physical pin numbers of the
individual packaging variants of the chip in order to expose them to
customers (see my previous mail at https://lkml.org/lkml/2013/5/22/207).

Adapting the kernel-internal pin numbering in function of the product
variant doesn't seem such a good idea to me: All pin groups etc. will
have to be redefined for every product, a huge source of bugs and
unnecessary static data within the drivers.

> This is from:
> Documentation/devicetree/bindings/gpio/gpio.txt
>
> 2.1) gpio-controller and pinctrl subsystem
> ------------------------------------------
>
> gpio-controller on a SOC might be tightly coupled with the pinctrl
> subsystem, in the sense that the pins can be used by other functions
> together with optional gpio feature.
>
> While the pin allocation is totally managed by the pin ctrl subsystem,
> gpio (under gpiolib) is still maintained by gpio drivers. It may happen
> that different pin ranges in a SoC is managed by different gpio drivers.
>
> This makes it logical to let gpio drivers announce their pin ranges to
> the pin ctrl subsystem and call 'pinctrl_request_gpio' in order to
> request the corresponding pin before any gpio usage.
>
> For this, the gpio controller can use a pinctrl phandle and pins to
> announce the pinrange to the pin ctrl subsystem. For example,
>
> qe_pio_e: gpio-controller@1460 {
> #gpio-cells = <2>;
> compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
> reg = <0x1460 0x18>;
> gpio-controller;
> gpio-ranges = <&pinctrl1 0 20 10>, <&pinctrl2 10 50 20>;
>
> }
>
> where,
> &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
>
> Next values specify the base pin and number of pins for the range
> handled by 'qe_pio_e' gpio. In the given example from base pin 20 to
> pin 29 under pinctrl1 with gpio offset 0 and pin 50 to pin 69 under
> pinctrl2 with gpio offset 10 is handled by this gpio controller.
>
> The pinctrl node must have "#gpio-range-cells" property to show number of
> arguments to pass with phandle from gpio controllers node.
>
> Yours,
> Linus Walleij

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-05-23 07:43:30

by Haojian Zhuang

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 22 May 2013 22:28, Christian Ruppert <[email protected]> wrote:
>
> On Mon, May 20, 2013 at 10:10:33AM +0200, Linus Walleij wrote:
> > On Thu, May 16, 2013 at 2:12 AM, Stephen Warren <[email protected]> wrote:
> > > On 05/10/2013 02:25 AM, Christian Ruppert wrote:
> >
> > >> (*1) TB100 GPIO ranges are defined as a phandle to the I/O function
> > >> which provides all pins of a given GPIO port. This function is not
> > >> necessarily requested from pinctrl and GPIO ports may overlap with
> > >> other functions. The pin controller knows about this and provides
> > >> whatever GPIO pin is available after mapping other requested
> > >> functions.
> > >> (*2) Here, the entire GPIOB port is explicitly requested by the GPIO
> > >> module, i.e. all pins of the port are made available as GPIOs.
> > >
> > > So I think all you're looking for is a way in DT to represent GPIO
> > > ranges? I don't think that should be by string name, but rather numbers:
> > >
> > > (actually, doesn't pinctrl-simple already have this?)
> >
> > Now I'm ever more confused ... we already have this :-)
> >
> > It's not even pinctrl-simple-centric it is completely generic.
> > The code is in drivers/gpio/gpiolib-of.c.
> >
> > It was written by Shiraz Hashin and Haojian Zhuang.
> > At the time I augmented the core code quite a bit to make
> > a good fit.
>
> I agree. Unluckily, it uses pinctrl-internal pin numbering which we
> would have to make coherent with the physical pin numbers of the
> individual packaging variants of the chip in order to expose them to
> customers (see my previous mail at https://lkml.org/lkml/2013/5/22/207).
>
> Adapting the kernel-internal pin numbering in function of the product
> variant doesn't seem such a good idea to me: All pin groups etc. will
> have to be redefined for every product, a huge source of bugs and
> unnecessary static data within the drivers.
>I review two methods you mentioned in this mail.

I think that you want to keep the logic simple. If so, I prefer you can
check pinctrl-single driver first. All pins are defined in DTS instead.

Here's the sample on Hi3620 SoC. And some pins even share one pinctrl
registers.

gpio controller:
gpio-ranges = < &pmx0 3 94 1 &pmx0 7 96 1>;
Each gpio pins in gpio controller could trace to the pin controller.

pin controller:
pinctrl-single,gpio-range = <&range 0 3 0 &range 3 9 1
&range 12 1 0 &range 13 29 1
&range 43 1 0 &range 44 49 1
&range 94 1 1 &range 96 2 1>;

range: gpio-range {
#pinctrl-single,gpio-range-cells = <3>;
};
If the pin could be configured as GPIO mode, we could trace to the
gpio controller by this table. We can see those pins are not continuous,
and it doesn't matter. And it could handle the relations between
multiple gpio controllers and multiple pin controllers.

These pin numbers are only used to bind GPIO number. Customers
needn't to know the details. The device driver only set the pins to
right mode (GPIO or any other) with right configuration. The device
driver even needn't know the GPIO number and the pin number.
Because everything is already handled. They only need the pinctrl
array. I prefer you to check drivers/tty/serial/amba-pl011.c.

In some SoC, both pin26 & pin104 could be configured as GPIO3.
We only need to define one in your DTS because only one could
be effect in the real world.

And we abstract pin number from the register offset. You also needn't
worry the pin name such as AA1, AB1. Those names are used to
communicate with hardware guys, not software guys.

If you want to redefine the pin groups because they're used as LCD pins,
not MMC pins any more. You needn't have to modify the pin arrays in
your pinctrl driver anymore. Because pinctrl-single driver don't define
any pin arrays. You can only define pin arrays in your DTS file.

Is it helpful to you?

Regards
Haojian

2013-05-24 09:20:34

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Wed, May 22, 2013 at 4:28 PM, Christian Ruppert
<[email protected]> wrote:
> On Mon, May 20, 2013 at 10:10:33AM +0200, Linus Walleij wrote:

>> It's not even pinctrl-simple-centric it is completely generic.
>> The code is in drivers/gpio/gpiolib-of.c.
>>
>> It was written by Shiraz Hashin and Haojian Zhuang.
>> At the time I augmented the core code quite a bit to make
>> a good fit.
>
> I agree. Unluckily, it uses pinctrl-internal pin numbering which we
> would have to make coherent with the physical pin numbers of the
> individual packaging variants of the chip in order to expose them to
> customers (see my previous mail at https://lkml.org/lkml/2013/5/22/207).

Again, the Linux pin numberspace is sparse, you can use whatever
pin number you like as long as it fits in a u32 and does not overlap
with other pins.

If you have a problem that the physical pin numbers and the
bank offsets or bits you need to poke or something doesn't
match, that is an issue for the *driver* not for the pin control
subsystem. However the pin control core may provide
cross-mapping helpers as discussed elsewhere, but the
pin control internal numbering is *not* an issue. Those are
just "some number" on a certain pin controller, use the number
from the datasheet if you like.

Yours,
Linus Walleij

2013-05-24 11:51:21

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

Hello Haojian,

On Thu, May 23, 2013 at 03:43:27PM +0800, Haojian Zhuang wrote:
> On 22 May 2013 22:28, Christian Ruppert <[email protected]> wrote:
> >
> > On Mon, May 20, 2013 at 10:10:33AM +0200, Linus Walleij wrote:
> > > On Thu, May 16, 2013 at 2:12 AM, Stephen Warren <[email protected]> wrote:
> > > > On 05/10/2013 02:25 AM, Christian Ruppert wrote:
> > >
> > > >> (*1) TB100 GPIO ranges are defined as a phandle to the I/O function
> > > >> which provides all pins of a given GPIO port. This function is not
> > > >> necessarily requested from pinctrl and GPIO ports may overlap with
> > > >> other functions. The pin controller knows about this and provides
> > > >> whatever GPIO pin is available after mapping other requested
> > > >> functions.
> > > >> (*2) Here, the entire GPIOB port is explicitly requested by the GPIO
> > > >> module, i.e. all pins of the port are made available as GPIOs.
> > > >
> > > > So I think all you're looking for is a way in DT to represent GPIO
> > > > ranges? I don't think that should be by string name, but rather numbers:
> > > >
> > > > (actually, doesn't pinctrl-simple already have this?)
> > >
> > > Now I'm ever more confused ... we already have this :-)
> > >
> > > It's not even pinctrl-simple-centric it is completely generic.
> > > The code is in drivers/gpio/gpiolib-of.c.
> > >
> > > It was written by Shiraz Hashin and Haojian Zhuang.
> > > At the time I augmented the core code quite a bit to make
> > > a good fit.
> >
> > I agree. Unluckily, it uses pinctrl-internal pin numbering which we
> > would have to make coherent with the physical pin numbers of the
> > individual packaging variants of the chip in order to expose them to
> > customers (see my previous mail at https://lkml.org/lkml/2013/5/22/207).
> >
> > Adapting the kernel-internal pin numbering in function of the product
> > variant doesn't seem such a good idea to me: All pin groups etc. will
> > have to be redefined for every product, a huge source of bugs and
> > unnecessary static data within the drivers.
> >I review two methods you mentioned in this mail.
>
> I think that you want to keep the logic simple. If so, I prefer you can
> check pinctrl-single driver first. All pins are defined in DTS instead.

Thanks for the hint. I haven't understood how to associate GPIOs to
other functions, however: Our hardware pin controller makes GPIO pins
available depending on the configuration of the non-GPIO interfaces.
This means that in many configurations, GPIO banks are only partially
available because some pins are used for other purposes. We can't expect
our customers to manually change the pin assignments in the device tree
in order to take this into account for every PCB.

Is there a way to make different pinmux functions mutually exclusive in
pinctrl-single, e.g. a pin is either a GPIO or part of an SPI interface?
Can the same thing be done for example to mux either SPI or I2C on the
same pins? If the answer to both questions is yes, we could predefine
all functions as we currently do and our pinctrl driver could be
entirely replaced with pinctrl-single.

> [...]

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-05-24 12:03:18

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Fri, May 24, 2013 at 11:20:31AM +0200, Linus Walleij wrote:
> On Wed, May 22, 2013 at 4:28 PM, Christian Ruppert
> <[email protected]> wrote:
> > On Mon, May 20, 2013 at 10:10:33AM +0200, Linus Walleij wrote:
>
> >> It's not even pinctrl-simple-centric it is completely generic.
> >> The code is in drivers/gpio/gpiolib-of.c.
> >>
> >> It was written by Shiraz Hashin and Haojian Zhuang.
> >> At the time I augmented the core code quite a bit to make
> >> a good fit.
> >
> > I agree. Unluckily, it uses pinctrl-internal pin numbering which we
> > would have to make coherent with the physical pin numbers of the
> > individual packaging variants of the chip in order to expose them to
> > customers (see my previous mail at https://lkml.org/lkml/2013/5/22/207).
>
> Again, the Linux pin numberspace is sparse, you can use whatever
> pin number you like as long as it fits in a u32 and does not overlap
> with other pins.
>
> If you have a problem that the physical pin numbers and the
> bank offsets or bits you need to poke or something doesn't
> match, that is an issue for the *driver* not for the pin control
> subsystem. However the pin control core may provide
> cross-mapping helpers as discussed elsewhere, but the
> pin control internal numbering is *not* an issue. Those are
> just "some number" on a certain pin controller, use the number
> from the datasheet if you like.

Understood. What I'm looking for is a way to support different products
with different physical pin numbering, ideally without changing Linux
internal pin numbers from one product variant under the constraints
highlighted in the thread you call "elsewhere".

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-05-26 15:49:23

by Haojian Zhuang

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 24 May 2013 19:50, Christian Ruppert <[email protected]> wrote:
> Hello Haojian,
>
> On Thu, May 23, 2013 at 03:43:27PM +0800, Haojian Zhuang wrote:
>> On 22 May 2013 22:28, Christian Ruppert <[email protected]> wrote:
>> >
>> > On Mon, May 20, 2013 at 10:10:33AM +0200, Linus Walleij wrote:
>> > > On Thu, May 16, 2013 at 2:12 AM, Stephen Warren <[email protected]> wrote:
>> > > > On 05/10/2013 02:25 AM, Christian Ruppert wrote:
>> > >
>> > > >> (*1) TB100 GPIO ranges are defined as a phandle to the I/O function
>> > > >> which provides all pins of a given GPIO port. This function is not
>> > > >> necessarily requested from pinctrl and GPIO ports may overlap with
>> > > >> other functions. The pin controller knows about this and provides
>> > > >> whatever GPIO pin is available after mapping other requested
>> > > >> functions.
>> > > >> (*2) Here, the entire GPIOB port is explicitly requested by the GPIO
>> > > >> module, i.e. all pins of the port are made available as GPIOs.
>> > > >
>> > > > So I think all you're looking for is a way in DT to represent GPIO
>> > > > ranges? I don't think that should be by string name, but rather numbers:
>> > > >
>> > > > (actually, doesn't pinctrl-simple already have this?)
>> > >
>> > > Now I'm ever more confused ... we already have this :-)
>> > >
>> > > It's not even pinctrl-simple-centric it is completely generic.
>> > > The code is in drivers/gpio/gpiolib-of.c.
>> > >
>> > > It was written by Shiraz Hashin and Haojian Zhuang.
>> > > At the time I augmented the core code quite a bit to make
>> > > a good fit.
>> >
>> > I agree. Unluckily, it uses pinctrl-internal pin numbering which we
>> > would have to make coherent with the physical pin numbers of the
>> > individual packaging variants of the chip in order to expose them to
>> > customers (see my previous mail at https://lkml.org/lkml/2013/5/22/207).
>> >
>> > Adapting the kernel-internal pin numbering in function of the product
>> > variant doesn't seem such a good idea to me: All pin groups etc. will
>> > have to be redefined for every product, a huge source of bugs and
>> > unnecessary static data within the drivers.
>> >I review two methods you mentioned in this mail.
>>
>> I think that you want to keep the logic simple. If so, I prefer you can
>> check pinctrl-single driver first. All pins are defined in DTS instead.
>
> Thanks for the hint. I haven't understood how to associate GPIOs to
> other functions, however: Our hardware pin controller makes GPIO pins
> available depending on the configuration of the non-GPIO interfaces.
> This means that in many configurations, GPIO banks are only partially

They're multiple function pins. You can find those pins in most modern SoCs.

> available because some pins are used for other purposes. We can't expect
> our customers to manually change the pin assignments in the device tree
> in order to take this into account for every PCB.

Yes, you need to define the gpio-range & pinctrl-single,gpio-range.
If you define them in your DTSI file, customer only need to include it.
If you define them in your DTS file, customer only need to copy them into
their DTS file.

If you don't have the requirements of routing multiple pins to the same GPIO,
I suggest you to define them in your DTSI file.

>
> Is there a way to make different pinmux functions mutually exclusive in
> pinctrl-single, e.g. a pin is either a GPIO or part of an SPI interface?

gpio_request() could help you to request GPIO if the pin isn't used yet.
And even your pin is in I2C mode without usage.

But if you want to the mutually exclusive, you could do by this way.

1) Function is only in GPIO mode.
In uart node, GPIO is function 1.
pinctrl-0 = <&uart1_pmx_func>;
uart1_pmx_func: uart1_pmx_func {
pinctrl-single,pins = <0x104 0x1>;
};

Then you can't switch to SPI interface, unless you want to switch pin state.

2) Function is only in UART mode.
You can hack gpio function in pinctrl-single,gpio-range property. i.e. you
always set gpio function as UART mode by hack.

But I still don't understand why you need this feature.

> Can the same thing be done for example to mux either SPI or I2C on the
> same pins?

Are you using the develop board that one pin may be routed to multiple
functions? And you can choose SPI or I2C by switch.

It means that the sames pin are shared between SPI and I2C driver. Either
SPI driver gets this pin, or I2C driver gets this pin. Only one driver could
get the pins even you don't use pinctrl-single driver. It likes GPIO.

SPI & I2C driver are always enabled in your kernel image. So you're enabling
different devices with different hardware configuration, and you need to
prepare two DTS files. Your boot loader should find which
hardware configuration is enabled & loaded the right DTS file.
So different pinmux settings are written in these two DTS files.

>
>> [...]
>
> Greetings,
> Christian
>
> --
> Christian Ruppert , <[email protected]>
> /|
> Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
> _// | bilis Systems CH-1228 Plan-les-Ouates

2013-05-29 12:21:08

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Fri, May 24, 2013 at 1:50 PM, Christian Ruppert
<[email protected]> wrote:

> I haven't understood how to associate GPIOs to
> other functions, however: Our hardware pin controller makes GPIO pins
> available depending on the configuration of the non-GPIO interfaces.
> This means that in many configurations, GPIO banks are only partially
> available because some pins are used for other purposes. We can't expect
> our customers to manually change the pin assignments in the device tree
> in order to take this into account for every PCB.

But what is it your customers do when customizing a board then?

Part of the promise of the device tree is to make it easy for downstream
users to customize the kernel, *especially* for different boards/systems
using the same SoC, for example. It is very much intended as a
customization tools for embedded developers getting boards from
a chipset vendor.

Maybe I don't understand what is meant by changing the pin
assignments above ...

Yours,
Linus Walleij

2013-06-03 09:42:33

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Wed, May 29, 2013 at 02:21:05PM +0200, Linus Walleij wrote:
> On Fri, May 24, 2013 at 1:50 PM, Christian Ruppert
> <[email protected]> wrote:
>
> > I haven't understood how to associate GPIOs to
> > other functions, however: Our hardware pin controller makes GPIO pins
> > available depending on the configuration of the non-GPIO interfaces.
> > This means that in many configurations, GPIO banks are only partially
> > available because some pins are used for other purposes. We can't expect
> > our customers to manually change the pin assignments in the device tree
> > in order to take this into account for every PCB.
>
> But what is it your customers do when customizing a board then?

Ideally, they just select the pin functions they would like to use on
their PCB using the phandle mechanism outlined in
Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt.
We will prepare the nodes to point to in our SOC .dtsi files. Obviously,
customers will look at these files, in particular the nodes they point
to. The simpler these nodes are (and the easier to understand) the
better.

> Part of the promise of the device tree is to make it easy for downstream
> users to customize the kernel, *especially* for different boards/systems
> using the same SoC, for example. It is very much intended as a
> customization tools for embedded developers getting boards from
> a chipset vendor.

Yes, this is our understanding as well. This is why we would like to
avoid confusion through the exposure of obscure number spaces, even
if a customer is not supposed to modify them.

Ease of use is also the reason why I added the gpio-base property to the
original driver: Finding out the global GPIO number to use in
/sys/class/gpio for a given GPIO of a given bank seems to be a recurring
headache for our customers and the definition of the bank's base number
in the device tree is an attempt to improve this situation. I am looking
forward to the patch you said Alexandre is working on which will
probably be a much better solution.

> Maybe I don't understand what is meant by changing the pin
> assignments above ...

Every pin can have exactly one function at a time and is thus assigned
to that function. Ideally, conflicts are cleanly managed in the pinmux
driver and an error message is generated so customers can understand why
something doesn't work and how to fix it.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-03 12:30:59

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Sun, May 26, 2013 at 11:49:20PM +0800, Haojian Zhuang wrote:
> On 24 May 2013 19:50, Christian Ruppert <[email protected]> wrote:
> > Hello Haojian,
> >
> > On Thu, May 23, 2013 at 03:43:27PM +0800, Haojian Zhuang wrote:
> >> On 22 May 2013 22:28, Christian Ruppert <[email protected]> wrote:
> >> >
> >> > On Mon, May 20, 2013 at 10:10:33AM +0200, Linus Walleij wrote:
> >> > > On Thu, May 16, 2013 at 2:12 AM, Stephen Warren <[email protected]> wrote:
> >> > > > On 05/10/2013 02:25 AM, Christian Ruppert wrote:
> [...]
> >> I think that you want to keep the logic simple. If so, I prefer you can
> >> check pinctrl-single driver first. All pins are defined in DTS instead.
> >
> > Thanks for the hint. I haven't understood how to associate GPIOs to
> > other functions, however: Our hardware pin controller makes GPIO pins
> > available depending on the configuration of the non-GPIO interfaces.
> > This means that in many configurations, GPIO banks are only partially
>
> They're multiple function pins. You can find those pins in most modern SoCs.
>
> > available because some pins are used for other purposes. We can't expect
> > our customers to manually change the pin assignments in the device tree
> > in order to take this into account for every PCB.
>
> Yes, you need to define the gpio-range & pinctrl-single,gpio-range.
> If you define them in your DTSI file, customer only need to include it.
> If you define them in your DTS file, customer only need to copy them into
> their DTS file.
>
> If you don't have the requirements of routing multiple pins to the same GPIO,
> I suggest you to define them in your DTSI file.

OK, here's a simplified example of what we would like to do (this seems
pretty common so I suppose there is a way I haven't understood). Our
situation is slightly more complex but for the purpose of discussion
let's assume a chip with 8 pins which can be configured for the
following functions:

Pin GPIO-A I2C SPI0 SPI1
------------------------------------
1 GPIOA0 SDA MISO1
2 GPIOA1 SCL MOSI1
3 GPIOA2 SS1_B
4 GPIOA3 SCLK1
5 GPIOA4 MISO0
6 GPIOA5 MOSI0
7 GPIOA6 SS0_B
8 GPIOA7 SCLK0

We can now define the following pinctrl-single:

pinmux: pinmux@0xFFEE0000 {
compatible = "pinctrl-single";
reg = <0xFFEE0000 0x8>;
#address-cells = <1>;
#size-cells = <0>;
#gpio-range-cells = <3>;
pinctrl-single,register-width = <32>;
pinctrl-single,function-mask = <0xffffffff>;
pinctrl-single,gpio-range = <&range 1 8 0>;
gpioa_pins: pinmux_gpioa_pins {
pinctrl-single,pins = <0x0 0 0x4 0>
};
i2c_pins: pinmux_i2c_pins {
pinctrl-single,pins = <0x0 1>
};
spi0_pins: pinmux_spi0_pins {
pinctrl-single,pins = <0x1 1>
};
spi1_pins: pinmux_spi1_pins {
pinctrl-single,pins = <0x0 2>
};
range: gpio-range {
#pinctrl-single,gpio-range-cells = <3>;
};
};
gpioa: gpio_a {
/* ... */
gpio-controller;
gpio-ranges = <&pinmux 0 0 8>;
};

How do I tell pinctrl-single that:
1. I2C and SPI1 cannot be selected at the same time?
2. In case I2C is selected, GPIOA0 and GPIOA1 cannot be requested but
GPIOA2 and GPIOA3 are available?
3. In case SPI1 is selected GPIOA0-GPIOA3 are not available?
4. In case SPI0 is selected GPIOA4-GPIOA7 are not available?

> > Is there a way to make different pinmux functions mutually exclusive in
> > pinctrl-single, e.g. a pin is either a GPIO or part of an SPI interface?
>
> gpio_request() could help you to request GPIO if the pin isn't used yet.
> And even your pin is in I2C mode without usage.
>
> But if you want to the mutually exclusive, you could do by this way.
>
> 1) Function is only in GPIO mode.
> In uart node, GPIO is function 1.
> pinctrl-0 = <&uart1_pmx_func>;
> uart1_pmx_func: uart1_pmx_func {
> pinctrl-single,pins = <0x104 0x1>;
> };
>
> Then you can't switch to SPI interface, unless you want to switch pin state.
>
> 2) Function is only in UART mode.
> You can hack gpio function in pinctrl-single,gpio-range property. i.e. you
> always set gpio function as UART mode by hack.
>
> But I still don't understand why you need this feature.
>
> > Can the same thing be done for example to mux either SPI or I2C on the
> > same pins?
>
> Are you using the develop board that one pin may be routed to multiple
> functions? And you can choose SPI or I2C by switch.

We do have development boards to which the customer can connect their
own peripherals (and adapt the device tree accordingly). We are also
looking for a comprehensive way to configure the I/Os even for customers
designing their own boards.

> It means that the sames pin are shared between SPI and I2C driver. Either
> SPI driver gets this pin, or I2C driver gets this pin. Only one driver could
> get the pins even you don't use pinctrl-single driver. It likes GPIO.

Exactly. This is what I'm wondering about in the example above. What
must I do to get a clear error message in case someone by mistake tries
the following in the above example:

spi0: tb10x_spi0 {
/* ... */
pinctrl-names = "default";
pinctrl-0 = <&spi0_pins>;
};
i2c: tb10x_i2c {
/* ... */
pinctrl-names = "default";
pinctrl-0 = <&i2c_pins>;
};

And the following:

i2c: tb10x_i2c {
/* ... */
pinctrl-names = "default";
pinctrl-0 = <&i2c_pins>;
};
some_external_component: ext_comp {
/* ... */
gpios = <&gpioa 0>;
};

> SPI & I2C driver are always enabled in your kernel image. So you're enabling
> different devices with different hardware configuration, and you need to
> prepare two DTS files. Your boot loader should find which
> hardware configuration is enabled & loaded the right DTS file.
> So different pinmux settings are written in these two DTS files.

Exactly. In addition, even pinmux modes which are not used in a given
board are still defined in our SOC .dtsi file.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-05 01:44:30

by Haojian Zhuang

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 3 June 2013 20:30, Christian Ruppert <[email protected]> wrote:
> OK, here's a simplified example of what we would like to do (this seems
> pretty common so I suppose there is a way I haven't understood). Our
> situation is slightly more complex but for the purpose of discussion
> let's assume a chip with 8 pins which can be configured for the
> following functions:
>
> Pin GPIO-A I2C SPI0 SPI1
> ------------------------------------
> 1 GPIOA0 SDA MISO1
> 2 GPIOA1 SCL MOSI1
> 3 GPIOA2 SS1_B
> 4 GPIOA3 SCLK1
> 5 GPIOA4 MISO0
> 6 GPIOA5 MOSI0
> 7 GPIOA6 SS0_B
> 8 GPIOA7 SCLK0
>
> We can now define the following pinctrl-single:
>
> pinmux: pinmux@0xFFEE0000 {
> compatible = "pinctrl-single";
> reg = <0xFFEE0000 0x8>;
> #address-cells = <1>;
> #size-cells = <0>;
> #gpio-range-cells = <3>;
> pinctrl-single,register-width = <32>;
> pinctrl-single,function-mask = <0xffffffff>;
> pinctrl-single,gpio-range = <&range 1 8 0>;
> gpioa_pins: pinmux_gpioa_pins {
> pinctrl-single,pins = <0x0 0 0x4 0>
> };
> i2c_pins: pinmux_i2c_pins {
> pinctrl-single,pins = <0x0 1>
> };
> spi0_pins: pinmux_spi0_pins {
> pinctrl-single,pins = <0x1 1>
<0x1 1>?

If each pinmux register is only for one pin in your SoC.
I think that your definitions are wrong above. We use
register offset as the first argument, not pin number.
And the second argument should be pin function number.

If multiple pins are sharing one register with different bits,
you need to enable "pinctrl-single,bit-per-mux".

> };
> spi1_pins: pinmux_spi1_pins {
> pinctrl-single,pins = <0x0 2>
> };
> range: gpio-range {
> #pinctrl-single,gpio-range-cells = <3>;
> };
> };
> gpioa: gpio_a {
> /* ... */
> gpio-controller;
> gpio-ranges = <&pinmux 0 0 8>;
> };
>
> How do I tell pinctrl-single that:
> 1. I2C and SPI1 cannot be selected at the same time?
You needn't. If the pin is gotten by I2C driver, SPI driver can't get
this pin any more.

> 2. In case I2C is selected, GPIOA0 and GPIOA1 cannot be requested but
> GPIOA2 and GPIOA3 are available?
I think that you have your own GPIO driver. At first, you need to
define .request()
& .free() in gpio chip.

If I2C function is only configured by bootloader & the pin isn't
controlled by any
driver, you can use gpio_request() directly to request this GPIO pin.

If the pin is controlled by I2C driver & you want to declare it as
GPIO function in
I2C driver, you can use gpio_request() directly. Then the pin function becomes
GPIO.

If the pin is controlled by I2C driver & you want to declare it as
GPIO function in
other driver, you'll meet failure on requesting this pin.

> 3. In case SPI1 is selected GPIOA0-GPIOA3 are not available?
Of course, you can set one pin with two function mode at the same time.

But you can switch it between SPI and GPIO mode in the same driver. I mentioned
it in #2 by details.

> 4. In case SPI0 is selected GPIOA4-GPIOA7 are not available?
Same #3.

>
>
> Exactly. This is what I'm wondering about in the example above. What
> must I do to get a clear error message in case someone by mistake tries
> the following in the above example:
>
> spi0: tb10x_spi0 {
> /* ... */
> pinctrl-names = "default";
> pinctrl-0 = <&spi0_pins>;
> };
> i2c: tb10x_i2c {
> /* ... */
> pinctrl-names = "default";
> pinctrl-0 = <&i2c_pins>;
> };
>
> And the following:
>
> i2c: tb10x_i2c {
> /* ... */
> pinctrl-names = "default";
> pinctrl-0 = <&i2c_pins>;
> };
> some_external_component: ext_comp {
> /* ... */
> gpios = <&gpioa 0>;
> };

Which kind of error message do you need? If you're concerning on pin conflict,
you'll get it while kernel is running.

2013-06-06 14:11:47

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Wed, Jun 05, 2013 at 09:44:27AM +0800, Haojian Zhuang wrote:
> On 3 June 2013 20:30, Christian Ruppert <[email protected]> wrote:
> > OK, here's a simplified example of what we would like to do (this seems
> > pretty common so I suppose there is a way I haven't understood). Our
> > situation is slightly more complex but for the purpose of discussion
> > let's assume a chip with 8 pins which can be configured for the
> > following functions:
> >
> > Pin GPIO-A I2C SPI0 SPI1
> > ------------------------------------
> > 1 GPIOA0 SDA MISO1
> > 2 GPIOA1 SCL MOSI1
> > 3 GPIOA2 SS1_B
> > 4 GPIOA3 SCLK1
> > 5 GPIOA4 MISO0
> > 6 GPIOA5 MOSI0
> > 7 GPIOA6 SS0_B
> > 8 GPIOA7 SCLK0
> >
> > We can now define the following pinctrl-single:
> >
> > pinmux: pinmux@0xFFEE0000 {
> > compatible = "pinctrl-single";
> > reg = <0xFFEE0000 0x8>;
> > #address-cells = <1>;
> > #size-cells = <0>;
> > #gpio-range-cells = <3>;
> > pinctrl-single,register-width = <32>;
> > pinctrl-single,function-mask = <0xffffffff>;
> > pinctrl-single,gpio-range = <&range 1 8 0>;
> > gpioa_pins: pinmux_gpioa_pins {
> > pinctrl-single,pins = <0x0 0 0x4 0>
> > };
> > i2c_pins: pinmux_i2c_pins {
> > pinctrl-single,pins = <0x0 1>
> > };
> > spi0_pins: pinmux_spi0_pins {
> > pinctrl-single,pins = <0x1 1>
> <0x1 1>?
>
> If each pinmux register is only for one pin in your SoC.
> I think that your definitions are wrong above. We use
> register offset as the first argument, not pin number.
> And the second argument should be pin function number.

In our case each pinmux register (bit field) actually controls an entire
group of pins.

> If multiple pins are sharing one register with different bits,
> you need to enable "pinctrl-single,bit-per-mux".

Multiple pins are sharing the same bits in the same register. Do you
think this prevents us from using pinctrl-single?

> > };
> > spi1_pins: pinmux_spi1_pins {
> > pinctrl-single,pins = <0x0 2>
> > };
> > range: gpio-range {
> > #pinctrl-single,gpio-range-cells = <3>;
> > };
> > };
> > gpioa: gpio_a {
> > /* ... */
> > gpio-controller;
> > gpio-ranges = <&pinmux 0 0 8>;
> > };
> >
> > How do I tell pinctrl-single that:
> > 1. I2C and SPI1 cannot be selected at the same time?
> You needn't. If the pin is gotten by I2C driver, SPI driver can't get
> this pin any more.

OK

> > 2. In case I2C is selected, GPIOA0 and GPIOA1 cannot be requested but
> > GPIOA2 and GPIOA3 are available?
> I think that you have your own GPIO driver. At first, you need to
> define .request()
> & .free() in gpio chip.
>
> If I2C function is only configured by bootloader & the pin isn't
> controlled by any
> driver, you can use gpio_request() directly to request this GPIO pin.
>
> If the pin is controlled by I2C driver & you want to declare it as
> GPIO function in
> I2C driver, you can use gpio_request() directly. Then the pin function becomes
> GPIO.
>
> If the pin is controlled by I2C driver & you want to declare it as
> GPIO function in
> other driver, you'll meet failure on requesting this pin.

This seems to be an issue for us. I think we'll keep pinctrl-single in
mind for the evaluation phase of future chips but in production we
definitely need to be able to use e.g. GPIOA2 from other drivers than
the I2C bus driver.

> > 3. In case SPI1 is selected GPIOA0-GPIOA3 are not available?
> Of course, you can set one pin with two function mode at the same time.
>
> But you can switch it between SPI and GPIO mode in the same driver. I mentioned
> it in #2 by details.

OK

> > 4. In case SPI0 is selected GPIOA4-GPIOA7 are not available?
> Same #3.

OK

> > Exactly. This is what I'm wondering about in the example above. What
> > must I do to get a clear error message in case someone by mistake tries
> > the following in the above example:
> >
> > spi0: tb10x_spi0 {
> > /* ... */
> > pinctrl-names = "default";
> > pinctrl-0 = <&spi0_pins>;
> > };
> > i2c: tb10x_i2c {
> > /* ... */
> > pinctrl-names = "default";
> > pinctrl-0 = <&i2c_pins>;
> > };
> >
> > And the following:
> >
> > i2c: tb10x_i2c {
> > /* ... */
> > pinctrl-names = "default";
> > pinctrl-0 = <&i2c_pins>;
> > };
> > some_external_component: ext_comp {
> > /* ... */
> > gpios = <&gpioa 0>;
> > };
>
> Which kind of error message do you need? If you're concerning on pin conflict,
> you'll get it while kernel is running.

OK

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-06 14:32:24

by Haojian Zhuang

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 6 June 2013 22:11, Christian Ruppert <[email protected]> wrote:
> On Wed, Jun 05, 2013 at 09:44:27AM +0800, Haojian Zhuang wrote:
>> On 3 June 2013 20:30, Christian Ruppert <[email protected]> wrote:
>> > OK, here's a simplified example of what we would like to do (this seems
>> > pretty common so I suppose there is a way I haven't understood). Our
>> > situation is slightly more complex but for the purpose of discussion
>> > let's assume a chip with 8 pins which can be configured for the
>> > following functions:
>> >
>> > Pin GPIO-A I2C SPI0 SPI1
>> > ------------------------------------
>> > 1 GPIOA0 SDA MISO1
>> > 2 GPIOA1 SCL MOSI1
>> > 3 GPIOA2 SS1_B
>> > 4 GPIOA3 SCLK1
>> > 5 GPIOA4 MISO0
>> > 6 GPIOA5 MOSI0
>> > 7 GPIOA6 SS0_B
>> > 8 GPIOA7 SCLK0
>> >
>> > We can now define the following pinctrl-single:
>> >
>> > pinmux: pinmux@0xFFEE0000 {
>> > compatible = "pinctrl-single";
>> > reg = <0xFFEE0000 0x8>;
>> > #address-cells = <1>;
>> > #size-cells = <0>;
>> > #gpio-range-cells = <3>;
>> > pinctrl-single,register-width = <32>;
>> > pinctrl-single,function-mask = <0xffffffff>;
>> > pinctrl-single,gpio-range = <&range 1 8 0>;
>> > gpioa_pins: pinmux_gpioa_pins {
>> > pinctrl-single,pins = <0x0 0 0x4 0>
>> > };
>> > i2c_pins: pinmux_i2c_pins {
>> > pinctrl-single,pins = <0x0 1>
>> > };
>> > spi0_pins: pinmux_spi0_pins {
>> > pinctrl-single,pins = <0x1 1>
>> <0x1 1>?
>>
>> If each pinmux register is only for one pin in your SoC.
>> I think that your definitions are wrong above. We use
>> register offset as the first argument, not pin number.
>> And the second argument should be pin function number.
>
> In our case each pinmux register (bit field) actually controls an entire
> group of pins.
>
>> If multiple pins are sharing one register with different bits,
>> you need to enable "pinctrl-single,bit-per-mux".
>
> Multiple pins are sharing the same bits in the same register. Do you
> think this prevents us from using pinctrl-single?
>
Could you give me your register definition? Then I can understand you
better.

2013-06-06 15:30:58

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Thu, Jun 06, 2013 at 10:32:21PM +0800, Haojian Zhuang wrote:
> On 6 June 2013 22:11, Christian Ruppert <[email protected]> wrote:
> > On Wed, Jun 05, 2013 at 09:44:27AM +0800, Haojian Zhuang wrote:
> >> On 3 June 2013 20:30, Christian Ruppert <[email protected]> wrote:
> >> > OK, here's a simplified example of what we would like to do (this seems
> >> > pretty common so I suppose there is a way I haven't understood). Our
> >> > situation is slightly more complex but for the purpose of discussion
> >> > let's assume a chip with 8 pins which can be configured for the
> >> > following functions:
> >> >
> >> > Pin GPIO-A I2C SPI0 SPI1
> >> > ------------------------------------
> >> > 1 GPIOA0 SDA MISO1
> >> > 2 GPIOA1 SCL MOSI1
> >> > 3 GPIOA2 SS1_B
> >> > 4 GPIOA3 SCLK1
> >> > 5 GPIOA4 MISO0
> >> > 6 GPIOA5 MOSI0
> >> > 7 GPIOA6 SS0_B
> >> > 8 GPIOA7 SCLK0
> >> >
> >> > We can now define the following pinctrl-single:
> >> >
> >> > pinmux: pinmux@0xFFEE0000 {
> >> > compatible = "pinctrl-single";
> >> > reg = <0xFFEE0000 0x8>;
> >> > #address-cells = <1>;
> >> > #size-cells = <0>;
> >> > #gpio-range-cells = <3>;
> >> > pinctrl-single,register-width = <32>;
> >> > pinctrl-single,function-mask = <0xffffffff>;
> >> > pinctrl-single,gpio-range = <&range 1 8 0>;
> >> > gpioa_pins: pinmux_gpioa_pins {
> >> > pinctrl-single,pins = <0x0 0 0x4 0>
> >> > };
> >> > i2c_pins: pinmux_i2c_pins {
> >> > pinctrl-single,pins = <0x0 1>
> >> > };
> >> > spi0_pins: pinmux_spi0_pins {
> >> > pinctrl-single,pins = <0x1 1>
> >> <0x1 1>?
> >>
> >> If each pinmux register is only for one pin in your SoC.
> >> I think that your definitions are wrong above. We use
> >> register offset as the first argument, not pin number.
> >> And the second argument should be pin function number.
> >
> > In our case each pinmux register (bit field) actually controls an entire
> > group of pins.
> >
> >> If multiple pins are sharing one register with different bits,
> >> you need to enable "pinctrl-single,bit-per-mux".
> >
> > Multiple pins are sharing the same bits in the same register. Do you
> > think this prevents us from using pinctrl-single?
> >
> Could you give me your register definition? Then I can understand you
> better.

In our example, the register map would look a bit like the following.
Note that every register configures four pins at a time.

Register 0x0:
Mode GPIO-A I2C SPI1
Value 0x0 0x1 0x2
---------------------------
Pin1 GPIOA0 SDA MISO1
Pin2 GPIOA1 SCL MOSI1
Pin3 GPIOA2 SS1_B
Pin4 GPIOA3 SCLK1

Register 0x4:
Mode GPIO-A SPI0
Value 0x0 0x1
---------------------
Pin5 GPIOA4 MISO0
Pin6 GPIOA5 MOSI0
Pin7 GPIOA6 SS0_B
Pin8 GPIOA7 SCLK0

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-07 00:01:00

by Haojian Zhuang

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 6 June 2013 23:30, Christian Ruppert <[email protected]> wrote:
> On Thu, Jun 06, 2013 at 10:32:21PM +0800, Haojian Zhuang wrote:
>> On 6 June 2013 22:11, Christian Ruppert <[email protected]> wrote:
>> > On Wed, Jun 05, 2013 at 09:44:27AM +0800, Haojian Zhuang wrote:
>> >> On 3 June 2013 20:30, Christian Ruppert <[email protected]> wrote:
>> >> > OK, here's a simplified example of what we would like to do (this seems
>> >> > pretty common so I suppose there is a way I haven't understood). Our
>> >> > situation is slightly more complex but for the purpose of discussion
>> >> > let's assume a chip with 8 pins which can be configured for the
>> >> > following functions:
>> >> >
>> >> > Pin GPIO-A I2C SPI0 SPI1
>> >> > ------------------------------------
>> >> > 1 GPIOA0 SDA MISO1
>> >> > 2 GPIOA1 SCL MOSI1
>> >> > 3 GPIOA2 SS1_B
>> >> > 4 GPIOA3 SCLK1
>> >> > 5 GPIOA4 MISO0
>> >> > 6 GPIOA5 MOSI0
>> >> > 7 GPIOA6 SS0_B
>> >> > 8 GPIOA7 SCLK0
>> >> >
>> >> > We can now define the following pinctrl-single:
>> >> >
>> >> > pinmux: pinmux@0xFFEE0000 {
>> >> > compatible = "pinctrl-single";
>> >> > reg = <0xFFEE0000 0x8>;
>> >> > #address-cells = <1>;
>> >> > #size-cells = <0>;
>> >> > #gpio-range-cells = <3>;
>> >> > pinctrl-single,register-width = <32>;
>> >> > pinctrl-single,function-mask = <0xffffffff>;
>> >> > pinctrl-single,gpio-range = <&range 1 8 0>;
>> >> > gpioa_pins: pinmux_gpioa_pins {
>> >> > pinctrl-single,pins = <0x0 0 0x4 0>
>> >> > };
>> >> > i2c_pins: pinmux_i2c_pins {
>> >> > pinctrl-single,pins = <0x0 1>
>> >> > };
>> >> > spi0_pins: pinmux_spi0_pins {
>> >> > pinctrl-single,pins = <0x1 1>
>> >> <0x1 1>?
>> >>
>> >> If each pinmux register is only for one pin in your SoC.
>> >> I think that your definitions are wrong above. We use
>> >> register offset as the first argument, not pin number.
>> >> And the second argument should be pin function number.
>> >
>> > In our case each pinmux register (bit field) actually controls an entire
>> > group of pins.
>> >
>> >> If multiple pins are sharing one register with different bits,
>> >> you need to enable "pinctrl-single,bit-per-mux".
>> >
>> > Multiple pins are sharing the same bits in the same register. Do you
>> > think this prevents us from using pinctrl-single?
>> >
>> Could you give me your register definition? Then I can understand you
>> better.
>
> In our example, the register map would look a bit like the following.
> Note that every register configures four pins at a time.
>
> Register 0x0:
> Mode GPIO-A I2C SPI1
> Value 0x0 0x1 0x2
> ---------------------------
> Pin1 GPIOA0 SDA MISO1
> Pin2 GPIOA1 SCL MOSI1
> Pin3 GPIOA2 SS1_B
> Pin4 GPIOA3 SCLK1
>
> Register 0x4:
> Mode GPIO-A SPI0
> Value 0x0 0x1
> ---------------------
> Pin5 GPIOA4 MISO0
> Pin6 GPIOA5 MOSI0
> Pin7 GPIOA6 SS0_B
> Pin8 GPIOA7 SCLK0
>

You said "Multiple pins are sharing the same bits in the same register.".
I need to understand which bits you're talking about in your register.

Regards
Haojian

2013-06-07 11:33:46

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Fri, Jun 07, 2013 at 08:00:57AM +0800, Haojian Zhuang wrote:
> On 6 June 2013 23:30, Christian Ruppert <[email protected]> wrote:
> > On Thu, Jun 06, 2013 at 10:32:21PM +0800, Haojian Zhuang wrote:
> >> On 6 June 2013 22:11, Christian Ruppert <[email protected]> wrote:
> >> > On Wed, Jun 05, 2013 at 09:44:27AM +0800, Haojian Zhuang wrote:
> >> >> On 3 June 2013 20:30, Christian Ruppert <[email protected]> wrote:
> >> >> > OK, here's a simplified example of what we would like to do (this seems
> >> >> > pretty common so I suppose there is a way I haven't understood). Our
> >> >> > situation is slightly more complex but for the purpose of discussion
> >> >> > let's assume a chip with 8 pins which can be configured for the
> >> >> > following functions:
> >> >> >
> >> >> > Pin GPIO-A I2C SPI0 SPI1
> >> >> > ------------------------------------
> >> >> > 1 GPIOA0 SDA MISO1
> >> >> > 2 GPIOA1 SCL MOSI1
> >> >> > 3 GPIOA2 SS1_B
> >> >> > 4 GPIOA3 SCLK1
> >> >> > 5 GPIOA4 MISO0
> >> >> > 6 GPIOA5 MOSI0
> >> >> > 7 GPIOA6 SS0_B
> >> >> > 8 GPIOA7 SCLK0
> >> >> >
> >> >> > We can now define the following pinctrl-single:
> >> >> >
> >> >> > pinmux: pinmux@0xFFEE0000 {
> >> >> > compatible = "pinctrl-single";
> >> >> > reg = <0xFFEE0000 0x8>;
> >> >> > #address-cells = <1>;
> >> >> > #size-cells = <0>;
> >> >> > #gpio-range-cells = <3>;
> >> >> > pinctrl-single,register-width = <32>;
> >> >> > pinctrl-single,function-mask = <0xffffffff>;
> >> >> > pinctrl-single,gpio-range = <&range 1 8 0>;
> >> >> > gpioa_pins: pinmux_gpioa_pins {
> >> >> > pinctrl-single,pins = <0x0 0 0x4 0>
> >> >> > };
> >> >> > i2c_pins: pinmux_i2c_pins {
> >> >> > pinctrl-single,pins = <0x0 1>
> >> >> > };
> >> >> > spi0_pins: pinmux_spi0_pins {
> >> >> > pinctrl-single,pins = <0x1 1>
> >> >> <0x1 1>?
> >> >>
> >> >> If each pinmux register is only for one pin in your SoC.
> >> >> I think that your definitions are wrong above. We use
> >> >> register offset as the first argument, not pin number.
> >> >> And the second argument should be pin function number.
> >> >
> >> > In our case each pinmux register (bit field) actually controls an entire
> >> > group of pins.
> >> >
> >> >> If multiple pins are sharing one register with different bits,
> >> >> you need to enable "pinctrl-single,bit-per-mux".
> >> >
> >> > Multiple pins are sharing the same bits in the same register. Do you
> >> > think this prevents us from using pinctrl-single?
> >> >
> >> Could you give me your register definition? Then I can understand you
> >> better.
> >
> > In our example, the register map would look a bit like the following.
> > Note that every register configures four pins at a time.
> >
> > Register 0x0:
> > Mode GPIO-A I2C SPI1
> > Value 0x0 0x1 0x2
> > ---------------------------
> > Pin1 GPIOA0 SDA MISO1
> > Pin2 GPIOA1 SCL MOSI1
> > Pin3 GPIOA2 SS1_B
> > Pin4 GPIOA3 SCLK1
> >
> > Register 0x4:
> > Mode GPIO-A SPI0
> > Value 0x0 0x1
> > ---------------------
> > Pin5 GPIOA4 MISO0
> > Pin6 GPIOA5 MOSI0
> > Pin7 GPIOA6 SS0_B
> > Pin8 GPIOA7 SCLK0
> >
>
> You said "Multiple pins are sharing the same bits in the same register.".
> I need to understand which bits you're talking about in your register.

In the above example, bits 0 and 1 of register 0x0 control pins 1
through 4 and bit 0 of register 0x4 controls pins 5 through 8. The
moment you write a new value in either of those registers, all four pins
will change functionality simultaneously. There is no way to control the
functionality of each pin individually.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-07 11:36:23

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Mon, Jun 3, 2013 at 11:42 AM, Christian Ruppert
<[email protected]> wrote:

> Ease of use is also the reason why I added the gpio-base property to the
> original driver: Finding out the global GPIO number to use in
> /sys/class/gpio for a given GPIO of a given bank seems to be a recurring
> headache for our customers and the definition of the bank's base number
> in the device tree is an attempt to improve this situation.

What you need to do in that case is to find a way to name the pins
in sysfs (creating symbolic links with the GPIO pin name) so they
can use these names in sysfs instead.

There is no ambition from my side to try to correlate the
GPIO sysfs interface and device trees. This is because the GPIO
sysfs is not universally liked. And when you say you want to make the
sysfs ABI easy to use that lights a big red light on my panel. I will
explain why.

What is very important is that your customers understand that
the GPIO sysfs shall not be used for things like:

- LEDs
- Switches
- Regulators
- Camera muxes
- etc

>From the kernel community we have tried (or atleast I have tried)
that this kind of hardware shall be handled by the apropriate linux
subsystems, and not by obscure userspace code.

In a fight between device tree and GPIO sysfs device tree
*wins*.

Consider this example from the Snowball device tree:

en_3v3_reg: en_3v3 {
compatible = "regulator-fixed";
regulator-name = "en-3v3-fixed-supply";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpios = <&gpio0 26 0x4>; // 26
startup-delay-us = <5000>;
enable-active-high;
};

gpio_keys {
compatible = "gpio-keys";
#address-cells = <1>;
#size-cells = <0>;

button@1 {
debounce_interval = <50>;
wakeup = <1>;
linux,code = <2>;
label = "userpb";
gpios = <&gpio1 0 0x4>;
};
button@2 {
debounce_interval = <50>;
wakeup = <1>;
linux,code = <3>;
label = "extkb1";
gpios = <&gpio4 23 0x4>;
};
button@3 {
debounce_interval = <50>;
wakeup = <1>;
linux,code = <4>;
label = "extkb2";
gpios = <&gpio4 24 0x4>;
};
button@4 {
debounce_interval = <50>;
wakeup = <1>;
linux,code = <5>;
label = "extkb3";
gpios = <&gpio5 1 0x4>;
};
button@5 {
debounce_interval = <50>;
wakeup = <1>;
linux,code = <6>;
label = "extkb4";
gpios = <&gpio5 2 0x4>;
};
};

leds {
compatible = "gpio-leds";
used-led {
label = "user_led";
gpios = <&gpio4 14 0x4>;
default-state = "on";
linux,default-trigger = "heartbeat";
};
};

As you immediately realize, if people don't know how to specify
the above and start writing a userspace daemon to do the same
thing by hammering on sysfs files, they are doing the wrong thing.

What you need to make sure is that before your customers start
to do userspace tricks in GPIO sysfs they need to answer the
question "why?".

If the GPIO sysfs is encouraging people to do things like the
above from userspace, it needs to be actively discouraged,
because that is hurting the people doing that.

Yours,
Linus Walleij

2013-06-07 13:35:16

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Fri, Jun 07, 2013 at 01:36:16PM +0200, Linus Walleij wrote:
> On Mon, Jun 3, 2013 at 11:42 AM, Christian Ruppert
> <[email protected]> wrote:
>
> > Ease of use is also the reason why I added the gpio-base property to the
> > original driver: Finding out the global GPIO number to use in
> > /sys/class/gpio for a given GPIO of a given bank seems to be a recurring
> > headache for our customers and the definition of the bank's base number
> > in the device tree is an attempt to improve this situation.
>
> What you need to do in that case is to find a way to name the pins
> in sysfs (creating symbolic links with the GPIO pin name) so they
> can use these names in sysfs instead.
>
> There is no ambition from my side to try to correlate the
> GPIO sysfs interface and device trees. This is because the GPIO
> sysfs is not universally liked. And when you say you want to make the
> sysfs ABI easy to use that lights a big red light on my panel. I will
> explain why.
>
> What is very important is that your customers understand that
> the GPIO sysfs shall not be used for things like:
>
> - LEDs
> - Switches
> - Regulators
> - Camera muxes
> - etc
>
> From the kernel community we have tried (or atleast I have tried)
> that this kind of hardware shall be handled by the apropriate linux
> subsystems, and not by obscure userspace code.

I totally agree with that and we already declare the LEDs etc. on our
PCBs in the device trees. However, we have at least one customer who has
a user space driver for some peripheral on i2c which needs to be reset
through GPIO. I'm not sure which framework to use for this sort of
applications.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-07 14:57:44

by Haojian Zhuang

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 7 June 2013 19:32, Christian Ruppert <[email protected]> wrote:
> On Fri, Jun 07, 2013 at 08:00:57AM +0800, Haojian Zhuang wrote:
>> On 6 June 2013 23:30, Christian Ruppert <[email protected]> wrote:
>> > On Thu, Jun 06, 2013 at 10:32:21PM +0800, Haojian Zhuang wrote:
>> >> On 6 June 2013 22:11, Christian Ruppert <[email protected]> wrote:
>> >> > On Wed, Jun 05, 2013 at 09:44:27AM +0800, Haojian Zhuang wrote:
>> >> >> On 3 June 2013 20:30, Christian Ruppert <[email protected]> wrote:
>> >> >> > OK, here's a simplified example of what we would like to do (this seems
>> >> >> > pretty common so I suppose there is a way I haven't understood). Our
>> >> >> > situation is slightly more complex but for the purpose of discussion
>> >> >> > let's assume a chip with 8 pins which can be configured for the
>> >> >> > following functions:
>> >> >> >
>> >> >> > Pin GPIO-A I2C SPI0 SPI1
>> >> >> > ------------------------------------
>> >> >> > 1 GPIOA0 SDA MISO1
>> >> >> > 2 GPIOA1 SCL MOSI1
>> >> >> > 3 GPIOA2 SS1_B
>> >> >> > 4 GPIOA3 SCLK1
>> >> >> > 5 GPIOA4 MISO0
>> >> >> > 6 GPIOA5 MOSI0
>> >> >> > 7 GPIOA6 SS0_B
>> >> >> > 8 GPIOA7 SCLK0
>> >> >> >
>> >> >> > We can now define the following pinctrl-single:
>> >> >> >
>> >> >> > pinmux: pinmux@0xFFEE0000 {
>> >> >> > compatible = "pinctrl-single";
>> >> >> > reg = <0xFFEE0000 0x8>;
>> >> >> > #address-cells = <1>;
>> >> >> > #size-cells = <0>;
>> >> >> > #gpio-range-cells = <3>;
>> >> >> > pinctrl-single,register-width = <32>;
>> >> >> > pinctrl-single,function-mask = <0xffffffff>;
>> >> >> > pinctrl-single,gpio-range = <&range 1 8 0>;
>> >> >> > gpioa_pins: pinmux_gpioa_pins {
>> >> >> > pinctrl-single,pins = <0x0 0 0x4 0>
>> >> >> > };
>> >> >> > i2c_pins: pinmux_i2c_pins {
>> >> >> > pinctrl-single,pins = <0x0 1>
>> >> >> > };
>> >> >> > spi0_pins: pinmux_spi0_pins {
>> >> >> > pinctrl-single,pins = <0x1 1>
>> >> >> <0x1 1>?
>> >> >>
>> >> >> If each pinmux register is only for one pin in your SoC.
>> >> >> I think that your definitions are wrong above. We use
>> >> >> register offset as the first argument, not pin number.
>> >> >> And the second argument should be pin function number.
>> >> >
>> >> > In our case each pinmux register (bit field) actually controls an entire
>> >> > group of pins.
>> >> >
>> >> >> If multiple pins are sharing one register with different bits,
>> >> >> you need to enable "pinctrl-single,bit-per-mux".
>> >> >
>> >> > Multiple pins are sharing the same bits in the same register. Do you
>> >> > think this prevents us from using pinctrl-single?
>> >> >
>> >> Could you give me your register definition? Then I can understand you
>> >> better.
>> >
>> > In our example, the register map would look a bit like the following.
>> > Note that every register configures four pins at a time.
>> >
>> > Register 0x0:
>> > Mode GPIO-A I2C SPI1
>> > Value 0x0 0x1 0x2
>> > ---------------------------
>> > Pin1 GPIOA0 SDA MISO1
>> > Pin2 GPIOA1 SCL MOSI1
>> > Pin3 GPIOA2 SS1_B
>> > Pin4 GPIOA3 SCLK1
>> >
>> > Register 0x4:
>> > Mode GPIO-A SPI0
>> > Value 0x0 0x1
>> > ---------------------
>> > Pin5 GPIOA4 MISO0
>> > Pin6 GPIOA5 MOSI0
>> > Pin7 GPIOA6 SS0_B
>> > Pin8 GPIOA7 SCLK0
>> >
>>
>> You said "Multiple pins are sharing the same bits in the same register.".
>> I need to understand which bits you're talking about in your register.
>
> In the above example, bits 0 and 1 of register 0x0 control pins 1
> through 4 and bit 0 of register 0x4 controls pins 5 through 8. The
> moment you write a new value in either of those registers, all four pins
> will change functionality simultaneously. There is no way to control the
> functionality of each pin individually.
>

Oh. So some bits in the same register control multiple pins.

Yeah, I also meet this in Hisilicon SoC. My solution is to only define the
pinmux register for one pin, and skip other pins.

1. You're using GPIOA0 & GPIOA1 in two different driver.
You only need to define GPIOA0 or GPIOA1 in one of driver. Don't define
them at the same time.

2. You're using GPIOA0 & GPIOA1 in the same driver.
You only need to define GPIOA0 or GPIOA1.

3. If you're using SPI or any other function, it's same as GPIO function.

4. There's no #4. Since you won't use GPIOA0 with SCL pin together.

Regards
Haojian

2013-06-07 19:18:41

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 06/06/2013 09:30 AM, Christian Ruppert wrote:
> On Thu, Jun 06, 2013 at 10:32:21PM +0800, Haojian Zhuang wrote:
>> On 6 June 2013 22:11, Christian Ruppert <[email protected]> wrote:
>>> On Wed, Jun 05, 2013 at 09:44:27AM +0800, Haojian Zhuang wrote:
>>>> On 3 June 2013 20:30, Christian Ruppert <[email protected]> wrote:
>>>>> OK, here's a simplified example of what we would like to do (this seems
>>>>> pretty common so I suppose there is a way I haven't understood). Our
>>>>> situation is slightly more complex but for the purpose of discussion
>>>>> let's assume a chip with 8 pins which can be configured for the
>>>>> following functions:
>>>>>
>>>>> Pin GPIO-A I2C SPI0 SPI1
>>>>> ------------------------------------
>>>>> 1 GPIOA0 SDA MISO1
>>>>> 2 GPIOA1 SCL MOSI1
>>>>> 3 GPIOA2 SS1_B
>>>>> 4 GPIOA3 SCLK1
>>>>> 5 GPIOA4 MISO0
>>>>> 6 GPIOA5 MOSI0
>>>>> 7 GPIOA6 SS0_B
>>>>> 8 GPIOA7 SCLK0
>>>>>
>>>>> We can now define the following pinctrl-single:
>>>>>
>>>>> pinmux: pinmux@0xFFEE0000 {
>>>>> compatible = "pinctrl-single";
>>>>> reg = <0xFFEE0000 0x8>;
>>>>> #address-cells = <1>;
>>>>> #size-cells = <0>;
>>>>> #gpio-range-cells = <3>;
>>>>> pinctrl-single,register-width = <32>;
>>>>> pinctrl-single,function-mask = <0xffffffff>;
>>>>> pinctrl-single,gpio-range = <&range 1 8 0>;
>>>>> gpioa_pins: pinmux_gpioa_pins {
>>>>> pinctrl-single,pins = <0x0 0 0x4 0>
>>>>> };
>>>>> i2c_pins: pinmux_i2c_pins {
>>>>> pinctrl-single,pins = <0x0 1>
>>>>> };
>>>>> spi0_pins: pinmux_spi0_pins {
>>>>> pinctrl-single,pins = <0x1 1>
>>>> <0x1 1>?
>>>>
>>>> If each pinmux register is only for one pin in your SoC.
>>>> I think that your definitions are wrong above. We use
>>>> register offset as the first argument, not pin number.
>>>> And the second argument should be pin function number.
>>>
>>> In our case each pinmux register (bit field) actually controls an entire
>>> group of pins.
>>>
>>>> If multiple pins are sharing one register with different bits,
>>>> you need to enable "pinctrl-single,bit-per-mux".
>>>
>>> Multiple pins are sharing the same bits in the same register. Do you
>>> think this prevents us from using pinctrl-single?
>>>
>> Could you give me your register definition? Then I can understand you
>> better.
>
> In our example, the register map would look a bit like the following.
> Note that every register configures four pins at a time.
>
> Register 0x0:
> Mode GPIO-A I2C SPI1
> Value 0x0 0x1 0x2
> ---------------------------
> Pin1 GPIOA0 SDA MISO1
> Pin2 GPIOA1 SCL MOSI1
> Pin3 GPIOA2 SS1_B
> Pin4 GPIOA3 SCLK1
>
> Register 0x4:
> Mode GPIO-A SPI0
> Value 0x0 0x1
> ---------------------
> Pin5 GPIOA4 MISO0
> Pin6 GPIOA5 MOSI0
> Pin7 GPIOA6 SS0_B
> Pin8 GPIOA7 SCLK0

My suggestion here is that pinctrl-single isn't appropriate. The only
way it could work is if you pretend that each group-of-pins is actually
a single pin.

However, then the correlation between these pretend pins (i.e. really
the groups) and GPIOs won't work, because each "pin" is really 4 pins,
and hence 4 GPIOs, and hence you won't be able to gpio_get() more than 1
GPIO per pin group, I think.

It's not hard (although possibly data intensive depending on your SoC)
to represent your HW just fine with a native pinctrl driver; pinctrl
itself has the ability to separate the concepts of pins, groups-of-pins,
and the mux-functions-that-are-assigned-to-groups. If any of your HW
registers actually do control only a single pin, you can simply create
both a pin and a group that contains only that one pin. This is all very
similar to how Tegra works, although it sounds like your registers may
be a bit more regular than Tegras - Tegra has a very variable number of
pins in each grop, and even some overlap between groups (mux function
groups and pin configuration groups aren't aligned).

2013-06-08 08:31:08

by Haojian Zhuang

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 8 June 2013 03:18, Stephen Warren <[email protected]> wrote:
> On 06/06/2013 09:30 AM, Christian Ruppert wrote:
>> On Thu, Jun 06, 2013 at 10:32:21PM +0800, Haojian Zhuang wrote:
>>> On 6 June 2013 22:11, Christian Ruppert <[email protected]> wrote:
>>>> On Wed, Jun 05, 2013 at 09:44:27AM +0800, Haojian Zhuang wrote:
>>>>> On 3 June 2013 20:30, Christian Ruppert <[email protected]> wrote:
>>>>>> OK, here's a simplified example of what we would like to do (this seems
>>>>>> pretty common so I suppose there is a way I haven't understood). Our
>>>>>> situation is slightly more complex but for the purpose of discussion
>>>>>> let's assume a chip with 8 pins which can be configured for the
>>>>>> following functions:
>>>>>>
>>>>>> Pin GPIO-A I2C SPI0 SPI1
>>>>>> ------------------------------------
>>>>>> 1 GPIOA0 SDA MISO1
>>>>>> 2 GPIOA1 SCL MOSI1
>>>>>> 3 GPIOA2 SS1_B
>>>>>> 4 GPIOA3 SCLK1
>>>>>> 5 GPIOA4 MISO0
>>>>>> 6 GPIOA5 MOSI0
>>>>>> 7 GPIOA6 SS0_B
>>>>>> 8 GPIOA7 SCLK0
>>>>>>
>>>>>> We can now define the following pinctrl-single:
>>>>>>
>>>>>> pinmux: pinmux@0xFFEE0000 {
>>>>>> compatible = "pinctrl-single";
>>>>>> reg = <0xFFEE0000 0x8>;
>>>>>> #address-cells = <1>;
>>>>>> #size-cells = <0>;
>>>>>> #gpio-range-cells = <3>;
>>>>>> pinctrl-single,register-width = <32>;
>>>>>> pinctrl-single,function-mask = <0xffffffff>;
>>>>>> pinctrl-single,gpio-range = <&range 1 8 0>;
>>>>>> gpioa_pins: pinmux_gpioa_pins {
>>>>>> pinctrl-single,pins = <0x0 0 0x4 0>
>>>>>> };
>>>>>> i2c_pins: pinmux_i2c_pins {
>>>>>> pinctrl-single,pins = <0x0 1>
>>>>>> };
>>>>>> spi0_pins: pinmux_spi0_pins {
>>>>>> pinctrl-single,pins = <0x1 1>
>>>>> <0x1 1>?
>>>>>
>>>>> If each pinmux register is only for one pin in your SoC.
>>>>> I think that your definitions are wrong above. We use
>>>>> register offset as the first argument, not pin number.
>>>>> And the second argument should be pin function number.
>>>>
>>>> In our case each pinmux register (bit field) actually controls an entire
>>>> group of pins.
>>>>
>>>>> If multiple pins are sharing one register with different bits,
>>>>> you need to enable "pinctrl-single,bit-per-mux".
>>>>
>>>> Multiple pins are sharing the same bits in the same register. Do you
>>>> think this prevents us from using pinctrl-single?
>>>>
>>> Could you give me your register definition? Then I can understand you
>>> better.
>>
>> In our example, the register map would look a bit like the following.
>> Note that every register configures four pins at a time.
>>
>> Register 0x0:
>> Mode GPIO-A I2C SPI1
>> Value 0x0 0x1 0x2
>> ---------------------------
>> Pin1 GPIOA0 SDA MISO1
>> Pin2 GPIOA1 SCL MOSI1
>> Pin3 GPIOA2 SS1_B
>> Pin4 GPIOA3 SCLK1
>>
>> Register 0x4:
>> Mode GPIO-A SPI0
>> Value 0x0 0x1
>> ---------------------
>> Pin5 GPIOA4 MISO0
>> Pin6 GPIOA5 MOSI0
>> Pin7 GPIOA6 SS0_B
>> Pin8 GPIOA7 SCLK0
>
> My suggestion here is that pinctrl-single isn't appropriate. The only
> way it could work is if you pretend that each group-of-pins is actually
> a single pin.
>
> However, then the correlation between these pretend pins (i.e. really
> the groups) and GPIOs won't work, because each "pin" is really 4 pins,
> and hence 4 GPIOs, and hence you won't be able to gpio_get() more than 1
> GPIO per pin group, I think.

Actually we can get each GPIO in the SoC. But we need to do some workaround.

1. As we discussed, we need to pretend a pin group as a single pin.

2. In DTS, we need to define "gpio-ranges" in gpio node and
"pinctrl-single,gpio-range"
in pinmux node as below.

gpio {
/* gpio offset, pin offset, nr pins */
/* skip GPIOA1 & GPIOA3, PIN0 means pin1/pin2, PIN1 means
pin3/pin4 */
gpio-ranges = <&pmx 0 0 1 &pmx 2 1 1>;
};

pmx {
/* pin offset, nr pins, gpio function */
pinctrl-single,gpio-range = <&range 0 1 0 &range 1 1 0>
};
range {
#pinctrl-single,gpio-range-cells = <3>;
};

Because we pretend pin1/pin2 as one single pin (PIN1), we skip to define it
in gpio-ranges. This range is only help you to find right pinmux controller.

Yes, I agree that pinctrl-single driver isn't 100% appropriate. But it
could work.
I verified it.

Regards
Haojian

>
> It's not hard (although possibly data intensive depending on your SoC)
> to represent your HW just fine with a native pinctrl driver; pinctrl
> itself has the ability to separate the concepts of pins, groups-of-pins,
> and the mux-functions-that-are-assigned-to-groups. If any of your HW
> registers actually do control only a single pin, you can simply create
> both a pin and a group that contains only that one pin. This is all very
> similar to how Tegra works, although it sounds like your registers may
> be a bit more regular than Tegras - Tegra has a very variable number of
> pins in each grop, and even some overlap between groups (mux function
> groups and pin configuration groups aren't aligned).

2013-06-09 02:47:40

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On 06/08/2013 02:31 AM, Haojian Zhuang wrote:
> On 8 June 2013 03:18, Stephen Warren <[email protected]> wrote:
>> On 06/06/2013 09:30 AM, Christian Ruppert wrote:
>>> On Thu, Jun 06, 2013 at 10:32:21PM +0800, Haojian Zhuang wrote:
>>>> On 6 June 2013 22:11, Christian Ruppert <[email protected]> wrote:
>>>>> On Wed, Jun 05, 2013 at 09:44:27AM +0800, Haojian Zhuang wrote:
>>>>>> On 3 June 2013 20:30, Christian Ruppert <[email protected]> wrote:
>>>>>>> OK, here's a simplified example of what we would like to do (this seems
>>>>>>> pretty common so I suppose there is a way I haven't understood). Our
>>>>>>> situation is slightly more complex but for the purpose of discussion
>>>>>>> let's assume a chip with 8 pins which can be configured for the
>>>>>>> following functions:
>>>>>>>
>>>>>>> Pin GPIO-A I2C SPI0 SPI1
>>>>>>> ------------------------------------
>>>>>>> 1 GPIOA0 SDA MISO1
>>>>>>> 2 GPIOA1 SCL MOSI1
>>>>>>> 3 GPIOA2 SS1_B
>>>>>>> 4 GPIOA3 SCLK1
>>>>>>> 5 GPIOA4 MISO0
>>>>>>> 6 GPIOA5 MOSI0
>>>>>>> 7 GPIOA6 SS0_B
>>>>>>> 8 GPIOA7 SCLK0
>>>>>>>
>>>>>>> We can now define the following pinctrl-single:
>>>>>>>
>>>>>>> pinmux: pinmux@0xFFEE0000 {
>>>>>>> compatible = "pinctrl-single";
>>>>>>> reg = <0xFFEE0000 0x8>;
>>>>>>> #address-cells = <1>;
>>>>>>> #size-cells = <0>;
>>>>>>> #gpio-range-cells = <3>;
>>>>>>> pinctrl-single,register-width = <32>;
>>>>>>> pinctrl-single,function-mask = <0xffffffff>;
>>>>>>> pinctrl-single,gpio-range = <&range 1 8 0>;
>>>>>>> gpioa_pins: pinmux_gpioa_pins {
>>>>>>> pinctrl-single,pins = <0x0 0 0x4 0>
>>>>>>> };
>>>>>>> i2c_pins: pinmux_i2c_pins {
>>>>>>> pinctrl-single,pins = <0x0 1>
>>>>>>> };
>>>>>>> spi0_pins: pinmux_spi0_pins {
>>>>>>> pinctrl-single,pins = <0x1 1>
>>>>>> <0x1 1>?
>>>>>>
>>>>>> If each pinmux register is only for one pin in your SoC.
>>>>>> I think that your definitions are wrong above. We use
>>>>>> register offset as the first argument, not pin number.
>>>>>> And the second argument should be pin function number.
>>>>>
>>>>> In our case each pinmux register (bit field) actually controls an entire
>>>>> group of pins.
>>>>>
>>>>>> If multiple pins are sharing one register with different bits,
>>>>>> you need to enable "pinctrl-single,bit-per-mux".
>>>>>
>>>>> Multiple pins are sharing the same bits in the same register. Do you
>>>>> think this prevents us from using pinctrl-single?
>>>>>
>>>> Could you give me your register definition? Then I can understand you
>>>> better.
>>>
>>> In our example, the register map would look a bit like the following.
>>> Note that every register configures four pins at a time.
>>>
>>> Register 0x0:
>>> Mode GPIO-A I2C SPI1
>>> Value 0x0 0x1 0x2
>>> ---------------------------
>>> Pin1 GPIOA0 SDA MISO1
>>> Pin2 GPIOA1 SCL MOSI1
>>> Pin3 GPIOA2 SS1_B
>>> Pin4 GPIOA3 SCLK1
>>>
>>> Register 0x4:
>>> Mode GPIO-A SPI0
>>> Value 0x0 0x1
>>> ---------------------
>>> Pin5 GPIOA4 MISO0
>>> Pin6 GPIOA5 MOSI0
>>> Pin7 GPIOA6 SS0_B
>>> Pin8 GPIOA7 SCLK0
>>
>> My suggestion here is that pinctrl-single isn't appropriate. The only
>> way it could work is if you pretend that each group-of-pins is actually
>> a single pin.
>>
>> However, then the correlation between these pretend pins (i.e. really
>> the groups) and GPIOs won't work, because each "pin" is really 4 pins,
>> and hence 4 GPIOs, and hence you won't be able to gpio_get() more than 1
>> GPIO per pin group, I think.
>
> Actually we can get each GPIO in the SoC. But we need to do some workaround.
>
> 1. As we discussed, we need to pretend a pin group as a single pin.
>
> 2. In DTS, we need to define "gpio-ranges" in gpio node and
> "pinctrl-single,gpio-range"
> in pinmux node as below.
>
> gpio {
> /* gpio offset, pin offset, nr pins */
> /* skip GPIOA1 & GPIOA3, PIN0 means pin1/pin2, PIN1 means
> pin3/pin4 */
> gpio-ranges = <&pmx 0 0 1 &pmx 2 1 1>;
> };
>
> pmx {
> /* pin offset, nr pins, gpio function */
> pinctrl-single,gpio-range = <&range 0 1 0 &range 1 1 0>
> };
> range {
> #pinctrl-single,gpio-range-cells = <3>;
> };
>
> Because we pretend pin1/pin2 as one single pin (PIN1), we skip to define it
> in gpio-ranges. This range is only help you to find right pinmux controller.
>
> Yes, I agree that pinctrl-single driver isn't 100% appropriate. But it
> could work.
> I verified it.

Yeah, that sounds pretty horrible, sorry.

2013-06-11 07:28:16

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Fri, Jun 07, 2013 at 01:18:35PM -0600, Stephen Warren wrote:
> On 06/06/2013 09:30 AM, Christian Ruppert wrote:
> > On Thu, Jun 06, 2013 at 10:32:21PM +0800, Haojian Zhuang wrote:
> >> On 6 June 2013 22:11, Christian Ruppert <[email protected]> wrote:
> >>> On Wed, Jun 05, 2013 at 09:44:27AM +0800, Haojian Zhuang wrote:
> >>>> On 3 June 2013 20:30, Christian Ruppert <[email protected]> wrote:
> >>>>> OK, here's a simplified example of what we would like to do (this seems
> >>>>> pretty common so I suppose there is a way I haven't understood). Our
> >>>>> situation is slightly more complex but for the purpose of discussion
> >>>>> let's assume a chip with 8 pins which can be configured for the
> >>>>> following functions:
> >>>>>
> >>>>> Pin GPIO-A I2C SPI0 SPI1
> >>>>> ------------------------------------
> >>>>> 1 GPIOA0 SDA MISO1
> >>>>> 2 GPIOA1 SCL MOSI1
> >>>>> 3 GPIOA2 SS1_B
> >>>>> 4 GPIOA3 SCLK1
> >>>>> 5 GPIOA4 MISO0
> >>>>> 6 GPIOA5 MOSI0
> >>>>> 7 GPIOA6 SS0_B
> >>>>> 8 GPIOA7 SCLK0
> >>>>>
> >>>>> We can now define the following pinctrl-single:
> >>>>>
> >>>>> pinmux: pinmux@0xFFEE0000 {
> >>>>> compatible = "pinctrl-single";
> >>>>> reg = <0xFFEE0000 0x8>;
> >>>>> #address-cells = <1>;
> >>>>> #size-cells = <0>;
> >>>>> #gpio-range-cells = <3>;
> >>>>> pinctrl-single,register-width = <32>;
> >>>>> pinctrl-single,function-mask = <0xffffffff>;
> >>>>> pinctrl-single,gpio-range = <&range 1 8 0>;
> >>>>> gpioa_pins: pinmux_gpioa_pins {
> >>>>> pinctrl-single,pins = <0x0 0 0x4 0>
> >>>>> };
> >>>>> i2c_pins: pinmux_i2c_pins {
> >>>>> pinctrl-single,pins = <0x0 1>
> >>>>> };
> >>>>> spi0_pins: pinmux_spi0_pins {
> >>>>> pinctrl-single,pins = <0x1 1>
> >>>> <0x1 1>?
> >>>>
> >>>> If each pinmux register is only for one pin in your SoC.
> >>>> I think that your definitions are wrong above. We use
> >>>> register offset as the first argument, not pin number.
> >>>> And the second argument should be pin function number.
> >>>
> >>> In our case each pinmux register (bit field) actually controls an entire
> >>> group of pins.
> >>>
> >>>> If multiple pins are sharing one register with different bits,
> >>>> you need to enable "pinctrl-single,bit-per-mux".
> >>>
> >>> Multiple pins are sharing the same bits in the same register. Do you
> >>> think this prevents us from using pinctrl-single?
> >>>
> >> Could you give me your register definition? Then I can understand you
> >> better.
> >
> > In our example, the register map would look a bit like the following.
> > Note that every register configures four pins at a time.
> >
> > Register 0x0:
> > Mode GPIO-A I2C SPI1
> > Value 0x0 0x1 0x2
> > ---------------------------
> > Pin1 GPIOA0 SDA MISO1
> > Pin2 GPIOA1 SCL MOSI1
> > Pin3 GPIOA2 SS1_B
> > Pin4 GPIOA3 SCLK1
> >
> > Register 0x4:
> > Mode GPIO-A SPI0
> > Value 0x0 0x1
> > ---------------------
> > Pin5 GPIOA4 MISO0
> > Pin6 GPIOA5 MOSI0
> > Pin7 GPIOA6 SS0_B
> > Pin8 GPIOA7 SCLK0
>
> My suggestion here is that pinctrl-single isn't appropriate. The only
> way it could work is if you pretend that each group-of-pins is actually
> a single pin.
>
> However, then the correlation between these pretend pins (i.e. really
> the groups) and GPIOs won't work, because each "pin" is really 4 pins,
> and hence 4 GPIOs, and hence you won't be able to gpio_get() more than 1
> GPIO per pin group, I think.

I agree: Unluckily, pinctrl-single doesn't seem to be what we are looking
for.

> It's not hard (although possibly data intensive depending on your SoC)
> to represent your HW just fine with a native pinctrl driver; pinctrl
> itself has the ability to separate the concepts of pins, groups-of-pins,
> and the mux-functions-that-are-assigned-to-groups. If any of your HW
> registers actually do control only a single pin, you can simply create
> both a pin and a group that contains only that one pin. This is all very
> similar to how Tegra works, although it sounds like your registers may
> be a bit more regular than Tegras - Tegra has a very variable number of
> pins in each grop, and even some overlap between groups (mux function
> groups and pin configuration groups aren't aligned).

Actually, our registers are quite a bit more complex than the above
example (interdependencies between pin groups, overlapping
functionalities etc.) but I set the example to understand the concepts
which could then possibly be extended/applied to our real case.

We already have a draft for a native pinctrl driver (see original post)
but there was some disagreement about exposing kernel internal pin
numbering to device tree users. Now that it's pretty sure that we cannot
reasonably use pinctrl-single I'll take up that work again and make a
proposal based on 1) in https://lkml.org/lkml/2013/5/22/207.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-12 16:44:49

by Christian Ruppert

[permalink] [raw]
Subject: [RFC] Allow GPIO ranges based on pinctl pin groups

Hello Linus and Stephen,

I have tried to satisfy your request to make this as generic to the
GPIO/pinctrl frameworks as possible. The patch cleanly applies to
Linux-3.10rc5 and compiles. I satisfies several of the requests in reply
to the original post, e.g. no more driver cross-calling, directly use
gpio ranges in device tree etc. I haven't sent the new drivers based on
this modifications since many of the other comments aren't cleaned up
yet but they will follow with a cleaned up version of this RFC as soon
as we agree on the principle.

This patch allows the definition of GPIO ranges based on pin groups in
addition to the traditional linear pin ranges. GPIO ranges based on pin
groups have the following advantages over traditional pin ranges:
. Previously, pins associated to a given functionality were defined
inside the pin controller (e.g. a pin controller can define a group
spi0_pins defining which pins are used by SPI port 0). This mechanism
did not apply to GPIO controllers, however, which had to define GPIO
ranges based on pin numbers otherwise confined to the pin controller.
With the possibility to use pin groups for pin ranges, the pins
associated to any functionality, including GPIO, can now be entirely
defined inside the pin controller. Everything that needs to be known
to the outside world is the name of the pin group.
. Non-consecutive pin ranges and arbitrary pin ordering is now possible
in GPIO ranges.
. Linux internal pin numbers now no longer leak out of the kernel, e.g.
to device tree. If the pinctrl driver author chooses to, GPIO range
management can now entirely be based on symbolic names of pin groups.

Signed-off-by: Christian Ruppert <[email protected]>
---
Documentation/devicetree/bindings/gpio/gpio.txt | 36 ++++++++++++
drivers/gpio/gpiolib-of.c | 20 +++++++-
drivers/gpio/gpiolib.c | 48 ++++++++++++++++
drivers/pinctrl/core.c | 67 +++++++++++++++++++----
include/asm-generic/gpio.h | 10 ++++
include/linux/gpio.h | 9 +++
include/linux/pinctrl/pinctrl.h | 9 +++-
7 files changed, 187 insertions(+), 12 deletions(-)

diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
index d933af3..f8237c0 100644
--- a/Documentation/devicetree/bindings/gpio/gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio.txt
@@ -112,3 +112,39 @@ where,

The pinctrl node must have "#gpio-range-cells" property to show number of
arguments to pass with phandle from gpio controllers node.
+
+In addition, gpio ranges can be mapped to pin groups of a given pin
+controller (see Documentation/pinctrl.txt):
+
+ gpio_pio_g: gpio-controller@1480 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1480 0x18>;
+ gpio-controller;
+ gpio-pingrps = <&pinctrl1 0>, <&pinctrl2 3>;
+ gpio-pingrp-names = "pctl1_gpio_g", "pctl2_gpio_g";
+ }
+where,
+ &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
+
+ Next values specify the base GPIO offset of the pin range with respect to
+ the GPIO controller's base. The number of pins in the range is the number
+ of pins in the pin group.
+
+ gpio-pingrp-names defines the name of each pingroup of the respective pin
+ controller.
+
+The pinctrl node msut have a "#gpio-pingrp-cells" property set to one to
+define the number of arguments to pass with the phandle.
+
+Both methods can be combined in the same GPIO controller, e.g.
+
+ gpio_pio_i: gpio-controller@14B0 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1480 0x18>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl1 0 20 10>;
+ gpio-pingrps = <&pinctrl2 10>;
+ gpio-pingrp-names = "gpio_g_pins";
+ }
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 665f953..337dfc7 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -188,7 +188,9 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
struct device_node *np = chip->of_node;
struct of_phandle_args pinspec;
struct pinctrl_dev *pctldev;
- int index = 0, ret;
+ struct property *prop;
+ int index = 0, ret, namecnt;
+ const char *name;

if (!np)
return;
@@ -212,6 +214,22 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
if (ret)
break;
}
+
+ index = 0;
+ of_property_for_each_string(np, "gpio-pingrp-names", prop, name) {
+ ret = of_parse_phandle_with_args(np, "gpio-pingrps",
+ "#gpio-pingrp-cells",
+ index, &pinspec);
+ if (ret < 0)
+ break;
+
+ index ++;
+
+ pctldev = of_pinctrl_get(pinspec.np);
+
+ gpiochip_add_pingroup_range(chip, pctldev,
+ pinspec.args[0], name);
+ }
}

#else
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c2534d6..bc899f0 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1318,6 +1318,54 @@ EXPORT_SYMBOL_GPL(gpiochip_find);
#ifdef CONFIG_PINCTRL

/**
+ * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping
+ * @chip: the gpiochip to add the range for
+ * @pinctrl: the dev_name() of the pin controller to map to
+ * @gpio_offset: the start offset in the current gpio_chip number space
+ * @pin_group: name of the pin group inside the pin controller
+ */
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ struct gpio_pin_range *pin_range;
+ int ret;
+
+ pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
+ if (!pin_range) {
+ pr_err("%s: GPIO chip: failed to allocate pin ranges\n",
+ chip->label);
+ return -ENOMEM;
+ }
+
+ /* Use local offset as range ID */
+ pin_range->range.id = gpio_offset;
+ pin_range->range.gc = chip;
+ pin_range->range.name = chip->label;
+ pin_range->range.base = chip->base + gpio_offset;
+ pin_range->range.pin_base = 0;
+ ret = pinctrl_get_group_pins(pctldev, pin_group,
+ &pin_range->range.pins,
+ &pin_range->range.npins);
+ if (ret < 0) {
+ pr_err("%s: GPIO chip: could not create pin range %s\n",
+ chip->label, pin_group);
+ }
+ pin_range->pctldev = pctldev;
+ pinctrl_add_gpio_range(pctldev, &pin_range->range);
+
+ pr_debug("GPIO chip %s: created GPIO range %d->%d ==> %s PINGRP %s\n",
+ chip->label, gpio_offset,
+ gpio_offset + pin_range->range.npins - 1,
+ pinctrl_dev_get_devname(pctldev), pin_group);
+
+ list_add_tail(&pin_range->node, &chip->pin_ranges);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
+
+/**
* gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping
* @chip: the gpiochip to add the range for
* @pinctrl_name: the dev_name() of the pin controller to map to
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index 5327f35..cdb070c 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -279,6 +279,15 @@ static int pinctrl_register_pins(struct pinctrl_dev *pctldev,
return 0;
}

+static inline int gpio2pin(struct pinctrl_gpio_range *range, unsigned int gpio)
+{
+ unsigned int offset = gpio - range->base;
+ if (range->pins)
+ return range->pins[offset];
+ else
+ return range->pin_base + offset;
+}
+
/**
* pinctrl_match_gpio_range() - check if a certain GPIO pin is in range
* @pctldev: pin controller device to check
@@ -444,8 +453,14 @@ pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
/* Loop over the ranges */
list_for_each_entry(range, &pctldev->gpio_ranges, node) {
/* Check if we're in the valid range */
- if (pin >= range->pin_base &&
- pin < range->pin_base + range->npins) {
+ if (range->pins) {
+ int a;
+ for (a = 0; a < range->npins; a++) {
+ if (range->pins[a] == pin)
+ return range;
+ }
+ } else if (pin >= range->pin_base &&
+ pin < range->pin_base + range->npins) {
mutex_unlock(&pctldev->mutex);
return range;
}
@@ -503,6 +518,28 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev,
}

/**
+ * pinctrl_get_group_pins() - returns a pin group
+ * @pctldev: the pin controller handling the group
+ * @pin_group: the pin group to look up
+ * @pins: returns a pointer to an array of pin numbers in the group
+ * @npins: returns the number of pins in the group
+ */
+int pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+ const char *pin_group,
+ unsigned const **pins, unsigned * const npins)
+{
+ const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ unsigned group_selector;
+
+ group_selector = pinctrl_get_group_selector(pctldev, pin_group);
+ if (group_selector < 0)
+ return group_selector;
+
+ return pctlops->get_group_pins(pctldev, group_selector, pins, npins);
+}
+EXPORT_SYMBOL_GPL(pinctrl_get_group_pins);
+
+/**
* pinctrl_request_gpio() - request a single pin to be used in as GPIO
* @gpio: the GPIO pin number from the GPIO subsystem number space
*
@@ -528,7 +565,7 @@ int pinctrl_request_gpio(unsigned gpio)
}

/* Convert to the pin controllers number space */
- pin = gpio - range->base + range->pin_base;
+ pin = gpio2pin(range, gpio);

ret = pinmux_request_gpio(pctldev, range, pin, gpio);

@@ -562,7 +599,7 @@ void pinctrl_free_gpio(unsigned gpio)
mutex_lock(&pctldev->mutex);

/* Convert to the pin controllers number space */
- pin = gpio - range->base + range->pin_base;
+ pin = gpio2pin(range, gpio);

pinmux_free_gpio(pctldev, pin, range);

@@ -589,7 +626,7 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input)
mutex_lock(&pctldev->mutex);

/* Convert to the pin controllers number space */
- pin = gpio - range->base + range->pin_base;
+ pin = gpio2pin(range, gpio);
ret = pinmux_gpio_direction(pctldev, range, pin, input);

mutex_unlock(&pctldev->mutex);
@@ -1296,11 +1333,21 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what)

/* Loop over the ranges */
list_for_each_entry(range, &pctldev->gpio_ranges, node) {
- seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n",
- range->id, range->name,
- range->base, (range->base + range->npins - 1),
- range->pin_base,
- (range->pin_base + range->npins - 1));
+ if (range->pins) {
+ int a;
+ seq_printf(s, "%u: %s GPIOS [%u - %u] PINS {",
+ range->id, range->name,
+ range->base, (range->base + range->npins - 1));
+ for (a = 0; a < range->npins - 1; a++)
+ seq_printf(s, "%u, ", range->pins[a]);
+ seq_printf(s, "%u}\n", range->pins[a]);
+ }
+ else
+ seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n",
+ range->id, range->name,
+ range->base, (range->base + range->npins - 1),
+ range->pin_base,
+ (range->pin_base + range->npins - 1));
}

mutex_unlock(&pctldev->mutex);
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index bde6469..523f405 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -228,6 +228,9 @@ struct gpio_pin_range {
int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
unsigned int gpio_offset, unsigned int pin_offset,
unsigned int npins);
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group);
void gpiochip_remove_pin_ranges(struct gpio_chip *chip);

#else
@@ -239,6 +242,13 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
{
return 0;
}
+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ return 0;
+}

static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 552e3f4..234b32f 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -220,6 +220,15 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
return -EINVAL;
}

+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ WARN_ON(1);
+ return -EINVAL;
+}
+
static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
{
diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 2c2a9e8..e3ba2bb 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -49,7 +49,8 @@ struct pinctrl_pin_desc {
* @name: a name for the chip in this range
* @id: an ID number for the chip in this range
* @base: base offset of the GPIO range
- * @pin_base: base pin number of the GPIO range
+ * @pin_base: base pin number of the GPIO range if pins != NULL
+ * @pins: enumeration of pins in GPIO range or NULL
* @npins: number of pins in the GPIO range, including the base number
* @gc: an optional pointer to a gpio_chip
*/
@@ -59,6 +60,7 @@ struct pinctrl_gpio_range {
unsigned int id;
unsigned int base;
unsigned int pin_base;
+ unsigned const *pins;
unsigned int npins;
struct gpio_chip *gc;
};
@@ -142,6 +144,11 @@ extern struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
extern struct pinctrl_gpio_range *
pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
unsigned int pin);
+extern int pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+ const char *pin_group,
+ unsigned const **pins,
+ unsigned * const npins);
+

#ifdef CONFIG_OF
extern struct pinctrl_dev *of_pinctrl_get(struct device_node *np);
--
1.7.1

2013-06-13 09:00:32

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC] Allow GPIO ranges based on pinctl pin groups

On Wed, Jun 12, 2013 at 6:44 PM, Christian Ruppert
<[email protected]> wrote:

> This patch allows the definition of GPIO ranges based on pin groups in
> addition to the traditional linear pin ranges. GPIO ranges based on pin
> groups have the following advantages over traditional pin ranges:
> . Previously, pins associated to a given functionality were defined
> inside the pin controller (e.g. a pin controller can define a group
> spi0_pins defining which pins are used by SPI port 0). This mechanism
> did not apply to GPIO controllers, however, which had to define GPIO
> ranges based on pin numbers otherwise confined to the pin controller.
> With the possibility to use pin groups for pin ranges, the pins
> associated to any functionality, including GPIO, can now be entirely
> defined inside the pin controller. Everything that needs to be known
> to the outside world is the name of the pin group.
> . Non-consecutive pin ranges and arbitrary pin ordering is now possible
> in GPIO ranges.
> . Linux internal pin numbers now no longer leak out of the kernel, e.g.
> to device tree. If the pinctrl driver author chooses to, GPIO range
> management can now entirely be based on symbolic names of pin groups.
>
> Signed-off-by: Christian Ruppert <[email protected]>

Overall this approach looks nice.

There are details that need fixing though:

> +++ b/Documentation/devicetree/bindings/gpio/gpio.txt
(...)
> @@ -112,3 +112,39 @@ where,
>
> The pinctrl node must have "#gpio-range-cells" property to show number of
> arguments to pass with phandle from gpio controllers node.
> +
> +In addition, gpio ranges can be mapped to pin groups of a given pin
> +controller (see Documentation/pinctrl.txt):

Do not reference Linux particulars in bindings. The idea is to
reuse these bindings with other operating systems as well.

> +
> + gpio_pio_g: gpio-controller@1480 {
> + #gpio-cells = <2>;
> + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
> + reg = <0x1480 0x18>;
> + gpio-controller;
> + gpio-pingrps = <&pinctrl1 0>, <&pinctrl2 3>;
> + gpio-pingrp-names = "pctl1_gpio_g", "pctl2_gpio_g";
> + }

End with semicolon?

> +where,
> + &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.

There is something weird with spacing above.

> + Next values specify the base GPIO offset of the pin range with respect to
> + the GPIO controller's base. The number of pins in the range is the number
> + of pins in the pin group.
> +
> + gpio-pingrp-names defines the name of each pingroup of the respective pin
> + controller.

That seems like a good idea.

> +The pinctrl node msut have a "#gpio-pingrp-cells" property set to one to

must

> +define the number of arguments to pass with the phandle.
> +
> +Both methods can be combined in the same GPIO controller, e.g.
> +
> + gpio_pio_i: gpio-controller@14B0 {
> + #gpio-cells = <2>;
> + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
> + reg = <0x1480 0x18>;
> + gpio-controller;
> + gpio-ranges = <&pinctrl1 0 20 10>;
> + gpio-pingrps = <&pinctrl2 10>;
> + gpio-pingrp-names = "gpio_g_pins";
> + }

Semicolon after that closing brace.

> +++ b/drivers/gpio/gpiolib-of.c
(...)
> + index = 0;
> + of_property_for_each_string(np, "gpio-pingrp-names", prop, name) {
> + ret = of_parse_phandle_with_args(np, "gpio-pingrps",
> + "#gpio-pingrp-cells",
> + index, &pinspec);
> + if (ret < 0)
> + break;
> +
> + index ++;
> +
> + pctldev = of_pinctrl_get(pinspec.np);
> +
> + gpiochip_add_pingroup_range(chip, pctldev,
> + pinspec.args[0], name);
> + }
> }

This part looks fine.

(...)
> +++ b/drivers/gpio/gpiolib.c
> @@ -1318,6 +1318,54 @@ EXPORT_SYMBOL_GPL(gpiochip_find);
> #ifdef CONFIG_PINCTRL
>
> /**
> + * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping
> + * @chip: the gpiochip to add the range for
> + * @pinctrl: the dev_name() of the pin controller to map to
> + * @gpio_offset: the start offset in the current gpio_chip number space
> + * @pin_group: name of the pin group inside the pin controller
> + */
> +int gpiochip_add_pingroup_range(struct gpio_chip *chip,
> + struct pinctrl_dev *pctldev,
> + unsigned int gpio_offset, const char *pin_group)
> +{
> + struct gpio_pin_range *pin_range;
> + int ret;
> +
> + pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
> + if (!pin_range) {
> + pr_err("%s: GPIO chip: failed to allocate pin ranges\n",
> + chip->label);
> + return -ENOMEM;
> + }
> +
> + /* Use local offset as range ID */
> + pin_range->range.id = gpio_offset;
> + pin_range->range.gc = chip;
> + pin_range->range.name = chip->label;
> + pin_range->range.base = chip->base + gpio_offset;
> + pin_range->range.pin_base = 0;
> + ret = pinctrl_get_group_pins(pctldev, pin_group,
> + &pin_range->range.pins,
> + &pin_range->range.npins);
> + if (ret < 0) {
> + pr_err("%s: GPIO chip: could not create pin range %s\n",
> + chip->label, pin_group);
> + }
> + pin_range->pctldev = pctldev;
> + pinctrl_add_gpio_range(pctldev, &pin_range->range);
> +
> + pr_debug("GPIO chip %s: created GPIO range %d->%d ==> %s PINGRP %s\n",
> + chip->label, gpio_offset,
> + gpio_offset + pin_range->range.npins - 1,
> + pinctrl_dev_get_devname(pctldev), pin_group);
> +
> + list_add_tail(&pin_range->node, &chip->pin_ranges);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);

This thing has *waaaay* to much pinctrl stuff in it. You need to find
a better way to partition this between gpiolib and drivers/pinctrl/core.c.
For example: get rid of the function pinctrl_get_group_pins(),
why should GPIOlib know about that stuff?

Pass the groupname to the pinctrl core and let it handle this
business.

> +/**
> * gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping
> * @chip: the gpiochip to add the range for
> * @pinctrl_name: the dev_name() of the pin controller to map to

That seems like an unrelated fix I'd like a separate patch for.

(...)
> +++ b/drivers/pinctrl/core.c
> @@ -279,6 +279,15 @@ static int pinctrl_register_pins(struct pinctrl_dev *pctldev,
> return 0;
> }
>
> +static inline int gpio2pin(struct pinctrl_gpio_range *range, unsigned int gpio)
> +{
> + unsigned int offset = gpio - range->base;
> + if (range->pins)
> + return range->pins[offset];
> + else
> + return range->pin_base + offset;
> +}

Clever function. Document it! :-)

> /**
> * pinctrl_match_gpio_range() - check if a certain GPIO pin is in range
> * @pctldev: pin controller device to check
> @@ -444,8 +453,14 @@ pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
> /* Loop over the ranges */
> list_for_each_entry(range, &pctldev->gpio_ranges, node) {
> /* Check if we're in the valid range */
> - if (pin >= range->pin_base &&
> - pin < range->pin_base + range->npins) {
> + if (range->pins) {
> + int a;
> + for (a = 0; a < range->npins; a++) {
> + if (range->pins[a] == pin)
> + return range;
> + }
> + } else if (pin >= range->pin_base &&
> + pin < range->pin_base + range->npins) {
> mutex_unlock(&pctldev->mutex);
> return range;
> }

This seems like it could be a separate refactoring patch, i.e. a
patch refactoring the ranges to be an array of disparate pins
instead of a linear, consecutive range.

> /**
> + * pinctrl_get_group_pins() - returns a pin group
> + * @pctldev: the pin controller handling the group
> + * @pin_group: the pin group to look up
> + * @pins: returns a pointer to an array of pin numbers in the group
> + * @npins: returns the number of pins in the group
> + */
> +int pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
> + const char *pin_group,
> + unsigned const **pins, unsigned * const npins)
> +{
> + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
> + unsigned group_selector;
> +
> + group_selector = pinctrl_get_group_selector(pctldev, pin_group);
> + if (group_selector < 0)
> + return group_selector;
> +
> + return pctlops->get_group_pins(pctldev, group_selector, pins, npins);
> +}
> +EXPORT_SYMBOL_GPL(pinctrl_get_group_pins);

As mentioned I don't like these pinctrl internals to be exposed
to the whole wide world. You need to find another partitioning.

> /* Convert to the pin controllers number space */
> - pin = gpio - range->base + range->pin_base;
> + pin = gpio2pin(range, gpio);

Nice, can I have a patch adding this gpio2pin (hm maybe
you can rename that gpio_to_pin() I don't mind...)
and refactoring the code? It's a nice refactoring in its own
right.

Yours,
Linus Walleij

2013-06-13 12:56:14

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

This patch adds the infrastructure required to register non-linear gpio
ranges through gpiolib and the standard GPIO device tree bindings.

Signed-off-by: Christian Ruppert <[email protected]>
---
Documentation/devicetree/bindings/gpio/gpio.txt | 37 ++++++++++++++++++
drivers/gpio/gpiolib-of.c | 18 +++++++++
drivers/gpio/gpiolib.c | 47 +++++++++++++++++++++++
drivers/pinctrl/core.c | 23 +++++++++++
include/asm-generic/gpio.h | 10 +++++
include/linux/gpio.h | 9 ++++
include/linux/pinctrl/pinctrl.h | 3 +
7 files changed, 147 insertions(+), 0 deletions(-)

diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
index d933af3..ad1cef3 100644
--- a/Documentation/devicetree/bindings/gpio/gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio.txt
@@ -112,3 +112,40 @@ where,

The pinctrl node must have "#gpio-range-cells" property to show number of
arguments to pass with phandle from gpio controllers node.
+
+In addition, named groups of pins can be mapped to pin groups of a given
+pin controller:
+
+ gpio_pio_g: gpio-controller@1480 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1480 0x18>;
+ gpio-controller;
+ gpio-pingrps = <&pinctrl1 0>, <&pinctrl2 3>;
+ gpio-pingrp-names = "pctl1_gpio_g", "pctl2_gpio_g";
+ };
+
+where,
+ &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
+
+ Next values specify the base GPIO offset of the pin range with respect to
+ the GPIO controller's base. The number of pins in the range is the number
+ of pins in the pin group.
+
+ gpio-pingrp-names defines the name of each pingroup of the respective pin
+ controller.
+
+The pinctrl node must have a "#gpio-pingrp-cells" property set to one to
+define the number of arguments to pass with the phandle.
+
+Both methods can be combined in the same GPIO controller, e.g.
+
+ gpio_pio_i: gpio-controller@14B0 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1480 0x18>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl1 0 20 10>;
+ gpio-pingrps = <&pinctrl2 10>;
+ gpio-pingrp-names = "gpio_g_pins";
+ };
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 665f953..5cd16a1 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -188,7 +188,9 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
struct device_node *np = chip->of_node;
struct of_phandle_args pinspec;
struct pinctrl_dev *pctldev;
+ struct property *prop;
int index = 0, ret;
+ const char *name;

if (!np)
return;
@@ -212,6 +214,22 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
if (ret)
break;
}
+
+ index = 0;
+ of_property_for_each_string(np, "gpio-pingrp-names", prop, name) {
+ ret = of_parse_phandle_with_args(np, "gpio-pingrps",
+ "#gpio-pingrp-cells",
+ index, &pinspec);
+ if (ret < 0)
+ break;
+
+ index ++;
+
+ pctldev = of_pinctrl_get(pinspec.np);
+
+ gpiochip_add_pingroup_range(chip, pctldev,
+ pinspec.args[0], name);
+ }
}

#else
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c2534d6..b2dc810 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1318,6 +1318,53 @@ EXPORT_SYMBOL_GPL(gpiochip_find);
#ifdef CONFIG_PINCTRL

/**
+ * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping
+ * @chip: the gpiochip to add the range for
+ * @pinctrl: the dev_name() of the pin controller to map to
+ * @gpio_offset: the start offset in the current gpio_chip number space
+ * @pin_group: name of the pin group inside the pin controller
+ */
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ struct gpio_pin_range *pin_range;
+ int ret;
+
+ pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
+ if (!pin_range) {
+ pr_err("%s: GPIO chip: failed to allocate pin ranges\n",
+ chip->label);
+ return -ENOMEM;
+ }
+
+ /* Use local offset as range ID */
+ pin_range->range.id = gpio_offset;
+ pin_range->range.gc = chip;
+ pin_range->range.name = chip->label;
+ pin_range->range.base = chip->base + gpio_offset;
+ pin_range->pctldev = pctldev;
+
+ ret = pinctrl_add_gpio_pingrp_range(pctldev, &pin_range->range,
+ pin_group);
+ if (ret < 0) {
+ pr_err("%s: GPIO chip: could not create pin range %s\n",
+ chip->label, pin_group);
+ return ret;
+ }
+
+ pr_debug("GPIO chip %s: created GPIO range %d->%d ==> %s PINGRP %s\n",
+ chip->label, gpio_offset,
+ gpio_offset + pin_range->range.npins - 1,
+ pinctrl_dev_get_devname(pctldev), pin_group);
+
+ list_add_tail(&pin_range->node, &chip->pin_ranges);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
+
+/**
* gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping
* @chip: the gpiochip to add the range for
* @pinctrl_name: the dev_name() of the pin controller to map to
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index 25bb17e..3730c4f 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -452,6 +452,29 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
}
EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range);

+int pinctrl_add_gpio_pingrp_range(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ char *pin_group)
+{
+ const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ int group_selector, ret;
+
+ group_selector = pinctrl_get_group_selector(pctldev, pin_group);
+ if (group_selector < 0)
+ return group_selector;
+
+ ret = pctlops->get_group_pins(pctldev, group_selector,
+ &range->pins,
+ &range->npins);
+ if (ret < 0)
+ return ret;
+
+ pinctrl_add_gpio_range(pctldev, range);
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(pinctrl_add_gpio_pingrp_range);
+
/**
* pinctrl_find_gpio_range_from_pin() - locate the GPIO range for a pin
* @pctldev: the pin controller device to look in
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index bde6469..523f405 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -228,6 +228,9 @@ struct gpio_pin_range {
int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
unsigned int gpio_offset, unsigned int pin_offset,
unsigned int npins);
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group);
void gpiochip_remove_pin_ranges(struct gpio_chip *chip);

#else
@@ -239,6 +242,13 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
{
return 0;
}
+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ return 0;
+}

static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 552e3f4..234b32f 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -220,6 +220,15 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
return -EINVAL;
}

+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ WARN_ON(1);
+ return -EINVAL;
+}
+
static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
{
diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 176a6c1..286c5e5 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -141,6 +141,9 @@ extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev,

extern struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
struct pinctrl_gpio_range *range);
+extern int pinctrl_add_gpio_pingrp_range(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ char *pin_group);
extern struct pinctrl_gpio_range *
pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
unsigned int pin);
--
1.7.1

2013-06-13 12:56:12

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 1/2] Add pin list based GPIO ranges

Traditionally, GPIO ranges are based on consecutive ranges of both GPIO
and pin numbers. This patch allows for GPIO ranges with arbitrary lists
of pin numbers.

Signed-off-by: Christian Ruppert <[email protected]>
---
drivers/pinctrl/core.c | 59 ++++++++++++++++++++++++++++++++------
include/linux/pinctrl/pinctrl.h | 4 ++-
2 files changed, 52 insertions(+), 11 deletions(-)

diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index 5327f35..25bb17e 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -280,6 +280,29 @@ static int pinctrl_register_pins(struct pinctrl_dev *pctldev,
}

/**
+ * gpio_to_pin() - GPIO range GPIO number to pin number translation
+ * @range: GPIO range used for the translation
+ * @gpio: gpio pin to translate to a pin number
+ *
+ * Finds the pin number for a given GPIO using the specified GPIO range
+ * as a base for translation. The distinction between linear GPIO ranges
+ * and pin list based GPIO ranges is managed correctly by this function.
+ *
+ * This function assumes the gpio is part of the specified GPIO range, use
+ * only after making sure this is the case (e.g. by calling it on the
+ * result of successful pinctrl_get_device_gpio_range calls)!
+ */
+static inline int gpio_to_pin(struct pinctrl_gpio_range *range,
+ unsigned int gpio)
+{
+ unsigned int offset = gpio - range->base;
+ if (range->pins)
+ return range->pins[offset];
+ else
+ return range->pin_base + offset;
+}
+
+/**
* pinctrl_match_gpio_range() - check if a certain GPIO pin is in range
* @pctldev: pin controller device to check
* @gpio: gpio pin to check taken from the global GPIO pin space
@@ -444,8 +467,14 @@ pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
/* Loop over the ranges */
list_for_each_entry(range, &pctldev->gpio_ranges, node) {
/* Check if we're in the valid range */
- if (pin >= range->pin_base &&
- pin < range->pin_base + range->npins) {
+ if (range->pins) {
+ int a;
+ for (a = 0; a < range->npins; a++) {
+ if (range->pins[a] == pin)
+ return range;
+ }
+ } else if (pin >= range->pin_base &&
+ pin < range->pin_base + range->npins) {
mutex_unlock(&pctldev->mutex);
return range;
}
@@ -528,7 +557,7 @@ int pinctrl_request_gpio(unsigned gpio)
}

/* Convert to the pin controllers number space */
- pin = gpio - range->base + range->pin_base;
+ pin = gpio_to_pin(range, gpio);

ret = pinmux_request_gpio(pctldev, range, pin, gpio);

@@ -562,7 +591,7 @@ void pinctrl_free_gpio(unsigned gpio)
mutex_lock(&pctldev->mutex);

/* Convert to the pin controllers number space */
- pin = gpio - range->base + range->pin_base;
+ pin = gpio_to_pin(range, gpio);

pinmux_free_gpio(pctldev, pin, range);

@@ -589,7 +618,7 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input)
mutex_lock(&pctldev->mutex);

/* Convert to the pin controllers number space */
- pin = gpio - range->base + range->pin_base;
+ pin = gpio_to_pin(range, gpio);
ret = pinmux_gpio_direction(pctldev, range, pin, input);

mutex_unlock(&pctldev->mutex);
@@ -1296,11 +1325,21 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what)

/* Loop over the ranges */
list_for_each_entry(range, &pctldev->gpio_ranges, node) {
- seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n",
- range->id, range->name,
- range->base, (range->base + range->npins - 1),
- range->pin_base,
- (range->pin_base + range->npins - 1));
+ if (range->pins) {
+ int a;
+ seq_printf(s, "%u: %s GPIOS [%u - %u] PINS {",
+ range->id, range->name,
+ range->base, (range->base + range->npins - 1));
+ for (a = 0; a < range->npins - 1; a++)
+ seq_printf(s, "%u, ", range->pins[a]);
+ seq_printf(s, "%u}\n", range->pins[a]);
+ }
+ else
+ seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n",
+ range->id, range->name,
+ range->base, (range->base + range->npins - 1),
+ range->pin_base,
+ (range->pin_base + range->npins - 1));
}

mutex_unlock(&pctldev->mutex);
diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 2c2a9e8..176a6c1 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -49,7 +49,8 @@ struct pinctrl_pin_desc {
* @name: a name for the chip in this range
* @id: an ID number for the chip in this range
* @base: base offset of the GPIO range
- * @pin_base: base pin number of the GPIO range
+ * @pin_base: base pin number of the GPIO range if pins != NULL
+ * @pins: enumeration of pins in GPIO range or NULL
* @npins: number of pins in the GPIO range, including the base number
* @gc: an optional pointer to a gpio_chip
*/
@@ -59,6 +60,7 @@ struct pinctrl_gpio_range {
unsigned int id;
unsigned int base;
unsigned int pin_base;
+ unsigned const *pins;
unsigned int npins;
struct gpio_chip *gc;
};
--
1.7.1

2013-06-13 18:30:17

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/2] Add pin list based GPIO ranges

On Thu, Jun 13, 2013 at 2:55 PM, Christian Ruppert
<[email protected]> wrote:

> Traditionally, GPIO ranges are based on consecutive ranges of both GPIO
> and pin numbers. This patch allows for GPIO ranges with arbitrary lists
> of pin numbers.
>
> Signed-off-by: Christian Ruppert <[email protected]>

I really like this patch and it seems to be useful for other purposes so I've
applied it so we get some rotation on it.

Yours,
Linus Walleij

2013-06-13 18:36:24

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On Thu, Jun 13, 2013 at 2:55 PM, Christian Ruppert
<[email protected]> wrote:

> This patch adds the infrastructure required to register non-linear gpio
> ranges through gpiolib and the standard GPIO device tree bindings.
>
> Signed-off-by: Christian Ruppert <[email protected]>

Splendid. I'll just wait some more time before applying so that others
get a chance to comment on this, but I'm happy!

Yours,
Linus Walleij

2013-06-13 21:38:15

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On 06/13/2013 06:55 AM, Christian Ruppert wrote:
> This patch adds the infrastructure required to register non-linear gpio
> ranges through gpiolib and the standard GPIO device tree bindings.

That's not exactly true. The existing gpio-ranges property already
allows non-linear ranges to be represented quite easily; each entry in
the gpio-ranges list is <gpio-base> <pinctrl-base> <count>, so you can
piece together any mapping you want.

The potential advantage of this patch is that the pinctrl-side of the
mapping can be a group name rather than pin IDs, which might reduce the
size of the mapping list if you have an extremely sparse or non-linear
mapping /and/ parts of that mapping just happen to align with the pin
groups in the pin controller HW, since each entry in the gpio-ranges
property can be sparse/non-linear, rather than being a small linear
chunk of the mapping.

As an aside, for Tegra I solved this differently: In the pinctrl driver,
I simply defined the pin IDs to exactly match the GPIO IDs in order, so
the mapping is 100% linear. Of course, this only works if your pinctrl
HW documentation doesn't define any kind of numbering/ordering for your
pins, so you can pick any order you want for the pinctrl binding/driver.
This was true for Tegra20, since the HW only used groups for muxing.
Given more recent Tegra SoCs have moved to per-pin muxing, that might
not have been the best decision though, since now the HW registers at
least do have a defined ordering/ID for each pin. If only we'd started
with Tegra30 support not Tegra20:-)

> diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt

> +In addition, named groups of pins can be mapped to pin groups of a given
> +pin controller:
> +
> + gpio_pio_g: gpio-controller@1480 {
> + #gpio-cells = <2>;
> + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
> + reg = <0x1480 0x18>;
> + gpio-controller;
> + gpio-pingrps = <&pinctrl1 0>, <&pinctrl2 3>;
> + gpio-pingrp-names = "pctl1_gpio_g", "pctl2_gpio_g";

A few thoughts here:

"gpio-pingrps" doesn't sound very similar to the existing "gpio-ranges".
Can we make their equivalent purpose more obvious by renaming this
"gpio-group-ranges"?

I'm not actually even sure we need a new property for this, except for
the string group names. We could fold the new gpio-pingrps into the
existing gpio-ranges, whose entries have the format:

<gpio-base> <pinctrl-base> <count>

... by saying that if count==0, it means to use a group name instead of
a pinctrl-base value. We can insist that pinctrl-base==0 in this case
too. If we go made, count==0 could mean "special" and pinctrl-base==0
could mean "by pinctrl group name", and other values of pinctrl-base
could be added later to mean other things!

gpio-ranges =
<&pinctrl1 0 20 10>, /* Existing numeric style */
<&pinctrl2 10 0 0>, /* Count==0, so group name style */
<&pinctrl1 0 20 10>, /* Existing numeric style */
gpio-ranges-group-names =
"", /* No group name required for entry 0 */
"gr1", /* Group name for entry 1 */
""; /* No group name required for entry 2 */

Does this seem better?

> + gpio-pingrp-names = "pctl1_gpio_g", "pctl2_gpio_g";

I'm slightly worried that those example group names appear to be
globally scoped. I would hope the group names are interpreted
relative-to or within the pin controller that they are associated with.
I wouldn't expect the pin controllers to include their own name in the
names of the pin groups they expose. In other words, I'd expect that
example to be more like:

> + gpio-pingrp-names = "foo", "bar";

...
> +The pinctrl node must have a "#gpio-pingrp-cells" property set to one to
> +define the number of arguments to pass with the phandle.

This shouldn't be required.

Such properties are useful when one node references a second node, and
that second node dictates the format of the reference. However, that is
not the case here; the definition of gpio-pingrp-names itself always
dictates its format entirely, and hence the value #gpio-pingrp-cells
must always be 1, and hence there is no point requiring any referenced
node to include this property.

I realize this issue was inherited from the existing gpio-ranges
documentation/implementation, but I've posted patches to solve that,
triggered by thinking about this patch:

https://lists.ozlabs.org/pipermail/devicetree-discuss/2013-June/035462.html
(the rest of the series will make it more clear, i.e. the new of_ API)

> +Both methods can be combined in the same GPIO controller, e.g.

In the proposal I have above, combining the mechanisms is slightly more
cohesive, I think.

> + gpio_pio_i: gpio-controller@14B0 {
> + #gpio-cells = <2>;
> + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
> + reg = <0x1480 0x18>;
> + gpio-controller;
> + gpio-ranges = <&pinctrl1 0 20 10>;
> + gpio-pingrps = <&pinctrl2 10>;
> + gpio-pingrp-names = "gpio_g_pins";
> + };

2013-06-14 07:18:24

by Patrice CHOTARD

[permalink] [raw]
Subject: Re: [PATCH 1/2] Add pin list based GPIO ranges

On 06/13/2013 02:55 PM, Christian Ruppert wrote:

> Traditionally, GPIO ranges are based on consecutive ranges of both GPIO
> and pin numbers. This patch allows for GPIO ranges with arbitrary lists
> of pin numbers.
>
> Signed-off-by: Christian Ruppert <[email protected]>
> ---
> drivers/pinctrl/core.c | 59 ++++++++++++++++++++++++++++++++------
> include/linux/pinctrl/pinctrl.h | 4 ++-
> 2 files changed, 52 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
> index 5327f35..25bb17e 100644
> --- a/drivers/pinctrl/core.c
> +++ b/drivers/pinctrl/core.c
> @@ -280,6 +280,29 @@ static int pinctrl_register_pins(struct pinctrl_dev *pctldev,
> }
>
> /**
> + * gpio_to_pin() - GPIO range GPIO number to pin number translation
> + * @range: GPIO range used for the translation
> + * @gpio: gpio pin to translate to a pin number
> + *
> + * Finds the pin number for a given GPIO using the specified GPIO range
> + * as a base for translation. The distinction between linear GPIO ranges
> + * and pin list based GPIO ranges is managed correctly by this function.
> + *
> + * This function assumes the gpio is part of the specified GPIO range, use
> + * only after making sure this is the case (e.g. by calling it on the
> + * result of successful pinctrl_get_device_gpio_range calls)!
> + */
> +static inline int gpio_to_pin(struct pinctrl_gpio_range *range,
> + unsigned int gpio)
> +{
> + unsigned int offset = gpio - range->base;
> + if (range->pins)
> + return range->pins[offset];
> + else
> + return range->pin_base + offset;
> +}
> +
> +/**
> * pinctrl_match_gpio_range() - check if a certain GPIO pin is in range
> * @pctldev: pin controller device to check
> * @gpio: gpio pin to check taken from the global GPIO pin space
> @@ -444,8 +467,14 @@ pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
> /* Loop over the ranges */
> list_for_each_entry(range, &pctldev->gpio_ranges, node) {
> /* Check if we're in the valid range */
> - if (pin >= range->pin_base &&
> - pin < range->pin_base + range->npins) {
> + if (range->pins) {
> + int a;
> + for (a = 0; a < range->npins; a++) {
> + if (range->pins[a] == pin)
> + return range;
> + }
> + } else if (pin >= range->pin_base &&
> + pin < range->pin_base + range->npins) {
> mutex_unlock(&pctldev->mutex);
> return range;
> }
> @@ -528,7 +557,7 @@ int pinctrl_request_gpio(unsigned gpio)
> }
>
> /* Convert to the pin controllers number space */
> - pin = gpio - range->base + range->pin_base;
> + pin = gpio_to_pin(range, gpio);
>
> ret = pinmux_request_gpio(pctldev, range, pin, gpio);
>
> @@ -562,7 +591,7 @@ void pinctrl_free_gpio(unsigned gpio)
> mutex_lock(&pctldev->mutex);
>
> /* Convert to the pin controllers number space */
> - pin = gpio - range->base + range->pin_base;
> + pin = gpio_to_pin(range, gpio);
>
> pinmux_free_gpio(pctldev, pin, range);
>
> @@ -589,7 +618,7 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input)
> mutex_lock(&pctldev->mutex);
>
> /* Convert to the pin controllers number space */
> - pin = gpio - range->base + range->pin_base;
> + pin = gpio_to_pin(range, gpio);
> ret = pinmux_gpio_direction(pctldev, range, pin, input);
>
> mutex_unlock(&pctldev->mutex);
> @@ -1296,11 +1325,21 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what)
>
> /* Loop over the ranges */
> list_for_each_entry(range, &pctldev->gpio_ranges, node) {
> - seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n",
> - range->id, range->name,
> - range->base, (range->base + range->npins - 1),
> - range->pin_base,
> - (range->pin_base + range->npins - 1));
> + if (range->pins) {
> + int a;
> + seq_printf(s, "%u: %s GPIOS [%u - %u] PINS {",
> + range->id, range->name,
> + range->base, (range->base + range->npins - 1));
> + for (a = 0; a < range->npins - 1; a++)
> + seq_printf(s, "%u, ", range->pins[a]);
> + seq_printf(s, "%u}\n", range->pins[a]);
> + }
> + else
> + seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n",
> + range->id, range->name,
> + range->base, (range->base + range->npins - 1),
> + range->pin_base,
> + (range->pin_base + range->npins - 1));
> }
>
> mutex_unlock(&pctldev->mutex);
> diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
> index 2c2a9e8..176a6c1 100644
> --- a/include/linux/pinctrl/pinctrl.h
> +++ b/include/linux/pinctrl/pinctrl.h
> @@ -49,7 +49,8 @@ struct pinctrl_pin_desc {
> * @name: a name for the chip in this range
> * @id: an ID number for the chip in this range
> * @base: base offset of the GPIO range
> - * @pin_base: base pin number of the GPIO range
> + * @pin_base: base pin number of the GPIO range if pins != NULL



Hi Christian,

It seems that your comment is not correct, it should be :
* @pin_base: base pin number of the GPIO range if pins == NULL

Patrice

> + * @pins: enumeration of pins in GPIO range or NULL
> * @npins: number of pins in the GPIO range, including the base number
> * @gc: an optional pointer to a gpio_chip
> */
> @@ -59,6 +60,7 @@ struct pinctrl_gpio_range {
> unsigned int id;
> unsigned int base;
> unsigned int pin_base;
> + unsigned const *pins;
> unsigned int npins;
> struct gpio_chip *gc;
> };


2013-06-14 08:25:28

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH] Fix comment on pinctrl_gpio_range.pin_base

The comment introduced with the recently added pinctrl_gpio_range.pins
element was wrong. This corrects it.
Thanks to Patrice Chotard for pointing this out.

Signed-off-by: Christian Ruppert <[email protected]>
---
include/linux/pinctrl/pinctrl.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 286c5e5..47ab2fb 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -49,7 +49,7 @@ struct pinctrl_pin_desc {
* @name: a name for the chip in this range
* @id: an ID number for the chip in this range
* @base: base offset of the GPIO range
- * @pin_base: base pin number of the GPIO range if pins != NULL
+ * @pin_base: base pin number of the GPIO range if pins == NULL
* @pins: enumeration of pins in GPIO range or NULL
* @npins: number of pins in the GPIO range, including the base number
* @gc: an optional pointer to a gpio_chip
--
1.7.1

2013-06-14 09:13:19

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On Thu, Jun 13, 2013 at 03:38:09PM -0600, Stephen Warren wrote:
> On 06/13/2013 06:55 AM, Christian Ruppert wrote:
> > This patch adds the infrastructure required to register non-linear gpio
> > ranges through gpiolib and the standard GPIO device tree bindings.
>
> That's not exactly true. The existing gpio-ranges property already
> allows non-linear ranges to be represented quite easily; each entry in
> the gpio-ranges list is <gpio-base> <pinctrl-base> <count>, so you can
> piece together any mapping you want.

You're right, my description is somewhat imprecise here.

> The potential advantage of this patch is that the pinctrl-side of the
> mapping can be a group name rather than pin IDs, which might reduce the
> size of the mapping list if you have an extremely sparse or non-linear
> mapping /and/ parts of that mapping just happen to align with the pin
> groups in the pin controller HW, since each entry in the gpio-ranges
> property can be sparse/non-linear, rather than being a small linear
> chunk of the mapping.

Pin controller authors have the freedom to define pin groups just for
the purpose of "predefining" the pinctrl side of GPIO ranges. For
example, the pinctrl driver from the original patch in this discussion
has different types of pin groups treated differently by the driver's
internal logic.

> As an aside, for Tegra I solved this differently: In the pinctrl driver,
> I simply defined the pin IDs to exactly match the GPIO IDs in order, so
> the mapping is 100% linear. Of course, this only works if your pinctrl
> HW documentation doesn't define any kind of numbering/ordering for your
> pins, so you can pick any order you want for the pinctrl binding/driver.
> This was true for Tegra20, since the HW only used groups for muxing.
> Given more recent Tegra SoCs have moved to per-pin muxing, that might
> not have been the best decision though, since now the HW registers at
> least do have a defined ordering/ID for each pin. If only we'd started
> with Tegra30 support not Tegra20:-)

My motivation of this patch is exactly the same problem put differently:
Confine the pin numbering (or what could be called a pin data base) to
the pin controller and free it from external constraints (which can be
gpio-ranges consecutiveness, customer documentation coherency or
whatever).

> > diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
>
> > +In addition, named groups of pins can be mapped to pin groups of a given
> > +pin controller:
> > +
> > + gpio_pio_g: gpio-controller@1480 {
> > + #gpio-cells = <2>;
> > + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
> > + reg = <0x1480 0x18>;
> > + gpio-controller;
> > + gpio-pingrps = <&pinctrl1 0>, <&pinctrl2 3>;
> > + gpio-pingrp-names = "pctl1_gpio_g", "pctl2_gpio_g";
>
> A few thoughts here:
>
> "gpio-pingrps" doesn't sound very similar to the existing "gpio-ranges".
> Can we make their equivalent purpose more obvious by renaming this
> "gpio-group-ranges"?
>
> I'm not actually even sure we need a new property for this, except for
> the string group names. We could fold the new gpio-pingrps into the
> existing gpio-ranges, whose entries have the format:
>
> <gpio-base> <pinctrl-base> <count>
>
> ... by saying that if count==0, it means to use a group name instead of
> a pinctrl-base value. We can insist that pinctrl-base==0 in this case
> too. If we go made, count==0 could mean "special" and pinctrl-base==0
> could mean "by pinctrl group name", and other values of pinctrl-base
> could be added later to mean other things!
>
> gpio-ranges =
> <&pinctrl1 0 20 10>, /* Existing numeric style */
> <&pinctrl2 10 0 0>, /* Count==0, so group name style */
> <&pinctrl1 0 20 10>, /* Existing numeric style */
> gpio-ranges-group-names =
> "", /* No group name required for entry 0 */
> "gr1", /* Group name for entry 1 */
> ""; /* No group name required for entry 2 */
>
> Does this seem better?

I had a similar hesitation when implementing this and found the
gpio-ranges-contains-everything approach slightly confusing. On the
other hand I think it is more elegant to have only one property (plus
the strings). I didn't reach a clear conclusion and I'll happily follow
the advice of people having more device-tree experience than myself.
Just tell me if you prefer to rename the gpio-pingrps or merge
everything into gpio-ranges.

> > + gpio-pingrp-names = "pctl1_gpio_g", "pctl2_gpio_g";
>
> I'm slightly worried that those example group names appear to be
> globally scoped. I would hope the group names are interpreted
> relative-to or within the pin controller that they are associated with.

They are relative to the pin controller that they are associated with.

> I wouldn't expect the pin controllers to include their own name in the
> names of the pin groups they expose. In other words, I'd expect that
> example to be more like:
>
> > + gpio-pingrp-names = "foo", "bar";

OK, I'll change that in the doc.

> > +The pinctrl node must have a "#gpio-pingrp-cells" property set to one to
> > +define the number of arguments to pass with the phandle.
>
> This shouldn't be required.
>
> Such properties are useful when one node references a second node, and
> that second node dictates the format of the reference. However, that is
> not the case here; the definition of gpio-pingrp-names itself always
> dictates its format entirely, and hence the value #gpio-pingrp-cells
> must always be 1, and hence there is no point requiring any referenced
> node to include this property.

This is a residue from my hesitation between a single gpio-ranges
property and the proposed format: My alternative idea was that a pin
controller either provides pin numbers or named gpio ranges and that
#gpio-range-cells defines which (3 for pin numbers, 1 for named ranges).
Your proposal with the 0s is probably more flexible for future
extensions.

> I realize this issue was inherited from the existing gpio-ranges
> documentation/implementation, but I've posted patches to solve that,
> triggered by thinking about this patch:
>
> https://lists.ozlabs.org/pipermail/devicetree-discuss/2013-June/035462.html
> (the rest of the series will make it more clear, i.e. the new of_ API)
>
> > +Both methods can be combined in the same GPIO controller, e.g.
>
> In the proposal I have above, combining the mechanisms is slightly more
> cohesive, I think.

I agree. Shall I go for the combined gpio-ranges then?

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-16 10:19:35

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH] Fix comment on pinctrl_gpio_range.pin_base

On Fri, Jun 14, 2013 at 10:24 AM, Christian Ruppert
<[email protected]> wrote:

> The comment introduced with the recently added pinctrl_gpio_range.pins
> element was wrong. This corrects it.
> Thanks to Patrice Chotard for pointing this out.
>
> Signed-off-by: Christian Ruppert <[email protected]>

Patch applied.

Yours,
Linus Walleij

2013-06-16 11:11:25

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/2] pinmux: Add TB10x pinmux driver

On Tue, Jun 11, 2013 at 9:27 AM, Christian Ruppert
<[email protected]> wrote:
> On Fri, Jun 07, 2013 at 01:18:35PM -0600, Stephen Warren wrote:

>> However, then the correlation between these pretend pins (i.e. really
>> the groups) and GPIOs won't work, because each "pin" is really 4 pins,
>> and hence 4 GPIOs, and hence you won't be able to gpio_get() more than 1
>> GPIO per pin group, I think.
>
> I agree: Unluckily, pinctrl-single doesn't seem to be what we are looking
> for.

I agree on this conclusion.

> We already have a draft for a native pinctrl driver (see original post)
> but there was some disagreement about exposing kernel internal pin
> numbering to device tree users.

But that is an orthogonal issue I believe.

Yours,
Linus Walleij

2013-06-17 16:04:01

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On Thu, Jun 13, 2013 at 03:38:09PM -0600, Stephen Warren wrote:
> On 06/13/2013 06:55 AM, Christian Ruppert wrote:
> [...]
> > diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
>
> > +In addition, named groups of pins can be mapped to pin groups of a given
> > +pin controller:
> > +
> > + gpio_pio_g: gpio-controller@1480 {
> > + #gpio-cells = <2>;
> > + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
> > + reg = <0x1480 0x18>;
> > + gpio-controller;
> > + gpio-pingrps = <&pinctrl1 0>, <&pinctrl2 3>;
> > + gpio-pingrp-names = "pctl1_gpio_g", "pctl2_gpio_g";
>
> A few thoughts here:
>
> "gpio-pingrps" doesn't sound very similar to the existing "gpio-ranges".
> Can we make their equivalent purpose more obvious by renaming this
> "gpio-group-ranges"?
>
> I'm not actually even sure we need a new property for this, except for
> the string group names. We could fold the new gpio-pingrps into the
> existing gpio-ranges, whose entries have the format:
>
> <gpio-base> <pinctrl-base> <count>
>
> ... by saying that if count==0, it means to use a group name instead of
> a pinctrl-base value. We can insist that pinctrl-base==0 in this case
> too. If we go made, count==0 could mean "special" and pinctrl-base==0
> could mean "by pinctrl group name", and other values of pinctrl-base
> could be added later to mean other things!
>
> gpio-ranges =
> <&pinctrl1 0 20 10>, /* Existing numeric style */
> <&pinctrl2 10 0 0>, /* Count==0, so group name style */
> <&pinctrl1 0 20 10>, /* Existing numeric style */
> gpio-ranges-group-names =
> "", /* No group name required for entry 0 */
> "gr1", /* Group name for entry 1 */
> ""; /* No group name required for entry 2 */
>
> Does this seem better?
> [...]

In the follow-up a patch set based on this proposal. I have also
included our drivers (and device tree updates) with this to see how this
looks in a real (working) system. To me it does actually look quite nice
and logical but take a look at it yourselves and tell me what you think.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-17 16:05:17

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 1/4] Make non-linear GPIO ranges accesible from gpiolib

This patch adds the infrastructure required to register non-linear gpio
ranges through gpiolib and the standard GPIO device tree bindings.

Signed-off-by: Christian Ruppert <[email protected]>
---
Documentation/devicetree/bindings/gpio/gpio.txt | 44 ++++++++++++++++++
arch/arc/boot/dts/abilis_tb100.dtsi | 56 +++++++++++-----------
arch/arc/boot/dts/abilis_tb101.dtsi | 56 +++++++++++-----------
arch/arc/boot/dts/abilis_tb10x.dtsi | 3 +-
drivers/gpio/gpiolib-of.c | 31 +++++++++---
drivers/gpio/gpiolib.c | 47 +++++++++++++++++++
drivers/pinctrl/core.c | 23 +++++++++
include/asm-generic/gpio.h | 10 ++++
include/linux/gpio.h | 9 ++++
include/linux/pinctrl/pinctrl.h | 3 +
10 files changed, 216 insertions(+), 66 deletions(-)

diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
index d933af3..c8afbea 100644
--- a/Documentation/devicetree/bindings/gpio/gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio.txt
@@ -112,3 +112,47 @@ where,

The pinctrl node must have "#gpio-range-cells" property to show number of
arguments to pass with phandle from gpio controllers node.
+
+In addition, named groups of pins can be mapped to pin groups of a given
+pin controller:
+
+ gpio_pio_g: gpio-controller@1480 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1480 0x18>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl1 0 0 0>, <&pinctrl2 3 0 0>;
+ gpio-ranges-group-names = "foo", "bar";
+ };
+
+where,
+ &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
+
+ The following value specifies the base GPIO offset of the pin range with
+ respect to the GPIO controller's base. The remaining two values must be
+ 0 to indicate that a named pin group should be used for the respective
+ range. The number of pins in the range is the number of pins in the pin
+ group.
+
+ gpio-ranges-group-names defines the name of each pingroup of the
+ respective pin controller.
+
+The pinctrl node must have a "#gpio-#gpio-range-cells" property set to three
+to define the number of arguments to pass with the phandle.
+
+Both methods can be combined in the same GPIO controller, e.g.
+
+ gpio_pio_i: gpio-controller@14B0 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1480 0x18>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl1 0 20 10>,
+ <&pinctrl2 10 0 0>,
+ <&pinctrl1 15 0 10>,
+ <&pinctrl2 25 0 0>;
+ gpio-ranges-group-names = "",
+ "foo",
+ "",
+ "bar";
+ };
diff --git a/arch/arc/boot/dts/abilis_tb100.dtsi b/arch/arc/boot/dts/abilis_tb100.dtsi
index 941ad11..1a42cf1 100644
--- a/arch/arc/boot/dts/abilis_tb100.dtsi
+++ b/arch/arc/boot/dts/abilis_tb100.dtsi
@@ -177,8 +177,8 @@
reg = <0xFF140000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <0>;
- gpio-pins = <&pctl_gpio_a>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioa_pins";
};
gpiob: gpio@FF141000 {
compatible = "abilis,tb10x-gpio";
@@ -189,8 +189,8 @@
reg = <0xFF141000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <3>;
- gpio-pins = <&pctl_gpio_b>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiob_pins";
};
gpioc: gpio@FF142000 {
compatible = "abilis,tb10x-gpio";
@@ -201,8 +201,8 @@
reg = <0xFF142000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <5>;
- gpio-pins = <&pctl_gpio_c>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioc_pins";
};
gpiod: gpio@FF143000 {
compatible = "abilis,tb10x-gpio";
@@ -213,8 +213,8 @@
reg = <0xFF143000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <8>;
- gpio-pins = <&pctl_gpio_d>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiod_pins";
};
gpioe: gpio@FF144000 {
compatible = "abilis,tb10x-gpio";
@@ -225,8 +225,8 @@
reg = <0xFF144000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <10>;
- gpio-pins = <&pctl_gpio_e>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioe_pins";
};
gpiof: gpio@FF145000 {
compatible = "abilis,tb10x-gpio";
@@ -237,8 +237,8 @@
reg = <0xFF145000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <13>;
- gpio-pins = <&pctl_gpio_f>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiof_pins";
};
gpiog: gpio@FF146000 {
compatible = "abilis,tb10x-gpio";
@@ -249,8 +249,8 @@
reg = <0xFF146000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <15>;
- gpio-pins = <&pctl_gpio_g>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiog_pins";
};
gpioh: gpio@FF147000 {
compatible = "abilis,tb10x-gpio";
@@ -261,8 +261,8 @@
reg = <0xFF147000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <18>;
- gpio-pins = <&pctl_gpio_h>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioh_pins";
};
gpioi: gpio@FF148000 {
compatible = "abilis,tb10x-gpio";
@@ -273,8 +273,8 @@
reg = <0xFF148000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <20>;
- gpio-pins = <&pctl_gpio_i>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioi_pins";
};
gpioj: gpio@FF149000 {
compatible = "abilis,tb10x-gpio";
@@ -285,8 +285,8 @@
reg = <0xFF149000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <32>;
- gpio-pins = <&pctl_gpio_j>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioj_pins";
};
gpiok: gpio@FF14a000 {
compatible = "abilis,tb10x-gpio";
@@ -297,8 +297,8 @@
reg = <0xFF14A000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <64>;
- gpio-pins = <&pctl_gpio_k>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiok_pins";
};
gpiol: gpio@FF14b000 {
compatible = "abilis,tb10x-gpio";
@@ -309,8 +309,8 @@
reg = <0xFF14B000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <86>;
- gpio-pins = <&pctl_gpio_l>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiol_pins";
};
gpiom: gpio@FF14c000 {
compatible = "abilis,tb10x-gpio";
@@ -321,8 +321,8 @@
reg = <0xFF14C000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <90>;
- gpio-pins = <&pctl_gpio_m>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiom_pins";
};
gpion: gpio@FF14d000 {
compatible = "abilis,tb10x-gpio";
@@ -333,8 +333,8 @@
reg = <0xFF14D000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <94>;
- gpio-pins = <&pctl_gpio_n>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpion_pins";
};
};
};
diff --git a/arch/arc/boot/dts/abilis_tb101.dtsi b/arch/arc/boot/dts/abilis_tb101.dtsi
index fd25c21..08986cd 100644
--- a/arch/arc/boot/dts/abilis_tb101.dtsi
+++ b/arch/arc/boot/dts/abilis_tb101.dtsi
@@ -186,8 +186,8 @@
reg = <0xFF140000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <0>;
- gpio-pins = <&pctl_gpio_a>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioa_pins";
};
gpiob: gpio@FF141000 {
compatible = "abilis,tb10x-gpio";
@@ -198,8 +198,8 @@
reg = <0xFF141000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <3>;
- gpio-pins = <&pctl_gpio_b>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiob_pins";
};
gpioc: gpio@FF142000 {
compatible = "abilis,tb10x-gpio";
@@ -210,8 +210,8 @@
reg = <0xFF142000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <5>;
- gpio-pins = <&pctl_gpio_c>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioc_pins";
};
gpiod: gpio@FF143000 {
compatible = "abilis,tb10x-gpio";
@@ -222,8 +222,8 @@
reg = <0xFF143000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <8>;
- gpio-pins = <&pctl_gpio_d>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiod_pins";
};
gpioe: gpio@FF144000 {
compatible = "abilis,tb10x-gpio";
@@ -234,8 +234,8 @@
reg = <0xFF144000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <10>;
- gpio-pins = <&pctl_gpio_e>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioe_pins";
};
gpiof: gpio@FF145000 {
compatible = "abilis,tb10x-gpio";
@@ -246,8 +246,8 @@
reg = <0xFF145000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <13>;
- gpio-pins = <&pctl_gpio_f>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiof_pins";
};
gpiog: gpio@FF146000 {
compatible = "abilis,tb10x-gpio";
@@ -258,8 +258,8 @@
reg = <0xFF146000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <15>;
- gpio-pins = <&pctl_gpio_g>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiog_pins";
};
gpioh: gpio@FF147000 {
compatible = "abilis,tb10x-gpio";
@@ -270,8 +270,8 @@
reg = <0xFF147000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <18>;
- gpio-pins = <&pctl_gpio_h>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioh_pins";
};
gpioi: gpio@FF148000 {
compatible = "abilis,tb10x-gpio";
@@ -282,8 +282,8 @@
reg = <0xFF148000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <20>;
- gpio-pins = <&pctl_gpio_i>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioi_pins";
};
gpioj: gpio@FF149000 {
compatible = "abilis,tb10x-gpio";
@@ -294,8 +294,8 @@
reg = <0xFF149000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <32>;
- gpio-pins = <&pctl_gpio_j>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioj_pins";
};
gpiok: gpio@FF14a000 {
compatible = "abilis,tb10x-gpio";
@@ -306,8 +306,8 @@
reg = <0xFF14A000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <64>;
- gpio-pins = <&pctl_gpio_k>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiok_pins";
};
gpiol: gpio@FF14b000 {
compatible = "abilis,tb10x-gpio";
@@ -318,8 +318,8 @@
reg = <0xFF14B000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <86>;
- gpio-pins = <&pctl_gpio_l>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiol_pins";
};
gpiom: gpio@FF14c000 {
compatible = "abilis,tb10x-gpio";
@@ -330,8 +330,8 @@
reg = <0xFF14C000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <90>;
- gpio-pins = <&pctl_gpio_m>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpiom_pins";
};
gpion: gpio@FF14d000 {
compatible = "abilis,tb10x-gpio";
@@ -342,8 +342,8 @@
reg = <0xFF14D000 0x1000>;
gpio-controller;
#gpio-cells = <1>;
- gpio-base = <94>;
- gpio-pins = <&pctl_gpio_n>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpion_pins";
};
};
};
diff --git a/arch/arc/boot/dts/abilis_tb10x.dtsi b/arch/arc/boot/dts/abilis_tb10x.dtsi
index b97e305..62bc952 100644
--- a/arch/arc/boot/dts/abilis_tb10x.dtsi
+++ b/arch/arc/boot/dts/abilis_tb10x.dtsi
@@ -66,9 +66,8 @@
};

iomux: iomux@FF10601c {
- #address-cells = <1>;
- #size-cells = <1>;
compatible = "abilis,tb10x-iomux";
+ #gpio-range-cells = <3>;
reg = <0xFF10601c 0x4>;
};

diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 665f953..e29cc71 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -189,6 +189,7 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
struct of_phandle_args pinspec;
struct pinctrl_dev *pctldev;
int index = 0, ret;
+ const char *name;

if (!np)
return;
@@ -203,14 +204,28 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
if (!pctldev)
break;

- ret = gpiochip_add_pin_range(chip,
- pinctrl_dev_get_devname(pctldev),
- pinspec.args[0],
- pinspec.args[1],
- pinspec.args[2]);
-
- if (ret)
- break;
+ if (pinspec.args[2]) {
+ /* npins != 0: linear range */
+ ret = gpiochip_add_pin_range(chip,
+ pinctrl_dev_get_devname(pctldev),
+ pinspec.args[0],
+ pinspec.args[1],
+ pinspec.args[2]);
+ if (ret)
+ break;
+ } else {
+ /* npins == 0: pin group based range */
+ ret = of_property_read_string_index(np,
+ "gpio-ranges-group-names",
+ index, &name);
+ if (ret)
+ break;
+
+ ret = gpiochip_add_pingroup_range(chip, pctldev,
+ pinspec.args[0], name);
+ if (ret)
+ break;
+ }
}
}

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c2534d6..b2dc810 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1318,6 +1318,53 @@ EXPORT_SYMBOL_GPL(gpiochip_find);
#ifdef CONFIG_PINCTRL

/**
+ * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping
+ * @chip: the gpiochip to add the range for
+ * @pinctrl: the dev_name() of the pin controller to map to
+ * @gpio_offset: the start offset in the current gpio_chip number space
+ * @pin_group: name of the pin group inside the pin controller
+ */
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ struct gpio_pin_range *pin_range;
+ int ret;
+
+ pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
+ if (!pin_range) {
+ pr_err("%s: GPIO chip: failed to allocate pin ranges\n",
+ chip->label);
+ return -ENOMEM;
+ }
+
+ /* Use local offset as range ID */
+ pin_range->range.id = gpio_offset;
+ pin_range->range.gc = chip;
+ pin_range->range.name = chip->label;
+ pin_range->range.base = chip->base + gpio_offset;
+ pin_range->pctldev = pctldev;
+
+ ret = pinctrl_add_gpio_pingrp_range(pctldev, &pin_range->range,
+ pin_group);
+ if (ret < 0) {
+ pr_err("%s: GPIO chip: could not create pin range %s\n",
+ chip->label, pin_group);
+ return ret;
+ }
+
+ pr_debug("GPIO chip %s: created GPIO range %d->%d ==> %s PINGRP %s\n",
+ chip->label, gpio_offset,
+ gpio_offset + pin_range->range.npins - 1,
+ pinctrl_dev_get_devname(pctldev), pin_group);
+
+ list_add_tail(&pin_range->node, &chip->pin_ranges);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
+
+/**
* gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping
* @chip: the gpiochip to add the range for
* @pinctrl_name: the dev_name() of the pin controller to map to
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index 25bb17e..3730c4f 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -452,6 +452,29 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
}
EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range);

+int pinctrl_add_gpio_pingrp_range(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ char *pin_group)
+{
+ const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ int group_selector, ret;
+
+ group_selector = pinctrl_get_group_selector(pctldev, pin_group);
+ if (group_selector < 0)
+ return group_selector;
+
+ ret = pctlops->get_group_pins(pctldev, group_selector,
+ &range->pins,
+ &range->npins);
+ if (ret < 0)
+ return ret;
+
+ pinctrl_add_gpio_range(pctldev, range);
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(pinctrl_add_gpio_pingrp_range);
+
/**
* pinctrl_find_gpio_range_from_pin() - locate the GPIO range for a pin
* @pctldev: the pin controller device to look in
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index bde6469..523f405 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -228,6 +228,9 @@ struct gpio_pin_range {
int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
unsigned int gpio_offset, unsigned int pin_offset,
unsigned int npins);
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group);
void gpiochip_remove_pin_ranges(struct gpio_chip *chip);

#else
@@ -239,6 +242,13 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
{
return 0;
}
+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ return 0;
+}

static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 552e3f4..234b32f 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -220,6 +220,15 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
return -EINVAL;
}

+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ WARN_ON(1);
+ return -EINVAL;
+}
+
static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
{
diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 5979147..47ab2fb 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -141,6 +141,9 @@ extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev,

extern struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
struct pinctrl_gpio_range *range);
+extern int pinctrl_add_gpio_pingrp_range(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ char *pin_group);
extern struct pinctrl_gpio_range *
pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
unsigned int pin);
--
1.7.1

2013-06-17 16:05:32

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 4/4] Add Abilis Systems Sarl to device tree vendor prefixes


Signed-off-by: Christian Ruppert <[email protected]>
---
.../devicetree/bindings/vendor-prefixes.txt | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 6931c43..e2a8fff 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -3,6 +3,7 @@ Device tree binding vendor prefix registry. Keep list in alphabetical order.
This isn't an exhaustive list, but you should add new prefixes to it before
using them to avoid name-space collisions.

+abilis Abilis Systems Sarl
ad Avionic Design GmbH
adi Analog Devices, Inc.
aeroflexgaisler Aeroflex Gaisler AB
--
1.7.1

2013-06-17 16:05:45

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 3/4] GPIO: Add TB10x GPIO driver

The GPIO driver for the Abilis Systems TB10x series of SOCs based on ARC700
CPUs. It supports GPIO control and GPIO interrupt generation.

Signed-off-by: Sascha Leuenberger <[email protected]>
Signed-off-by: Christian Ruppert <[email protected]>
---
.../devicetree/bindings/gpio/abilis,tb10x-gpio.txt | 36 ++
arch/arc/boot/dts/abilis_tb100.dtsi | 42 ++-
arch/arc/boot/dts/abilis_tb100_dvk.dts | 24 +-
arch/arc/boot/dts/abilis_tb101.dtsi | 42 ++-
arch/arc/boot/dts/abilis_tb101_dvk.dts | 24 +-
drivers/gpio/Kconfig | 4 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-tb10x.c | 341 ++++++++++++++++++++
8 files changed, 462 insertions(+), 52 deletions(-)
create mode 100644 Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
create mode 100644 drivers/gpio/gpio-tb10x.c

diff --git a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
new file mode 100644
index 0000000..26a3c68
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
@@ -0,0 +1,36 @@
+* Abilis TB10x GPIO controller
+
+Required Properties:
+- compatible: Should be "abilis,tb10x-gpio"
+- reg: Address and length of the register set for the device
+- gpio-controller: Marks the device node as a gpio controller.
+- #gpio-cells: Should be <2>. The first cell is the pin number and the
+ second cell is used to specify optional parameters:
+ - bit 0 specifies polarity (0 for normal, 1 for inverted).
+- ngpio: the number of GPIO pins this driver controls.
+
+Optional Properties:
+- interrupt-controller: Marks the device node as an interrupt controller.
+- #interrupt-cells: Should be <1>. Interrupts are triggered on both edges.
+- interrupts: Defines the interrupt line connecting this GPIO controller to
+ its parent interrupt controller.
+- interrupt-parent: Defines the parent interrupt controller.
+
+GPIO ranges are specified as described in
+Documentation/devicetree/bindings/gpio/gpio.txt
+
+Example:
+
+ gpioa: gpio@FF140000 {
+ compatible = "abilis,tb10x-gpio";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&tb10x_ictl>;
+ interrupts = <27 2>;
+ reg = <0xFF140000 0x1000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpio = <3>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioa_pins";
+ };
diff --git a/arch/arc/boot/dts/abilis_tb100.dtsi b/arch/arc/boot/dts/abilis_tb100.dtsi
index 1a42cf1..f1fa416 100644
--- a/arch/arc/boot/dts/abilis_tb100.dtsi
+++ b/arch/arc/boot/dts/abilis_tb100.dtsi
@@ -176,7 +176,8 @@
interrupts = <27 1>;
reg = <0xFF140000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <3>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpioa_pins";
};
@@ -188,7 +189,8 @@
interrupts = <27 1>;
reg = <0xFF141000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <2>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiob_pins";
};
@@ -200,7 +202,8 @@
interrupts = <27 1>;
reg = <0xFF142000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <3>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpioc_pins";
};
@@ -212,7 +215,8 @@
interrupts = <27 1>;
reg = <0xFF143000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <2>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiod_pins";
};
@@ -224,7 +228,8 @@
interrupts = <27 1>;
reg = <0xFF144000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <3>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpioe_pins";
};
@@ -236,7 +241,8 @@
interrupts = <27 1>;
reg = <0xFF145000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <2>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiof_pins";
};
@@ -248,7 +254,8 @@
interrupts = <27 1>;
reg = <0xFF146000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <3>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiog_pins";
};
@@ -260,7 +267,8 @@
interrupts = <27 1>;
reg = <0xFF147000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <2>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpioh_pins";
};
@@ -272,7 +280,8 @@
interrupts = <27 1>;
reg = <0xFF148000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <12>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpioi_pins";
};
@@ -284,7 +293,8 @@
interrupts = <27 1>;
reg = <0xFF149000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <32>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpioj_pins";
};
@@ -296,7 +306,8 @@
interrupts = <27 1>;
reg = <0xFF14A000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <22>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiok_pins";
};
@@ -308,7 +319,8 @@
interrupts = <27 1>;
reg = <0xFF14B000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <4>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiol_pins";
};
@@ -320,7 +332,8 @@
interrupts = <27 1>;
reg = <0xFF14C000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <4>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiom_pins";
};
@@ -332,7 +345,8 @@
interrupts = <27 1>;
reg = <0xFF14D000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <5>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpion_pins";
};
diff --git a/arch/arc/boot/dts/abilis_tb100_dvk.dts b/arch/arc/boot/dts/abilis_tb100_dvk.dts
index 0fa0d4a..d6af955 100644
--- a/arch/arc/boot/dts/abilis_tb100_dvk.dts
+++ b/arch/arc/boot/dts/abilis_tb100_dvk.dts
@@ -64,62 +64,62 @@
compatible = "gpio-leds";
power {
label = "Power";
- gpios = <&gpioi 0>;
+ gpios = <&gpioi 0 0>;
linux,default-trigger = "default-on";
};
heartbeat {
label = "Heartbeat";
- gpios = <&gpioi 1>;
+ gpios = <&gpioi 1 0>;
linux,default-trigger = "heartbeat";
};
led2 {
label = "LED2";
- gpios = <&gpioi 2>;
+ gpios = <&gpioi 2 0>;
default-state = "off";
};
led3 {
label = "LED3";
- gpios = <&gpioi 3>;
+ gpios = <&gpioi 3 0>;
default-state = "off";
};
led4 {
label = "LED4";
- gpios = <&gpioi 4>;
+ gpios = <&gpioi 4 0>;
default-state = "off";
};
led5 {
label = "LED5";
- gpios = <&gpioi 5>;
+ gpios = <&gpioi 5 0>;
default-state = "off";
};
led6 {
label = "LED6";
- gpios = <&gpioi 6>;
+ gpios = <&gpioi 6 0>;
default-state = "off";
};
led7 {
label = "LED7";
- gpios = <&gpioi 7>;
+ gpios = <&gpioi 7 0>;
default-state = "off";
};
led8 {
label = "LED8";
- gpios = <&gpioi 8>;
+ gpios = <&gpioi 8 0>;
default-state = "off";
};
led9 {
label = "LED9";
- gpios = <&gpioi 9>;
+ gpios = <&gpioi 9 0>;
default-state = "off";
};
led10 {
label = "LED10";
- gpios = <&gpioi 10>;
+ gpios = <&gpioi 10 0>;
default-state = "off";
};
led11 {
label = "LED11";
- gpios = <&gpioi 11>;
+ gpios = <&gpioi 11 0>;
default-state = "off";
};
};
diff --git a/arch/arc/boot/dts/abilis_tb101.dtsi b/arch/arc/boot/dts/abilis_tb101.dtsi
index 08986cd..311116f 100644
--- a/arch/arc/boot/dts/abilis_tb101.dtsi
+++ b/arch/arc/boot/dts/abilis_tb101.dtsi
@@ -185,7 +185,8 @@
interrupts = <27 1>;
reg = <0xFF140000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <3>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpioa_pins";
};
@@ -197,7 +198,8 @@
interrupts = <27 1>;
reg = <0xFF141000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <2>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiob_pins";
};
@@ -209,7 +211,8 @@
interrupts = <27 1>;
reg = <0xFF142000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <3>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpioc_pins";
};
@@ -221,7 +224,8 @@
interrupts = <27 1>;
reg = <0xFF143000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <2>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiod_pins";
};
@@ -233,7 +237,8 @@
interrupts = <27 1>;
reg = <0xFF144000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <3>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpioe_pins";
};
@@ -245,7 +250,8 @@
interrupts = <27 1>;
reg = <0xFF145000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <2>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiof_pins";
};
@@ -257,7 +263,8 @@
interrupts = <27 1>;
reg = <0xFF146000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <3>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiog_pins";
};
@@ -269,7 +276,8 @@
interrupts = <27 1>;
reg = <0xFF147000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <2>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpioh_pins";
};
@@ -281,7 +289,8 @@
interrupts = <27 1>;
reg = <0xFF148000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <12>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpioi_pins";
};
@@ -293,7 +302,8 @@
interrupts = <27 1>;
reg = <0xFF149000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <32>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpioj_pins";
};
@@ -305,7 +315,8 @@
interrupts = <27 1>;
reg = <0xFF14A000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <22>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiok_pins";
};
@@ -317,7 +328,8 @@
interrupts = <27 1>;
reg = <0xFF14B000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <4>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiol_pins";
};
@@ -329,7 +341,8 @@
interrupts = <27 1>;
reg = <0xFF14C000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <4>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpiom_pins";
};
@@ -341,7 +354,8 @@
interrupts = <27 1>;
reg = <0xFF14D000 0x1000>;
gpio-controller;
- #gpio-cells = <1>;
+ #gpio-cells = <2>;
+ ngpio = <5>;
gpio-ranges = <&iomux 0 0 0>;
gpio-ranges-group-names = "gpion_pins";
};
diff --git a/arch/arc/boot/dts/abilis_tb101_dvk.dts b/arch/arc/boot/dts/abilis_tb101_dvk.dts
index a4d80ce..811a73b 100644
--- a/arch/arc/boot/dts/abilis_tb101_dvk.dts
+++ b/arch/arc/boot/dts/abilis_tb101_dvk.dts
@@ -64,62 +64,62 @@
compatible = "gpio-leds";
power {
label = "Power";
- gpios = <&gpioi 0>;
+ gpios = <&gpioi 0 0>;
linux,default-trigger = "default-on";
};
heartbeat {
label = "Heartbeat";
- gpios = <&gpioi 1>;
+ gpios = <&gpioi 1 0>;
linux,default-trigger = "heartbeat";
};
led2 {
label = "LED2";
- gpios = <&gpioi 2>;
+ gpios = <&gpioi 2 0>;
default-state = "off";
};
led3 {
label = "LED3";
- gpios = <&gpioi 3>;
+ gpios = <&gpioi 3 0>;
default-state = "off";
};
led4 {
label = "LED4";
- gpios = <&gpioi 4>;
+ gpios = <&gpioi 4 0>;
default-state = "off";
};
led5 {
label = "LED5";
- gpios = <&gpioi 5>;
+ gpios = <&gpioi 5 0>;
default-state = "off";
};
led6 {
label = "LED6";
- gpios = <&gpioi 6>;
+ gpios = <&gpioi 6 0>;
default-state = "off";
};
led7 {
label = "LED7";
- gpios = <&gpioi 7>;
+ gpios = <&gpioi 7 0>;
default-state = "off";
};
led8 {
label = "LED8";
- gpios = <&gpioi 8>;
+ gpios = <&gpioi 8 0>;
default-state = "off";
};
led9 {
label = "LED9";
- gpios = <&gpioi 9>;
+ gpios = <&gpioi 9 0>;
default-state = "off";
};
led10 {
label = "LED10";
- gpios = <&gpioi 10>;
+ gpios = <&gpioi 10 0>;
default-state = "off";
};
led11 {
label = "LED11";
- gpios = <&gpioi 11>;
+ gpios = <&gpioi 11 0>;
default-state = "off";
};
};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 573c449..1faeb7c 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -317,6 +317,10 @@ config GPIO_GRGPIO
Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB
VHDL IP core library.

+config GPIO_TB10X
+ bool
+ select OF_GPIO
+
comment "I2C GPIO expanders:"

config GPIO_ARIZONA
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 0cb2d65..7bc567f 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o
+obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c
new file mode 100644
index 0000000..7f67a4c
--- /dev/null
+++ b/drivers/gpio/gpio-tb10x.c
@@ -0,0 +1,341 @@
+/* Abilis Systems MODULE DESCRIPTION
+ *
+ * Copyright (C) Abilis Systems 2013
+ *
+ * Authors: Sascha Leuenberger <[email protected]>
+ * Christian Ruppert <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/pinctrl/consumer.h>
+
+#define TB10X_GPIO_DIR_IN (0x00000000)
+#define TB10X_GPIO_DIR_OUT (0x00000001)
+#define OFFSET_TO_REG_DDR (0x00)
+#define OFFSET_TO_REG_DATA (0x04)
+#define OFFSET_TO_REG_INT_EN (0x08)
+#define OFFSET_TO_REG_CHANGE (0x0C)
+#define OFFSET_TO_REG_WRMASK (0x10)
+#define OFFSET_TO_REG_INT_TYPE (0x14)
+
+
+/**
+ * @spinlock: used for atomic read/modify/write of registers
+ * @base: register base address
+ * @domain: IRQ domain of GPIO generated interrupts managed by this controller
+ * @irq: Interrupt line of parent interrupt controller
+ * @gc: gpio_chip structure associated to this GPIO controller
+ */
+struct tb10x_gpio {
+ spinlock_t spinlock;
+ void __iomem *base;
+ struct irq_domain *domain;
+ int irq;
+ struct gpio_chip gc;
+};
+
+static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs)
+{
+ return ioread32(gpio->base + offs);
+}
+
+static inline void tb10x_reg_write(struct tb10x_gpio *gpio, unsigned int offs,
+ u32 val)
+{
+ iowrite32(val, gpio->base + offs);
+}
+
+static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs,
+ u32 mask, u32 val)
+{
+ u32 r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio->spinlock, flags);
+
+ r = tb10x_reg_read(gpio, offs);
+ r = (r & ~mask) | (val & mask);
+
+ tb10x_reg_write(gpio, offs, r);
+
+ spin_unlock_irqrestore(&gpio->spinlock, flags);
+}
+
+static inline struct tb10x_gpio *to_tb10x_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct tb10x_gpio, gc);
+}
+
+static int tb10x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = BIT(offset);
+ int val = TB10X_GPIO_DIR_IN << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
+
+ return 0;
+}
+
+static int tb10x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int val;
+
+ val = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_DATA);
+
+ if (val & BIT(offset))
+ return 1;
+ else
+ return 0;
+}
+
+static void tb10x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = BIT(offset);
+ int val = value << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DATA, mask, val);
+}
+
+static int tb10x_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = BIT(offset);
+ int val = TB10X_GPIO_DIR_OUT << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
+
+ return 0;
+}
+
+static int tb10x_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void tb10x_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+}
+
+static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+
+ return irq_create_mapping(tb10x_gpio->domain, offset);
+}
+
+static int tb10x_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ if ((type & IRQF_TRIGGER_MASK) != IRQ_TYPE_EDGE_BOTH) {
+ pr_err("Only (both) edge triggered interrupts supported.\n");
+ return -EINVAL;
+ }
+
+ irqd_set_trigger_type(data, type);
+
+ return IRQ_SET_MASK_OK;
+}
+
+static irqreturn_t tb10x_gpio_irq_cascade(int irq, void *data)
+{
+ struct tb10x_gpio *tb10x_gpio = data;
+ u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_CHANGE);
+ u32 m = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);
+ const unsigned long bits = r & m;
+ int i;
+
+ for_each_set_bit(i, &bits, 32)
+ generic_handle_irq(irq_find_mapping(tb10x_gpio->domain, i));
+
+ return IRQ_HANDLED;
+}
+
+static int tb10x_gpio_probe(struct platform_device *pdev)
+{
+ struct tb10x_gpio *tb10x_gpio;
+ struct resource *mem;
+ struct device_node *dn = pdev->dev.of_node;
+ int ret = -EBUSY;
+ u32 ngpio;
+
+ if (!dn)
+ return -EINVAL;
+
+ if (of_property_read_u32(dn, "ngpio", &ngpio))
+ return -EINVAL;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource defined.\n");
+ return -EINVAL;
+ }
+
+ tb10x_gpio = devm_kzalloc(&pdev->dev, sizeof(*tb10x_gpio), GFP_KERNEL);
+ if (tb10x_gpio == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&tb10x_gpio->spinlock);
+
+ tb10x_gpio->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (!tb10x_gpio->base) {
+ dev_err(&pdev->dev, "Could not remap reg space.\n");
+ goto fail_ioremap;
+ }
+
+ tb10x_gpio->gc.label = of_node_full_name(dn);
+ tb10x_gpio->gc.dev = &pdev->dev;
+ tb10x_gpio->gc.owner = THIS_MODULE;
+ tb10x_gpio->gc.direction_input = tb10x_gpio_direction_in;
+ tb10x_gpio->gc.get = tb10x_gpio_get;
+ tb10x_gpio->gc.direction_output = tb10x_gpio_direction_out;
+ tb10x_gpio->gc.set = tb10x_gpio_set;
+ tb10x_gpio->gc.request = tb10x_gpio_request;
+ tb10x_gpio->gc.free = tb10x_gpio_free;
+ tb10x_gpio->gc.base = -1;
+ tb10x_gpio->gc.ngpio = ngpio;
+ tb10x_gpio->gc.can_sleep = 0;
+
+
+ ret = gpiochip_add(&tb10x_gpio->gc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not add gpiochip.\n");
+ goto fail_gpiochip_registration;
+ }
+
+ platform_set_drvdata(pdev, tb10x_gpio);
+
+ if (of_find_property(dn, "interrupt-controller", NULL)) {
+ struct irq_chip_generic *gc;
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "No interrupt specified.\n");
+ goto fail_get_irq;
+ }
+
+ tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
+ tb10x_gpio->irq = ret;
+
+ ret = devm_request_irq(&pdev->dev, ret, tb10x_gpio_irq_cascade,
+ IRQF_TRIGGER_NONE | IRQF_SHARED,
+ dev_name(&pdev->dev), tb10x_gpio);
+ if (ret != 0)
+ goto fail_request_irq;
+
+ tb10x_gpio->domain = irq_domain_add_linear(dn,
+ tb10x_gpio->gc.ngpio,
+ &irq_generic_chip_ops, NULL);
+ if (!tb10x_gpio->domain) {
+ ret = -ENOMEM;
+ goto fail_irq_domain;
+ }
+
+ ret = irq_alloc_domain_generic_chips(tb10x_gpio->domain,
+ tb10x_gpio->gc.ngpio, 1, tb10x_gpio->gc.label,
+ handle_edge_irq, IRQ_NOREQUEST, IRQ_NOPROBE,
+ IRQ_GC_INIT_MASK_CACHE);
+ if (ret)
+ goto fail_irq_domain;
+
+ gc = tb10x_gpio->domain->gc->gc[0];
+ gc->reg_base = tb10x_gpio->base;
+ gc->chip_types[0].type = IRQ_TYPE_EDGE_BOTH;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+ gc->chip_types[0].chip.irq_set_type = tb10x_gpio_irq_set_type;
+ gc->chip_types[0].regs.ack = OFFSET_TO_REG_CHANGE;
+ gc->chip_types[0].regs.mask = OFFSET_TO_REG_INT_EN;
+ }
+
+ return 0;
+
+fail_irq_domain:
+fail_request_irq:
+fail_get_irq:
+ gpiochip_remove(&tb10x_gpio->gc);
+fail_gpiochip_registration:
+fail_ioremap:
+ return ret;
+}
+
+static int __exit tb10x_gpio_remove(struct platform_device *pdev)
+{
+ struct tb10x_gpio *tb10x_gpio = platform_get_drvdata(pdev);
+ int ret;
+
+ if (tb10x_gpio->gc.to_irq) {
+ irq_remove_generic_chip(tb10x_gpio->domain->gc->gc[0],
+ BIT(tb10x_gpio->gc.ngpio) - 1, 0, 0);
+ kfree(tb10x_gpio->domain->gc);
+ irq_domain_remove(tb10x_gpio->domain);
+ free_irq(tb10x_gpio->irq, tb10x_gpio);
+ }
+ ret = gpiochip_remove(&tb10x_gpio->gc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct of_device_id tb10x_gpio_dt_ids[] = {
+ { .compatible = "abilis,tb10x-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tb10x_gpio_dt_ids);
+
+static struct platform_driver tb10x_gpio_driver = {
+ .probe = tb10x_gpio_probe,
+ .remove = tb10x_gpio_remove,
+ .driver = {
+ .name = "tb10x-gpio",
+ .of_match_table = of_match_ptr(tb10x_gpio_dt_ids),
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init ab_gpio_init(void)
+{
+ return platform_driver_register(&tb10x_gpio_driver);
+}
+
+static void __exit ab_gpio_exit(void)
+{
+ platform_driver_unregister(&tb10x_gpio_driver);
+}
+
+module_init(ab_gpio_init);
+module_exit(ab_gpio_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("tb10x gpio.");
+MODULE_VERSION("0.0.1");
--
1.7.1

2013-06-17 16:05:30

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 2/4] pinmux: Add TB10x pinmux driver

The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.

Signed-off-by: Christian Ruppert <[email protected]>
Signed-off-by: Pierrick Hascoet <[email protected]>
---
.../bindings/pinctrl/abilis,tb10x-iomux.txt | 81 ++
drivers/pinctrl/Kconfig | 4 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-tb10x.c | 878 ++++++++++++++++++++
4 files changed, 964 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
create mode 100644 drivers/pinctrl/pinctrl-tb10x.c

diff --git a/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
new file mode 100644
index 0000000..236b628
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
@@ -0,0 +1,81 @@
+Abilis Systems TB10x pin controller
+===================================
+
+Required properties
+-------------------
+
+- compatible: should be "abilis,tb10x-iomux";
+- reg: should contain the physical address and size of the pin controller's
+ register range.
+
+
+Port definitions
+----------------
+
+Ports are defined (and referenced) by sub-nodes of the pin controller. Every
+sub-node defines exactly one port (i.e. a set of pins). Ports are predefined
+as named pin groups inside the pin controller driver and these names are used
+to associate pin group predefinitions to pin controller sub-nodes.
+
+Required port definition subnode properties:
+ - pingrp: should be set to the name of the port's pin group.
+
+The following pin groups are available:
+ - GPIO ports: gpioa_pins, gpiob_pins, gpioc_pins, gpiod_pins, gpioe_pins,
+ gpiof_pins, gpiog_pins, gpioh_pins, gpioi_pins, gpioj_pins,
+ gpiok_pins, gpiol_pins, gpiom_pins, gpion_pins
+ - Serial TS input ports: mis0_pins, mis1_pins, mis2_pins, mis3_pins,
+ mis4_pins, mis5_pins, mis6_pins, mis7_pins
+ - Parallel TS input ports: mip1_pins, mip3_pins, mip5_pins, mip7_pins
+ - Serial TS output ports: mos0_pins, mos1_pins, mos2_pins, mos3_pins
+ - Parallel TS output port: mop_pins
+ - CI+ port: ciplus_pins
+ - CableCard (Mcard) port: mcard_pins
+ - Smart card ports: stc0_pins, stc1_pins
+ - UART ports: uart0_pins, uart1_pins
+ - SPI ports: spi1_pins, spi3_pins
+ - JTAG: jtag_pins
+
+All other ports of the chip are not multiplexed and thus not managed by this
+driver.
+
+
+GPIO ranges definition
+----------------------
+
+The named pin groups of GPIO ports can be used to define GPIO ranges as
+explained in Documentation/devicetree/bindings/gpio/gpio.txt.
+
+
+Example
+-------
+
+iomux: iomux@FF10601c {
+ compatible = "abilis,tb10x-iomux";
+ reg = <0xFF10601c 0x4>;
+ pctl_gpio_a: pctl-gpio-a {
+ pingrp = "gpioa_pins";
+ };
+ pctl_uart0: pctl-uart0 {
+ pingrp = "uart0_pins";
+ };
+};
+uart@FF100000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0xFF100000 0x100>;
+ clock-frequency = <166666666>;
+ interrupts = <25 1>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pctl_uart0>;
+};
+gpioa: gpio@FF140000 {
+ compatible = "abilis,tb10x-gpio";
+ reg = <0xFF140000 0x1000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpio = <3>;
+ gpio-ranges = <&iomux 0 0>;
+ gpio-ranges-group-names = "gpioa_pins";
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 8f66924..c6cb6ee 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -231,6 +231,10 @@ config PINCTRL_XWAY
depends on SOC_TYPE_XWAY
depends on PINCTRL_LANTIQ

+config PINCTRL_TB10X
+ bool
+ depends on ARC_PLAT_TB10X
+
endmenu

endif
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 9bdaeb8..3b21b20 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_PINCTRL_EXYNOS5440) += pinctrl-exynos5440.o
obj-$(CONFIG_PINCTRL_S3C64XX) += pinctrl-s3c64xx.o
obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o
obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o
+obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o

obj-$(CONFIG_PLAT_ORION) += mvebu/
obj-$(CONFIG_ARCH_SHMOBILE) += sh-pfc/
diff --git a/drivers/pinctrl/pinctrl-tb10x.c b/drivers/pinctrl/pinctrl-tb10x.c
new file mode 100644
index 0000000..719f3c1
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tb10x.c
@@ -0,0 +1,878 @@
+/*
+ * Abilis Systems TB10x pin control driver
+ *
+ * Copyright (C) Abilis Systems 2012
+ *
+ * Author: Christian Ruppert <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/stringify.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#define TB10X_PORT1 (0)
+#define TB10X_PORT2 (16)
+#define TB10X_PORT3 (32)
+#define TB10X_PORT4 (48)
+#define TB10X_PORT5 (128)
+#define TB10X_PORT6 (64)
+#define TB10X_PORT7 (80)
+#define TB10X_PORT8 (96)
+#define TB10X_PORT9 (112)
+#define TB10X_GPIOS (256)
+
+#define PCFG_PORT_BITWIDTH (2)
+#define PCFG_PORT_MASK(PORT) \
+ (((1 << PCFG_PORT_BITWIDTH) - 1) << (PCFG_PORT_BITWIDTH * (PORT)))
+
+static const struct pinctrl_pin_desc tb10x_pins[] = {
+ /* Port 1 */
+ PINCTRL_PIN(TB10X_PORT1 + 0, "MICLK_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 1, "MISTRT_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 2, "MIVAL_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 3, "MDI_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 4, "GPIOA0"),
+ PINCTRL_PIN(TB10X_PORT1 + 5, "GPIOA1"),
+ PINCTRL_PIN(TB10X_PORT1 + 6, "GPIOA2"),
+ PINCTRL_PIN(TB10X_PORT1 + 7, "MDI_S1"),
+ PINCTRL_PIN(TB10X_PORT1 + 8, "MIVAL_S1"),
+ PINCTRL_PIN(TB10X_PORT1 + 9, "MISTRT_S1"),
+ PINCTRL_PIN(TB10X_PORT1 + 10, "MICLK_S1"),
+ /* Port 2 */
+ PINCTRL_PIN(TB10X_PORT2 + 0, "MICLK_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 1, "MISTRT_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 2, "MIVAL_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 3, "MDI_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 4, "GPIOC0"),
+ PINCTRL_PIN(TB10X_PORT2 + 5, "GPIOC1"),
+ PINCTRL_PIN(TB10X_PORT2 + 6, "GPIOC2"),
+ PINCTRL_PIN(TB10X_PORT2 + 7, "MDI_S3"),
+ PINCTRL_PIN(TB10X_PORT2 + 8, "MIVAL_S3"),
+ PINCTRL_PIN(TB10X_PORT2 + 9, "MISTRT_S3"),
+ PINCTRL_PIN(TB10X_PORT2 + 10, "MICLK_S3"),
+ /* Port 3 */
+ PINCTRL_PIN(TB10X_PORT3 + 0, "MICLK_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 1, "MISTRT_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 2, "MIVAL_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 3, "MDI_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 4, "GPIOE0"),
+ PINCTRL_PIN(TB10X_PORT3 + 5, "GPIOE1"),
+ PINCTRL_PIN(TB10X_PORT3 + 6, "GPIOE2"),
+ PINCTRL_PIN(TB10X_PORT3 + 7, "MDI_S5"),
+ PINCTRL_PIN(TB10X_PORT3 + 8, "MIVAL_S5"),
+ PINCTRL_PIN(TB10X_PORT3 + 9, "MISTRT_S5"),
+ PINCTRL_PIN(TB10X_PORT3 + 10, "MICLK_S5"),
+ /* Port 4 */
+ PINCTRL_PIN(TB10X_PORT4 + 0, "MICLK_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 1, "MISTRT_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 2, "MIVAL_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 3, "MDI_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 4, "GPIOG0"),
+ PINCTRL_PIN(TB10X_PORT4 + 5, "GPIOG1"),
+ PINCTRL_PIN(TB10X_PORT4 + 6, "GPIOG2"),
+ PINCTRL_PIN(TB10X_PORT4 + 7, "MDI_S7"),
+ PINCTRL_PIN(TB10X_PORT4 + 8, "MIVAL_S7"),
+ PINCTRL_PIN(TB10X_PORT4 + 9, "MISTRT_S7"),
+ PINCTRL_PIN(TB10X_PORT4 + 10, "MICLK_S7"),
+ /* Port 5 */
+ PINCTRL_PIN(TB10X_PORT5 + 0, "PC_CE1N"),
+ PINCTRL_PIN(TB10X_PORT5 + 1, "PC_CE2N"),
+ PINCTRL_PIN(TB10X_PORT5 + 2, "PC_REGN"),
+ PINCTRL_PIN(TB10X_PORT5 + 3, "PC_INPACKN"),
+ PINCTRL_PIN(TB10X_PORT5 + 4, "PC_OEN"),
+ PINCTRL_PIN(TB10X_PORT5 + 5, "PC_WEN"),
+ PINCTRL_PIN(TB10X_PORT5 + 6, "PC_IORDN"),
+ PINCTRL_PIN(TB10X_PORT5 + 7, "PC_IOWRN"),
+ PINCTRL_PIN(TB10X_PORT5 + 8, "PC_RDYIRQN"),
+ PINCTRL_PIN(TB10X_PORT5 + 9, "PC_WAITN"),
+ PINCTRL_PIN(TB10X_PORT5 + 10, "PC_A0"),
+ PINCTRL_PIN(TB10X_PORT5 + 11, "PC_A1"),
+ PINCTRL_PIN(TB10X_PORT5 + 12, "PC_A2"),
+ PINCTRL_PIN(TB10X_PORT5 + 13, "PC_A3"),
+ PINCTRL_PIN(TB10X_PORT5 + 14, "PC_A4"),
+ PINCTRL_PIN(TB10X_PORT5 + 15, "PC_A5"),
+ PINCTRL_PIN(TB10X_PORT5 + 16, "PC_A6"),
+ PINCTRL_PIN(TB10X_PORT5 + 17, "PC_A7"),
+ PINCTRL_PIN(TB10X_PORT5 + 18, "PC_A8"),
+ PINCTRL_PIN(TB10X_PORT5 + 19, "PC_A9"),
+ PINCTRL_PIN(TB10X_PORT5 + 20, "PC_A10"),
+ PINCTRL_PIN(TB10X_PORT5 + 21, "PC_A11"),
+ PINCTRL_PIN(TB10X_PORT5 + 22, "PC_A12"),
+ PINCTRL_PIN(TB10X_PORT5 + 23, "PC_A13"),
+ PINCTRL_PIN(TB10X_PORT5 + 24, "PC_A14"),
+ PINCTRL_PIN(TB10X_PORT5 + 25, "PC_D0"),
+ PINCTRL_PIN(TB10X_PORT5 + 26, "PC_D1"),
+ PINCTRL_PIN(TB10X_PORT5 + 27, "PC_D2"),
+ PINCTRL_PIN(TB10X_PORT5 + 28, "PC_D3"),
+ PINCTRL_PIN(TB10X_PORT5 + 29, "PC_D4"),
+ PINCTRL_PIN(TB10X_PORT5 + 30, "PC_D5"),
+ PINCTRL_PIN(TB10X_PORT5 + 31, "PC_D6"),
+ PINCTRL_PIN(TB10X_PORT5 + 32, "PC_D7"),
+ PINCTRL_PIN(TB10X_PORT5 + 33, "PC_MOSTRT"),
+ PINCTRL_PIN(TB10X_PORT5 + 34, "PC_MOVAL"),
+ PINCTRL_PIN(TB10X_PORT5 + 35, "PC_MDO0"),
+ PINCTRL_PIN(TB10X_PORT5 + 36, "PC_MDO1"),
+ PINCTRL_PIN(TB10X_PORT5 + 37, "PC_MDO2"),
+ PINCTRL_PIN(TB10X_PORT5 + 38, "PC_MDO3"),
+ PINCTRL_PIN(TB10X_PORT5 + 39, "PC_MDO4"),
+ PINCTRL_PIN(TB10X_PORT5 + 40, "PC_MDO5"),
+ PINCTRL_PIN(TB10X_PORT5 + 41, "PC_MDO6"),
+ PINCTRL_PIN(TB10X_PORT5 + 42, "PC_MDO7"),
+ PINCTRL_PIN(TB10X_PORT5 + 43, "PC_MISTRT"),
+ PINCTRL_PIN(TB10X_PORT5 + 44, "PC_MIVAL"),
+ PINCTRL_PIN(TB10X_PORT5 + 45, "PC_MDI0"),
+ PINCTRL_PIN(TB10X_PORT5 + 46, "PC_MDI1"),
+ PINCTRL_PIN(TB10X_PORT5 + 47, "PC_MDI2"),
+ PINCTRL_PIN(TB10X_PORT5 + 48, "PC_MDI3"),
+ PINCTRL_PIN(TB10X_PORT5 + 49, "PC_MDI4"),
+ PINCTRL_PIN(TB10X_PORT5 + 50, "PC_MDI5"),
+ PINCTRL_PIN(TB10X_PORT5 + 51, "PC_MDI6"),
+ PINCTRL_PIN(TB10X_PORT5 + 52, "PC_MDI7"),
+ PINCTRL_PIN(TB10X_PORT5 + 53, "PC_MICLK"),
+ /* Port 6 */
+ PINCTRL_PIN(TB10X_PORT6 + 0, "T_MOSTRT_S0"),
+ PINCTRL_PIN(TB10X_PORT6 + 1, "T_MOVAL_S0"),
+ PINCTRL_PIN(TB10X_PORT6 + 2, "T_MDO_S0"),
+ PINCTRL_PIN(TB10X_PORT6 + 3, "T_MOSTRT_S1"),
+ PINCTRL_PIN(TB10X_PORT6 + 4, "T_MOVAL_S1"),
+ PINCTRL_PIN(TB10X_PORT6 + 5, "T_MDO_S1"),
+ PINCTRL_PIN(TB10X_PORT6 + 6, "T_MOSTRT_S2"),
+ PINCTRL_PIN(TB10X_PORT6 + 7, "T_MOVAL_S2"),
+ PINCTRL_PIN(TB10X_PORT6 + 8, "T_MDO_S2"),
+ PINCTRL_PIN(TB10X_PORT6 + 9, "T_MOSTRT_S3"),
+ /* Port 7 */
+ PINCTRL_PIN(TB10X_PORT7 + 0, "UART0_TXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 1, "UART0_RXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 2, "UART0_CTS"),
+ PINCTRL_PIN(TB10X_PORT7 + 3, "UART0_RTS"),
+ PINCTRL_PIN(TB10X_PORT7 + 4, "UART1_TXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 5, "UART1_RXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 6, "UART1_CTS"),
+ PINCTRL_PIN(TB10X_PORT7 + 7, "UART1_RTS"),
+ /* Port 8 */
+ PINCTRL_PIN(TB10X_PORT8 + 0, "SPI3_CLK"),
+ PINCTRL_PIN(TB10X_PORT8 + 1, "SPI3_MISO"),
+ PINCTRL_PIN(TB10X_PORT8 + 2, "SPI3_MOSI"),
+ PINCTRL_PIN(TB10X_PORT8 + 3, "SPI3_SSN"),
+ /* Port 9 */
+ PINCTRL_PIN(TB10X_PORT9 + 0, "SPI1_CLK"),
+ PINCTRL_PIN(TB10X_PORT9 + 1, "SPI1_MISO"),
+ PINCTRL_PIN(TB10X_PORT9 + 2, "SPI1_MOSI"),
+ PINCTRL_PIN(TB10X_PORT9 + 3, "SPI1_SSN0"),
+ PINCTRL_PIN(TB10X_PORT9 + 4, "SPI1_SSN1"),
+ /* Unmuxed GPIOs */
+ PINCTRL_PIN(TB10X_GPIOS + 0, "GPIOB0"),
+ PINCTRL_PIN(TB10X_GPIOS + 1, "GPIOB1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 2, "GPIOD0"),
+ PINCTRL_PIN(TB10X_GPIOS + 3, "GPIOD1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 4, "GPIOF0"),
+ PINCTRL_PIN(TB10X_GPIOS + 5, "GPIOF1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 6, "GPIOH0"),
+ PINCTRL_PIN(TB10X_GPIOS + 7, "GPIOH1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 8, "GPIOI0"),
+ PINCTRL_PIN(TB10X_GPIOS + 9, "GPIOI1"),
+ PINCTRL_PIN(TB10X_GPIOS + 10, "GPIOI2"),
+ PINCTRL_PIN(TB10X_GPIOS + 11, "GPIOI3"),
+ PINCTRL_PIN(TB10X_GPIOS + 12, "GPIOI4"),
+ PINCTRL_PIN(TB10X_GPIOS + 13, "GPIOI5"),
+ PINCTRL_PIN(TB10X_GPIOS + 14, "GPIOI6"),
+ PINCTRL_PIN(TB10X_GPIOS + 15, "GPIOI7"),
+ PINCTRL_PIN(TB10X_GPIOS + 16, "GPIOI8"),
+ PINCTRL_PIN(TB10X_GPIOS + 17, "GPIOI9"),
+ PINCTRL_PIN(TB10X_GPIOS + 18, "GPIOI10"),
+ PINCTRL_PIN(TB10X_GPIOS + 19, "GPIOI11"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 20, "GPION0"),
+ PINCTRL_PIN(TB10X_GPIOS + 21, "GPION1"),
+ PINCTRL_PIN(TB10X_GPIOS + 22, "GPION2"),
+ PINCTRL_PIN(TB10X_GPIOS + 23, "GPION3"),
+#define MAX_PIN (TB10X_GPIOS + 24)
+ PINCTRL_PIN(MAX_PIN, "GPION4"),
+};
+
+
+/* Port 1 */
+static const unsigned mis0_pins[] = { TB10X_PORT1 + 0, TB10X_PORT1 + 1,
+ TB10X_PORT1 + 2, TB10X_PORT1 + 3};
+static const unsigned gpioa_pins[] = { TB10X_PORT1 + 4, TB10X_PORT1 + 5,
+ TB10X_PORT1 + 6};
+static const unsigned mis1_pins[] = { TB10X_PORT1 + 7, TB10X_PORT1 + 8,
+ TB10X_PORT1 + 9, TB10X_PORT1 + 10};
+static const unsigned mip1_pins[] = { TB10X_PORT1 + 0, TB10X_PORT1 + 1,
+ TB10X_PORT1 + 2, TB10X_PORT1 + 3,
+ TB10X_PORT1 + 4, TB10X_PORT1 + 5,
+ TB10X_PORT1 + 6, TB10X_PORT1 + 7,
+ TB10X_PORT1 + 8, TB10X_PORT1 + 9,
+ TB10X_PORT1 + 10};
+
+/* Port 2 */
+static const unsigned mis2_pins[] = { TB10X_PORT2 + 0, TB10X_PORT2 + 1,
+ TB10X_PORT2 + 2, TB10X_PORT2 + 3};
+static const unsigned gpioc_pins[] = { TB10X_PORT2 + 4, TB10X_PORT2 + 5,
+ TB10X_PORT2 + 6};
+static const unsigned mis3_pins[] = { TB10X_PORT2 + 7, TB10X_PORT2 + 8,
+ TB10X_PORT2 + 9, TB10X_PORT2 + 10};
+static const unsigned mip3_pins[] = { TB10X_PORT2 + 0, TB10X_PORT2 + 1,
+ TB10X_PORT2 + 2, TB10X_PORT2 + 3,
+ TB10X_PORT2 + 4, TB10X_PORT2 + 5,
+ TB10X_PORT2 + 6, TB10X_PORT2 + 7,
+ TB10X_PORT2 + 8, TB10X_PORT2 + 9,
+ TB10X_PORT2 + 10};
+
+/* Port 3 */
+static const unsigned mis4_pins[] = { TB10X_PORT3 + 0, TB10X_PORT3 + 1,
+ TB10X_PORT3 + 2, TB10X_PORT3 + 3};
+static const unsigned gpioe_pins[] = { TB10X_PORT3 + 4, TB10X_PORT3 + 5,
+ TB10X_PORT3 + 6};
+static const unsigned mis5_pins[] = { TB10X_PORT3 + 7, TB10X_PORT3 + 8,
+ TB10X_PORT3 + 9, TB10X_PORT3 + 10};
+static const unsigned mip5_pins[] = { TB10X_PORT3 + 0, TB10X_PORT3 + 1,
+ TB10X_PORT3 + 2, TB10X_PORT3 + 3,
+ TB10X_PORT3 + 4, TB10X_PORT3 + 5,
+ TB10X_PORT3 + 6, TB10X_PORT3 + 7,
+ TB10X_PORT3 + 8, TB10X_PORT3 + 9,
+ TB10X_PORT3 + 10};
+
+/* Port 4 */
+static const unsigned mis6_pins[] = { TB10X_PORT4 + 0, TB10X_PORT4 + 1,
+ TB10X_PORT4 + 2, TB10X_PORT4 + 3};
+static const unsigned gpiog_pins[] = { TB10X_PORT4 + 4, TB10X_PORT4 + 5,
+ TB10X_PORT4 + 6};
+static const unsigned mis7_pins[] = { TB10X_PORT4 + 7, TB10X_PORT4 + 8,
+ TB10X_PORT4 + 9, TB10X_PORT4 + 10};
+static const unsigned mip7_pins[] = { TB10X_PORT4 + 0, TB10X_PORT4 + 1,
+ TB10X_PORT4 + 2, TB10X_PORT4 + 3,
+ TB10X_PORT4 + 4, TB10X_PORT4 + 5,
+ TB10X_PORT4 + 6, TB10X_PORT4 + 7,
+ TB10X_PORT4 + 8, TB10X_PORT4 + 9,
+ TB10X_PORT4 + 10};
+
+/* Port 6 */
+static const unsigned mop_pins[] = { TB10X_PORT6 + 0, TB10X_PORT6 + 1,
+ TB10X_PORT6 + 2, TB10X_PORT6 + 3,
+ TB10X_PORT6 + 4, TB10X_PORT6 + 5,
+ TB10X_PORT6 + 6, TB10X_PORT6 + 7,
+ TB10X_PORT6 + 8, TB10X_PORT6 + 9};
+static const unsigned mos0_pins[] = { TB10X_PORT6 + 0, TB10X_PORT6 + 1,
+ TB10X_PORT6 + 2};
+static const unsigned mos1_pins[] = { TB10X_PORT6 + 3, TB10X_PORT6 + 4,
+ TB10X_PORT6 + 5};
+static const unsigned mos2_pins[] = { TB10X_PORT6 + 6, TB10X_PORT6 + 7,
+ TB10X_PORT6 + 8};
+static const unsigned mos3_pins[] = { TB10X_PORT6 + 9};
+
+/* Port 7 */
+static const unsigned uart0_pins[] = { TB10X_PORT7 + 0, TB10X_PORT7 + 1,
+ TB10X_PORT7 + 2, TB10X_PORT7 + 3};
+static const unsigned uart1_pins[] = { TB10X_PORT7 + 4, TB10X_PORT7 + 5,
+ TB10X_PORT7 + 6, TB10X_PORT7 + 7};
+static const unsigned gpiol_pins[] = { TB10X_PORT7 + 0, TB10X_PORT7 + 1,
+ TB10X_PORT7 + 2, TB10X_PORT7 + 3};
+static const unsigned gpiom_pins[] = { TB10X_PORT7 + 4, TB10X_PORT7 + 5,
+ TB10X_PORT7 + 6, TB10X_PORT7 + 7};
+
+/* Port 8 */
+static const unsigned spi3_pins[] = { TB10X_PORT8 + 0, TB10X_PORT8 + 1,
+ TB10X_PORT8 + 2, TB10X_PORT8 + 3};
+static const unsigned jtag_pins[] = { TB10X_PORT8 + 0, TB10X_PORT8 + 1,
+ TB10X_PORT8 + 2, TB10X_PORT8 + 3};
+
+/* Port 9 */
+static const unsigned spi1_pins[] = { TB10X_PORT9 + 0, TB10X_PORT9 + 1,
+ TB10X_PORT9 + 2, TB10X_PORT9 + 3,
+ TB10X_PORT9 + 4};
+static const unsigned gpion_pins[] = { TB10X_PORT9 + 0, TB10X_PORT9 + 1,
+ TB10X_PORT9 + 2, TB10X_PORT9 + 3,
+ TB10X_PORT9 + 4};
+
+/* Port 5 */
+static const unsigned gpioj_pins[] = { TB10X_PORT5 + 0, TB10X_PORT5 + 1,
+ TB10X_PORT5 + 2, TB10X_PORT5 + 3,
+ TB10X_PORT5 + 4, TB10X_PORT5 + 5,
+ TB10X_PORT5 + 6, TB10X_PORT5 + 7,
+ TB10X_PORT5 + 8, TB10X_PORT5 + 9,
+ TB10X_PORT5 + 10, TB10X_PORT5 + 11,
+ TB10X_PORT5 + 12, TB10X_PORT5 + 13,
+ TB10X_PORT5 + 14, TB10X_PORT5 + 15,
+ TB10X_PORT5 + 16, TB10X_PORT5 + 17,
+ TB10X_PORT5 + 18, TB10X_PORT5 + 19,
+ TB10X_PORT5 + 20, TB10X_PORT5 + 21,
+ TB10X_PORT5 + 22, TB10X_PORT5 + 23,
+ TB10X_PORT5 + 24, TB10X_PORT5 + 25,
+ TB10X_PORT5 + 26, TB10X_PORT5 + 27,
+ TB10X_PORT5 + 28, TB10X_PORT5 + 29,
+ TB10X_PORT5 + 30, TB10X_PORT5 + 31};
+static const unsigned gpiok_pins[] = { TB10X_PORT5 + 32, TB10X_PORT5 + 33,
+ TB10X_PORT5 + 34, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40, TB10X_PORT5 + 41,
+ TB10X_PORT5 + 42, TB10X_PORT5 + 43,
+ TB10X_PORT5 + 44, TB10X_PORT5 + 45,
+ TB10X_PORT5 + 46, TB10X_PORT5 + 47,
+ TB10X_PORT5 + 48, TB10X_PORT5 + 49,
+ TB10X_PORT5 + 50, TB10X_PORT5 + 51,
+ TB10X_PORT5 + 52, TB10X_PORT5 + 53};
+static const unsigned ciplus_pins[] = { TB10X_PORT5 + 0, TB10X_PORT5 + 1,
+ TB10X_PORT5 + 2, TB10X_PORT5 + 3,
+ TB10X_PORT5 + 4, TB10X_PORT5 + 5,
+ TB10X_PORT5 + 6, TB10X_PORT5 + 7,
+ TB10X_PORT5 + 8, TB10X_PORT5 + 9,
+ TB10X_PORT5 + 10, TB10X_PORT5 + 11,
+ TB10X_PORT5 + 12, TB10X_PORT5 + 13,
+ TB10X_PORT5 + 14, TB10X_PORT5 + 15,
+ TB10X_PORT5 + 16, TB10X_PORT5 + 17,
+ TB10X_PORT5 + 18, TB10X_PORT5 + 19,
+ TB10X_PORT5 + 20, TB10X_PORT5 + 21,
+ TB10X_PORT5 + 22, TB10X_PORT5 + 23,
+ TB10X_PORT5 + 24, TB10X_PORT5 + 25,
+ TB10X_PORT5 + 26, TB10X_PORT5 + 27,
+ TB10X_PORT5 + 28, TB10X_PORT5 + 29,
+ TB10X_PORT5 + 30, TB10X_PORT5 + 31,
+ TB10X_PORT5 + 32, TB10X_PORT5 + 33,
+ TB10X_PORT5 + 34, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40, TB10X_PORT5 + 41,
+ TB10X_PORT5 + 42, TB10X_PORT5 + 43,
+ TB10X_PORT5 + 44, TB10X_PORT5 + 45,
+ TB10X_PORT5 + 46, TB10X_PORT5 + 47,
+ TB10X_PORT5 + 48, TB10X_PORT5 + 49,
+ TB10X_PORT5 + 50, TB10X_PORT5 + 51,
+ TB10X_PORT5 + 52, TB10X_PORT5 + 53};
+static const unsigned mcard_pins[] = { TB10X_PORT5 + 3, TB10X_PORT5 + 10,
+ TB10X_PORT5 + 11, TB10X_PORT5 + 12,
+ TB10X_PORT5 + 22, TB10X_PORT5 + 23,
+ TB10X_PORT5 + 33, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40, TB10X_PORT5 + 41,
+ TB10X_PORT5 + 42, TB10X_PORT5 + 43,
+ TB10X_PORT5 + 45, TB10X_PORT5 + 46,
+ TB10X_PORT5 + 47, TB10X_PORT5 + 48,
+ TB10X_PORT5 + 49, TB10X_PORT5 + 50,
+ TB10X_PORT5 + 51, TB10X_PORT5 + 52,
+ TB10X_PORT5 + 53};
+static const unsigned stc0_pins[] = { TB10X_PORT5 + 34, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40};
+static const unsigned stc1_pins[] = { TB10X_PORT5 + 25, TB10X_PORT5 + 26,
+ TB10X_PORT5 + 27, TB10X_PORT5 + 28,
+ TB10X_PORT5 + 29, TB10X_PORT5 + 30,
+ TB10X_PORT5 + 44};
+
+/* Unmuxed GPIOs */
+static const unsigned gpiob_pins[] = { TB10X_GPIOS + 0, TB10X_GPIOS + 1};
+static const unsigned gpiod_pins[] = { TB10X_GPIOS + 2, TB10X_GPIOS + 3};
+static const unsigned gpiof_pins[] = { TB10X_GPIOS + 4, TB10X_GPIOS + 5};
+static const unsigned gpioh_pins[] = { TB10X_GPIOS + 6, TB10X_GPIOS + 7};
+static const unsigned gpioi_pins[] = { TB10X_GPIOS + 8, TB10X_GPIOS + 9,
+ TB10X_GPIOS + 10, TB10X_GPIOS + 11,
+ TB10X_GPIOS + 12, TB10X_GPIOS + 13,
+ TB10X_GPIOS + 14, TB10X_GPIOS + 15,
+ TB10X_GPIOS + 16, TB10X_GPIOS + 17,
+ TB10X_GPIOS + 18, TB10X_GPIOS + 19};
+
+struct tb10x_pinfuncgrp {
+ const char *name;
+ const unsigned int *pins;
+ const unsigned int pincnt;
+ const int port;
+ const unsigned int mode;
+ const int isgpio;
+};
+#define DEFPINFUNCGRP(NAME, PORT, MODE, ISGPIO) { \
+ .name = __stringify(NAME), \
+ .pins = NAME, .pincnt = ARRAY_SIZE(NAME), \
+ .port = (PORT), .mode = (MODE), \
+ .isgpio = (ISGPIO), \
+ }
+static const struct tb10x_pinfuncgrp tb10x_pingroups[] = {
+ DEFPINFUNCGRP(mis0_pins, 0, 0, 0),
+ DEFPINFUNCGRP(gpioa_pins, 0, 0, 1),
+ DEFPINFUNCGRP(mis1_pins, 0, 0, 0),
+ DEFPINFUNCGRP(mip1_pins, 0, 1, 0),
+ DEFPINFUNCGRP(mis2_pins, 1, 0, 0),
+ DEFPINFUNCGRP(gpioc_pins, 1, 0, 1),
+ DEFPINFUNCGRP(mis3_pins, 1, 0, 0),
+ DEFPINFUNCGRP(mip3_pins, 1, 1, 0),
+ DEFPINFUNCGRP(mis4_pins, 2, 0, 0),
+ DEFPINFUNCGRP(gpioe_pins, 2, 0, 1),
+ DEFPINFUNCGRP(mis5_pins, 2, 0, 0),
+ DEFPINFUNCGRP(mip5_pins, 2, 1, 0),
+ DEFPINFUNCGRP(mis6_pins, 3, 0, 0),
+ DEFPINFUNCGRP(gpiog_pins, 3, 0, 1),
+ DEFPINFUNCGRP(mis7_pins, 3, 0, 0),
+ DEFPINFUNCGRP(mip7_pins, 3, 1, 0),
+ DEFPINFUNCGRP(gpioj_pins, 4, 0, 1),
+ DEFPINFUNCGRP(gpiok_pins, 4, 0, 1),
+ DEFPINFUNCGRP(ciplus_pins, 4, 1, 0),
+ DEFPINFUNCGRP(mcard_pins, 4, 2, 0),
+ DEFPINFUNCGRP(stc0_pins, 4, 3, 0),
+ DEFPINFUNCGRP(stc1_pins, 4, 3, 0),
+ DEFPINFUNCGRP(mop_pins, 5, 0, 0),
+ DEFPINFUNCGRP(mos0_pins, 5, 1, 0),
+ DEFPINFUNCGRP(mos1_pins, 5, 1, 0),
+ DEFPINFUNCGRP(mos2_pins, 5, 1, 0),
+ DEFPINFUNCGRP(mos3_pins, 5, 1, 0),
+ DEFPINFUNCGRP(uart0_pins, 6, 0, 0),
+ DEFPINFUNCGRP(uart1_pins, 6, 0, 0),
+ DEFPINFUNCGRP(gpiol_pins, 6, 1, 1),
+ DEFPINFUNCGRP(gpiom_pins, 6, 1, 1),
+ DEFPINFUNCGRP(spi3_pins, 7, 0, 0),
+ DEFPINFUNCGRP(jtag_pins, 7, 1, 0),
+ DEFPINFUNCGRP(spi1_pins, 8, 0, 0),
+ DEFPINFUNCGRP(gpion_pins, 8, 1, 1),
+ DEFPINFUNCGRP(gpiob_pins, -1, 0, 1),
+ DEFPINFUNCGRP(gpiod_pins, -1, 0, 1),
+ DEFPINFUNCGRP(gpiof_pins, -1, 0, 1),
+ DEFPINFUNCGRP(gpioh_pins, -1, 0, 1),
+ DEFPINFUNCGRP(gpioi_pins, -1, 0, 1),
+};
+#undef DEFPINFUNCGRP
+
+struct tb10x_of_pinfunc {
+ const char *name;
+ const char *group;
+};
+
+#define TB10X_PORTS (9)
+
+/**
+ * struct tb10x_port - state of an I/O port
+ * @mode: Node this port is currently in.
+ * @count: Number of enabled functions which require this port to be
+ * configured in @mode.
+ */
+struct tb10x_port {
+ unsigned int mode;
+ unsigned int count;
+};
+
+/**
+ * struct tb10x_pinctrl - TB10x pin controller internal state
+ * @pctl: pointer to the pinctrl_dev structure of this pin controller.
+ * @base: register set base address.
+ * @pingroups: pointer to an array of the pin groups this driver manages.
+ * @pinfuncgrpcnt: number of pingroups in @pingroups.
+ * @pinfuncs: pointer to an array of pin functions this driver manages.
+ * @pinfuncnt: number of pin functions in @pinfuncs.
+ * @mutex: mutex for exclusive access to a pin controller's state.
+ * @ports: current state of each port.
+ * @gpios: Indicates if a given pin is currently used as GPIO (1) or not (0).
+ */
+struct tb10x_pinctrl {
+ struct pinctrl_dev *pctl;
+ void *base;
+ const struct tb10x_pinfuncgrp *pingroups;
+ unsigned int pinfuncgrpcnt;
+ struct tb10x_of_pinfunc *pinfuncs;
+ unsigned int pinfuncnt;
+ struct mutex mutex;
+ struct tb10x_port ports[TB10X_PORTS];
+ DECLARE_BITMAP(gpios, MAX_PIN + 1);
+};
+
+static inline void tb10x_pinctrl_set_config(struct tb10x_pinctrl *state,
+ unsigned int port, unsigned int mode)
+{
+ u32 pcfg;
+
+ if (state->ports[port].count)
+ return;
+
+ state->ports[port].mode = mode;
+
+ pcfg = ioread32(state->base) & ~(PCFG_PORT_MASK(port));
+ pcfg |= (mode << (PCFG_PORT_BITWIDTH * port)) & PCFG_PORT_MASK(port);
+ iowrite32(pcfg, state->base);
+}
+
+static inline unsigned int tb10x_pinctrl_get_config(
+ struct tb10x_pinctrl *state,
+ unsigned int port)
+{
+ return (ioread32(state->base) & PCFG_PORT_MASK(port))
+ >> (PCFG_PORT_BITWIDTH * port);
+}
+
+static int tb10x_get_groups_count(struct pinctrl_dev *pctl)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncgrpcnt;
+}
+
+static const char *tb10x_get_group_name(struct pinctrl_dev *pctl, unsigned n)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pingroups[n].name;
+}
+
+static int tb10x_get_group_pins(struct pinctrl_dev *pctl, unsigned n,
+ unsigned const **pins,
+ unsigned * const num_pins)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+
+ *pins = state->pingroups[n].pins;
+ *num_pins = state->pingroups[n].pincnt;
+
+ return 0;
+}
+
+static int tb10x_dt_node_to_map(struct pinctrl_dev *pctl,
+ struct device_node *np_config,
+ struct pinctrl_map **map, unsigned *num_maps)
+{
+ struct pinctrl_map *m;
+ const char *string;
+
+ if (of_property_read_string(np_config, "pingrp", &string)) {
+ pr_err("%s: No pingrp property in device tree.\n",
+ np_config->full_name);
+ return -EINVAL;
+ }
+
+ m = kzalloc(sizeof(struct pinctrl_map), GFP_KERNEL);
+ if (!m)
+ return -ENOMEM;
+
+ m->type = PIN_MAP_TYPE_MUX_GROUP;
+ m->data.mux.group = string;
+ m->data.mux.function = np_config->name;
+ *map = m;
+ *num_maps = 1;
+
+ return 0;
+}
+
+static void tb10x_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ kfree(map);
+}
+
+static struct pinctrl_ops tb10x_pinctrl_ops = {
+ .get_groups_count = tb10x_get_groups_count,
+ .get_group_name = tb10x_get_group_name,
+ .get_group_pins = tb10x_get_group_pins,
+ .dt_node_to_map = tb10x_dt_node_to_map,
+ .dt_free_map = tb10x_dt_free_map,
+};
+
+static int tb10x_get_functions_count(struct pinctrl_dev *pctl)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncnt;
+}
+
+static const char *tb10x_get_function_name(struct pinctrl_dev *pctl,
+ unsigned n)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncs[n].name;
+}
+
+static int tb10x_get_function_groups(struct pinctrl_dev *pctl,
+ unsigned n, const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+
+ *groups = &state->pinfuncs[n].group;
+ *num_groups = 1;
+
+ return 0;
+}
+
+static int tb10x_gpio_request_enable(struct pinctrl_dev *pctl,
+ struct pinctrl_gpio_range *range,
+ unsigned pin)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ int muxport = -1;
+ int muxmode = -1;
+ int i;
+
+ mutex_lock(&state->mutex);
+
+ /* Figure out to which port the requested GPIO belongs and how to
+ * configure that port.
+ * This loop also checks for pin conflicts between GPIOs and other
+ * functions.
+ */
+ for (i = 0; i < state->pinfuncgrpcnt; i++) {
+ const struct tb10x_pinfuncgrp *pfg = &state->pingroups[i];
+ unsigned int port = pfg->port;
+ unsigned int mode = pfg->mode;
+ int j;
+
+ /* Skip pin groups which are always mapped and don't need
+ * to be configured.
+ */
+ if (port < 0)
+ continue;
+
+ for (j = 0; j < pfg->pincnt; j++) {
+ if (pin == pfg->pins[j]) {
+ if (pfg->isgpio) {
+ /* Remember the GPIO-only setting of
+ * the port this pin belongs to.
+ */
+ muxport = port;
+ muxmode = mode;
+ } else if (state->ports[port].count
+ && (state->ports[port].mode == mode)) {
+ /* Error: The requested pin is already
+ * used for something else.
+ */
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+ break;
+ }
+ }
+ }
+
+ /* If we haven't returned an error at this point, the GPIO pin is not
+ * used by another function and the GPIO request can be granted:
+ * Register pin as being used as GPIO so we don't allocate it to
+ * another function later.
+ */
+ set_bit(pin, state->gpios);
+
+ /* Potential conflicts between GPIOs and pin functions were caught
+ * earlier in this function and tb10x_pinctrl_set_config will do the
+ * Right Thing, either configure the port in GPIO only mode or leave
+ * another mode compatible with this GPIO request untouched.
+ */
+ if (muxport >= 0)
+ tb10x_pinctrl_set_config(state, muxport, muxmode);
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static void tb10x_gpio_disable_free(struct pinctrl_dev *pctl,
+ struct pinctrl_gpio_range *range,
+ unsigned pin)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+
+ mutex_lock(&state->mutex);
+
+ clear_bit(pin, state->gpios);
+
+ mutex_unlock(&state->mutex);
+}
+
+static int tb10x_pctl_enable(struct pinctrl_dev *pctl,
+ unsigned func_selector, unsigned group_selector)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector];
+ int i;
+
+ if (grp->port < 0)
+ return 0;
+
+ mutex_lock(&state->mutex);
+
+ /* Check if the requested function is compatible with previously
+ * requested functions.
+ */
+ if (state->ports[grp->port].count
+ && (state->ports[grp->port].mode != grp->mode)) {
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+
+ /* Check if the requested function is compatible with previously
+ * requested GPIOs.
+ */
+ for (i = 0; i < grp->pincnt; i++)
+ if (test_bit(grp->pins[i], state->gpios)) {
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+
+ tb10x_pinctrl_set_config(state, grp->port, grp->mode);
+
+ state->ports[grp->port].count++;
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static void tb10x_pctl_disable(struct pinctrl_dev *pctl,
+ unsigned func_selector, unsigned group_selector)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector];
+
+ if (grp->port < 0)
+ return;
+
+ mutex_lock(&state->mutex);
+
+ state->ports[grp->port].count--;
+
+ mutex_unlock(&state->mutex);
+}
+
+static struct pinmux_ops tb10x_pinmux_ops = {
+ .get_functions_count = tb10x_get_functions_count,
+ .get_function_name = tb10x_get_function_name,
+ .get_function_groups = tb10x_get_function_groups,
+ .gpio_request_enable = tb10x_gpio_request_enable,
+ .gpio_disable_free = tb10x_gpio_disable_free,
+ .enable = tb10x_pctl_enable,
+ .disable = tb10x_pctl_disable,
+};
+
+static struct pinctrl_desc tb10x_pindesc = {
+ .name = "TB10x",
+ .pins = tb10x_pins,
+ .npins = ARRAY_SIZE(tb10x_pins),
+ .owner = THIS_MODULE,
+ .pctlops = &tb10x_pinctrl_ops,
+ .pmxops = &tb10x_pinmux_ops,
+};
+
+static int tb10x_pinctrl_probe(struct platform_device *pdev)
+{
+ int ret = -EINVAL;
+ struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct device *dev = &pdev->dev;
+ struct device_node *of_node = dev->of_node;
+ struct device_node *child;
+ struct tb10x_pinctrl *state;
+ int i;
+
+ if (!of_node) {
+ dev_err(dev, "No device tree node found.\n");
+ return -EINVAL;
+ }
+
+ if (!mem) {
+ dev_err(dev, "No memory resource defined.\n");
+ return -EINVAL;
+ }
+
+ state = devm_kzalloc(dev, sizeof(struct tb10x_pinctrl) +
+ of_get_child_count(of_node)
+ * sizeof(struct tb10x_of_pinfunc),
+ GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, state);
+ state->pinfuncs = (struct tb10x_of_pinfunc *)(state + 1);
+ mutex_init(&state->mutex);
+
+ state->base = devm_ioremap_resource(dev, mem);
+ if (!state->base) {
+ dev_err(dev, "Request register region failed.\n");
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ state->pingroups = tb10x_pingroups;
+ state->pinfuncgrpcnt = ARRAY_SIZE(tb10x_pingroups);
+
+ for (i = 0; i < TB10X_PORTS; i++)
+ state->ports[i].mode = tb10x_pinctrl_get_config(state, i);
+
+ for_each_child_of_node(of_node, child) {
+ const char *name;
+
+ if (!of_property_read_string(child, "pingrp", &name)) {
+ state->pinfuncs[state->pinfuncnt].name = child->name;
+ state->pinfuncs[state->pinfuncnt].group = name;
+ state->pinfuncnt++;
+ }
+ }
+
+ state->pctl = pinctrl_register(&tb10x_pindesc, dev, state);
+ if (IS_ERR(state->pctl)) {
+ dev_err(dev, "could not register TB10x pin driver\n");
+ ret = PTR_ERR(state->pctl);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ mutex_destroy(&state->mutex);
+ return ret;
+}
+
+static int tb10x_pinctrl_remove(struct platform_device *pdev)
+{
+ struct tb10x_pinctrl *state = platform_get_drvdata(pdev);
+
+ pinctrl_unregister(state->pctl);
+ mutex_destroy(&state->mutex);
+
+ return 0;
+}
+
+
+static const struct of_device_id tb10x_pinctrl_dt_ids[] = {
+ { .compatible = "abilis,tb10x-iomux" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tb10x_pinctrl_dt_ids);
+
+static struct platform_driver tb10x_pinctrl_pdrv = {
+ .probe = tb10x_pinctrl_probe,
+ .remove = tb10x_pinctrl_remove,
+ .driver = {
+ .name = "tb10x_pinctrl",
+ .of_match_table = of_match_ptr(tb10x_pinctrl_dt_ids),
+ .owner = THIS_MODULE
+ }
+};
+
+static int __init tb10x_iopinctrl_init(void)
+{
+ return platform_driver_register(&tb10x_pinctrl_pdrv);
+}
+
+static void __exit tb10x_iopinctrl_exit(void)
+{
+ platform_driver_unregister(&tb10x_pinctrl_pdrv);
+}
+
+MODULE_AUTHOR("Christian Ruppert <[email protected]>");
+MODULE_LICENSE("GPL");
+module_init(tb10x_iopinctrl_init);
+module_exit(tb10x_iopinctrl_exit);
--
1.7.1

2013-06-18 08:09:52

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/4] Make non-linear GPIO ranges accesible from gpiolib

On Mon, Jun 17, 2013 at 6:04 PM, Christian Ruppert
<[email protected]> wrote:

> This patch adds the infrastructure required to register non-linear gpio
> ranges through gpiolib and the standard GPIO device tree bindings.
>
> Signed-off-by: Christian Ruppert <[email protected]>

> Documentation/devicetree/bindings/gpio/gpio.txt | 44 ++++++++++++++++++
> arch/arc/boot/dts/abilis_tb100.dtsi | 56 +++++++++++-----------
> arch/arc/boot/dts/abilis_tb101.dtsi | 56 +++++++++++-----------
> arch/arc/boot/dts/abilis_tb10x.dtsi | 3 +-
> drivers/gpio/gpiolib-of.c | 31 +++++++++---
> drivers/gpio/gpiolib.c | 47 +++++++++++++++++++
> drivers/pinctrl/core.c | 23 +++++++++
> include/asm-generic/gpio.h | 10 ++++
> include/linux/gpio.h | 9 ++++
> include/linux/pinctrl/pinctrl.h | 3 +
> 10 files changed, 216 insertions(+), 66 deletions(-)

Can you please split this patch in two?

One altering drivers/* include/* and the Documentation/*

And a separate patch to alter all the abilis trees?

I do not maintain bisectability across device trees
to the rest of the kernel, as these trees are supposed
to be moved out of the kernel at some point.

Yours,
Linus Walleij

2013-06-18 09:25:54

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/4] Make non-linear GPIO ranges accesible from gpiolib

On Tue, Jun 18, 2013 at 10:09:47AM +0200, Linus Walleij wrote:
> On Mon, Jun 17, 2013 at 6:04 PM, Christian Ruppert
> <[email protected]> wrote:
>
> > This patch adds the infrastructure required to register non-linear gpio
> > ranges through gpiolib and the standard GPIO device tree bindings.
> >
> > Signed-off-by: Christian Ruppert <[email protected]>
>
> > Documentation/devicetree/bindings/gpio/gpio.txt | 44 ++++++++++++++++++
> > arch/arc/boot/dts/abilis_tb100.dtsi | 56 +++++++++++-----------
> > arch/arc/boot/dts/abilis_tb101.dtsi | 56 +++++++++++-----------
> > arch/arc/boot/dts/abilis_tb10x.dtsi | 3 +-
> > drivers/gpio/gpiolib-of.c | 31 +++++++++---
> > drivers/gpio/gpiolib.c | 47 +++++++++++++++++++
> > drivers/pinctrl/core.c | 23 +++++++++
> > include/asm-generic/gpio.h | 10 ++++
> > include/linux/gpio.h | 9 ++++
> > include/linux/pinctrl/pinctrl.h | 3 +
> > 10 files changed, 216 insertions(+), 66 deletions(-)
>
> Can you please split this patch in two?
>
> One altering drivers/* include/* and the Documentation/*
>
> And a separate patch to alter all the abilis trees?
>
> I do not maintain bisectability across device trees
> to the rest of the kernel, as these trees are supposed
> to be moved out of the kernel at some point.

Hi Linus,

In the follow up the complete patch series without the device tree
modifications. I'll check their upstream separately with Vineet (ARC
platform maintainer) when everything else is applied. Tell me if you
would like to apply the device tree changes yourself nevertheless and
I'll send a patch.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-18 09:29:42

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 4/4] Add Abilis Systems Sarl to device tree vendor prefixes


Signed-off-by: Christian Ruppert <[email protected]>
---
.../devicetree/bindings/vendor-prefixes.txt | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 6931c43..e2a8fff 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -3,6 +3,7 @@ Device tree binding vendor prefix registry. Keep list in alphabetical order.
This isn't an exhaustive list, but you should add new prefixes to it before
using them to avoid name-space collisions.

+abilis Abilis Systems Sarl
ad Avionic Design GmbH
adi Analog Devices, Inc.
aeroflexgaisler Aeroflex Gaisler AB
--
1.7.1

2013-06-18 09:29:55

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 3/4] GPIO: Add TB10x GPIO driver

The GPIO driver for the Abilis Systems TB10x series of SOCs based on ARC700
CPUs. It supports GPIO control and GPIO interrupt generation. This driver
works in conjunction with the TB10x pinctrl driver.

Signed-off-by: Sascha Leuenberger <[email protected]>
Signed-off-by: Christian Ruppert <[email protected]>
---
.../devicetree/bindings/gpio/abilis,tb10x-gpio.txt | 36 ++
drivers/gpio/Kconfig | 4 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-tb10x.c | 341 ++++++++++++++++++++
4 files changed, 382 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
create mode 100644 drivers/gpio/gpio-tb10x.c

diff --git a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
new file mode 100644
index 0000000..26a3c68
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
@@ -0,0 +1,36 @@
+* Abilis TB10x GPIO controller
+
+Required Properties:
+- compatible: Should be "abilis,tb10x-gpio"
+- reg: Address and length of the register set for the device
+- gpio-controller: Marks the device node as a gpio controller.
+- #gpio-cells: Should be <2>. The first cell is the pin number and the
+ second cell is used to specify optional parameters:
+ - bit 0 specifies polarity (0 for normal, 1 for inverted).
+- ngpio: the number of GPIO pins this driver controls.
+
+Optional Properties:
+- interrupt-controller: Marks the device node as an interrupt controller.
+- #interrupt-cells: Should be <1>. Interrupts are triggered on both edges.
+- interrupts: Defines the interrupt line connecting this GPIO controller to
+ its parent interrupt controller.
+- interrupt-parent: Defines the parent interrupt controller.
+
+GPIO ranges are specified as described in
+Documentation/devicetree/bindings/gpio/gpio.txt
+
+Example:
+
+ gpioa: gpio@FF140000 {
+ compatible = "abilis,tb10x-gpio";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&tb10x_ictl>;
+ interrupts = <27 2>;
+ reg = <0xFF140000 0x1000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpio = <3>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioa_pins";
+ };
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 573c449..1faeb7c 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -317,6 +317,10 @@ config GPIO_GRGPIO
Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB
VHDL IP core library.

+config GPIO_TB10X
+ bool
+ select OF_GPIO
+
comment "I2C GPIO expanders:"

config GPIO_ARIZONA
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 0cb2d65..7bc567f 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o
+obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c
new file mode 100644
index 0000000..7f67a4c
--- /dev/null
+++ b/drivers/gpio/gpio-tb10x.c
@@ -0,0 +1,341 @@
+/* Abilis Systems MODULE DESCRIPTION
+ *
+ * Copyright (C) Abilis Systems 2013
+ *
+ * Authors: Sascha Leuenberger <[email protected]>
+ * Christian Ruppert <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/pinctrl/consumer.h>
+
+#define TB10X_GPIO_DIR_IN (0x00000000)
+#define TB10X_GPIO_DIR_OUT (0x00000001)
+#define OFFSET_TO_REG_DDR (0x00)
+#define OFFSET_TO_REG_DATA (0x04)
+#define OFFSET_TO_REG_INT_EN (0x08)
+#define OFFSET_TO_REG_CHANGE (0x0C)
+#define OFFSET_TO_REG_WRMASK (0x10)
+#define OFFSET_TO_REG_INT_TYPE (0x14)
+
+
+/**
+ * @spinlock: used for atomic read/modify/write of registers
+ * @base: register base address
+ * @domain: IRQ domain of GPIO generated interrupts managed by this controller
+ * @irq: Interrupt line of parent interrupt controller
+ * @gc: gpio_chip structure associated to this GPIO controller
+ */
+struct tb10x_gpio {
+ spinlock_t spinlock;
+ void __iomem *base;
+ struct irq_domain *domain;
+ int irq;
+ struct gpio_chip gc;
+};
+
+static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs)
+{
+ return ioread32(gpio->base + offs);
+}
+
+static inline void tb10x_reg_write(struct tb10x_gpio *gpio, unsigned int offs,
+ u32 val)
+{
+ iowrite32(val, gpio->base + offs);
+}
+
+static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs,
+ u32 mask, u32 val)
+{
+ u32 r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio->spinlock, flags);
+
+ r = tb10x_reg_read(gpio, offs);
+ r = (r & ~mask) | (val & mask);
+
+ tb10x_reg_write(gpio, offs, r);
+
+ spin_unlock_irqrestore(&gpio->spinlock, flags);
+}
+
+static inline struct tb10x_gpio *to_tb10x_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct tb10x_gpio, gc);
+}
+
+static int tb10x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = BIT(offset);
+ int val = TB10X_GPIO_DIR_IN << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
+
+ return 0;
+}
+
+static int tb10x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int val;
+
+ val = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_DATA);
+
+ if (val & BIT(offset))
+ return 1;
+ else
+ return 0;
+}
+
+static void tb10x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = BIT(offset);
+ int val = value << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DATA, mask, val);
+}
+
+static int tb10x_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = BIT(offset);
+ int val = TB10X_GPIO_DIR_OUT << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
+
+ return 0;
+}
+
+static int tb10x_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void tb10x_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+}
+
+static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+
+ return irq_create_mapping(tb10x_gpio->domain, offset);
+}
+
+static int tb10x_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ if ((type & IRQF_TRIGGER_MASK) != IRQ_TYPE_EDGE_BOTH) {
+ pr_err("Only (both) edge triggered interrupts supported.\n");
+ return -EINVAL;
+ }
+
+ irqd_set_trigger_type(data, type);
+
+ return IRQ_SET_MASK_OK;
+}
+
+static irqreturn_t tb10x_gpio_irq_cascade(int irq, void *data)
+{
+ struct tb10x_gpio *tb10x_gpio = data;
+ u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_CHANGE);
+ u32 m = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);
+ const unsigned long bits = r & m;
+ int i;
+
+ for_each_set_bit(i, &bits, 32)
+ generic_handle_irq(irq_find_mapping(tb10x_gpio->domain, i));
+
+ return IRQ_HANDLED;
+}
+
+static int tb10x_gpio_probe(struct platform_device *pdev)
+{
+ struct tb10x_gpio *tb10x_gpio;
+ struct resource *mem;
+ struct device_node *dn = pdev->dev.of_node;
+ int ret = -EBUSY;
+ u32 ngpio;
+
+ if (!dn)
+ return -EINVAL;
+
+ if (of_property_read_u32(dn, "ngpio", &ngpio))
+ return -EINVAL;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource defined.\n");
+ return -EINVAL;
+ }
+
+ tb10x_gpio = devm_kzalloc(&pdev->dev, sizeof(*tb10x_gpio), GFP_KERNEL);
+ if (tb10x_gpio == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&tb10x_gpio->spinlock);
+
+ tb10x_gpio->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (!tb10x_gpio->base) {
+ dev_err(&pdev->dev, "Could not remap reg space.\n");
+ goto fail_ioremap;
+ }
+
+ tb10x_gpio->gc.label = of_node_full_name(dn);
+ tb10x_gpio->gc.dev = &pdev->dev;
+ tb10x_gpio->gc.owner = THIS_MODULE;
+ tb10x_gpio->gc.direction_input = tb10x_gpio_direction_in;
+ tb10x_gpio->gc.get = tb10x_gpio_get;
+ tb10x_gpio->gc.direction_output = tb10x_gpio_direction_out;
+ tb10x_gpio->gc.set = tb10x_gpio_set;
+ tb10x_gpio->gc.request = tb10x_gpio_request;
+ tb10x_gpio->gc.free = tb10x_gpio_free;
+ tb10x_gpio->gc.base = -1;
+ tb10x_gpio->gc.ngpio = ngpio;
+ tb10x_gpio->gc.can_sleep = 0;
+
+
+ ret = gpiochip_add(&tb10x_gpio->gc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not add gpiochip.\n");
+ goto fail_gpiochip_registration;
+ }
+
+ platform_set_drvdata(pdev, tb10x_gpio);
+
+ if (of_find_property(dn, "interrupt-controller", NULL)) {
+ struct irq_chip_generic *gc;
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "No interrupt specified.\n");
+ goto fail_get_irq;
+ }
+
+ tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
+ tb10x_gpio->irq = ret;
+
+ ret = devm_request_irq(&pdev->dev, ret, tb10x_gpio_irq_cascade,
+ IRQF_TRIGGER_NONE | IRQF_SHARED,
+ dev_name(&pdev->dev), tb10x_gpio);
+ if (ret != 0)
+ goto fail_request_irq;
+
+ tb10x_gpio->domain = irq_domain_add_linear(dn,
+ tb10x_gpio->gc.ngpio,
+ &irq_generic_chip_ops, NULL);
+ if (!tb10x_gpio->domain) {
+ ret = -ENOMEM;
+ goto fail_irq_domain;
+ }
+
+ ret = irq_alloc_domain_generic_chips(tb10x_gpio->domain,
+ tb10x_gpio->gc.ngpio, 1, tb10x_gpio->gc.label,
+ handle_edge_irq, IRQ_NOREQUEST, IRQ_NOPROBE,
+ IRQ_GC_INIT_MASK_CACHE);
+ if (ret)
+ goto fail_irq_domain;
+
+ gc = tb10x_gpio->domain->gc->gc[0];
+ gc->reg_base = tb10x_gpio->base;
+ gc->chip_types[0].type = IRQ_TYPE_EDGE_BOTH;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+ gc->chip_types[0].chip.irq_set_type = tb10x_gpio_irq_set_type;
+ gc->chip_types[0].regs.ack = OFFSET_TO_REG_CHANGE;
+ gc->chip_types[0].regs.mask = OFFSET_TO_REG_INT_EN;
+ }
+
+ return 0;
+
+fail_irq_domain:
+fail_request_irq:
+fail_get_irq:
+ gpiochip_remove(&tb10x_gpio->gc);
+fail_gpiochip_registration:
+fail_ioremap:
+ return ret;
+}
+
+static int __exit tb10x_gpio_remove(struct platform_device *pdev)
+{
+ struct tb10x_gpio *tb10x_gpio = platform_get_drvdata(pdev);
+ int ret;
+
+ if (tb10x_gpio->gc.to_irq) {
+ irq_remove_generic_chip(tb10x_gpio->domain->gc->gc[0],
+ BIT(tb10x_gpio->gc.ngpio) - 1, 0, 0);
+ kfree(tb10x_gpio->domain->gc);
+ irq_domain_remove(tb10x_gpio->domain);
+ free_irq(tb10x_gpio->irq, tb10x_gpio);
+ }
+ ret = gpiochip_remove(&tb10x_gpio->gc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct of_device_id tb10x_gpio_dt_ids[] = {
+ { .compatible = "abilis,tb10x-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tb10x_gpio_dt_ids);
+
+static struct platform_driver tb10x_gpio_driver = {
+ .probe = tb10x_gpio_probe,
+ .remove = tb10x_gpio_remove,
+ .driver = {
+ .name = "tb10x-gpio",
+ .of_match_table = of_match_ptr(tb10x_gpio_dt_ids),
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init ab_gpio_init(void)
+{
+ return platform_driver_register(&tb10x_gpio_driver);
+}
+
+static void __exit ab_gpio_exit(void)
+{
+ platform_driver_unregister(&tb10x_gpio_driver);
+}
+
+module_init(ab_gpio_init);
+module_exit(ab_gpio_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("tb10x gpio.");
+MODULE_VERSION("0.0.1");
--
1.7.1

2013-06-18 09:30:04

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 2/4] pinmux: Add TB10x pinmux driver

The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
Used to control the pinmux and is a prerequisite for the GPIO driver.

Signed-off-by: Christian Ruppert <[email protected]>
Signed-off-by: Pierrick Hascoet <[email protected]>
---
.../bindings/pinctrl/abilis,tb10x-iomux.txt | 81 ++
drivers/pinctrl/Kconfig | 4 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-tb10x.c | 878 ++++++++++++++++++++
4 files changed, 964 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
create mode 100644 drivers/pinctrl/pinctrl-tb10x.c

diff --git a/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
new file mode 100644
index 0000000..236b628
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
@@ -0,0 +1,81 @@
+Abilis Systems TB10x pin controller
+===================================
+
+Required properties
+-------------------
+
+- compatible: should be "abilis,tb10x-iomux";
+- reg: should contain the physical address and size of the pin controller's
+ register range.
+
+
+Port definitions
+----------------
+
+Ports are defined (and referenced) by sub-nodes of the pin controller. Every
+sub-node defines exactly one port (i.e. a set of pins). Ports are predefined
+as named pin groups inside the pin controller driver and these names are used
+to associate pin group predefinitions to pin controller sub-nodes.
+
+Required port definition subnode properties:
+ - pingrp: should be set to the name of the port's pin group.
+
+The following pin groups are available:
+ - GPIO ports: gpioa_pins, gpiob_pins, gpioc_pins, gpiod_pins, gpioe_pins,
+ gpiof_pins, gpiog_pins, gpioh_pins, gpioi_pins, gpioj_pins,
+ gpiok_pins, gpiol_pins, gpiom_pins, gpion_pins
+ - Serial TS input ports: mis0_pins, mis1_pins, mis2_pins, mis3_pins,
+ mis4_pins, mis5_pins, mis6_pins, mis7_pins
+ - Parallel TS input ports: mip1_pins, mip3_pins, mip5_pins, mip7_pins
+ - Serial TS output ports: mos0_pins, mos1_pins, mos2_pins, mos3_pins
+ - Parallel TS output port: mop_pins
+ - CI+ port: ciplus_pins
+ - CableCard (Mcard) port: mcard_pins
+ - Smart card ports: stc0_pins, stc1_pins
+ - UART ports: uart0_pins, uart1_pins
+ - SPI ports: spi1_pins, spi3_pins
+ - JTAG: jtag_pins
+
+All other ports of the chip are not multiplexed and thus not managed by this
+driver.
+
+
+GPIO ranges definition
+----------------------
+
+The named pin groups of GPIO ports can be used to define GPIO ranges as
+explained in Documentation/devicetree/bindings/gpio/gpio.txt.
+
+
+Example
+-------
+
+iomux: iomux@FF10601c {
+ compatible = "abilis,tb10x-iomux";
+ reg = <0xFF10601c 0x4>;
+ pctl_gpio_a: pctl-gpio-a {
+ pingrp = "gpioa_pins";
+ };
+ pctl_uart0: pctl-uart0 {
+ pingrp = "uart0_pins";
+ };
+};
+uart@FF100000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0xFF100000 0x100>;
+ clock-frequency = <166666666>;
+ interrupts = <25 1>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pctl_uart0>;
+};
+gpioa: gpio@FF140000 {
+ compatible = "abilis,tb10x-gpio";
+ reg = <0xFF140000 0x1000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpio = <3>;
+ gpio-ranges = <&iomux 0 0>;
+ gpio-ranges-group-names = "gpioa_pins";
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 8f66924..c6cb6ee 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -231,6 +231,10 @@ config PINCTRL_XWAY
depends on SOC_TYPE_XWAY
depends on PINCTRL_LANTIQ

+config PINCTRL_TB10X
+ bool
+ depends on ARC_PLAT_TB10X
+
endmenu

endif
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 9bdaeb8..3b21b20 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_PINCTRL_EXYNOS5440) += pinctrl-exynos5440.o
obj-$(CONFIG_PINCTRL_S3C64XX) += pinctrl-s3c64xx.o
obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o
obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o
+obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o

obj-$(CONFIG_PLAT_ORION) += mvebu/
obj-$(CONFIG_ARCH_SHMOBILE) += sh-pfc/
diff --git a/drivers/pinctrl/pinctrl-tb10x.c b/drivers/pinctrl/pinctrl-tb10x.c
new file mode 100644
index 0000000..719f3c1
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tb10x.c
@@ -0,0 +1,878 @@
+/*
+ * Abilis Systems TB10x pin control driver
+ *
+ * Copyright (C) Abilis Systems 2012
+ *
+ * Author: Christian Ruppert <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/stringify.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#define TB10X_PORT1 (0)
+#define TB10X_PORT2 (16)
+#define TB10X_PORT3 (32)
+#define TB10X_PORT4 (48)
+#define TB10X_PORT5 (128)
+#define TB10X_PORT6 (64)
+#define TB10X_PORT7 (80)
+#define TB10X_PORT8 (96)
+#define TB10X_PORT9 (112)
+#define TB10X_GPIOS (256)
+
+#define PCFG_PORT_BITWIDTH (2)
+#define PCFG_PORT_MASK(PORT) \
+ (((1 << PCFG_PORT_BITWIDTH) - 1) << (PCFG_PORT_BITWIDTH * (PORT)))
+
+static const struct pinctrl_pin_desc tb10x_pins[] = {
+ /* Port 1 */
+ PINCTRL_PIN(TB10X_PORT1 + 0, "MICLK_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 1, "MISTRT_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 2, "MIVAL_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 3, "MDI_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 4, "GPIOA0"),
+ PINCTRL_PIN(TB10X_PORT1 + 5, "GPIOA1"),
+ PINCTRL_PIN(TB10X_PORT1 + 6, "GPIOA2"),
+ PINCTRL_PIN(TB10X_PORT1 + 7, "MDI_S1"),
+ PINCTRL_PIN(TB10X_PORT1 + 8, "MIVAL_S1"),
+ PINCTRL_PIN(TB10X_PORT1 + 9, "MISTRT_S1"),
+ PINCTRL_PIN(TB10X_PORT1 + 10, "MICLK_S1"),
+ /* Port 2 */
+ PINCTRL_PIN(TB10X_PORT2 + 0, "MICLK_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 1, "MISTRT_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 2, "MIVAL_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 3, "MDI_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 4, "GPIOC0"),
+ PINCTRL_PIN(TB10X_PORT2 + 5, "GPIOC1"),
+ PINCTRL_PIN(TB10X_PORT2 + 6, "GPIOC2"),
+ PINCTRL_PIN(TB10X_PORT2 + 7, "MDI_S3"),
+ PINCTRL_PIN(TB10X_PORT2 + 8, "MIVAL_S3"),
+ PINCTRL_PIN(TB10X_PORT2 + 9, "MISTRT_S3"),
+ PINCTRL_PIN(TB10X_PORT2 + 10, "MICLK_S3"),
+ /* Port 3 */
+ PINCTRL_PIN(TB10X_PORT3 + 0, "MICLK_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 1, "MISTRT_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 2, "MIVAL_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 3, "MDI_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 4, "GPIOE0"),
+ PINCTRL_PIN(TB10X_PORT3 + 5, "GPIOE1"),
+ PINCTRL_PIN(TB10X_PORT3 + 6, "GPIOE2"),
+ PINCTRL_PIN(TB10X_PORT3 + 7, "MDI_S5"),
+ PINCTRL_PIN(TB10X_PORT3 + 8, "MIVAL_S5"),
+ PINCTRL_PIN(TB10X_PORT3 + 9, "MISTRT_S5"),
+ PINCTRL_PIN(TB10X_PORT3 + 10, "MICLK_S5"),
+ /* Port 4 */
+ PINCTRL_PIN(TB10X_PORT4 + 0, "MICLK_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 1, "MISTRT_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 2, "MIVAL_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 3, "MDI_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 4, "GPIOG0"),
+ PINCTRL_PIN(TB10X_PORT4 + 5, "GPIOG1"),
+ PINCTRL_PIN(TB10X_PORT4 + 6, "GPIOG2"),
+ PINCTRL_PIN(TB10X_PORT4 + 7, "MDI_S7"),
+ PINCTRL_PIN(TB10X_PORT4 + 8, "MIVAL_S7"),
+ PINCTRL_PIN(TB10X_PORT4 + 9, "MISTRT_S7"),
+ PINCTRL_PIN(TB10X_PORT4 + 10, "MICLK_S7"),
+ /* Port 5 */
+ PINCTRL_PIN(TB10X_PORT5 + 0, "PC_CE1N"),
+ PINCTRL_PIN(TB10X_PORT5 + 1, "PC_CE2N"),
+ PINCTRL_PIN(TB10X_PORT5 + 2, "PC_REGN"),
+ PINCTRL_PIN(TB10X_PORT5 + 3, "PC_INPACKN"),
+ PINCTRL_PIN(TB10X_PORT5 + 4, "PC_OEN"),
+ PINCTRL_PIN(TB10X_PORT5 + 5, "PC_WEN"),
+ PINCTRL_PIN(TB10X_PORT5 + 6, "PC_IORDN"),
+ PINCTRL_PIN(TB10X_PORT5 + 7, "PC_IOWRN"),
+ PINCTRL_PIN(TB10X_PORT5 + 8, "PC_RDYIRQN"),
+ PINCTRL_PIN(TB10X_PORT5 + 9, "PC_WAITN"),
+ PINCTRL_PIN(TB10X_PORT5 + 10, "PC_A0"),
+ PINCTRL_PIN(TB10X_PORT5 + 11, "PC_A1"),
+ PINCTRL_PIN(TB10X_PORT5 + 12, "PC_A2"),
+ PINCTRL_PIN(TB10X_PORT5 + 13, "PC_A3"),
+ PINCTRL_PIN(TB10X_PORT5 + 14, "PC_A4"),
+ PINCTRL_PIN(TB10X_PORT5 + 15, "PC_A5"),
+ PINCTRL_PIN(TB10X_PORT5 + 16, "PC_A6"),
+ PINCTRL_PIN(TB10X_PORT5 + 17, "PC_A7"),
+ PINCTRL_PIN(TB10X_PORT5 + 18, "PC_A8"),
+ PINCTRL_PIN(TB10X_PORT5 + 19, "PC_A9"),
+ PINCTRL_PIN(TB10X_PORT5 + 20, "PC_A10"),
+ PINCTRL_PIN(TB10X_PORT5 + 21, "PC_A11"),
+ PINCTRL_PIN(TB10X_PORT5 + 22, "PC_A12"),
+ PINCTRL_PIN(TB10X_PORT5 + 23, "PC_A13"),
+ PINCTRL_PIN(TB10X_PORT5 + 24, "PC_A14"),
+ PINCTRL_PIN(TB10X_PORT5 + 25, "PC_D0"),
+ PINCTRL_PIN(TB10X_PORT5 + 26, "PC_D1"),
+ PINCTRL_PIN(TB10X_PORT5 + 27, "PC_D2"),
+ PINCTRL_PIN(TB10X_PORT5 + 28, "PC_D3"),
+ PINCTRL_PIN(TB10X_PORT5 + 29, "PC_D4"),
+ PINCTRL_PIN(TB10X_PORT5 + 30, "PC_D5"),
+ PINCTRL_PIN(TB10X_PORT5 + 31, "PC_D6"),
+ PINCTRL_PIN(TB10X_PORT5 + 32, "PC_D7"),
+ PINCTRL_PIN(TB10X_PORT5 + 33, "PC_MOSTRT"),
+ PINCTRL_PIN(TB10X_PORT5 + 34, "PC_MOVAL"),
+ PINCTRL_PIN(TB10X_PORT5 + 35, "PC_MDO0"),
+ PINCTRL_PIN(TB10X_PORT5 + 36, "PC_MDO1"),
+ PINCTRL_PIN(TB10X_PORT5 + 37, "PC_MDO2"),
+ PINCTRL_PIN(TB10X_PORT5 + 38, "PC_MDO3"),
+ PINCTRL_PIN(TB10X_PORT5 + 39, "PC_MDO4"),
+ PINCTRL_PIN(TB10X_PORT5 + 40, "PC_MDO5"),
+ PINCTRL_PIN(TB10X_PORT5 + 41, "PC_MDO6"),
+ PINCTRL_PIN(TB10X_PORT5 + 42, "PC_MDO7"),
+ PINCTRL_PIN(TB10X_PORT5 + 43, "PC_MISTRT"),
+ PINCTRL_PIN(TB10X_PORT5 + 44, "PC_MIVAL"),
+ PINCTRL_PIN(TB10X_PORT5 + 45, "PC_MDI0"),
+ PINCTRL_PIN(TB10X_PORT5 + 46, "PC_MDI1"),
+ PINCTRL_PIN(TB10X_PORT5 + 47, "PC_MDI2"),
+ PINCTRL_PIN(TB10X_PORT5 + 48, "PC_MDI3"),
+ PINCTRL_PIN(TB10X_PORT5 + 49, "PC_MDI4"),
+ PINCTRL_PIN(TB10X_PORT5 + 50, "PC_MDI5"),
+ PINCTRL_PIN(TB10X_PORT5 + 51, "PC_MDI6"),
+ PINCTRL_PIN(TB10X_PORT5 + 52, "PC_MDI7"),
+ PINCTRL_PIN(TB10X_PORT5 + 53, "PC_MICLK"),
+ /* Port 6 */
+ PINCTRL_PIN(TB10X_PORT6 + 0, "T_MOSTRT_S0"),
+ PINCTRL_PIN(TB10X_PORT6 + 1, "T_MOVAL_S0"),
+ PINCTRL_PIN(TB10X_PORT6 + 2, "T_MDO_S0"),
+ PINCTRL_PIN(TB10X_PORT6 + 3, "T_MOSTRT_S1"),
+ PINCTRL_PIN(TB10X_PORT6 + 4, "T_MOVAL_S1"),
+ PINCTRL_PIN(TB10X_PORT6 + 5, "T_MDO_S1"),
+ PINCTRL_PIN(TB10X_PORT6 + 6, "T_MOSTRT_S2"),
+ PINCTRL_PIN(TB10X_PORT6 + 7, "T_MOVAL_S2"),
+ PINCTRL_PIN(TB10X_PORT6 + 8, "T_MDO_S2"),
+ PINCTRL_PIN(TB10X_PORT6 + 9, "T_MOSTRT_S3"),
+ /* Port 7 */
+ PINCTRL_PIN(TB10X_PORT7 + 0, "UART0_TXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 1, "UART0_RXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 2, "UART0_CTS"),
+ PINCTRL_PIN(TB10X_PORT7 + 3, "UART0_RTS"),
+ PINCTRL_PIN(TB10X_PORT7 + 4, "UART1_TXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 5, "UART1_RXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 6, "UART1_CTS"),
+ PINCTRL_PIN(TB10X_PORT7 + 7, "UART1_RTS"),
+ /* Port 8 */
+ PINCTRL_PIN(TB10X_PORT8 + 0, "SPI3_CLK"),
+ PINCTRL_PIN(TB10X_PORT8 + 1, "SPI3_MISO"),
+ PINCTRL_PIN(TB10X_PORT8 + 2, "SPI3_MOSI"),
+ PINCTRL_PIN(TB10X_PORT8 + 3, "SPI3_SSN"),
+ /* Port 9 */
+ PINCTRL_PIN(TB10X_PORT9 + 0, "SPI1_CLK"),
+ PINCTRL_PIN(TB10X_PORT9 + 1, "SPI1_MISO"),
+ PINCTRL_PIN(TB10X_PORT9 + 2, "SPI1_MOSI"),
+ PINCTRL_PIN(TB10X_PORT9 + 3, "SPI1_SSN0"),
+ PINCTRL_PIN(TB10X_PORT9 + 4, "SPI1_SSN1"),
+ /* Unmuxed GPIOs */
+ PINCTRL_PIN(TB10X_GPIOS + 0, "GPIOB0"),
+ PINCTRL_PIN(TB10X_GPIOS + 1, "GPIOB1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 2, "GPIOD0"),
+ PINCTRL_PIN(TB10X_GPIOS + 3, "GPIOD1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 4, "GPIOF0"),
+ PINCTRL_PIN(TB10X_GPIOS + 5, "GPIOF1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 6, "GPIOH0"),
+ PINCTRL_PIN(TB10X_GPIOS + 7, "GPIOH1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 8, "GPIOI0"),
+ PINCTRL_PIN(TB10X_GPIOS + 9, "GPIOI1"),
+ PINCTRL_PIN(TB10X_GPIOS + 10, "GPIOI2"),
+ PINCTRL_PIN(TB10X_GPIOS + 11, "GPIOI3"),
+ PINCTRL_PIN(TB10X_GPIOS + 12, "GPIOI4"),
+ PINCTRL_PIN(TB10X_GPIOS + 13, "GPIOI5"),
+ PINCTRL_PIN(TB10X_GPIOS + 14, "GPIOI6"),
+ PINCTRL_PIN(TB10X_GPIOS + 15, "GPIOI7"),
+ PINCTRL_PIN(TB10X_GPIOS + 16, "GPIOI8"),
+ PINCTRL_PIN(TB10X_GPIOS + 17, "GPIOI9"),
+ PINCTRL_PIN(TB10X_GPIOS + 18, "GPIOI10"),
+ PINCTRL_PIN(TB10X_GPIOS + 19, "GPIOI11"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 20, "GPION0"),
+ PINCTRL_PIN(TB10X_GPIOS + 21, "GPION1"),
+ PINCTRL_PIN(TB10X_GPIOS + 22, "GPION2"),
+ PINCTRL_PIN(TB10X_GPIOS + 23, "GPION3"),
+#define MAX_PIN (TB10X_GPIOS + 24)
+ PINCTRL_PIN(MAX_PIN, "GPION4"),
+};
+
+
+/* Port 1 */
+static const unsigned mis0_pins[] = { TB10X_PORT1 + 0, TB10X_PORT1 + 1,
+ TB10X_PORT1 + 2, TB10X_PORT1 + 3};
+static const unsigned gpioa_pins[] = { TB10X_PORT1 + 4, TB10X_PORT1 + 5,
+ TB10X_PORT1 + 6};
+static const unsigned mis1_pins[] = { TB10X_PORT1 + 7, TB10X_PORT1 + 8,
+ TB10X_PORT1 + 9, TB10X_PORT1 + 10};
+static const unsigned mip1_pins[] = { TB10X_PORT1 + 0, TB10X_PORT1 + 1,
+ TB10X_PORT1 + 2, TB10X_PORT1 + 3,
+ TB10X_PORT1 + 4, TB10X_PORT1 + 5,
+ TB10X_PORT1 + 6, TB10X_PORT1 + 7,
+ TB10X_PORT1 + 8, TB10X_PORT1 + 9,
+ TB10X_PORT1 + 10};
+
+/* Port 2 */
+static const unsigned mis2_pins[] = { TB10X_PORT2 + 0, TB10X_PORT2 + 1,
+ TB10X_PORT2 + 2, TB10X_PORT2 + 3};
+static const unsigned gpioc_pins[] = { TB10X_PORT2 + 4, TB10X_PORT2 + 5,
+ TB10X_PORT2 + 6};
+static const unsigned mis3_pins[] = { TB10X_PORT2 + 7, TB10X_PORT2 + 8,
+ TB10X_PORT2 + 9, TB10X_PORT2 + 10};
+static const unsigned mip3_pins[] = { TB10X_PORT2 + 0, TB10X_PORT2 + 1,
+ TB10X_PORT2 + 2, TB10X_PORT2 + 3,
+ TB10X_PORT2 + 4, TB10X_PORT2 + 5,
+ TB10X_PORT2 + 6, TB10X_PORT2 + 7,
+ TB10X_PORT2 + 8, TB10X_PORT2 + 9,
+ TB10X_PORT2 + 10};
+
+/* Port 3 */
+static const unsigned mis4_pins[] = { TB10X_PORT3 + 0, TB10X_PORT3 + 1,
+ TB10X_PORT3 + 2, TB10X_PORT3 + 3};
+static const unsigned gpioe_pins[] = { TB10X_PORT3 + 4, TB10X_PORT3 + 5,
+ TB10X_PORT3 + 6};
+static const unsigned mis5_pins[] = { TB10X_PORT3 + 7, TB10X_PORT3 + 8,
+ TB10X_PORT3 + 9, TB10X_PORT3 + 10};
+static const unsigned mip5_pins[] = { TB10X_PORT3 + 0, TB10X_PORT3 + 1,
+ TB10X_PORT3 + 2, TB10X_PORT3 + 3,
+ TB10X_PORT3 + 4, TB10X_PORT3 + 5,
+ TB10X_PORT3 + 6, TB10X_PORT3 + 7,
+ TB10X_PORT3 + 8, TB10X_PORT3 + 9,
+ TB10X_PORT3 + 10};
+
+/* Port 4 */
+static const unsigned mis6_pins[] = { TB10X_PORT4 + 0, TB10X_PORT4 + 1,
+ TB10X_PORT4 + 2, TB10X_PORT4 + 3};
+static const unsigned gpiog_pins[] = { TB10X_PORT4 + 4, TB10X_PORT4 + 5,
+ TB10X_PORT4 + 6};
+static const unsigned mis7_pins[] = { TB10X_PORT4 + 7, TB10X_PORT4 + 8,
+ TB10X_PORT4 + 9, TB10X_PORT4 + 10};
+static const unsigned mip7_pins[] = { TB10X_PORT4 + 0, TB10X_PORT4 + 1,
+ TB10X_PORT4 + 2, TB10X_PORT4 + 3,
+ TB10X_PORT4 + 4, TB10X_PORT4 + 5,
+ TB10X_PORT4 + 6, TB10X_PORT4 + 7,
+ TB10X_PORT4 + 8, TB10X_PORT4 + 9,
+ TB10X_PORT4 + 10};
+
+/* Port 6 */
+static const unsigned mop_pins[] = { TB10X_PORT6 + 0, TB10X_PORT6 + 1,
+ TB10X_PORT6 + 2, TB10X_PORT6 + 3,
+ TB10X_PORT6 + 4, TB10X_PORT6 + 5,
+ TB10X_PORT6 + 6, TB10X_PORT6 + 7,
+ TB10X_PORT6 + 8, TB10X_PORT6 + 9};
+static const unsigned mos0_pins[] = { TB10X_PORT6 + 0, TB10X_PORT6 + 1,
+ TB10X_PORT6 + 2};
+static const unsigned mos1_pins[] = { TB10X_PORT6 + 3, TB10X_PORT6 + 4,
+ TB10X_PORT6 + 5};
+static const unsigned mos2_pins[] = { TB10X_PORT6 + 6, TB10X_PORT6 + 7,
+ TB10X_PORT6 + 8};
+static const unsigned mos3_pins[] = { TB10X_PORT6 + 9};
+
+/* Port 7 */
+static const unsigned uart0_pins[] = { TB10X_PORT7 + 0, TB10X_PORT7 + 1,
+ TB10X_PORT7 + 2, TB10X_PORT7 + 3};
+static const unsigned uart1_pins[] = { TB10X_PORT7 + 4, TB10X_PORT7 + 5,
+ TB10X_PORT7 + 6, TB10X_PORT7 + 7};
+static const unsigned gpiol_pins[] = { TB10X_PORT7 + 0, TB10X_PORT7 + 1,
+ TB10X_PORT7 + 2, TB10X_PORT7 + 3};
+static const unsigned gpiom_pins[] = { TB10X_PORT7 + 4, TB10X_PORT7 + 5,
+ TB10X_PORT7 + 6, TB10X_PORT7 + 7};
+
+/* Port 8 */
+static const unsigned spi3_pins[] = { TB10X_PORT8 + 0, TB10X_PORT8 + 1,
+ TB10X_PORT8 + 2, TB10X_PORT8 + 3};
+static const unsigned jtag_pins[] = { TB10X_PORT8 + 0, TB10X_PORT8 + 1,
+ TB10X_PORT8 + 2, TB10X_PORT8 + 3};
+
+/* Port 9 */
+static const unsigned spi1_pins[] = { TB10X_PORT9 + 0, TB10X_PORT9 + 1,
+ TB10X_PORT9 + 2, TB10X_PORT9 + 3,
+ TB10X_PORT9 + 4};
+static const unsigned gpion_pins[] = { TB10X_PORT9 + 0, TB10X_PORT9 + 1,
+ TB10X_PORT9 + 2, TB10X_PORT9 + 3,
+ TB10X_PORT9 + 4};
+
+/* Port 5 */
+static const unsigned gpioj_pins[] = { TB10X_PORT5 + 0, TB10X_PORT5 + 1,
+ TB10X_PORT5 + 2, TB10X_PORT5 + 3,
+ TB10X_PORT5 + 4, TB10X_PORT5 + 5,
+ TB10X_PORT5 + 6, TB10X_PORT5 + 7,
+ TB10X_PORT5 + 8, TB10X_PORT5 + 9,
+ TB10X_PORT5 + 10, TB10X_PORT5 + 11,
+ TB10X_PORT5 + 12, TB10X_PORT5 + 13,
+ TB10X_PORT5 + 14, TB10X_PORT5 + 15,
+ TB10X_PORT5 + 16, TB10X_PORT5 + 17,
+ TB10X_PORT5 + 18, TB10X_PORT5 + 19,
+ TB10X_PORT5 + 20, TB10X_PORT5 + 21,
+ TB10X_PORT5 + 22, TB10X_PORT5 + 23,
+ TB10X_PORT5 + 24, TB10X_PORT5 + 25,
+ TB10X_PORT5 + 26, TB10X_PORT5 + 27,
+ TB10X_PORT5 + 28, TB10X_PORT5 + 29,
+ TB10X_PORT5 + 30, TB10X_PORT5 + 31};
+static const unsigned gpiok_pins[] = { TB10X_PORT5 + 32, TB10X_PORT5 + 33,
+ TB10X_PORT5 + 34, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40, TB10X_PORT5 + 41,
+ TB10X_PORT5 + 42, TB10X_PORT5 + 43,
+ TB10X_PORT5 + 44, TB10X_PORT5 + 45,
+ TB10X_PORT5 + 46, TB10X_PORT5 + 47,
+ TB10X_PORT5 + 48, TB10X_PORT5 + 49,
+ TB10X_PORT5 + 50, TB10X_PORT5 + 51,
+ TB10X_PORT5 + 52, TB10X_PORT5 + 53};
+static const unsigned ciplus_pins[] = { TB10X_PORT5 + 0, TB10X_PORT5 + 1,
+ TB10X_PORT5 + 2, TB10X_PORT5 + 3,
+ TB10X_PORT5 + 4, TB10X_PORT5 + 5,
+ TB10X_PORT5 + 6, TB10X_PORT5 + 7,
+ TB10X_PORT5 + 8, TB10X_PORT5 + 9,
+ TB10X_PORT5 + 10, TB10X_PORT5 + 11,
+ TB10X_PORT5 + 12, TB10X_PORT5 + 13,
+ TB10X_PORT5 + 14, TB10X_PORT5 + 15,
+ TB10X_PORT5 + 16, TB10X_PORT5 + 17,
+ TB10X_PORT5 + 18, TB10X_PORT5 + 19,
+ TB10X_PORT5 + 20, TB10X_PORT5 + 21,
+ TB10X_PORT5 + 22, TB10X_PORT5 + 23,
+ TB10X_PORT5 + 24, TB10X_PORT5 + 25,
+ TB10X_PORT5 + 26, TB10X_PORT5 + 27,
+ TB10X_PORT5 + 28, TB10X_PORT5 + 29,
+ TB10X_PORT5 + 30, TB10X_PORT5 + 31,
+ TB10X_PORT5 + 32, TB10X_PORT5 + 33,
+ TB10X_PORT5 + 34, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40, TB10X_PORT5 + 41,
+ TB10X_PORT5 + 42, TB10X_PORT5 + 43,
+ TB10X_PORT5 + 44, TB10X_PORT5 + 45,
+ TB10X_PORT5 + 46, TB10X_PORT5 + 47,
+ TB10X_PORT5 + 48, TB10X_PORT5 + 49,
+ TB10X_PORT5 + 50, TB10X_PORT5 + 51,
+ TB10X_PORT5 + 52, TB10X_PORT5 + 53};
+static const unsigned mcard_pins[] = { TB10X_PORT5 + 3, TB10X_PORT5 + 10,
+ TB10X_PORT5 + 11, TB10X_PORT5 + 12,
+ TB10X_PORT5 + 22, TB10X_PORT5 + 23,
+ TB10X_PORT5 + 33, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40, TB10X_PORT5 + 41,
+ TB10X_PORT5 + 42, TB10X_PORT5 + 43,
+ TB10X_PORT5 + 45, TB10X_PORT5 + 46,
+ TB10X_PORT5 + 47, TB10X_PORT5 + 48,
+ TB10X_PORT5 + 49, TB10X_PORT5 + 50,
+ TB10X_PORT5 + 51, TB10X_PORT5 + 52,
+ TB10X_PORT5 + 53};
+static const unsigned stc0_pins[] = { TB10X_PORT5 + 34, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40};
+static const unsigned stc1_pins[] = { TB10X_PORT5 + 25, TB10X_PORT5 + 26,
+ TB10X_PORT5 + 27, TB10X_PORT5 + 28,
+ TB10X_PORT5 + 29, TB10X_PORT5 + 30,
+ TB10X_PORT5 + 44};
+
+/* Unmuxed GPIOs */
+static const unsigned gpiob_pins[] = { TB10X_GPIOS + 0, TB10X_GPIOS + 1};
+static const unsigned gpiod_pins[] = { TB10X_GPIOS + 2, TB10X_GPIOS + 3};
+static const unsigned gpiof_pins[] = { TB10X_GPIOS + 4, TB10X_GPIOS + 5};
+static const unsigned gpioh_pins[] = { TB10X_GPIOS + 6, TB10X_GPIOS + 7};
+static const unsigned gpioi_pins[] = { TB10X_GPIOS + 8, TB10X_GPIOS + 9,
+ TB10X_GPIOS + 10, TB10X_GPIOS + 11,
+ TB10X_GPIOS + 12, TB10X_GPIOS + 13,
+ TB10X_GPIOS + 14, TB10X_GPIOS + 15,
+ TB10X_GPIOS + 16, TB10X_GPIOS + 17,
+ TB10X_GPIOS + 18, TB10X_GPIOS + 19};
+
+struct tb10x_pinfuncgrp {
+ const char *name;
+ const unsigned int *pins;
+ const unsigned int pincnt;
+ const int port;
+ const unsigned int mode;
+ const int isgpio;
+};
+#define DEFPINFUNCGRP(NAME, PORT, MODE, ISGPIO) { \
+ .name = __stringify(NAME), \
+ .pins = NAME, .pincnt = ARRAY_SIZE(NAME), \
+ .port = (PORT), .mode = (MODE), \
+ .isgpio = (ISGPIO), \
+ }
+static const struct tb10x_pinfuncgrp tb10x_pingroups[] = {
+ DEFPINFUNCGRP(mis0_pins, 0, 0, 0),
+ DEFPINFUNCGRP(gpioa_pins, 0, 0, 1),
+ DEFPINFUNCGRP(mis1_pins, 0, 0, 0),
+ DEFPINFUNCGRP(mip1_pins, 0, 1, 0),
+ DEFPINFUNCGRP(mis2_pins, 1, 0, 0),
+ DEFPINFUNCGRP(gpioc_pins, 1, 0, 1),
+ DEFPINFUNCGRP(mis3_pins, 1, 0, 0),
+ DEFPINFUNCGRP(mip3_pins, 1, 1, 0),
+ DEFPINFUNCGRP(mis4_pins, 2, 0, 0),
+ DEFPINFUNCGRP(gpioe_pins, 2, 0, 1),
+ DEFPINFUNCGRP(mis5_pins, 2, 0, 0),
+ DEFPINFUNCGRP(mip5_pins, 2, 1, 0),
+ DEFPINFUNCGRP(mis6_pins, 3, 0, 0),
+ DEFPINFUNCGRP(gpiog_pins, 3, 0, 1),
+ DEFPINFUNCGRP(mis7_pins, 3, 0, 0),
+ DEFPINFUNCGRP(mip7_pins, 3, 1, 0),
+ DEFPINFUNCGRP(gpioj_pins, 4, 0, 1),
+ DEFPINFUNCGRP(gpiok_pins, 4, 0, 1),
+ DEFPINFUNCGRP(ciplus_pins, 4, 1, 0),
+ DEFPINFUNCGRP(mcard_pins, 4, 2, 0),
+ DEFPINFUNCGRP(stc0_pins, 4, 3, 0),
+ DEFPINFUNCGRP(stc1_pins, 4, 3, 0),
+ DEFPINFUNCGRP(mop_pins, 5, 0, 0),
+ DEFPINFUNCGRP(mos0_pins, 5, 1, 0),
+ DEFPINFUNCGRP(mos1_pins, 5, 1, 0),
+ DEFPINFUNCGRP(mos2_pins, 5, 1, 0),
+ DEFPINFUNCGRP(mos3_pins, 5, 1, 0),
+ DEFPINFUNCGRP(uart0_pins, 6, 0, 0),
+ DEFPINFUNCGRP(uart1_pins, 6, 0, 0),
+ DEFPINFUNCGRP(gpiol_pins, 6, 1, 1),
+ DEFPINFUNCGRP(gpiom_pins, 6, 1, 1),
+ DEFPINFUNCGRP(spi3_pins, 7, 0, 0),
+ DEFPINFUNCGRP(jtag_pins, 7, 1, 0),
+ DEFPINFUNCGRP(spi1_pins, 8, 0, 0),
+ DEFPINFUNCGRP(gpion_pins, 8, 1, 1),
+ DEFPINFUNCGRP(gpiob_pins, -1, 0, 1),
+ DEFPINFUNCGRP(gpiod_pins, -1, 0, 1),
+ DEFPINFUNCGRP(gpiof_pins, -1, 0, 1),
+ DEFPINFUNCGRP(gpioh_pins, -1, 0, 1),
+ DEFPINFUNCGRP(gpioi_pins, -1, 0, 1),
+};
+#undef DEFPINFUNCGRP
+
+struct tb10x_of_pinfunc {
+ const char *name;
+ const char *group;
+};
+
+#define TB10X_PORTS (9)
+
+/**
+ * struct tb10x_port - state of an I/O port
+ * @mode: Node this port is currently in.
+ * @count: Number of enabled functions which require this port to be
+ * configured in @mode.
+ */
+struct tb10x_port {
+ unsigned int mode;
+ unsigned int count;
+};
+
+/**
+ * struct tb10x_pinctrl - TB10x pin controller internal state
+ * @pctl: pointer to the pinctrl_dev structure of this pin controller.
+ * @base: register set base address.
+ * @pingroups: pointer to an array of the pin groups this driver manages.
+ * @pinfuncgrpcnt: number of pingroups in @pingroups.
+ * @pinfuncs: pointer to an array of pin functions this driver manages.
+ * @pinfuncnt: number of pin functions in @pinfuncs.
+ * @mutex: mutex for exclusive access to a pin controller's state.
+ * @ports: current state of each port.
+ * @gpios: Indicates if a given pin is currently used as GPIO (1) or not (0).
+ */
+struct tb10x_pinctrl {
+ struct pinctrl_dev *pctl;
+ void *base;
+ const struct tb10x_pinfuncgrp *pingroups;
+ unsigned int pinfuncgrpcnt;
+ struct tb10x_of_pinfunc *pinfuncs;
+ unsigned int pinfuncnt;
+ struct mutex mutex;
+ struct tb10x_port ports[TB10X_PORTS];
+ DECLARE_BITMAP(gpios, MAX_PIN + 1);
+};
+
+static inline void tb10x_pinctrl_set_config(struct tb10x_pinctrl *state,
+ unsigned int port, unsigned int mode)
+{
+ u32 pcfg;
+
+ if (state->ports[port].count)
+ return;
+
+ state->ports[port].mode = mode;
+
+ pcfg = ioread32(state->base) & ~(PCFG_PORT_MASK(port));
+ pcfg |= (mode << (PCFG_PORT_BITWIDTH * port)) & PCFG_PORT_MASK(port);
+ iowrite32(pcfg, state->base);
+}
+
+static inline unsigned int tb10x_pinctrl_get_config(
+ struct tb10x_pinctrl *state,
+ unsigned int port)
+{
+ return (ioread32(state->base) & PCFG_PORT_MASK(port))
+ >> (PCFG_PORT_BITWIDTH * port);
+}
+
+static int tb10x_get_groups_count(struct pinctrl_dev *pctl)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncgrpcnt;
+}
+
+static const char *tb10x_get_group_name(struct pinctrl_dev *pctl, unsigned n)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pingroups[n].name;
+}
+
+static int tb10x_get_group_pins(struct pinctrl_dev *pctl, unsigned n,
+ unsigned const **pins,
+ unsigned * const num_pins)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+
+ *pins = state->pingroups[n].pins;
+ *num_pins = state->pingroups[n].pincnt;
+
+ return 0;
+}
+
+static int tb10x_dt_node_to_map(struct pinctrl_dev *pctl,
+ struct device_node *np_config,
+ struct pinctrl_map **map, unsigned *num_maps)
+{
+ struct pinctrl_map *m;
+ const char *string;
+
+ if (of_property_read_string(np_config, "pingrp", &string)) {
+ pr_err("%s: No pingrp property in device tree.\n",
+ np_config->full_name);
+ return -EINVAL;
+ }
+
+ m = kzalloc(sizeof(struct pinctrl_map), GFP_KERNEL);
+ if (!m)
+ return -ENOMEM;
+
+ m->type = PIN_MAP_TYPE_MUX_GROUP;
+ m->data.mux.group = string;
+ m->data.mux.function = np_config->name;
+ *map = m;
+ *num_maps = 1;
+
+ return 0;
+}
+
+static void tb10x_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ kfree(map);
+}
+
+static struct pinctrl_ops tb10x_pinctrl_ops = {
+ .get_groups_count = tb10x_get_groups_count,
+ .get_group_name = tb10x_get_group_name,
+ .get_group_pins = tb10x_get_group_pins,
+ .dt_node_to_map = tb10x_dt_node_to_map,
+ .dt_free_map = tb10x_dt_free_map,
+};
+
+static int tb10x_get_functions_count(struct pinctrl_dev *pctl)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncnt;
+}
+
+static const char *tb10x_get_function_name(struct pinctrl_dev *pctl,
+ unsigned n)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncs[n].name;
+}
+
+static int tb10x_get_function_groups(struct pinctrl_dev *pctl,
+ unsigned n, const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+
+ *groups = &state->pinfuncs[n].group;
+ *num_groups = 1;
+
+ return 0;
+}
+
+static int tb10x_gpio_request_enable(struct pinctrl_dev *pctl,
+ struct pinctrl_gpio_range *range,
+ unsigned pin)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ int muxport = -1;
+ int muxmode = -1;
+ int i;
+
+ mutex_lock(&state->mutex);
+
+ /* Figure out to which port the requested GPIO belongs and how to
+ * configure that port.
+ * This loop also checks for pin conflicts between GPIOs and other
+ * functions.
+ */
+ for (i = 0; i < state->pinfuncgrpcnt; i++) {
+ const struct tb10x_pinfuncgrp *pfg = &state->pingroups[i];
+ unsigned int port = pfg->port;
+ unsigned int mode = pfg->mode;
+ int j;
+
+ /* Skip pin groups which are always mapped and don't need
+ * to be configured.
+ */
+ if (port < 0)
+ continue;
+
+ for (j = 0; j < pfg->pincnt; j++) {
+ if (pin == pfg->pins[j]) {
+ if (pfg->isgpio) {
+ /* Remember the GPIO-only setting of
+ * the port this pin belongs to.
+ */
+ muxport = port;
+ muxmode = mode;
+ } else if (state->ports[port].count
+ && (state->ports[port].mode == mode)) {
+ /* Error: The requested pin is already
+ * used for something else.
+ */
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+ break;
+ }
+ }
+ }
+
+ /* If we haven't returned an error at this point, the GPIO pin is not
+ * used by another function and the GPIO request can be granted:
+ * Register pin as being used as GPIO so we don't allocate it to
+ * another function later.
+ */
+ set_bit(pin, state->gpios);
+
+ /* Potential conflicts between GPIOs and pin functions were caught
+ * earlier in this function and tb10x_pinctrl_set_config will do the
+ * Right Thing, either configure the port in GPIO only mode or leave
+ * another mode compatible with this GPIO request untouched.
+ */
+ if (muxport >= 0)
+ tb10x_pinctrl_set_config(state, muxport, muxmode);
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static void tb10x_gpio_disable_free(struct pinctrl_dev *pctl,
+ struct pinctrl_gpio_range *range,
+ unsigned pin)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+
+ mutex_lock(&state->mutex);
+
+ clear_bit(pin, state->gpios);
+
+ mutex_unlock(&state->mutex);
+}
+
+static int tb10x_pctl_enable(struct pinctrl_dev *pctl,
+ unsigned func_selector, unsigned group_selector)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector];
+ int i;
+
+ if (grp->port < 0)
+ return 0;
+
+ mutex_lock(&state->mutex);
+
+ /* Check if the requested function is compatible with previously
+ * requested functions.
+ */
+ if (state->ports[grp->port].count
+ && (state->ports[grp->port].mode != grp->mode)) {
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+
+ /* Check if the requested function is compatible with previously
+ * requested GPIOs.
+ */
+ for (i = 0; i < grp->pincnt; i++)
+ if (test_bit(grp->pins[i], state->gpios)) {
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+
+ tb10x_pinctrl_set_config(state, grp->port, grp->mode);
+
+ state->ports[grp->port].count++;
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static void tb10x_pctl_disable(struct pinctrl_dev *pctl,
+ unsigned func_selector, unsigned group_selector)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector];
+
+ if (grp->port < 0)
+ return;
+
+ mutex_lock(&state->mutex);
+
+ state->ports[grp->port].count--;
+
+ mutex_unlock(&state->mutex);
+}
+
+static struct pinmux_ops tb10x_pinmux_ops = {
+ .get_functions_count = tb10x_get_functions_count,
+ .get_function_name = tb10x_get_function_name,
+ .get_function_groups = tb10x_get_function_groups,
+ .gpio_request_enable = tb10x_gpio_request_enable,
+ .gpio_disable_free = tb10x_gpio_disable_free,
+ .enable = tb10x_pctl_enable,
+ .disable = tb10x_pctl_disable,
+};
+
+static struct pinctrl_desc tb10x_pindesc = {
+ .name = "TB10x",
+ .pins = tb10x_pins,
+ .npins = ARRAY_SIZE(tb10x_pins),
+ .owner = THIS_MODULE,
+ .pctlops = &tb10x_pinctrl_ops,
+ .pmxops = &tb10x_pinmux_ops,
+};
+
+static int tb10x_pinctrl_probe(struct platform_device *pdev)
+{
+ int ret = -EINVAL;
+ struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct device *dev = &pdev->dev;
+ struct device_node *of_node = dev->of_node;
+ struct device_node *child;
+ struct tb10x_pinctrl *state;
+ int i;
+
+ if (!of_node) {
+ dev_err(dev, "No device tree node found.\n");
+ return -EINVAL;
+ }
+
+ if (!mem) {
+ dev_err(dev, "No memory resource defined.\n");
+ return -EINVAL;
+ }
+
+ state = devm_kzalloc(dev, sizeof(struct tb10x_pinctrl) +
+ of_get_child_count(of_node)
+ * sizeof(struct tb10x_of_pinfunc),
+ GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, state);
+ state->pinfuncs = (struct tb10x_of_pinfunc *)(state + 1);
+ mutex_init(&state->mutex);
+
+ state->base = devm_ioremap_resource(dev, mem);
+ if (!state->base) {
+ dev_err(dev, "Request register region failed.\n");
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ state->pingroups = tb10x_pingroups;
+ state->pinfuncgrpcnt = ARRAY_SIZE(tb10x_pingroups);
+
+ for (i = 0; i < TB10X_PORTS; i++)
+ state->ports[i].mode = tb10x_pinctrl_get_config(state, i);
+
+ for_each_child_of_node(of_node, child) {
+ const char *name;
+
+ if (!of_property_read_string(child, "pingrp", &name)) {
+ state->pinfuncs[state->pinfuncnt].name = child->name;
+ state->pinfuncs[state->pinfuncnt].group = name;
+ state->pinfuncnt++;
+ }
+ }
+
+ state->pctl = pinctrl_register(&tb10x_pindesc, dev, state);
+ if (IS_ERR(state->pctl)) {
+ dev_err(dev, "could not register TB10x pin driver\n");
+ ret = PTR_ERR(state->pctl);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ mutex_destroy(&state->mutex);
+ return ret;
+}
+
+static int tb10x_pinctrl_remove(struct platform_device *pdev)
+{
+ struct tb10x_pinctrl *state = platform_get_drvdata(pdev);
+
+ pinctrl_unregister(state->pctl);
+ mutex_destroy(&state->mutex);
+
+ return 0;
+}
+
+
+static const struct of_device_id tb10x_pinctrl_dt_ids[] = {
+ { .compatible = "abilis,tb10x-iomux" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tb10x_pinctrl_dt_ids);
+
+static struct platform_driver tb10x_pinctrl_pdrv = {
+ .probe = tb10x_pinctrl_probe,
+ .remove = tb10x_pinctrl_remove,
+ .driver = {
+ .name = "tb10x_pinctrl",
+ .of_match_table = of_match_ptr(tb10x_pinctrl_dt_ids),
+ .owner = THIS_MODULE
+ }
+};
+
+static int __init tb10x_iopinctrl_init(void)
+{
+ return platform_driver_register(&tb10x_pinctrl_pdrv);
+}
+
+static void __exit tb10x_iopinctrl_exit(void)
+{
+ platform_driver_unregister(&tb10x_pinctrl_pdrv);
+}
+
+MODULE_AUTHOR("Christian Ruppert <[email protected]>");
+MODULE_LICENSE("GPL");
+module_init(tb10x_iopinctrl_init);
+module_exit(tb10x_iopinctrl_exit);
--
1.7.1

2013-06-18 09:30:11

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 1/4] Make non-linear GPIO ranges accesible from gpiolib

This patch adds the infrastructure required to register non-linear gpio
ranges through gpiolib and the standard GPIO device tree bindings.

Signed-off-by: Christian Ruppert <[email protected]>
---
Documentation/devicetree/bindings/gpio/gpio.txt | 44 +++++++++++++++++++++
drivers/gpio/gpiolib-of.c | 31 +++++++++++----
drivers/gpio/gpiolib.c | 47 +++++++++++++++++++++++
drivers/pinctrl/core.c | 23 +++++++++++
include/asm-generic/gpio.h | 10 +++++
include/linux/gpio.h | 9 ++++
include/linux/pinctrl/pinctrl.h | 3 +
7 files changed, 159 insertions(+), 8 deletions(-)

diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
index d933af3..c8afbea 100644
--- a/Documentation/devicetree/bindings/gpio/gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio.txt
@@ -112,3 +112,47 @@ where,

The pinctrl node must have "#gpio-range-cells" property to show number of
arguments to pass with phandle from gpio controllers node.
+
+In addition, named groups of pins can be mapped to pin groups of a given
+pin controller:
+
+ gpio_pio_g: gpio-controller@1480 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1480 0x18>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl1 0 0 0>, <&pinctrl2 3 0 0>;
+ gpio-ranges-group-names = "foo", "bar";
+ };
+
+where,
+ &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
+
+ The following value specifies the base GPIO offset of the pin range with
+ respect to the GPIO controller's base. The remaining two values must be
+ 0 to indicate that a named pin group should be used for the respective
+ range. The number of pins in the range is the number of pins in the pin
+ group.
+
+ gpio-ranges-group-names defines the name of each pingroup of the
+ respective pin controller.
+
+The pinctrl node must have a "#gpio-#gpio-range-cells" property set to three
+to define the number of arguments to pass with the phandle.
+
+Both methods can be combined in the same GPIO controller, e.g.
+
+ gpio_pio_i: gpio-controller@14B0 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1480 0x18>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl1 0 20 10>,
+ <&pinctrl2 10 0 0>,
+ <&pinctrl1 15 0 10>,
+ <&pinctrl2 25 0 0>;
+ gpio-ranges-group-names = "",
+ "foo",
+ "",
+ "bar";
+ };
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 665f953..e29cc71 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -189,6 +189,7 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
struct of_phandle_args pinspec;
struct pinctrl_dev *pctldev;
int index = 0, ret;
+ const char *name;

if (!np)
return;
@@ -203,14 +204,28 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
if (!pctldev)
break;

- ret = gpiochip_add_pin_range(chip,
- pinctrl_dev_get_devname(pctldev),
- pinspec.args[0],
- pinspec.args[1],
- pinspec.args[2]);
-
- if (ret)
- break;
+ if (pinspec.args[2]) {
+ /* npins != 0: linear range */
+ ret = gpiochip_add_pin_range(chip,
+ pinctrl_dev_get_devname(pctldev),
+ pinspec.args[0],
+ pinspec.args[1],
+ pinspec.args[2]);
+ if (ret)
+ break;
+ } else {
+ /* npins == 0: pin group based range */
+ ret = of_property_read_string_index(np,
+ "gpio-ranges-group-names",
+ index, &name);
+ if (ret)
+ break;
+
+ ret = gpiochip_add_pingroup_range(chip, pctldev,
+ pinspec.args[0], name);
+ if (ret)
+ break;
+ }
}
}

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c2534d6..b2dc810 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1318,6 +1318,53 @@ EXPORT_SYMBOL_GPL(gpiochip_find);
#ifdef CONFIG_PINCTRL

/**
+ * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping
+ * @chip: the gpiochip to add the range for
+ * @pinctrl: the dev_name() of the pin controller to map to
+ * @gpio_offset: the start offset in the current gpio_chip number space
+ * @pin_group: name of the pin group inside the pin controller
+ */
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ struct gpio_pin_range *pin_range;
+ int ret;
+
+ pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
+ if (!pin_range) {
+ pr_err("%s: GPIO chip: failed to allocate pin ranges\n",
+ chip->label);
+ return -ENOMEM;
+ }
+
+ /* Use local offset as range ID */
+ pin_range->range.id = gpio_offset;
+ pin_range->range.gc = chip;
+ pin_range->range.name = chip->label;
+ pin_range->range.base = chip->base + gpio_offset;
+ pin_range->pctldev = pctldev;
+
+ ret = pinctrl_add_gpio_pingrp_range(pctldev, &pin_range->range,
+ pin_group);
+ if (ret < 0) {
+ pr_err("%s: GPIO chip: could not create pin range %s\n",
+ chip->label, pin_group);
+ return ret;
+ }
+
+ pr_debug("GPIO chip %s: created GPIO range %d->%d ==> %s PINGRP %s\n",
+ chip->label, gpio_offset,
+ gpio_offset + pin_range->range.npins - 1,
+ pinctrl_dev_get_devname(pctldev), pin_group);
+
+ list_add_tail(&pin_range->node, &chip->pin_ranges);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
+
+/**
* gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping
* @chip: the gpiochip to add the range for
* @pinctrl_name: the dev_name() of the pin controller to map to
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index 25bb17e..3730c4f 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -452,6 +452,29 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
}
EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range);

+int pinctrl_add_gpio_pingrp_range(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ char *pin_group)
+{
+ const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ int group_selector, ret;
+
+ group_selector = pinctrl_get_group_selector(pctldev, pin_group);
+ if (group_selector < 0)
+ return group_selector;
+
+ ret = pctlops->get_group_pins(pctldev, group_selector,
+ &range->pins,
+ &range->npins);
+ if (ret < 0)
+ return ret;
+
+ pinctrl_add_gpio_range(pctldev, range);
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(pinctrl_add_gpio_pingrp_range);
+
/**
* pinctrl_find_gpio_range_from_pin() - locate the GPIO range for a pin
* @pctldev: the pin controller device to look in
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index bde6469..523f405 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -228,6 +228,9 @@ struct gpio_pin_range {
int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
unsigned int gpio_offset, unsigned int pin_offset,
unsigned int npins);
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group);
void gpiochip_remove_pin_ranges(struct gpio_chip *chip);

#else
@@ -239,6 +242,13 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
{
return 0;
}
+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ return 0;
+}

static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 552e3f4..234b32f 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -220,6 +220,15 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
return -EINVAL;
}

+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ WARN_ON(1);
+ return -EINVAL;
+}
+
static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
{
diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 5979147..47ab2fb 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -141,6 +141,9 @@ extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev,

extern struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
struct pinctrl_gpio_range *range);
+extern int pinctrl_add_gpio_pingrp_range(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ char *pin_group);
extern struct pinctrl_gpio_range *
pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
unsigned int pin);
--
1.7.1

2013-06-19 12:03:06

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/4] Make non-linear GPIO ranges accesible from gpiolib

On Tue, Jun 18, 2013 at 11:29 AM, Christian Ruppert
<[email protected]> wrote:

> This patch adds the infrastructure required to register non-linear gpio
> ranges through gpiolib and the standard GPIO device tree bindings.
>
> Signed-off-by: Christian Ruppert <[email protected]>

I'm basically fine with this, but would like Stephen's ACK if possible.

> +In addition, named groups of pins can be mapped to pin groups of a given
> +pin controller:
> +
> + gpio_pio_g: gpio-controller@1480 {
> + #gpio-cells = <2>;
> + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
> + reg = <0x1480 0x18>;
> + gpio-controller;
> + gpio-ranges = <&pinctrl1 0 0 0>, <&pinctrl2 3 0 0>;
> + gpio-ranges-group-names = "foo", "bar";
> + };
> +
> +where,
> + &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
> +
> + The following value specifies the base GPIO offset of the pin range with
> + respect to the GPIO controller's base. The remaining two values must be
> + 0 to indicate that a named pin group should be used for the respective
> + range. The number of pins in the range is the number of pins in the pin
> + group.

So while this works, these zeroes seem a bit awkward, but maybe
it's the only way?

I'm not good enough on device tree conventions, but isn't this possible:

gpio-ranges = <&pinctrl1 0>, <&pinctrl2 3>;
gpio-ranges-group-names = "foo", "bar";

Since we don't have any #gpio-ranges-cells or anything like that I
guess we can define this to have a flexible number of cells
depending on use case?

In the mixed case the blank groups does not look
good eitherm, wouldn't this be possible:


gpio-ranges = <&pinctrl1 0 20 10>,
<&pinctrl2 10>,
<&pinctrl1 15 0 10>,
<&pinctrl2 25>;
// Matches the two ranges without pins
gpio-ranges-group-names = "foo", "bar";

Or is this just making things complicated?

Yours,
Linus Walleij

2013-06-19 18:15:19

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 1/4] Make non-linear GPIO ranges accesible from gpiolib

On 06/19/2013 06:03 AM, Linus Walleij wrote:
> On Tue, Jun 18, 2013 at 11:29 AM, Christian Ruppert
> <[email protected]> wrote:
>
>> This patch adds the infrastructure required to register non-linear gpio
>> ranges through gpiolib and the standard GPIO device tree bindings.
>>
>> Signed-off-by: Christian Ruppert <[email protected]>
>
> I'm basically fine with this, but would like Stephen's ACK if possible.
>
>> +In addition, named groups of pins can be mapped to pin groups of a given
>> +pin controller:
>> +
>> + gpio_pio_g: gpio-controller@1480 {
>> + #gpio-cells = <2>;
>> + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
>> + reg = <0x1480 0x18>;
>> + gpio-controller;
>> + gpio-ranges = <&pinctrl1 0 0 0>, <&pinctrl2 3 0 0>;
>> + gpio-ranges-group-names = "foo", "bar";
>> + };
>> +
>> +where,
>> + &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
>> +
>> + The following value specifies the base GPIO offset of the pin range with
>> + respect to the GPIO controller's base. The remaining two values must be
>> + 0 to indicate that a named pin group should be used for the respective
>> + range. The number of pins in the range is the number of pins in the pin
>> + group.
>
> So while this works, these zeroes seem a bit awkward, but maybe
> it's the only way?
>
> I'm not good enough on device tree conventions, but isn't this possible:
>
> gpio-ranges = <&pinctrl1 0>, <&pinctrl2 3>;
> gpio-ranges-group-names = "foo", "bar";
>
> Since we don't have any #gpio-ranges-cells or anything like that I
> guess we can define this to have a flexible number of cells
> depending on use case?

If we're willing to have gpio-ranges be either *all* group names, or
*all* IDs, we can define the format of gpio-ranges to have two cells
(phandle and GPIO number) if the property gpio-ranges-group-names
exists, but four cells (phandle, GPIO number, pin number, count)
otherwise. However, that's a little restrictive, since then what if one
GPIO controller is hooked to two different pinmux controllers, and you
want to use different formats for the references to each. A
#gpio-ranges-cells in the target of the phandle would allow this, but I
don't think this is something the pinctrl node should dictate to those
who reference it; it's quite legitimate for a GPIO node to use the pure
numeric mapping even if the pin controller happens to expose some pin
groups that allow you to do the mapping by name.

2013-06-19 18:20:17

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On 06/14/2013 03:12 AM, Christian Ruppert wrote:
> On Thu, Jun 13, 2013 at 03:38:09PM -0600, Stephen Warren wrote:
>> On 06/13/2013 06:55 AM, Christian Ruppert wrote:
>>> This patch adds the infrastructure required to register non-linear gpio
>>> ranges through gpiolib and the standard GPIO device tree bindings.
>>
>> That's not exactly true. The existing gpio-ranges property already
>> allows non-linear ranges to be represented quite easily; each entry in
>> the gpio-ranges list is <gpio-base> <pinctrl-base> <count>, so you can
>> piece together any mapping you want.
>
> You're right, my description is somewhat imprecise here.
>
>> The potential advantage of this patch is that the pinctrl-side of the
>> mapping can be a group name rather than pin IDs, which might reduce the
>> size of the mapping list if you have an extremely sparse or non-linear
>> mapping /and/ parts of that mapping just happen to align with the pin
>> groups in the pin controller HW, since each entry in the gpio-ranges
>> property can be sparse/non-linear, rather than being a small linear
>> chunk of the mapping.
>
> Pin controller authors have the freedom to define pin groups just for
> the purpose of "predefining" the pinctrl side of GPIO ranges.

Hmm. I suppose that's true. I'm not sure how enthusiastic I am about
doing this though... The reason I'm unsure is because it starts using
pin groups from something other than groups of pins in HW that are all
affected by the same mux or config bits in a register, and starts using
pin groups for something else; GPIO<->pinmux pins mapping. Perhaps it's
OK though, considering the other abuses of pin groups that are already
present, such as using pin groups to represent default/common uses of
groups of pins that don't actually exist in HW.

2013-06-19 18:28:03

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On 06/19/2013 12:10 PM, Stephen Warren wrote:
> On 06/14/2013 03:12 AM, Christian Ruppert wrote:
>> On Thu, Jun 13, 2013 at 03:38:09PM -0600, Stephen Warren wrote:
>>> On 06/13/2013 06:55 AM, Christian Ruppert wrote:
>>>> This patch adds the infrastructure required to register non-linear gpio
>>>> ranges through gpiolib and the standard GPIO device tree bindings.
>>>
>>> That's not exactly true. The existing gpio-ranges property already
>>> allows non-linear ranges to be represented quite easily; each entry in
>>> the gpio-ranges list is <gpio-base> <pinctrl-base> <count>, so you can
>>> piece together any mapping you want.
>>
>> You're right, my description is somewhat imprecise here.
>>
>>> The potential advantage of this patch is that the pinctrl-side of the
>>> mapping can be a group name rather than pin IDs, which might reduce the
>>> size of the mapping list if you have an extremely sparse or non-linear
>>> mapping /and/ parts of that mapping just happen to align with the pin
>>> groups in the pin controller HW, since each entry in the gpio-ranges
>>> property can be sparse/non-linear, rather than being a small linear
>>> chunk of the mapping.
>>
>> Pin controller authors have the freedom to define pin groups just for
>> the purpose of "predefining" the pinctrl side of GPIO ranges.
>
> Hmm. I suppose that's true. I'm not sure how enthusiastic I am about
> doing this though... The reason I'm unsure is because it starts using
> pin groups from something other than groups of pins in HW that are all
> affected by the same mux or config bits in a register, and starts using
> pin groups for something else; GPIO<->pinmux pins mapping. Perhaps it's
> OK though, considering the other abuses of pin groups that are already
> present, such as using pin groups to represent default/common uses of
> groups of pins that don't actually exist in HW.

I've realized what I don't like about this.

Pin groups are supposed to be something that represents some property of
the pinctrl HW itself. So, if you have register "X" bits 3-0 that define
the mux function for pins 8, 9, 10, and 11, then there really is a pin
group that exists in HW, and that pin group will still exist with that
same definition no matter what SoC you put the pinctrl HW into. If this
changes, it's not the same pinctrl HW module.

However, the connectivity between GPIO HW module "pins" (i.e. the GPIOs)
and pinctrl HW module "pins" (inputs to mux functions) is something that
only exists at the top-level of the SoC; outside the GPIO HW module
itself, and outside the pinctrl HW module itself.

In other words, you could have the exact same GPIO and pinctrl HW
modules instantiated into two different SoCs, but with completely
different mapping of GPIO IDs to pinctrl pin IDs.

As such, it isn't even generally possible for the pinctrl HW module to
define pin groups that describe the mapping, because the mapping is not
a property of the pinctrl HW module, and hence should not be defined,
even partially, by the pinctrl HW module's driver. In a similar fashion,
the DT binding for the pinctrl HW module should describe only the HW
module itself, and not the mapping/interaction with the outside world.
In other words, the DT binding for the pinctrl HW module also can't
define the names of any pin groups used in the GPIO<->pinctrl mapping,
for the same reasons.

As such, I'm not sure that I conceptually agree with this patch series.

Sure, it may make the gpio-ranges property more compact in some
(unusual?) non-linear cases. However, it's representing things
semantically incorrectly.

So, I'd like to question the motivation for using names here again.
Presumably the SoC vendor will write the gpio-ranges property for each
SoC, and put that into the SoC's .dtsi file. As such, no customer is
ever going to have to care about the property or its contents. So, I
don't really see how this helps you with your issue re: wanting to hide
details of multiple different ball-out options on similar SoCs, since
even with a manually-written purely numeric gpio-ranges property, all
that information is already essentially hidden; it's something that will
be written once, and then never looked at.

2013-06-19 22:27:49

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 1/4] Make non-linear GPIO ranges accesible from gpiolib

On 06/18/2013 03:29 AM, Christian Ruppert wrote:
> This patch adds the infrastructure required to register non-linear gpio
> ranges through gpiolib and the standard GPIO device tree bindings.

I review this in case we decide to go with it anyway.

> diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt

> +In addition, named groups of pins can be mapped to pin groups of a given
> +pin controller:
> +
> + gpio_pio_g: gpio-controller@1480 {
> + #gpio-cells = <2>;
> + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
> + reg = <0x1480 0x18>;
> + gpio-controller;
> + gpio-ranges = <&pinctrl1 0 0 0>, <&pinctrl2 3 0 0>;
> + gpio-ranges-group-names = "foo", "bar";
> + };
> +
> +where,
> + &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
> +
> + The following value specifies the base GPIO offset of the pin range with
> + respect to the GPIO controller's base. The remaining two values must be
> + 0 to indicate that a named pin group should be used for the respective
> + range. The number of pins in the range is the number of pins in the pin
> + group.

It'd be good to re-write this section in a similar style to the cleanup
patches that I sent for the existing gpio-ranges documentation. That
makes the format description more of a raw syntax than English text.

> + gpio-ranges-group-names defines the name of each pingroup of the
> + respective pin controller.
> +
> +The pinctrl node must have a "#gpio-#gpio-range-cells" property set to three
> +to define the number of arguments to pass with the phandle.

There's some mistake in the property name there. I'd assert we should
remove those two lines anyway, and use the new OF parsing code I posted
when cleaning up gpio-ranges.

> diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c

> + if (pinspec.args[2]) {
> + /* npins != 0: linear range */
> + ret = gpiochip_add_pin_range(chip,
> + pinctrl_dev_get_devname(pctldev),
> + pinspec.args[0],
> + pinspec.args[1],
> + pinspec.args[2]);
> + if (ret)
> + break;
> + } else {

I think here we should validate !pinspec.args[1], to ensure that value
doesn't get set to anything wonky.

2013-06-19 22:35:20

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On 06/18/2013 03:29 AM, Christian Ruppert wrote:
> The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
> Used to control the pinmux and is a prerequisite for the GPIO driver.

> diff --git a/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt

> +Port definitions
> +----------------
> +
> +Ports are defined (and referenced) by sub-nodes of the pin controller. Every
> +sub-node defines exactly one port (i.e. a set of pins). Ports are predefined
> +as named pin groups inside the pin controller driver and these names are used
> +to associate pin group predefinitions to pin controller sub-nodes.
> +
> +Required port definition subnode properties:
> + - pingrp: should be set to the name of the port's pin group.

This seems odd.... More on that where I comment on the example.

> +The following pin groups are available:
> + - GPIO ports: gpioa_pins, gpiob_pins, gpioc_pins, gpiod_pins, gpioe_pins,
> + gpiof_pins, gpiog_pins, gpioh_pins, gpioi_pins, gpioj_pins,
> + gpiok_pins, gpiol_pins, gpiom_pins, gpion_pins
...
> + - JTAG: jtag_pins

I'd suggest removing "_pins" from all those names, since it's the same
in all names and hence isn't necessary.

> +GPIO ranges definition
> +----------------------
> +
> +The named pin groups of GPIO ports can be used to define GPIO ranges as
> +explained in Documentation/devicetree/bindings/gpio/gpio.txt.

I wouldn't mention that here; the GPIO node contains the gpio-ranges
property, not the pin controller node. Hence, the binding for the GPIO
DT node should describe the property, not the binding for this node.

> +Example
> +-------
> +
> +iomux: iomux@FF10601c {
> + compatible = "abilis,tb10x-iomux";
> + reg = <0xFF10601c 0x4>;
> + pctl_gpio_a: pctl-gpio-a {
> + pingrp = "gpioa_pins";
> + };
> + pctl_uart0: pctl-uart0 {
> + pingrp = "uart0_pins";
> + };
> +};

The two nodes pctl-gpio-a and pctl-uart0 seem to be missing data. The
idea here is that you define nodes that says:

* This node applies to these pin(s)/group(s).
* Select mux function X on those pins/groups and/or apply these pin
configuration options to those pins/groups.

The examples above don't include any mux/config options, nor does the
binding say how to do specify them.

The set of pin groups defined by this binding should correspond directly
to the set of pin groups that actually exist in HW. So, if you have 3
pin groups (A, B, C) in HW each of which has two mux functions (X, Y),
your DT binding should define just 3 pin groups (A, B, C), not 6 (A_X,
A_Y, B_X, B_Y, C_X, C_Y). In other words, the pin group name shouldn't
imply the mux function.

2013-06-19 22:37:23

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 3/4] GPIO: Add TB10x GPIO driver

On 06/18/2013 03:29 AM, Christian Ruppert wrote:
> The GPIO driver for the Abilis Systems TB10x series of SOCs based on ARC700
> CPUs. It supports GPIO control and GPIO interrupt generation. This driver
> works in conjunction with the TB10x pinctrl driver.

Just one comment on the binding (I didn't read the code):

> diff --git a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt

> +Required Properties:

> +- ngpio: the number of GPIO pins this driver controls.

That's a custom property, so should include a vendor prefix: abilis,ngpios.

2013-06-20 11:57:47

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

Hello Stephen,

On Wed, Jun 19, 2013 at 12:27:56PM -0600, Stephen Warren wrote:
> On 06/19/2013 12:10 PM, Stephen Warren wrote:
> > On 06/14/2013 03:12 AM, Christian Ruppert wrote:
> >> On Thu, Jun 13, 2013 at 03:38:09PM -0600, Stephen Warren wrote:
> >>> On 06/13/2013 06:55 AM, Christian Ruppert wrote:
> [...]
> >>> The potential advantage of this patch is that the pinctrl-side of the
> >>> mapping can be a group name rather than pin IDs, which might reduce the
> >>> size of the mapping list if you have an extremely sparse or non-linear
> >>> mapping /and/ parts of that mapping just happen to align with the pin
> >>> groups in the pin controller HW, since each entry in the gpio-ranges
> >>> property can be sparse/non-linear, rather than being a small linear
> >>> chunk of the mapping.
> >>
> >> Pin controller authors have the freedom to define pin groups just for
> >> the purpose of "predefining" the pinctrl side of GPIO ranges.
> >
> > Hmm. I suppose that's true. I'm not sure how enthusiastic I am about
> > doing this though... The reason I'm unsure is because it starts using
> > pin groups from something other than groups of pins in HW that are all
> > affected by the same mux or config bits in a register, and starts using
> > pin groups for something else; GPIO<->pinmux pins mapping. Perhaps it's
> > OK though, considering the other abuses of pin groups that are already
> > present, such as using pin groups to represent default/common uses of
> > groups of pins that don't actually exist in HW.

The reason for these "abuses" might just be that every company (or maybe
even hardware team) has a different understanding of how pin muxing
should be implemented in hardware. There are probably as many different
pin muxing architectures out there as companies (maybe even more).
Finding a "one size fits all"-approach to this is extremely difficult
and driver authors adapt the kernel infrastructure as well as they can
to the hardware they have. As an example, see below for a fundamental
cultural difference between your hardware/integration team and ours.

> I've realized what I don't like about this.
>
> Pin groups are supposed to be something that represents some property of
> the pinctrl HW itself. So, if you have register "X" bits 3-0 that define
> the mux function for pins 8, 9, 10, and 11, then there really is a pin
> group that exists in HW, and that pin group will still exist with that
> same definition no matter what SoC you put the pinctrl HW into. If this
> changes, it's not the same pinctrl HW module.

Let me see if I get this right (Let's take the example in the section
"What is pinmuxing" of Documentation/pinctrl.txt in Linux-3.10-rc6 which
is similar to TB10x):
If I understand you correctly, you define two pin groups in this
example:
gpr1 = {A5, A6, A7, A8, B5};
grp2 = {A1, B1, C1, D1, E1, F1, G1, G2, G3, G4, H1};

grp1 has three configurations: allgpio, i2c and spi
grp2 has five configurations: allgpio, mmc2, mmc2_spi, mmc4, mmc4_spi, mmc8

Let's assume that unused pins are automatically configured as GPIOs in
each configuration. The pin controller thus requires a second list of
pins (for each of the modes), defining for which pins in each group it
can grant gpio_requests in a given mode. Furthermore, the mmc node will
have to "know" if mode "mmc2" or "mmc2_spi" must be selected for a given
setup, making the thing somewhat unorthogonal.

When writing our pinctrl driver, my understanding was slightly
different: I define seven pin groups:
spi1 = {A5, A6, A7, A8};
i2c = {A5, B5};
mmc2 = {A1, B1};
mmc4 = {A1, B1, C1, D1};
mmc8 = {A1, B1, C1, D1, E1, F1, G1, H1};
spi2 = {G1, G2, G3, G4};
gpios = {A1, A5, A6, A7, A8, B1, B5, C1, D1, E1, F1, G1, G2, G3, G4, H1};

Now each peripheral can individually request the pins it requires,
independently of the others. Conflicts (e.g. between spi1 and i2c or
between mmc4 and the GPIO at D1) are managed by the program logic in the
pin controller. The advantages are the following:
. The pin controller knows implicitly which pins are used for what and
can easily grant or refuse pin and GPIO requests.
. Conceptually, GPIO requests are now the same as any other
configuration request.
. The information of whether SPI2 is active or not is associated to
spi2 and spi2 only. mmc does not need to know.
. Implementation details of the pin controller hardware (which are the
ports, which configuration to apply to a port to obtain a certain
function) are confined in the pin controller driver.

> However, the connectivity between GPIO HW module "pins" (i.e. the GPIOs)
> and pinctrl HW module "pins" (inputs to mux functions) is something that
> only exists at the top-level of the SoC; outside the GPIO HW module
> itself, and outside the pinctrl HW module itself.

Well, in the case TB10x, GPIO pins are just the same as any other pins:
They go through the pin controller all the same and there is really
nothing that distinguishes them from, say, the mmc port in the example
above - which can also be mapped either partially or completely. There's
no bypassing of the pin controller going on at the top level or such.

And honestly: Have you ever seen a pin controller in which not only the
program logic but also the pin data base can be reused from one SOC to
another? At least I haven't. On the other hand, For all Abilis chips,
the program logic (the actual C functions) of the TB10x pinctrl could be
reused with very minor modifications. It would be easy to extend the
logic to a generic "Abilis pin controller" where all that needs to be
changed in function of the "compatible" string is the pointer to the pin
database.

> In other words, you could have the exact same GPIO and pinctrl HW
> modules instantiated into two different SoCs, but with completely
> different mapping of GPIO IDs to pinctrl pin IDs.

Well, you could very well conect a 4 bit mmc port instead of an spi in
the above example without changing one thing in the pin controller or
the mmc block...

> As such, it isn't even generally possible for the pinctrl HW module to
> define pin groups that describe the mapping, because the mapping is not
> a property of the pinctrl HW module, and hence should not be defined,
> even partially, by the pinctrl HW module's driver.

This is clearly implementation dependent. In the case of our chips, the
opposite is actually the case:

Your remark seems to reflect one of the following two hardware
architectures:

+- SPI
Physical pins --- GPIO --- pinctrl -+- I2C
+- mmc


+- GPIO
Physical pins -+ +- SPI
+- pinctrl -+- I2C
+- mmc
TB10x hardware architecture:

+- SPI
Physical pins --- pinctrl -+- I2C
+- mmc
+- GPIO

> In a similar fashion,
> the DT binding for the pinctrl HW module should describe only the HW
> module itself, and not the mapping/interaction with the outside world.
> In other words, the DT binding for the pinctrl HW module also can't
> define the names of any pin groups used in the GPIO<->pinctrl mapping,
> for the same reasons.

Except if this mapping is done _inside_ the pin controller, see above.

> As such, I'm not sure that I conceptually agree with this patch series.
>
> Sure, it may make the gpio-ranges property more compact in some
> (unusual?) non-linear cases. However, it's representing things
> semantically incorrectly.

Is it really in the TB10x case?
To me it looks actually semantically more correct.

> So, I'd like to question the motivation for using names here again.
> Presumably the SoC vendor will write the gpio-ranges property for each
> SoC, and put that into the SoC's .dtsi file. As such, no customer is
> ever going to have to care about the property or its contents. So, I
> don't really see how this helps you with your issue re: wanting to hide
> details of multiple different ball-out options on similar SoCs, since
> even with a manually-written purely numeric gpio-ranges property, all
> that information is already essentially hidden; it's something that will
> be written once, and then never looked at.

Well, in the case of TB10x we are the SOC vendor and it was decided that
all dts and dtsi files we publish are customer-facing data and must thus
be coherent with the data sheet.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-21 21:17:40

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On 06/20/2013 05:57 AM, Christian Ruppert wrote:
> Hello Stephen,
>
> On Wed, Jun 19, 2013 at 12:27:56PM -0600, Stephen Warren wrote:
>> On 06/19/2013 12:10 PM, Stephen Warren wrote:
>>> On 06/14/2013 03:12 AM, Christian Ruppert wrote:
>>>> On Thu, Jun 13, 2013 at 03:38:09PM -0600, Stephen Warren wrote:
>>>>> On 06/13/2013 06:55 AM, Christian Ruppert wrote:
>> [...]
>>>>> The potential advantage of this patch is that the pinctrl-side of the
>>>>> mapping can be a group name rather than pin IDs, which might reduce the
>>>>> size of the mapping list if you have an extremely sparse or non-linear
>>>>> mapping /and/ parts of that mapping just happen to align with the pin
>>>>> groups in the pin controller HW, since each entry in the gpio-ranges
>>>>> property can be sparse/non-linear, rather than being a small linear
>>>>> chunk of the mapping.
>>>>
>>>> Pin controller authors have the freedom to define pin groups just for
>>>> the purpose of "predefining" the pinctrl side of GPIO ranges.
>>>
>>> Hmm. I suppose that's true. I'm not sure how enthusiastic I am about
>>> doing this though... The reason I'm unsure is because it starts using
>>> pin groups from something other than groups of pins in HW that are all
>>> affected by the same mux or config bits in a register, and starts using
>>> pin groups for something else; GPIO<->pinmux pins mapping. Perhaps it's
>>> OK though, considering the other abuses of pin groups that are already
>>> present, such as using pin groups to represent default/common uses of
>>> groups of pins that don't actually exist in HW.
>
> The reason for these "abuses" might just be that every company (or maybe
> even hardware team) has a different understanding of how pin muxing
> should be implemented in hardware. There are probably as many different
> pin muxing architectures out there as companies (maybe even more).
> Finding a "one size fits all"-approach to this is extremely difficult
> and driver authors adapt the kernel infrastructure as well as they can
> to the hardware they have. As an example, see below for a fundamental
> cultural difference between your hardware/integration team and ours.

The issues I'm talking about are more SW issues; people have created
"pin groups" that represent both a set of pins/groups *and* the function
the is muxed onto them (and perhaps pin config settings too), rather
than having the driver create "pin groups" that actually represent just
groups of pins, and then using the DT pinctrl bindings as intended to
select which mux/config settings to use for that group.

>> I've realized what I don't like about this.
>>
>> Pin groups are supposed to be something that represents some property of
>> the pinctrl HW itself. So, if you have register "X" bits 3-0 that define
>> the mux function for pins 8, 9, 10, and 11, then there really is a pin
>> group that exists in HW, and that pin group will still exist with that
>> same definition no matter what SoC you put the pinctrl HW into. If this
>> changes, it's not the same pinctrl HW module.
>
> Let me see if I get this right (Let's take the example in the section
> "What is pinmuxing" of Documentation/pinctrl.txt in Linux-3.10-rc6 which
> is similar to TB10x):
> If I understand you correctly, you define two pin groups in this
> example:
> gpr1 = {A5, A6, A7, A8, B5};
> grp2 = {A1, B1, C1, D1, E1, F1, G1, G2, G3, G4, H1};
>
> grp1 has three configurations: allgpio, i2c and spi
> grp2 has five configurations: allgpio, mmc2, mmc2_spi, mmc4, mmc4_spi, mmc8

No, I don't think so at all.

When I pushed for the concept of groups, I intended it to mean precisely
one single thing. The points below describe this.

1) A pin is a single pin/ball/pad on the package.

2) Some register fields affect just a single pin. For example, there may
be a register field that affects pin A8's mux setting only.

3) Some register fields affect multiple pins at once. For example,
perhaps one register field affects both pin A8's an pin A7's mux setting
at once.

4) Depending on HW design, all register fields might be of type
described at (2) above, or all of the type described at (3) above, or a
mixture of both. Tegra is a mixture.

5) I expect the concept of a pin group to solely represent the various
groups of pins affected by each register field; in (2) above one pin per
group, in (3) above many pins per group.

Thus, to my mind, a pin group is purely a HW concept, and dictated
purely by HW design.

> Let's assume that unused pins are automatically configured as GPIOs in
> each configuration. The pin controller thus requires a second list of
> pins (for each of the modes), defining for which pins in each group it
> can grant gpio_requests in a given mode. Furthermore, the mmc node will
> have to "know" if mode "mmc2" or "mmc2_spi" must be selected for a given
> setup, making the thing somewhat unorthogonal.

The Linux pinctrl subsystem specifically doesn't provide mutual
exclusion between "mux function" and GPIO usage within a pin group,
although perhaps a driver could internally.

Consider a pin group in HW that encompasses 10 pins, but you've selected
a function onto it that only actually uses 6 pins for that logical
function. The other 4 pins aren't used, and can be GPIO. However, all
pins in the group are "claimed" because some mux function has been
selected onto the group that includes those 10 pins. In order to allow
some of those pins to be claimed as a GPIO, the pinctrl core simply
allows GPIO usage and mux function usage to be claimed on each
individual pin without regard for each-other.

Now, it would indeed be possible for each combination of (pin group, mux
function) to be associated with a list of pins from the group that could
be used as GPIO, and then for the pinctrl core to additionally enforce
that only those pins be claimed for GPIO usage. However, the pinctrl
core does not do this at present.

It's also a little difficult to completely validate that. Consider a mux
function that routes the pins to a HW module that considers some of the
signals to be optional. If one of those optional signals is used, then
the pin that would have carried it shouldn't be claimed as a GPIO, but
it the signal isn't used, then the pin will be free to use as a GPIO.
The selection of whether to use that optional signal may be outside the
realm of the pinctrl HW, i.e. in the HW module associated with the
selected mux function. Hence, pinctrl can't know whether the optional
signal is actually used, and hence can't conditionally allow it to be
used as a GPIO. To avoid pinctrl having to be completely nit-picky and
complex, it implements a simple approach and just allows absolutely any
GPIO/mux-function co-existence, with the expectation that if someone
attempts to use the HW incorrectly, it won't work, and they'll just fix
their SW/DT to actually request the correct configuration.

> When writing our pinctrl driver, my understanding was slightly
> different: I define seven pin groups:
> spi1 = {A5, A6, A7, A8};
> i2c = {A5, B5};
> mmc2 = {A1, B1};
> mmc4 = {A1, B1, C1, D1};
> mmc8 = {A1, B1, C1, D1, E1, F1, G1, H1};
> spi2 = {G1, G2, G3, G4};
> gpios = {A1, A5, A6, A7, A8, B1, B5, C1, D1, E1, F1, G1, G2, G3, G4, H1};
>
> Now each peripheral can individually request the pins it requires,
> independently of the others. Conflicts (e.g. between spi1 and i2c or
> between mmc4 and the GPIO at D1) are managed by the program logic in the
> pin controller. The advantages are the following:
> . The pin controller knows implicitly which pins are used for what and
> can easily grant or refuse pin and GPIO requests.
> . Conceptually, GPIO requests are now the same as any other
> configuration request.
> . The information of whether SPI2 is active or not is associated to
> spi2 and spi2 only. mmc does not need to know.
> . Implementation details of the pin controller hardware (which are the
> ports, which configuration to apply to a port to obtain a certain
> function) are confined in the pin controller driver.

I think that set of advantages is all true with the definition of a "pin
group" that matches exactly what groups exist in HW, i.e. the definition
I outlined above.

>> However, the connectivity between GPIO HW module "pins" (i.e. the GPIOs)
>> and pinctrl HW module "pins" (inputs to mux functions) is something that
>> only exists at the top-level of the SoC; outside the GPIO HW module
>> itself, and outside the pinctrl HW module itself.
>
> Well, in the case TB10x, GPIO pins are just the same as any other pins:
> They go through the pin controller all the same and there is really
> nothing that distinguishes them from, say, the mmc port in the example
> above - which can also be mapped either partially or completely. There's
> no bypassing of the pin controller going on at the top level or such.

I'd imagine that is true on most chips. It's certainly true on Tegra,
except for one minor irrelevant detail.

> And honestly: Have you ever seen a pin controller in which not only the
> program logic but also the pin data base can be reused from one SOC to
> another? At least I haven't.

Yes. A good few years ago I worked on some PPC chip where the pin
controller itself was 100% generic. It simply provided muxing for each
of N pins, each of which could have M mux functions (and indeed GPIO
controllers were one of those mux functions, for at least some of the
pins). From the pinctrl HW's perspective, the mux functions for each
pins were just "input 0", "input 1", ... "input n-1". A driver for such
HW would work no matter what SoC the pinctrl HW was placed into. The DT
binding would presumably just have pins labelled "0" .. "n-1" and mux
functions labelled "0" .. "n-1", and it would be up to the DT author to
consult each specific SoC's datasheet to determine which mux function
represented "MMC", "SPI", "I2C", ... for each pin. IIRC, IBM actually
had separate documentation for:

* The pinmux registers for muxing.

* The connectivity between the pinmux "pin-side" and the chip
pins/balls/pads.

* The GPIO registers.

* The connectivity between the GPIO controller's GPIO outputs, and the
pinmux HW's "mux-side" inputs, and similarly for all the other HW
modules that fed into the pinmux HW's muxes.

And honestly, I think likely all pinmux HW is this way. The only issue are:

* Datasheet authors tend to document the top-level connectivity of the
chip as if it were somehow part of the pinmux HW rather than actually
part of the top-level SoC routing.

* Some pinmux HW is designed to fit into a specific chip, so connect to
specific other HW modules, and hence not need to control every pin's mux
function independantly. This leads to "pin groups" where a single
register controls N pins at once. This reduces the number of registers
and amount of flop storage the pinmux HW needs. The exact trade-offs are
driven by the use-cases intended for the chip. This was the case on
Tegra20. However, this isn't very flexible, and requires significant
pinmux HW changes should the use-case change. I haven't actually talked
to the Tegra HW designers about this, but I strongly suspect that's why
Tegra30 now controls mux function individually per-pin, rather than in
use-case-targeted groups.

> On the other hand, For all Abilis chips,
> the program logic (the actual C functions) of the TB10x pinctrl could be
> reused with very minor modifications. It would be easy to extend the
> logic to a generic "Abilis pin controller" where all that needs to be
> changed in function of the "compatible" string is the pointer to the pin
> database.
>
>> In other words, you could have the exact same GPIO and pinctrl HW
>> modules instantiated into two different SoCs, but with completely
>> different mapping of GPIO IDs to pinctrl pin IDs.
>
> Well, you could very well conect a 4 bit mmc port instead of an spi in
> the above example without changing one thing in the pin controller or
> the mmc block...

Right. I expect that's all mostly true of many/most pinmux HW.

>> As such, it isn't even generally possible for the pinctrl HW module to
>> define pin groups that describe the mapping, because the mapping is not
>> a property of the pinctrl HW module, and hence should not be defined,
>> even partially, by the pinctrl HW module's driver.
>
> This is clearly implementation dependent. In the case of our chips, the
> opposite is actually the case:
>
> Your remark seems to reflect one of the following two hardware
> architectures:
>
> +- SPI
> Physical pins --- GPIO --- pinctrl -+- I2C
> +- mmc

(that's diagram 1)

>
> +- GPIO
> Physical pins -+ +- SPI
> +- pinctrl -+- I2C
> +- mmc

(that's diagram 2)

> TB10x hardware architecture:
>
> +- SPI
> Physical pins --- pinctrl -+- I2C
> +- mmc
> +- GPIO

(that's diagram 3)

No, I was thinking of diagram 3 above. I'm not sure if diagrams (1) or
(2) are common or exist? Certainly when I was contributing the Linux's
pinctrl SW design, I didn't really consider (1) or (2).

The issue is this: If the pinctrl and GPIO modules are separate modules,
then there is some wiring between them. Perhaps you're lucky and the
GPIO IDs end up exactly matching the mux-side pin IDs in the pin
controller HW. Perhaps that's not the case.

Now, inside the pinctrl HW, perhaps there's some other re-ordering;
perhaps the order of register addresses for mux functions doesn't match
the order of the datasheet's numbering of pins/bads/balls, or doesn't
match the HW block's numbering of the mux-side inputs.

So, there can be multiple levels of GPIO ID <-> pin ID mapping required.

My point is that:

Any mapping inside the pinctrl HW block is static and part of the HW
block's definition. This can be represented statically inside the
pinctrl driver source, or perhaps with some custom DT properties.

In that case, it /might/ be appropriate to define pin groups to help
define that mapping, since both pin groups and the mapping would be
strictly part of the internal HW definition of the pinctrl HW block.

Any mapping between the pinctrl HW and the GPIO HW is something at the
top-level of the SoC, and hence not something purely driven the the
pinctrl HW's.

In that case, the pinctrl driver or DT binding really shouldn't define
pin groups to help define that mapping, since the mapping is something
that exists outside the realm of the pinctrl HW block itself.

I would furthermore argue that the gpio-ranges DT property should be
only representing any mapping between the pinctrl and GPIO HW, not any
mapping inside the pinctrl HW.

However, I suppose I can see an argument that it might be useful to have
gpio-ranges represent the combination of *both* mappings as a single
mapping.

However, even in that case, I still don't think that the pinctrl HW
should be defining pin groups to help define that mapping. To do so
would encode information about the environment outside the pin
controller into the pin controller driver and/or DT binding, and that
information would not be purely driven by the pinmux HW itself.

And finally, I don't really like using pin groups for the purpose of
defining these mappings, since I intended them to purely represent the
mapping from register fields to the set of affected pins. However, I can
see an argument for doing this, since the pin groups are in fact still
representing /some/ aspect of the pinctrl internal HW.

>> In a similar fashion,
>> the DT binding for the pinctrl HW module should describe only the HW
>> module itself, and not the mapping/interaction with the outside world.
>> In other words, the DT binding for the pinctrl HW module also can't
>> define the names of any pin groups used in the GPIO<->pinctrl mapping,
>> for the same reasons.
>
> Except if this mapping is done _inside_ the pin controller, see above.

Yes, I got ahead of myself above. There are multiple places the mapping
could occur. Some would perhaps be appropriate to influence pin groups,
some not.

>> As such, I'm not sure that I conceptually agree with this patch series.
>>
>> Sure, it may make the gpio-ranges property more compact in some
>> (unusual?) non-linear cases. However, it's representing things
>> semantically incorrectly.
>
> Is it really in the TB10x case?
> To me it looks actually semantically more correct.

If they are purely representing a mapping internal to the pinctrl HW,
then it may be fine.

>> So, I'd like to question the motivation for using names here again.
>> Presumably the SoC vendor will write the gpio-ranges property for each
>> SoC, and put that into the SoC's .dtsi file. As such, no customer is
>> ever going to have to care about the property or its contents. So, I
>> don't really see how this helps you with your issue re: wanting to hide
>> details of multiple different ball-out options on similar SoCs, since
>> even with a manually-written purely numeric gpio-ranges property, all
>> that information is already essentially hidden; it's something that will
>> be written once, and then never looked at.
>
> Well, in the case of TB10x we are the SOC vendor and it was decided that
> all dts and dtsi files we publish are customer-facing data and must thus
> be coherent with the data sheet.

I would still tend to use gpio-ranges to represent the inter-module
connectivity, and use some alternative approach to re-jig that mapping
based on the mapping inside the pin controller.

For Tegra, I ended up choosing pinctrl pin IDs such that there was a 1:1
mapping between GPIO IDs and pinctrl pin IDs. I then ended up needing a
table that mapped pinctrl pin IDs (actually, the pin group IDs that
included those pins... and I would have needed this no matter what since
Tegra20 at least uses pin groups not per-pin configuration) to HW
register addresses. This is rather like saying that the pinctrl HW has
named inputs GPIO 0..n which are connected 1:1 with the GPIO HW module's
GPIO 0..n output signals, and then the pinctrl HW has a purely internal
mapping between the pinctrl HW module's GPIO signals and the pinctrl HW
module's pin/ball/pad-side pins.

2013-06-25 12:00:39

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On Fri, Jun 21, 2013 at 03:17:33PM -0600, Stephen Warren wrote:
> On 06/20/2013 05:57 AM, Christian Ruppert wrote:
> > Hello Stephen,
> >
> > On Wed, Jun 19, 2013 at 12:27:56PM -0600, Stephen Warren wrote:
> >> On 06/19/2013 12:10 PM, Stephen Warren wrote:
> >>> On 06/14/2013 03:12 AM, Christian Ruppert wrote:
> >>>> On Thu, Jun 13, 2013 at 03:38:09PM -0600, Stephen Warren wrote:
> >>>>> On 06/13/2013 06:55 AM, Christian Ruppert wrote:
> >> [...]
> >>>>> The potential advantage of this patch is that the pinctrl-side of the
> >>>>> mapping can be a group name rather than pin IDs, which might reduce the
> >>>>> size of the mapping list if you have an extremely sparse or non-linear
> >>>>> mapping /and/ parts of that mapping just happen to align with the pin
> >>>>> groups in the pin controller HW, since each entry in the gpio-ranges
> >>>>> property can be sparse/non-linear, rather than being a small linear
> >>>>> chunk of the mapping.
> >>>>
> >>>> Pin controller authors have the freedom to define pin groups just for
> >>>> the purpose of "predefining" the pinctrl side of GPIO ranges.
> >>>
> >>> Hmm. I suppose that's true. I'm not sure how enthusiastic I am about
> >>> doing this though... The reason I'm unsure is because it starts using
> >>> pin groups from something other than groups of pins in HW that are all
> >>> affected by the same mux or config bits in a register, and starts using
> >>> pin groups for something else; GPIO<->pinmux pins mapping. Perhaps it's
> >>> OK though, considering the other abuses of pin groups that are already
> >>> present, such as using pin groups to represent default/common uses of
> >>> groups of pins that don't actually exist in HW.
> >
> > The reason for these "abuses" might just be that every company (or maybe
> > even hardware team) has a different understanding of how pin muxing
> > should be implemented in hardware. There are probably as many different
> > pin muxing architectures out there as companies (maybe even more).
> > Finding a "one size fits all"-approach to this is extremely difficult
> > and driver authors adapt the kernel infrastructure as well as they can
> > to the hardware they have. As an example, see below for a fundamental
> > cultural difference between your hardware/integration team and ours.
>
> The issues I'm talking about are more SW issues; people have created
> "pin groups" that represent both a set of pins/groups *and* the function
> the is muxed onto them (and perhaps pin config settings too), rather
> than having the driver create "pin groups" that actually represent just
> groups of pins, and then using the DT pinctrl bindings as intended to
> select which mux/config settings to use for that group.

It looks like the TB10x is similar to this, see below for the details
and our reasoning.

> >> I've realized what I don't like about this.
> >>
> >> Pin groups are supposed to be something that represents some property of
> >> the pinctrl HW itself. So, if you have register "X" bits 3-0 that define
> >> the mux function for pins 8, 9, 10, and 11, then there really is a pin
> >> group that exists in HW, and that pin group will still exist with that
> >> same definition no matter what SoC you put the pinctrl HW into. If this
> >> changes, it's not the same pinctrl HW module.
> >
> > Let me see if I get this right (Let's take the example in the section
> > "What is pinmuxing" of Documentation/pinctrl.txt in Linux-3.10-rc6 which
> > is similar to TB10x):
> > If I understand you correctly, you define two pin groups in this
> > example:
> > gpr1 = {A5, A6, A7, A8, B5};
> > grp2 = {A1, B1, C1, D1, E1, F1, G1, G2, G3, G4, H1};
> >
> > grp1 has three configurations: allgpio, i2c and spi
> > grp2 has five configurations: allgpio, mmc2, mmc2_spi, mmc4, mmc4_spi, mmc8
>
> No, I don't think so at all.
>
> When I pushed for the concept of groups, I intended it to mean precisely
> one single thing. The points below describe this.
>
> 1) A pin is a single pin/ball/pad on the package.
>
> 2) Some register fields affect just a single pin. For example, there may
> be a register field that affects pin A8's mux setting only.
>
> 3) Some register fields affect multiple pins at once. For example,
> perhaps one register field affects both pin A8's an pin A7's mux setting
> at once.

To define some terminology, let's call a set of pins affected by the
same register / bit field a "port".

> 4) Depending on HW design, all register fields might be of type
> described at (2) above, or all of the type described at (3) above, or a
> mixture of both. Tegra is a mixture.

TB10x is all (3).

> 5) I expect the concept of a pin group to solely represent the various
> groups of pins affected by each register field; in (2) above one pin per
> group, in (3) above many pins per group.

In our example above, TB10x would have two ports, i.e. two bit fields in
its pinctrl register. Each of those bit fields would allow selecting one
of the configurations for grp1 and grp2 respectively.
Do I understand your explanation correctly: You would implement the TB10x
pinctrl exactly like the example above, two pin groups with three and
five configurations?

> Thus, to my mind, a pin group is purely a HW concept, and dictated
> purely by HW design.
>
> > Let's assume that unused pins are automatically configured as GPIOs in
> > each configuration. The pin controller thus requires a second list of
> > pins (for each of the modes), defining for which pins in each group it
> > can grant gpio_requests in a given mode. Furthermore, the mmc node will
> > have to "know" if mode "mmc2" or "mmc2_spi" must be selected for a given
> > setup, making the thing somewhat unorthogonal.
>
> The Linux pinctrl subsystem specifically doesn't provide mutual
> exclusion between "mux function" and GPIO usage within a pin group,
> although perhaps a driver could internally.

The TB10x driver does.

> Consider a pin group in HW that encompasses 10 pins, but you've selected
> a function onto it that only actually uses 6 pins for that logical
> function. The other 4 pins aren't used, and can be GPIO. However, all
> pins in the group are "claimed" because some mux function has been
> selected onto the group that includes those 10 pins. In order to allow
> some of those pins to be claimed as a GPIO, the pinctrl core simply
> allows GPIO usage and mux function usage to be claimed on each
> individual pin without regard for each-other.

This might be the main source of confusion. I admit that I hadn't
understood (and still think it is at least unorthogonal if not
semantically wrong) that an entire port must be claimed if just one
interface inside the port is required. Probably many other driver
authors (those you mention above) didn't understand this either.

> Now, it would indeed be possible for each combination of (pin group, mux
> function) to be associated with a list of pins from the group that could
> be used as GPIO, and then for the pinctrl core to additionally enforce
> that only those pins be claimed for GPIO usage. However, the pinctrl
> core does not do this at present.

This is why it is implemented inside the TB10x driver.

> It's also a little difficult to completely validate that. Consider a mux
> function that routes the pins to a HW module that considers some of the
> signals to be optional. If one of those optional signals is used, then
> the pin that would have carried it shouldn't be claimed as a GPIO, but
> it the signal isn't used, then the pin will be free to use as a GPIO.
> The selection of whether to use that optional signal may be outside the
> realm of the pinctrl HW, i.e. in the HW module associated with the
> selected mux function. Hence, pinctrl can't know whether the optional
> signal is actually used, and hence can't conditionally allow it to be
> used as a GPIO.

If the assumption you make below is correct and pinctrl hardware is
generally implemented as shown in diagram (3), the pin controller cannot
be completely agnostic of the fact that a given pin is either a GPIO or
part of an interface.

> To avoid pinctrl having to be completely nit-picky and
> complex, it implements a simple approach and just allows absolutely any
> GPIO/mux-function co-existence, with the expectation that if someone
> attempts to use the HW incorrectly, it won't work, and they'll just fix
> their SW/DT to actually request the correct configuration.

I'm perfectly happy with that: Today we have both options, let the
pinctrl driver manage conflicts or let the software/device tree author
manage everything manually. Every SoC vendor can decide for every
product which strategy to employ and depending on the context both can
make perfect sense.

> > When writing our pinctrl driver, my understanding was slightly
> > different: I define seven pin groups:
> > spi1 = {A5, A6, A7, A8};
> > i2c = {A5, B5};
> > mmc2 = {A1, B1};
> > mmc4 = {A1, B1, C1, D1};
> > mmc8 = {A1, B1, C1, D1, E1, F1, G1, H1};
> > spi2 = {G1, G2, G3, G4};
> > gpios = {A1, A5, A6, A7, A8, B1, B5, C1, D1, E1, F1, G1, G2, G3, G4, H1};

Do I understand your explanation on the very top correctly that this is
actually the strategy you said was wrongly employed in several other
drivers?

> > Now each peripheral can individually request the pins it requires,
> > independently of the others. Conflicts (e.g. between spi1 and i2c or
> > between mmc4 and the GPIO at D1) are managed by the program logic in the
> > pin controller. The advantages are the following:
> > . The pin controller knows implicitly which pins are used for what and
> > can easily grant or refuse pin and GPIO requests.
> > . Conceptually, GPIO requests are now the same as any other
> > configuration request.
> > . The information of whether SPI2 is active or not is associated to
> > spi2 and spi2 only. mmc does not need to know.
> > . Implementation details of the pin controller hardware (which are the
> > ports, which configuration to apply to a port to obtain a certain
> > function) are confined in the pin controller driver.
>
> I think that set of advantages is all true with the definition of a "pin
> group" that matches exactly what groups exist in HW, i.e. the definition
> I outlined above.

In my understanding, none of them applies:
. If the pinctrl driver doesn't explicitly have special lists of
available GPIOs in every mode, it is unable to manage GPIO/interface
conflicts.
. GPIO requests are something really special in case the pinctrl
driver doesn't manage those conflicts: As you explain above the GPIO
allocation system exists in parallel and completely independently of
the interface pin claiming system in the pinctrl core.
. In our example above, different interfaces inside the same port are
not orthogonal: MMC needs to know if SPI2 is required or not when
requesting its configuration.
. Thus, implementation details (which configuration to apply to a
given port to obtain a given set of functions) leak out of the
pinctrl driver.

> >> However, the connectivity between GPIO HW module "pins" (i.e. the GPIOs)
> >> and pinctrl HW module "pins" (inputs to mux functions) is something that
> >> only exists at the top-level of the SoC; outside the GPIO HW module
> >> itself, and outside the pinctrl HW module itself.
> >
> > Well, in the case TB10x, GPIO pins are just the same as any other pins:
> > They go through the pin controller all the same and there is really
> > nothing that distinguishes them from, say, the mmc port in the example
> > above - which can also be mapped either partially or completely. There's
> > no bypassing of the pin controller going on at the top level or such.
>
> I'd imagine that is true on most chips. It's certainly true on Tegra,
> except for one minor irrelevant detail.
>
> > And honestly: Have you ever seen a pin controller in which not only the
> > program logic but also the pin data base can be reused from one SOC to
> > another? At least I haven't.
>
> Yes. A good few years ago I worked on some PPC chip where the pin
> controller itself was 100% generic. It simply provided muxing for each
> of N pins, each of which could have M mux functions (and indeed GPIO
> controllers were one of those mux functions, for at least some of the
> pins). From the pinctrl HW's perspective, the mux functions for each
> pins were just "input 0", "input 1", ... "input n-1". A driver for such
> HW would work no matter what SoC the pinctrl HW was placed into. The DT
> binding would presumably just have pins labelled "0" .. "n-1" and mux
> functions labelled "0" .. "n-1", and it would be up to the DT author to
> consult each specific SoC's datasheet to determine which mux function
> represented "MMC", "SPI", "I2C", ... for each pin. IIRC, IBM actually
> had separate documentation for:
>
> * The pinmux registers for muxing.
>
> * The connectivity between the pinmux "pin-side" and the chip
> pins/balls/pads.
>
> * The GPIO registers.
>
> * The connectivity between the GPIO controller's GPIO outputs, and the
> pinmux HW's "mux-side" inputs, and similarly for all the other HW
> modules that fed into the pinmux HW's muxes.
>
> And honestly, I think likely all pinmux HW is this way.

Agreed.

> The only issue are:
>
> * Datasheet authors tend to document the top-level connectivity of the
> chip as if it were somehow part of the pinmux HW rather than actually
> part of the top-level SoC routing.

This is what I call the pin data base above. You said in a previous mail
that you didn't like declaring pins and pin groups in the device tree. I
don't like that idea either. Thus, they must be part of the driver and
even if the control logic is the same for many chips, the pin data base
is not in most cases (It might not even be the same for different
packaging options of the same die).

> * Some pinmux HW is designed to fit into a specific chip, so connect to
> specific other HW modules, and hence not need to control every pin's mux
> function independantly. This leads to "pin groups" where a single
> register controls N pins at once. This reduces the number of registers
> and amount of flop storage the pinmux HW needs. The exact trade-offs are
> driven by the use-cases intended for the chip. This was the case on
> Tegra20.

It is also the case for TB10x.

> However, this isn't very flexible, and requires significant
> pinmux HW changes should the use-case change. I haven't actually talked
> to the Tegra HW designers about this, but I strongly suspect that's why
> Tegra30 now controls mux function individually per-pin, rather than in
> use-case-targeted groups.
>
> > On the other hand, For all Abilis chips,
> > the program logic (the actual C functions) of the TB10x pinctrl could be
> > reused with very minor modifications. It would be easy to extend the
> > logic to a generic "Abilis pin controller" where all that needs to be
> > changed in function of the "compatible" string is the pointer to the pin
> > database.
> >
> >> In other words, you could have the exact same GPIO and pinctrl HW
> >> modules instantiated into two different SoCs, but with completely
> >> different mapping of GPIO IDs to pinctrl pin IDs.
> >
> > Well, you could very well conect a 4 bit mmc port instead of an spi in
> > the above example without changing one thing in the pin controller or
> > the mmc block...
>
> Right. I expect that's all mostly true of many/most pinmux HW.
>
> >> As such, it isn't even generally possible for the pinctrl HW module to
> >> define pin groups that describe the mapping, because the mapping is not
> >> a property of the pinctrl HW module, and hence should not be defined,
> >> even partially, by the pinctrl HW module's driver.
> >
> > This is clearly implementation dependent. In the case of our chips, the
> > opposite is actually the case:
> >
> > Your remark seems to reflect one of the following two hardware
> > architectures:
> >
> > +- SPI
> > Physical pins --- GPIO --- pinctrl -+- I2C
> > +- mmc
>
> (that's diagram 1)
>
> >
> > +- GPIO
> > Physical pins -+ +- SPI
> > +- pinctrl -+- I2C
> > +- mmc
>
> (that's diagram 2)
>
> > TB10x hardware architecture:
> >
> > +- SPI
> > Physical pins --- pinctrl -+- I2C
> > +- mmc
> > +- GPIO
>
> (that's diagram 3)
>
> No, I was thinking of diagram 3 above. I'm not sure if diagrams (1) or
> (2) are common or exist?

If you use per-pin muxing with GPIO functionality implemented inside the
per-pin mux this would e.g. correspond to diagram 2. If you use the same
approach with GPIO plus only one alternative input per pin (e.g. one
configuration bit) and another level of pinctrl for further muxing of
interfaces to that input that would be diagram 1.
At least per-pin-muxing with integrated GPIO functionality (diagram 2)
seems to be used in some cases.

> Certainly when I was contributing the Linux's
> pinctrl SW design, I didn't really consider (1) or (2).

OK, I think I misunderstood you there.

> The issue is this: If the pinctrl and GPIO modules are separate modules,
> then there is some wiring between them. Perhaps you're lucky and the
> GPIO IDs end up exactly matching the mux-side pin IDs in the pin
> controller HW. Perhaps that's not the case.
>
> Now, inside the pinctrl HW, perhaps there's some other re-ordering;
> perhaps the order of register addresses for mux functions doesn't match
> the order of the datasheet's numbering of pins/bads/balls, or doesn't
> match the HW block's numbering of the mux-side inputs.
>
> So, there can be multiple levels of GPIO ID <-> pin ID mapping required.
>
> My point is that:
>
> Any mapping inside the pinctrl HW block is static and part of the HW
> block's definition. This can be represented statically inside the
> pinctrl driver source, or perhaps with some custom DT properties.
>
> In that case, it /might/ be appropriate to define pin groups to help
> define that mapping, since both pin groups and the mapping would be
> strictly part of the internal HW definition of the pinctrl HW block.
>
> Any mapping between the pinctrl HW and the GPIO HW is something at the
> top-level of the SoC, and hence not something purely driven the the
> pinctrl HW's.
>
> In that case, the pinctrl driver or DT binding really shouldn't define
> pin groups to help define that mapping, since the mapping is something
> that exists outside the realm of the pinctrl HW block itself.

I agree. This is why the proposed pinctrl driver defines mux-side pin
controller interfaces as pingroups (these are actual hardware interfaces
of the pinctrl RTL module). GPIOs (and any other interfaces) are
connected at the top level to those interfaces.

In the case of a non-GPIO interface, the driver (e.g. SPI) tells pinctrl
which mux-side interface it is connected to and that that interface
should be mapped to the physical pins using the "default" pinctrl-0
property.

In the case of a GPIO bank, the GPIO controller tells the pin controller
through GPIO ranges to which of these hardware mux-side interfaces it is
hooked up on the top level.

In our original patch (where GPIO ranges were still half managed inside
the drivers), both mechanisms used the same phandle semantics. The
second generation patch now still uses standard phandle semantics for
non-GPIO interfaces (managed through pinctrl-0) and strings directly
referencing the mux-side ports for GPIOs.

> I would furthermore argue that the gpio-ranges DT property should be
> only representing any mapping between the pinctrl and GPIO HW, not any
> mapping inside the pinctrl HW.

I agree, see above. In the TB10x case, everything pinctrl-internal is
managed inside the driver (by the tables in the beginning), the mux side
top-level connectivity is managed by gpio ranges and pinctrl-0
properties and the pin-side connectivity doesn't matter as long as
kernel-internal pin numbering doesn't leak out.

> However, I suppose I can see an argument that it might be useful to have
> gpio-ranges represent the combination of *both* mappings as a single
> mapping.

We actually tried to avoid that in the TB10x driver but maybe there are
some use cases for it.

> However, even in that case, I still don't think that the pinctrl HW
> should be defining pin groups to help define that mapping. To do so
> would encode information about the environment outside the pin
> controller into the pin controller driver and/or DT binding, and that
> information would not be purely driven by the pinmux HW itself.
>
> And finally, I don't really like using pin groups for the purpose of
> defining these mappings, since I intended them to purely represent the
> mapping from register fields to the set of affected pins.

My philosophy of driver design is slightly different here: IMHO a driver
should provide some level of abstraction. The programming interface
should be independent of implementation details such as configuration
registers of the hardware block. Otherwise, why not just implement some
rudimentary locking mechanism and write to those registers directly?
This doesn't mean that hardware implementation and driver APIs must
never match but hardware implementation should (as far as possible) not
be a constraint for the driver API.

> However, I can
> see an argument for doing this, since the pin groups are in fact still
> representing /some/ aspect of the pinctrl internal HW.
>
> >> In a similar fashion,
> >> the DT binding for the pinctrl HW module should describe only the HW
> >> module itself, and not the mapping/interaction with the outside world.
> >> In other words, the DT binding for the pinctrl HW module also can't
> >> define the names of any pin groups used in the GPIO<->pinctrl mapping,
> >> for the same reasons.
> >
> > Except if this mapping is done _inside_ the pin controller, see above.
>
> Yes, I got ahead of myself above. There are multiple places the mapping
> could occur. Some would perhaps be appropriate to influence pin groups,
> some not.
>
> >> As such, I'm not sure that I conceptually agree with this patch series.
> >>
> >> Sure, it may make the gpio-ranges property more compact in some
> >> (unusual?) non-linear cases. However, it's representing things
> >> semantically incorrectly.
> >
> > Is it really in the TB10x case?
> > To me it looks actually semantically more correct.
>
> If they are purely representing a mapping internal to the pinctrl HW,
> then it may be fine.
>
> >> So, I'd like to question the motivation for using names here again.
> >> Presumably the SoC vendor will write the gpio-ranges property for each
> >> SoC, and put that into the SoC's .dtsi file. As such, no customer is
> >> ever going to have to care about the property or its contents. So, I
> >> don't really see how this helps you with your issue re: wanting to hide
> >> details of multiple different ball-out options on similar SoCs, since
> >> even with a manually-written purely numeric gpio-ranges property, all
> >> that information is already essentially hidden; it's something that will
> >> be written once, and then never looked at.
> >
> > Well, in the case of TB10x we are the SOC vendor and it was decided that
> > all dts and dtsi files we publish are customer-facing data and must thus
> > be coherent with the data sheet.
>
> I would still tend to use gpio-ranges to represent the inter-module
> connectivity, and use some alternative approach to re-jig that mapping
> based on the mapping inside the pin controller.

But we DO use GPIO-ranges. We just predefine the pinctrl side of them
inside the driver to avoid kernel-internals leaking out.

> For Tegra, I ended up choosing pinctrl pin IDs such that there was a 1:1
> mapping between GPIO IDs and pinctrl pin IDs. I then ended up needing a
> table that mapped pinctrl pin IDs (actually, the pin group IDs that
> included those pins... and I would have needed this no matter what since
> Tegra20 at least uses pin groups not per-pin configuration) to HW
> register addresses. This is rather like saying that the pinctrl HW has
> named inputs GPIO 0..n which are connected 1:1 with the GPIO HW module's
> GPIO 0..n output signals, and then the pinctrl HW has a purely internal
> mapping between the pinctrl HW module's GPIO signals and the pinctrl HW
> module's pin/ball/pad-side pins.

If I understand this correctly, the only difference between this and
TB10x is that TB10x uses symbolic names instead of numeric values to
indicate start pin and pin count.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-25 14:27:39

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On Fri, Jun 21, 2013 at 11:17 PM, Stephen Warren <[email protected]> wrote:

> When I pushed for the concept of groups, I intended it to mean precisely
> one single thing. The points below describe this.
>
> 1) A pin is a single pin/ball/pad on the package.
>
> 2) Some register fields affect just a single pin. For example, there may
> be a register field that affects pin A8's mux setting only.
>
> 3) Some register fields affect multiple pins at once. For example,
> perhaps one register field affects both pin A8's an pin A7's mux setting
> at once.
>
> 4) Depending on HW design, all register fields might be of type
> described at (2) above, or all of the type described at (3) above, or a
> mixture of both. Tegra is a mixture.
>
> 5) I expect the concept of a pin group to solely represent the various
> groups of pins affected by each register field; in (2) above one pin per
> group, in (3) above many pins per group.
>
> Thus, to my mind, a pin group is purely a HW concept, and dictated
> purely by HW design.

This we can discuss perpetually it seems.

For Nomadik, as I've pointed out in the past it is actually:

(6): it is one register/set if bits per pin, BUT the register settings
pertain to physical lines having electrical settings which postulate
that they be handled in batch or wreak havoc.

I.e. it is a HW limitation in the *silicon* of *all* implementations,
but that is *not* expressed in the register map.

For the practical consequences see __nmk_config_pins if (glitch)
runpath. Handling this as a group makes perfect sense from
a hardware point of view.

Yours,
Linus Walleij

2013-06-25 14:32:54

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On Fri, Jun 21, 2013 at 11:17 PM, Stephen Warren <[email protected]> wrote:
> On 06/20/2013 05:57 AM, Christian Ruppert wrote:

> The Linux pinctrl subsystem specifically doesn't provide mutual
> exclusion between "mux function" and GPIO usage within a pin group,
> although perhaps a driver could internally.

It used to block this at one point. But it doesn't make sense
when the hardware looks like so:

>> +- SPI
>> Physical pins --- GPIO --- pinctrl -+- I2C
>> +- mmc

As in this case it is perfectly legal to enable the GPIO as
input while the I2C bus is running and "spy" on the signals.

The driver should probably not allow the GPIO output to be
driven while some peripheral is muxed in though, that could be
disastrous...

Yours,
Linus Walleij

2013-06-25 14:56:32

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On Fri, Jun 21, 2013 at 11:17 PM, Stephen Warren <[email protected]> wrote:
> On 06/20/2013 05:57 AM, Christian Ruppert wrote:

>> Your remark seems to reflect one of the following two hardware
>> architectures:
>>
>> +- SPI
>> Physical pins --- GPIO --- pinctrl -+- I2C
>> +- mmc
>
> (that's diagram 1)
>
>>
>> +- GPIO
>> Physical pins -+ +- SPI
>> +- pinctrl -+- I2C
>> +- mmc
>
> (that's diagram 2)
>
>> TB10x hardware architecture:
>>
>> +- SPI
>> Physical pins --- pinctrl -+- I2C
>> +- mmc
>> +- GPIO
>
> (that's diagram 3)
>
> No, I was thinking of diagram 3 above. I'm not sure if diagrams (1) or
> (2) are common or exist?

The U300 pin controller is obviously of type (1) as it can spy on
the signals.

The Nomadik pin controller is basically type (2).

Maybe this is part of the explanation to why we sometimes tend
to talk past each other :-D

(I would replace the string "pinctrl" with "pinmux" above,
as I just did in my documentation patch adding these
diagrams.)

Yours,
Linus Walleij

2013-06-25 15:19:27

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On 06/25/2013 08:27 AM, Linus Walleij wrote:
> On Fri, Jun 21, 2013 at 11:17 PM, Stephen Warren <[email protected]> wrote:
>
>> When I pushed for the concept of groups, I intended it to mean precisely
>> one single thing. The points below describe this.
>>
>> 1) A pin is a single pin/ball/pad on the package.
>>
>> 2) Some register fields affect just a single pin. For example, there may
>> be a register field that affects pin A8's mux setting only.
>>
>> 3) Some register fields affect multiple pins at once. For example,
>> perhaps one register field affects both pin A8's an pin A7's mux setting
>> at once.
>>
>> 4) Depending on HW design, all register fields might be of type
>> described at (2) above, or all of the type described at (3) above, or a
>> mixture of both. Tegra is a mixture.
>>
>> 5) I expect the concept of a pin group to solely represent the various
>> groups of pins affected by each register field; in (2) above one pin per
>> group, in (3) above many pins per group.
>>
>> Thus, to my mind, a pin group is purely a HW concept, and dictated
>> purely by HW design.
>
> This we can discuss perpetually it seems.
>
> For Nomadik, as I've pointed out in the past it is actually:
>
> (6): it is one register/set if bits per pin, BUT the register settings
> pertain to physical lines having electrical settings which postulate
> that they be handled in batch or wreak havoc.
>
> I.e. it is a HW limitation in the *silicon* of *all* implementations,
> but that is *not* expressed in the register map.
>
> For the practical consequences see __nmk_config_pins if (glitch)
> runpath. Handling this as a group makes perfect sense from
> a hardware point of view.

OK, so there are certainly some HW designs that may benefit from using
groups even where the registers are per-pin. Using them there makes sense.

2013-06-25 15:22:09

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On 06/25/2013 08:32 AM, Linus Walleij wrote:
> On Fri, Jun 21, 2013 at 11:17 PM, Stephen Warren <[email protected]> wrote:
>> On 06/20/2013 05:57 AM, Christian Ruppert wrote:
>
>> The Linux pinctrl subsystem specifically doesn't provide mutual
>> exclusion between "mux function" and GPIO usage within a pin group,
>> although perhaps a driver could internally.
>
> It used to block this at one point. But it doesn't make sense
> when the hardware looks like so:
>
>>> +- SPI
>>> Physical pins --- GPIO --- pinctrl -+- I2C
>>> +- mmc
>
> As in this case it is perfectly legal to enable the GPIO as
> input while the I2C bus is running and "spy" on the signals.
>
> The driver should probably not allow the GPIO output to be
> driven while some peripheral is muxed in though, that could be
> disastrous...

Well, in the HW diagram above, GPIO output probably simply overrides
"mux function" output, so everything would work as requested. Whether it
makes sense to request such an override is a policy question that
pinctrl itself probably shouldn't decide. After all, what if there's a
pin group containing 4 pins, which are used by the board as 2 GPIO and 2
I2C. pinctrl shouldn't disallow selecting GPIO on any pin in that pin
group simply because I2C is selected on it; that fact doesn't
necessarily mean that the selected mux function actually uses every
single pin in the group.

2013-06-25 15:28:45

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On Fri, Jun 21, 2013 at 11:17 PM, Stephen Warren <[email protected]> wrote:

> And finally, I don't really like using pin groups for the purpose of
> defining these mappings, since I intended them to purely represent the
> mapping from register fields to the set of affected pins. However, I can
> see an argument for doing this, since the pin groups are in fact still
> representing /some/ aspect of the pinctrl internal HW.

The groups concept was part of the first pinctrl commit
2744e8afb3b76343e7eb8197e8b3e085036010a5
and there the intention was clearly just to define a discrete
set of pins.

And some drivers use it like this still, with no connection to
physical registers, i.MX come to mind.

But I do seem to recall some endless discussions about this,
I think we need to agree to disagree.

I never saw any heavy reason for the driver not to define groups
in some way it seemed would fit the muxing purposes right, we could
say, if we compare to mathematical philiosophy, that I am a formalist
rather than a platonist (i.e. I'm not requiring the description to strictly
match a reality, but rather prefer that they fulfil a practical purpose).
Here is an item for vacation reading...
http://en.wikipedia.org/wiki/Philosophy_of_mathematics

Maybe I will switch schools one day who knows.

Here is a tongue-in-cheek patch making fun of the whole debate:

diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 5979147..d3ad8b0 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -102,6 +102,21 @@ struct pinctrl_ops {
};

/**
+ * enum pinctrl_school - define the interpretation of group concept
+ * @PINCTRL_SCHOOL_PLATONIST: the group concept represents the authors
+ * idea of a reality "behind" the pin controller, though in data
+ * sheets and registers we may only see shadows of this reality
+ * reflected as cavernous shades
+ * @PINCTRL_SCHOOL_FORMALIST: the group concept represents the authors
+ * practical needs of dealing with pins in ordered groups and nothing
+ * else
+ */
+enum pinctrl_school {
+ PINCTRL_SCHOOL_PLATONIST,
+ PINCTRL_SCHOOL_FORMALIST,
+};
+
+/**
* struct pinctrl_desc - pin controller descriptor, register this to pin
* control subsystem
* @name: name for the pin controller
@@ -115,6 +130,7 @@ struct pinctrl_ops {
* @confops: pin config operations vtable, if you support pin configuration in
* your driver
* @owner: module providing the pin controller, used for refcounting
+ * @school: which philosophical school the controller belongs to.
*/
struct pinctrl_desc {
const char *name;
@@ -124,6 +140,7 @@ struct pinctrl_desc {
const struct pinmux_ops *pmxops;
const struct pinconf_ops *confops;
struct module *owner;
+ enum pinctrl_school school;
};

/* External interface to pin controller */

Yours,
Linus Walleij

2013-06-25 15:31:07

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On 06/25/2013 08:56 AM, Linus Walleij wrote:
> On Fri, Jun 21, 2013 at 11:17 PM, Stephen Warren <[email protected]> wrote:
>> On 06/20/2013 05:57 AM, Christian Ruppert wrote:
>
>>> Your remark seems to reflect one of the following two hardware
>>> architectures:
>>>
>>> +- SPI
>>> Physical pins --- GPIO --- pinctrl -+- I2C
>>> +- mmc
>>
>> (that's diagram 1)
>>
>>>
>>> +- GPIO
>>> Physical pins -+ +- SPI
>>> +- pinctrl -+- I2C
>>> +- mmc
>>
>> (that's diagram 2)
>>
>>> TB10x hardware architecture:
>>>
>>> +- SPI
>>> Physical pins --- pinctrl -+- I2C
>>> +- mmc
>>> +- GPIO
>>
>> (that's diagram 3)
>>
>> No, I was thinking of diagram 3 above. I'm not sure if diagrams (1) or
>> (2) are common or exist?
>
> The U300 pin controller is obviously of type (1) as it can spy on
> the signals.

U300 HW might be diagram (1) - I can't say since I'm not familiar with
the HW. However, the fact that GPIO can spy on signals in no way at all
implies that the HW must conform to diagram (1).

In diagram (2), the pins are routed to both the GPIO and pinctrl module,
so there's no reason why GPIO couldn't spy on any input signals there too.

Even in diagram 3, GPIO may be able to spy. The issue is how the
"inbound" muxes work.

For output signals from HW modules through the pinctrl HW to the pins,
there's likely a plain n:1 mux that selects a single signal and drives
it out.

For input signals, there are still some choices; you might have a 1:n
mux that only drives 1 single HW module's input signals from the pins,
or you may simply wire the input pin through to all the possible HW
modules in parallel on the input side, or you may always wire the input
pin to the GPIO HW module, but have a 1:n mux for the dedicated HW
modules so that (at most) one of those is driven by the pin, etc.

For reference, the Tegra HW is diagram (3), although actually the GPIO
module has the register bits that select between GPIO and mux-function
for each pin! The signals that control this are fed from the GPIO
register bits into input signals on the pinmux HW, which does all the
actual muxing. The input path muxing is set up so that without affecting
the pinmux programming, the GPIO module can spy on any input signal,
although while doing so, it disconnects the input pin from the dedicated
mux function, thus potentially causing some disruption.

> The Nomadik pin controller is basically type (2).
>
> Maybe this is part of the explanation to why we sometimes tend
> to talk past each other :-D
>
> (I would replace the string "pinctrl" with "pinmux" above,
> as I just did in my documentation patch adding these
> diagrams.)

2013-06-25 15:39:32

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On 06/25/2013 09:28 AM, Linus Walleij wrote:
> On Fri, Jun 21, 2013 at 11:17 PM, Stephen Warren <[email protected]> wrote:
>
>> And finally, I don't really like using pin groups for the purpose of
>> defining these mappings, since I intended them to purely represent the
>> mapping from register fields to the set of affected pins. However, I can
>> see an argument for doing this, since the pin groups are in fact still
>> representing /some/ aspect of the pinctrl internal HW.
>
> The groups concept was part of the first pinctrl commit
> 2744e8afb3b76343e7eb8197e8b3e085036010a5
> and there the intention was clearly just to define a discrete
> set of pins.
>
> And some drivers use it like this still, with no connection to
> physical registers, i.MX come to mind.
>
> But I do seem to recall some endless discussions about this,
> I think we need to agree to disagree.

But the whole point of a subsystem is to provide clear common semantics
across all the different drivers that comprise it. IMHO, it's a great
failing of pinctrl that it doesn't clearly define its data model at all,
and just leaves individual driver authors to use groups in whatever
random fashion they want. We really should have different entries in the
pinctrl data model for these different concepts (real HW groups, and
logical/virtual/SW groups) since they're entirely different things with
different semantics.

Perhaps it's simplest if I just step out of pinctrl and let it exist as
it is.

2013-06-25 15:47:16

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On Tue, Jun 25, 2013 at 5:31 PM, Stephen Warren <[email protected]> wrote:
> On 06/25/2013 08:56 AM, Linus Walleij wrote:
>> On Fri, Jun 21, 2013 at 11:17 PM, Stephen Warren <[email protected]> wrote:
>>> On 06/20/2013 05:57 AM, Christian Ruppert wrote:
>>
>>>> Your remark seems to reflect one of the following two hardware
>>>> architectures:
>>>>
>>>> +- SPI
>>>> Physical pins --- GPIO --- pinctrl -+- I2C
>>>> +- mmc
>>>
>>> (that's diagram 1)
>>>
>>>>
>>>> +- GPIO
>>>> Physical pins -+ +- SPI
>>>> +- pinctrl -+- I2C
>>>> +- mmc
>>>
>>> (that's diagram 2)
>>>
>>>> TB10x hardware architecture:
>>>>
>>>> +- SPI
>>>> Physical pins --- pinctrl -+- I2C
>>>> +- mmc
>>>> +- GPIO
>>>
>>> (that's diagram 3)
>>>
>>> No, I was thinking of diagram 3 above. I'm not sure if diagrams (1) or
>>> (2) are common or exist?
>>
>> The U300 pin controller is obviously of type (1) as it can spy on
>> the signals.
>
> U300 HW might be diagram (1) - I can't say since I'm not familiar with
> the HW. However, the fact that GPIO can spy on signals in no way at all
> implies that the HW must conform to diagram (1).

That's true. And I don't know what it actually is in this case.
That hardware is actually weird in many ways, thanks to
helpful HW engineers modeling use cases into the HW.

>> The Nomadik pin controller is basically type (2).

This I know however to be true, as I have access to the
low-level schematics of the ASIC.

Yours,
Linus Walleij

2013-06-25 15:53:48

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On Tue, Jun 25, 2013 at 5:39 PM, Stephen Warren <[email protected]> wrote:
> On 06/25/2013 09:28 AM, Linus Walleij wrote:

>> But I do seem to recall some endless discussions about this,
>> I think we need to agree to disagree.
>
> But the whole point of a subsystem is to provide clear common semantics
> across all the different drivers that comprise it.

I guess we simply disagree on how deeply these semantics
should go.

> IMHO, it's a great
> failing of pinctrl that it doesn't clearly define its data model at all,
> and just leaves individual driver authors to use groups in whatever
> random fashion they want.

I do not think any driver is using it in a "random" fashion.
I would agree if the authors just stuck any pins into some
random groups named after colors or rock bands.

Obviously there is a mental model of the uses somewhere
behind the code.

> We really should have different entries in the
> pinctrl data model for these different concepts (real HW groups, and
> logical/virtual/SW groups) since they're entirely different things with
> different semantics.

That's what the tongue-in-cheek patch tried to convey, in some
kind of humorous manner. I was just trying to lighten up the
discussion a bit.

> Perhaps it's simplest if I just step out of pinctrl and let it exist as
> it is.

No not at all. Your work on defining and reviewing the pinctrl
drivers and DT bindings is much appreciated. However all
comittee work tend to lead to a few compromises. I don't
think this one compromise is especially hard to live with.

Yours,
Linus Walleij

2013-06-25 15:59:16

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/2] Make non-linear GPIO ranges accesible from gpiolib

On 06/25/2013 05:59 AM, Christian Ruppert wrote:
> On Fri, Jun 21, 2013 at 03:17:33PM -0600, Stephen Warren wrote:

...
> To define some terminology, let's call a set of pins affected by the
> same register / bit field a "port".

Well, the terminology "group" already exists for this really, but I
suppose we can call it a port instead...

...
>> 4) Depending on HW design, all register fields might be of type
>> described at (2) above, or all of the type described at (3) above, or a
>> mixture of both. Tegra is a mixture.
>
> TB10x is all (3).
>
>> 5) I expect the concept of a pin group to solely represent the various
>> groups of pins affected by each register field; in (2) above one pin per
>> group, in (3) above many pins per group.
>
> In our example above, TB10x would have two ports, i.e. two bit fields in
> its pinctrl register. Each of those bit fields would allow selecting one
> of the configurations for grp1 and grp2 respectively.
>
> Do I understand your explanation correctly: You would implement the TB10x
> pinctrl exactly like the example above, two pin groups with three and
> five configurations?

Assuming there's a register field for grp1 that takes one of 3 values,
and a register field for grp2 that takes one of 5 values, then yes.

...
>> Consider a pin group in HW that encompasses 10 pins, but you've selected
>> a function onto it that only actually uses 6 pins for that logical
>> function. The other 4 pins aren't used, and can be GPIO. However, all
>> pins in the group are "claimed" because some mux function has been
>> selected onto the group that includes those 10 pins. In order to allow
>> some of those pins to be claimed as a GPIO, the pinctrl core simply
>> allows GPIO usage and mux function usage to be claimed on each
>> individual pin without regard for each-other.
>
> This might be the main source of confusion. I admit that I hadn't
> understood (and still think it is at least unorthogonal if not
> semantically wrong) that an entire port must be claimed if just one
> interface inside the port is required. Probably many other driver
> authors (those you mention above) didn't understand this either.

It's not that the "entire port /must/ be claimed", it's that there's
choice in the matter; no concept of claiming anything (other than GPIO)
at a level less than a port. The port (group in pinctrl terminology)
*is* the level of granularity at which pinctrl claims pins.

>> Now, it would indeed be possible for each combination of (pin group, mux
>> function) to be associated with a list of pins from the group that could
>> be used as GPIO, and then for the pinctrl core to additionally enforce
>> that only those pins be claimed for GPIO usage. However, the pinctrl
>> core does not do this at present.
>
> This is why it is implemented inside the TB10x driver.

OK.

If that concept ends up being more generally applicable, then it might
make sense to move the validation into the pinctrl core. I don't know if
this level of detail makes sense there or not though.

>>> When writing our pinctrl driver, my understanding was slightly
>>> different: I define seven pin groups:
>>> spi1 = {A5, A6, A7, A8};
>>> i2c = {A5, B5};
>>> mmc2 = {A1, B1};
>>> mmc4 = {A1, B1, C1, D1};
>>> mmc8 = {A1, B1, C1, D1, E1, F1, G1, H1};
>>> spi2 = {G1, G2, G3, G4};
>>> gpios = {A1, A5, A6, A7, A8, B1, B5, C1, D1, E1, F1, G1, G2, G3, G4, H1};
>
> Do I understand your explanation on the very top correctly that this is
> actually the strategy you said was wrongly employed in several other
> drivers?

It looks like it on the surface. I'd have to look at the HW register
specs to say for sure.

...
>> In that case, the pinctrl driver or DT binding really shouldn't define
>> pin groups to help define that mapping, since the mapping is something
>> that exists outside the realm of the pinctrl HW block itself.
>
> I agree. This is why the proposed pinctrl driver defines mux-side pin
> controller interfaces as pingroups (these are actual hardware interfaces
> of the pinctrl RTL module). GPIOs (and any other interfaces) are
> connected at the top level to those interfaces.

Pingroup definitions for "actual HW interfaces of the RTL module" make
sense to me.

2013-06-26 11:42:38

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/4] Make non-linear GPIO ranges accesible from gpiolib

On Wed, Jun 19, 2013 at 12:15:13PM -0600, Stephen Warren wrote:
> On 06/19/2013 06:03 AM, Linus Walleij wrote:
> > On Tue, Jun 18, 2013 at 11:29 AM, Christian Ruppert
> > <[email protected]> wrote:
> >
> >> This patch adds the infrastructure required to register non-linear gpio
> >> ranges through gpiolib and the standard GPIO device tree bindings.
> >>
> >> Signed-off-by: Christian Ruppert <[email protected]>
> >
> > I'm basically fine with this, but would like Stephen's ACK if possible.
> >
> >> +In addition, named groups of pins can be mapped to pin groups of a given
> >> +pin controller:
> >> +
> >> + gpio_pio_g: gpio-controller@1480 {
> >> + #gpio-cells = <2>;
> >> + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
> >> + reg = <0x1480 0x18>;
> >> + gpio-controller;
> >> + gpio-ranges = <&pinctrl1 0 0 0>, <&pinctrl2 3 0 0>;
> >> + gpio-ranges-group-names = "foo", "bar";
> >> + };
> >> +
> >> +where,
> >> + &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
> >> +
> >> + The following value specifies the base GPIO offset of the pin range with
> >> + respect to the GPIO controller's base. The remaining two values must be
> >> + 0 to indicate that a named pin group should be used for the respective
> >> + range. The number of pins in the range is the number of pins in the pin
> >> + group.
> >
> > So while this works, these zeroes seem a bit awkward, but maybe
> > it's the only way?
> >
> > I'm not good enough on device tree conventions, but isn't this possible:
> >
> > gpio-ranges = <&pinctrl1 0>, <&pinctrl2 3>;
> > gpio-ranges-group-names = "foo", "bar";
> >
> > Since we don't have any #gpio-ranges-cells or anything like that I
> > guess we can define this to have a flexible number of cells
> > depending on use case?
>
> If we're willing to have gpio-ranges be either *all* group names, or
> *all* IDs, we can define the format of gpio-ranges to have two cells
> (phandle and GPIO number) if the property gpio-ranges-group-names
> exists, but four cells (phandle, GPIO number, pin number, count)
> otherwise. However, that's a little restrictive, since then what if one
> GPIO controller is hooked to two different pinmux controllers, and you
> want to use different formats for the references to each. A
> #gpio-ranges-cells in the target of the phandle would allow this, but I
> don't think this is something the pinctrl node should dictate to those
> who reference it; it's quite legitimate for a GPIO node to use the pure
> numeric mapping even if the pin controller happens to expose some pin
> groups that allow you to do the mapping by name.

I actually had a version of the patch with #gpio-range-cells specifying
the format (one argument for named ranges, three for classical ranges)
before deciding to use a separate property and sending that version. As
I said in a previous mail, I don't have a preference which of the
following three possibilities to use and would be grateful for some
guidance (if it matters at all).

1.) separate property:
gpio {
gpio-ranges = <&pinctrl1 0 0 5>;
gpio-range-groups = <&pinctrl2 5>;
gpio-range-group-names = "gpios";
};

2.) fixed number of three arguments:
gpio {
gpio-ranges = <&pinctrl1 0 0 5>, <&pinctrl2 5 0 0>;
gpio-range-names = "", "gpios";
};

3.) pinctrl-defined format.
pinctrl1: pctl1 {
#gpio-range-cells = <3>;
};
pinctrl2: pctl2 {
#gpio-range-cells = <1>;
};
gpio {
gpio-ranges = <&pinctrl1 0 0 5>, <&pinctrl2 5>;
gpio-range-names = "", "gpios";
};

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-26 11:47:21

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 1/4] Make non-linear GPIO ranges accesible from gpiolib

On Wed, Jun 19, 2013 at 04:27:44PM -0600, Stephen Warren wrote:
> On 06/18/2013 03:29 AM, Christian Ruppert wrote:
> > This patch adds the infrastructure required to register non-linear gpio
> > ranges through gpiolib and the standard GPIO device tree bindings.
>
> I review this in case we decide to go with it anyway.
>
> > diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
>
> > +In addition, named groups of pins can be mapped to pin groups of a given
> > +pin controller:
> > +
> > + gpio_pio_g: gpio-controller@1480 {
> > + #gpio-cells = <2>;
> > + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
> > + reg = <0x1480 0x18>;
> > + gpio-controller;
> > + gpio-ranges = <&pinctrl1 0 0 0>, <&pinctrl2 3 0 0>;
> > + gpio-ranges-group-names = "foo", "bar";
> > + };
> > +
> > +where,
> > + &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
> > +
> > + The following value specifies the base GPIO offset of the pin range with
> > + respect to the GPIO controller's base. The remaining two values must be
> > + 0 to indicate that a named pin group should be used for the respective
> > + range. The number of pins in the range is the number of pins in the pin
> > + group.
>
> It'd be good to re-write this section in a similar style to the cleanup
> patches that I sent for the existing gpio-ranges documentation. That
> makes the format description more of a raw syntax than English text.

can you please point me to some place where I can find those patches on
the web?

> > + gpio-ranges-group-names defines the name of each pingroup of the
> > + respective pin controller.
> > +
> > +The pinctrl node must have a "#gpio-#gpio-range-cells" property set to three
> > +to define the number of arguments to pass with the phandle.
>
> There's some mistake in the property name there. I'd assert we should
> remove those two lines anyway, and use the new OF parsing code I posted
> when cleaning up gpio-ranges.
>
> > diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
>
> > + if (pinspec.args[2]) {
> > + /* npins != 0: linear range */
> > + ret = gpiochip_add_pin_range(chip,
> > + pinctrl_dev_get_devname(pctldev),
> > + pinspec.args[0],
> > + pinspec.args[1],
> > + pinspec.args[2]);
> > + if (ret)
> > + break;
> > + } else {
>
> I think here we should validate !pinspec.args[1], to ensure that value
> doesn't get set to anything wonky.
>

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-26 11:51:19

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Wed, Jun 19, 2013 at 04:35:14PM -0600, Stephen Warren wrote:
> On 06/18/2013 03:29 AM, Christian Ruppert wrote:
> > The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
> > Used to control the pinmux and is a prerequisite for the GPIO driver.
>
> > diff --git a/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
>
> > +Port definitions
> > +----------------
> > +
> > +Ports are defined (and referenced) by sub-nodes of the pin controller. Every
> > +sub-node defines exactly one port (i.e. a set of pins). Ports are predefined
> > +as named pin groups inside the pin controller driver and these names are used
> > +to associate pin group predefinitions to pin controller sub-nodes.
> > +
> > +Required port definition subnode properties:
> > + - pingrp: should be set to the name of the port's pin group.
>
> This seems odd.... More on that where I comment on the example.
>
> > +The following pin groups are available:
> > + - GPIO ports: gpioa_pins, gpiob_pins, gpioc_pins, gpiod_pins, gpioe_pins,
> > + gpiof_pins, gpiog_pins, gpioh_pins, gpioi_pins, gpioj_pins,
> > + gpiok_pins, gpiol_pins, gpiom_pins, gpion_pins
> ...
> > + - JTAG: jtag_pins
>
> I'd suggest removing "_pins" from all those names, since it's the same
> in all names and hence isn't necessary.
>
> > +GPIO ranges definition
> > +----------------------
> > +
> > +The named pin groups of GPIO ports can be used to define GPIO ranges as
> > +explained in Documentation/devicetree/bindings/gpio/gpio.txt.
>
> I wouldn't mention that here; the GPIO node contains the gpio-ranges
> property, not the pin controller node. Hence, the binding for the GPIO
> DT node should describe the property, not the binding for this node.
>
> > +Example
> > +-------
> > +
> > +iomux: iomux@FF10601c {
> > + compatible = "abilis,tb10x-iomux";
> > + reg = <0xFF10601c 0x4>;
> > + pctl_gpio_a: pctl-gpio-a {
> > + pingrp = "gpioa_pins";
> > + };
> > + pctl_uart0: pctl-uart0 {
> > + pingrp = "uart0_pins";
> > + };
> > +};
>
> The two nodes pctl-gpio-a and pctl-uart0 seem to be missing data. The
> idea here is that you define nodes that says:
>
> * This node applies to these pin(s)/group(s).
> * Select mux function X on those pins/groups and/or apply these pin
> configuration options to those pins/groups.
>
> The examples above don't include any mux/config options, nor does the
> binding say how to do specify them.
>
> The set of pin groups defined by this binding should correspond directly
> to the set of pin groups that actually exist in HW. So, if you have 3
> pin groups (A, B, C) in HW each of which has two mux functions (X, Y),
> your DT binding should define just 3 pin groups (A, B, C), not 6 (A_X,
> A_Y, B_X, B_Y, C_X, C_Y). In other words, the pin group name shouldn't
> imply the mux function.

Can we consider it as agreed now that this implementation is acceptable
for the TB10x pin controller?

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-06-26 17:33:24

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 1/4] Make non-linear GPIO ranges accesible from gpiolib

On 06/26/2013 05:42 AM, Christian Ruppert wrote:
> On Wed, Jun 19, 2013 at 12:15:13PM -0600, Stephen Warren wrote:
>> On 06/19/2013 06:03 AM, Linus Walleij wrote:
>>> On Tue, Jun 18, 2013 at 11:29 AM, Christian Ruppert
>>> <[email protected]> wrote:
>>>
>>>> This patch adds the infrastructure required to register non-linear gpio
>>>> ranges through gpiolib and the standard GPIO device tree bindings.
>>>>
>>>> Signed-off-by: Christian Ruppert <[email protected]>
>>>
>>> I'm basically fine with this, but would like Stephen's ACK if possible.
>>>
>>>> +In addition, named groups of pins can be mapped to pin groups of a given
>>>> +pin controller:
>>>> +
>>>> + gpio_pio_g: gpio-controller@1480 {
>>>> + #gpio-cells = <2>;
>>>> + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
>>>> + reg = <0x1480 0x18>;
>>>> + gpio-controller;
>>>> + gpio-ranges = <&pinctrl1 0 0 0>, <&pinctrl2 3 0 0>;
>>>> + gpio-ranges-group-names = "foo", "bar";
>>>> + };
>>>> +
>>>> +where,
>>>> + &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
>>>> +
>>>> + The following value specifies the base GPIO offset of the pin range with
>>>> + respect to the GPIO controller's base. The remaining two values must be
>>>> + 0 to indicate that a named pin group should be used for the respective
>>>> + range. The number of pins in the range is the number of pins in the pin
>>>> + group.
>>>
>>> So while this works, these zeroes seem a bit awkward, but maybe
>>> it's the only way?
>>>
>>> I'm not good enough on device tree conventions, but isn't this possible:
>>>
>>> gpio-ranges = <&pinctrl1 0>, <&pinctrl2 3>;
>>> gpio-ranges-group-names = "foo", "bar";
>>>
>>> Since we don't have any #gpio-ranges-cells or anything like that I
>>> guess we can define this to have a flexible number of cells
>>> depending on use case?
>>
>> If we're willing to have gpio-ranges be either *all* group names, or
>> *all* IDs, we can define the format of gpio-ranges to have two cells
>> (phandle and GPIO number) if the property gpio-ranges-group-names
>> exists, but four cells (phandle, GPIO number, pin number, count)
>> otherwise. However, that's a little restrictive, since then what if one
>> GPIO controller is hooked to two different pinmux controllers, and you
>> want to use different formats for the references to each. A
>> #gpio-ranges-cells in the target of the phandle would allow this, but I
>> don't think this is something the pinctrl node should dictate to those
>> who reference it; it's quite legitimate for a GPIO node to use the pure
>> numeric mapping even if the pin controller happens to expose some pin
>> groups that allow you to do the mapping by name.
>
> I actually had a version of the patch with #gpio-range-cells specifying
> the format (one argument for named ranges, three for classical ranges)
> before deciding to use a separate property and sending that version. As
> I said in a previous mail, I don't have a preference which of the
> following three possibilities to use and would be grateful for some
> guidance (if it matters at all).
>
> 1.) separate property:
> gpio {
> gpio-ranges = <&pinctrl1 0 0 5>;
> gpio-range-groups = <&pinctrl2 5>;
> gpio-range-group-names = "gpios";
> };

Multiple properties seems a little like over-kill, although I agree it
makes specifying the format of the properties simplest.

> 2.) fixed number of three arguments:
> gpio {
> gpio-ranges = <&pinctrl1 0 0 5>, <&pinctrl2 5 0 0>;
> gpio-range-names = "", "gpios";
> };

This one seems fine to me. In many ways it's the simplest.

I guess I'd be OK with either (1) or (2) if someone else had a strong
opinion either way, although I'd tend towards (2) myself I think.

It's a pity properties don't carry type information in them, or we could
just put the string inline with the numbers in gpio-ranges:-(

> 3.) pinctrl-defined format.
> pinctrl1: pctl1 {
> #gpio-range-cells = <3>;
> };
> pinctrl2: pctl2 {
> #gpio-range-cells = <1>;
> };
> gpio {
> gpio-ranges = <&pinctrl1 0 0 5>, <&pinctrl2 5>;
> gpio-range-names = "", "gpios";
> };

I don't like that, because the pin controller node shouldn't determine
the format of the gpio-ranges entries here; a DT author would always
have the choice to use purely numerical values in gpio-ranges even if
the pinctrl node's binding did actually define named pin groups that
would allow you to use group names. Hence, the concept of the pinctrl
node having a #gpio-range-cells property seems wrong to me.

2013-06-26 17:35:02

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 1/4] Make non-linear GPIO ranges accesible from gpiolib

On 06/26/2013 05:46 AM, Christian Ruppert wrote:
> On Wed, Jun 19, 2013 at 04:27:44PM -0600, Stephen Warren wrote:
>> On 06/18/2013 03:29 AM, Christian Ruppert wrote:
>>> This patch adds the infrastructure required to register non-linear gpio
>>> ranges through gpiolib and the standard GPIO device tree bindings.
>>
>> I review this in case we decide to go with it anyway.
>>
>>> diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
>>
>>> +In addition, named groups of pins can be mapped to pin groups of a given
>>> +pin controller:
>>> +
>>> + gpio_pio_g: gpio-controller@1480 {
>>> + #gpio-cells = <2>;
>>> + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
>>> + reg = <0x1480 0x18>;
>>> + gpio-controller;
>>> + gpio-ranges = <&pinctrl1 0 0 0>, <&pinctrl2 3 0 0>;
>>> + gpio-ranges-group-names = "foo", "bar";
>>> + };
>>> +
>>> +where,
>>> + &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
>>> +
>>> + The following value specifies the base GPIO offset of the pin range with
>>> + respect to the GPIO controller's base. The remaining two values must be
>>> + 0 to indicate that a named pin group should be used for the respective
>>> + range. The number of pins in the range is the number of pins in the pin
>>> + group.
>>
>> It'd be good to re-write this section in a similar style to the cleanup
>> patches that I sent for the existing gpio-ranges documentation. That
>> makes the format description more of a raw syntax than English text.
>
> can you please point me to some place where I can find those patches on
> the web?

The start of the thread is at:

https://lists.ozlabs.org/pipermail/devicetree-discuss/2013-June/thread.html#35462

Patch 1/7 has already been applied. 2/7 and on are really the conversion.

2013-06-26 17:40:48

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On 06/26/2013 05:50 AM, Christian Ruppert wrote:
> On Wed, Jun 19, 2013 at 04:35:14PM -0600, Stephen Warren wrote:
>> On 06/18/2013 03:29 AM, Christian Ruppert wrote:
>>> The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
>>> Used to control the pinmux and is a prerequisite for the GPIO driver.
>>
>>> diff --git a/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
>>
>>> +Port definitions
>>> +----------------
>>> +
>>> +Ports are defined (and referenced) by sub-nodes of the pin controller. Every
>>> +sub-node defines exactly one port (i.e. a set of pins). Ports are predefined
>>> +as named pin groups inside the pin controller driver and these names are used
>>> +to associate pin group predefinitions to pin controller sub-nodes.
>>> +
>>> +Required port definition subnode properties:
>>> + - pingrp: should be set to the name of the port's pin group.
>>
>> This seems odd.... More on that where I comment on the example.
>>
>>> +The following pin groups are available:
>>> + - GPIO ports: gpioa_pins, gpiob_pins, gpioc_pins, gpiod_pins, gpioe_pins,
>>> + gpiof_pins, gpiog_pins, gpioh_pins, gpioi_pins, gpioj_pins,
>>> + gpiok_pins, gpiol_pins, gpiom_pins, gpion_pins
>> ...
>>> + - JTAG: jtag_pins
>>
>> I'd suggest removing "_pins" from all those names, since it's the same
>> in all names and hence isn't necessary.
>>
>>> +GPIO ranges definition
>>> +----------------------
>>> +
>>> +The named pin groups of GPIO ports can be used to define GPIO ranges as
>>> +explained in Documentation/devicetree/bindings/gpio/gpio.txt.
>>
>> I wouldn't mention that here; the GPIO node contains the gpio-ranges
>> property, not the pin controller node. Hence, the binding for the GPIO
>> DT node should describe the property, not the binding for this node.
>>
>>> +Example
>>> +-------
>>> +
>>> +iomux: iomux@FF10601c {
>>> + compatible = "abilis,tb10x-iomux";
>>> + reg = <0xFF10601c 0x4>;
>>> + pctl_gpio_a: pctl-gpio-a {
>>> + pingrp = "gpioa_pins";
>>> + };
>>> + pctl_uart0: pctl-uart0 {
>>> + pingrp = "uart0_pins";
>>> + };
>>> +};
>>
>> The two nodes pctl-gpio-a and pctl-uart0 seem to be missing data. The
>> idea here is that you define nodes that says:
>>
>> * This node applies to these pin(s)/group(s).
>> * Select mux function X on those pins/groups and/or apply these pin
>> configuration options to those pins/groups.
>>
>> The examples above don't include any mux/config options, nor does the
>> binding say how to do specify them.
>>
>> The set of pin groups defined by this binding should correspond directly
>> to the set of pin groups that actually exist in HW. So, if you have 3
>> pin groups (A, B, C) in HW each of which has two mux functions (X, Y),
>> your DT binding should define just 3 pin groups (A, B, C), not 6 (A_X,
>> A_Y, B_X, B_Y, C_X, C_Y). In other words, the pin group name shouldn't
>> imply the mux function.
>
> Can we consider it as agreed now that this implementation is acceptable
> for the TB10x pin controller?

There are two issues here:

1) What is a pin group:

1a) Must it solely represent a group of pins that actually exists in HW
(e.g. it's an RTL port, or a set of pins all controlled at once by a
single bit/field in a register)

1b) A SW-defined group of pins, simply because it's convenient to talk
about that set of pins at once, even though HW doesn't impose that those
pins are in a group in any way.

Defining groups for either of those reasons is fine, although this is
the area where my preference and LinusW's differ.

2) Can groups represent just a set of pins, or can it also imply that a
particular mux function is selected on that group?

I believe that both LinusW and I are in agreement that a group is simply
a list/set/group of pins. You select mux functions onto groups. A
groups's definition can't imply that a particular mux function is
selected onto it.

If we don't follow this rule, then you end up with a combinatorial
explosion of groups; the cross-product of all possible groups of pins
vs. the mux function to select on them, rather than simply having a list
of groups of pins, which is a much smaller set/list.

So, in the DT example above, I still believe that you need an extra
property that defines which mux function to select onto the specified
group. The group name can't imply this, so there needs to be some way of
specifying it.

2013-07-05 09:49:44

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Wed, Jun 26, 2013 at 11:40:42AM -0600, Stephen Warren wrote:
> On 06/26/2013 05:50 AM, Christian Ruppert wrote:
> > On Wed, Jun 19, 2013 at 04:35:14PM -0600, Stephen Warren wrote:
> >> On 06/18/2013 03:29 AM, Christian Ruppert wrote:
> [...]
> >>> +Example
> >>> +-------
> >>> +
> >>> +iomux: iomux@FF10601c {
> >>> + compatible = "abilis,tb10x-iomux";
> >>> + reg = <0xFF10601c 0x4>;
> >>> + pctl_gpio_a: pctl-gpio-a {
> >>> + pingrp = "gpioa_pins";
> >>> + };
> >>> + pctl_uart0: pctl-uart0 {
> >>> + pingrp = "uart0_pins";
> >>> + };
> >>> +};
> >>
> >> The two nodes pctl-gpio-a and pctl-uart0 seem to be missing data. The
> >> idea here is that you define nodes that says:
> >>
> >> * This node applies to these pin(s)/group(s).
> >> * Select mux function X on those pins/groups and/or apply these pin
> >> configuration options to those pins/groups.
> >>
> >> The examples above don't include any mux/config options, nor does the
> >> binding say how to do specify them.
> >>
> >> The set of pin groups defined by this binding should correspond directly
> >> to the set of pin groups that actually exist in HW. So, if you have 3
> >> pin groups (A, B, C) in HW each of which has two mux functions (X, Y),
> >> your DT binding should define just 3 pin groups (A, B, C), not 6 (A_X,
> >> A_Y, B_X, B_Y, C_X, C_Y). In other words, the pin group name shouldn't
> >> imply the mux function.
> >
> > Can we consider it as agreed now that this implementation is acceptable
> > for the TB10x pin controller?
>
> There are two issues here:
>
> 1) What is a pin group:
>
> 1a) Must it solely represent a group of pins that actually exists in HW
> (e.g. it's an RTL port, or a set of pins all controlled at once by a
> single bit/field in a register)
>
> 1b) A SW-defined group of pins, simply because it's convenient to talk
> about that set of pins at once, even though HW doesn't impose that those
> pins are in a group in any way.
>
> Defining groups for either of those reasons is fine, although this is
> the area where my preference and LinusW's differ.
>
> 2) Can groups represent just a set of pins, or can it also imply that a
> particular mux function is selected on that group?
>
> I believe that both LinusW and I are in agreement that a group is simply
> a list/set/group of pins. You select mux functions onto groups. A
> groups's definition can't imply that a particular mux function is
> selected onto it.
>
> If we don't follow this rule, then you end up with a combinatorial
> explosion of groups; the cross-product of all possible groups of pins
> vs. the mux function to select on them, rather than simply having a list
> of groups of pins, which is a much smaller set/list.
>
> So, in the DT example above, I still believe that you need an extra
> property that defines which mux function to select onto the specified
> group. The group name can't imply this, so there needs to be some way of
> specifying it.

In your opinion, would something in the lines of

pctl_spi1: pctl-spi1 {
abilis,pingrp = "spi1";
abilis,ioport = <4>; /* spi1 is routed to port4 inside the
pin controller */
abilis,ioconf = <1>; /* spi1 is available in configuration 1
of that port. */
};

be more appropriate? This nicely defines which pins of which port are
requested and what is the configuration required (and it reflects our
hardware very well).
In future, this could even be extended to allow several alternative
configurations for a given function, e.g.

pctl_spi3: pctl-spi3 {
abilis,pingrp = "spi3";
abilis,ioport = <6>;
abilis,ioconf = <0 3>; /* spi3 is available in both
configurations 0 and 3. Depending on
what other functions are requested, the
pinctrl driver can choose either of the
two. */
};

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-07-05 18:41:00

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On 07/05/2013 03:49 AM, Christian Ruppert wrote:
> On Wed, Jun 26, 2013 at 11:40:42AM -0600, Stephen Warren wrote:
>> On 06/26/2013 05:50 AM, Christian Ruppert wrote:
>>> On Wed, Jun 19, 2013 at 04:35:14PM -0600, Stephen Warren wrote:
>>>> On 06/18/2013 03:29 AM, Christian Ruppert wrote:
>> [...]
>>>>> +Example
>>>>> +-------
>>>>> +
>>>>> +iomux: iomux@FF10601c {
>>>>> + compatible = "abilis,tb10x-iomux";
>>>>> + reg = <0xFF10601c 0x4>;
>>>>> + pctl_gpio_a: pctl-gpio-a {
>>>>> + pingrp = "gpioa_pins";
>>>>> + };
>>>>> + pctl_uart0: pctl-uart0 {
>>>>> + pingrp = "uart0_pins";
>>>>> + };
>>>>> +};
>>>>
>>>> The two nodes pctl-gpio-a and pctl-uart0 seem to be missing data. The
>>>> idea here is that you define nodes that says:
>>>>
>>>> * This node applies to these pin(s)/group(s).
>>>> * Select mux function X on those pins/groups and/or apply these pin
>>>> configuration options to those pins/groups.
>>>>
>>>> The examples above don't include any mux/config options, nor does the
>>>> binding say how to do specify them.
>>>>
>>>> The set of pin groups defined by this binding should correspond directly
>>>> to the set of pin groups that actually exist in HW. So, if you have 3
>>>> pin groups (A, B, C) in HW each of which has two mux functions (X, Y),
>>>> your DT binding should define just 3 pin groups (A, B, C), not 6 (A_X,
>>>> A_Y, B_X, B_Y, C_X, C_Y). In other words, the pin group name shouldn't
>>>> imply the mux function.
>>>
>>> Can we consider it as agreed now that this implementation is acceptable
>>> for the TB10x pin controller?
>>
>> There are two issues here:
>>
>> 1) What is a pin group:
>>
>> 1a) Must it solely represent a group of pins that actually exists in HW
>> (e.g. it's an RTL port, or a set of pins all controlled at once by a
>> single bit/field in a register)
>>
>> 1b) A SW-defined group of pins, simply because it's convenient to talk
>> about that set of pins at once, even though HW doesn't impose that those
>> pins are in a group in any way.
>>
>> Defining groups for either of those reasons is fine, although this is
>> the area where my preference and LinusW's differ.
>>
>> 2) Can groups represent just a set of pins, or can it also imply that a
>> particular mux function is selected on that group?
>>
>> I believe that both LinusW and I are in agreement that a group is simply
>> a list/set/group of pins. You select mux functions onto groups. A
>> groups's definition can't imply that a particular mux function is
>> selected onto it.
>>
>> If we don't follow this rule, then you end up with a combinatorial
>> explosion of groups; the cross-product of all possible groups of pins
>> vs. the mux function to select on them, rather than simply having a list
>> of groups of pins, which is a much smaller set/list.
>>
>> So, in the DT example above, I still believe that you need an extra
>> property that defines which mux function to select onto the specified
>> group. The group name can't imply this, so there needs to be some way of
>> specifying it.
>
> In your opinion, would something in the lines of
>
> pctl_spi1: pctl-spi1 {
> abilis,pingrp = "spi1";

So that defines a list of pins.

> abilis,ioport = <4>; /* spi1 is routed to port4 inside the
> pin controller */

I assume that defines the mux function value; the value that's
programmed into the HW register to select which HW module's signals are
routed out to the pins specified by abilis,pingrp.

> abilis,ioconf = <1>; /* spi1 is available in configuration 1
> of that port. */

But I don't understand what that is. ...

...
> In future, this could even be extended to allow several alternative
> configurations for a given function, e.g.
>
> pctl_spi3: pctl-spi3 {
> abilis,pingrp = "spi3";
> abilis,ioport = <6>;
> abilis,ioconf = <0 3>; /* spi3 is available in both
> configurations 0 and 3. Depending on
> what other functions are requested, the
> pinctrl driver can choose either of the
> two. */

... especially if you're talking about "spi3 being available in multiple
configurations". What's a configuration?

2013-07-08 13:03:57

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Fri, Jul 05, 2013 at 12:40:55PM -0600, Stephen Warren wrote:
> On 07/05/2013 03:49 AM, Christian Ruppert wrote:
> > On Wed, Jun 26, 2013 at 11:40:42AM -0600, Stephen Warren wrote:
> >> On 06/26/2013 05:50 AM, Christian Ruppert wrote:
> >>> On Wed, Jun 19, 2013 at 04:35:14PM -0600, Stephen Warren wrote:
> >>>> On 06/18/2013 03:29 AM, Christian Ruppert wrote:
> >> [...]
> >>>>> +Example
> >>>>> +-------
> >>>>> +
> >>>>> +iomux: iomux@FF10601c {
> >>>>> + compatible = "abilis,tb10x-iomux";
> >>>>> + reg = <0xFF10601c 0x4>;
> >>>>> + pctl_gpio_a: pctl-gpio-a {
> >>>>> + pingrp = "gpioa_pins";
> >>>>> + };
> >>>>> + pctl_uart0: pctl-uart0 {
> >>>>> + pingrp = "uart0_pins";
> >>>>> + };
> >>>>> +};
> >>>>
> >>>> The two nodes pctl-gpio-a and pctl-uart0 seem to be missing data. The
> >>>> idea here is that you define nodes that says:
> >>>>
> >>>> * This node applies to these pin(s)/group(s).
> >>>> * Select mux function X on those pins/groups and/or apply these pin
> >>>> configuration options to those pins/groups.
> >>>>
> >>>> The examples above don't include any mux/config options, nor does the
> >>>> binding say how to do specify them.
> >>>>
> >>>> The set of pin groups defined by this binding should correspond directly
> >>>> to the set of pin groups that actually exist in HW. So, if you have 3
> >>>> pin groups (A, B, C) in HW each of which has two mux functions (X, Y),
> >>>> your DT binding should define just 3 pin groups (A, B, C), not 6 (A_X,
> >>>> A_Y, B_X, B_Y, C_X, C_Y). In other words, the pin group name shouldn't
> >>>> imply the mux function.
> >>>
> >>> Can we consider it as agreed now that this implementation is acceptable
> >>> for the TB10x pin controller?
> >>
> >> There are two issues here:
> >>
> >> 1) What is a pin group:
> >>
> >> 1a) Must it solely represent a group of pins that actually exists in HW
> >> (e.g. it's an RTL port, or a set of pins all controlled at once by a
> >> single bit/field in a register)
> >>
> >> 1b) A SW-defined group of pins, simply because it's convenient to talk
> >> about that set of pins at once, even though HW doesn't impose that those
> >> pins are in a group in any way.
> >>
> >> Defining groups for either of those reasons is fine, although this is
> >> the area where my preference and LinusW's differ.
> >>
> >> 2) Can groups represent just a set of pins, or can it also imply that a
> >> particular mux function is selected on that group?
> >>
> >> I believe that both LinusW and I are in agreement that a group is simply
> >> a list/set/group of pins. You select mux functions onto groups. A
> >> groups's definition can't imply that a particular mux function is
> >> selected onto it.
> >>
> >> If we don't follow this rule, then you end up with a combinatorial
> >> explosion of groups; the cross-product of all possible groups of pins
> >> vs. the mux function to select on them, rather than simply having a list
> >> of groups of pins, which is a much smaller set/list.
> >>
> >> So, in the DT example above, I still believe that you need an extra
> >> property that defines which mux function to select onto the specified
> >> group. The group name can't imply this, so there needs to be some way of
> >> specifying it.
> >
> > In your opinion, would something in the lines of
> >
> > pctl_spi1: pctl-spi1 {
> > abilis,pingrp = "spi1";
>
> So that defines a list of pins.
>
> > abilis,ioport = <4>; /* spi1 is routed to port4 inside the
> > pin controller */
>
> I assume that defines the mux function value; the value that's
> programmed into the HW register to select which HW module's signals are
> routed out to the pins specified by abilis,pingrp.

No, it actually defines the mux inside the pin controller.

> > abilis,ioconf = <1>; /* spi1 is available in configuration 1
> > of that port. */
>
> But I don't understand what that is. ...

This one defines the mux setting/configuration.

> ...
> > In future, this could even be extended to allow several alternative
> > configurations for a given function, e.g.
> >
> > pctl_spi3: pctl-spi3 {
> > abilis,pingrp = "spi3";
> > abilis,ioport = <6>;
> > abilis,ioconf = <0 3>; /* spi3 is available in both
> > configurations 0 and 3. Depending on
> > what other functions are requested, the
> > pinctrl driver can choose either of the
> > two. */
>
> ... especially if you're talking about "spi3 being available in multiple
> configurations". What's a configuration?

OK, a small drawing of our hardware should make this clear, let's take
an imaginary example of one port with 10 pins, one i2c interface, one
spi interface and one GPIO bank:

| mux N-1|
+........+
| | 2
| +--/-- i2c
| |
10 | | 4
Pins --/--+ mux N +--/-- spi
| |
| | 10
| +--/-- GPIO
| |
+........+
| mux N+1|

This example shows the mux N inside the pin controller. It controls
all pins associated to port N through a single register value M. Let's
assume the pins are configured as follows in function of the register
value:

pin M=0 M=1 M=2 M=3
0 GPIO0 SPI_MISO GPIO0 SPI_MISO
1 GPIO1 SPI_MOSI GPIO1 SPI_MOSI
2 GPIO2 SPI_CK GPIO2 SPI_CK
3 GPIO3 SPI_CS GPIO3 SPI_CS
4 GPIO4 GPIO4 GPIO4 GPIO4
5 GPIO5 GPIO5 GPIO5 GPIO5
6 GPIO6 GPIO6 GPIO6 GPIO6
7 GPIO7 GPIO7 GPIO7 GPIO7
8 GPIO8 GPIO8 I2C_SDA I2C_SDA
9 GPIO9 GPIO9 I2C_SCL I2C_SCL

We now have three pin groups defined, corresponding to the chip-side
ports of the pin controller:
GPIO = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
SPI = {0, 1, 2, 3}
I2C = {8, 9}

abilis,pingrp now specifies one of the three pin groups. Note that I2C
and SPI can be requested independently in a completely orthogonal
manner: The information if I2C is reqired or not is confined to
the I2C request and does not leak into the SPI request as would
be the case if we configured the entire port at the same time.
abilis,ioport specifies N.
abilis,ioconf specifies M.

The above example would be described as follows:

portN_gpio: portN_gpio { // This is optional, GPIO mode is the default
abilis,pingrp = "GPIO";
abilis,ioport = <N>;
abilis,ioconf = <0>;
};
portN_SPI: portN_SPI {
abilis,pingrp = "GPIO";
abilis,ioport = <N>;
abilis,ioconf = <1 3>;
};
portN_I2C: portN_I2C {
abilis,pingrp = "GPIO";
abilis,ioport = <N>;
abilis,ioconf = <2 3>;
};

/* ... */

spi {
/* ... */
pinctrl-names = "default";
pinctrl-0 = <&portN_SPI>; /* No need to specify here if I2C is
required as well */
};
i2c {
/* ... */
pinctrl-names = "default";
pinctrl-0 = <&portN_I2C>; /* No need to specify here if SPI is
required as well */
};

The choice between the actual mode to be applied is done inside the pin
controller driver and depends which of SPI and I2C are requested. Also,
in all cases, the pin controller is perfectly aware which of the pins
were previously requested for hardware interfaces and which are
available as GPIOs. Interface - GPIO conflicts are thus easy to manage.

One could argue that the port to which a given interface is connected
(as well as the register values for which this interface is available)
are defined by the pin controller's internal wiring. This is why
abilis,ioport and abilis,ioconf were not part of the initial proposal. I
don't mind putting this information in the device tree, however, since
it is already part of our chip documentation.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-07-10 19:27:58

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On 07/08/2013 07:02 AM, Christian Ruppert wrote:
...
> OK, a small drawing of our hardware should make this clear, let's take
> an imaginary example of one port with 10 pins, one i2c interface, one
> spi interface and one GPIO bank:
>
> | mux N-1|
> +........+
> | | 2
> | +--/-- i2c
> | |
> 10 | | 4
> Pins --/--+ mux N +--/-- spi
> | |
> | | 10
> | +--/-- GPIO
> | |
> +........+
> | mux N+1|
>
> This example shows the mux N inside the pin controller. It controls
> all pins associated to port N through a single register value M. Let's
> assume the pins are configured as follows in function of the register
> value:
>
> pin M=0 M=1 M=2 M=3
> 0 GPIO0 SPI_MISO GPIO0 SPI_MISO
> 1 GPIO1 SPI_MOSI GPIO1 SPI_MOSI
> 2 GPIO2 SPI_CK GPIO2 SPI_CK
> 3 GPIO3 SPI_CS GPIO3 SPI_CS
> 4 GPIO4 GPIO4 GPIO4 GPIO4
> 5 GPIO5 GPIO5 GPIO5 GPIO5
> 6 GPIO6 GPIO6 GPIO6 GPIO6
> 7 GPIO7 GPIO7 GPIO7 GPIO7
> 8 GPIO8 GPIO8 I2C_SDA I2C_SDA
> 9 GPIO9 GPIO9 I2C_SCL I2C_SCL


In that scenario, in the language of Linux's pinctrl subsystem, what you
have is:

10 pins, named 0..9
1 pin group, named perhaps "mux N".
4 different functions; values M==0, 1, 2, 3.

> We now have three pin groups defined, corresponding to the chip-side
> ports of the pin controller:
> GPIO = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
> SPI = {0, 1, 2, 3}
> I2C = {8, 9}

You would usually only define pin groups for the pin/ball/package side
of the pinmux HW. IIRC, you're also wanting to define pin groups for the
intra-chip side of the pinmux HW. However, you're not muxing functions
onto those pingroups; they're just there to help with naming the
GPIO<->pinmux mapping. You only mux functions onto the pin/ball/package
side pins/pingroups.


> abilis,pingrp now specifies one of the three pin groups. Note that I2C
> and SPI can be requested independently in a completely orthogonal
> manner: The information if I2C is reqired or not is confined to
> the I2C request and does not leak into the SPI request as would
> be the case if we configured the entire port at the same time.

The pingrp should represent the pin/ball/package side pins/groups. In
this case, it should specify "N".

> abilis,ioport specifies N.

That is replaced be pingrp.

> abilis,ioconf specifies M.

That'd be better named "function" or something like that, in order to
indicate that it specifies which function is mux'd onto the specified
pin(s)/pingroup(s).

2013-07-16 08:48:33

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Wed, Jul 10, 2013 at 01:27:52PM -0600, Stephen Warren wrote:
> On 07/08/2013 07:02 AM, Christian Ruppert wrote:
> ...
> > OK, a small drawing of our hardware should make this clear, let's take
> > an imaginary example of one port with 10 pins, one i2c interface, one
> > spi interface and one GPIO bank:
> >
> > | mux N-1|
> > +........+
> > | | 2
> > | +--/-- i2c
> > | |
> > 10 | | 4
> > Pins --/--+ mux N +--/-- spi
> > | |
> > | | 10
> > | +--/-- GPIO
> > | |
> > +........+
> > | mux N+1|
> >
> > This example shows the mux N inside the pin controller. It controls
> > all pins associated to port N through a single register value M. Let's
> > assume the pins are configured as follows in function of the register
> > value:
> >
> > pin M=0 M=1 M=2 M=3
> > 0 GPIO0 SPI_MISO GPIO0 SPI_MISO
> > 1 GPIO1 SPI_MOSI GPIO1 SPI_MOSI
> > 2 GPIO2 SPI_CK GPIO2 SPI_CK
> > 3 GPIO3 SPI_CS GPIO3 SPI_CS
> > 4 GPIO4 GPIO4 GPIO4 GPIO4
> > 5 GPIO5 GPIO5 GPIO5 GPIO5
> > 6 GPIO6 GPIO6 GPIO6 GPIO6
> > 7 GPIO7 GPIO7 GPIO7 GPIO7
> > 8 GPIO8 GPIO8 I2C_SDA I2C_SDA
> > 9 GPIO9 GPIO9 I2C_SCL I2C_SCL
>
>
> In that scenario, in the language of Linux's pinctrl subsystem, what you
> have is:
>
> 10 pins, named 0..9
> 1 pin group, named perhaps "mux N".
> 4 different functions; values M==0, 1, 2, 3.
>
> > We now have three pin groups defined, corresponding to the chip-side
> > ports of the pin controller:
> > GPIO = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
> > SPI = {0, 1, 2, 3}
> > I2C = {8, 9}
>
> You would usually only define pin groups for the pin/ball/package side
> of the pinmux HW. IIRC, you're also wanting to define pin groups for the
> intra-chip side of the pinmux HW. However, you're not muxing functions
> onto those pingroups; they're just there to help with naming the
> GPIO<->pinmux mapping. You only mux functions onto the pin/ball/package
> side pins/pingroups.

Well, the GPIO<->pinmux mapping is not the only reason for defining
these groups wrt. the chip-side of the pin controller. The other reasons
are:
- Make different interfaces on the same MUX orthogonal wrt. each
other, i.e. make it possible to request one independently of the
other. In the example above, SPI and I2C can be requested completely
independently and the pin controller driver decides which mode to
use.
- Make pin allocation more fine-grained (in the example above, only
pins 0-4 are "allocated" in case SPI is requested). This makes
GPIO<->interface pin conflict management more natural.
BTW: When managing conflicts between hardware interfaces and GPIOs,
this scheme actually leads to _less_ pin groups than if we defined
one group at the pin side and four different modes:
chip-side groups: GPIO, SPI, I2C
=> 3 groups.
pin-side groups: MUX_N, GPIOS_M0, GPIOS_M1, GPIOS_M2, GPIOS_M3
in our special case MUX_N and GPIOS_M0 are the
same.
=> 4 groups (or 5 in the general case).
It is true that GPIO_Mx don't need to be named but they need to be
defined inside the driver nevertheless.

> > abilis,pingrp now specifies one of the three pin groups. Note that I2C
> > and SPI can be requested independently in a completely orthogonal
> > manner: The information if I2C is reqired or not is confined to
> > the I2C request and does not leak into the SPI request as would
> > be the case if we configured the entire port at the same time.
>
> The pingrp should represent the pin/ball/package side pins/groups. In
> this case, it should specify "N".
>
> > abilis,ioport specifies N.
>
> That is replaced be pingrp.
>
> > abilis,ioconf specifies M.
>
> That'd be better named "function" or something like that, in order to
> indicate that it specifies which function is mux'd onto the specified
> pin(s)/pingroup(s).

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-07-16 16:04:11

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On 07/16/2013 02:47 AM, Christian Ruppert wrote:
> On Wed, Jul 10, 2013 at 01:27:52PM -0600, Stephen Warren wrote:
>> On 07/08/2013 07:02 AM, Christian Ruppert wrote:
>> ...
>>> OK, a small drawing of our hardware should make this clear, let's take
>>> an imaginary example of one port with 10 pins, one i2c interface, one
>>> spi interface and one GPIO bank:
>>>
>>> | mux N-1|
>>> +........+
>>> | | 2
>>> | +--/-- i2c
>>> | |
>>> 10 | | 4
>>> Pins --/--+ mux N +--/-- spi
>>> | |
>>> | | 10
>>> | +--/-- GPIO
>>> | |
>>> +........+
>>> | mux N+1|
>>>
>>> This example shows the mux N inside the pin controller. It controls
>>> all pins associated to port N through a single register value M. Let's
>>> assume the pins are configured as follows in function of the register
>>> value:
>>>
>>> pin M=0 M=1 M=2 M=3
>>> 0 GPIO0 SPI_MISO GPIO0 SPI_MISO
>>> 1 GPIO1 SPI_MOSI GPIO1 SPI_MOSI
>>> 2 GPIO2 SPI_CK GPIO2 SPI_CK
>>> 3 GPIO3 SPI_CS GPIO3 SPI_CS
>>> 4 GPIO4 GPIO4 GPIO4 GPIO4
>>> 5 GPIO5 GPIO5 GPIO5 GPIO5
>>> 6 GPIO6 GPIO6 GPIO6 GPIO6
>>> 7 GPIO7 GPIO7 GPIO7 GPIO7
>>> 8 GPIO8 GPIO8 I2C_SDA I2C_SDA
>>> 9 GPIO9 GPIO9 I2C_SCL I2C_SCL
>>
>>
>> In that scenario, in the language of Linux's pinctrl subsystem, what you
>> have is:
>>
>> 10 pins, named 0..9
>> 1 pin group, named perhaps "mux N".
>> 4 different functions; values M==0, 1, 2, 3.
>>
>>> We now have three pin groups defined, corresponding to the chip-side
>>> ports of the pin controller:
>>> GPIO = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> SPI = {0, 1, 2, 3}
>>> I2C = {8, 9}
>>
>> You would usually only define pin groups for the pin/ball/package side
>> of the pinmux HW. IIRC, you're also wanting to define pin groups for the
>> intra-chip side of the pinmux HW. However, you're not muxing functions
>> onto those pingroups; they're just there to help with naming the
>> GPIO<->pinmux mapping. You only mux functions onto the pin/ball/package
>> side pins/pingroups.
>
> Well, the GPIO<->pinmux mapping is not the only reason for defining
> these groups wrt. the chip-side of the pin controller. The other reasons
> are:
> - Make different interfaces on the same MUX orthogonal wrt. each
> other, i.e. make it possible to request one independently of the
> other. In the example above, SPI and I2C can be requested completely
> independently and the pin controller driver decides which mode to
> use.

But the pinctrl subsystem and bindings don't have any concept of that;
what gets requested is the pins on the chip, or the "outer" side of the
pin controller. There's no concept of resource management for the
"inside" of the pin controller.

> - Make pin allocation more fine-grained (in the example above, only
> pins 0-4 are "allocated" in case SPI is requested). This makes
> GPIO<->interface pin conflict management more natural.

I think you'd want to either:

a) Just deal with this in the driver; it knows the HW, and it knows
which mux function is selected for each mux, and hence knows exactly
which pins can be requested as GPIOs for each combination, and can
therefore allow/disallow any GPIO request or mux function change.

b) Extend the pinctrl core to know about this explicitly, and pass
information to the pinctrl core. Presumably, for each combination of
(pingroup, mux function), you'd need a list or bitmask indicating which
pins within the pingroup are actually used. Then, the pinctrl core can
perform all the validation. If you do this, you don't need to invent new
pinctrl groups in order to try and shoe-horn this into pinctrl.

2013-07-18 16:07:39

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Tue, Jul 16, 2013 at 10:04:03AM -0600, Stephen Warren wrote:
> On 07/16/2013 02:47 AM, Christian Ruppert wrote:
> > On Wed, Jul 10, 2013 at 01:27:52PM -0600, Stephen Warren wrote:
> >> On 07/08/2013 07:02 AM, Christian Ruppert wrote:
> >> ...
> >>> OK, a small drawing of our hardware should make this clear, let's take
> >>> an imaginary example of one port with 10 pins, one i2c interface, one
> >>> spi interface and one GPIO bank:
> >>>
> >>> | mux N-1|
> >>> +........+
> >>> | | 2
> >>> | +--/-- i2c
> >>> | |
> >>> 10 | | 4
> >>> Pins --/--+ mux N +--/-- spi
> >>> | |
> >>> | | 10
> >>> | +--/-- GPIO
> >>> | |
> >>> +........+
> >>> | mux N+1|
> >>>
> >>> This example shows the mux N inside the pin controller. It controls
> >>> all pins associated to port N through a single register value M. Let's
> >>> assume the pins are configured as follows in function of the register
> >>> value:
> >>>
> >>> pin M=0 M=1 M=2 M=3
> >>> 0 GPIO0 SPI_MISO GPIO0 SPI_MISO
> >>> 1 GPIO1 SPI_MOSI GPIO1 SPI_MOSI
> >>> 2 GPIO2 SPI_CK GPIO2 SPI_CK
> >>> 3 GPIO3 SPI_CS GPIO3 SPI_CS
> >>> 4 GPIO4 GPIO4 GPIO4 GPIO4
> >>> 5 GPIO5 GPIO5 GPIO5 GPIO5
> >>> 6 GPIO6 GPIO6 GPIO6 GPIO6
> >>> 7 GPIO7 GPIO7 GPIO7 GPIO7
> >>> 8 GPIO8 GPIO8 I2C_SDA I2C_SDA
> >>> 9 GPIO9 GPIO9 I2C_SCL I2C_SCL
> >>
> >>
> >> In that scenario, in the language of Linux's pinctrl subsystem, what you
> >> have is:
> >>
> >> 10 pins, named 0..9
> >> 1 pin group, named perhaps "mux N".
> >> 4 different functions; values M==0, 1, 2, 3.
> >>
> >>> We now have three pin groups defined, corresponding to the chip-side
> >>> ports of the pin controller:
> >>> GPIO = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
> >>> SPI = {0, 1, 2, 3}
> >>> I2C = {8, 9}
> >>
> >> You would usually only define pin groups for the pin/ball/package side
> >> of the pinmux HW. IIRC, you're also wanting to define pin groups for the
> >> intra-chip side of the pinmux HW. However, you're not muxing functions
> >> onto those pingroups; they're just there to help with naming the
> >> GPIO<->pinmux mapping. You only mux functions onto the pin/ball/package
> >> side pins/pingroups.
> >
> > Well, the GPIO<->pinmux mapping is not the only reason for defining
> > these groups wrt. the chip-side of the pin controller. The other reasons
> > are:
> > - Make different interfaces on the same MUX orthogonal wrt. each
> > other, i.e. make it possible to request one independently of the
> > other. In the example above, SPI and I2C can be requested completely
> > independently and the pin controller driver decides which mode to
> > use.
>
> But the pinctrl subsystem and bindings don't have any concept of that;
> what gets requested is the pins on the chip, or the "outer" side of the
> pin controller. There's no concept of resource management for the
> "inside" of the pin controller.

Well, perhaps my definition of "inside"/"outside" pins was not quite
clear: The pin groups define the set of (kernel internal) pin numbers of
"outside" pins which are used by pin controller to map a given
interface. Inside pins are not numbered and the inside interfaces are
only used to determine which outside pins are part of the same group
(namely those for which the pin controller hardware provides a mux
connection to the same inside interface):

4
4 /|--/-- SPI
PINS[0..3] --/--|| 4
\|--/-- GPIO[0..3]

4
PINS[4..7] -----/------ GPIO[4..7]

2
2 /|--/-- I2C
PINS[8..9] --/--|| 2
\|--/-- GPIO[8..9]

Pins 0..3 are in the SPI group because on the "inside" they can be muxed
to the SPI interface.
Pins 8..9 are in the I2C group because on the "inside" they can be muxed
to the I2C interface.
Pins 0..9 are in the GPIO group because on the "inside" they can be
muxed to the GPIO controller.

All pin numbers are relative to the "outside", however, or conflict
management would not be possible. I hope this is more understandable
than my previous explanations.
Both muxes are controlled by the same register. In our overly simplistic
example this is not strictly necessary but in reality you might have pin
conflicts between the different interfaces.

> > - Make pin allocation more fine-grained (in the example above, only
> > pins 0-4 are "allocated" in case SPI is requested). This makes
> > GPIO<->interface pin conflict management more natural.
>
> I think you'd want to either:
>
> a) Just deal with this in the driver; it knows the HW, and it knows
> which mux function is selected for each mux, and hence knows exactly
> which pins can be requested as GPIOs for each combination, and can
> therefore allow/disallow any GPIO request or mux function change.

This is actually what is implemented today.
1) Functional conflicts inside the pin controller hardware are managed
by the driver:
. Conflicts between interfaces and GPIOs: As LinusW said in a
previous mail there are cases (not in TB10x but in other chips)
where it is possible to enable GPIO and another interface on the
same pin at the same time. In TB10x this is not possible and the
driver prevents that.
. Conflicts in which (non-GPIO) interfaces don't share pins but are
nevertheless interdependent, e.g. because they share the same
configuration register. The pinctrl core does not know about this
type of constraints.

2) Pure pin conflicts are managed by the pinctrl core:
. Conflicts between different interfaces which use the same pin.
. GPIO conflicts (the same GPIO cannot be requested twice etc.)

> b) Extend the pinctrl core to know about this explicitly, and pass
> information to the pinctrl core. Presumably, for each combination of
> (pingroup, mux function), you'd need a list or bitmask indicating which
> pins within the pingroup are actually used. Then, the pinctrl core can
> perform all the validation. If you do this, you don't need to invent new
> pinctrl groups in order to try and shoe-horn this into pinctrl.

After the discussion we had so far I'm not so sure if extending the
pinctrl system with this kind of features is a very good idea. In
pinctrl systems many constraints are chip-specific (or at least pin
controller specific) and I don't think it would be a good idea to imply
a given pin controller model for all drivers which use this framework.

In my opinion, the current system is flexible enough to implement
different pin controller models and as the proposed driver shows,
integration/extension of the pinctrl core mechanisms with
driver-specific mechanisms to reflect a specific hardware is quite
straight forward.

The only thing I could think of adding to the core is a way to query if
a given pin is already allocated as GPIO and/or "normal" pin to make
GPIO/interface conflict management easier but I guess that's not what
you mean?

Greetings,
Christian

2013-07-18 19:54:26

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On 07/18/2013 10:07 AM, Christian Ruppert wrote:
...
> Well, perhaps my definition of "inside"/"outside" pins was not quite
> clear: The pin groups define the set of (kernel internal) pin numbers of
> "outside" pins which are used by pin controller to map a given
> interface. Inside pins are not numbered and the inside interfaces are
> only used to determine which outside pins are part of the same group
> (namely those for which the pin controller hardware provides a mux
> connection to the same inside interface):
>
> 4
> 4 /|--/-- SPI
> PINS[0..3] --/--|| 4
> \|--/-- GPIO[0..3]
>
> 4
> PINS[4..7] -----/------ GPIO[4..7]
>
> 2
> 2 /|--/-- I2C
> PINS[8..9] --/--|| 2
> \|--/-- GPIO[8..9]
>
> Pins 0..3 are in the SPI group because on the "inside" they can be muxed
> to the SPI interface.
> Pins 8..9 are in the I2C group because on the "inside" they can be muxed
> to the I2C interface.
> Pins 0..9 are in the GPIO group because on the "inside" they can be
> muxed to the GPIO controller.
>
> All pin numbers are relative to the "outside", however, or conflict
> management would not be possible. I hope this is more understandable
> than my previous explanations.
> Both muxes are controlled by the same register. In our overly simplistic
> example this is not strictly necessary but in reality you might have pin
> conflicts between the different interfaces.

Same register, or same field/bits in that register?

If it's the same field/bits, I would expect to see the following pin groups:

1) PINS[0..3], PINS[8..9]
2) PINS[4..7]

... since those are the things that are independently muxable.
Otherwise, I'd expect to see the following groups:

1) PINS[0..3]
2) PINS[4..7]
3) PINS[8..9]

> After the discussion we had so far I'm not so sure if extending the
> pinctrl system with this kind of features is a very good idea.

That makes things simple:-)

One thing I still don't understand; in a previous mail, you'd mentioned
3 DT properties for configuring the pinmux; one represented the pin
group, one represented the mux function that was selected for that pin
group, and there was a third ("config"?) property. I still don't
understand that third property. I only see pins/pingroups and mux
functions in the diagram I quoted above.

2013-07-26 09:43:20

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Thu, Jul 18, 2013 at 01:54:18PM -0600, Stephen Warren wrote:
> On 07/18/2013 10:07 AM, Christian Ruppert wrote:
> ...
> > Well, perhaps my definition of "inside"/"outside" pins was not quite
> > clear: The pin groups define the set of (kernel internal) pin numbers of
> > "outside" pins which are used by pin controller to map a given
> > interface. Inside pins are not numbered and the inside interfaces are
> > only used to determine which outside pins are part of the same group
> > (namely those for which the pin controller hardware provides a mux
> > connection to the same inside interface):
> >
> > 4
> > 4 /|--/-- SPI
> > PINS[0..3] --/--|| 4
> > \|--/-- GPIO[0..3]
> >
> > 4
> > PINS[4..7] -----/------ GPIO[4..7]
> >
> > 2
> > 2 /|--/-- I2C
> > PINS[8..9] --/--|| 2
> > \|--/-- GPIO[8..9]
> >
> > Pins 0..3 are in the SPI group because on the "inside" they can be muxed
> > to the SPI interface.
> > Pins 8..9 are in the I2C group because on the "inside" they can be muxed
> > to the I2C interface.
> > Pins 0..9 are in the GPIO group because on the "inside" they can be
> > muxed to the GPIO controller.
> >
> > All pin numbers are relative to the "outside", however, or conflict
> > management would not be possible. I hope this is more understandable
> > than my previous explanations.
> > Both muxes are controlled by the same register. In our overly simplistic
> > example this is not strictly necessary but in reality you might have pin
> > conflicts between the different interfaces.
>
> Same register, or same field/bits in that register?
>
> If it's the same field/bits, I would expect to see the following pin groups:
>
> 1) PINS[0..3], PINS[8..9]
> 2) PINS[4..7]
>
> ... since those are the things that are independently muxable.
> Otherwise, I'd expect to see the following groups:
>
> 1) PINS[0..3]
> 2) PINS[4..7]
> 3) PINS[8..9]
>
> > After the discussion we had so far I'm not so sure if extending the
> > pinctrl system with this kind of features is a very good idea.
>
> That makes things simple:-)
>
> One thing I still don't understand; in a previous mail, you'd mentioned
> 3 DT properties for configuring the pinmux; one represented the pin
> group, one represented the mux function that was selected for that pin
> group, and there was a third ("config"?) property. I still don't
> understand that third property. I only see pins/pingroups and mux
> functions in the diagram I quoted above.

In my proposal, pin groups represent interfaces instead of ports: All
three pin groups are configured through the same bit field in the same
register but they represent _logically_ independent functionalities.
The three DT properties are:

1. interface (which pins are we actually interested in when requesting
this)
2. port (which bit field/register is used to configure this)
3. configuration of that port (which mux function(s) in that bit
field/register are possible to make the interface available)

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-07-26 16:05:22

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On 07/26/2013 03:42 AM, Christian Ruppert wrote:
> On Thu, Jul 18, 2013 at 01:54:18PM -0600, Stephen Warren wrote:
>> On 07/18/2013 10:07 AM, Christian Ruppert wrote:
>> ...
>>> Well, perhaps my definition of "inside"/"outside" pins was not quite
>>> clear: The pin groups define the set of (kernel internal) pin numbers of
>>> "outside" pins which are used by pin controller to map a given
>>> interface. Inside pins are not numbered and the inside interfaces are
>>> only used to determine which outside pins are part of the same group
>>> (namely those for which the pin controller hardware provides a mux
>>> connection to the same inside interface):
>>>
>>> 4
>>> 4 /|--/-- SPI
>>> PINS[0..3] --/--|| 4
>>> \|--/-- GPIO[0..3]
>>>
>>> 4
>>> PINS[4..7] -----/------ GPIO[4..7]
>>>
>>> 2
>>> 2 /|--/-- I2C
>>> PINS[8..9] --/--|| 2
>>> \|--/-- GPIO[8..9]
>>>
>>> Pins 0..3 are in the SPI group because on the "inside" they can be muxed
>>> to the SPI interface.
>>> Pins 8..9 are in the I2C group because on the "inside" they can be muxed
>>> to the I2C interface.
>>> Pins 0..9 are in the GPIO group because on the "inside" they can be
>>> muxed to the GPIO controller.
>>>
>>> All pin numbers are relative to the "outside", however, or conflict
>>> management would not be possible. I hope this is more understandable
>>> than my previous explanations.
>>> Both muxes are controlled by the same register. In our overly simplistic
>>> example this is not strictly necessary but in reality you might have pin
>>> conflicts between the different interfaces.
>>
>> Same register, or same field/bits in that register?
>>
>> If it's the same field/bits, I would expect to see the following pin groups:
>>
>> 1) PINS[0..3], PINS[8..9]
>> 2) PINS[4..7]
>>
>> ... since those are the things that are independently muxable.
>> Otherwise, I'd expect to see the following groups:
>>
>> 1) PINS[0..3]
>> 2) PINS[4..7]
>> 3) PINS[8..9]
>>
>>> After the discussion we had so far I'm not so sure if extending the
>>> pinctrl system with this kind of features is a very good idea.
>>
>> That makes things simple:-)
>>
>> One thing I still don't understand; in a previous mail, you'd mentioned
>> 3 DT properties for configuring the pinmux; one represented the pin
>> group, one represented the mux function that was selected for that pin
>> group, and there was a third ("config"?) property. I still don't
>> understand that third property. I only see pins/pingroups and mux
>> functions in the diagram I quoted above.
>
> In my proposal, pin groups represent interfaces instead of ports: All
> three pin groups are configured through the same bit field in the same
> register but they represent _logically_ independent functionalities.

Oh, that's a pretty "coupled" HW design!

I would suggest having a single pin group for all 10 pins (since that's
what HW really has). I imagine with that HW design, there's very little
potential for any kind of dynamic pin-muxing, since it would affect
multiple unrelated HW modules (I2C, SPI), and hence the co-ordination
required for dynamic muxing would make it impractical. As such I would
also specify the pinctrl configuration as a "hog" in the pin controller,
since each configuration bit affects multiple other devices, so it
doesn't make logical sense to try to specify the pinctrl configuration
anywhere other than the pin controller.

> The three DT properties are:
>
> 1. interface (which pins are we actually interested in when requesting
> this)
> 2. port (which bit field/register is used to configure this)
> 3. configuration of that port (which mux function(s) in that bit
> field/register are possible to make the interface available)

2013-07-29 22:35:07

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

Sorry for taking eternities to look into this.

On Tue, Jun 18, 2013 at 11:29 AM, Christian Ruppert
<[email protected]> wrote:

> The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
> Used to control the pinmux and is a prerequisite for the GPIO driver.
>
> Signed-off-by: Christian Ruppert <[email protected]>
> Signed-off-by: Pierrick Hascoet <[email protected]>
(...)
> +The following pin groups are available:
> + - GPIO ports: gpioa_pins, gpiob_pins, gpioc_pins, gpiod_pins, gpioe_pins,
> + gpiof_pins, gpiog_pins, gpioh_pins, gpioi_pins, gpioj_pins,
> + gpiok_pins, gpiol_pins, gpiom_pins, gpion_pins

I would not attempt to define groups for all GPIO pins.

(...)
> +gpioa: gpio@FF140000 {
> + compatible = "abilis,tb10x-gpio";
> + reg = <0xFF140000 0x1000>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + ngpio = <3>;
> + gpio-ranges = <&iomux 0 0>;
> + gpio-ranges-group-names = "gpioa_pins";

This uses that feature to define GPIO ranges from a group does
it not? I'm not certain about that feature.

I don't see any of the port concept creeping into the device tree
in this version and that is how I think it should be kept:
the "port" particulars is a thing for the driver and not the
device tree.

Yours,
Linus Walleij

2013-08-05 11:51:45

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Tue, Jul 30, 2013 at 12:35:03AM +0200, Linus Walleij wrote:
> Sorry for taking eternities to look into this.
>
> On Tue, Jun 18, 2013 at 11:29 AM, Christian Ruppert
> <[email protected]> wrote:
>
> > The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
> > Used to control the pinmux and is a prerequisite for the GPIO driver.
> >
> > Signed-off-by: Christian Ruppert <[email protected]>
> > Signed-off-by: Pierrick Hascoet <[email protected]>
> (...)
> > +The following pin groups are available:
> > + - GPIO ports: gpioa_pins, gpiob_pins, gpioc_pins, gpiod_pins, gpioe_pins,
> > + gpiof_pins, gpiog_pins, gpioh_pins, gpioi_pins, gpioj_pins,
> > + gpiok_pins, gpiol_pins, gpiom_pins, gpion_pins
>
> I would not attempt to define groups for all GPIO pins.
>
> (...)
> > +gpioa: gpio@FF140000 {
> > + compatible = "abilis,tb10x-gpio";
> > + reg = <0xFF140000 0x1000>;
> > + gpio-controller;
> > + #gpio-cells = <2>;
> > + ngpio = <3>;
> > + gpio-ranges = <&iomux 0 0>;
> > + gpio-ranges-group-names = "gpioa_pins";
>
> This uses that feature to define GPIO ranges from a group does
> it not? I'm not certain about that feature.

It does. The idea is that the entire pin data base is defined inside the
pin controller (or the pin controller device tree nodes) and the rest of
the world just uses symbolic names. The possibility of non-contiguous
ranges comes for free. What is the argument against this? In my
understanding it was agreed that this was a desired feature, patch
c8587eeef8fc219e806e868c6f0c7170c769efab is the first step in this
direction?

> I don't see any of the port concept creeping into the device tree
> in this version and that is how I think it should be kept:
> the "port" particulars is a thing for the driver and not the
> device tree.

I'm not sure if everybody is aligned here (or if we even understand each
other): In my terminology, a "port" is a set of pins controlled by the
same register/bit field. An "interface" is a set of pins which form a
functional unit, e.g. an SPI interface. One port can contain several
interfaces which may or may not be mapped at the same time. Inversely
(especially if every pin can be configured separately), mapping of an
interface might require the configuration of more than one ports. The
concept of interfaces is on a higher level of abstraction (in the sense
"further away from physical pinmux configuration") than the concept of a
port.

In the driver under discussion, pin groups are defined for every
"interface" to make sure that interfaces can be requested in an
orthogonal way by different modules and modules don't have to be "aware"
of which interfaces are grouped into which port (and which other modules
request which other interfaces). A request either succeeds or fails.
Resource management (which interfaces can be mapped simultaneously) is
done inside the pinctrl driver.

If I understand Stephen correctly, the traditional way of requesting pin
configurations is at "port" level, e.g. a configuration is defined by a
port and its mux setting. The TB10x driver works on a higher level of
abstraction ("interface" level), where interfaces are requested and the
driver internally decides which configuration(s) to apply to which
port(s). Ports are not used in the device tree indeed, but interfaces
are.

Based on this, I don't quite understand your comment: You say you don't
like ports starting to leak outside of the pinctrl driver but according
to Stephen that's what is common practice today? Did you mean
interfaces? The TB10x driver's configuration nodes are currently defined
based on interfaces.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-08-14 16:53:59

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Mon, Aug 5, 2013 at 1:51 PM, Christian Ruppert
<[email protected]> wrote:
> [Me]
>> I don't see any of the port concept creeping into the device tree
>> in this version and that is how I think it should be kept:
>> the "port" particulars is a thing for the driver and not the
>> device tree.
>
> I'm not sure if everybody is aligned here (or if we even understand each
> other): In my terminology, a "port" is a set of pins controlled by the
> same register/bit field.

OK, that can also be called a "bank" or "register" but whatever.

> An "interface" is a set of pins which form a
> functional unit, e.g. an SPI interface.

This is called a pinmux setting in the pinctrl terminology.

A group is a number of pins, a function is a functionality such as SPI.
When the function SPI is combined with a group of pins in a map, it
creates a pinmux setting.

> One port can contain several
> interfaces

In pinctrl terminology this means it controls several functions.

> which may or may not be mapped at the same time. Inversely
> (especially if every pin can be configured separately), mapping of an
> interface might require the configuration of more than one ports. The
> concept of interfaces is on a higher level of abstraction (in the sense
> "further away from physical pinmux configuration") than the concept of a
> port.

Hm maybe I still do not understand what an "interface" really is
on this hardware.

> In the driver under discussion, pin groups are defined for every
> "interface" to make sure that interfaces can be requested in an
> orthogonal way by different modules and modules don't have to be "aware"
> of which interfaces are grouped into which port (and which other modules
> request which other interfaces). A request either succeeds or fails.
> Resource management (which interfaces can be mapped simultaneously) is
> done inside the pinctrl driver.

OK

> If I understand Stephen correctly, the traditional way of requesting pin
> configurations is at "port" level, e.g. a configuration is defined by a
> port and its mux setting.

Now it is ever more confused.

Pin configuration is about things like pull-up in pinctrl terminology.

Please talk about functions, groups and settings that combine
functions with groups.

> The TB10x driver works on a higher level of
> abstraction ("interface" level), where interfaces are requested and the
> driver internally decides which configuration(s) to apply to which
> port(s). Ports are not used in the device tree indeed, but interfaces
> are.
>
> Based on this, I don't quite understand your comment: You say you don't
> like ports starting to leak outside of the pinctrl driver but according
> to Stephen that's what is common practice today? Did you mean
> interfaces? The TB10x driver's configuration nodes are currently defined
> based on interfaces.

I think that language is part of the problem here.

Can you please double-check my definitions of terms in
Documentation/pinctrl.txt so we are talking the same language?

Yours,
Linus Walleij

2013-08-21 15:58:18

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Wed, Aug 14, 2013 at 06:53:56PM +0200, Linus Walleij wrote:
> On Mon, Aug 5, 2013 at 1:51 PM, Christian Ruppert
> <[email protected]> wrote:
> > [Me]
> >> I don't see any of the port concept creeping into the device tree
> >> in this version and that is how I think it should be kept:
> >> the "port" particulars is a thing for the driver and not the
> >> device tree.
> >
> > I'm not sure if everybody is aligned here (or if we even understand each
> > other): In my terminology, a "port" is a set of pins controlled by the
> > same register/bit field.
>
> OK, that can also be called a "bank" or "register" but whatever.

As you suggested below I re-read Documentation/pinctrl.txt and it got me
even more confused:
Am I right in my understanding that the whole concept of a
"port/bank/register" or whatever we would like to call it does not exist
in the pinctrl framework?

> > An "interface" is a set of pins which form a
> > functional unit, e.g. an SPI interface.
>
> This is called a pinmux setting in the pinctrl terminology.

Actually, I was more thinking of the association of a function to a pin
group. It comes probably closest to a mapping in
Documentation/pinctrl.txt terminology although I'm not sure if an
interface and a mapping are 100% identical. An interface is not
necessarily mapped so it is probably rather what one could call a
potential mapping.

> A group is a number of pins, a function is a functionality such as SPI.
> When the function SPI is combined with a group of pins in a map, it
> creates a pinmux setting.

What is the difference between a map and a pinmux setting? Or are they
the same?

> [...]
> > In the driver under discussion, pin groups are defined for every
> > "interface" to make sure that interfaces can be requested in an
> > orthogonal way by different modules and modules don't have to be "aware"
> > of which interfaces are grouped into which port (and which other modules
> > request which other interfaces). A request either succeeds or fails.
> > Resource management (which interfaces can be mapped simultaneously) is
> > done inside the pinctrl driver.
>
> OK

This actually looks 100% coherent with Documentation/pinctrl.txt. But
then I don't understand Stephen's request to introduce the concept of
"ports" in the device tree. IMHO ports are a hardware limitation which
should be managed inside the pinctrl driver and if possible not leak
out of it. Also (as stated above), the concept of "ports" does not even
exist in the pinmux framework so why introduce it for DT?

I might have thoroughly misunderstood you here, Stephen. Please be
patient with me and explain once more.

> > If I understand Stephen correctly, the traditional way of requesting pin
> > configurations is at "port" level, e.g. a configuration is defined by a
> > port and its mux setting.
>
> Now it is ever more confused.
>
> Pin configuration is about things like pull-up in pinctrl terminology.

Sorry, my fault. I wanted to say pin muxing. What I call configuration
above is a mux configuration (i.e. a register field setting). The whole
concept of functions and groups as I understand it from
Documentation/pinctrl.txt does not seem to apply to my understanding of
Stephen's request. As I said, I probably misunderstood Stephen
completely here and would be thankful for clarification.

> Please talk about functions, groups and settings that combine
> functions with groups.
>
> > The TB10x driver works on a higher level of
> > abstraction ("interface" level), where interfaces are requested and the
> > driver internally decides which configuration(s) to apply to which
> > port(s). Ports are not used in the device tree indeed, but interfaces
> > are.
> >
> > Based on this, I don't quite understand your comment: You say you don't
> > like ports starting to leak outside of the pinctrl driver but according
> > to Stephen that's what is common practice today? Did you mean
> > interfaces? The TB10x driver's configuration nodes are currently defined
> > based on interfaces.
>
> I think that language is part of the problem here.
>
> Can you please double-check my definitions of terms in
> Documentation/pinctrl.txt so we are talking the same language?

Did it and promise to use Documentation/pinctrl.txt language where
applicable (I haven't found an equivalent for "port", though, and as I
stated above I consider this a Good Thing since IMHO ports should remain
a private concept inside the driver).

Greetings,
Christian

2013-08-22 20:11:03

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On 08/21/2013 09:57 AM, Christian Ruppert wrote:
> On Wed, Aug 14, 2013 at 06:53:56PM +0200, Linus Walleij wrote:
>> On Mon, Aug 5, 2013 at 1:51 PM, Christian Ruppert
>> <[email protected]> wrote:
>>> [Me]
>>>> I don't see any of the port concept creeping into the device tree
>>>> in this version and that is how I think it should be kept:
>>>> the "port" particulars is a thing for the driver and not the
>>>> device tree.
...
>>> In the driver under discussion, pin groups are defined for every
>>> "interface" to make sure that interfaces can be requested in an
>>> orthogonal way by different modules and modules don't have to be "aware"
>>> of which interfaces are grouped into which port (and which other modules
>>> request which other interfaces). A request either succeeds or fails.
>>> Resource management (which interfaces can be mapped simultaneously) is
>>> done inside the pinctrl driver.
>>
>> OK
>
> This actually looks 100% coherent with Documentation/pinctrl.txt. But
> then I don't understand Stephen's request to introduce the concept of
> "ports" in the device tree. IMHO ports are a hardware limitation which
> should be managed inside the pinctrl driver and if possible not leak
> out of it. Also (as stated above), the concept of "ports" does not even
> exist in the pinmux framework so why introduce it for DT?
>
> I might have thoroughly misunderstood you here, Stephen. Please be
> patient with me and explain once more.

I don't think I asked for ports to be represented in DT. Do you have
more context?

2013-08-28 14:48:01

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Thu, Aug 22, 2013 at 02:10:55PM -0600, Stephen Warren wrote:
> On 08/21/2013 09:57 AM, Christian Ruppert wrote:
> > On Wed, Aug 14, 2013 at 06:53:56PM +0200, Linus Walleij wrote:
> >> On Mon, Aug 5, 2013 at 1:51 PM, Christian Ruppert
> >> <[email protected]> wrote:
> >>> [Me]
> >>>> I don't see any of the port concept creeping into the device tree
> >>>> in this version and that is how I think it should be kept:
> >>>> the "port" particulars is a thing for the driver and not the
> >>>> device tree.
> ...
> >>> In the driver under discussion, pin groups are defined for every
> >>> "interface" to make sure that interfaces can be requested in an
> >>> orthogonal way by different modules and modules don't have to be "aware"
> >>> of which interfaces are grouped into which port (and which other modules
> >>> request which other interfaces). A request either succeeds or fails.
> >>> Resource management (which interfaces can be mapped simultaneously) is
> >>> done inside the pinctrl driver.
> >>
> >> OK
> >
> > This actually looks 100% coherent with Documentation/pinctrl.txt. But
> > then I don't understand Stephen's request to introduce the concept of
> > "ports" in the device tree. IMHO ports are a hardware limitation which
> > should be managed inside the pinctrl driver and if possible not leak
> > out of it. Also (as stated above), the concept of "ports" does not even
> > exist in the pinmux framework so why introduce it for DT?
> >
> > I might have thoroughly misunderstood you here, Stephen. Please be
> > patient with me and explain once more.
>
> I don't think I asked for ports to be represented in DT. Do you have
> more context?

Well, you never asked about ports but you asked specifically about pin
groups. The most concise definition of pin groups I have found in your
mails is from https://lkml.org/lkml/2013/6/21/462:

> When I pushed for the concept of groups, I intended it to mean precisely
> one single thing. The points below describe this.
>
> 1) A pin is a single pin/ball/pad on the package.
>
> 2) Some register fields affect just a single pin. For example, there may
> be a register field that affects pin A8's mux setting only.
>
> 3) Some register fields affect multiple pins at once. For example,
> perhaps one register field affects both pin A8's an pin A7's mux setting
> at once.
>
> 4) Depending on HW design, all register fields might be of type
> described at (2) above, or all of the type described at (3) above, or a
> mixture of both. Tegra is a mixture.
>
> 5) I expect the concept of a pin group to solely represent the various
> groups of pins affected by each register field; in (2) above one pin per
> group, in (3) above many pins per group.
>
> Thus, to my mind, a pin group is purely a HW concept, and dictated
> purely by HW design.

Point 5 clearly defines the "concept of a pin group to solely represent
the various groups of pins affected by each register field". The TB10x
definition of a "port" (concept which doesn't seem to exist in the pinctrl
framework) is the set of pins controlled by the same register field
(i.e. the same mux). In my understanding this matches point 5 but as I
said this is probably a misunderstanding and I would be grateful for
clarification. There are other comments which fuel that
misunderstanding, e.g. https://lkml.org/lkml/2013/6/19/589:

> Pin groups are supposed to be something that represents some property of
> the pinctrl HW itself. So, if you have register "X" bits 3-0 that define
> the mux function for pins 8, 9, 10, and 11, then there really is a pin
> group that exists in HW, and that pin group will still exist with that
> same definition no matter what SoC you put the pinctrl HW into. If this
> changes, it's not the same pinctrl HW module.

In TB10x, every function can be activated on exactly one pin group, and
Documentation/pinctrl.txt says "If only one possible group of pins is
available for the function, no group name need to be supplied.".

Maybe the answer to our concrete question of the tb10x driver is thus
renaming the pingrp device tree property of the original patch into
something like function (by which a pin group can be implied)?

For example:
iomux: iomux@FF10601c {
compatible = "abilis,tb10x-iomux";
reg = <0xFF10601c 0x4>;
pctl_gpio_a: pctl-gpio-a {
abilis,function = "gpioa";
};
pctl_uart0: pctl-uart0 {
abilis,function = "uart0";
};
};

What do you think?
Christian

2013-08-28 18:49:55

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Wed, Aug 21, 2013 at 5:57 PM, Christian Ruppert
<[email protected]> wrote:
> On Wed, Aug 14, 2013 at 06:53:56PM +0200, Linus Walleij wrote:
>>
>> OK, that can also be called a "bank" or "register" but whatever.
>
> As you suggested below I re-read Documentation/pinctrl.txt and it got me
> even more confused:
> Am I right in my understanding that the whole concept of a
> "port/bank/register" or whatever we would like to call it does not exist
> in the pinctrl framework?

Not that I know :-)

If what it means is a number of registers from address x thru x+n
words in memory that is called a register range usually, the
Device Tree "regs" property.

If you're referring to a subset of registers dealing with a batch
of pins or a single pin that can use whatever terminology you
want, I consider it a driver-internal detail. Some GPIO drivers
talk about "ports" when they have e.g. 2 x 32bit registers
handling a total of 64 pins, then that is port 0 and port 1 or
something like this, but it's really up to the driver.

Yours,
Linus Walleij

2013-08-29 07:40:31

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Wed, Aug 28, 2013 at 08:49:36PM +0200, Linus Walleij wrote:
> On Wed, Aug 21, 2013 at 5:57 PM, Christian Ruppert
> <[email protected]> wrote:
> > On Wed, Aug 14, 2013 at 06:53:56PM +0200, Linus Walleij wrote:
> >>
> >> OK, that can also be called a "bank" or "register" but whatever.
> >
> > As you suggested below I re-read Documentation/pinctrl.txt and it got me
> > even more confused:
> > Am I right in my understanding that the whole concept of a
> > "port/bank/register" or whatever we would like to call it does not exist
> > in the pinctrl framework?
>
> Not that I know :-)
>
> If what it means is a number of registers from address x thru x+n
> words in memory that is called a register range usually, the
> Device Tree "regs" property.
>
> If you're referring to a subset of registers dealing with a batch
> of pins or a single pin that can use whatever terminology you
> want, I consider it a driver-internal detail. Some GPIO drivers
> talk about "ports" when they have e.g. 2 x 32bit registers
> handling a total of 64 pins, then that is port 0 and port 1 or
> something like this, but it's really up to the driver.

Actually, it's a set of pins the muxing of which is controlled by the
same register. E.g. Port A is the set of all the pins which are
controlled by register field A.

Greetings,
Christian

--
Christian Ruppert , <[email protected]>
/|
Tel: +41/(0)22 816 19-42 //| 3, Chemin du Pr?-Fleuri
_// | bilis Systems CH-1228 Plan-les-Ouates

2013-08-29 08:24:34

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Thu, Aug 29, 2013 at 9:35 AM, Christian Ruppert
<[email protected]> wrote:
> On Wed, Aug 28, 2013 at 08:49:36PM +0200, Linus Walleij wrote:
>> On Wed, Aug 21, 2013 at 5:57 PM, Christian Ruppert
>> <[email protected]> wrote:
>> > On Wed, Aug 14, 2013 at 06:53:56PM +0200, Linus Walleij wrote:
>> >>
>> >> OK, that can also be called a "bank" or "register" but whatever.
>> >
>> > As you suggested below I re-read Documentation/pinctrl.txt and it got me
>> > even more confused:
>> > Am I right in my understanding that the whole concept of a
>> > "port/bank/register" or whatever we would like to call it does not exist
>> > in the pinctrl framework?
>>
>> Not that I know :-)
>>
>> If what it means is a number of registers from address x thru x+n
>> words in memory that is called a register range usually, the
>> Device Tree "regs" property.
>>
>> If you're referring to a subset of registers dealing with a batch
>> of pins or a single pin that can use whatever terminology you
>> want, I consider it a driver-internal detail. Some GPIO drivers
>> talk about "ports" when they have e.g. 2 x 32bit registers
>> handling a total of 64 pins, then that is port 0 and port 1 or
>> something like this, but it's really up to the driver.
>
> Actually, it's a set of pins the muxing of which is controlled by the
> same register. E.g. Port A is the set of all the pins which are
> controlled by register field A.

What is wrong with calling that a "pin-set register" or something?

Yours,
Linus Walleij

2013-08-30 08:19:42

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Thu, Aug 29, 2013 at 10:24:30AM +0200, Linus Walleij wrote:
> On Thu, Aug 29, 2013 at 9:35 AM, Christian Ruppert
> <[email protected]> wrote:
> > On Wed, Aug 28, 2013 at 08:49:36PM +0200, Linus Walleij wrote:
> >> On Wed, Aug 21, 2013 at 5:57 PM, Christian Ruppert
> >> <[email protected]> wrote:
> >> > On Wed, Aug 14, 2013 at 06:53:56PM +0200, Linus Walleij wrote:
> >> >>
> >> >> OK, that can also be called a "bank" or "register" but whatever.
> >> >
> >> > As you suggested below I re-read Documentation/pinctrl.txt and it got me
> >> > even more confused:
> >> > Am I right in my understanding that the whole concept of a
> >> > "port/bank/register" or whatever we would like to call it does not exist
> >> > in the pinctrl framework?
> >>
> >> Not that I know :-)
> >>
> >> If what it means is a number of registers from address x thru x+n
> >> words in memory that is called a register range usually, the
> >> Device Tree "regs" property.
> >>
> >> If you're referring to a subset of registers dealing with a batch
> >> of pins or a single pin that can use whatever terminology you
> >> want, I consider it a driver-internal detail. Some GPIO drivers
> >> talk about "ports" when they have e.g. 2 x 32bit registers
> >> handling a total of 64 pins, then that is port 0 and port 1 or
> >> something like this, but it's really up to the driver.
> >
> > Actually, it's a set of pins the muxing of which is controlled by the
> > same register. E.g. Port A is the set of all the pins which are
> > controlled by register field A.
>
> What is wrong with calling that a "pin-set register" or something?

Nothing. Except perhaps that I hope we can confine that concept to the
driver and thus eliminate the need to talk about it altogether ;)

Greetings,
Christian

2013-10-08 13:25:12

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 2/4] pinmux: Add TB10x pinmux driver

On Wed, Aug 28, 2013 at 04:47:36PM +0200, Christian Ruppert wrote:
> On Thu, Aug 22, 2013 at 02:10:55PM -0600, Stephen Warren wrote:
> [...]
> > Pin groups are supposed to be something that represents some property of
> > the pinctrl HW itself. So, if you have register "X" bits 3-0 that define
> > the mux function for pins 8, 9, 10, and 11, then there really is a pin
> > group that exists in HW, and that pin group will still exist with that
> > same definition no matter what SoC you put the pinctrl HW into. If this
> > changes, it's not the same pinctrl HW module.
>
> In TB10x, every function can be activated on exactly one pin group, and
> Documentation/pinctrl.txt says "If only one possible group of pins is
> available for the function, no group name need to be supplied.".
>
> Maybe the answer to our concrete question of the tb10x driver is thus
> renaming the pingrp device tree property of the original patch into
> something like function (by which a pin group can be implied)?
>
> For example:
> iomux: iomux@FF10601c {
> compatible = "abilis,tb10x-iomux";
> reg = <0xFF10601c 0x4>;
> pctl_gpio_a: pctl-gpio-a {
> abilis,function = "gpioa";
> };
> pctl_uart0: pctl-uart0 {
> abilis,function = "uart0";
> };
> };
>
> What do you think?

There doesn't seem to be any opposition to this proposal so please find
a revised patch set in the follow up.

Greetings,
Christian

2013-10-08 13:25:21

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 03/03] GPIO: Add TB10x GPIO driver

The GPIO driver for the Abilis Systems TB10x series of SOCs based on ARC700
CPUs. It supports GPIO control and GPIO interrupt generation. This driver
works in conjunction with the TB10x pinctrl driver.

Signed-off-by: Sascha Leuenberger <[email protected]>
Signed-off-by: Christian Ruppert <[email protected]>
---
.../devicetree/bindings/gpio/abilis,tb10x-gpio.txt | 36 ++
drivers/gpio/Kconfig | 4 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-tb10x.c | 341 ++++++++++++++++++++
4 files changed, 382 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
create mode 100644 drivers/gpio/gpio-tb10x.c

diff --git a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
new file mode 100644
index 0000000..00611ac
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
@@ -0,0 +1,36 @@
+* Abilis TB10x GPIO controller
+
+Required Properties:
+- compatible: Should be "abilis,tb10x-gpio"
+- reg: Address and length of the register set for the device
+- gpio-controller: Marks the device node as a gpio controller.
+- #gpio-cells: Should be <2>. The first cell is the pin number and the
+ second cell is used to specify optional parameters:
+ - bit 0 specifies polarity (0 for normal, 1 for inverted).
+- abilis,ngpio: the number of GPIO pins this driver controls.
+
+Optional Properties:
+- interrupt-controller: Marks the device node as an interrupt controller.
+- #interrupt-cells: Should be <1>. Interrupts are triggered on both edges.
+- interrupts: Defines the interrupt line connecting this GPIO controller to
+ its parent interrupt controller.
+- interrupt-parent: Defines the parent interrupt controller.
+
+GPIO ranges are specified as described in
+Documentation/devicetree/bindings/gpio/gpio.txt
+
+Example:
+
+ gpioa: gpio@FF140000 {
+ compatible = "abilis,tb10x-gpio";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&tb10x_ictl>;
+ interrupts = <27 2>;
+ reg = <0xFF140000 0x1000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ abilis,ngpio = <3>;
+ gpio-ranges = <&iomux 0 0 0>;
+ gpio-ranges-group-names = "gpioa_pins";
+ };
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b2450ba..79be382 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -327,6 +327,10 @@ config GPIO_GRGPIO
Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB
VHDL IP core library.

+config GPIO_TB10X
+ bool
+ select OF_GPIO
+
comment "I2C GPIO expanders:"

config GPIO_ARIZONA
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ef3e983..f2956e1 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o
+obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c
new file mode 100644
index 0000000..833d0f4
--- /dev/null
+++ b/drivers/gpio/gpio-tb10x.c
@@ -0,0 +1,341 @@
+/* Abilis Systems MODULE DESCRIPTION
+ *
+ * Copyright (C) Abilis Systems 2013
+ *
+ * Authors: Sascha Leuenberger <[email protected]>
+ * Christian Ruppert <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/pinctrl/consumer.h>
+
+#define TB10X_GPIO_DIR_IN (0x00000000)
+#define TB10X_GPIO_DIR_OUT (0x00000001)
+#define OFFSET_TO_REG_DDR (0x00)
+#define OFFSET_TO_REG_DATA (0x04)
+#define OFFSET_TO_REG_INT_EN (0x08)
+#define OFFSET_TO_REG_CHANGE (0x0C)
+#define OFFSET_TO_REG_WRMASK (0x10)
+#define OFFSET_TO_REG_INT_TYPE (0x14)
+
+
+/**
+ * @spinlock: used for atomic read/modify/write of registers
+ * @base: register base address
+ * @domain: IRQ domain of GPIO generated interrupts managed by this controller
+ * @irq: Interrupt line of parent interrupt controller
+ * @gc: gpio_chip structure associated to this GPIO controller
+ */
+struct tb10x_gpio {
+ spinlock_t spinlock;
+ void __iomem *base;
+ struct irq_domain *domain;
+ int irq;
+ struct gpio_chip gc;
+};
+
+static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs)
+{
+ return ioread32(gpio->base + offs);
+}
+
+static inline void tb10x_reg_write(struct tb10x_gpio *gpio, unsigned int offs,
+ u32 val)
+{
+ iowrite32(val, gpio->base + offs);
+}
+
+static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs,
+ u32 mask, u32 val)
+{
+ u32 r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio->spinlock, flags);
+
+ r = tb10x_reg_read(gpio, offs);
+ r = (r & ~mask) | (val & mask);
+
+ tb10x_reg_write(gpio, offs, r);
+
+ spin_unlock_irqrestore(&gpio->spinlock, flags);
+}
+
+static inline struct tb10x_gpio *to_tb10x_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct tb10x_gpio, gc);
+}
+
+static int tb10x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = BIT(offset);
+ int val = TB10X_GPIO_DIR_IN << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
+
+ return 0;
+}
+
+static int tb10x_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int val;
+
+ val = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_DATA);
+
+ if (val & BIT(offset))
+ return 1;
+ else
+ return 0;
+}
+
+static void tb10x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = BIT(offset);
+ int val = value << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DATA, mask, val);
+}
+
+static int tb10x_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+ int mask = BIT(offset);
+ int val = TB10X_GPIO_DIR_OUT << offset;
+
+ tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
+
+ return 0;
+}
+
+static int tb10x_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ return pinctrl_request_gpio(chip->base + offset);
+}
+
+static void tb10x_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ pinctrl_free_gpio(chip->base + offset);
+}
+
+static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct tb10x_gpio *tb10x_gpio = to_tb10x_gpio(chip);
+
+ return irq_create_mapping(tb10x_gpio->domain, offset);
+}
+
+static int tb10x_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ if ((type & IRQF_TRIGGER_MASK) != IRQ_TYPE_EDGE_BOTH) {
+ pr_err("Only (both) edge triggered interrupts supported.\n");
+ return -EINVAL;
+ }
+
+ irqd_set_trigger_type(data, type);
+
+ return IRQ_SET_MASK_OK;
+}
+
+static irqreturn_t tb10x_gpio_irq_cascade(int irq, void *data)
+{
+ struct tb10x_gpio *tb10x_gpio = data;
+ u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_CHANGE);
+ u32 m = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);
+ const unsigned long bits = r & m;
+ int i;
+
+ for_each_set_bit(i, &bits, 32)
+ generic_handle_irq(irq_find_mapping(tb10x_gpio->domain, i));
+
+ return IRQ_HANDLED;
+}
+
+static int tb10x_gpio_probe(struct platform_device *pdev)
+{
+ struct tb10x_gpio *tb10x_gpio;
+ struct resource *mem;
+ struct device_node *dn = pdev->dev.of_node;
+ int ret = -EBUSY;
+ u32 ngpio;
+
+ if (!dn)
+ return -EINVAL;
+
+ if (of_property_read_u32(dn, "abilis,ngpio", &ngpio))
+ return -EINVAL;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource defined.\n");
+ return -EINVAL;
+ }
+
+ tb10x_gpio = devm_kzalloc(&pdev->dev, sizeof(*tb10x_gpio), GFP_KERNEL);
+ if (tb10x_gpio == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&tb10x_gpio->spinlock);
+
+ tb10x_gpio->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (!tb10x_gpio->base) {
+ dev_err(&pdev->dev, "Could not remap reg space.\n");
+ goto fail_ioremap;
+ }
+
+ tb10x_gpio->gc.label = of_node_full_name(dn);
+ tb10x_gpio->gc.dev = &pdev->dev;
+ tb10x_gpio->gc.owner = THIS_MODULE;
+ tb10x_gpio->gc.direction_input = tb10x_gpio_direction_in;
+ tb10x_gpio->gc.get = tb10x_gpio_get;
+ tb10x_gpio->gc.direction_output = tb10x_gpio_direction_out;
+ tb10x_gpio->gc.set = tb10x_gpio_set;
+ tb10x_gpio->gc.request = tb10x_gpio_request;
+ tb10x_gpio->gc.free = tb10x_gpio_free;
+ tb10x_gpio->gc.base = -1;
+ tb10x_gpio->gc.ngpio = ngpio;
+ tb10x_gpio->gc.can_sleep = 0;
+
+
+ ret = gpiochip_add(&tb10x_gpio->gc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not add gpiochip.\n");
+ goto fail_gpiochip_registration;
+ }
+
+ platform_set_drvdata(pdev, tb10x_gpio);
+
+ if (of_find_property(dn, "interrupt-controller", NULL)) {
+ struct irq_chip_generic *gc;
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "No interrupt specified.\n");
+ goto fail_get_irq;
+ }
+
+ tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
+ tb10x_gpio->irq = ret;
+
+ ret = devm_request_irq(&pdev->dev, ret, tb10x_gpio_irq_cascade,
+ IRQF_TRIGGER_NONE | IRQF_SHARED,
+ dev_name(&pdev->dev), tb10x_gpio);
+ if (ret != 0)
+ goto fail_request_irq;
+
+ tb10x_gpio->domain = irq_domain_add_linear(dn,
+ tb10x_gpio->gc.ngpio,
+ &irq_generic_chip_ops, NULL);
+ if (!tb10x_gpio->domain) {
+ ret = -ENOMEM;
+ goto fail_irq_domain;
+ }
+
+ ret = irq_alloc_domain_generic_chips(tb10x_gpio->domain,
+ tb10x_gpio->gc.ngpio, 1, tb10x_gpio->gc.label,
+ handle_edge_irq, IRQ_NOREQUEST, IRQ_NOPROBE,
+ IRQ_GC_INIT_MASK_CACHE);
+ if (ret)
+ goto fail_irq_domain;
+
+ gc = tb10x_gpio->domain->gc->gc[0];
+ gc->reg_base = tb10x_gpio->base;
+ gc->chip_types[0].type = IRQ_TYPE_EDGE_BOTH;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+ gc->chip_types[0].chip.irq_set_type = tb10x_gpio_irq_set_type;
+ gc->chip_types[0].regs.ack = OFFSET_TO_REG_CHANGE;
+ gc->chip_types[0].regs.mask = OFFSET_TO_REG_INT_EN;
+ }
+
+ return 0;
+
+fail_irq_domain:
+fail_request_irq:
+fail_get_irq:
+ gpiochip_remove(&tb10x_gpio->gc);
+fail_gpiochip_registration:
+fail_ioremap:
+ return ret;
+}
+
+static int __exit tb10x_gpio_remove(struct platform_device *pdev)
+{
+ struct tb10x_gpio *tb10x_gpio = platform_get_drvdata(pdev);
+ int ret;
+
+ if (tb10x_gpio->gc.to_irq) {
+ irq_remove_generic_chip(tb10x_gpio->domain->gc->gc[0],
+ BIT(tb10x_gpio->gc.ngpio) - 1, 0, 0);
+ kfree(tb10x_gpio->domain->gc);
+ irq_domain_remove(tb10x_gpio->domain);
+ free_irq(tb10x_gpio->irq, tb10x_gpio);
+ }
+ ret = gpiochip_remove(&tb10x_gpio->gc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct of_device_id tb10x_gpio_dt_ids[] = {
+ { .compatible = "abilis,tb10x-gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tb10x_gpio_dt_ids);
+
+static struct platform_driver tb10x_gpio_driver = {
+ .probe = tb10x_gpio_probe,
+ .remove = tb10x_gpio_remove,
+ .driver = {
+ .name = "tb10x-gpio",
+ .of_match_table = of_match_ptr(tb10x_gpio_dt_ids),
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init ab_gpio_init(void)
+{
+ return platform_driver_register(&tb10x_gpio_driver);
+}
+
+static void __exit ab_gpio_exit(void)
+{
+ platform_driver_unregister(&tb10x_gpio_driver);
+}
+
+module_init(ab_gpio_init);
+module_exit(ab_gpio_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("tb10x gpio.");
+MODULE_VERSION("0.0.1");
--
1.7.1

2013-10-08 13:25:46

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 02/03] pinmux: Add TB10x pinmux driver

The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
Used to control the pinmux and is a prerequisite for the GPIO driver.

Signed-off-by: Christian Ruppert <[email protected]>
Signed-off-by: Pierrick Hascoet <[email protected]>
---
.../bindings/pinctrl/abilis,tb10x-iomux.txt | 79 ++
drivers/pinctrl/Kconfig | 4 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-tb10x.c | 878 ++++++++++++++++++++
4 files changed, 962 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
create mode 100644 drivers/pinctrl/pinctrl-tb10x.c

diff --git a/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
new file mode 100644
index 0000000..f8ccd6c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
@@ -0,0 +1,80 @@
+Abilis Systems TB10x pin controller
+===================================
+
+Required properties
+-------------------
+
+- compatible: should be "abilis,tb10x-iomux";
+- reg: should contain the physical address and size of the pin controller's
+ register range.
+
+
+Function definitions
+--------------------
+
+Functions are defined (and referenced) by sub-nodes of the pin controller.
+Every sub-node defines exactly one function (implying a set of pins).
+Every function is associated to one named pin group inside the pin controller
+driver and these names are used to associate pin group predefinitions to pin
+controller sub-nodes.
+
+Required function definition subnode properties:
+ - abilis,function: should be set to the name of the function's pin group.
+
+The following pin groups are available:
+ - GPIO ports: gpioa, gpiob, gpioc, gpiod, gpioe, gpiof, gpiog,
+ gpioh, gpioi, gpioj, gpiok, gpiol, gpiom, gpion
+ - Serial TS input ports: mis0, mis1, mis2, mis3, mis4, mis5, mis6, mis7
+ - Parallel TS input ports: mip1, mip3, mip5, mip7
+ - Serial TS output ports: mos0, mos1, mos2, mos3
+ - Parallel TS output port: mop
+ - CI+ port: ciplus
+ - CableCard (Mcard) port: mcard
+ - Smart card ports: stc0, stc1
+ - UART ports: uart0, uart1
+ - SPI ports: spi1, spi3
+ - JTAG: jtag
+
+All other ports of the chip are not multiplexed and thus not managed by this
+driver.
+
+
+GPIO ranges definition
+----------------------
+
+The named pin groups of GPIO ports can be used to define GPIO ranges as
+explained in Documentation/devicetree/bindings/gpio/gpio.txt.
+
+
+Example
+-------
+
+iomux: iomux@FF10601c {
+ compatible = "abilis,tb10x-iomux";
+ reg = <0xFF10601c 0x4>;
+ pctl_gpio_a: pctl-gpio-a {
+ abilis,function = "gpioa";
+ };
+ pctl_uart0: pctl-uart0 {
+ abilis,function = "uart0";
+ };
+};
+uart@FF100000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0xFF100000 0x100>;
+ clock-frequency = <166666666>;
+ interrupts = <25 1>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pctl_uart0>;
+};
+gpioa: gpio@FF140000 {
+ compatible = "abilis,tb10x-gpio";
+ reg = <0xFF140000 0x1000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpio = <3>;
+ gpio-ranges = <&iomux 0 0>;
+ gpio-ranges-group-names = "gpioa";
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 5a8ad51..e9ede63 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -281,6 +281,10 @@ config PINCTRL_XWAY
depends on SOC_TYPE_XWAY
depends on PINCTRL_LANTIQ

+config PINCTRL_TB10X
+ bool
+ depends on ARC_PLAT_TB10X
+
endmenu

endif
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index d64563b..b740a85 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_PINCTRL_S3C24XX) += pinctrl-s3c24xx.o
obj-$(CONFIG_PINCTRL_S3C64XX) += pinctrl-s3c64xx.o
obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o
obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o
+obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o
obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o

diff --git a/drivers/pinctrl/pinctrl-tb10x.c b/drivers/pinctrl/pinctrl-tb10x.c
new file mode 100644
index 0000000..1889967
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tb10x.c
@@ -0,0 +1,879 @@
+/*
+ * Abilis Systems TB10x pin control driver
+ *
+ * Copyright (C) Abilis Systems 2012
+ *
+ * Author: Christian Ruppert <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/stringify.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#define TB10X_PORT1 (0)
+#define TB10X_PORT2 (16)
+#define TB10X_PORT3 (32)
+#define TB10X_PORT4 (48)
+#define TB10X_PORT5 (128)
+#define TB10X_PORT6 (64)
+#define TB10X_PORT7 (80)
+#define TB10X_PORT8 (96)
+#define TB10X_PORT9 (112)
+#define TB10X_GPIOS (256)
+
+#define PCFG_PORT_BITWIDTH (2)
+#define PCFG_PORT_MASK(PORT) \
+ (((1 << PCFG_PORT_BITWIDTH) - 1) << (PCFG_PORT_BITWIDTH * (PORT)))
+
+static const struct pinctrl_pin_desc tb10x_pins[] = {
+ /* Port 1 */
+ PINCTRL_PIN(TB10X_PORT1 + 0, "MICLK_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 1, "MISTRT_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 2, "MIVAL_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 3, "MDI_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 4, "GPIOA0"),
+ PINCTRL_PIN(TB10X_PORT1 + 5, "GPIOA1"),
+ PINCTRL_PIN(TB10X_PORT1 + 6, "GPIOA2"),
+ PINCTRL_PIN(TB10X_PORT1 + 7, "MDI_S1"),
+ PINCTRL_PIN(TB10X_PORT1 + 8, "MIVAL_S1"),
+ PINCTRL_PIN(TB10X_PORT1 + 9, "MISTRT_S1"),
+ PINCTRL_PIN(TB10X_PORT1 + 10, "MICLK_S1"),
+ /* Port 2 */
+ PINCTRL_PIN(TB10X_PORT2 + 0, "MICLK_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 1, "MISTRT_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 2, "MIVAL_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 3, "MDI_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 4, "GPIOC0"),
+ PINCTRL_PIN(TB10X_PORT2 + 5, "GPIOC1"),
+ PINCTRL_PIN(TB10X_PORT2 + 6, "GPIOC2"),
+ PINCTRL_PIN(TB10X_PORT2 + 7, "MDI_S3"),
+ PINCTRL_PIN(TB10X_PORT2 + 8, "MIVAL_S3"),
+ PINCTRL_PIN(TB10X_PORT2 + 9, "MISTRT_S3"),
+ PINCTRL_PIN(TB10X_PORT2 + 10, "MICLK_S3"),
+ /* Port 3 */
+ PINCTRL_PIN(TB10X_PORT3 + 0, "MICLK_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 1, "MISTRT_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 2, "MIVAL_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 3, "MDI_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 4, "GPIOE0"),
+ PINCTRL_PIN(TB10X_PORT3 + 5, "GPIOE1"),
+ PINCTRL_PIN(TB10X_PORT3 + 6, "GPIOE2"),
+ PINCTRL_PIN(TB10X_PORT3 + 7, "MDI_S5"),
+ PINCTRL_PIN(TB10X_PORT3 + 8, "MIVAL_S5"),
+ PINCTRL_PIN(TB10X_PORT3 + 9, "MISTRT_S5"),
+ PINCTRL_PIN(TB10X_PORT3 + 10, "MICLK_S5"),
+ /* Port 4 */
+ PINCTRL_PIN(TB10X_PORT4 + 0, "MICLK_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 1, "MISTRT_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 2, "MIVAL_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 3, "MDI_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 4, "GPIOG0"),
+ PINCTRL_PIN(TB10X_PORT4 + 5, "GPIOG1"),
+ PINCTRL_PIN(TB10X_PORT4 + 6, "GPIOG2"),
+ PINCTRL_PIN(TB10X_PORT4 + 7, "MDI_S7"),
+ PINCTRL_PIN(TB10X_PORT4 + 8, "MIVAL_S7"),
+ PINCTRL_PIN(TB10X_PORT4 + 9, "MISTRT_S7"),
+ PINCTRL_PIN(TB10X_PORT4 + 10, "MICLK_S7"),
+ /* Port 5 */
+ PINCTRL_PIN(TB10X_PORT5 + 0, "PC_CE1N"),
+ PINCTRL_PIN(TB10X_PORT5 + 1, "PC_CE2N"),
+ PINCTRL_PIN(TB10X_PORT5 + 2, "PC_REGN"),
+ PINCTRL_PIN(TB10X_PORT5 + 3, "PC_INPACKN"),
+ PINCTRL_PIN(TB10X_PORT5 + 4, "PC_OEN"),
+ PINCTRL_PIN(TB10X_PORT5 + 5, "PC_WEN"),
+ PINCTRL_PIN(TB10X_PORT5 + 6, "PC_IORDN"),
+ PINCTRL_PIN(TB10X_PORT5 + 7, "PC_IOWRN"),
+ PINCTRL_PIN(TB10X_PORT5 + 8, "PC_RDYIRQN"),
+ PINCTRL_PIN(TB10X_PORT5 + 9, "PC_WAITN"),
+ PINCTRL_PIN(TB10X_PORT5 + 10, "PC_A0"),
+ PINCTRL_PIN(TB10X_PORT5 + 11, "PC_A1"),
+ PINCTRL_PIN(TB10X_PORT5 + 12, "PC_A2"),
+ PINCTRL_PIN(TB10X_PORT5 + 13, "PC_A3"),
+ PINCTRL_PIN(TB10X_PORT5 + 14, "PC_A4"),
+ PINCTRL_PIN(TB10X_PORT5 + 15, "PC_A5"),
+ PINCTRL_PIN(TB10X_PORT5 + 16, "PC_A6"),
+ PINCTRL_PIN(TB10X_PORT5 + 17, "PC_A7"),
+ PINCTRL_PIN(TB10X_PORT5 + 18, "PC_A8"),
+ PINCTRL_PIN(TB10X_PORT5 + 19, "PC_A9"),
+ PINCTRL_PIN(TB10X_PORT5 + 20, "PC_A10"),
+ PINCTRL_PIN(TB10X_PORT5 + 21, "PC_A11"),
+ PINCTRL_PIN(TB10X_PORT5 + 22, "PC_A12"),
+ PINCTRL_PIN(TB10X_PORT5 + 23, "PC_A13"),
+ PINCTRL_PIN(TB10X_PORT5 + 24, "PC_A14"),
+ PINCTRL_PIN(TB10X_PORT5 + 25, "PC_D0"),
+ PINCTRL_PIN(TB10X_PORT5 + 26, "PC_D1"),
+ PINCTRL_PIN(TB10X_PORT5 + 27, "PC_D2"),
+ PINCTRL_PIN(TB10X_PORT5 + 28, "PC_D3"),
+ PINCTRL_PIN(TB10X_PORT5 + 29, "PC_D4"),
+ PINCTRL_PIN(TB10X_PORT5 + 30, "PC_D5"),
+ PINCTRL_PIN(TB10X_PORT5 + 31, "PC_D6"),
+ PINCTRL_PIN(TB10X_PORT5 + 32, "PC_D7"),
+ PINCTRL_PIN(TB10X_PORT5 + 33, "PC_MOSTRT"),
+ PINCTRL_PIN(TB10X_PORT5 + 34, "PC_MOVAL"),
+ PINCTRL_PIN(TB10X_PORT5 + 35, "PC_MDO0"),
+ PINCTRL_PIN(TB10X_PORT5 + 36, "PC_MDO1"),
+ PINCTRL_PIN(TB10X_PORT5 + 37, "PC_MDO2"),
+ PINCTRL_PIN(TB10X_PORT5 + 38, "PC_MDO3"),
+ PINCTRL_PIN(TB10X_PORT5 + 39, "PC_MDO4"),
+ PINCTRL_PIN(TB10X_PORT5 + 40, "PC_MDO5"),
+ PINCTRL_PIN(TB10X_PORT5 + 41, "PC_MDO6"),
+ PINCTRL_PIN(TB10X_PORT5 + 42, "PC_MDO7"),
+ PINCTRL_PIN(TB10X_PORT5 + 43, "PC_MISTRT"),
+ PINCTRL_PIN(TB10X_PORT5 + 44, "PC_MIVAL"),
+ PINCTRL_PIN(TB10X_PORT5 + 45, "PC_MDI0"),
+ PINCTRL_PIN(TB10X_PORT5 + 46, "PC_MDI1"),
+ PINCTRL_PIN(TB10X_PORT5 + 47, "PC_MDI2"),
+ PINCTRL_PIN(TB10X_PORT5 + 48, "PC_MDI3"),
+ PINCTRL_PIN(TB10X_PORT5 + 49, "PC_MDI4"),
+ PINCTRL_PIN(TB10X_PORT5 + 50, "PC_MDI5"),
+ PINCTRL_PIN(TB10X_PORT5 + 51, "PC_MDI6"),
+ PINCTRL_PIN(TB10X_PORT5 + 52, "PC_MDI7"),
+ PINCTRL_PIN(TB10X_PORT5 + 53, "PC_MICLK"),
+ /* Port 6 */
+ PINCTRL_PIN(TB10X_PORT6 + 0, "T_MOSTRT_S0"),
+ PINCTRL_PIN(TB10X_PORT6 + 1, "T_MOVAL_S0"),
+ PINCTRL_PIN(TB10X_PORT6 + 2, "T_MDO_S0"),
+ PINCTRL_PIN(TB10X_PORT6 + 3, "T_MOSTRT_S1"),
+ PINCTRL_PIN(TB10X_PORT6 + 4, "T_MOVAL_S1"),
+ PINCTRL_PIN(TB10X_PORT6 + 5, "T_MDO_S1"),
+ PINCTRL_PIN(TB10X_PORT6 + 6, "T_MOSTRT_S2"),
+ PINCTRL_PIN(TB10X_PORT6 + 7, "T_MOVAL_S2"),
+ PINCTRL_PIN(TB10X_PORT6 + 8, "T_MDO_S2"),
+ PINCTRL_PIN(TB10X_PORT6 + 9, "T_MOSTRT_S3"),
+ /* Port 7 */
+ PINCTRL_PIN(TB10X_PORT7 + 0, "UART0_TXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 1, "UART0_RXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 2, "UART0_CTS"),
+ PINCTRL_PIN(TB10X_PORT7 + 3, "UART0_RTS"),
+ PINCTRL_PIN(TB10X_PORT7 + 4, "UART1_TXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 5, "UART1_RXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 6, "UART1_CTS"),
+ PINCTRL_PIN(TB10X_PORT7 + 7, "UART1_RTS"),
+ /* Port 8 */
+ PINCTRL_PIN(TB10X_PORT8 + 0, "SPI3_CLK"),
+ PINCTRL_PIN(TB10X_PORT8 + 1, "SPI3_MISO"),
+ PINCTRL_PIN(TB10X_PORT8 + 2, "SPI3_MOSI"),
+ PINCTRL_PIN(TB10X_PORT8 + 3, "SPI3_SSN"),
+ /* Port 9 */
+ PINCTRL_PIN(TB10X_PORT9 + 0, "SPI1_CLK"),
+ PINCTRL_PIN(TB10X_PORT9 + 1, "SPI1_MISO"),
+ PINCTRL_PIN(TB10X_PORT9 + 2, "SPI1_MOSI"),
+ PINCTRL_PIN(TB10X_PORT9 + 3, "SPI1_SSN0"),
+ PINCTRL_PIN(TB10X_PORT9 + 4, "SPI1_SSN1"),
+ /* Unmuxed GPIOs */
+ PINCTRL_PIN(TB10X_GPIOS + 0, "GPIOB0"),
+ PINCTRL_PIN(TB10X_GPIOS + 1, "GPIOB1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 2, "GPIOD0"),
+ PINCTRL_PIN(TB10X_GPIOS + 3, "GPIOD1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 4, "GPIOF0"),
+ PINCTRL_PIN(TB10X_GPIOS + 5, "GPIOF1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 6, "GPIOH0"),
+ PINCTRL_PIN(TB10X_GPIOS + 7, "GPIOH1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 8, "GPIOI0"),
+ PINCTRL_PIN(TB10X_GPIOS + 9, "GPIOI1"),
+ PINCTRL_PIN(TB10X_GPIOS + 10, "GPIOI2"),
+ PINCTRL_PIN(TB10X_GPIOS + 11, "GPIOI3"),
+ PINCTRL_PIN(TB10X_GPIOS + 12, "GPIOI4"),
+ PINCTRL_PIN(TB10X_GPIOS + 13, "GPIOI5"),
+ PINCTRL_PIN(TB10X_GPIOS + 14, "GPIOI6"),
+ PINCTRL_PIN(TB10X_GPIOS + 15, "GPIOI7"),
+ PINCTRL_PIN(TB10X_GPIOS + 16, "GPIOI8"),
+ PINCTRL_PIN(TB10X_GPIOS + 17, "GPIOI9"),
+ PINCTRL_PIN(TB10X_GPIOS + 18, "GPIOI10"),
+ PINCTRL_PIN(TB10X_GPIOS + 19, "GPIOI11"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 20, "GPION0"),
+ PINCTRL_PIN(TB10X_GPIOS + 21, "GPION1"),
+ PINCTRL_PIN(TB10X_GPIOS + 22, "GPION2"),
+ PINCTRL_PIN(TB10X_GPIOS + 23, "GPION3"),
+#define MAX_PIN (TB10X_GPIOS + 24)
+ PINCTRL_PIN(MAX_PIN, "GPION4"),
+};
+
+
+/* Port 1 */
+static const unsigned mis0_pins[] = { TB10X_PORT1 + 0, TB10X_PORT1 + 1,
+ TB10X_PORT1 + 2, TB10X_PORT1 + 3};
+static const unsigned gpioa_pins[] = { TB10X_PORT1 + 4, TB10X_PORT1 + 5,
+ TB10X_PORT1 + 6};
+static const unsigned mis1_pins[] = { TB10X_PORT1 + 7, TB10X_PORT1 + 8,
+ TB10X_PORT1 + 9, TB10X_PORT1 + 10};
+static const unsigned mip1_pins[] = { TB10X_PORT1 + 0, TB10X_PORT1 + 1,
+ TB10X_PORT1 + 2, TB10X_PORT1 + 3,
+ TB10X_PORT1 + 4, TB10X_PORT1 + 5,
+ TB10X_PORT1 + 6, TB10X_PORT1 + 7,
+ TB10X_PORT1 + 8, TB10X_PORT1 + 9,
+ TB10X_PORT1 + 10};
+
+/* Port 2 */
+static const unsigned mis2_pins[] = { TB10X_PORT2 + 0, TB10X_PORT2 + 1,
+ TB10X_PORT2 + 2, TB10X_PORT2 + 3};
+static const unsigned gpioc_pins[] = { TB10X_PORT2 + 4, TB10X_PORT2 + 5,
+ TB10X_PORT2 + 6};
+static const unsigned mis3_pins[] = { TB10X_PORT2 + 7, TB10X_PORT2 + 8,
+ TB10X_PORT2 + 9, TB10X_PORT2 + 10};
+static const unsigned mip3_pins[] = { TB10X_PORT2 + 0, TB10X_PORT2 + 1,
+ TB10X_PORT2 + 2, TB10X_PORT2 + 3,
+ TB10X_PORT2 + 4, TB10X_PORT2 + 5,
+ TB10X_PORT2 + 6, TB10X_PORT2 + 7,
+ TB10X_PORT2 + 8, TB10X_PORT2 + 9,
+ TB10X_PORT2 + 10};
+
+/* Port 3 */
+static const unsigned mis4_pins[] = { TB10X_PORT3 + 0, TB10X_PORT3 + 1,
+ TB10X_PORT3 + 2, TB10X_PORT3 + 3};
+static const unsigned gpioe_pins[] = { TB10X_PORT3 + 4, TB10X_PORT3 + 5,
+ TB10X_PORT3 + 6};
+static const unsigned mis5_pins[] = { TB10X_PORT3 + 7, TB10X_PORT3 + 8,
+ TB10X_PORT3 + 9, TB10X_PORT3 + 10};
+static const unsigned mip5_pins[] = { TB10X_PORT3 + 0, TB10X_PORT3 + 1,
+ TB10X_PORT3 + 2, TB10X_PORT3 + 3,
+ TB10X_PORT3 + 4, TB10X_PORT3 + 5,
+ TB10X_PORT3 + 6, TB10X_PORT3 + 7,
+ TB10X_PORT3 + 8, TB10X_PORT3 + 9,
+ TB10X_PORT3 + 10};
+
+/* Port 4 */
+static const unsigned mis6_pins[] = { TB10X_PORT4 + 0, TB10X_PORT4 + 1,
+ TB10X_PORT4 + 2, TB10X_PORT4 + 3};
+static const unsigned gpiog_pins[] = { TB10X_PORT4 + 4, TB10X_PORT4 + 5,
+ TB10X_PORT4 + 6};
+static const unsigned mis7_pins[] = { TB10X_PORT4 + 7, TB10X_PORT4 + 8,
+ TB10X_PORT4 + 9, TB10X_PORT4 + 10};
+static const unsigned mip7_pins[] = { TB10X_PORT4 + 0, TB10X_PORT4 + 1,
+ TB10X_PORT4 + 2, TB10X_PORT4 + 3,
+ TB10X_PORT4 + 4, TB10X_PORT4 + 5,
+ TB10X_PORT4 + 6, TB10X_PORT4 + 7,
+ TB10X_PORT4 + 8, TB10X_PORT4 + 9,
+ TB10X_PORT4 + 10};
+
+/* Port 6 */
+static const unsigned mop_pins[] = { TB10X_PORT6 + 0, TB10X_PORT6 + 1,
+ TB10X_PORT6 + 2, TB10X_PORT6 + 3,
+ TB10X_PORT6 + 4, TB10X_PORT6 + 5,
+ TB10X_PORT6 + 6, TB10X_PORT6 + 7,
+ TB10X_PORT6 + 8, TB10X_PORT6 + 9};
+static const unsigned mos0_pins[] = { TB10X_PORT6 + 0, TB10X_PORT6 + 1,
+ TB10X_PORT6 + 2};
+static const unsigned mos1_pins[] = { TB10X_PORT6 + 3, TB10X_PORT6 + 4,
+ TB10X_PORT6 + 5};
+static const unsigned mos2_pins[] = { TB10X_PORT6 + 6, TB10X_PORT6 + 7,
+ TB10X_PORT6 + 8};
+static const unsigned mos3_pins[] = { TB10X_PORT6 + 9};
+
+/* Port 7 */
+static const unsigned uart0_pins[] = { TB10X_PORT7 + 0, TB10X_PORT7 + 1,
+ TB10X_PORT7 + 2, TB10X_PORT7 + 3};
+static const unsigned uart1_pins[] = { TB10X_PORT7 + 4, TB10X_PORT7 + 5,
+ TB10X_PORT7 + 6, TB10X_PORT7 + 7};
+static const unsigned gpiol_pins[] = { TB10X_PORT7 + 0, TB10X_PORT7 + 1,
+ TB10X_PORT7 + 2, TB10X_PORT7 + 3};
+static const unsigned gpiom_pins[] = { TB10X_PORT7 + 4, TB10X_PORT7 + 5,
+ TB10X_PORT7 + 6, TB10X_PORT7 + 7};
+
+/* Port 8 */
+static const unsigned spi3_pins[] = { TB10X_PORT8 + 0, TB10X_PORT8 + 1,
+ TB10X_PORT8 + 2, TB10X_PORT8 + 3};
+static const unsigned jtag_pins[] = { TB10X_PORT8 + 0, TB10X_PORT8 + 1,
+ TB10X_PORT8 + 2, TB10X_PORT8 + 3};
+
+/* Port 9 */
+static const unsigned spi1_pins[] = { TB10X_PORT9 + 0, TB10X_PORT9 + 1,
+ TB10X_PORT9 + 2, TB10X_PORT9 + 3,
+ TB10X_PORT9 + 4};
+static const unsigned gpion_pins[] = { TB10X_PORT9 + 0, TB10X_PORT9 + 1,
+ TB10X_PORT9 + 2, TB10X_PORT9 + 3,
+ TB10X_PORT9 + 4};
+
+/* Port 5 */
+static const unsigned gpioj_pins[] = { TB10X_PORT5 + 0, TB10X_PORT5 + 1,
+ TB10X_PORT5 + 2, TB10X_PORT5 + 3,
+ TB10X_PORT5 + 4, TB10X_PORT5 + 5,
+ TB10X_PORT5 + 6, TB10X_PORT5 + 7,
+ TB10X_PORT5 + 8, TB10X_PORT5 + 9,
+ TB10X_PORT5 + 10, TB10X_PORT5 + 11,
+ TB10X_PORT5 + 12, TB10X_PORT5 + 13,
+ TB10X_PORT5 + 14, TB10X_PORT5 + 15,
+ TB10X_PORT5 + 16, TB10X_PORT5 + 17,
+ TB10X_PORT5 + 18, TB10X_PORT5 + 19,
+ TB10X_PORT5 + 20, TB10X_PORT5 + 21,
+ TB10X_PORT5 + 22, TB10X_PORT5 + 23,
+ TB10X_PORT5 + 24, TB10X_PORT5 + 25,
+ TB10X_PORT5 + 26, TB10X_PORT5 + 27,
+ TB10X_PORT5 + 28, TB10X_PORT5 + 29,
+ TB10X_PORT5 + 30, TB10X_PORT5 + 31};
+static const unsigned gpiok_pins[] = { TB10X_PORT5 + 32, TB10X_PORT5 + 33,
+ TB10X_PORT5 + 34, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40, TB10X_PORT5 + 41,
+ TB10X_PORT5 + 42, TB10X_PORT5 + 43,
+ TB10X_PORT5 + 44, TB10X_PORT5 + 45,
+ TB10X_PORT5 + 46, TB10X_PORT5 + 47,
+ TB10X_PORT5 + 48, TB10X_PORT5 + 49,
+ TB10X_PORT5 + 50, TB10X_PORT5 + 51,
+ TB10X_PORT5 + 52, TB10X_PORT5 + 53};
+static const unsigned ciplus_pins[] = { TB10X_PORT5 + 0, TB10X_PORT5 + 1,
+ TB10X_PORT5 + 2, TB10X_PORT5 + 3,
+ TB10X_PORT5 + 4, TB10X_PORT5 + 5,
+ TB10X_PORT5 + 6, TB10X_PORT5 + 7,
+ TB10X_PORT5 + 8, TB10X_PORT5 + 9,
+ TB10X_PORT5 + 10, TB10X_PORT5 + 11,
+ TB10X_PORT5 + 12, TB10X_PORT5 + 13,
+ TB10X_PORT5 + 14, TB10X_PORT5 + 15,
+ TB10X_PORT5 + 16, TB10X_PORT5 + 17,
+ TB10X_PORT5 + 18, TB10X_PORT5 + 19,
+ TB10X_PORT5 + 20, TB10X_PORT5 + 21,
+ TB10X_PORT5 + 22, TB10X_PORT5 + 23,
+ TB10X_PORT5 + 24, TB10X_PORT5 + 25,
+ TB10X_PORT5 + 26, TB10X_PORT5 + 27,
+ TB10X_PORT5 + 28, TB10X_PORT5 + 29,
+ TB10X_PORT5 + 30, TB10X_PORT5 + 31,
+ TB10X_PORT5 + 32, TB10X_PORT5 + 33,
+ TB10X_PORT5 + 34, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40, TB10X_PORT5 + 41,
+ TB10X_PORT5 + 42, TB10X_PORT5 + 43,
+ TB10X_PORT5 + 44, TB10X_PORT5 + 45,
+ TB10X_PORT5 + 46, TB10X_PORT5 + 47,
+ TB10X_PORT5 + 48, TB10X_PORT5 + 49,
+ TB10X_PORT5 + 50, TB10X_PORT5 + 51,
+ TB10X_PORT5 + 52, TB10X_PORT5 + 53};
+static const unsigned mcard_pins[] = { TB10X_PORT5 + 3, TB10X_PORT5 + 10,
+ TB10X_PORT5 + 11, TB10X_PORT5 + 12,
+ TB10X_PORT5 + 22, TB10X_PORT5 + 23,
+ TB10X_PORT5 + 33, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40, TB10X_PORT5 + 41,
+ TB10X_PORT5 + 42, TB10X_PORT5 + 43,
+ TB10X_PORT5 + 45, TB10X_PORT5 + 46,
+ TB10X_PORT5 + 47, TB10X_PORT5 + 48,
+ TB10X_PORT5 + 49, TB10X_PORT5 + 50,
+ TB10X_PORT5 + 51, TB10X_PORT5 + 52,
+ TB10X_PORT5 + 53};
+static const unsigned stc0_pins[] = { TB10X_PORT5 + 34, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40};
+static const unsigned stc1_pins[] = { TB10X_PORT5 + 25, TB10X_PORT5 + 26,
+ TB10X_PORT5 + 27, TB10X_PORT5 + 28,
+ TB10X_PORT5 + 29, TB10X_PORT5 + 30,
+ TB10X_PORT5 + 44};
+
+/* Unmuxed GPIOs */
+static const unsigned gpiob_pins[] = { TB10X_GPIOS + 0, TB10X_GPIOS + 1};
+static const unsigned gpiod_pins[] = { TB10X_GPIOS + 2, TB10X_GPIOS + 3};
+static const unsigned gpiof_pins[] = { TB10X_GPIOS + 4, TB10X_GPIOS + 5};
+static const unsigned gpioh_pins[] = { TB10X_GPIOS + 6, TB10X_GPIOS + 7};
+static const unsigned gpioi_pins[] = { TB10X_GPIOS + 8, TB10X_GPIOS + 9,
+ TB10X_GPIOS + 10, TB10X_GPIOS + 11,
+ TB10X_GPIOS + 12, TB10X_GPIOS + 13,
+ TB10X_GPIOS + 14, TB10X_GPIOS + 15,
+ TB10X_GPIOS + 16, TB10X_GPIOS + 17,
+ TB10X_GPIOS + 18, TB10X_GPIOS + 19};
+
+struct tb10x_pinfuncgrp {
+ const char *name;
+ const unsigned int *pins;
+ const unsigned int pincnt;
+ const int port;
+ const unsigned int mode;
+ const int isgpio;
+};
+#define DEFPINFUNCGRP(NAME, PORT, MODE, ISGPIO) { \
+ .name = __stringify(NAME), \
+ .pins = NAME##_pins, .pincnt = ARRAY_SIZE(NAME##_pins), \
+ .port = (PORT), .mode = (MODE), \
+ .isgpio = (ISGPIO), \
+ }
+static const struct tb10x_pinfuncgrp tb10x_pingroups[] = {
+ DEFPINFUNCGRP(mis0, 0, 0, 0),
+ DEFPINFUNCGRP(gpioa, 0, 0, 1),
+ DEFPINFUNCGRP(mis1, 0, 0, 0),
+ DEFPINFUNCGRP(mip1, 0, 1, 0),
+ DEFPINFUNCGRP(mis2, 1, 0, 0),
+ DEFPINFUNCGRP(gpioc, 1, 0, 1),
+ DEFPINFUNCGRP(mis3, 1, 0, 0),
+ DEFPINFUNCGRP(mip3, 1, 1, 0),
+ DEFPINFUNCGRP(mis4, 2, 0, 0),
+ DEFPINFUNCGRP(gpioe, 2, 0, 1),
+ DEFPINFUNCGRP(mis5, 2, 0, 0),
+ DEFPINFUNCGRP(mip5, 2, 1, 0),
+ DEFPINFUNCGRP(mis6, 3, 0, 0),
+ DEFPINFUNCGRP(gpiog, 3, 0, 1),
+ DEFPINFUNCGRP(mis7, 3, 0, 0),
+ DEFPINFUNCGRP(mip7, 3, 1, 0),
+ DEFPINFUNCGRP(gpioj, 4, 0, 1),
+ DEFPINFUNCGRP(gpiok, 4, 0, 1),
+ DEFPINFUNCGRP(ciplus, 4, 1, 0),
+ DEFPINFUNCGRP(mcard, 4, 2, 0),
+ DEFPINFUNCGRP(stc0, 4, 3, 0),
+ DEFPINFUNCGRP(stc1, 4, 3, 0),
+ DEFPINFUNCGRP(mop, 5, 0, 0),
+ DEFPINFUNCGRP(mos0, 5, 1, 0),
+ DEFPINFUNCGRP(mos1, 5, 1, 0),
+ DEFPINFUNCGRP(mos2, 5, 1, 0),
+ DEFPINFUNCGRP(mos3, 5, 1, 0),
+ DEFPINFUNCGRP(uart0, 6, 0, 0),
+ DEFPINFUNCGRP(uart1, 6, 0, 0),
+ DEFPINFUNCGRP(gpiol, 6, 1, 1),
+ DEFPINFUNCGRP(gpiom, 6, 1, 1),
+ DEFPINFUNCGRP(spi3, 7, 0, 0),
+ DEFPINFUNCGRP(jtag, 7, 1, 0),
+ DEFPINFUNCGRP(spi1, 8, 0, 0),
+ DEFPINFUNCGRP(gpion, 8, 1, 1),
+ DEFPINFUNCGRP(gpiob, -1, 0, 1),
+ DEFPINFUNCGRP(gpiod, -1, 0, 1),
+ DEFPINFUNCGRP(gpiof, -1, 0, 1),
+ DEFPINFUNCGRP(gpioh, -1, 0, 1),
+ DEFPINFUNCGRP(gpioi, -1, 0, 1),
+};
+#undef DEFPINFUNCGRP
+
+struct tb10x_of_pinfunc {
+ const char *name;
+ const char *group;
+};
+
+#define TB10X_PORTS (9)
+
+/**
+ * struct tb10x_port - state of an I/O port
+ * @mode: Node this port is currently in.
+ * @count: Number of enabled functions which require this port to be
+ * configured in @mode.
+ */
+struct tb10x_port {
+ unsigned int mode;
+ unsigned int count;
+};
+
+/**
+ * struct tb10x_pinctrl - TB10x pin controller internal state
+ * @pctl: pointer to the pinctrl_dev structure of this pin controller.
+ * @base: register set base address.
+ * @pingroups: pointer to an array of the pin groups this driver manages.
+ * @pinfuncgrpcnt: number of pingroups in @pingroups.
+ * @pinfuncs: pointer to an array of pin functions this driver manages.
+ * @pinfuncnt: number of pin functions in @pinfuncs.
+ * @mutex: mutex for exclusive access to a pin controller's state.
+ * @ports: current state of each port.
+ * @gpios: Indicates if a given pin is currently used as GPIO (1) or not (0).
+ */
+struct tb10x_pinctrl {
+ struct pinctrl_dev *pctl;
+ void *base;
+ const struct tb10x_pinfuncgrp *pingroups;
+ unsigned int pinfuncgrpcnt;
+ struct tb10x_of_pinfunc *pinfuncs;
+ unsigned int pinfuncnt;
+ struct mutex mutex;
+ struct tb10x_port ports[TB10X_PORTS];
+ DECLARE_BITMAP(gpios, MAX_PIN + 1);
+};
+
+static inline void tb10x_pinctrl_set_config(struct tb10x_pinctrl *state,
+ unsigned int port, unsigned int mode)
+{
+ u32 pcfg;
+
+ if (state->ports[port].count)
+ return;
+
+ state->ports[port].mode = mode;
+
+ pcfg = ioread32(state->base) & ~(PCFG_PORT_MASK(port));
+ pcfg |= (mode << (PCFG_PORT_BITWIDTH * port)) & PCFG_PORT_MASK(port);
+ iowrite32(pcfg, state->base);
+}
+
+static inline unsigned int tb10x_pinctrl_get_config(
+ struct tb10x_pinctrl *state,
+ unsigned int port)
+{
+ return (ioread32(state->base) & PCFG_PORT_MASK(port))
+ >> (PCFG_PORT_BITWIDTH * port);
+}
+
+static int tb10x_get_groups_count(struct pinctrl_dev *pctl)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncgrpcnt;
+}
+
+static const char *tb10x_get_group_name(struct pinctrl_dev *pctl, unsigned n)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pingroups[n].name;
+}
+
+static int tb10x_get_group_pins(struct pinctrl_dev *pctl, unsigned n,
+ unsigned const **pins,
+ unsigned * const num_pins)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+
+ *pins = state->pingroups[n].pins;
+ *num_pins = state->pingroups[n].pincnt;
+
+ return 0;
+}
+
+static int tb10x_dt_node_to_map(struct pinctrl_dev *pctl,
+ struct device_node *np_config,
+ struct pinctrl_map **map, unsigned *num_maps)
+{
+ struct pinctrl_map *m;
+ const char *string;
+
+ if (of_property_read_string(np_config, "abilis,function", &string)) {
+ pr_err("%s: No abilis,function property in device tree.\n",
+ np_config->full_name);
+ return -EINVAL;
+ }
+
+ m = kzalloc(sizeof(struct pinctrl_map), GFP_KERNEL);
+ if (!m)
+ return -ENOMEM;
+
+ m->type = PIN_MAP_TYPE_MUX_GROUP;
+ m->data.mux.group = string;
+ m->data.mux.function = np_config->name;
+ *map = m;
+ *num_maps = 1;
+
+ return 0;
+}
+
+static void tb10x_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ kfree(map);
+}
+
+static struct pinctrl_ops tb10x_pinctrl_ops = {
+ .get_groups_count = tb10x_get_groups_count,
+ .get_group_name = tb10x_get_group_name,
+ .get_group_pins = tb10x_get_group_pins,
+ .dt_node_to_map = tb10x_dt_node_to_map,
+ .dt_free_map = tb10x_dt_free_map,
+};
+
+static int tb10x_get_functions_count(struct pinctrl_dev *pctl)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncnt;
+}
+
+static const char *tb10x_get_function_name(struct pinctrl_dev *pctl,
+ unsigned n)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncs[n].name;
+}
+
+static int tb10x_get_function_groups(struct pinctrl_dev *pctl,
+ unsigned n, const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+
+ *groups = &state->pinfuncs[n].group;
+ *num_groups = 1;
+
+ return 0;
+}
+
+static int tb10x_gpio_request_enable(struct pinctrl_dev *pctl,
+ struct pinctrl_gpio_range *range,
+ unsigned pin)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ int muxport = -1;
+ int muxmode = -1;
+ int i;
+
+ mutex_lock(&state->mutex);
+
+ /* Figure out to which port the requested GPIO belongs and how to
+ * configure that port.
+ * This loop also checks for pin conflicts between GPIOs and other
+ * functions.
+ */
+ for (i = 0; i < state->pinfuncgrpcnt; i++) {
+ const struct tb10x_pinfuncgrp *pfg = &state->pingroups[i];
+ unsigned int port = pfg->port;
+ unsigned int mode = pfg->mode;
+ int j;
+
+ /* Skip pin groups which are always mapped and don't need
+ * to be configured.
+ */
+ if (port < 0)
+ continue;
+
+ for (j = 0; j < pfg->pincnt; j++) {
+ if (pin == pfg->pins[j]) {
+ if (pfg->isgpio) {
+ /* Remember the GPIO-only setting of
+ * the port this pin belongs to.
+ */
+ muxport = port;
+ muxmode = mode;
+ } else if (state->ports[port].count
+ && (state->ports[port].mode == mode)) {
+ /* Error: The requested pin is already
+ * used for something else.
+ */
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+ break;
+ }
+ }
+ }
+
+ /* If we haven't returned an error at this point, the GPIO pin is not
+ * used by another function and the GPIO request can be granted:
+ * Register pin as being used as GPIO so we don't allocate it to
+ * another function later.
+ */
+ set_bit(pin, state->gpios);
+
+ /* Potential conflicts between GPIOs and pin functions were caught
+ * earlier in this function and tb10x_pinctrl_set_config will do the
+ * Right Thing, either configure the port in GPIO only mode or leave
+ * another mode compatible with this GPIO request untouched.
+ */
+ if (muxport >= 0)
+ tb10x_pinctrl_set_config(state, muxport, muxmode);
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static void tb10x_gpio_disable_free(struct pinctrl_dev *pctl,
+ struct pinctrl_gpio_range *range,
+ unsigned pin)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+
+ mutex_lock(&state->mutex);
+
+ clear_bit(pin, state->gpios);
+
+ mutex_unlock(&state->mutex);
+}
+
+static int tb10x_pctl_enable(struct pinctrl_dev *pctl,
+ unsigned func_selector, unsigned group_selector)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector];
+ int i;
+
+ if (grp->port < 0)
+ return 0;
+
+ mutex_lock(&state->mutex);
+
+ /* Check if the requested function is compatible with previously
+ * requested functions.
+ */
+ if (state->ports[grp->port].count
+ && (state->ports[grp->port].mode != grp->mode)) {
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+
+ /* Check if the requested function is compatible with previously
+ * requested GPIOs.
+ */
+ for (i = 0; i < grp->pincnt; i++)
+ if (test_bit(grp->pins[i], state->gpios)) {
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+
+ tb10x_pinctrl_set_config(state, grp->port, grp->mode);
+
+ state->ports[grp->port].count++;
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static void tb10x_pctl_disable(struct pinctrl_dev *pctl,
+ unsigned func_selector, unsigned group_selector)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector];
+
+ if (grp->port < 0)
+ return;
+
+ mutex_lock(&state->mutex);
+
+ state->ports[grp->port].count--;
+
+ mutex_unlock(&state->mutex);
+}
+
+static struct pinmux_ops tb10x_pinmux_ops = {
+ .get_functions_count = tb10x_get_functions_count,
+ .get_function_name = tb10x_get_function_name,
+ .get_function_groups = tb10x_get_function_groups,
+ .gpio_request_enable = tb10x_gpio_request_enable,
+ .gpio_disable_free = tb10x_gpio_disable_free,
+ .enable = tb10x_pctl_enable,
+ .disable = tb10x_pctl_disable,
+};
+
+static struct pinctrl_desc tb10x_pindesc = {
+ .name = "TB10x",
+ .pins = tb10x_pins,
+ .npins = ARRAY_SIZE(tb10x_pins),
+ .owner = THIS_MODULE,
+ .pctlops = &tb10x_pinctrl_ops,
+ .pmxops = &tb10x_pinmux_ops,
+};
+
+static int tb10x_pinctrl_probe(struct platform_device *pdev)
+{
+ int ret = -EINVAL;
+ struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct device *dev = &pdev->dev;
+ struct device_node *of_node = dev->of_node;
+ struct device_node *child;
+ struct tb10x_pinctrl *state;
+ int i;
+
+ if (!of_node) {
+ dev_err(dev, "No device tree node found.\n");
+ return -EINVAL;
+ }
+
+ if (!mem) {
+ dev_err(dev, "No memory resource defined.\n");
+ return -EINVAL;
+ }
+
+ state = devm_kzalloc(dev, sizeof(struct tb10x_pinctrl) +
+ of_get_child_count(of_node)
+ * sizeof(struct tb10x_of_pinfunc),
+ GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, state);
+ state->pinfuncs = (struct tb10x_of_pinfunc *)(state + 1);
+ mutex_init(&state->mutex);
+
+ state->base = devm_ioremap_resource(dev, mem);
+ if (!state->base) {
+ dev_err(dev, "Request register region failed.\n");
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ state->pingroups = tb10x_pingroups;
+ state->pinfuncgrpcnt = ARRAY_SIZE(tb10x_pingroups);
+
+ for (i = 0; i < TB10X_PORTS; i++)
+ state->ports[i].mode = tb10x_pinctrl_get_config(state, i);
+
+ for_each_child_of_node(of_node, child) {
+ const char *name;
+
+ if (!of_property_read_string(child, "abilis,function",
+ &name)) {
+ state->pinfuncs[state->pinfuncnt].name = child->name;
+ state->pinfuncs[state->pinfuncnt].group = name;
+ state->pinfuncnt++;
+ }
+ }
+
+ state->pctl = pinctrl_register(&tb10x_pindesc, dev, state);
+ if (IS_ERR(state->pctl)) {
+ dev_err(dev, "could not register TB10x pin driver\n");
+ ret = PTR_ERR(state->pctl);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ mutex_destroy(&state->mutex);
+ return ret;
+}
+
+static int tb10x_pinctrl_remove(struct platform_device *pdev)
+{
+ struct tb10x_pinctrl *state = platform_get_drvdata(pdev);
+
+ pinctrl_unregister(state->pctl);
+ mutex_destroy(&state->mutex);
+
+ return 0;
+}
+
+
+static const struct of_device_id tb10x_pinctrl_dt_ids[] = {
+ { .compatible = "abilis,tb10x-iomux" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tb10x_pinctrl_dt_ids);
+
+static struct platform_driver tb10x_pinctrl_pdrv = {
+ .probe = tb10x_pinctrl_probe,
+ .remove = tb10x_pinctrl_remove,
+ .driver = {
+ .name = "tb10x_pinctrl",
+ .of_match_table = of_match_ptr(tb10x_pinctrl_dt_ids),
+ .owner = THIS_MODULE
+ }
+};
+
+static int __init tb10x_iopinctrl_init(void)
+{
+ return platform_driver_register(&tb10x_pinctrl_pdrv);
+}
+
+static void __exit tb10x_iopinctrl_exit(void)
+{
+ platform_driver_unregister(&tb10x_pinctrl_pdrv);
+}
+
+MODULE_AUTHOR("Christian Ruppert <[email protected]>");
+MODULE_LICENSE("GPL");
+module_init(tb10x_iopinctrl_init);
+module_exit(tb10x_iopinctrl_exit);
--
1.7.1

2013-10-08 13:26:36

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH 01/03] Make non-linear GPIO ranges accesible from gpiolib

This patch adds the infrastructure required to register non-linear gpio
ranges through gpiolib and the standard GPIO device tree bindings.

Signed-off-by: Christian Ruppert <[email protected]>
---
Documentation/devicetree/bindings/gpio/gpio.txt | 40 +++++++++++++++++++
drivers/gpio/gpiolib-of.c | 36 +++++++++++++----
drivers/gpio/gpiolib.c | 47 +++++++++++++++++++++++
drivers/pinctrl/core.c | 23 +++++++++++
include/asm-generic/gpio.h | 10 +++++
include/linux/gpio.h | 9 ++++
include/linux/pinctrl/pinctrl.h | 3 +
7 files changed, 160 insertions(+), 8 deletions(-)

diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
index d933af3..da0e7fb 100644
--- a/Documentation/devicetree/bindings/gpio/gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio.txt
@@ -112,3 +112,43 @@ where,
Here, a single GPIO controller has GPIOs 0..9 routed to pin controller
pinctrl1's pins 20..29, and GPIOs 10..19 routed to pin controller pinctrl2's
pins 50..59.
+
+In addition, named groups of pins can be mapped to pin groups of a given
+pin controller using the gpio-ranges property as described below:
+
+ gpio-range-list ::= <single-gpio-range> [gpio-range-list]
+ single-gpio-range ::= <numeric-gpio-range> | <named-gpio-range>
+ numeric-gpio-range ::=
+ <pinctrl-phandle> <gpio-base> <pinctrl-base> <count>
+ named-gpio-range ::= <pinctrl-phandle> <gpio-base> '<0 0>'
+ pinctrl-phanle : phandle to pin controller node.
+ gpio-base : Base GPIO ID in the GPIO controller
+ pinctrl-base : Base pinctrl pin ID in the pin controller
+ count : The number of GPIOs/pins in this range
+
+In this case, the property gpio-ranges-group-names contains one string for
+every single-gpio-range in gpio-ranges:
+ gpiorange-names-list ::= <gpiorange-name> [gpiorange-names-list]
+ gpiorange-name : Name of the pingroup associated to the GPIO range in
+ the respective pin controller.
+
+Example:
+
+ gpio_pio_i: gpio-controller@14B0 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1480 0x18>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl1 0 20 10>,
+ <&pinctrl2 10 0 0>,
+ <&pinctrl1 15 0 10>,
+ <&pinctrl2 25 0 0>;
+ gpio-ranges-group-names = "",
+ "foo",
+ "",
+ "bar";
+ };
+
+Here, three GPIO ranges are defined wrt. two pin controllers. pinctrl1 GPIO
+ranges are defined using pin numbers whereas the GPIO ranges wrt. pinctrl2
+are named "foo" and "bar".
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 665f953..07a7b81 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -189,6 +189,7 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
struct of_phandle_args pinspec;
struct pinctrl_dev *pctldev;
int index = 0, ret;
+ const char *name;

if (!np)
return;
@@ -203,14 +204,33 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
if (!pctldev)
break;

- ret = gpiochip_add_pin_range(chip,
- pinctrl_dev_get_devname(pctldev),
- pinspec.args[0],
- pinspec.args[1],
- pinspec.args[2]);
-
- if (ret)
- break;
+ if (pinspec.args[2]) {
+ /* npins != 0: linear range */
+ ret = gpiochip_add_pin_range(chip,
+ pinctrl_dev_get_devname(pctldev),
+ pinspec.args[0],
+ pinspec.args[1],
+ pinspec.args[2]);
+ if (ret)
+ break;
+ } else {
+ /* npins == 0: special range */
+ if (pinspec.args[1]) {
+ pr_err("%s: Illegal gpio-range format.\n",
+ np->full_name);
+ break;
+ }
+ ret = of_property_read_string_index(np,
+ "gpio-ranges-group-names",
+ index, &name);
+ if (ret)
+ break;
+
+ ret = gpiochip_add_pingroup_range(chip, pctldev,
+ pinspec.args[0], name);
+ if (ret)
+ break;
+ }
}
}

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index ff0fd65..c75002b 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1320,6 +1320,53 @@ EXPORT_SYMBOL_GPL(gpiochip_find);
#ifdef CONFIG_PINCTRL

/**
+ * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping
+ * @chip: the gpiochip to add the range for
+ * @pinctrl: the dev_name() of the pin controller to map to
+ * @gpio_offset: the start offset in the current gpio_chip number space
+ * @pin_group: name of the pin group inside the pin controller
+ */
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ struct gpio_pin_range *pin_range;
+ int ret;
+
+ pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
+ if (!pin_range) {
+ pr_err("%s: GPIO chip: failed to allocate pin ranges\n",
+ chip->label);
+ return -ENOMEM;
+ }
+
+ /* Use local offset as range ID */
+ pin_range->range.id = gpio_offset;
+ pin_range->range.gc = chip;
+ pin_range->range.name = chip->label;
+ pin_range->range.base = chip->base + gpio_offset;
+ pin_range->pctldev = pctldev;
+
+ ret = pinctrl_add_gpio_pingrp_range(pctldev, &pin_range->range,
+ pin_group);
+ if (ret < 0) {
+ pr_err("%s: GPIO chip: could not create pin range %s\n",
+ chip->label, pin_group);
+ return ret;
+ }
+
+ pr_debug("GPIO chip %s: created GPIO range %d->%d ==> %s PINGRP %s\n",
+ chip->label, gpio_offset,
+ gpio_offset + pin_range->range.npins - 1,
+ pinctrl_dev_get_devname(pctldev), pin_group);
+
+ list_add_tail(&pin_range->node, &chip->pin_ranges);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
+
+/**
* gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping
* @chip: the gpiochip to add the range for
* @pinctrl_name: the dev_name() of the pin controller to map to
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index 2a00239..52e2e6f 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -456,6 +456,29 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
}
EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range);

+int pinctrl_add_gpio_pingrp_range(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ char *pin_group)
+{
+ const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ int group_selector, ret;
+
+ group_selector = pinctrl_get_group_selector(pctldev, pin_group);
+ if (group_selector < 0)
+ return group_selector;
+
+ ret = pctlops->get_group_pins(pctldev, group_selector,
+ &range->pins,
+ &range->npins);
+ if (ret < 0)
+ return ret;
+
+ pinctrl_add_gpio_range(pctldev, range);
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(pinctrl_add_gpio_pingrp_range);
+
/**
* pinctrl_find_gpio_range_from_pin() - locate the GPIO range for a pin
* @pctldev: the pin controller device to look in
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index bde6469..523f405 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -228,6 +228,9 @@ struct gpio_pin_range {
int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
unsigned int gpio_offset, unsigned int pin_offset,
unsigned int npins);
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group);
void gpiochip_remove_pin_ranges(struct gpio_chip *chip);

#else
@@ -239,6 +242,13 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
{
return 0;
}
+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ return 0;
+}

static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 552e3f4..234b32f 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -220,6 +220,15 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
return -EINVAL;
}

+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ WARN_ON(1);
+ return -EINVAL;
+}
+
static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
{
diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 5979147..47ab2fb 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -141,6 +141,9 @@ extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev,

extern struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
struct pinctrl_gpio_range *range);
+extern int pinctrl_add_gpio_pingrp_range(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ char *pin_group);
extern struct pinctrl_gpio_range *
pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
unsigned int pin);
--
1.7.1

2013-10-09 11:58:39

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 01/03] Make non-linear GPIO ranges accesible from gpiolib

On Tue, Oct 8, 2013 at 2:25 PM, Christian Ruppert
<[email protected]> wrote:

> This patch adds the infrastructure required to register non-linear gpio
> ranges through gpiolib and the standard GPIO device tree bindings.
>
> Signed-off-by: Christian Ruppert <[email protected]>

I understand the goal of this patch, and why you want it this way,
but it needs some scrutiny, especially from the device tree people.

> Here, a single GPIO controller has GPIOs 0..9 routed to pin controller
> pinctrl1's pins 20..29, and GPIOs 10..19 routed to pin controller pinctrl2's
> pins 50..59.

(Yes this is previous text)

This is quite straight-forward since it deals with a very definitive
coupling of a physical pin and a GPIO line.

> +In addition, named groups of pins can be mapped to pin groups of a given
> +pin controller using the gpio-ranges property as described below:
> +
> + gpio-range-list ::= <single-gpio-range> [gpio-range-list]
> + single-gpio-range ::= <numeric-gpio-range> | <named-gpio-range>
> + numeric-gpio-range ::=
> + <pinctrl-phandle> <gpio-base> <pinctrl-base> <count>
> + named-gpio-range ::= <pinctrl-phandle> <gpio-base> '<0 0>'
> + pinctrl-phanle : phandle to pin controller node.

phanle really?

> + gpio-base : Base GPIO ID in the GPIO controller
> + pinctrl-base : Base pinctrl pin ID in the pin controller
> + count : The number of GPIOs/pins in this range
(...)

You did not describe here that count can be 0 if a group is
given and that 0 is a valid count for that case.

> +In this case, the property gpio-ranges-group-names contains one string for
> +every single-gpio-range in gpio-ranges:
> + gpiorange-names-list ::= <gpiorange-name> [gpiorange-names-list]
> + gpiorange-name : Name of the pingroup associated to the GPIO range in
> + the respective pin controller.
(...)
> +Example:
> +
> + gpio_pio_i: gpio-controller@14B0 {
> + #gpio-cells = <2>;
> + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
> + reg = <0x1480 0x18>;
> + gpio-controller;
> + gpio-ranges = <&pinctrl1 0 20 10>,
> + <&pinctrl2 10 0 0>,
> + <&pinctrl1 15 0 10>,
> + <&pinctrl2 25 0 0>;
> + gpio-ranges-group-names = "",
> + "foo",
> + "",
> + "bar";
> + };

And here, I get a bit uneasy as I remember Stephen's stance that such
groups must be a physical property of the controller. I am generally a
bit soft on that, but that is from a driver point of view, and describing
hardware in the devicetree must be reflecting an objective view of the
hardware, not how the particular driver is written for Linux.

This is very questionable :-/

Are you sure this is useable on Windows and OpenBSD without
first verbatim copying the structure of the Linux pinctrl driver?

I am not 100% sure of this case but maybe I will come to realize
as I read the rest of the patches...

Empty group names for "anonymous" groups (I guess these become
linear then?) is really looking strange. They are empty strings with
a semantic effect, that is quite disturbing. but I don't know a better
way either.

Clarify that the number of ranges names must match the array of
ranges, and that if you don't supply group names all ranges
will be linear.


> - ret = gpiochip_add_pin_range(chip,
> - pinctrl_dev_get_devname( pctldev),
> - pinspec.args[0],
> - pinspec.args[1],
> - pinspec.args[2]);
> -
> - if (ret)
> - break;
> + if (pinspec.args[2]) {
> + /* npins != 0: linear range */
> + ret = gpiochip_add_pin_range(chip,
> + pinctrl_dev_get_devname(pctldev),
> + pinspec.args[0],
> + pinspec.args[1],
> + pinspec.args[2]);
> + if (ret)
> + break;
> + } else {
> + /* npins == 0: special range */
> + if (pinspec.args[1]) {
> + pr_err("%s: Illegal gpio-range format.\n",
> + np->full_name);
> + break;
> + }
> + ret = of_property_read_string_index(np,
> + "gpio-ranges-group-names",
> + index, &name);
> + if (ret)
> + break;
> +
> + ret = gpiochip_add_pingroup_range(chip, pctldev,
> + pinspec.args[0], name);
> + if (ret)
> + break;
> + }

Here you just alter the runpath if the count argument is zero.

Try to be more strict:

- If the count argument is zero, the
gpio-ranges-group-names index *must* also be set to a non-empty
string.

- If the count argument is non-zeo, the gpio-ranges-group-names
index *must* be an empty string, or the gpio-ranges-group-names
must be non-existant (you have to check for it's presence before
entering the iterating loop).

Both above checks need to be implemented and proper
error messages printed on detected errors.

(...)
> diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
> index 2a00239..52e2e6f 100644
> --- a/drivers/pinctrl/core.c
> +++ b/drivers/pinctrl/core.c
> @@ -456,6 +456,29 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
> }
> EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range);
>
> +int pinctrl_add_gpio_pingrp_range(struct pinctrl_dev *pctldev,
> + struct pinctrl_gpio_range *range,
> + char *pin_group)
> +{
> + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
> + int group_selector, ret;
> +
> + group_selector = pinctrl_get_group_selector(pctldev, pin_group);
> + if (group_selector < 0)
> + return group_selector;
> +
> + ret = pctlops->get_group_pins(pctldev, group_selector,
> + &range->pins,
> + &range->npins);
> + if (ret < 0)
> + return ret;
> +
> + pinctrl_add_gpio_range(pctldev, range);
> + return 0;
> +
> +}
> +EXPORT_SYMBOL_GPL(pinctrl_add_gpio_pingrp_range);

I don't think this is very elegant. It's quite strange that the range partly
populated from the GPIO side gets the corresponding pins filled
in by this function. Can't we think about something more elegant?
It reflects the strangeness I'm reacting to above I guess.

Yours,
Linus Walleij

2013-10-09 12:19:22

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 03/03] GPIO: Add TB10x GPIO driver

On Tue, Oct 8, 2013 at 2:25 PM, Christian Ruppert
<[email protected]> wrote:

Overall this driver is looking very nice, we just need to figure out this
group range concept in the other patch.

> +Example:
> +
> + gpioa: gpio@FF140000 {
> + compatible = "abilis,tb10x-gpio";
> + interrupt-controller;
> + #interrupt-cells = <1>;
> + interrupt-parent = <&tb10x_ictl>;
> + interrupts = <27 2>;

So this is cascaded off some HWIRQ offset 27 on some interrupt
controller, OK...

> + reg = <0xFF140000 0x1000>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + abilis,ngpio = <3>;
> + gpio-ranges = <&iomux 0 0 0>;
> + gpio-ranges-group-names = "gpioa_pins";
> + };


But this thing:

(...)
> +static irqreturn_t tb10x_gpio_irq_cascade(int irq, void *data)
> +{
> + struct tb10x_gpio *tb10x_gpio = data;
> + u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_CHANGE);
> + u32 m = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);
> + const unsigned long bits = r & m;
> + int i;
> +
> + for_each_set_bit(i, &bits, 32)
> + generic_handle_irq(irq_find_mapping(tb10x_gpio->domain, i));
> +
> + return IRQ_HANDLED;
> +}

(...)
> + ret = platform_get_irq(pdev, 0);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "No interrupt specified.\n");
> + goto fail_get_irq;
> + }
> +
> + tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
> + tb10x_gpio->irq = ret;
> +
> + ret = devm_request_irq(&pdev->dev, ret, tb10x_gpio_irq_cascade,
> + IRQF_TRIGGER_NONE | IRQF_SHARED,
> + dev_name(&pdev->dev), tb10x_gpio);

Why aren't you simply using

irq_set_chained_handler()
irq_set_handler_data(tb10x_gpio);

And in the handler function that need a signature like
this:

static void tb10x_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
{
struct tb10x_gpio *tb10x_gpio = = irq_get_handler_data(irq);
struct irq_chip *host_chip = irq_get_chip(irq);
chained_irq_enter(host_chip, desc);
(...)
chained_irq_exit(host_chip, desc);
}

?

It's not like I'm 100% certain on where to use one or the other
construct (a mechanism like the above is needed for threaded
IRQs I've noticed) but the chained handler seems more to the
point does it not?

The only downside I've seen is that the parent IRQ does not get
a name and the accumulated IRQ stats in /proc/interrupts but
surely we can live without that (or fix it).

Since I'm a bit rusty on chained IRQs correct me if I'm wrong...

Yours,
Linus Walleij

2013-10-09 12:30:50

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 02/03] pinmux: Add TB10x pinmux driver

On Tue, Oct 8, 2013 at 2:25 PM, Christian Ruppert
<[email protected]> wrote:

> The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
> Used to control the pinmux and is a prerequisite for the GPIO driver.
>
> Signed-off-by: Christian Ruppert <[email protected]>
> Signed-off-by: Pierrick Hascoet <[email protected]>

Overall this is looking pretty nice!

> +static int tb10x_dt_node_to_map(struct pinctrl_dev *pctl,
> + struct device_node *np_config,
> + struct pinctrl_map **map, unsigned *num_maps)
> +{
> + struct pinctrl_map *m;
> + const char *string;
> +
> + if (of_property_read_string(np_config, "abilis,function", &string)) {
> + pr_err("%s: No abilis,function property in device tree.\n",
> + np_config->full_name);
> + return -EINVAL;
> + }
> +
> + m = kzalloc(sizeof(struct pinctrl_map), GFP_KERNEL);
> + if (!m)
> + return -ENOMEM;

Could you make use of:

#include "pinctrl-utils.h"

pinctrl_utils_reserve_map()
pinctrl_utils_add_map_mux()
pinctrl_utils_dt_free_map()

?

> +static int tb10x_gpio_request_enable(struct pinctrl_dev *pctl,
> + struct pinctrl_gpio_range *range,
> + unsigned pin)
> +{
> + struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
> + int muxport = -1;
> + int muxmode = -1;
> + int i;
> +
> + mutex_lock(&state->mutex);
> +
> + /* Figure out to which port the requested GPIO belongs and how to
> + * configure that port.
> + * This loop also checks for pin conflicts between GPIOs and other
> + * functions.
> + */

/*
* I really like all comments with a blank
* line at the top and bottom like this.
*/

Yours,
Linus Walleij

2013-10-09 13:29:09

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 01/03] Make non-linear GPIO ranges accesible from gpiolib

On Wed, Oct 09, 2013 at 01:58:35PM +0200, Linus Walleij wrote:
> On Tue, Oct 8, 2013 at 2:25 PM, Christian Ruppert
> <[email protected]> wrote:
>
> > This patch adds the infrastructure required to register non-linear gpio
> > ranges through gpiolib and the standard GPIO device tree bindings.
> >
> > Signed-off-by: Christian Ruppert <[email protected]>
>
> I understand the goal of this patch, and why you want it this way,
> but it needs some scrutiny, especially from the device tree people.
>
> > Here, a single GPIO controller has GPIOs 0..9 routed to pin controller
> > pinctrl1's pins 20..29, and GPIOs 10..19 routed to pin controller pinctrl2's
> > pins 50..59.
>
> (Yes this is previous text)
>
> This is quite straight-forward since it deals with a very definitive
> coupling of a physical pin and a GPIO line.
>
> > +In addition, named groups of pins can be mapped to pin groups of a given
> > +pin controller using the gpio-ranges property as described below:
> > +
> > + gpio-range-list ::= <single-gpio-range> [gpio-range-list]
> > + single-gpio-range ::= <numeric-gpio-range> | <named-gpio-range>
> > + numeric-gpio-range ::=
> > + <pinctrl-phandle> <gpio-base> <pinctrl-base> <count>
> > + named-gpio-range ::= <pinctrl-phandle> <gpio-base> '<0 0>'
> > + pinctrl-phanle : phandle to pin controller node.
>
> phanle really?

Yes, this is actually identical to the numeric ranges. It specifies the
pin controller which is "responsible" for the pins/pin group in
question.

> > + gpio-base : Base GPIO ID in the GPIO controller
> > + pinctrl-base : Base pinctrl pin ID in the pin controller
> > + count : The number of GPIOs/pins in this range
> (...)
>
> You did not describe here that count can be 0 if a group is
> given and that 0 is a valid count for that case.

It is implicit in "named-gpio-range ::= <pinctrl-phandle> <gpio-base> '<0 0>'"
but you are probably right that this needs to be more explicit. When
rereading the whole section now (with all the changes that were made to
the documentation since the patch was originally written), I think I'll
rewrite this paragraph entirely, integrating it in the explanation of
numeric ranges.

> > +In this case, the property gpio-ranges-group-names contains one string for
> > +every single-gpio-range in gpio-ranges:
> > + gpiorange-names-list ::= <gpiorange-name> [gpiorange-names-list]
> > + gpiorange-name : Name of the pingroup associated to the GPIO range in
> > + the respective pin controller.
> (...)
> > +Example:
> > +
> > + gpio_pio_i: gpio-controller@14B0 {
> > + #gpio-cells = <2>;
> > + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
> > + reg = <0x1480 0x18>;
> > + gpio-controller;
> > + gpio-ranges = <&pinctrl1 0 20 10>,
> > + <&pinctrl2 10 0 0>,
> > + <&pinctrl1 15 0 10>,
> > + <&pinctrl2 25 0 0>;
> > + gpio-ranges-group-names = "",
> > + "foo",
> > + "",
> > + "bar";
> > + };
>
> And here, I get a bit uneasy as I remember Stephen's stance that such
> groups must be a physical property of the controller. I am generally a
> bit soft on that, but that is from a driver point of view, and describing
> hardware in the devicetree must be reflecting an objective view of the
> hardware, not how the particular driver is written for Linux.

The named pin groups actually are a physical property of the pin
controller hardware (well, at least the pin groups are, the names are
not ;). Obviously, a driver (no matter for which OS) must be aware of
this hardware property so it can decide which value to write to which
register in order to control the pin mux. If we don't want to split (or
worse: duplicate) that information in the driver and device tree, I see
two alternatives.
1. Define register offsets, masks and register values and the list of
pins controlled by those register/value pairs in the device tree.
2. Define register offsets, masks and register values and the list of
pins controlled by those register/value pairs in the drivers. Define
a portable high level interface for the device tree.

My personal preference is clearly 2.

> This is very questionable :-/
>
> Are you sure this is useable on Windows and OpenBSD without
> first verbatim copying the structure of the Linux pinctrl driver?

Without knowing the pinctrl infrastructure in either Windows or OpenBSD,
I am actually convinced that this is _more_ portable than the numeric
ranges:
- The numeric ranges used today depend on a numbering scheme which
is specific to the Linux pinctrl framework. One could very well
imagine pinctrl frameworks without any numbering scheme at all.
- The actual numbers depend on the particulars of each individual Linux
pinctrl driver implementation. There is no guarantee that such a
numbering is compatible with pin numbering constraints of other OSses.
Nobody would be surprised if any given OS didn't allow sparse pin
numbering ranges, for example.

Adding the higher level concept of named pin groups (which accidentally
exists in Linux already) seems to be an elegant path to better
portability here.

> I am not 100% sure of this case but maybe I will come to realize
> as I read the rest of the patches...
>
> Empty group names for "anonymous" groups (I guess these become
> linear then?) is really looking strange. They are empty strings with
> a semantic effect, that is quite disturbing. but I don't know a better
> way either.

The alternative would probably be to skip them altogether but I'm afraid
that that makes it even worse...

> Clarify that the number of ranges names must match the array of
> ranges, and that if you don't supply group names all ranges
> will be linear.

OK. Will be done when rewriting the documentation.

> > - ret = gpiochip_add_pin_range(chip,
> > - pinctrl_dev_get_devname( pctldev),
> > - pinspec.args[0],
> > - pinspec.args[1],
> > - pinspec.args[2]);
> > -
> > - if (ret)
> > - break;
> > + if (pinspec.args[2]) {
> > + /* npins != 0: linear range */
> > + ret = gpiochip_add_pin_range(chip,
> > + pinctrl_dev_get_devname(pctldev),
> > + pinspec.args[0],
> > + pinspec.args[1],
> > + pinspec.args[2]);
> > + if (ret)
> > + break;
> > + } else {
> > + /* npins == 0: special range */
> > + if (pinspec.args[1]) {
> > + pr_err("%s: Illegal gpio-range format.\n",
> > + np->full_name);
> > + break;
> > + }
> > + ret = of_property_read_string_index(np,
> > + "gpio-ranges-group-names",
> > + index, &name);
> > + if (ret)
> > + break;
> > +
> > + ret = gpiochip_add_pingroup_range(chip, pctldev,
> > + pinspec.args[0], name);
> > + if (ret)
> > + break;
> > + }
>
> Here you just alter the runpath if the count argument is zero.
>
> Try to be more strict:
>
> - If the count argument is zero, the
> gpio-ranges-group-names index *must* also be set to a non-empty
> string.
>
> - If the count argument is non-zeo, the gpio-ranges-group-names
> index *must* be an empty string, or the gpio-ranges-group-names
> must be non-existant (you have to check for it's presence before
> entering the iterating loop).
>
> Both above checks need to be implemented and proper
> error messages printed on detected errors.

OK.

> > diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
> > index 2a00239..52e2e6f 100644
> > --- a/drivers/pinctrl/core.c
> > +++ b/drivers/pinctrl/core.c
> > @@ -456,6 +456,29 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
> > }
> > EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range);
> >
> > +int pinctrl_add_gpio_pingrp_range(struct pinctrl_dev *pctldev,
> > + struct pinctrl_gpio_range *range,
> > + char *pin_group)
> > +{
> > + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
> > + int group_selector, ret;
> > +
> > + group_selector = pinctrl_get_group_selector(pctldev, pin_group);
> > + if (group_selector < 0)
> > + return group_selector;
> > +
> > + ret = pctlops->get_group_pins(pctldev, group_selector,
> > + &range->pins,
> > + &range->npins);
> > + if (ret < 0)
> > + return ret;
> > +
> > + pinctrl_add_gpio_range(pctldev, range);
> > + return 0;
> > +
> > +}
> > +EXPORT_SYMBOL_GPL(pinctrl_add_gpio_pingrp_range);
>
> I don't think this is very elegant. It's quite strange that the range partly
> populated from the GPIO side gets the corresponding pins filled
> in by this function. Can't we think about something more elegant?
> It reflects the strangeness I'm reacting to above I guess.

I don't see in which respect the existence of this function is different
from the existence of pinctrl_find_and_add_gpio_range (the sole purpose
of which is to be called from gpiochip_add_pin_range). I like the idea
of being strict about the gpio framework only managing GPIO related
things and the pinctrl framework only managing pinctrl related things.
Thus, I adopted this concept to gpiochip_add_pingroup_range (which calls
pinctrl_add_gpio_pingrp_range).

Tell me if you want me to migrate the get_group_pins call to
gpiochip_add_pingroup_range but IMHO this would be less elegant.

Greetings,
Christian

2013-10-09 14:01:08

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 01/03] Make non-linear GPIO ranges accesible from gpiolib

On Wed, Oct 9, 2013 at 3:28 PM, Christian Ruppert
<[email protected]> wrote:
> On Wed, Oct 09, 2013 at 01:58:35PM +0200, Linus Walleij wrote:
>> On Tue, Oct 8, 2013 at 2:25 PM, Christian Ruppert
>> <[email protected]> wrote:
>> > +In addition, named groups of pins can be mapped to pin groups of a given
>> > +pin controller using the gpio-ranges property as described below:
>> > +
>> > + gpio-range-list ::= <single-gpio-range> [gpio-range-list]
>> > + single-gpio-range ::= <numeric-gpio-range> | <named-gpio-range>
>> > + numeric-gpio-range ::=
>> > + <pinctrl-phandle> <gpio-base> <pinctrl-base> <count>
>> > + named-gpio-range ::= <pinctrl-phandle> <gpio-base> '<0 0>'
>> > + pinctrl-phanle : phandle to pin controller node.
>>
>> phanle really?
>
> Yes, this is actually identical to the numeric ranges. It specifies the
> pin controller which is "responsible" for the pins/pin group in
> question.

I was more thinking about the speling, should it not be "phandle"?

> The named pin groups actually are a physical property of the pin
> controller hardware (well, at least the pin groups are, the names are
> not ;).

OK.

It would be good if the names of the group could match, as close
as possible, the names given to the same identity in the data
sheet or similar documentation.

> Obviously, a driver (no matter for which OS) must be aware of
> this hardware property so it can decide which value to write to which
> register in order to control the pin mux. If we don't want to split (or
> worse: duplicate) that information in the driver and device tree, I see
> two alternatives.
> 1. Define register offsets, masks and register values and the list of
> pins controlled by those register/value pairs in the device tree.

This is not a viable alternative of course.

> 2. Define register offsets, masks and register values and the list of
> pins controlled by those register/value pairs in the drivers. Define
> a portable high level interface for the device tree.
>
> My personal preference is clearly 2.

Yes you are also making a very good case for it, and I tend to
think the same.

But alt 1 is not the alternative, the alternative (3) is to reference
the pin numbers in the device tree, if need be by a largeish
set of one-pin-per-range ranges if these are disparate.

Which is arguably quite ugly compared to (2) as well, but atleast
reasonable.

>> Are you sure this is useable on Windows and OpenBSD without
>> first verbatim copying the structure of the Linux pinctrl driver?
>
> Without knowing the pinctrl infrastructure in either Windows or OpenBSD,
> I am actually convinced that this is _more_ portable than the numeric
> ranges:
>
> - The numeric ranges used today depend on a numbering scheme which
> is specific to the Linux pinctrl framework.

Not really, it's just a number, it is quite possible for driver developers
to have this match an index number from the hardware documentation.
Also these numbers are sparse, they don't have to be an array that
begin with 0 and go to N. So *any* discrete numbering scheme will
do, and it's not any more strange than the named groups, instead of
putting five apples in a basket and labeling it "group" you can number
the five apples { 7, 19, 22, 67, 216 } and say each one belongs in
the basket.

> One could very well
> imagine pinctrl frameworks without any numbering scheme at all.

Whether numbers or text labels are used to label the physical
entities does not matter, what matters is that the device tree
binding documentation is implementable by several OS:es.

> - The actual numbers depend on the particulars of each individual Linux
> pinctrl driver implementation.

No, not from a device tree point of view. It is possible to select
a numbering scheme coming from the device tree and use that
in the driver instead of the other way around.

Do not always assume that the driver comes first. It is possible
to enumerate the pins in a DT binding and tree before even starting
to implement the driver, and the pinctrl framework will cope with
whatever number scheme you choose.

> Adding the higher level concept of named pin groups (which accidentally
> exists in Linux already) seems to be an elegant path to better
> portability here.

Same same, but it deals with sets of pins instead of individual
pins.

>> Empty group names for "anonymous" groups (I guess these become
>> linear then?) is really looking strange. They are empty strings with
>> a semantic effect, that is quite disturbing. but I don't know a better
>> way either.
>
> The alternative would probably be to skip them altogether but I'm afraid
> that that makes it even worse...

We'll have to live with this...

>> > diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
>> > index 2a00239..52e2e6f 100644
>> > --- a/drivers/pinctrl/core.c
>> > +++ b/drivers/pinctrl/core.c
>> > @@ -456,6 +456,29 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
>> > }
>> > EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range);
>> >
>> > +int pinctrl_add_gpio_pingrp_range(struct pinctrl_dev *pctldev,
>> > + struct pinctrl_gpio_range *range,
>> > + char *pin_group)
>> > +{
>> > + const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
>> > + int group_selector, ret;
>> > +
>> > + group_selector = pinctrl_get_group_selector(pctldev, pin_group);
>> > + if (group_selector < 0)
>> > + return group_selector;
>> > +
>> > + ret = pctlops->get_group_pins(pctldev, group_selector,
>> > + &range->pins,
>> > + &range->npins);
>> > + if (ret < 0)
>> > + return ret;
>> > +
>> > + pinctrl_add_gpio_range(pctldev, range);
>> > + return 0;
>> > +
>> > +}
>> > +EXPORT_SYMBOL_GPL(pinctrl_add_gpio_pingrp_range);
>>
>> I don't think this is very elegant. It's quite strange that the range partly
>> populated from the GPIO side gets the corresponding pins filled
>> in by this function. Can't we think about something more elegant?
>> It reflects the strangeness I'm reacting to above I guess.
>
> I don't see in which respect the existence of this function is different
> from the existence of pinctrl_find_and_add_gpio_range (the sole purpose
> of which is to be called from gpiochip_add_pin_range). I like the idea
> of being strict about the gpio framework only managing GPIO related
> things and the pinctrl framework only managing pinctrl related things.
> Thus, I adopted this concept to gpiochip_add_pingroup_range (which calls
> pinctrl_add_gpio_pingrp_range).
>
> Tell me if you want me to migrate the get_group_pins call to
> gpiochip_add_pingroup_range but IMHO this would be less elegant.

Hm there are several ways to skin this cat but well, this is OK.

Yours,
Linus Walleij

2013-10-10 20:47:31

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 01/03] Make non-linear GPIO ranges accesible from gpiolib

On 10/09/2013 05:58 AM, Linus Walleij wrote:
> On Tue, Oct 8, 2013 at 2:25 PM, Christian Ruppert
> <[email protected]> wrote:
>> This patch adds the infrastructure required to register non-linear gpio
>> ranges through gpiolib and the standard GPIO device tree bindings.
...
>> +Example:
>> +
>> + gpio_pio_i: gpio-controller@14B0 {
>> + #gpio-cells = <2>;
>> + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
>> + reg = <0x1480 0x18>;
>> + gpio-controller;
>> + gpio-ranges = <&pinctrl1 0 20 10>,
>> + <&pinctrl2 10 0 0>,
>> + <&pinctrl1 15 0 10>,
>> + <&pinctrl2 25 0 0>;
>> + gpio-ranges-group-names = "",
>> + "foo",
>> + "",
>> + "bar";
>> + };
>
> And here, I get a bit uneasy as I remember Stephen's stance that such
> groups must be a physical property of the controller. I am generally a
> bit soft on that, but that is from a driver point of view, and describing
> hardware in the devicetree must be reflecting an objective view of the
> hardware, not how the particular driver is written for Linux.
>
> This is very questionable :-/

I think describing the GPIO ranges is an aspect of the HW; it's
describing actual routing of signal lines between the GPIO and pinctrl
HW blocks. It seems reasonable for DT to describe that concept (although
perhaps the actual mapping could be part of some driver rather than DT,
but that's a separate issue).

Using groups to describe aspects of this mapping doesn't seem totally
unreasonable either. We have named groups for muxing, which is a real HW
property. We can have named groups for GPIO/pinctrl routing, which again
is a HW property. Certainly in the muxing case, and hopefully for this
case too, the naming of those groups is driven purely by the HW design
of the SoC top-level or GPIO/pinctrl HW blocks, and hence it's
reasonable for a DT binding to define (or rather, use) those names.
Hopefully different OSs don't use different names for the groups, since
the naming is derived directly from HW.

2013-10-10 20:49:19

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH 01/03] Make non-linear GPIO ranges accesible from gpiolib

On 10/09/2013 08:01 AM, Linus Walleij wrote:
> On Wed, Oct 9, 2013 at 3:28 PM, Christian Ruppert
> <[email protected]> wrote:
>> On Wed, Oct 09, 2013 at 01:58:35PM +0200, Linus Walleij wrote:
>>> On Tue, Oct 8, 2013 at 2:25 PM, Christian Ruppert
>>> <[email protected]> wrote:
...
>> Obviously, a driver (no matter for which OS) must be aware of
>> this hardware property so it can decide which value to write to which
>> register in order to control the pin mux. If we don't want to split (or
>> worse: duplicate) that information in the driver and device tree, I see
>> two alternatives.
>> 1. Define register offsets, masks and register values and the list of
>> pins controlled by those register/value pairs in the device tree.
>
> This is not a viable alternative of course.

Cough. pinctrl-single. Cough.

2013-10-11 07:53:56

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 01/03] Make non-linear GPIO ranges accesible from gpiolib

On Thu, Oct 10, 2013 at 10:49 PM, Stephen Warren <[email protected]> wrote:
> On 10/09/2013 08:01 AM, Linus Walleij wrote:
>> On Wed, Oct 9, 2013 at 3:28 PM, Christian Ruppert
>> <[email protected]> wrote:
>>> On Wed, Oct 09, 2013 at 01:58:35PM +0200, Linus Walleij wrote:
>>>> On Tue, Oct 8, 2013 at 2:25 PM, Christian Ruppert
>>>> <[email protected]> wrote:
> ...
>>> Obviously, a driver (no matter for which OS) must be aware of
>>> this hardware property so it can decide which value to write to which
>>> register in order to control the pin mux. If we don't want to split (or
>>> worse: duplicate) that information in the driver and device tree, I see
>>> two alternatives.
>>> 1. Define register offsets, masks and register values and the list of
>>> pins controlled by those register/value pairs in the device tree.
>>
>> This is not a viable alternative of course.
>
> Cough. pinctrl-single. Cough.

Indeed I have had second thoughts about pinctrl-single both
during its inception and later. Especially yesteray when it
sort of popped up that "yeah one register per pin ... then we
have this one extra register on the side to flush the pin settings
out to the IO-ring". (Haven't caught up with that discussion
though.)

It really needs to be kept in its very constrained cage.

For pinctrl-single there are quite a few strings attached,
like the pin can only be controlled by a single register, not
two of them. The indata to the device tree should be
in some nonsensical machine-readable form, not a
human-readable manual, such that the work of writing a
"proper" driver would involve reverse-engineering that
manual.

But it is definately in a grey area.

Yours,
Linus Walleij

2013-10-15 13:36:46

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 01/03] Make non-linear GPIO ranges accesible from gpiolib

On Wed, Oct 09, 2013 at 04:01:05PM +0200, Linus Walleij wrote:
> On Wed, Oct 9, 2013 at 3:28 PM, Christian Ruppert
> <[email protected]> wrote:
> > On Wed, Oct 09, 2013 at 01:58:35PM +0200, Linus Walleij wrote:
> >> On Tue, Oct 8, 2013 at 2:25 PM, Christian Ruppert
> >> <[email protected]> wrote:
> >> > +In addition, named groups of pins can be mapped to pin groups of a given
> >> > +pin controller using the gpio-ranges property as described below:
> >> > +
> >> > + gpio-range-list ::= <single-gpio-range> [gpio-range-list]
> >> > + single-gpio-range ::= <numeric-gpio-range> | <named-gpio-range>
> >> > + numeric-gpio-range ::=
> >> > + <pinctrl-phandle> <gpio-base> <pinctrl-base> <count>
> >> > + named-gpio-range ::= <pinctrl-phandle> <gpio-base> '<0 0>'
> >> > + pinctrl-phanle : phandle to pin controller node.
> >>
> >> phanle really?
> >
> > Yes, this is actually identical to the numeric ranges. It specifies the
> > pin controller which is "responsible" for the pins/pin group in
> > question.
>
> I was more thinking about the speling, should it not be "phandle"?
>
> > The named pin groups actually are a physical property of the pin
> > controller hardware (well, at least the pin groups are, the names are
> > not ;).
>
> OK.
>
> It would be good if the names of the group could match, as close
> as possible, the names given to the same identity in the data
> sheet or similar documentation.

This is the idea. In many cases, the names are more coherent over
several product/package variants than the numbers and it is much easier
to make names coherent with (at least our) hardware documentation than
numbers.

[An updated patch with all the other comments integrated will follow]

2013-10-15 13:38:23

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH V2] Make non-linear GPIO ranges accesible from gpiolib

This patch adds the infrastructure required to register non-linear gpio
ranges through gpiolib and the standard GPIO device tree bindings.

Signed-off-by: Christian Ruppert <[email protected]>
---
Documentation/devicetree/bindings/gpio/gpio.txt | 40 ++++++++++++++-
drivers/gpio/gpiolib-of.c | 63 ++++++++++++++++++++---
drivers/gpio/gpiolib.c | 47 +++++++++++++++++
drivers/pinctrl/core.c | 14 +++++
include/asm-generic/gpio.h | 10 ++++
include/linux/gpio.h | 9 +++
include/linux/pinctrl/pinctrl.h | 3 +
7 files changed, 176 insertions(+), 10 deletions(-)

diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
index 6cec6ff..0c85bb6 100644
--- a/Documentation/devicetree/bindings/gpio/gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio.txt
@@ -87,8 +87,10 @@ controllers. The gpio-ranges property described below represents this, and
contains information structures as follows:

gpio-range-list ::= <single-gpio-range> [gpio-range-list]
- single-gpio-range ::=
+ single-gpio-range ::= <numeric-gpio-range> | <named-gpio-range>
+ numeric-gpio-range ::=
<pinctrl-phandle> <gpio-base> <pinctrl-base> <count>
+ named-gpio-range ::= <pinctrl-phandle> <gpio-base> '<0 0>'
gpio-phandle : phandle to pin controller node.
gpio-base : Base GPIO ID in the GPIO controller
pinctrl-base : Base pinctrl pin ID in the pin controller
@@ -97,6 +99,19 @@ contains information structures as follows:
The "pin controller node" mentioned above must conform to the bindings
described in ../pinctrl/pinctrl-bindings.txt.

+In case named gpio ranges are used (ranges with both <pinctrl-base> and
+<count> set to 0), the property gpio-ranges-group-names contains one string
+for every single-gpio-range in gpio-ranges:
+ gpiorange-names-list ::= <gpiorange-name> [gpiorange-names-list]
+ gpiorange-name : Name of the pingroup associated to the GPIO range in
+ the respective pin controller.
+
+Elements of gpiorange-names-list corresponding to numeric ranges contain
+the empty string. Elements of gpiorange-names-list corresponding to named
+ranges contain the name of a pin group defined in the respective pin
+controller. The number of pins/GPIOs in the range is the number of pins in
+that pin group.
+
Previous versions of this binding required all pin controller nodes that
were referenced by any gpio-ranges property to contain a property named
#gpio-range-cells with value <3>. This requirement is now deprecated.
@@ -104,7 +119,7 @@ However, that property may still exist in older device trees for
compatibility reasons, and would still be required even in new device
trees that need to be compatible with older software.

-Example:
+Example 1:

qe_pio_e: gpio-controller@1460 {
#gpio-cells = <2>;
@@ -117,3 +132,24 @@ Example:
Here, a single GPIO controller has GPIOs 0..9 routed to pin controller
pinctrl1's pins 20..29, and GPIOs 10..19 routed to pin controller pinctrl2's
pins 50..59.
+
+Example 2:
+
+ gpio_pio_i: gpio-controller@14B0 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1480 0x18>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl1 0 20 10>,
+ <&pinctrl2 10 0 0>,
+ <&pinctrl1 15 0 10>,
+ <&pinctrl2 25 0 0>;
+ gpio-ranges-group-names = "",
+ "foo",
+ "",
+ "bar";
+ };
+
+Here, three GPIO ranges are defined wrt. two pin controllers. pinctrl1 GPIO
+ranges are defined using pin numbers whereas the GPIO ranges wrt. pinctrl2
+are named "foo" and "bar".
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 0dfaf20..e787609 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -190,10 +190,15 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
struct of_phandle_args pinspec;
struct pinctrl_dev *pctldev;
int index = 0, ret;
+ const char *name;
+ static const char group_names_propname[] = "gpio-ranges-group-names";
+ struct property *group_names;

if (!np)
return;

+ group_names = of_find_property(np, group_names_propname, NULL);
+
for (;; index++) {
ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3,
index, &pinspec);
@@ -204,14 +209,56 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
if (!pctldev)
break;

- ret = gpiochip_add_pin_range(chip,
- pinctrl_dev_get_devname(pctldev),
- pinspec.args[0],
- pinspec.args[1],
- pinspec.args[2]);
-
- if (ret)
- break;
+ if (pinspec.args[2]) {
+ if (group_names) {
+ ret = of_property_read_string_index(np,
+ group_names_propname,
+ index, &name);
+ if (strlen(name)) {
+ pr_err("%s: Group name of numeric GPIO ranges must be the empty string.\n",
+ np->full_name);
+ break;
+ }
+ }
+ /* npins != 0: linear range */
+ ret = gpiochip_add_pin_range(chip,
+ pinctrl_dev_get_devname(pctldev),
+ pinspec.args[0],
+ pinspec.args[1],
+ pinspec.args[2]);
+ if (ret)
+ break;
+ } else {
+ /* npins == 0: special range */
+ if (pinspec.args[1]) {
+ pr_err("%s: Illegal gpio-range format.\n",
+ np->full_name);
+ break;
+ }
+
+ if (!group_names) {
+ pr_err("%s: GPIO group range requested but no %s property.\n",
+ np->full_name, group_names_propname);
+ break;
+ }
+
+ ret = of_property_read_string_index(np,
+ group_names_propname,
+ index, &name);
+ if (ret)
+ break;
+
+ if (!strlen(name)) {
+ pr_err("%s: Group name of GPIO group range cannot be the empty string.\n",
+ np->full_name);
+ break;
+ }
+
+ ret = gpiochip_add_pingroup_range(chip, pctldev,
+ pinspec.args[0], name);
+ if (ret)
+ break;
+ }
}
}

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 86ef346..b83b7e4 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1320,6 +1320,53 @@ EXPORT_SYMBOL_GPL(gpiochip_find);
#ifdef CONFIG_PINCTRL

/**
+ * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping
+ * @chip: the gpiochip to add the range for
+ * @pinctrl: the dev_name() of the pin controller to map to
+ * @gpio_offset: the start offset in the current gpio_chip number space
+ * @pin_group: name of the pin group inside the pin controller
+ */
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ struct gpio_pin_range *pin_range;
+ int ret;
+
+ pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
+ if (!pin_range) {
+ pr_err("%s: GPIO chip: failed to allocate pin ranges\n",
+ chip->label);
+ return -ENOMEM;
+ }
+
+ /* Use local offset as range ID */
+ pin_range->range.id = gpio_offset;
+ pin_range->range.gc = chip;
+ pin_range->range.name = chip->label;
+ pin_range->range.base = chip->base + gpio_offset;
+ pin_range->pctldev = pctldev;
+
+ ret = pinctrl_get_group_pins(pctldev, pin_group,
+ &pin_range->range.pins,
+ &pin_range->range.npins);
+ if (ret < 0)
+ return ret;
+
+ pinctrl_add_gpio_range(pctldev, &pin_range->range);
+
+ pr_debug("GPIO chip %s: created GPIO range %d->%d ==> %s PINGRP %s\n",
+ chip->label, gpio_offset,
+ gpio_offset + pin_range->range.npins - 1,
+ pinctrl_dev_get_devname(pctldev), pin_group);
+
+ list_add_tail(&pin_range->node, &chip->pin_ranges);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
+
+/**
* gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping
* @chip: the gpiochip to add the range for
* @pinctrl_name: the dev_name() of the pin controller to map to
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index 92f86ab..5ee61a4 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -462,6 +462,20 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
}
EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range);

+int pinctrl_get_group_pins(struct pinctrl_dev *pctldev, const char *pin_group,
+ const unsigned **pins, unsigned *num_pins)
+{
+ const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ int gs;
+
+ gs = pinctrl_get_group_selector(pctldev, pin_group);
+ if (gs < 0)
+ return gs;
+
+ return pctlops->get_group_pins(pctldev, gs, pins, num_pins);
+}
+EXPORT_SYMBOL_GPL(pinctrl_get_group_pins);
+
/**
* pinctrl_find_gpio_range_from_pin() - locate the GPIO range for a pin
* @pctldev: the pin controller device to look in
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index bde6469..523f405 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -228,6 +228,9 @@ struct gpio_pin_range {
int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
unsigned int gpio_offset, unsigned int pin_offset,
unsigned int npins);
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group);
void gpiochip_remove_pin_ranges(struct gpio_chip *chip);

#else
@@ -239,6 +242,13 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
{
return 0;
}
+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ return 0;
+}

static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 552e3f4..234b32f 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -220,6 +220,15 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
return -EINVAL;
}

+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ WARN_ON(1);
+ return -EINVAL;
+}
+
static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
{
diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 5979147..fefb886 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -144,6 +144,9 @@ extern struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
extern struct pinctrl_gpio_range *
pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
unsigned int pin);
+extern int pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+ const char *pin_group, const unsigned **pins,
+ unsigned *num_pins);

#ifdef CONFIG_OF
extern struct pinctrl_dev *of_pinctrl_get(struct device_node *np);
--
1.7.1

2013-10-15 13:40:22

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH V2] pinmux: Add TB10x pinmux driver

The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
Used to control the pinmux and is a prerequisite for the GPIO driver.

Signed-off-by: Christian Ruppert <[email protected]>
Signed-off-by: Pierrick Hascoet <[email protected]>
---
.../bindings/pinctrl/abilis,tb10x-iomux.txt | 80 ++
drivers/pinctrl/Kconfig | 4 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-tb10x.c | 886 ++++++++++++++++++++
4 files changed, 971 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
create mode 100644 drivers/pinctrl/pinctrl-tb10x.c

diff --git a/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
new file mode 100644
index 0000000..2c11866
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/abilis,tb10x-iomux.txt
@@ -0,0 +1,80 @@
+Abilis Systems TB10x pin controller
+===================================
+
+Required properties
+-------------------
+
+- compatible: should be "abilis,tb10x-iomux";
+- reg: should contain the physical address and size of the pin controller's
+ register range.
+
+
+Function definitions
+--------------------
+
+Functions are defined (and referenced) by sub-nodes of the pin controller.
+Every sub-node defines exactly one function (implying a set of pins).
+Every function is associated to one named pin group inside the pin controller
+driver and these names are used to associate pin group predefinitions to pin
+controller sub-nodes.
+
+Required function definition subnode properties:
+ - abilis,function: should be set to the name of the function's pin group.
+
+The following pin groups are available:
+ - GPIO ports: gpioa, gpiob, gpioc, gpiod, gpioe, gpiof, gpiog,
+ gpioh, gpioi, gpioj, gpiok, gpiol, gpiom, gpion
+ - Serial TS input ports: mis0, mis1, mis2, mis3, mis4, mis5, mis6, mis7
+ - Parallel TS input ports: mip1, mip3, mip5, mip7
+ - Serial TS output ports: mos0, mos1, mos2, mos3
+ - Parallel TS output port: mop
+ - CI+ port: ciplus
+ - CableCard (Mcard) port: mcard
+ - Smart card ports: stc0, stc1
+ - UART ports: uart0, uart1
+ - SPI ports: spi1, spi3
+ - JTAG: jtag
+
+All other ports of the chip are not multiplexed and thus not managed by this
+driver.
+
+
+GPIO ranges definition
+----------------------
+
+The named pin groups of GPIO ports can be used to define GPIO ranges as
+explained in Documentation/devicetree/bindings/gpio/gpio.txt.
+
+
+Example
+-------
+
+iomux: iomux@FF10601c {
+ compatible = "abilis,tb10x-iomux";
+ reg = <0xFF10601c 0x4>;
+ pctl_gpio_a: pctl-gpio-a {
+ abilis,function = "gpioa";
+ };
+ pctl_uart0: pctl-uart0 {
+ abilis,function = "uart0";
+ };
+};
+uart@FF100000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0xFF100000 0x100>;
+ clock-frequency = <166666666>;
+ interrupts = <25 1>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pctl_uart0>;
+};
+gpioa: gpio@FF140000 {
+ compatible = "abilis,tb10x-gpio";
+ reg = <0xFF140000 0x1000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpio = <3>;
+ gpio-ranges = <&iomux 0 0>;
+ gpio-ranges-group-names = "gpioa";
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index b6e864e..0a5660d 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -292,6 +292,10 @@ config PINCTRL_XWAY
depends on SOC_TYPE_XWAY
depends on PINCTRL_LANTIQ

+config PINCTRL_TB10X
+ bool
+ depends on ARC_PLAT_TB10X
+
endmenu

endif
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 496d9bf..b13eb76 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_PINCTRL_S3C24XX) += pinctrl-s3c24xx.o
obj-$(CONFIG_PINCTRL_S3C64XX) += pinctrl-s3c64xx.o
obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o
obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o
+obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o
obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o

diff --git a/drivers/pinctrl/pinctrl-tb10x.c b/drivers/pinctrl/pinctrl-tb10x.c
new file mode 100644
index 0000000..2e1ea56
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tb10x.c
@@ -0,0 +1,886 @@
+/*
+ * Abilis Systems TB10x pin control driver
+ *
+ * Copyright (C) Abilis Systems 2012
+ *
+ * Author: Christian Ruppert <[email protected]>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/stringify.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#include "pinctrl-utils.h"
+
+#define TB10X_PORT1 (0)
+#define TB10X_PORT2 (16)
+#define TB10X_PORT3 (32)
+#define TB10X_PORT4 (48)
+#define TB10X_PORT5 (128)
+#define TB10X_PORT6 (64)
+#define TB10X_PORT7 (80)
+#define TB10X_PORT8 (96)
+#define TB10X_PORT9 (112)
+#define TB10X_GPIOS (256)
+
+#define PCFG_PORT_BITWIDTH (2)
+#define PCFG_PORT_MASK(PORT) \
+ (((1 << PCFG_PORT_BITWIDTH) - 1) << (PCFG_PORT_BITWIDTH * (PORT)))
+
+static const struct pinctrl_pin_desc tb10x_pins[] = {
+ /* Port 1 */
+ PINCTRL_PIN(TB10X_PORT1 + 0, "MICLK_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 1, "MISTRT_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 2, "MIVAL_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 3, "MDI_S0"),
+ PINCTRL_PIN(TB10X_PORT1 + 4, "GPIOA0"),
+ PINCTRL_PIN(TB10X_PORT1 + 5, "GPIOA1"),
+ PINCTRL_PIN(TB10X_PORT1 + 6, "GPIOA2"),
+ PINCTRL_PIN(TB10X_PORT1 + 7, "MDI_S1"),
+ PINCTRL_PIN(TB10X_PORT1 + 8, "MIVAL_S1"),
+ PINCTRL_PIN(TB10X_PORT1 + 9, "MISTRT_S1"),
+ PINCTRL_PIN(TB10X_PORT1 + 10, "MICLK_S1"),
+ /* Port 2 */
+ PINCTRL_PIN(TB10X_PORT2 + 0, "MICLK_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 1, "MISTRT_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 2, "MIVAL_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 3, "MDI_S2"),
+ PINCTRL_PIN(TB10X_PORT2 + 4, "GPIOC0"),
+ PINCTRL_PIN(TB10X_PORT2 + 5, "GPIOC1"),
+ PINCTRL_PIN(TB10X_PORT2 + 6, "GPIOC2"),
+ PINCTRL_PIN(TB10X_PORT2 + 7, "MDI_S3"),
+ PINCTRL_PIN(TB10X_PORT2 + 8, "MIVAL_S3"),
+ PINCTRL_PIN(TB10X_PORT2 + 9, "MISTRT_S3"),
+ PINCTRL_PIN(TB10X_PORT2 + 10, "MICLK_S3"),
+ /* Port 3 */
+ PINCTRL_PIN(TB10X_PORT3 + 0, "MICLK_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 1, "MISTRT_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 2, "MIVAL_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 3, "MDI_S4"),
+ PINCTRL_PIN(TB10X_PORT3 + 4, "GPIOE0"),
+ PINCTRL_PIN(TB10X_PORT3 + 5, "GPIOE1"),
+ PINCTRL_PIN(TB10X_PORT3 + 6, "GPIOE2"),
+ PINCTRL_PIN(TB10X_PORT3 + 7, "MDI_S5"),
+ PINCTRL_PIN(TB10X_PORT3 + 8, "MIVAL_S5"),
+ PINCTRL_PIN(TB10X_PORT3 + 9, "MISTRT_S5"),
+ PINCTRL_PIN(TB10X_PORT3 + 10, "MICLK_S5"),
+ /* Port 4 */
+ PINCTRL_PIN(TB10X_PORT4 + 0, "MICLK_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 1, "MISTRT_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 2, "MIVAL_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 3, "MDI_S6"),
+ PINCTRL_PIN(TB10X_PORT4 + 4, "GPIOG0"),
+ PINCTRL_PIN(TB10X_PORT4 + 5, "GPIOG1"),
+ PINCTRL_PIN(TB10X_PORT4 + 6, "GPIOG2"),
+ PINCTRL_PIN(TB10X_PORT4 + 7, "MDI_S7"),
+ PINCTRL_PIN(TB10X_PORT4 + 8, "MIVAL_S7"),
+ PINCTRL_PIN(TB10X_PORT4 + 9, "MISTRT_S7"),
+ PINCTRL_PIN(TB10X_PORT4 + 10, "MICLK_S7"),
+ /* Port 5 */
+ PINCTRL_PIN(TB10X_PORT5 + 0, "PC_CE1N"),
+ PINCTRL_PIN(TB10X_PORT5 + 1, "PC_CE2N"),
+ PINCTRL_PIN(TB10X_PORT5 + 2, "PC_REGN"),
+ PINCTRL_PIN(TB10X_PORT5 + 3, "PC_INPACKN"),
+ PINCTRL_PIN(TB10X_PORT5 + 4, "PC_OEN"),
+ PINCTRL_PIN(TB10X_PORT5 + 5, "PC_WEN"),
+ PINCTRL_PIN(TB10X_PORT5 + 6, "PC_IORDN"),
+ PINCTRL_PIN(TB10X_PORT5 + 7, "PC_IOWRN"),
+ PINCTRL_PIN(TB10X_PORT5 + 8, "PC_RDYIRQN"),
+ PINCTRL_PIN(TB10X_PORT5 + 9, "PC_WAITN"),
+ PINCTRL_PIN(TB10X_PORT5 + 10, "PC_A0"),
+ PINCTRL_PIN(TB10X_PORT5 + 11, "PC_A1"),
+ PINCTRL_PIN(TB10X_PORT5 + 12, "PC_A2"),
+ PINCTRL_PIN(TB10X_PORT5 + 13, "PC_A3"),
+ PINCTRL_PIN(TB10X_PORT5 + 14, "PC_A4"),
+ PINCTRL_PIN(TB10X_PORT5 + 15, "PC_A5"),
+ PINCTRL_PIN(TB10X_PORT5 + 16, "PC_A6"),
+ PINCTRL_PIN(TB10X_PORT5 + 17, "PC_A7"),
+ PINCTRL_PIN(TB10X_PORT5 + 18, "PC_A8"),
+ PINCTRL_PIN(TB10X_PORT5 + 19, "PC_A9"),
+ PINCTRL_PIN(TB10X_PORT5 + 20, "PC_A10"),
+ PINCTRL_PIN(TB10X_PORT5 + 21, "PC_A11"),
+ PINCTRL_PIN(TB10X_PORT5 + 22, "PC_A12"),
+ PINCTRL_PIN(TB10X_PORT5 + 23, "PC_A13"),
+ PINCTRL_PIN(TB10X_PORT5 + 24, "PC_A14"),
+ PINCTRL_PIN(TB10X_PORT5 + 25, "PC_D0"),
+ PINCTRL_PIN(TB10X_PORT5 + 26, "PC_D1"),
+ PINCTRL_PIN(TB10X_PORT5 + 27, "PC_D2"),
+ PINCTRL_PIN(TB10X_PORT5 + 28, "PC_D3"),
+ PINCTRL_PIN(TB10X_PORT5 + 29, "PC_D4"),
+ PINCTRL_PIN(TB10X_PORT5 + 30, "PC_D5"),
+ PINCTRL_PIN(TB10X_PORT5 + 31, "PC_D6"),
+ PINCTRL_PIN(TB10X_PORT5 + 32, "PC_D7"),
+ PINCTRL_PIN(TB10X_PORT5 + 33, "PC_MOSTRT"),
+ PINCTRL_PIN(TB10X_PORT5 + 34, "PC_MOVAL"),
+ PINCTRL_PIN(TB10X_PORT5 + 35, "PC_MDO0"),
+ PINCTRL_PIN(TB10X_PORT5 + 36, "PC_MDO1"),
+ PINCTRL_PIN(TB10X_PORT5 + 37, "PC_MDO2"),
+ PINCTRL_PIN(TB10X_PORT5 + 38, "PC_MDO3"),
+ PINCTRL_PIN(TB10X_PORT5 + 39, "PC_MDO4"),
+ PINCTRL_PIN(TB10X_PORT5 + 40, "PC_MDO5"),
+ PINCTRL_PIN(TB10X_PORT5 + 41, "PC_MDO6"),
+ PINCTRL_PIN(TB10X_PORT5 + 42, "PC_MDO7"),
+ PINCTRL_PIN(TB10X_PORT5 + 43, "PC_MISTRT"),
+ PINCTRL_PIN(TB10X_PORT5 + 44, "PC_MIVAL"),
+ PINCTRL_PIN(TB10X_PORT5 + 45, "PC_MDI0"),
+ PINCTRL_PIN(TB10X_PORT5 + 46, "PC_MDI1"),
+ PINCTRL_PIN(TB10X_PORT5 + 47, "PC_MDI2"),
+ PINCTRL_PIN(TB10X_PORT5 + 48, "PC_MDI3"),
+ PINCTRL_PIN(TB10X_PORT5 + 49, "PC_MDI4"),
+ PINCTRL_PIN(TB10X_PORT5 + 50, "PC_MDI5"),
+ PINCTRL_PIN(TB10X_PORT5 + 51, "PC_MDI6"),
+ PINCTRL_PIN(TB10X_PORT5 + 52, "PC_MDI7"),
+ PINCTRL_PIN(TB10X_PORT5 + 53, "PC_MICLK"),
+ /* Port 6 */
+ PINCTRL_PIN(TB10X_PORT6 + 0, "T_MOSTRT_S0"),
+ PINCTRL_PIN(TB10X_PORT6 + 1, "T_MOVAL_S0"),
+ PINCTRL_PIN(TB10X_PORT6 + 2, "T_MDO_S0"),
+ PINCTRL_PIN(TB10X_PORT6 + 3, "T_MOSTRT_S1"),
+ PINCTRL_PIN(TB10X_PORT6 + 4, "T_MOVAL_S1"),
+ PINCTRL_PIN(TB10X_PORT6 + 5, "T_MDO_S1"),
+ PINCTRL_PIN(TB10X_PORT6 + 6, "T_MOSTRT_S2"),
+ PINCTRL_PIN(TB10X_PORT6 + 7, "T_MOVAL_S2"),
+ PINCTRL_PIN(TB10X_PORT6 + 8, "T_MDO_S2"),
+ PINCTRL_PIN(TB10X_PORT6 + 9, "T_MOSTRT_S3"),
+ /* Port 7 */
+ PINCTRL_PIN(TB10X_PORT7 + 0, "UART0_TXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 1, "UART0_RXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 2, "UART0_CTS"),
+ PINCTRL_PIN(TB10X_PORT7 + 3, "UART0_RTS"),
+ PINCTRL_PIN(TB10X_PORT7 + 4, "UART1_TXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 5, "UART1_RXD"),
+ PINCTRL_PIN(TB10X_PORT7 + 6, "UART1_CTS"),
+ PINCTRL_PIN(TB10X_PORT7 + 7, "UART1_RTS"),
+ /* Port 8 */
+ PINCTRL_PIN(TB10X_PORT8 + 0, "SPI3_CLK"),
+ PINCTRL_PIN(TB10X_PORT8 + 1, "SPI3_MISO"),
+ PINCTRL_PIN(TB10X_PORT8 + 2, "SPI3_MOSI"),
+ PINCTRL_PIN(TB10X_PORT8 + 3, "SPI3_SSN"),
+ /* Port 9 */
+ PINCTRL_PIN(TB10X_PORT9 + 0, "SPI1_CLK"),
+ PINCTRL_PIN(TB10X_PORT9 + 1, "SPI1_MISO"),
+ PINCTRL_PIN(TB10X_PORT9 + 2, "SPI1_MOSI"),
+ PINCTRL_PIN(TB10X_PORT9 + 3, "SPI1_SSN0"),
+ PINCTRL_PIN(TB10X_PORT9 + 4, "SPI1_SSN1"),
+ /* Unmuxed GPIOs */
+ PINCTRL_PIN(TB10X_GPIOS + 0, "GPIOB0"),
+ PINCTRL_PIN(TB10X_GPIOS + 1, "GPIOB1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 2, "GPIOD0"),
+ PINCTRL_PIN(TB10X_GPIOS + 3, "GPIOD1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 4, "GPIOF0"),
+ PINCTRL_PIN(TB10X_GPIOS + 5, "GPIOF1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 6, "GPIOH0"),
+ PINCTRL_PIN(TB10X_GPIOS + 7, "GPIOH1"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 8, "GPIOI0"),
+ PINCTRL_PIN(TB10X_GPIOS + 9, "GPIOI1"),
+ PINCTRL_PIN(TB10X_GPIOS + 10, "GPIOI2"),
+ PINCTRL_PIN(TB10X_GPIOS + 11, "GPIOI3"),
+ PINCTRL_PIN(TB10X_GPIOS + 12, "GPIOI4"),
+ PINCTRL_PIN(TB10X_GPIOS + 13, "GPIOI5"),
+ PINCTRL_PIN(TB10X_GPIOS + 14, "GPIOI6"),
+ PINCTRL_PIN(TB10X_GPIOS + 15, "GPIOI7"),
+ PINCTRL_PIN(TB10X_GPIOS + 16, "GPIOI8"),
+ PINCTRL_PIN(TB10X_GPIOS + 17, "GPIOI9"),
+ PINCTRL_PIN(TB10X_GPIOS + 18, "GPIOI10"),
+ PINCTRL_PIN(TB10X_GPIOS + 19, "GPIOI11"),
+
+ PINCTRL_PIN(TB10X_GPIOS + 20, "GPION0"),
+ PINCTRL_PIN(TB10X_GPIOS + 21, "GPION1"),
+ PINCTRL_PIN(TB10X_GPIOS + 22, "GPION2"),
+ PINCTRL_PIN(TB10X_GPIOS + 23, "GPION3"),
+#define MAX_PIN (TB10X_GPIOS + 24)
+ PINCTRL_PIN(MAX_PIN, "GPION4"),
+};
+
+
+/* Port 1 */
+static const unsigned mis0_pins[] = { TB10X_PORT1 + 0, TB10X_PORT1 + 1,
+ TB10X_PORT1 + 2, TB10X_PORT1 + 3};
+static const unsigned gpioa_pins[] = { TB10X_PORT1 + 4, TB10X_PORT1 + 5,
+ TB10X_PORT1 + 6};
+static const unsigned mis1_pins[] = { TB10X_PORT1 + 7, TB10X_PORT1 + 8,
+ TB10X_PORT1 + 9, TB10X_PORT1 + 10};
+static const unsigned mip1_pins[] = { TB10X_PORT1 + 0, TB10X_PORT1 + 1,
+ TB10X_PORT1 + 2, TB10X_PORT1 + 3,
+ TB10X_PORT1 + 4, TB10X_PORT1 + 5,
+ TB10X_PORT1 + 6, TB10X_PORT1 + 7,
+ TB10X_PORT1 + 8, TB10X_PORT1 + 9,
+ TB10X_PORT1 + 10};
+
+/* Port 2 */
+static const unsigned mis2_pins[] = { TB10X_PORT2 + 0, TB10X_PORT2 + 1,
+ TB10X_PORT2 + 2, TB10X_PORT2 + 3};
+static const unsigned gpioc_pins[] = { TB10X_PORT2 + 4, TB10X_PORT2 + 5,
+ TB10X_PORT2 + 6};
+static const unsigned mis3_pins[] = { TB10X_PORT2 + 7, TB10X_PORT2 + 8,
+ TB10X_PORT2 + 9, TB10X_PORT2 + 10};
+static const unsigned mip3_pins[] = { TB10X_PORT2 + 0, TB10X_PORT2 + 1,
+ TB10X_PORT2 + 2, TB10X_PORT2 + 3,
+ TB10X_PORT2 + 4, TB10X_PORT2 + 5,
+ TB10X_PORT2 + 6, TB10X_PORT2 + 7,
+ TB10X_PORT2 + 8, TB10X_PORT2 + 9,
+ TB10X_PORT2 + 10};
+
+/* Port 3 */
+static const unsigned mis4_pins[] = { TB10X_PORT3 + 0, TB10X_PORT3 + 1,
+ TB10X_PORT3 + 2, TB10X_PORT3 + 3};
+static const unsigned gpioe_pins[] = { TB10X_PORT3 + 4, TB10X_PORT3 + 5,
+ TB10X_PORT3 + 6};
+static const unsigned mis5_pins[] = { TB10X_PORT3 + 7, TB10X_PORT3 + 8,
+ TB10X_PORT3 + 9, TB10X_PORT3 + 10};
+static const unsigned mip5_pins[] = { TB10X_PORT3 + 0, TB10X_PORT3 + 1,
+ TB10X_PORT3 + 2, TB10X_PORT3 + 3,
+ TB10X_PORT3 + 4, TB10X_PORT3 + 5,
+ TB10X_PORT3 + 6, TB10X_PORT3 + 7,
+ TB10X_PORT3 + 8, TB10X_PORT3 + 9,
+ TB10X_PORT3 + 10};
+
+/* Port 4 */
+static const unsigned mis6_pins[] = { TB10X_PORT4 + 0, TB10X_PORT4 + 1,
+ TB10X_PORT4 + 2, TB10X_PORT4 + 3};
+static const unsigned gpiog_pins[] = { TB10X_PORT4 + 4, TB10X_PORT4 + 5,
+ TB10X_PORT4 + 6};
+static const unsigned mis7_pins[] = { TB10X_PORT4 + 7, TB10X_PORT4 + 8,
+ TB10X_PORT4 + 9, TB10X_PORT4 + 10};
+static const unsigned mip7_pins[] = { TB10X_PORT4 + 0, TB10X_PORT4 + 1,
+ TB10X_PORT4 + 2, TB10X_PORT4 + 3,
+ TB10X_PORT4 + 4, TB10X_PORT4 + 5,
+ TB10X_PORT4 + 6, TB10X_PORT4 + 7,
+ TB10X_PORT4 + 8, TB10X_PORT4 + 9,
+ TB10X_PORT4 + 10};
+
+/* Port 6 */
+static const unsigned mop_pins[] = { TB10X_PORT6 + 0, TB10X_PORT6 + 1,
+ TB10X_PORT6 + 2, TB10X_PORT6 + 3,
+ TB10X_PORT6 + 4, TB10X_PORT6 + 5,
+ TB10X_PORT6 + 6, TB10X_PORT6 + 7,
+ TB10X_PORT6 + 8, TB10X_PORT6 + 9};
+static const unsigned mos0_pins[] = { TB10X_PORT6 + 0, TB10X_PORT6 + 1,
+ TB10X_PORT6 + 2};
+static const unsigned mos1_pins[] = { TB10X_PORT6 + 3, TB10X_PORT6 + 4,
+ TB10X_PORT6 + 5};
+static const unsigned mos2_pins[] = { TB10X_PORT6 + 6, TB10X_PORT6 + 7,
+ TB10X_PORT6 + 8};
+static const unsigned mos3_pins[] = { TB10X_PORT6 + 9};
+
+/* Port 7 */
+static const unsigned uart0_pins[] = { TB10X_PORT7 + 0, TB10X_PORT7 + 1,
+ TB10X_PORT7 + 2, TB10X_PORT7 + 3};
+static const unsigned uart1_pins[] = { TB10X_PORT7 + 4, TB10X_PORT7 + 5,
+ TB10X_PORT7 + 6, TB10X_PORT7 + 7};
+static const unsigned gpiol_pins[] = { TB10X_PORT7 + 0, TB10X_PORT7 + 1,
+ TB10X_PORT7 + 2, TB10X_PORT7 + 3};
+static const unsigned gpiom_pins[] = { TB10X_PORT7 + 4, TB10X_PORT7 + 5,
+ TB10X_PORT7 + 6, TB10X_PORT7 + 7};
+
+/* Port 8 */
+static const unsigned spi3_pins[] = { TB10X_PORT8 + 0, TB10X_PORT8 + 1,
+ TB10X_PORT8 + 2, TB10X_PORT8 + 3};
+static const unsigned jtag_pins[] = { TB10X_PORT8 + 0, TB10X_PORT8 + 1,
+ TB10X_PORT8 + 2, TB10X_PORT8 + 3};
+
+/* Port 9 */
+static const unsigned spi1_pins[] = { TB10X_PORT9 + 0, TB10X_PORT9 + 1,
+ TB10X_PORT9 + 2, TB10X_PORT9 + 3,
+ TB10X_PORT9 + 4};
+static const unsigned gpion_pins[] = { TB10X_PORT9 + 0, TB10X_PORT9 + 1,
+ TB10X_PORT9 + 2, TB10X_PORT9 + 3,
+ TB10X_PORT9 + 4};
+
+/* Port 5 */
+static const unsigned gpioj_pins[] = { TB10X_PORT5 + 0, TB10X_PORT5 + 1,
+ TB10X_PORT5 + 2, TB10X_PORT5 + 3,
+ TB10X_PORT5 + 4, TB10X_PORT5 + 5,
+ TB10X_PORT5 + 6, TB10X_PORT5 + 7,
+ TB10X_PORT5 + 8, TB10X_PORT5 + 9,
+ TB10X_PORT5 + 10, TB10X_PORT5 + 11,
+ TB10X_PORT5 + 12, TB10X_PORT5 + 13,
+ TB10X_PORT5 + 14, TB10X_PORT5 + 15,
+ TB10X_PORT5 + 16, TB10X_PORT5 + 17,
+ TB10X_PORT5 + 18, TB10X_PORT5 + 19,
+ TB10X_PORT5 + 20, TB10X_PORT5 + 21,
+ TB10X_PORT5 + 22, TB10X_PORT5 + 23,
+ TB10X_PORT5 + 24, TB10X_PORT5 + 25,
+ TB10X_PORT5 + 26, TB10X_PORT5 + 27,
+ TB10X_PORT5 + 28, TB10X_PORT5 + 29,
+ TB10X_PORT5 + 30, TB10X_PORT5 + 31};
+static const unsigned gpiok_pins[] = { TB10X_PORT5 + 32, TB10X_PORT5 + 33,
+ TB10X_PORT5 + 34, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40, TB10X_PORT5 + 41,
+ TB10X_PORT5 + 42, TB10X_PORT5 + 43,
+ TB10X_PORT5 + 44, TB10X_PORT5 + 45,
+ TB10X_PORT5 + 46, TB10X_PORT5 + 47,
+ TB10X_PORT5 + 48, TB10X_PORT5 + 49,
+ TB10X_PORT5 + 50, TB10X_PORT5 + 51,
+ TB10X_PORT5 + 52, TB10X_PORT5 + 53};
+static const unsigned ciplus_pins[] = { TB10X_PORT5 + 0, TB10X_PORT5 + 1,
+ TB10X_PORT5 + 2, TB10X_PORT5 + 3,
+ TB10X_PORT5 + 4, TB10X_PORT5 + 5,
+ TB10X_PORT5 + 6, TB10X_PORT5 + 7,
+ TB10X_PORT5 + 8, TB10X_PORT5 + 9,
+ TB10X_PORT5 + 10, TB10X_PORT5 + 11,
+ TB10X_PORT5 + 12, TB10X_PORT5 + 13,
+ TB10X_PORT5 + 14, TB10X_PORT5 + 15,
+ TB10X_PORT5 + 16, TB10X_PORT5 + 17,
+ TB10X_PORT5 + 18, TB10X_PORT5 + 19,
+ TB10X_PORT5 + 20, TB10X_PORT5 + 21,
+ TB10X_PORT5 + 22, TB10X_PORT5 + 23,
+ TB10X_PORT5 + 24, TB10X_PORT5 + 25,
+ TB10X_PORT5 + 26, TB10X_PORT5 + 27,
+ TB10X_PORT5 + 28, TB10X_PORT5 + 29,
+ TB10X_PORT5 + 30, TB10X_PORT5 + 31,
+ TB10X_PORT5 + 32, TB10X_PORT5 + 33,
+ TB10X_PORT5 + 34, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40, TB10X_PORT5 + 41,
+ TB10X_PORT5 + 42, TB10X_PORT5 + 43,
+ TB10X_PORT5 + 44, TB10X_PORT5 + 45,
+ TB10X_PORT5 + 46, TB10X_PORT5 + 47,
+ TB10X_PORT5 + 48, TB10X_PORT5 + 49,
+ TB10X_PORT5 + 50, TB10X_PORT5 + 51,
+ TB10X_PORT5 + 52, TB10X_PORT5 + 53};
+static const unsigned mcard_pins[] = { TB10X_PORT5 + 3, TB10X_PORT5 + 10,
+ TB10X_PORT5 + 11, TB10X_PORT5 + 12,
+ TB10X_PORT5 + 22, TB10X_PORT5 + 23,
+ TB10X_PORT5 + 33, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40, TB10X_PORT5 + 41,
+ TB10X_PORT5 + 42, TB10X_PORT5 + 43,
+ TB10X_PORT5 + 45, TB10X_PORT5 + 46,
+ TB10X_PORT5 + 47, TB10X_PORT5 + 48,
+ TB10X_PORT5 + 49, TB10X_PORT5 + 50,
+ TB10X_PORT5 + 51, TB10X_PORT5 + 52,
+ TB10X_PORT5 + 53};
+static const unsigned stc0_pins[] = { TB10X_PORT5 + 34, TB10X_PORT5 + 35,
+ TB10X_PORT5 + 36, TB10X_PORT5 + 37,
+ TB10X_PORT5 + 38, TB10X_PORT5 + 39,
+ TB10X_PORT5 + 40};
+static const unsigned stc1_pins[] = { TB10X_PORT5 + 25, TB10X_PORT5 + 26,
+ TB10X_PORT5 + 27, TB10X_PORT5 + 28,
+ TB10X_PORT5 + 29, TB10X_PORT5 + 30,
+ TB10X_PORT5 + 44};
+
+/* Unmuxed GPIOs */
+static const unsigned gpiob_pins[] = { TB10X_GPIOS + 0, TB10X_GPIOS + 1};
+static const unsigned gpiod_pins[] = { TB10X_GPIOS + 2, TB10X_GPIOS + 3};
+static const unsigned gpiof_pins[] = { TB10X_GPIOS + 4, TB10X_GPIOS + 5};
+static const unsigned gpioh_pins[] = { TB10X_GPIOS + 6, TB10X_GPIOS + 7};
+static const unsigned gpioi_pins[] = { TB10X_GPIOS + 8, TB10X_GPIOS + 9,
+ TB10X_GPIOS + 10, TB10X_GPIOS + 11,
+ TB10X_GPIOS + 12, TB10X_GPIOS + 13,
+ TB10X_GPIOS + 14, TB10X_GPIOS + 15,
+ TB10X_GPIOS + 16, TB10X_GPIOS + 17,
+ TB10X_GPIOS + 18, TB10X_GPIOS + 19};
+
+struct tb10x_pinfuncgrp {
+ const char *name;
+ const unsigned int *pins;
+ const unsigned int pincnt;
+ const int port;
+ const unsigned int mode;
+ const int isgpio;
+};
+#define DEFPINFUNCGRP(NAME, PORT, MODE, ISGPIO) { \
+ .name = __stringify(NAME), \
+ .pins = NAME##_pins, .pincnt = ARRAY_SIZE(NAME##_pins), \
+ .port = (PORT), .mode = (MODE), \
+ .isgpio = (ISGPIO), \
+ }
+static const struct tb10x_pinfuncgrp tb10x_pingroups[] = {
+ DEFPINFUNCGRP(mis0, 0, 0, 0),
+ DEFPINFUNCGRP(gpioa, 0, 0, 1),
+ DEFPINFUNCGRP(mis1, 0, 0, 0),
+ DEFPINFUNCGRP(mip1, 0, 1, 0),
+ DEFPINFUNCGRP(mis2, 1, 0, 0),
+ DEFPINFUNCGRP(gpioc, 1, 0, 1),
+ DEFPINFUNCGRP(mis3, 1, 0, 0),
+ DEFPINFUNCGRP(mip3, 1, 1, 0),
+ DEFPINFUNCGRP(mis4, 2, 0, 0),
+ DEFPINFUNCGRP(gpioe, 2, 0, 1),
+ DEFPINFUNCGRP(mis5, 2, 0, 0),
+ DEFPINFUNCGRP(mip5, 2, 1, 0),
+ DEFPINFUNCGRP(mis6, 3, 0, 0),
+ DEFPINFUNCGRP(gpiog, 3, 0, 1),
+ DEFPINFUNCGRP(mis7, 3, 0, 0),
+ DEFPINFUNCGRP(mip7, 3, 1, 0),
+ DEFPINFUNCGRP(gpioj, 4, 0, 1),
+ DEFPINFUNCGRP(gpiok, 4, 0, 1),
+ DEFPINFUNCGRP(ciplus, 4, 1, 0),
+ DEFPINFUNCGRP(mcard, 4, 2, 0),
+ DEFPINFUNCGRP(stc0, 4, 3, 0),
+ DEFPINFUNCGRP(stc1, 4, 3, 0),
+ DEFPINFUNCGRP(mop, 5, 0, 0),
+ DEFPINFUNCGRP(mos0, 5, 1, 0),
+ DEFPINFUNCGRP(mos1, 5, 1, 0),
+ DEFPINFUNCGRP(mos2, 5, 1, 0),
+ DEFPINFUNCGRP(mos3, 5, 1, 0),
+ DEFPINFUNCGRP(uart0, 6, 0, 0),
+ DEFPINFUNCGRP(uart1, 6, 0, 0),
+ DEFPINFUNCGRP(gpiol, 6, 1, 1),
+ DEFPINFUNCGRP(gpiom, 6, 1, 1),
+ DEFPINFUNCGRP(spi3, 7, 0, 0),
+ DEFPINFUNCGRP(jtag, 7, 1, 0),
+ DEFPINFUNCGRP(spi1, 8, 0, 0),
+ DEFPINFUNCGRP(gpion, 8, 1, 1),
+ DEFPINFUNCGRP(gpiob, -1, 0, 1),
+ DEFPINFUNCGRP(gpiod, -1, 0, 1),
+ DEFPINFUNCGRP(gpiof, -1, 0, 1),
+ DEFPINFUNCGRP(gpioh, -1, 0, 1),
+ DEFPINFUNCGRP(gpioi, -1, 0, 1),
+};
+#undef DEFPINFUNCGRP
+
+struct tb10x_of_pinfunc {
+ const char *name;
+ const char *group;
+};
+
+#define TB10X_PORTS (9)
+
+/**
+ * struct tb10x_port - state of an I/O port
+ * @mode: Node this port is currently in.
+ * @count: Number of enabled functions which require this port to be
+ * configured in @mode.
+ */
+struct tb10x_port {
+ unsigned int mode;
+ unsigned int count;
+};
+
+/**
+ * struct tb10x_pinctrl - TB10x pin controller internal state
+ * @pctl: pointer to the pinctrl_dev structure of this pin controller.
+ * @base: register set base address.
+ * @pingroups: pointer to an array of the pin groups this driver manages.
+ * @pinfuncgrpcnt: number of pingroups in @pingroups.
+ * @pinfuncs: pointer to an array of pin functions this driver manages.
+ * @pinfuncnt: number of pin functions in @pinfuncs.
+ * @mutex: mutex for exclusive access to a pin controller's state.
+ * @ports: current state of each port.
+ * @gpios: Indicates if a given pin is currently used as GPIO (1) or not (0).
+ */
+struct tb10x_pinctrl {
+ struct pinctrl_dev *pctl;
+ void *base;
+ const struct tb10x_pinfuncgrp *pingroups;
+ unsigned int pinfuncgrpcnt;
+ struct tb10x_of_pinfunc *pinfuncs;
+ unsigned int pinfuncnt;
+ struct mutex mutex;
+ struct tb10x_port ports[TB10X_PORTS];
+ DECLARE_BITMAP(gpios, MAX_PIN + 1);
+};
+
+static inline void tb10x_pinctrl_set_config(struct tb10x_pinctrl *state,
+ unsigned int port, unsigned int mode)
+{
+ u32 pcfg;
+
+ if (state->ports[port].count)
+ return;
+
+ state->ports[port].mode = mode;
+
+ pcfg = ioread32(state->base) & ~(PCFG_PORT_MASK(port));
+ pcfg |= (mode << (PCFG_PORT_BITWIDTH * port)) & PCFG_PORT_MASK(port);
+ iowrite32(pcfg, state->base);
+}
+
+static inline unsigned int tb10x_pinctrl_get_config(
+ struct tb10x_pinctrl *state,
+ unsigned int port)
+{
+ return (ioread32(state->base) & PCFG_PORT_MASK(port))
+ >> (PCFG_PORT_BITWIDTH * port);
+}
+
+static int tb10x_get_groups_count(struct pinctrl_dev *pctl)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncgrpcnt;
+}
+
+static const char *tb10x_get_group_name(struct pinctrl_dev *pctl, unsigned n)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pingroups[n].name;
+}
+
+static int tb10x_get_group_pins(struct pinctrl_dev *pctl, unsigned n,
+ unsigned const **pins,
+ unsigned * const num_pins)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+
+ *pins = state->pingroups[n].pins;
+ *num_pins = state->pingroups[n].pincnt;
+
+ return 0;
+}
+
+static int tb10x_dt_node_to_map(struct pinctrl_dev *pctl,
+ struct device_node *np_config,
+ struct pinctrl_map **map, unsigned *num_maps)
+{
+ const char *string;
+ unsigned reserved_maps = 0;
+ int ret = 0;
+
+ if (of_property_read_string(np_config, "abilis,function", &string)) {
+ pr_err("%s: No abilis,function property in device tree.\n",
+ np_config->full_name);
+ return -EINVAL;
+ }
+
+ *map = NULL;
+ *num_maps = 0;
+
+ ret = pinctrl_utils_reserve_map(pctl, map, &reserved_maps,
+ num_maps, 1);
+ if (ret)
+ goto out;
+
+ ret = pinctrl_utils_add_map_mux(pctl, map, &reserved_maps,
+ num_maps, string, np_config->name);
+
+out:
+ return ret;
+}
+
+static struct pinctrl_ops tb10x_pinctrl_ops = {
+ .get_groups_count = tb10x_get_groups_count,
+ .get_group_name = tb10x_get_group_name,
+ .get_group_pins = tb10x_get_group_pins,
+ .dt_node_to_map = tb10x_dt_node_to_map,
+ .dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int tb10x_get_functions_count(struct pinctrl_dev *pctl)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncnt;
+}
+
+static const char *tb10x_get_function_name(struct pinctrl_dev *pctl,
+ unsigned n)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ return state->pinfuncs[n].name;
+}
+
+static int tb10x_get_function_groups(struct pinctrl_dev *pctl,
+ unsigned n, const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+
+ *groups = &state->pinfuncs[n].group;
+ *num_groups = 1;
+
+ return 0;
+}
+
+static int tb10x_gpio_request_enable(struct pinctrl_dev *pctl,
+ struct pinctrl_gpio_range *range,
+ unsigned pin)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ int muxport = -1;
+ int muxmode = -1;
+ int i;
+
+ mutex_lock(&state->mutex);
+
+ /*
+ * Figure out to which port the requested GPIO belongs and how to
+ * configure that port.
+ * This loop also checks for pin conflicts between GPIOs and other
+ * functions.
+ */
+ for (i = 0; i < state->pinfuncgrpcnt; i++) {
+ const struct tb10x_pinfuncgrp *pfg = &state->pingroups[i];
+ unsigned int port = pfg->port;
+ unsigned int mode = pfg->mode;
+ int j;
+
+ /*
+ * Skip pin groups which are always mapped and don't need
+ * to be configured.
+ */
+ if (port < 0)
+ continue;
+
+ for (j = 0; j < pfg->pincnt; j++) {
+ if (pin == pfg->pins[j]) {
+ if (pfg->isgpio) {
+ /*
+ * Remember the GPIO-only setting of
+ * the port this pin belongs to.
+ */
+ muxport = port;
+ muxmode = mode;
+ } else if (state->ports[port].count
+ && (state->ports[port].mode == mode)) {
+ /*
+ * Error: The requested pin is already
+ * used for something else.
+ */
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * If we haven't returned an error at this point, the GPIO pin is not
+ * used by another function and the GPIO request can be granted:
+ * Register pin as being used as GPIO so we don't allocate it to
+ * another function later.
+ */
+ set_bit(pin, state->gpios);
+
+ /*
+ * Potential conflicts between GPIOs and pin functions were caught
+ * earlier in this function and tb10x_pinctrl_set_config will do the
+ * Right Thing, either configure the port in GPIO only mode or leave
+ * another mode compatible with this GPIO request untouched.
+ */
+ if (muxport >= 0)
+ tb10x_pinctrl_set_config(state, muxport, muxmode);
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static void tb10x_gpio_disable_free(struct pinctrl_dev *pctl,
+ struct pinctrl_gpio_range *range,
+ unsigned pin)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+
+ mutex_lock(&state->mutex);
+
+ clear_bit(pin, state->gpios);
+
+ mutex_unlock(&state->mutex);
+}
+
+static int tb10x_pctl_enable(struct pinctrl_dev *pctl,
+ unsigned func_selector, unsigned group_selector)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector];
+ int i;
+
+ if (grp->port < 0)
+ return 0;
+
+ mutex_lock(&state->mutex);
+
+ /*
+ * Check if the requested function is compatible with previously
+ * requested functions.
+ */
+ if (state->ports[grp->port].count
+ && (state->ports[grp->port].mode != grp->mode)) {
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+
+ /*
+ * Check if the requested function is compatible with previously
+ * requested GPIOs.
+ */
+ for (i = 0; i < grp->pincnt; i++)
+ if (test_bit(grp->pins[i], state->gpios)) {
+ mutex_unlock(&state->mutex);
+ return -EBUSY;
+ }
+
+ tb10x_pinctrl_set_config(state, grp->port, grp->mode);
+
+ state->ports[grp->port].count++;
+
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static void tb10x_pctl_disable(struct pinctrl_dev *pctl,
+ unsigned func_selector, unsigned group_selector)
+{
+ struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl);
+ const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector];
+
+ if (grp->port < 0)
+ return;
+
+ mutex_lock(&state->mutex);
+
+ state->ports[grp->port].count--;
+
+ mutex_unlock(&state->mutex);
+}
+
+static struct pinmux_ops tb10x_pinmux_ops = {
+ .get_functions_count = tb10x_get_functions_count,
+ .get_function_name = tb10x_get_function_name,
+ .get_function_groups = tb10x_get_function_groups,
+ .gpio_request_enable = tb10x_gpio_request_enable,
+ .gpio_disable_free = tb10x_gpio_disable_free,
+ .enable = tb10x_pctl_enable,
+ .disable = tb10x_pctl_disable,
+};
+
+static struct pinctrl_desc tb10x_pindesc = {
+ .name = "TB10x",
+ .pins = tb10x_pins,
+ .npins = ARRAY_SIZE(tb10x_pins),
+ .owner = THIS_MODULE,
+ .pctlops = &tb10x_pinctrl_ops,
+ .pmxops = &tb10x_pinmux_ops,
+};
+
+static int tb10x_pinctrl_probe(struct platform_device *pdev)
+{
+ int ret = -EINVAL;
+ struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct device *dev = &pdev->dev;
+ struct device_node *of_node = dev->of_node;
+ struct device_node *child;
+ struct tb10x_pinctrl *state;
+ int i;
+
+ if (!of_node) {
+ dev_err(dev, "No device tree node found.\n");
+ return -EINVAL;
+ }
+
+ if (!mem) {
+ dev_err(dev, "No memory resource defined.\n");
+ return -EINVAL;
+ }
+
+ state = devm_kzalloc(dev, sizeof(struct tb10x_pinctrl) +
+ of_get_child_count(of_node)
+ * sizeof(struct tb10x_of_pinfunc),
+ GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, state);
+ state->pinfuncs = (struct tb10x_of_pinfunc *)(state + 1);
+ mutex_init(&state->mutex);
+
+ state->base = devm_ioremap_resource(dev, mem);
+ if (!state->base) {
+ dev_err(dev, "Request register region failed.\n");
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ state->pingroups = tb10x_pingroups;
+ state->pinfuncgrpcnt = ARRAY_SIZE(tb10x_pingroups);
+
+ for (i = 0; i < TB10X_PORTS; i++)
+ state->ports[i].mode = tb10x_pinctrl_get_config(state, i);
+
+ for_each_child_of_node(of_node, child) {
+ const char *name;
+
+ if (!of_property_read_string(child, "abilis,function",
+ &name)) {
+ state->pinfuncs[state->pinfuncnt].name = child->name;
+ state->pinfuncs[state->pinfuncnt].group = name;
+ state->pinfuncnt++;
+ }
+ }
+
+ state->pctl = pinctrl_register(&tb10x_pindesc, dev, state);
+ if (IS_ERR(state->pctl)) {
+ dev_err(dev, "could not register TB10x pin driver\n");
+ ret = PTR_ERR(state->pctl);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ mutex_destroy(&state->mutex);
+ return ret;
+}
+
+static int tb10x_pinctrl_remove(struct platform_device *pdev)
+{
+ struct tb10x_pinctrl *state = platform_get_drvdata(pdev);
+
+ pinctrl_unregister(state->pctl);
+ mutex_destroy(&state->mutex);
+
+ return 0;
+}
+
+
+static const struct of_device_id tb10x_pinctrl_dt_ids[] = {
+ { .compatible = "abilis,tb10x-iomux" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tb10x_pinctrl_dt_ids);
+
+static struct platform_driver tb10x_pinctrl_pdrv = {
+ .probe = tb10x_pinctrl_probe,
+ .remove = tb10x_pinctrl_remove,
+ .driver = {
+ .name = "tb10x_pinctrl",
+ .of_match_table = of_match_ptr(tb10x_pinctrl_dt_ids),
+ .owner = THIS_MODULE
+ }
+};
+
+static int __init tb10x_iopinctrl_init(void)
+{
+ return platform_driver_register(&tb10x_pinctrl_pdrv);
+}
+
+static void __exit tb10x_iopinctrl_exit(void)
+{
+ platform_driver_unregister(&tb10x_pinctrl_pdrv);
+}
+
+MODULE_AUTHOR("Christian Ruppert <[email protected]>");
+MODULE_LICENSE("GPL");
+module_init(tb10x_iopinctrl_init);
+module_exit(tb10x_iopinctrl_exit);
--
1.7.1

2013-10-15 13:45:54

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 03/03] GPIO: Add TB10x GPIO driver

On Wed, Oct 09, 2013 at 02:19:17PM +0200, Linus Walleij wrote:
> On Tue, Oct 8, 2013 at 2:25 PM, Christian Ruppert
> <[email protected]> wrote:
>
> Overall this driver is looking very nice, we just need to figure out this
> group range concept in the other patch.
>
> > +Example:
> > +
> > + gpioa: gpio@FF140000 {
> > + compatible = "abilis,tb10x-gpio";
> > + interrupt-controller;
> > + #interrupt-cells = <1>;
> > + interrupt-parent = <&tb10x_ictl>;
> > + interrupts = <27 2>;
>
> So this is cascaded off some HWIRQ offset 27 on some interrupt
> controller, OK...
>
> > + reg = <0xFF140000 0x1000>;
> > + gpio-controller;
> > + #gpio-cells = <2>;
> > + abilis,ngpio = <3>;
> > + gpio-ranges = <&iomux 0 0 0>;
> > + gpio-ranges-group-names = "gpioa_pins";
> > + };
>
>
> But this thing:
>
> (...)
> > +static irqreturn_t tb10x_gpio_irq_cascade(int irq, void *data)
> > +{
> > + struct tb10x_gpio *tb10x_gpio = data;
> > + u32 r = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_CHANGE);
> > + u32 m = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_INT_EN);
> > + const unsigned long bits = r & m;
> > + int i;
> > +
> > + for_each_set_bit(i, &bits, 32)
> > + generic_handle_irq(irq_find_mapping(tb10x_gpio->domain, i));
> > +
> > + return IRQ_HANDLED;
> > +}
>
> (...)
> > + ret = platform_get_irq(pdev, 0);
> > + if (ret < 0) {
> > + dev_err(&pdev->dev, "No interrupt specified.\n");
> > + goto fail_get_irq;
> > + }
> > +
> > + tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
> > + tb10x_gpio->irq = ret;
> > +
> > + ret = devm_request_irq(&pdev->dev, ret, tb10x_gpio_irq_cascade,
> > + IRQF_TRIGGER_NONE | IRQF_SHARED,
> > + dev_name(&pdev->dev), tb10x_gpio);
>
> Why aren't you simply using
>
> irq_set_chained_handler()
> irq_set_handler_data(tb10x_gpio);
>
> And in the handler function that need a signature like
> this:
>
> static void tb10x_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
> {
> struct tb10x_gpio *tb10x_gpio = = irq_get_handler_data(irq);
> struct irq_chip *host_chip = irq_get_chip(irq);
> chained_irq_enter(host_chip, desc);
> (...)
> chained_irq_exit(host_chip, desc);
> }
>
> ?
>
> It's not like I'm 100% certain on where to use one or the other
> construct (a mechanism like the above is needed for threaded
> IRQs I've noticed) but the chained handler seems more to the
> point does it not?
>
> The only downside I've seen is that the parent IRQ does not get
> a name and the accumulated IRQ stats in /proc/interrupts but
> surely we can live without that (or fix it).
>
> Since I'm a bit rusty on chained IRQs correct me if I'm wrong...

OK, it took me a while to figure this back out again because as far as
I'm familiar with the IRQ framework you're right. The reason I'm not
using irq_set_chained_handler is that we have one driver instance per
GPIO bank and all GPIO banks share the same interrupt line. This means
every driver instance needs its own (different) user data and a simple
call to irq_set_handler_data(tb10x_gpio) won't suffice. I'm not aware of
any mechanism that allows interrupt sharing with the
irq_set_chained_handler() mechanism.

Greetings,
Christian

2013-10-16 11:19:38

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH V2] Make non-linear GPIO ranges accesible from gpiolib

On Tue, Oct 15, 2013 at 3:37 PM, Christian Ruppert
<[email protected]> wrote:

> This patch adds the infrastructure required to register non-linear gpio
> ranges through gpiolib and the standard GPIO device tree bindings.
>
> Signed-off-by: Christian Ruppert <[email protected]>

OK this is looking fully acceptable, so applied it.

(If some DT reviewer dislikes it, this is a good time to protest.)

Christain, can you check if Documentation/pinctrl.txt needs to
be updated for this?

Yours,
Linus Walleij

2013-10-16 11:25:06

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH V2] pinmux: Add TB10x pinmux driver

On Tue, Oct 15, 2013 at 3:39 PM, Christian Ruppert
<[email protected]> wrote:

> The pinmux driver of the Abilis Systems TB10x platform based on ARC700 CPUs.
> Used to control the pinmux and is a prerequisite for the GPIO driver.
>
> Signed-off-by: Christian Ruppert <[email protected]>
> Signed-off-by: Pierrick Hascoet <[email protected]>

Excellent work Christian, of course I cannot resist this driver now
that everything is looking good and the deps are in place, so
patch applied.

Yours,
Linus Walleij

2013-10-16 11:29:47

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 03/03] GPIO: Add TB10x GPIO driver

On Tue, Oct 15, 2013 at 3:45 PM, Christian Ruppert
<[email protected]> wrote:
> On Wed, Oct 09, 2013 at 02:19:17PM +0200, Linus Walleij wrote:

>> It's not like I'm 100% certain on where to use one or the other
>> construct (a mechanism like the above is needed for threaded
>> IRQs I've noticed) but the chained handler seems more to the
>> point does it not?
>>
>> The only downside I've seen is that the parent IRQ does not get
>> a name and the accumulated IRQ stats in /proc/interrupts but
>> surely we can live without that (or fix it).
>>
>> Since I'm a bit rusty on chained IRQs correct me if I'm wrong...
>
> OK, it took me a while to figure this back out again because as far as
> I'm familiar with the IRQ framework you're right. The reason I'm not
> using irq_set_chained_handler is that we have one driver instance per
> GPIO bank and all GPIO banks share the same interrupt line. This means
> every driver instance needs its own (different) user data and a simple
> call to irq_set_handler_data(tb10x_gpio) won't suffice. I'm not aware of
> any mechanism that allows interrupt sharing with the
> irq_set_chained_handler() mechanism.

OK yes makes perfect sense. We'll live with this then.

I didn't see a new version of this patch with the other two, shall
I just apply this last version in the pin control tree with the
two other patches?

Yours,
Linus Walleij

2013-10-16 12:57:32

by Christian Ruppert

[permalink] [raw]
Subject: [PATCH] Add a short note on pinctrl_get_group_pins to pinctrl.txt

This patch adds a short description of how to use the newly added
pinctrl_get_group_pins function to the pinctrl documentation.

Signed-off-by: Christian Ruppert <[email protected]>
---
Documentation/pinctrl.txt | 7 ++++++-
1 files changed, 6 insertions(+), 1 deletions(-)

diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt
index c0ffd30..a7929cb 100644
--- a/Documentation/pinctrl.txt
+++ b/Documentation/pinctrl.txt
@@ -358,7 +358,12 @@ static struct pinctrl_gpio_range gpio_range = {
.gc = &chip;
};

-In this case the pin_base property will be ignored.
+In this case the pin_base property will be ignored. If the name of a pin
+group is known, the pins and npins elements of the above structure can be
+initialised using the function pinctrl_get_group_pins(), e.g. for pin
+group "foo":
+
+pinctrl_get_group_pins(pctl, "foo", &gpio_range.pins, &gpio_range.npins);

When GPIO-specific functions in the pin control subsystem are called, these
ranges will be used to look up the appropriate pin controller by inspecting
--
1.7.1

2013-10-16 12:59:24

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 03/03] GPIO: Add TB10x GPIO driver

On Wed, Oct 16, 2013 at 01:29:44PM +0200, Linus Walleij wrote:
> On Tue, Oct 15, 2013 at 3:45 PM, Christian Ruppert
> <[email protected]> wrote:
> > On Wed, Oct 09, 2013 at 02:19:17PM +0200, Linus Walleij wrote:
>
> >> It's not like I'm 100% certain on where to use one or the other
> >> construct (a mechanism like the above is needed for threaded
> >> IRQs I've noticed) but the chained handler seems more to the
> >> point does it not?
> >>
> >> The only downside I've seen is that the parent IRQ does not get
> >> a name and the accumulated IRQ stats in /proc/interrupts but
> >> surely we can live without that (or fix it).
> >>
> >> Since I'm a bit rusty on chained IRQs correct me if I'm wrong...
> >
> > OK, it took me a while to figure this back out again because as far as
> > I'm familiar with the IRQ framework you're right. The reason I'm not
> > using irq_set_chained_handler is that we have one driver instance per
> > GPIO bank and all GPIO banks share the same interrupt line. This means
> > every driver instance needs its own (different) user data and a simple
> > call to irq_set_handler_data(tb10x_gpio) won't suffice. I'm not aware of
> > any mechanism that allows interrupt sharing with the
> > irq_set_chained_handler() mechanism.
>
> OK yes makes perfect sense. We'll live with this then.
>
> I didn't see a new version of this patch with the other two, shall
> I just apply this last version in the pin control tree with the
> two other patches?

If you don't see anything else which needs changing please do so, yes.

Best regards,
Christian

2013-10-16 13:36:12

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH] Add a short note on pinctrl_get_group_pins to pinctrl.txt

On Wed, Oct 16, 2013 at 2:56 PM, Christian Ruppert
<[email protected]> wrote:

> This patch adds a short description of how to use the newly added
> pinctrl_get_group_pins function to the pinctrl documentation.
>
> Signed-off-by: Christian Ruppert <[email protected]>

OK patch applied.

Yours,
Linus Walleij

2013-10-24 16:24:19

by Christian Ruppert

[permalink] [raw]
Subject: Re: [PATCH 03/03] GPIO: Add TB10x GPIO driver

On Wed, Oct 16, 2013 at 02:58:55PM +0200, Christian Ruppert wrote:
> On Wed, Oct 16, 2013 at 01:29:44PM +0200, Linus Walleij wrote:
> > On Tue, Oct 15, 2013 at 3:45 PM, Christian Ruppert
> > <[email protected]> wrote:
> > > On Wed, Oct 09, 2013 at 02:19:17PM +0200, Linus Walleij wrote:
> >
> > >> It's not like I'm 100% certain on where to use one or the other
> > >> construct (a mechanism like the above is needed for threaded
> > >> IRQs I've noticed) but the chained handler seems more to the
> > >> point does it not?
> > >>
> > >> The only downside I've seen is that the parent IRQ does not get
> > >> a name and the accumulated IRQ stats in /proc/interrupts but
> > >> surely we can live without that (or fix it).
> > >>
> > >> Since I'm a bit rusty on chained IRQs correct me if I'm wrong...
> > >
> > > OK, it took me a while to figure this back out again because as far as
> > > I'm familiar with the IRQ framework you're right. The reason I'm not
> > > using irq_set_chained_handler is that we have one driver instance per
> > > GPIO bank and all GPIO banks share the same interrupt line. This means
> > > every driver instance needs its own (different) user data and a simple
> > > call to irq_set_handler_data(tb10x_gpio) won't suffice. I'm not aware of
> > > any mechanism that allows interrupt sharing with the
> > > irq_set_chained_handler() mechanism.
> >
> > OK yes makes perfect sense. We'll live with this then.
> >
> > I didn't see a new version of this patch with the other two, shall
> > I just apply this last version in the pin control tree with the
> > two other patches?
>
> If you don't see anything else which needs changing please do so, yes.

Hi Linus,

I didn't see this one pop up in the git repositories yet, are there any
issues left with the driver?

Best regards,
Christian

2013-10-25 03:27:37

by Kumar Gala

[permalink] [raw]
Subject: Re: [PATCH 03/03] GPIO: Add TB10x GPIO driver


On Oct 8, 2013, at 7:25 AM, Christian Ruppert wrote:

> The GPIO driver for the Abilis Systems TB10x series of SOCs based on ARC700
> CPUs. It supports GPIO control and GPIO interrupt generation. This driver
> works in conjunction with the TB10x pinctrl driver.
>
> Signed-off-by: Sascha Leuenberger <[email protected]>
> Signed-off-by: Christian Ruppert <[email protected]>
> ---
> .../devicetree/bindings/gpio/abilis,tb10x-gpio.txt | 36 ++
> drivers/gpio/Kconfig | 4 +
> drivers/gpio/Makefile | 1 +
> drivers/gpio/gpio-tb10x.c | 341 ++++++++++++++++++++
> 4 files changed, 382 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
> create mode 100644 drivers/gpio/gpio-tb10x.c
>
> diff --git a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
> new file mode 100644
> index 0000000..00611ac
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt
> @@ -0,0 +1,36 @@
> +* Abilis TB10x GPIO controller
> +
> +Required Properties:
> +- compatible: Should be "abilis,tb10x-gpio"
> +- reg: Address and length of the register set for the device
> +- gpio-controller: Marks the device node as a gpio controller.
> +- #gpio-cells: Should be <2>. The first cell is the pin number and the
> + second cell is used to specify optional parameters:
> + - bit 0 specifies polarity (0 for normal, 1 for inverted).
> +- abilis,ngpio: the number of GPIO pins this driver controls.
> +
> +Optional Properties:
> +- interrupt-controller: Marks the device node as an interrupt controller.
> +- #interrupt-cells: Should be <1>. Interrupts are triggered on both edges.
> +- interrupts: Defines the interrupt line connecting this GPIO controller to
> + its parent interrupt controller.
> +- interrupt-parent: Defines the parent interrupt controller.
> +
> +GPIO ranges are specified as described in
> +Documentation/devicetree/bindings/gpio/gpio.txt
> +
> +Example:
> +
> + gpioa: gpio@FF140000 {
> + compatible = "abilis,tb10x-gpio";
> + interrupt-controller;
> + #interrupt-cells = <1>;
> + interrupt-parent = <&tb10x_ictl>;
> + interrupts = <27 2>;
> + reg = <0xFF140000 0x1000>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + abilis,ngpio = <3>;
> + gpio-ranges = <&iomux 0 0 0>;
> + gpio-ranges-group-names = "gpioa_pins";
> + };

For the Binding part:

Acked-by: Kumar Gala <[email protected]>

- k

--
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2013-10-25 21:44:09

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 03/03] GPIO: Add TB10x GPIO driver

On Thu, Oct 24, 2013 at 6:23 PM, Christian Ruppert
<[email protected]> wrote:

> Hi Linus,
>
> I didn't see this one pop up in the git repositories yet, are there any
> issues left with the driver?

No, just me running around at the kernel meetings and being
busy, applied with Kumar's ACK now.

Yours,
Linus Walleij