|  | /* | 
|  | * Abilis Systems TB10x pin control driver | 
|  | * | 
|  | * Copyright (C) Abilis Systems 2012 | 
|  | * | 
|  | * Author: Christian Ruppert <christian.ruppert@abilis.com> | 
|  | * | 
|  | * 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 mode = pfg->mode; | 
|  | int j, port = pfg->port; | 
|  |  | 
|  | /* | 
|  | * 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_set_mux(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 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, | 
|  | .set_mux = tb10x_pctl_set_mux, | 
|  | }; | 
|  |  | 
|  | 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; | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  | 
|  | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | state->base = devm_ioremap_resource(dev, mem); | 
|  | if (IS_ERR(state->base)) { | 
|  | ret = PTR_ERR(state->base); | 
|  | 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 (!state->pctl) { | 
|  | dev_err(dev, "could not register TB10x pin driver\n"); | 
|  | ret = -EINVAL; | 
|  | 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), | 
|  | } | 
|  | }; | 
|  |  | 
|  | module_platform_driver(tb10x_pinctrl_pdrv); | 
|  |  | 
|  | MODULE_AUTHOR("Christian Ruppert <christian.ruppert@abilis.com>"); | 
|  | MODULE_LICENSE("GPL"); |