2013-10-15 17:39:38 +04:00
/*
* 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 ) ) {
2017-07-19 00:43:23 +03:00
pr_err ( " %pOF: No abilis,function property in device tree. \n " ,
np_config ) ;
2013-10-15 17:39:38 +04:00
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 ;
}
2017-08-10 13:06:25 +03:00
static const struct pinctrl_ops tb10x_pinctrl_ops = {
2013-10-15 17:39:38 +04:00
. 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 ,
2016-03-31 14:44:42 +03:00
. dt_free_map = pinctrl_utils_free_map ,
2013-10-15 17:39:38 +04:00
} ;
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 ;
2014-04-11 18:46:04 +04:00
int j , port = pfg - > port ;
2013-10-15 17:39:38 +04:00
/*
* 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 ) ;
}
2014-09-03 15:02:56 +04:00
static int tb10x_pctl_set_mux ( struct pinctrl_dev * pctl ,
2013-10-15 17:39:38 +04:00
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 ;
}
2017-08-10 13:06:25 +03:00
static const struct pinmux_ops tb10x_pinmux_ops = {
2013-10-15 17:39:38 +04:00
. 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 ,
2014-09-03 15:02:56 +04:00
. set_mux = tb10x_pctl_set_mux ,
2013-10-15 17:39:38 +04:00
} ;
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 ;
2014-10-21 13:06:59 +04:00
struct resource * mem ;
2013-10-15 17:39:38 +04:00
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 ) ;
2014-10-21 13:06:59 +04:00
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-10-15 17:39:38 +04:00
state - > base = devm_ioremap_resource ( dev , mem ) ;
2013-10-21 06:55:29 +04:00
if ( IS_ERR ( state - > base ) ) {
ret = PTR_ERR ( state - > base ) ;
2013-10-15 17:39:38 +04:00
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 + + ;
}
}
2016-02-24 12:14:07 +03:00
state - > pctl = devm_pinctrl_register ( dev , & tb10x_pindesc , state ) ;
2015-06-09 07:01:16 +03:00
if ( IS_ERR ( state - > pctl ) ) {
2013-10-15 17:39:38 +04:00
dev_err ( dev , " could not register TB10x pin driver \n " ) ;
2015-06-09 07:01:16 +03:00
ret = PTR_ERR ( state - > pctl ) ;
2013-10-15 17:39:38 +04:00
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 ) ;
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 ) ,
}
} ;
2013-10-23 12:46:00 +04:00
module_platform_driver ( tb10x_pinctrl_pdrv ) ;
2013-10-15 17:39:38 +04:00
MODULE_AUTHOR ( " Christian Ruppert <christian.ruppert@abilis.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;