diff options
Diffstat (limited to 'drivers/pinctrl/bcm')
-rw-r--r-- | drivers/pinctrl/bcm/Kconfig | 119 | ||||
-rw-r--r-- | drivers/pinctrl/bcm/Makefile | 10 | ||||
-rw-r--r-- | drivers/pinctrl/bcm/pinctrl-bcm281xx.c | 1448 | ||||
-rw-r--r-- | drivers/pinctrl/bcm/pinctrl-bcm2835.c | 1110 | ||||
-rw-r--r-- | drivers/pinctrl/bcm/pinctrl-cygnus-mux.c | 1022 | ||||
-rw-r--r-- | drivers/pinctrl/bcm/pinctrl-iproc-gpio.c | 909 | ||||
-rw-r--r-- | drivers/pinctrl/bcm/pinctrl-ns2-mux.c | 1113 | ||||
-rw-r--r-- | drivers/pinctrl/bcm/pinctrl-nsp-gpio.c | 731 | ||||
-rw-r--r-- | drivers/pinctrl/bcm/pinctrl-nsp-mux.c | 644 |
9 files changed, 7106 insertions, 0 deletions
diff --git a/drivers/pinctrl/bcm/Kconfig b/drivers/pinctrl/bcm/Kconfig new file mode 100644 index 000000000..e6cd31491 --- /dev/null +++ b/drivers/pinctrl/bcm/Kconfig @@ -0,0 +1,119 @@ +# +# Broadcom pinctrl drivers +# + +config PINCTRL_BCM281XX + bool "Broadcom BCM281xx pinctrl driver" + depends on OF && (ARCH_BCM_MOBILE || COMPILE_TEST) + select PINMUX + select PINCONF + select GENERIC_PINCONF + select REGMAP_MMIO + default ARCH_BCM_MOBILE + help + Say Y here to support Broadcom BCM281xx pinctrl driver, which is used + for the BCM281xx SoC family, including BCM11130, BCM11140, BCM11351, + BCM28145, and BCM28155 SoCs. This driver requires the pinctrl + framework. GPIO is provided by a separate GPIO driver. + +config PINCTRL_BCM2835 + bool + select PINMUX + select PINCONF + select GENERIC_PINCONF + select GPIOLIB + select GPIOLIB_IRQCHIP + +config PINCTRL_IPROC_GPIO + bool "Broadcom iProc GPIO (with PINCONF) driver" + depends on OF_GPIO && (ARCH_BCM_IPROC || COMPILE_TEST) + select GPIOLIB_IRQCHIP + select PINCONF + select GENERIC_PINCONF + default ARCH_BCM_IPROC + help + Say yes here to enable the Broadcom iProc GPIO driver. + + The Broadcom iProc based SoCs- Cygnus, NS2, NSP and Stingray, use + same GPIO Controller IP hence this driver could be used for all. + + The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU + GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and + the always-ON GPIO controller (CRMU/AON). All 3 GPIO controllers are + supported by this driver. + + The Broadcom NSP has two GPIO controllers including the ChipcommonA + GPIO, the ChipcommonB GPIO. Later controller is supported by this + driver. + + The Broadcom NS2 has two GPIO controller including the CRMU GPIO, + the ChipcommonG GPIO. Both controllers are supported by this driver. + + The Broadcom Stingray GPIO controllers are supported by this driver. + + All above SoCs GPIO controllers support basic PINCONF functions such + as bias pull up, pull down, and drive strength configurations, when + these pins are muxed to GPIO. + + It provides the framework where pins from the individual GPIO can be + individually muxed to GPIO function, through interaction with the + SoCs IOMUX controller. This features could be used only on SoCs which + support individual pin muxing. + +config PINCTRL_CYGNUS_MUX + bool "Broadcom Cygnus IOMUX driver" + depends on (ARCH_BCM_CYGNUS || COMPILE_TEST) + depends on OF + select PINMUX + select GENERIC_PINCONF + default ARCH_BCM_CYGNUS + help + Say yes here to enable the Broadcom Cygnus IOMUX driver. + + The Broadcom Cygnus IOMUX driver supports group based IOMUX + configuration, with the exception that certain individual pins + can be overridden to GPIO function + +config PINCTRL_NSP_GPIO + bool "Broadcom NSP GPIO (with PINCONF) driver" + depends on OF_GPIO && (ARCH_BCM_NSP || COMPILE_TEST) + select GPIOLIB_IRQCHIP + select PINCONF + select GENERIC_PINCONF + default ARCH_BCM_NSP + help + Say yes here to enable the Broadcom NSP GPIO driver. + + The Broadcom Northstar Plus SoC ChipcommonA GPIO controller is + supported by this driver. + + The ChipcommonA GPIO controller support basic PINCONF functions such + as bias pull up, pull down, and drive strength configurations, when + these pins are muxed to GPIO. + +config PINCTRL_NS2_MUX + bool "Broadcom Northstar2 pinmux driver" + depends on OF + depends on ARCH_BCM_IPROC || COMPILE_TEST + select PINMUX + select GENERIC_PINCONF + default ARM64 && ARCH_BCM_IPROC + help + Say yes here to enable the Broadcom NS2 MUX driver. + + The Broadcom Northstar2 IOMUX driver supports group based IOMUX + configuration. + +config PINCTRL_NSP_MUX + bool "Broadcom NSP IOMUX driver" + depends on (ARCH_BCM_NSP || COMPILE_TEST) + depends on OF + select PINMUX + select GENERIC_PINCONF + default ARCH_BCM_NSP + help + Say yes here to enable the Broadcom NSP SOC IOMUX driver. + + The Broadcom Northstar Plus IOMUX driver supports pin based IOMUX + configuration, with certain individual pins can be overridden + to GPIO function. diff --git a/drivers/pinctrl/bcm/Makefile b/drivers/pinctrl/bcm/Makefile new file mode 100644 index 000000000..80ceb9dae --- /dev/null +++ b/drivers/pinctrl/bcm/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# Broadcom pinctrl support + +obj-$(CONFIG_PINCTRL_BCM281XX) += pinctrl-bcm281xx.o +obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o +obj-$(CONFIG_PINCTRL_IPROC_GPIO) += pinctrl-iproc-gpio.o +obj-$(CONFIG_PINCTRL_CYGNUS_MUX) += pinctrl-cygnus-mux.o +obj-$(CONFIG_PINCTRL_NSP_GPIO) += pinctrl-nsp-gpio.o +obj-$(CONFIG_PINCTRL_NS2_MUX) += pinctrl-ns2-mux.o +obj-$(CONFIG_PINCTRL_NSP_MUX) += pinctrl-nsp-mux.o diff --git a/drivers/pinctrl/bcm/pinctrl-bcm281xx.c b/drivers/pinctrl/bcm/pinctrl-bcm281xx.c new file mode 100644 index 000000000..bc3b232a7 --- /dev/null +++ b/drivers/pinctrl/bcm/pinctrl-bcm281xx.c @@ -0,0 +1,1448 @@ +/* + * Copyright (C) 2013-2017 Broadcom + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include "../core.h" +#include "../pinctrl-utils.h" + +/* BCM281XX Pin Control Registers Definitions */ + +/* Function Select bits are the same for all pin control registers */ +#define BCM281XX_PIN_REG_F_SEL_MASK 0x0700 +#define BCM281XX_PIN_REG_F_SEL_SHIFT 8 + +/* Standard pin register */ +#define BCM281XX_STD_PIN_REG_DRV_STR_MASK 0x0007 +#define BCM281XX_STD_PIN_REG_DRV_STR_SHIFT 0 +#define BCM281XX_STD_PIN_REG_INPUT_DIS_MASK 0x0008 +#define BCM281XX_STD_PIN_REG_INPUT_DIS_SHIFT 3 +#define BCM281XX_STD_PIN_REG_SLEW_MASK 0x0010 +#define BCM281XX_STD_PIN_REG_SLEW_SHIFT 4 +#define BCM281XX_STD_PIN_REG_PULL_UP_MASK 0x0020 +#define BCM281XX_STD_PIN_REG_PULL_UP_SHIFT 5 +#define BCM281XX_STD_PIN_REG_PULL_DN_MASK 0x0040 +#define BCM281XX_STD_PIN_REG_PULL_DN_SHIFT 6 +#define BCM281XX_STD_PIN_REG_HYST_MASK 0x0080 +#define BCM281XX_STD_PIN_REG_HYST_SHIFT 7 + +/* I2C pin register */ +#define BCM281XX_I2C_PIN_REG_INPUT_DIS_MASK 0x0004 +#define BCM281XX_I2C_PIN_REG_INPUT_DIS_SHIFT 2 +#define BCM281XX_I2C_PIN_REG_SLEW_MASK 0x0008 +#define BCM281XX_I2C_PIN_REG_SLEW_SHIFT 3 +#define BCM281XX_I2C_PIN_REG_PULL_UP_STR_MASK 0x0070 +#define BCM281XX_I2C_PIN_REG_PULL_UP_STR_SHIFT 4 + +/* HDMI pin register */ +#define BCM281XX_HDMI_PIN_REG_INPUT_DIS_MASK 0x0008 +#define BCM281XX_HDMI_PIN_REG_INPUT_DIS_SHIFT 3 +#define BCM281XX_HDMI_PIN_REG_MODE_MASK 0x0010 +#define BCM281XX_HDMI_PIN_REG_MODE_SHIFT 4 + +/** + * bcm281xx_pin_type - types of pin register + */ +enum bcm281xx_pin_type { + BCM281XX_PIN_TYPE_UNKNOWN = 0, + BCM281XX_PIN_TYPE_STD, + BCM281XX_PIN_TYPE_I2C, + BCM281XX_PIN_TYPE_HDMI, +}; + +static enum bcm281xx_pin_type std_pin = BCM281XX_PIN_TYPE_STD; +static enum bcm281xx_pin_type i2c_pin = BCM281XX_PIN_TYPE_I2C; +static enum bcm281xx_pin_type hdmi_pin = BCM281XX_PIN_TYPE_HDMI; + +/** + * bcm281xx_pin_function- define pin function + */ +struct bcm281xx_pin_function { + const char *name; + const char * const *groups; + const unsigned ngroups; +}; + +/** + * bcm281xx_pinctrl_data - Broadcom-specific pinctrl data + * @reg_base - base of pinctrl registers + */ +struct bcm281xx_pinctrl_data { + void __iomem *reg_base; + + /* List of all pins */ + const struct pinctrl_pin_desc *pins; + const unsigned npins; + + const struct bcm281xx_pin_function *functions; + const unsigned nfunctions; + + struct regmap *regmap; +}; + +/* + * Pin number definition. The order here must be the same as defined in the + * PADCTRLREG block in the RDB. + */ +#define BCM281XX_PIN_ADCSYNC 0 +#define BCM281XX_PIN_BAT_RM 1 +#define BCM281XX_PIN_BSC1_SCL 2 +#define BCM281XX_PIN_BSC1_SDA 3 +#define BCM281XX_PIN_BSC2_SCL 4 +#define BCM281XX_PIN_BSC2_SDA 5 +#define BCM281XX_PIN_CLASSGPWR 6 +#define BCM281XX_PIN_CLK_CX8 7 +#define BCM281XX_PIN_CLKOUT_0 8 +#define BCM281XX_PIN_CLKOUT_1 9 +#define BCM281XX_PIN_CLKOUT_2 10 +#define BCM281XX_PIN_CLKOUT_3 11 +#define BCM281XX_PIN_CLKREQ_IN_0 12 +#define BCM281XX_PIN_CLKREQ_IN_1 13 +#define BCM281XX_PIN_CWS_SYS_REQ1 14 +#define BCM281XX_PIN_CWS_SYS_REQ2 15 +#define BCM281XX_PIN_CWS_SYS_REQ3 16 +#define BCM281XX_PIN_DIGMIC1_CLK 17 +#define BCM281XX_PIN_DIGMIC1_DQ 18 +#define BCM281XX_PIN_DIGMIC2_CLK 19 +#define BCM281XX_PIN_DIGMIC2_DQ 20 +#define BCM281XX_PIN_GPEN13 21 +#define BCM281XX_PIN_GPEN14 22 +#define BCM281XX_PIN_GPEN15 23 +#define BCM281XX_PIN_GPIO00 24 +#define BCM281XX_PIN_GPIO01 25 +#define BCM281XX_PIN_GPIO02 26 +#define BCM281XX_PIN_GPIO03 27 +#define BCM281XX_PIN_GPIO04 28 +#define BCM281XX_PIN_GPIO05 29 +#define BCM281XX_PIN_GPIO06 30 +#define BCM281XX_PIN_GPIO07 31 +#define BCM281XX_PIN_GPIO08 32 +#define BCM281XX_PIN_GPIO09 33 +#define BCM281XX_PIN_GPIO10 34 +#define BCM281XX_PIN_GPIO11 35 +#define BCM281XX_PIN_GPIO12 36 +#define BCM281XX_PIN_GPIO13 37 +#define BCM281XX_PIN_GPIO14 38 +#define BCM281XX_PIN_GPS_PABLANK 39 +#define BCM281XX_PIN_GPS_TMARK 40 +#define BCM281XX_PIN_HDMI_SCL 41 +#define BCM281XX_PIN_HDMI_SDA 42 +#define BCM281XX_PIN_IC_DM 43 +#define BCM281XX_PIN_IC_DP 44 +#define BCM281XX_PIN_KP_COL_IP_0 45 +#define BCM281XX_PIN_KP_COL_IP_1 46 +#define BCM281XX_PIN_KP_COL_IP_2 47 +#define BCM281XX_PIN_KP_COL_IP_3 48 +#define BCM281XX_PIN_KP_ROW_OP_0 49 +#define BCM281XX_PIN_KP_ROW_OP_1 50 +#define BCM281XX_PIN_KP_ROW_OP_2 51 +#define BCM281XX_PIN_KP_ROW_OP_3 52 +#define BCM281XX_PIN_LCD_B_0 53 +#define BCM281XX_PIN_LCD_B_1 54 +#define BCM281XX_PIN_LCD_B_2 55 +#define BCM281XX_PIN_LCD_B_3 56 +#define BCM281XX_PIN_LCD_B_4 57 +#define BCM281XX_PIN_LCD_B_5 58 +#define BCM281XX_PIN_LCD_B_6 59 +#define BCM281XX_PIN_LCD_B_7 60 +#define BCM281XX_PIN_LCD_G_0 61 +#define BCM281XX_PIN_LCD_G_1 62 +#define BCM281XX_PIN_LCD_G_2 63 +#define BCM281XX_PIN_LCD_G_3 64 +#define BCM281XX_PIN_LCD_G_4 65 +#define BCM281XX_PIN_LCD_G_5 66 +#define BCM281XX_PIN_LCD_G_6 67 +#define BCM281XX_PIN_LCD_G_7 68 +#define BCM281XX_PIN_LCD_HSYNC 69 +#define BCM281XX_PIN_LCD_OE 70 +#define BCM281XX_PIN_LCD_PCLK 71 +#define BCM281XX_PIN_LCD_R_0 72 +#define BCM281XX_PIN_LCD_R_1 73 +#define BCM281XX_PIN_LCD_R_2 74 +#define BCM281XX_PIN_LCD_R_3 75 +#define BCM281XX_PIN_LCD_R_4 76 +#define BCM281XX_PIN_LCD_R_5 77 +#define BCM281XX_PIN_LCD_R_6 78 +#define BCM281XX_PIN_LCD_R_7 79 +#define BCM281XX_PIN_LCD_VSYNC 80 +#define BCM281XX_PIN_MDMGPIO0 81 +#define BCM281XX_PIN_MDMGPIO1 82 +#define BCM281XX_PIN_MDMGPIO2 83 +#define BCM281XX_PIN_MDMGPIO3 84 +#define BCM281XX_PIN_MDMGPIO4 85 +#define BCM281XX_PIN_MDMGPIO5 86 +#define BCM281XX_PIN_MDMGPIO6 87 +#define BCM281XX_PIN_MDMGPIO7 88 +#define BCM281XX_PIN_MDMGPIO8 89 +#define BCM281XX_PIN_MPHI_DATA_0 90 +#define BCM281XX_PIN_MPHI_DATA_1 91 +#define BCM281XX_PIN_MPHI_DATA_2 92 +#define BCM281XX_PIN_MPHI_DATA_3 93 +#define BCM281XX_PIN_MPHI_DATA_4 94 +#define BCM281XX_PIN_MPHI_DATA_5 95 +#define BCM281XX_PIN_MPHI_DATA_6 96 +#define BCM281XX_PIN_MPHI_DATA_7 97 +#define BCM281XX_PIN_MPHI_DATA_8 98 +#define BCM281XX_PIN_MPHI_DATA_9 99 +#define BCM281XX_PIN_MPHI_DATA_10 100 +#define BCM281XX_PIN_MPHI_DATA_11 101 +#define BCM281XX_PIN_MPHI_DATA_12 102 +#define BCM281XX_PIN_MPHI_DATA_13 103 +#define BCM281XX_PIN_MPHI_DATA_14 104 +#define BCM281XX_PIN_MPHI_DATA_15 105 +#define BCM281XX_PIN_MPHI_HA0 106 +#define BCM281XX_PIN_MPHI_HAT0 107 +#define BCM281XX_PIN_MPHI_HAT1 108 +#define BCM281XX_PIN_MPHI_HCE0_N 109 +#define BCM281XX_PIN_MPHI_HCE1_N 110 +#define BCM281XX_PIN_MPHI_HRD_N 111 +#define BCM281XX_PIN_MPHI_HWR_N 112 +#define BCM281XX_PIN_MPHI_RUN0 113 +#define BCM281XX_PIN_MPHI_RUN1 114 +#define BCM281XX_PIN_MTX_SCAN_CLK 115 +#define BCM281XX_PIN_MTX_SCAN_DATA 116 +#define BCM281XX_PIN_NAND_AD_0 117 +#define BCM281XX_PIN_NAND_AD_1 118 +#define BCM281XX_PIN_NAND_AD_2 119 +#define BCM281XX_PIN_NAND_AD_3 120 +#define BCM281XX_PIN_NAND_AD_4 121 +#define BCM281XX_PIN_NAND_AD_5 122 +#define BCM281XX_PIN_NAND_AD_6 123 +#define BCM281XX_PIN_NAND_AD_7 124 +#define BCM281XX_PIN_NAND_ALE 125 +#define BCM281XX_PIN_NAND_CEN_0 126 +#define BCM281XX_PIN_NAND_CEN_1 127 +#define BCM281XX_PIN_NAND_CLE 128 +#define BCM281XX_PIN_NAND_OEN 129 +#define BCM281XX_PIN_NAND_RDY_0 130 +#define BCM281XX_PIN_NAND_RDY_1 131 +#define BCM281XX_PIN_NAND_WEN 132 +#define BCM281XX_PIN_NAND_WP 133 +#define BCM281XX_PIN_PC1 134 +#define BCM281XX_PIN_PC2 135 +#define BCM281XX_PIN_PMU_INT 136 +#define BCM281XX_PIN_PMU_SCL 137 +#define BCM281XX_PIN_PMU_SDA 138 +#define BCM281XX_PIN_RFST2G_MTSLOTEN3G 139 +#define BCM281XX_PIN_RGMII_0_RX_CTL 140 +#define BCM281XX_PIN_RGMII_0_RXC 141 +#define BCM281XX_PIN_RGMII_0_RXD_0 142 +#define BCM281XX_PIN_RGMII_0_RXD_1 143 +#define BCM281XX_PIN_RGMII_0_RXD_2 144 +#define BCM281XX_PIN_RGMII_0_RXD_3 145 +#define BCM281XX_PIN_RGMII_0_TX_CTL 146 +#define BCM281XX_PIN_RGMII_0_TXC 147 +#define BCM281XX_PIN_RGMII_0_TXD_0 148 +#define BCM281XX_PIN_RGMII_0_TXD_1 149 +#define BCM281XX_PIN_RGMII_0_TXD_2 150 +#define BCM281XX_PIN_RGMII_0_TXD_3 151 +#define BCM281XX_PIN_RGMII_1_RX_CTL 152 +#define BCM281XX_PIN_RGMII_1_RXC 153 +#define BCM281XX_PIN_RGMII_1_RXD_0 154 +#define BCM281XX_PIN_RGMII_1_RXD_1 155 +#define BCM281XX_PIN_RGMII_1_RXD_2 156 +#define BCM281XX_PIN_RGMII_1_RXD_3 157 +#define BCM281XX_PIN_RGMII_1_TX_CTL 158 +#define BCM281XX_PIN_RGMII_1_TXC 159 +#define BCM281XX_PIN_RGMII_1_TXD_0 160 +#define BCM281XX_PIN_RGMII_1_TXD_1 161 +#define BCM281XX_PIN_RGMII_1_TXD_2 162 +#define BCM281XX_PIN_RGMII_1_TXD_3 163 +#define BCM281XX_PIN_RGMII_GPIO_0 164 +#define BCM281XX_PIN_RGMII_GPIO_1 165 +#define BCM281XX_PIN_RGMII_GPIO_2 166 +#define BCM281XX_PIN_RGMII_GPIO_3 167 +#define BCM281XX_PIN_RTXDATA2G_TXDATA3G1 168 +#define BCM281XX_PIN_RTXEN2G_TXDATA3G2 169 +#define BCM281XX_PIN_RXDATA3G0 170 +#define BCM281XX_PIN_RXDATA3G1 171 +#define BCM281XX_PIN_RXDATA3G2 172 +#define BCM281XX_PIN_SDIO1_CLK 173 +#define BCM281XX_PIN_SDIO1_CMD 174 +#define BCM281XX_PIN_SDIO1_DATA_0 175 +#define BCM281XX_PIN_SDIO1_DATA_1 176 +#define BCM281XX_PIN_SDIO1_DATA_2 177 +#define BCM281XX_PIN_SDIO1_DATA_3 178 +#define BCM281XX_PIN_SDIO4_CLK 179 +#define BCM281XX_PIN_SDIO4_CMD 180 +#define BCM281XX_PIN_SDIO4_DATA_0 181 +#define BCM281XX_PIN_SDIO4_DATA_1 182 +#define BCM281XX_PIN_SDIO4_DATA_2 183 +#define BCM281XX_PIN_SDIO4_DATA_3 184 +#define BCM281XX_PIN_SIM_CLK 185 +#define BCM281XX_PIN_SIM_DATA 186 +#define BCM281XX_PIN_SIM_DET 187 +#define BCM281XX_PIN_SIM_RESETN 188 +#define BCM281XX_PIN_SIM2_CLK 189 +#define BCM281XX_PIN_SIM2_DATA 190 +#define BCM281XX_PIN_SIM2_DET 191 +#define BCM281XX_PIN_SIM2_RESETN 192 +#define BCM281XX_PIN_SRI_C 193 +#define BCM281XX_PIN_SRI_D 194 +#define BCM281XX_PIN_SRI_E 195 +#define BCM281XX_PIN_SSP_EXTCLK 196 +#define BCM281XX_PIN_SSP0_CLK 197 +#define BCM281XX_PIN_SSP0_FS 198 +#define BCM281XX_PIN_SSP0_RXD 199 +#define BCM281XX_PIN_SSP0_TXD 200 +#define BCM281XX_PIN_SSP2_CLK 201 +#define BCM281XX_PIN_SSP2_FS_0 202 +#define BCM281XX_PIN_SSP2_FS_1 203 +#define BCM281XX_PIN_SSP2_FS_2 204 +#define BCM281XX_PIN_SSP2_FS_3 205 +#define BCM281XX_PIN_SSP2_RXD_0 206 +#define BCM281XX_PIN_SSP2_RXD_1 207 +#define BCM281XX_PIN_SSP2_TXD_0 208 +#define BCM281XX_PIN_SSP2_TXD_1 209 +#define BCM281XX_PIN_SSP3_CLK 210 +#define BCM281XX_PIN_SSP3_FS 211 +#define BCM281XX_PIN_SSP3_RXD 212 +#define BCM281XX_PIN_SSP3_TXD 213 +#define BCM281XX_PIN_SSP4_CLK 214 +#define BCM281XX_PIN_SSP4_FS 215 +#define BCM281XX_PIN_SSP4_RXD 216 +#define BCM281XX_PIN_SSP4_TXD 217 +#define BCM281XX_PIN_SSP5_CLK 218 +#define BCM281XX_PIN_SSP5_FS 219 +#define BCM281XX_PIN_SSP5_RXD 220 +#define BCM281XX_PIN_SSP5_TXD 221 +#define BCM281XX_PIN_SSP6_CLK 222 +#define BCM281XX_PIN_SSP6_FS 223 +#define BCM281XX_PIN_SSP6_RXD 224 +#define BCM281XX_PIN_SSP6_TXD 225 +#define BCM281XX_PIN_STAT_1 226 +#define BCM281XX_PIN_STAT_2 227 +#define BCM281XX_PIN_SYSCLKEN 228 +#define BCM281XX_PIN_TRACECLK 229 +#define BCM281XX_PIN_TRACEDT00 230 +#define BCM281XX_PIN_TRACEDT01 231 +#define BCM281XX_PIN_TRACEDT02 232 +#define BCM281XX_PIN_TRACEDT03 233 +#define BCM281XX_PIN_TRACEDT04 234 +#define BCM281XX_PIN_TRACEDT05 235 +#define BCM281XX_PIN_TRACEDT06 236 +#define BCM281XX_PIN_TRACEDT07 237 +#define BCM281XX_PIN_TRACEDT08 238 +#define BCM281XX_PIN_TRACEDT09 239 +#define BCM281XX_PIN_TRACEDT10 240 +#define BCM281XX_PIN_TRACEDT11 241 +#define BCM281XX_PIN_TRACEDT12 242 +#define BCM281XX_PIN_TRACEDT13 243 +#define BCM281XX_PIN_TRACEDT14 244 +#define BCM281XX_PIN_TRACEDT15 245 +#define BCM281XX_PIN_TXDATA3G0 246 +#define BCM281XX_PIN_TXPWRIND 247 +#define BCM281XX_PIN_UARTB1_UCTS 248 +#define BCM281XX_PIN_UARTB1_URTS 249 +#define BCM281XX_PIN_UARTB1_URXD 250 +#define BCM281XX_PIN_UARTB1_UTXD 251 +#define BCM281XX_PIN_UARTB2_URXD 252 +#define BCM281XX_PIN_UARTB2_UTXD 253 +#define BCM281XX_PIN_UARTB3_UCTS 254 +#define BCM281XX_PIN_UARTB3_URTS 255 +#define BCM281XX_PIN_UARTB3_URXD 256 +#define BCM281XX_PIN_UARTB3_UTXD 257 +#define BCM281XX_PIN_UARTB4_UCTS 258 +#define BCM281XX_PIN_UARTB4_URTS 259 +#define BCM281XX_PIN_UARTB4_URXD 260 +#define BCM281XX_PIN_UARTB4_UTXD 261 +#define BCM281XX_PIN_VC_CAM1_SCL 262 +#define BCM281XX_PIN_VC_CAM1_SDA 263 +#define BCM281XX_PIN_VC_CAM2_SCL 264 +#define BCM281XX_PIN_VC_CAM2_SDA 265 +#define BCM281XX_PIN_VC_CAM3_SCL 266 +#define BCM281XX_PIN_VC_CAM3_SDA 267 + +#define BCM281XX_PIN_DESC(a, b, c) \ + { .number = a, .name = b, .drv_data = &c##_pin } + +/* + * Pin description definition. The order here must be the same as defined in + * the PADCTRLREG block in the RDB, since the pin number is used as an index + * into this array. + */ +static const struct pinctrl_pin_desc bcm281xx_pinctrl_pins[] = { + BCM281XX_PIN_DESC(BCM281XX_PIN_ADCSYNC, "adcsync", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_BAT_RM, "bat_rm", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_BSC1_SCL, "bsc1_scl", i2c), + BCM281XX_PIN_DESC(BCM281XX_PIN_BSC1_SDA, "bsc1_sda", i2c), + BCM281XX_PIN_DESC(BCM281XX_PIN_BSC2_SCL, "bsc2_scl", i2c), + BCM281XX_PIN_DESC(BCM281XX_PIN_BSC2_SDA, "bsc2_sda", i2c), + BCM281XX_PIN_DESC(BCM281XX_PIN_CLASSGPWR, "classgpwr", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_CLK_CX8, "clk_cx8", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_CLKOUT_0, "clkout_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_CLKOUT_1, "clkout_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_CLKOUT_2, "clkout_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_CLKOUT_3, "clkout_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_CLKREQ_IN_0, "clkreq_in_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_CLKREQ_IN_1, "clkreq_in_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_CWS_SYS_REQ1, "cws_sys_req1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_CWS_SYS_REQ2, "cws_sys_req2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_CWS_SYS_REQ3, "cws_sys_req3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_DIGMIC1_CLK, "digmic1_clk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_DIGMIC1_DQ, "digmic1_dq", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_DIGMIC2_CLK, "digmic2_clk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_DIGMIC2_DQ, "digmic2_dq", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPEN13, "gpen13", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPEN14, "gpen14", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPEN15, "gpen15", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO00, "gpio00", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO01, "gpio01", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO02, "gpio02", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO03, "gpio03", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO04, "gpio04", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO05, "gpio05", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO06, "gpio06", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO07, "gpio07", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO08, "gpio08", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO09, "gpio09", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO10, "gpio10", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO11, "gpio11", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO12, "gpio12", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO13, "gpio13", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPIO14, "gpio14", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPS_PABLANK, "gps_pablank", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_GPS_TMARK, "gps_tmark", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_HDMI_SCL, "hdmi_scl", hdmi), + BCM281XX_PIN_DESC(BCM281XX_PIN_HDMI_SDA, "hdmi_sda", hdmi), + BCM281XX_PIN_DESC(BCM281XX_PIN_IC_DM, "ic_dm", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_IC_DP, "ic_dp", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_KP_COL_IP_0, "kp_col_ip_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_KP_COL_IP_1, "kp_col_ip_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_KP_COL_IP_2, "kp_col_ip_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_KP_COL_IP_3, "kp_col_ip_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_KP_ROW_OP_0, "kp_row_op_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_KP_ROW_OP_1, "kp_row_op_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_KP_ROW_OP_2, "kp_row_op_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_KP_ROW_OP_3, "kp_row_op_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_B_0, "lcd_b_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_B_1, "lcd_b_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_B_2, "lcd_b_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_B_3, "lcd_b_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_B_4, "lcd_b_4", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_B_5, "lcd_b_5", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_B_6, "lcd_b_6", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_B_7, "lcd_b_7", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_G_0, "lcd_g_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_G_1, "lcd_g_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_G_2, "lcd_g_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_G_3, "lcd_g_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_G_4, "lcd_g_4", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_G_5, "lcd_g_5", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_G_6, "lcd_g_6", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_G_7, "lcd_g_7", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_HSYNC, "lcd_hsync", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_OE, "lcd_oe", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_PCLK, "lcd_pclk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_R_0, "lcd_r_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_R_1, "lcd_r_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_R_2, "lcd_r_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_R_3, "lcd_r_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_R_4, "lcd_r_4", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_R_5, "lcd_r_5", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_R_6, "lcd_r_6", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_R_7, "lcd_r_7", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_LCD_VSYNC, "lcd_vsync", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MDMGPIO0, "mdmgpio0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MDMGPIO1, "mdmgpio1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MDMGPIO2, "mdmgpio2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MDMGPIO3, "mdmgpio3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MDMGPIO4, "mdmgpio4", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MDMGPIO5, "mdmgpio5", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MDMGPIO6, "mdmgpio6", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MDMGPIO7, "mdmgpio7", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MDMGPIO8, "mdmgpio8", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_0, "mphi_data_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_1, "mphi_data_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_2, "mphi_data_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_3, "mphi_data_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_4, "mphi_data_4", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_5, "mphi_data_5", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_6, "mphi_data_6", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_7, "mphi_data_7", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_8, "mphi_data_8", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_9, "mphi_data_9", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_10, "mphi_data_10", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_11, "mphi_data_11", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_12, "mphi_data_12", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_13, "mphi_data_13", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_14, "mphi_data_14", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_DATA_15, "mphi_data_15", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_HA0, "mphi_ha0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_HAT0, "mphi_hat0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_HAT1, "mphi_hat1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_HCE0_N, "mphi_hce0_n", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_HCE1_N, "mphi_hce1_n", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_HRD_N, "mphi_hrd_n", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_HWR_N, "mphi_hwr_n", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_RUN0, "mphi_run0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MPHI_RUN1, "mphi_run1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MTX_SCAN_CLK, "mtx_scan_clk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_MTX_SCAN_DATA, "mtx_scan_data", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_AD_0, "nand_ad_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_AD_1, "nand_ad_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_AD_2, "nand_ad_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_AD_3, "nand_ad_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_AD_4, "nand_ad_4", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_AD_5, "nand_ad_5", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_AD_6, "nand_ad_6", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_AD_7, "nand_ad_7", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_ALE, "nand_ale", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_CEN_0, "nand_cen_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_CEN_1, "nand_cen_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_CLE, "nand_cle", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_OEN, "nand_oen", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_RDY_0, "nand_rdy_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_RDY_1, "nand_rdy_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_WEN, "nand_wen", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_NAND_WP, "nand_wp", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_PC1, "pc1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_PC2, "pc2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_PMU_INT, "pmu_int", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_PMU_SCL, "pmu_scl", i2c), + BCM281XX_PIN_DESC(BCM281XX_PIN_PMU_SDA, "pmu_sda", i2c), + BCM281XX_PIN_DESC(BCM281XX_PIN_RFST2G_MTSLOTEN3G, "rfst2g_mtsloten3g", + std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_0_RX_CTL, "rgmii_0_rx_ctl", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_0_RXC, "rgmii_0_rxc", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_0_RXD_0, "rgmii_0_rxd_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_0_RXD_1, "rgmii_0_rxd_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_0_RXD_2, "rgmii_0_rxd_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_0_RXD_3, "rgmii_0_rxd_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_0_TX_CTL, "rgmii_0_tx_ctl", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_0_TXC, "rgmii_0_txc", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_0_TXD_0, "rgmii_0_txd_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_0_TXD_1, "rgmii_0_txd_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_0_TXD_2, "rgmii_0_txd_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_0_TXD_3, "rgmii_0_txd_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_1_RX_CTL, "rgmii_1_rx_ctl", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_1_RXC, "rgmii_1_rxc", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_1_RXD_0, "rgmii_1_rxd_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_1_RXD_1, "rgmii_1_rxd_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_1_RXD_2, "rgmii_1_rxd_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_1_RXD_3, "rgmii_1_rxd_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_1_TX_CTL, "rgmii_1_tx_ctl", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_1_TXC, "rgmii_1_txc", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_1_TXD_0, "rgmii_1_txd_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_1_TXD_1, "rgmii_1_txd_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_1_TXD_2, "rgmii_1_txd_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_1_TXD_3, "rgmii_1_txd_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_GPIO_0, "rgmii_gpio_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_GPIO_1, "rgmii_gpio_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_GPIO_2, "rgmii_gpio_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RGMII_GPIO_3, "rgmii_gpio_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RTXDATA2G_TXDATA3G1, + "rtxdata2g_txdata3g1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RTXEN2G_TXDATA3G2, "rtxen2g_txdata3g2", + std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RXDATA3G0, "rxdata3g0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RXDATA3G1, "rxdata3g1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_RXDATA3G2, "rxdata3g2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SDIO1_CLK, "sdio1_clk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SDIO1_CMD, "sdio1_cmd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SDIO1_DATA_0, "sdio1_data_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SDIO1_DATA_1, "sdio1_data_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SDIO1_DATA_2, "sdio1_data_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SDIO1_DATA_3, "sdio1_data_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SDIO4_CLK, "sdio4_clk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SDIO4_CMD, "sdio4_cmd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SDIO4_DATA_0, "sdio4_data_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SDIO4_DATA_1, "sdio4_data_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SDIO4_DATA_2, "sdio4_data_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SDIO4_DATA_3, "sdio4_data_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SIM_CLK, "sim_clk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SIM_DATA, "sim_data", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SIM_DET, "sim_det", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SIM_RESETN, "sim_resetn", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SIM2_CLK, "sim2_clk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SIM2_DATA, "sim2_data", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SIM2_DET, "sim2_det", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SIM2_RESETN, "sim2_resetn", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SRI_C, "sri_c", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SRI_D, "sri_d", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SRI_E, "sri_e", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP_EXTCLK, "ssp_extclk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP0_CLK, "ssp0_clk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP0_FS, "ssp0_fs", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP0_RXD, "ssp0_rxd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP0_TXD, "ssp0_txd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP2_CLK, "ssp2_clk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP2_FS_0, "ssp2_fs_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP2_FS_1, "ssp2_fs_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP2_FS_2, "ssp2_fs_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP2_FS_3, "ssp2_fs_3", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP2_RXD_0, "ssp2_rxd_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP2_RXD_1, "ssp2_rxd_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP2_TXD_0, "ssp2_txd_0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP2_TXD_1, "ssp2_txd_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP3_CLK, "ssp3_clk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP3_FS, "ssp3_fs", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP3_RXD, "ssp3_rxd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP3_TXD, "ssp3_txd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP4_CLK, "ssp4_clk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP4_FS, "ssp4_fs", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP4_RXD, "ssp4_rxd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP4_TXD, "ssp4_txd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP5_CLK, "ssp5_clk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP5_FS, "ssp5_fs", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP5_RXD, "ssp5_rxd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP5_TXD, "ssp5_txd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP6_CLK, "ssp6_clk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP6_FS, "ssp6_fs", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP6_RXD, "ssp6_rxd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SSP6_TXD, "ssp6_txd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_STAT_1, "stat_1", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_STAT_2, "stat_2", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_SYSCLKEN, "sysclken", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACECLK, "traceclk", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT00, "tracedt00", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT01, "tracedt01", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT02, "tracedt02", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT03, "tracedt03", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT04, "tracedt04", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT05, "tracedt05", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT06, "tracedt06", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT07, "tracedt07", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT08, "tracedt08", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT09, "tracedt09", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT10, "tracedt10", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT11, "tracedt11", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT12, "tracedt12", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT13, "tracedt13", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT14, "tracedt14", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TRACEDT15, "tracedt15", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TXDATA3G0, "txdata3g0", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_TXPWRIND, "txpwrind", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB1_UCTS, "uartb1_ucts", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB1_URTS, "uartb1_urts", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB1_URXD, "uartb1_urxd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB1_UTXD, "uartb1_utxd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB2_URXD, "uartb2_urxd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB2_UTXD, "uartb2_utxd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB3_UCTS, "uartb3_ucts", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB3_URTS, "uartb3_urts", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB3_URXD, "uartb3_urxd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB3_UTXD, "uartb3_utxd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB4_UCTS, "uartb4_ucts", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB4_URTS, "uartb4_urts", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB4_URXD, "uartb4_urxd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_UARTB4_UTXD, "uartb4_utxd", std), + BCM281XX_PIN_DESC(BCM281XX_PIN_VC_CAM1_SCL, "vc_cam1_scl", i2c), + BCM281XX_PIN_DESC(BCM281XX_PIN_VC_CAM1_SDA, "vc_cam1_sda", i2c), + BCM281XX_PIN_DESC(BCM281XX_PIN_VC_CAM2_SCL, "vc_cam2_scl", i2c), + BCM281XX_PIN_DESC(BCM281XX_PIN_VC_CAM2_SDA, "vc_cam2_sda", i2c), + BCM281XX_PIN_DESC(BCM281XX_PIN_VC_CAM3_SCL, "vc_cam3_scl", i2c), + BCM281XX_PIN_DESC(BCM281XX_PIN_VC_CAM3_SDA, "vc_cam3_sda", i2c), +}; + +static const char * const bcm281xx_alt_groups[] = { + "adcsync", + "bat_rm", + "bsc1_scl", + "bsc1_sda", + "bsc2_scl", + "bsc2_sda", + "classgpwr", + "clk_cx8", + "clkout_0", + "clkout_1", + "clkout_2", + "clkout_3", + "clkreq_in_0", + "clkreq_in_1", + "cws_sys_req1", + "cws_sys_req2", + "cws_sys_req3", + "digmic1_clk", + "digmic1_dq", + "digmic2_clk", + "digmic2_dq", + "gpen13", + "gpen14", + "gpen15", + "gpio00", + "gpio01", + "gpio02", + "gpio03", + "gpio04", + "gpio05", + "gpio06", + "gpio07", + "gpio08", + "gpio09", + "gpio10", + "gpio11", + "gpio12", + "gpio13", + "gpio14", + "gps_pablank", + "gps_tmark", + "hdmi_scl", + "hdmi_sda", + "ic_dm", + "ic_dp", + "kp_col_ip_0", + "kp_col_ip_1", + "kp_col_ip_2", + "kp_col_ip_3", + "kp_row_op_0", + "kp_row_op_1", + "kp_row_op_2", + "kp_row_op_3", + "lcd_b_0", + "lcd_b_1", + "lcd_b_2", + "lcd_b_3", + "lcd_b_4", + "lcd_b_5", + "lcd_b_6", + "lcd_b_7", + "lcd_g_0", + "lcd_g_1", + "lcd_g_2", + "lcd_g_3", + "lcd_g_4", + "lcd_g_5", + "lcd_g_6", + "lcd_g_7", + "lcd_hsync", + "lcd_oe", + "lcd_pclk", + "lcd_r_0", + "lcd_r_1", + "lcd_r_2", + "lcd_r_3", + "lcd_r_4", + "lcd_r_5", + "lcd_r_6", + "lcd_r_7", + "lcd_vsync", + "mdmgpio0", + "mdmgpio1", + "mdmgpio2", + "mdmgpio3", + "mdmgpio4", + "mdmgpio5", + "mdmgpio6", + "mdmgpio7", + "mdmgpio8", + "mphi_data_0", + "mphi_data_1", + "mphi_data_2", + "mphi_data_3", + "mphi_data_4", + "mphi_data_5", + "mphi_data_6", + "mphi_data_7", + "mphi_data_8", + "mphi_data_9", + "mphi_data_10", + "mphi_data_11", + "mphi_data_12", + "mphi_data_13", + "mphi_data_14", + "mphi_data_15", + "mphi_ha0", + "mphi_hat0", + "mphi_hat1", + "mphi_hce0_n", + "mphi_hce1_n", + "mphi_hrd_n", + "mphi_hwr_n", + "mphi_run0", + "mphi_run1", + "mtx_scan_clk", + "mtx_scan_data", + "nand_ad_0", + "nand_ad_1", + "nand_ad_2", + "nand_ad_3", + "nand_ad_4", + "nand_ad_5", + "nand_ad_6", + "nand_ad_7", + "nand_ale", + "nand_cen_0", + "nand_cen_1", + "nand_cle", + "nand_oen", + "nand_rdy_0", + "nand_rdy_1", + "nand_wen", + "nand_wp", + "pc1", + "pc2", + "pmu_int", + "pmu_scl", + "pmu_sda", + "rfst2g_mtsloten3g", + "rgmii_0_rx_ctl", + "rgmii_0_rxc", + "rgmii_0_rxd_0", + "rgmii_0_rxd_1", + "rgmii_0_rxd_2", + "rgmii_0_rxd_3", + "rgmii_0_tx_ctl", + "rgmii_0_txc", + "rgmii_0_txd_0", + "rgmii_0_txd_1", + "rgmii_0_txd_2", + "rgmii_0_txd_3", + "rgmii_1_rx_ctl", + "rgmii_1_rxc", + "rgmii_1_rxd_0", + "rgmii_1_rxd_1", + "rgmii_1_rxd_2", + "rgmii_1_rxd_3", + "rgmii_1_tx_ctl", + "rgmii_1_txc", + "rgmii_1_txd_0", + "rgmii_1_txd_1", + "rgmii_1_txd_2", + "rgmii_1_txd_3", + "rgmii_gpio_0", + "rgmii_gpio_1", + "rgmii_gpio_2", + "rgmii_gpio_3", + "rtxdata2g_txdata3g1", + "rtxen2g_txdata3g2", + "rxdata3g0", + "rxdata3g1", + "rxdata3g2", + "sdio1_clk", + "sdio1_cmd", + "sdio1_data_0", + "sdio1_data_1", + "sdio1_data_2", + "sdio1_data_3", + "sdio4_clk", + "sdio4_cmd", + "sdio4_data_0", + "sdio4_data_1", + "sdio4_data_2", + "sdio4_data_3", + "sim_clk", + "sim_data", + "sim_det", + "sim_resetn", + "sim2_clk", + "sim2_data", + "sim2_det", + "sim2_resetn", + "sri_c", + "sri_d", + "sri_e", + "ssp_extclk", + "ssp0_clk", + "ssp0_fs", + "ssp0_rxd", + "ssp0_txd", + "ssp2_clk", + "ssp2_fs_0", + "ssp2_fs_1", + "ssp2_fs_2", + "ssp2_fs_3", + "ssp2_rxd_0", + "ssp2_rxd_1", + "ssp2_txd_0", + "ssp2_txd_1", + "ssp3_clk", + "ssp3_fs", + "ssp3_rxd", + "ssp3_txd", + "ssp4_clk", + "ssp4_fs", + "ssp4_rxd", + "ssp4_txd", + "ssp5_clk", + "ssp5_fs", + "ssp5_rxd", + "ssp5_txd", + "ssp6_clk", + "ssp6_fs", + "ssp6_rxd", + "ssp6_txd", + "stat_1", + "stat_2", + "sysclken", + "traceclk", + "tracedt00", + "tracedt01", + "tracedt02", + "tracedt03", + "tracedt04", + "tracedt05", + "tracedt06", + "tracedt07", + "tracedt08", + "tracedt09", + "tracedt10", + "tracedt11", + "tracedt12", + "tracedt13", + "tracedt14", + "tracedt15", + "txdata3g0", + "txpwrind", + "uartb1_ucts", + "uartb1_urts", + "uartb1_urxd", + "uartb1_utxd", + "uartb2_urxd", + "uartb2_utxd", + "uartb3_ucts", + "uartb3_urts", + "uartb3_urxd", + "uartb3_utxd", + "uartb4_ucts", + "uartb4_urts", + "uartb4_urxd", + "uartb4_utxd", + "vc_cam1_scl", + "vc_cam1_sda", + "vc_cam2_scl", + "vc_cam2_sda", + "vc_cam3_scl", + "vc_cam3_sda", +}; + +/* Every pin can implement all ALT1-ALT4 functions */ +#define BCM281XX_PIN_FUNCTION(fcn_name) \ +{ \ + .name = #fcn_name, \ + .groups = bcm281xx_alt_groups, \ + .ngroups = ARRAY_SIZE(bcm281xx_alt_groups), \ +} + +static const struct bcm281xx_pin_function bcm281xx_functions[] = { + BCM281XX_PIN_FUNCTION(alt1), + BCM281XX_PIN_FUNCTION(alt2), + BCM281XX_PIN_FUNCTION(alt3), + BCM281XX_PIN_FUNCTION(alt4), +}; + +static struct bcm281xx_pinctrl_data bcm281xx_pinctrl = { + .pins = bcm281xx_pinctrl_pins, + .npins = ARRAY_SIZE(bcm281xx_pinctrl_pins), + .functions = bcm281xx_functions, + .nfunctions = ARRAY_SIZE(bcm281xx_functions), +}; + +static inline enum bcm281xx_pin_type pin_type_get(struct pinctrl_dev *pctldev, + unsigned pin) +{ + struct bcm281xx_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); + + if (pin >= pdata->npins) + return BCM281XX_PIN_TYPE_UNKNOWN; + + return *(enum bcm281xx_pin_type *)(pdata->pins[pin].drv_data); +} + +#define BCM281XX_PIN_SHIFT(type, param) \ + (BCM281XX_ ## type ## _PIN_REG_ ## param ## _SHIFT) + +#define BCM281XX_PIN_MASK(type, param) \ + (BCM281XX_ ## type ## _PIN_REG_ ## param ## _MASK) + +/* + * This helper function is used to build up the value and mask used to write to + * a pin register, but does not actually write to the register. + */ +static inline void bcm281xx_pin_update(u32 *reg_val, u32 *reg_mask, + u32 param_val, u32 param_shift, + u32 param_mask) +{ + *reg_val &= ~param_mask; + *reg_val |= (param_val << param_shift) & param_mask; + *reg_mask |= param_mask; +} + +static const struct regmap_config bcm281xx_pinctrl_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = BCM281XX_PIN_VC_CAM3_SDA, +}; + +static int bcm281xx_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct bcm281xx_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); + + return pdata->npins; +} + +static const char *bcm281xx_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned group) +{ + struct bcm281xx_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); + + return pdata->pins[group].name; +} + +static int bcm281xx_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned group, + const unsigned **pins, + unsigned *num_pins) +{ + struct bcm281xx_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); + + *pins = &pdata->pins[group].number; + *num_pins = 1; + + return 0; +} + +static void bcm281xx_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned offset) +{ + seq_printf(s, " %s", dev_name(pctldev->dev)); +} + +static const struct pinctrl_ops bcm281xx_pinctrl_ops = { + .get_groups_count = bcm281xx_pinctrl_get_groups_count, + .get_group_name = bcm281xx_pinctrl_get_group_name, + .get_group_pins = bcm281xx_pinctrl_get_group_pins, + .pin_dbg_show = bcm281xx_pinctrl_pin_dbg_show, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int bcm281xx_pinctrl_get_fcns_count(struct pinctrl_dev *pctldev) +{ + struct bcm281xx_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); + + return pdata->nfunctions; +} + +static const char *bcm281xx_pinctrl_get_fcn_name(struct pinctrl_dev *pctldev, + unsigned function) +{ + struct bcm281xx_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); + + return pdata->functions[function].name; +} + +static int bcm281xx_pinctrl_get_fcn_groups(struct pinctrl_dev *pctldev, + unsigned function, + const char * const **groups, + unsigned * const num_groups) +{ + struct bcm281xx_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); + + *groups = pdata->functions[function].groups; + *num_groups = pdata->functions[function].ngroups; + + return 0; +} + +static int bcm281xx_pinmux_set(struct pinctrl_dev *pctldev, + unsigned function, + unsigned group) +{ + struct bcm281xx_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); + const struct bcm281xx_pin_function *f = &pdata->functions[function]; + u32 offset = 4 * pdata->pins[group].number; + int rc = 0; + + dev_dbg(pctldev->dev, + "%s(): Enable function %s (%d) of pin %s (%d) @offset 0x%x.\n", + __func__, f->name, function, pdata->pins[group].name, + pdata->pins[group].number, offset); + + rc = regmap_update_bits(pdata->regmap, offset, + BCM281XX_PIN_REG_F_SEL_MASK, + function << BCM281XX_PIN_REG_F_SEL_SHIFT); + if (rc) + dev_err(pctldev->dev, + "Error updating register for pin %s (%d).\n", + pdata->pins[group].name, pdata->pins[group].number); + + return rc; +} + +static const struct pinmux_ops bcm281xx_pinctrl_pinmux_ops = { + .get_functions_count = bcm281xx_pinctrl_get_fcns_count, + .get_function_name = bcm281xx_pinctrl_get_fcn_name, + .get_function_groups = bcm281xx_pinctrl_get_fcn_groups, + .set_mux = bcm281xx_pinmux_set, +}; + +static int bcm281xx_pinctrl_pin_config_get(struct pinctrl_dev *pctldev, + unsigned pin, + unsigned long *config) +{ + return -ENOTSUPP; +} + + +/* Goes through the configs and update register val/mask */ +static int bcm281xx_std_pin_update(struct pinctrl_dev *pctldev, + unsigned pin, + unsigned long *configs, + unsigned num_configs, + u32 *val, + u32 *mask) +{ + struct bcm281xx_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); + int i; + enum pin_config_param param; + u32 arg; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + arg = (arg >= 1 ? 1 : 0); + bcm281xx_pin_update(val, mask, arg, + BCM281XX_PIN_SHIFT(STD, HYST), + BCM281XX_PIN_MASK(STD, HYST)); + break; + /* + * The pin bias can only be one of pull-up, pull-down, or + * disable. The user does not need to specify a value for the + * property, and the default value from pinconf-generic is + * ignored. + */ + case PIN_CONFIG_BIAS_DISABLE: + bcm281xx_pin_update(val, mask, 0, + BCM281XX_PIN_SHIFT(STD, PULL_UP), + BCM281XX_PIN_MASK(STD, PULL_UP)); + bcm281xx_pin_update(val, mask, 0, + BCM281XX_PIN_SHIFT(STD, PULL_DN), + BCM281XX_PIN_MASK(STD, PULL_DN)); + break; + + case PIN_CONFIG_BIAS_PULL_UP: + bcm281xx_pin_update(val, mask, 1, + BCM281XX_PIN_SHIFT(STD, PULL_UP), + BCM281XX_PIN_MASK(STD, PULL_UP)); + bcm281xx_pin_update(val, mask, 0, + BCM281XX_PIN_SHIFT(STD, PULL_DN), + BCM281XX_PIN_MASK(STD, PULL_DN)); + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + bcm281xx_pin_update(val, mask, 0, + BCM281XX_PIN_SHIFT(STD, PULL_UP), + BCM281XX_PIN_MASK(STD, PULL_UP)); + bcm281xx_pin_update(val, mask, 1, + BCM281XX_PIN_SHIFT(STD, PULL_DN), + BCM281XX_PIN_MASK(STD, PULL_DN)); + break; + + case PIN_CONFIG_SLEW_RATE: + arg = (arg >= 1 ? 1 : 0); + bcm281xx_pin_update(val, mask, arg, + BCM281XX_PIN_SHIFT(STD, SLEW), + BCM281XX_PIN_MASK(STD, SLEW)); + break; + + case PIN_CONFIG_INPUT_ENABLE: + /* inversed since register is for input _disable_ */ + arg = (arg >= 1 ? 0 : 1); + bcm281xx_pin_update(val, mask, arg, + BCM281XX_PIN_SHIFT(STD, INPUT_DIS), + BCM281XX_PIN_MASK(STD, INPUT_DIS)); + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + /* Valid range is 2-16 mA, even numbers only */ + if ((arg < 2) || (arg > 16) || (arg % 2)) { + dev_err(pctldev->dev, + "Invalid Drive Strength value (%d) for " + "pin %s (%d). Valid values are " + "(2..16) mA, even numbers only.\n", + arg, pdata->pins[pin].name, pin); + return -EINVAL; + } + bcm281xx_pin_update(val, mask, (arg/2)-1, + BCM281XX_PIN_SHIFT(STD, DRV_STR), + BCM281XX_PIN_MASK(STD, DRV_STR)); + break; + + default: + dev_err(pctldev->dev, + "Unrecognized pin config %d for pin %s (%d).\n", + param, pdata->pins[pin].name, pin); + return -EINVAL; + + } /* switch config */ + } /* for each config */ + + return 0; +} + +/* + * The pull-up strength for an I2C pin is represented by bits 4-6 in the + * register with the following mapping: + * 0b000: No pull-up + * 0b001: 1200 Ohm + * 0b010: 1800 Ohm + * 0b011: 720 Ohm + * 0b100: 2700 Ohm + * 0b101: 831 Ohm + * 0b110: 1080 Ohm + * 0b111: 568 Ohm + * This array maps pull-up strength in Ohms to register values (1+index). + */ +static const u16 bcm281xx_pullup_map[] = { + 1200, 1800, 720, 2700, 831, 1080, 568 +}; + +/* Goes through the configs and update register val/mask */ +static int bcm281xx_i2c_pin_update(struct pinctrl_dev *pctldev, + unsigned pin, + unsigned long *configs, + unsigned num_configs, + u32 *val, + u32 *mask) +{ + struct bcm281xx_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); + int i, j; + enum pin_config_param param; + u32 arg; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_PULL_UP: + for (j = 0; j < ARRAY_SIZE(bcm281xx_pullup_map); j++) + if (bcm281xx_pullup_map[j] == arg) + break; + + if (j == ARRAY_SIZE(bcm281xx_pullup_map)) { + dev_err(pctldev->dev, + "Invalid pull-up value (%d) for pin %s " + "(%d). Valid values are 568, 720, 831, " + "1080, 1200, 1800, 2700 Ohms.\n", + arg, pdata->pins[pin].name, pin); + return -EINVAL; + } + + bcm281xx_pin_update(val, mask, j+1, + BCM281XX_PIN_SHIFT(I2C, PULL_UP_STR), + BCM281XX_PIN_MASK(I2C, PULL_UP_STR)); + break; + + case PIN_CONFIG_BIAS_DISABLE: + bcm281xx_pin_update(val, mask, 0, + BCM281XX_PIN_SHIFT(I2C, PULL_UP_STR), + BCM281XX_PIN_MASK(I2C, PULL_UP_STR)); + break; + + case PIN_CONFIG_SLEW_RATE: + arg = (arg >= 1 ? 1 : 0); + bcm281xx_pin_update(val, mask, arg, + BCM281XX_PIN_SHIFT(I2C, SLEW), + BCM281XX_PIN_MASK(I2C, SLEW)); + break; + + case PIN_CONFIG_INPUT_ENABLE: + /* inversed since register is for input _disable_ */ + arg = (arg >= 1 ? 0 : 1); + bcm281xx_pin_update(val, mask, arg, + BCM281XX_PIN_SHIFT(I2C, INPUT_DIS), + BCM281XX_PIN_MASK(I2C, INPUT_DIS)); + break; + + default: + dev_err(pctldev->dev, + "Unrecognized pin config %d for pin %s (%d).\n", + param, pdata->pins[pin].name, pin); + return -EINVAL; + + } /* switch config */ + } /* for each config */ + + return 0; +} + +/* Goes through the configs and update register val/mask */ +static int bcm281xx_hdmi_pin_update(struct pinctrl_dev *pctldev, + unsigned pin, + unsigned long *configs, + unsigned num_configs, + u32 *val, + u32 *mask) +{ + struct bcm281xx_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); + int i; + enum pin_config_param param; + u32 arg; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_SLEW_RATE: + arg = (arg >= 1 ? 1 : 0); + bcm281xx_pin_update(val, mask, arg, + BCM281XX_PIN_SHIFT(HDMI, MODE), + BCM281XX_PIN_MASK(HDMI, MODE)); + break; + + case PIN_CONFIG_INPUT_ENABLE: + /* inversed since register is for input _disable_ */ + arg = (arg >= 1 ? 0 : 1); + bcm281xx_pin_update(val, mask, arg, + BCM281XX_PIN_SHIFT(HDMI, INPUT_DIS), + BCM281XX_PIN_MASK(HDMI, INPUT_DIS)); + break; + + default: + dev_err(pctldev->dev, + "Unrecognized pin config %d for pin %s (%d).\n", + param, pdata->pins[pin].name, pin); + return -EINVAL; + + } /* switch config */ + } /* for each config */ + + return 0; +} + +static int bcm281xx_pinctrl_pin_config_set(struct pinctrl_dev *pctldev, + unsigned pin, + unsigned long *configs, + unsigned num_configs) +{ + struct bcm281xx_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); + enum bcm281xx_pin_type pin_type; + u32 offset = 4 * pin; + u32 cfg_val, cfg_mask; + int rc; + + cfg_val = 0; + cfg_mask = 0; + pin_type = pin_type_get(pctldev, pin); + + /* Different pins have different configuration options */ + switch (pin_type) { + case BCM281XX_PIN_TYPE_STD: + rc = bcm281xx_std_pin_update(pctldev, pin, configs, + num_configs, &cfg_val, &cfg_mask); + break; + + case BCM281XX_PIN_TYPE_I2C: + rc = bcm281xx_i2c_pin_update(pctldev, pin, configs, + num_configs, &cfg_val, &cfg_mask); + break; + + case BCM281XX_PIN_TYPE_HDMI: + rc = bcm281xx_hdmi_pin_update(pctldev, pin, configs, + num_configs, &cfg_val, &cfg_mask); + break; + + default: + dev_err(pctldev->dev, "Unknown pin type for pin %s (%d).\n", + pdata->pins[pin].name, pin); + return -EINVAL; + + } /* switch pin type */ + + if (rc) + return rc; + + dev_dbg(pctldev->dev, + "%s(): Set pin %s (%d) with config 0x%x, mask 0x%x\n", + __func__, pdata->pins[pin].name, pin, cfg_val, cfg_mask); + + rc = regmap_update_bits(pdata->regmap, offset, cfg_mask, cfg_val); + if (rc) { + dev_err(pctldev->dev, + "Error updating register for pin %s (%d).\n", + pdata->pins[pin].name, pin); + return rc; + } + + return 0; +} + +static const struct pinconf_ops bcm281xx_pinctrl_pinconf_ops = { + .pin_config_get = bcm281xx_pinctrl_pin_config_get, + .pin_config_set = bcm281xx_pinctrl_pin_config_set, +}; + +static struct pinctrl_desc bcm281xx_pinctrl_desc = { + /* name, pins, npins members initialized in probe function */ + .pctlops = &bcm281xx_pinctrl_ops, + .pmxops = &bcm281xx_pinctrl_pinmux_ops, + .confops = &bcm281xx_pinctrl_pinconf_ops, + .owner = THIS_MODULE, +}; + +static int __init bcm281xx_pinctrl_probe(struct platform_device *pdev) +{ + struct bcm281xx_pinctrl_data *pdata = &bcm281xx_pinctrl; + struct resource *res; + struct pinctrl_dev *pctl; + + /* So far We can assume there is only 1 bank of registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pdata->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pdata->reg_base)) { + dev_err(&pdev->dev, "Failed to ioremap MEM resource\n"); + return -ENODEV; + } + + /* Initialize the dynamic part of pinctrl_desc */ + pdata->regmap = devm_regmap_init_mmio(&pdev->dev, pdata->reg_base, + &bcm281xx_pinctrl_regmap_config); + if (IS_ERR(pdata->regmap)) { + dev_err(&pdev->dev, "Regmap MMIO init failed.\n"); + return -ENODEV; + } + + bcm281xx_pinctrl_desc.name = dev_name(&pdev->dev); + bcm281xx_pinctrl_desc.pins = bcm281xx_pinctrl.pins; + bcm281xx_pinctrl_desc.npins = bcm281xx_pinctrl.npins; + + pctl = devm_pinctrl_register(&pdev->dev, &bcm281xx_pinctrl_desc, pdata); + if (IS_ERR(pctl)) { + dev_err(&pdev->dev, "Failed to register pinctrl\n"); + return PTR_ERR(pctl); + } + + platform_set_drvdata(pdev, pdata); + + return 0; +} + +static const struct of_device_id bcm281xx_pinctrl_of_match[] = { + { .compatible = "brcm,bcm11351-pinctrl", }, + { }, +}; + +static struct platform_driver bcm281xx_pinctrl_driver = { + .driver = { + .name = "bcm281xx-pinctrl", + .of_match_table = bcm281xx_pinctrl_of_match, + }, +}; +builtin_platform_driver_probe(bcm281xx_pinctrl_driver, bcm281xx_pinctrl_probe); diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c new file mode 100644 index 000000000..1bd3c10ce --- /dev/null +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c @@ -0,0 +1,1110 @@ +/* + * Driver for Broadcom BCM2835 GPIO unit (pinctrl + GPIO) + * + * Copyright (C) 2012 Chris Boot, Simon Arlott, Stephen Warren + * + * This driver is inspired by: + * pinctrl-nomadik.c, please see original file for copyright information + * pinctrl-tegra.c, please see original file for copyright information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#include <linux/bitmap.h> +#include <linux/bug.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdesc.h> +#include <linux/init.h> +#include <linux/of_address.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/pinctrl/consumer.h> +#include <linux/pinctrl/machine.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <dt-bindings/pinctrl/bcm2835.h> + +#define MODULE_NAME "pinctrl-bcm2835" +#define BCM2835_NUM_GPIOS 54 +#define BCM2835_NUM_BANKS 2 +#define BCM2835_NUM_IRQS 3 + +#define BCM2835_PIN_BITMAP_SZ \ + DIV_ROUND_UP(BCM2835_NUM_GPIOS, sizeof(unsigned long) * 8) + +/* GPIO register offsets */ +#define GPFSEL0 0x0 /* Function Select */ +#define GPSET0 0x1c /* Pin Output Set */ +#define GPCLR0 0x28 /* Pin Output Clear */ +#define GPLEV0 0x34 /* Pin Level */ +#define GPEDS0 0x40 /* Pin Event Detect Status */ +#define GPREN0 0x4c /* Pin Rising Edge Detect Enable */ +#define GPFEN0 0x58 /* Pin Falling Edge Detect Enable */ +#define GPHEN0 0x64 /* Pin High Detect Enable */ +#define GPLEN0 0x70 /* Pin Low Detect Enable */ +#define GPAREN0 0x7c /* Pin Async Rising Edge Detect */ +#define GPAFEN0 0x88 /* Pin Async Falling Edge Detect */ +#define GPPUD 0x94 /* Pin Pull-up/down Enable */ +#define GPPUDCLK0 0x98 /* Pin Pull-up/down Enable Clock */ + +#define FSEL_REG(p) (GPFSEL0 + (((p) / 10) * 4)) +#define FSEL_SHIFT(p) (((p) % 10) * 3) +#define GPIO_REG_OFFSET(p) ((p) / 32) +#define GPIO_REG_SHIFT(p) ((p) % 32) + +/* argument: bcm2835_pinconf_pull */ +#define BCM2835_PINCONF_PARAM_PULL (PIN_CONFIG_END + 1) + +struct bcm2835_pinctrl { + struct device *dev; + void __iomem *base; + int irq[BCM2835_NUM_IRQS]; + + /* note: locking assumes each bank will have its own unsigned long */ + unsigned long enabled_irq_map[BCM2835_NUM_BANKS]; + unsigned int irq_type[BCM2835_NUM_GPIOS]; + + struct pinctrl_dev *pctl_dev; + struct gpio_chip gpio_chip; + struct pinctrl_gpio_range gpio_range; + + raw_spinlock_t irq_lock[BCM2835_NUM_BANKS]; +}; + +/* pins are just named GPIO0..GPIO53 */ +#define BCM2835_GPIO_PIN(a) PINCTRL_PIN(a, "gpio" #a) +static struct pinctrl_pin_desc bcm2835_gpio_pins[] = { + BCM2835_GPIO_PIN(0), + BCM2835_GPIO_PIN(1), + BCM2835_GPIO_PIN(2), + BCM2835_GPIO_PIN(3), + BCM2835_GPIO_PIN(4), + BCM2835_GPIO_PIN(5), + BCM2835_GPIO_PIN(6), + BCM2835_GPIO_PIN(7), + BCM2835_GPIO_PIN(8), + BCM2835_GPIO_PIN(9), + BCM2835_GPIO_PIN(10), + BCM2835_GPIO_PIN(11), + BCM2835_GPIO_PIN(12), + BCM2835_GPIO_PIN(13), + BCM2835_GPIO_PIN(14), + BCM2835_GPIO_PIN(15), + BCM2835_GPIO_PIN(16), + BCM2835_GPIO_PIN(17), + BCM2835_GPIO_PIN(18), + BCM2835_GPIO_PIN(19), + BCM2835_GPIO_PIN(20), + BCM2835_GPIO_PIN(21), + BCM2835_GPIO_PIN(22), + BCM2835_GPIO_PIN(23), + BCM2835_GPIO_PIN(24), + BCM2835_GPIO_PIN(25), + BCM2835_GPIO_PIN(26), + BCM2835_GPIO_PIN(27), + BCM2835_GPIO_PIN(28), + BCM2835_GPIO_PIN(29), + BCM2835_GPIO_PIN(30), + BCM2835_GPIO_PIN(31), + BCM2835_GPIO_PIN(32), + BCM2835_GPIO_PIN(33), + BCM2835_GPIO_PIN(34), + BCM2835_GPIO_PIN(35), + BCM2835_GPIO_PIN(36), + BCM2835_GPIO_PIN(37), + BCM2835_GPIO_PIN(38), + BCM2835_GPIO_PIN(39), + BCM2835_GPIO_PIN(40), + BCM2835_GPIO_PIN(41), + BCM2835_GPIO_PIN(42), + BCM2835_GPIO_PIN(43), + BCM2835_GPIO_PIN(44), + BCM2835_GPIO_PIN(45), + BCM2835_GPIO_PIN(46), + BCM2835_GPIO_PIN(47), + BCM2835_GPIO_PIN(48), + BCM2835_GPIO_PIN(49), + BCM2835_GPIO_PIN(50), + BCM2835_GPIO_PIN(51), + BCM2835_GPIO_PIN(52), + BCM2835_GPIO_PIN(53), +}; + +/* one pin per group */ +static const char * const bcm2835_gpio_groups[] = { + "gpio0", + "gpio1", + "gpio2", + "gpio3", + "gpio4", + "gpio5", + "gpio6", + "gpio7", + "gpio8", + "gpio9", + "gpio10", + "gpio11", + "gpio12", + "gpio13", + "gpio14", + "gpio15", + "gpio16", + "gpio17", + "gpio18", + "gpio19", + "gpio20", + "gpio21", + "gpio22", + "gpio23", + "gpio24", + "gpio25", + "gpio26", + "gpio27", + "gpio28", + "gpio29", + "gpio30", + "gpio31", + "gpio32", + "gpio33", + "gpio34", + "gpio35", + "gpio36", + "gpio37", + "gpio38", + "gpio39", + "gpio40", + "gpio41", + "gpio42", + "gpio43", + "gpio44", + "gpio45", + "gpio46", + "gpio47", + "gpio48", + "gpio49", + "gpio50", + "gpio51", + "gpio52", + "gpio53", +}; + +enum bcm2835_fsel { + BCM2835_FSEL_COUNT = 8, + BCM2835_FSEL_MASK = 0x7, +}; + +static const char * const bcm2835_functions[BCM2835_FSEL_COUNT] = { + [BCM2835_FSEL_GPIO_IN] = "gpio_in", + [BCM2835_FSEL_GPIO_OUT] = "gpio_out", + [BCM2835_FSEL_ALT0] = "alt0", + [BCM2835_FSEL_ALT1] = "alt1", + [BCM2835_FSEL_ALT2] = "alt2", + [BCM2835_FSEL_ALT3] = "alt3", + [BCM2835_FSEL_ALT4] = "alt4", + [BCM2835_FSEL_ALT5] = "alt5", +}; + +static const char * const irq_type_names[] = { + [IRQ_TYPE_NONE] = "none", + [IRQ_TYPE_EDGE_RISING] = "edge-rising", + [IRQ_TYPE_EDGE_FALLING] = "edge-falling", + [IRQ_TYPE_EDGE_BOTH] = "edge-both", + [IRQ_TYPE_LEVEL_HIGH] = "level-high", + [IRQ_TYPE_LEVEL_LOW] = "level-low", +}; + +static inline u32 bcm2835_gpio_rd(struct bcm2835_pinctrl *pc, unsigned reg) +{ + return readl(pc->base + reg); +} + +static inline void bcm2835_gpio_wr(struct bcm2835_pinctrl *pc, unsigned reg, + u32 val) +{ + writel(val, pc->base + reg); +} + +static inline int bcm2835_gpio_get_bit(struct bcm2835_pinctrl *pc, unsigned reg, + unsigned bit) +{ + reg += GPIO_REG_OFFSET(bit) * 4; + return (bcm2835_gpio_rd(pc, reg) >> GPIO_REG_SHIFT(bit)) & 1; +} + +/* note NOT a read/modify/write cycle */ +static inline void bcm2835_gpio_set_bit(struct bcm2835_pinctrl *pc, + unsigned reg, unsigned bit) +{ + reg += GPIO_REG_OFFSET(bit) * 4; + bcm2835_gpio_wr(pc, reg, BIT(GPIO_REG_SHIFT(bit))); +} + +static inline enum bcm2835_fsel bcm2835_pinctrl_fsel_get( + struct bcm2835_pinctrl *pc, unsigned pin) +{ + u32 val = bcm2835_gpio_rd(pc, FSEL_REG(pin)); + enum bcm2835_fsel status = (val >> FSEL_SHIFT(pin)) & BCM2835_FSEL_MASK; + + dev_dbg(pc->dev, "get %08x (%u => %s)\n", val, pin, + bcm2835_functions[status]); + + return status; +} + +static inline void bcm2835_pinctrl_fsel_set( + struct bcm2835_pinctrl *pc, unsigned pin, + enum bcm2835_fsel fsel) +{ + u32 val = bcm2835_gpio_rd(pc, FSEL_REG(pin)); + enum bcm2835_fsel cur = (val >> FSEL_SHIFT(pin)) & BCM2835_FSEL_MASK; + + dev_dbg(pc->dev, "read %08x (%u => %s)\n", val, pin, + bcm2835_functions[cur]); + + if (cur == fsel) + return; + + if (cur != BCM2835_FSEL_GPIO_IN && fsel != BCM2835_FSEL_GPIO_IN) { + /* always transition through GPIO_IN */ + val &= ~(BCM2835_FSEL_MASK << FSEL_SHIFT(pin)); + val |= BCM2835_FSEL_GPIO_IN << FSEL_SHIFT(pin); + + dev_dbg(pc->dev, "trans %08x (%u <= %s)\n", val, pin, + bcm2835_functions[BCM2835_FSEL_GPIO_IN]); + bcm2835_gpio_wr(pc, FSEL_REG(pin), val); + } + + val &= ~(BCM2835_FSEL_MASK << FSEL_SHIFT(pin)); + val |= fsel << FSEL_SHIFT(pin); + + dev_dbg(pc->dev, "write %08x (%u <= %s)\n", val, pin, + bcm2835_functions[fsel]); + bcm2835_gpio_wr(pc, FSEL_REG(pin), val); +} + +static int bcm2835_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_gpio_direction_input(chip->base + offset); +} + +static int bcm2835_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct bcm2835_pinctrl *pc = gpiochip_get_data(chip); + + return bcm2835_gpio_get_bit(pc, GPLEV0, offset); +} + +static int bcm2835_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct bcm2835_pinctrl *pc = gpiochip_get_data(chip); + enum bcm2835_fsel fsel = bcm2835_pinctrl_fsel_get(pc, offset); + + /* Alternative function doesn't clearly provide a direction */ + if (fsel > BCM2835_FSEL_GPIO_OUT) + return -EINVAL; + + return (fsel == BCM2835_FSEL_GPIO_IN); +} + +static void bcm2835_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct bcm2835_pinctrl *pc = gpiochip_get_data(chip); + + bcm2835_gpio_set_bit(pc, value ? GPSET0 : GPCLR0, offset); +} + +static int bcm2835_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + bcm2835_gpio_set(chip, offset, value); + return pinctrl_gpio_direction_output(chip->base + offset); +} + +static const struct gpio_chip bcm2835_gpio_chip = { + .label = MODULE_NAME, + .owner = THIS_MODULE, + .request = gpiochip_generic_request, + .free = gpiochip_generic_free, + .direction_input = bcm2835_gpio_direction_input, + .direction_output = bcm2835_gpio_direction_output, + .get_direction = bcm2835_gpio_get_direction, + .get = bcm2835_gpio_get, + .set = bcm2835_gpio_set, + .base = -1, + .ngpio = BCM2835_NUM_GPIOS, + .can_sleep = false, +}; + +static void bcm2835_gpio_irq_handle_bank(struct bcm2835_pinctrl *pc, + unsigned int bank, u32 mask) +{ + unsigned long events; + unsigned offset; + unsigned gpio; + + events = bcm2835_gpio_rd(pc, GPEDS0 + bank * 4); + events &= mask; + events &= pc->enabled_irq_map[bank]; + for_each_set_bit(offset, &events, 32) { + gpio = (32 * bank) + offset; + generic_handle_irq(irq_linear_revmap(pc->gpio_chip.irq.domain, + gpio)); + } +} + +static void bcm2835_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *chip = irq_desc_get_handler_data(desc); + struct bcm2835_pinctrl *pc = gpiochip_get_data(chip); + struct irq_chip *host_chip = irq_desc_get_chip(desc); + int irq = irq_desc_get_irq(desc); + int group; + int i; + + for (i = 0; i < ARRAY_SIZE(pc->irq); i++) { + if (pc->irq[i] == irq) { + group = i; + break; + } + } + /* This should not happen, every IRQ has a bank */ + if (i == ARRAY_SIZE(pc->irq)) + BUG(); + + chained_irq_enter(host_chip, desc); + + switch (group) { + case 0: /* IRQ0 covers GPIOs 0-27 */ + bcm2835_gpio_irq_handle_bank(pc, 0, 0x0fffffff); + break; + case 1: /* IRQ1 covers GPIOs 28-45 */ + bcm2835_gpio_irq_handle_bank(pc, 0, 0xf0000000); + bcm2835_gpio_irq_handle_bank(pc, 1, 0x00003fff); + break; + case 2: /* IRQ2 covers GPIOs 46-53 */ + bcm2835_gpio_irq_handle_bank(pc, 1, 0x003fc000); + break; + } + + chained_irq_exit(host_chip, desc); +} + +static inline void __bcm2835_gpio_irq_config(struct bcm2835_pinctrl *pc, + unsigned reg, unsigned offset, bool enable) +{ + u32 value; + reg += GPIO_REG_OFFSET(offset) * 4; + value = bcm2835_gpio_rd(pc, reg); + if (enable) + value |= BIT(GPIO_REG_SHIFT(offset)); + else + value &= ~(BIT(GPIO_REG_SHIFT(offset))); + bcm2835_gpio_wr(pc, reg, value); +} + +/* fast path for IRQ handler */ +static void bcm2835_gpio_irq_config(struct bcm2835_pinctrl *pc, + unsigned offset, bool enable) +{ + switch (pc->irq_type[offset]) { + case IRQ_TYPE_EDGE_RISING: + __bcm2835_gpio_irq_config(pc, GPREN0, offset, enable); + break; + + case IRQ_TYPE_EDGE_FALLING: + __bcm2835_gpio_irq_config(pc, GPFEN0, offset, enable); + break; + + case IRQ_TYPE_EDGE_BOTH: + __bcm2835_gpio_irq_config(pc, GPREN0, offset, enable); + __bcm2835_gpio_irq_config(pc, GPFEN0, offset, enable); + break; + + case IRQ_TYPE_LEVEL_HIGH: + __bcm2835_gpio_irq_config(pc, GPHEN0, offset, enable); + break; + + case IRQ_TYPE_LEVEL_LOW: + __bcm2835_gpio_irq_config(pc, GPLEN0, offset, enable); + break; + } +} + +static void bcm2835_gpio_irq_enable(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct bcm2835_pinctrl *pc = gpiochip_get_data(chip); + unsigned gpio = irqd_to_hwirq(data); + unsigned offset = GPIO_REG_SHIFT(gpio); + unsigned bank = GPIO_REG_OFFSET(gpio); + unsigned long flags; + + raw_spin_lock_irqsave(&pc->irq_lock[bank], flags); + set_bit(offset, &pc->enabled_irq_map[bank]); + bcm2835_gpio_irq_config(pc, gpio, true); + raw_spin_unlock_irqrestore(&pc->irq_lock[bank], flags); +} + +static void bcm2835_gpio_irq_disable(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct bcm2835_pinctrl *pc = gpiochip_get_data(chip); + unsigned gpio = irqd_to_hwirq(data); + unsigned offset = GPIO_REG_SHIFT(gpio); + unsigned bank = GPIO_REG_OFFSET(gpio); + unsigned long flags; + + raw_spin_lock_irqsave(&pc->irq_lock[bank], flags); + bcm2835_gpio_irq_config(pc, gpio, false); + /* Clear events that were latched prior to clearing event sources */ + bcm2835_gpio_set_bit(pc, GPEDS0, gpio); + clear_bit(offset, &pc->enabled_irq_map[bank]); + raw_spin_unlock_irqrestore(&pc->irq_lock[bank], flags); +} + +static int __bcm2835_gpio_irq_set_type_disabled(struct bcm2835_pinctrl *pc, + unsigned offset, unsigned int type) +{ + switch (type) { + case IRQ_TYPE_NONE: + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + case IRQ_TYPE_LEVEL_HIGH: + case IRQ_TYPE_LEVEL_LOW: + pc->irq_type[offset] = type; + break; + + default: + return -EINVAL; + } + return 0; +} + +/* slower path for reconfiguring IRQ type */ +static int __bcm2835_gpio_irq_set_type_enabled(struct bcm2835_pinctrl *pc, + unsigned offset, unsigned int type) +{ + switch (type) { + case IRQ_TYPE_NONE: + if (pc->irq_type[offset] != type) { + bcm2835_gpio_irq_config(pc, offset, false); + pc->irq_type[offset] = type; + } + break; + + case IRQ_TYPE_EDGE_RISING: + if (pc->irq_type[offset] == IRQ_TYPE_EDGE_BOTH) { + /* RISING already enabled, disable FALLING */ + pc->irq_type[offset] = IRQ_TYPE_EDGE_FALLING; + bcm2835_gpio_irq_config(pc, offset, false); + pc->irq_type[offset] = type; + } else if (pc->irq_type[offset] != type) { + bcm2835_gpio_irq_config(pc, offset, false); + pc->irq_type[offset] = type; + bcm2835_gpio_irq_config(pc, offset, true); + } + break; + + case IRQ_TYPE_EDGE_FALLING: + if (pc->irq_type[offset] == IRQ_TYPE_EDGE_BOTH) { + /* FALLING already enabled, disable RISING */ + pc->irq_type[offset] = IRQ_TYPE_EDGE_RISING; + bcm2835_gpio_irq_config(pc, offset, false); + pc->irq_type[offset] = type; + } else if (pc->irq_type[offset] != type) { + bcm2835_gpio_irq_config(pc, offset, false); + pc->irq_type[offset] = type; + bcm2835_gpio_irq_config(pc, offset, true); + } + break; + + case IRQ_TYPE_EDGE_BOTH: + if (pc->irq_type[offset] == IRQ_TYPE_EDGE_RISING) { + /* RISING already enabled, enable FALLING too */ + pc->irq_type[offset] = IRQ_TYPE_EDGE_FALLING; + bcm2835_gpio_irq_config(pc, offset, true); + pc->irq_type[offset] = type; + } else if (pc->irq_type[offset] == IRQ_TYPE_EDGE_FALLING) { + /* FALLING already enabled, enable RISING too */ + pc->irq_type[offset] = IRQ_TYPE_EDGE_RISING; + bcm2835_gpio_irq_config(pc, offset, true); + pc->irq_type[offset] = type; + } else if (pc->irq_type[offset] != type) { + bcm2835_gpio_irq_config(pc, offset, false); + pc->irq_type[offset] = type; + bcm2835_gpio_irq_config(pc, offset, true); + } + break; + + case IRQ_TYPE_LEVEL_HIGH: + case IRQ_TYPE_LEVEL_LOW: + if (pc->irq_type[offset] != type) { + bcm2835_gpio_irq_config(pc, offset, false); + pc->irq_type[offset] = type; + bcm2835_gpio_irq_config(pc, offset, true); + } + break; + + default: + return -EINVAL; + } + return 0; +} + +static int bcm2835_gpio_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct bcm2835_pinctrl *pc = gpiochip_get_data(chip); + unsigned gpio = irqd_to_hwirq(data); + unsigned offset = GPIO_REG_SHIFT(gpio); + unsigned bank = GPIO_REG_OFFSET(gpio); + unsigned long flags; + int ret; + + raw_spin_lock_irqsave(&pc->irq_lock[bank], flags); + + if (test_bit(offset, &pc->enabled_irq_map[bank])) + ret = __bcm2835_gpio_irq_set_type_enabled(pc, gpio, type); + else + ret = __bcm2835_gpio_irq_set_type_disabled(pc, gpio, type); + + if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(data, handle_edge_irq); + else + irq_set_handler_locked(data, handle_level_irq); + + raw_spin_unlock_irqrestore(&pc->irq_lock[bank], flags); + + return ret; +} + +static void bcm2835_gpio_irq_ack(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct bcm2835_pinctrl *pc = gpiochip_get_data(chip); + unsigned gpio = irqd_to_hwirq(data); + + bcm2835_gpio_set_bit(pc, GPEDS0, gpio); +} + +static struct irq_chip bcm2835_gpio_irq_chip = { + .name = MODULE_NAME, + .irq_enable = bcm2835_gpio_irq_enable, + .irq_disable = bcm2835_gpio_irq_disable, + .irq_set_type = bcm2835_gpio_irq_set_type, + .irq_ack = bcm2835_gpio_irq_ack, + .irq_mask = bcm2835_gpio_irq_disable, + .irq_unmask = bcm2835_gpio_irq_enable, +}; + +static int bcm2835_pctl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(bcm2835_gpio_groups); +} + +static const char *bcm2835_pctl_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return bcm2835_gpio_groups[selector]; +} + +static int bcm2835_pctl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned selector, + const unsigned **pins, + unsigned *num_pins) +{ + *pins = &bcm2835_gpio_pins[selector].number; + *num_pins = 1; + + return 0; +} + +static void bcm2835_pctl_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned offset) +{ + struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + struct gpio_chip *chip = &pc->gpio_chip; + enum bcm2835_fsel fsel = bcm2835_pinctrl_fsel_get(pc, offset); + const char *fname = bcm2835_functions[fsel]; + int value = bcm2835_gpio_get_bit(pc, GPLEV0, offset); + int irq = irq_find_mapping(chip->irq.domain, offset); + + seq_printf(s, "function %s in %s; irq %d (%s)", + fname, value ? "hi" : "lo", + irq, irq_type_names[pc->irq_type[offset]]); +} + +static void bcm2835_pctl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *maps, unsigned num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (maps[i].type == PIN_MAP_TYPE_CONFIGS_PIN) + kfree(maps[i].data.configs.configs); + + kfree(maps); +} + +static int bcm2835_pctl_dt_node_to_map_func(struct bcm2835_pinctrl *pc, + struct device_node *np, u32 pin, u32 fnum, + struct pinctrl_map **maps) +{ + struct pinctrl_map *map = *maps; + + if (fnum >= ARRAY_SIZE(bcm2835_functions)) { + dev_err(pc->dev, "%pOF: invalid brcm,function %d\n", np, fnum); + return -EINVAL; + } + + map->type = PIN_MAP_TYPE_MUX_GROUP; + map->data.mux.group = bcm2835_gpio_groups[pin]; + map->data.mux.function = bcm2835_functions[fnum]; + (*maps)++; + + return 0; +} + +static int bcm2835_pctl_dt_node_to_map_pull(struct bcm2835_pinctrl *pc, + struct device_node *np, u32 pin, u32 pull, + struct pinctrl_map **maps) +{ + struct pinctrl_map *map = *maps; + unsigned long *configs; + + if (pull > 2) { + dev_err(pc->dev, "%pOF: invalid brcm,pull %d\n", np, pull); + return -EINVAL; + } + + configs = kzalloc(sizeof(*configs), GFP_KERNEL); + if (!configs) + return -ENOMEM; + configs[0] = pinconf_to_config_packed(BCM2835_PINCONF_PARAM_PULL, pull); + + map->type = PIN_MAP_TYPE_CONFIGS_PIN; + map->data.configs.group_or_pin = bcm2835_gpio_pins[pin].name; + map->data.configs.configs = configs; + map->data.configs.num_configs = 1; + (*maps)++; + + return 0; +} + +static int bcm2835_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, unsigned int *num_maps) +{ + struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + struct property *pins, *funcs, *pulls; + int num_pins, num_funcs, num_pulls, maps_per_pin; + struct pinctrl_map *maps, *cur_map; + int i, err; + u32 pin, func, pull; + + /* Check for generic binding in this node */ + err = pinconf_generic_dt_node_to_map_all(pctldev, np, map, num_maps); + if (err || *num_maps) + return err; + + /* Generic binding did not find anything continue with legacy parse */ + pins = of_find_property(np, "brcm,pins", NULL); + if (!pins) { + dev_err(pc->dev, "%pOF: missing brcm,pins property\n", np); + return -EINVAL; + } + + funcs = of_find_property(np, "brcm,function", NULL); + pulls = of_find_property(np, "brcm,pull", NULL); + + if (!funcs && !pulls) { + dev_err(pc->dev, + "%pOF: neither brcm,function nor brcm,pull specified\n", + np); + return -EINVAL; + } + + num_pins = pins->length / 4; + num_funcs = funcs ? (funcs->length / 4) : 0; + num_pulls = pulls ? (pulls->length / 4) : 0; + + if (num_funcs > 1 && num_funcs != num_pins) { + dev_err(pc->dev, + "%pOF: brcm,function must have 1 or %d entries\n", + np, num_pins); + return -EINVAL; + } + + if (num_pulls > 1 && num_pulls != num_pins) { + dev_err(pc->dev, + "%pOF: brcm,pull must have 1 or %d entries\n", + np, num_pins); + return -EINVAL; + } + + maps_per_pin = 0; + if (num_funcs) + maps_per_pin++; + if (num_pulls) + maps_per_pin++; + cur_map = maps = kcalloc(num_pins * maps_per_pin, sizeof(*maps), + GFP_KERNEL); + if (!maps) + return -ENOMEM; + + for (i = 0; i < num_pins; i++) { + err = of_property_read_u32_index(np, "brcm,pins", i, &pin); + if (err) + goto out; + if (pin >= ARRAY_SIZE(bcm2835_gpio_pins)) { + dev_err(pc->dev, "%pOF: invalid brcm,pins value %d\n", + np, pin); + err = -EINVAL; + goto out; + } + + if (num_funcs) { + err = of_property_read_u32_index(np, "brcm,function", + (num_funcs > 1) ? i : 0, &func); + if (err) + goto out; + err = bcm2835_pctl_dt_node_to_map_func(pc, np, pin, + func, &cur_map); + if (err) + goto out; + } + if (num_pulls) { + err = of_property_read_u32_index(np, "brcm,pull", + (num_pulls > 1) ? i : 0, &pull); + if (err) + goto out; + err = bcm2835_pctl_dt_node_to_map_pull(pc, np, pin, + pull, &cur_map); + if (err) + goto out; + } + } + + *map = maps; + *num_maps = num_pins * maps_per_pin; + + return 0; + +out: + bcm2835_pctl_dt_free_map(pctldev, maps, num_pins * maps_per_pin); + return err; +} + +static const struct pinctrl_ops bcm2835_pctl_ops = { + .get_groups_count = bcm2835_pctl_get_groups_count, + .get_group_name = bcm2835_pctl_get_group_name, + .get_group_pins = bcm2835_pctl_get_group_pins, + .pin_dbg_show = bcm2835_pctl_pin_dbg_show, + .dt_node_to_map = bcm2835_pctl_dt_node_to_map, + .dt_free_map = bcm2835_pctl_dt_free_map, +}; + +static int bcm2835_pmx_free(struct pinctrl_dev *pctldev, + unsigned offset) +{ + struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + + /* disable by setting to GPIO_IN */ + bcm2835_pinctrl_fsel_set(pc, offset, BCM2835_FSEL_GPIO_IN); + return 0; +} + +static int bcm2835_pmx_get_functions_count(struct pinctrl_dev *pctldev) +{ + return BCM2835_FSEL_COUNT; +} + +static const char *bcm2835_pmx_get_function_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return bcm2835_functions[selector]; +} + +static int bcm2835_pmx_get_function_groups(struct pinctrl_dev *pctldev, + unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + /* every pin can do every function */ + *groups = bcm2835_gpio_groups; + *num_groups = ARRAY_SIZE(bcm2835_gpio_groups); + + return 0; +} + +static int bcm2835_pmx_set(struct pinctrl_dev *pctldev, + unsigned func_selector, + unsigned group_selector) +{ + struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + + bcm2835_pinctrl_fsel_set(pc, group_selector, func_selector); + + return 0; +} + +static void bcm2835_pmx_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + + /* disable by setting to GPIO_IN */ + bcm2835_pinctrl_fsel_set(pc, offset, BCM2835_FSEL_GPIO_IN); +} + +static int bcm2835_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset, + bool input) +{ + struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + enum bcm2835_fsel fsel = input ? + BCM2835_FSEL_GPIO_IN : BCM2835_FSEL_GPIO_OUT; + + bcm2835_pinctrl_fsel_set(pc, offset, fsel); + + return 0; +} + +static const struct pinmux_ops bcm2835_pmx_ops = { + .free = bcm2835_pmx_free, + .get_functions_count = bcm2835_pmx_get_functions_count, + .get_function_name = bcm2835_pmx_get_function_name, + .get_function_groups = bcm2835_pmx_get_function_groups, + .set_mux = bcm2835_pmx_set, + .gpio_disable_free = bcm2835_pmx_gpio_disable_free, + .gpio_set_direction = bcm2835_pmx_gpio_set_direction, +}; + +static int bcm2835_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin, unsigned long *config) +{ + /* No way to read back config in HW */ + return -ENOTSUPP; +} + +static void bcm2835_pull_config_set(struct bcm2835_pinctrl *pc, + unsigned int pin, unsigned int arg) +{ + u32 off, bit; + + off = GPIO_REG_OFFSET(pin); + bit = GPIO_REG_SHIFT(pin); + + bcm2835_gpio_wr(pc, GPPUD, arg & 3); + /* + * BCM2835 datasheet say to wait 150 cycles, but not of what. + * But the VideoCore firmware delay for this operation + * based nearly on the same amount of VPU cycles and this clock + * runs at 250 MHz. + */ + udelay(1); + bcm2835_gpio_wr(pc, GPPUDCLK0 + (off * 4), BIT(bit)); + udelay(1); + bcm2835_gpio_wr(pc, GPPUDCLK0 + (off * 4), 0); +} + +static int bcm2835_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *configs, + unsigned int num_configs) +{ + struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); + u32 param, arg; + int i; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + /* Set legacy brcm,pull */ + case BCM2835_PINCONF_PARAM_PULL: + bcm2835_pull_config_set(pc, pin, arg); + break; + + /* Set pull generic bindings */ + case PIN_CONFIG_BIAS_DISABLE: + bcm2835_pull_config_set(pc, pin, BCM2835_PUD_OFF); + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + bcm2835_pull_config_set(pc, pin, BCM2835_PUD_DOWN); + break; + + case PIN_CONFIG_BIAS_PULL_UP: + bcm2835_pull_config_set(pc, pin, BCM2835_PUD_UP); + break; + + /* Set output-high or output-low */ + case PIN_CONFIG_OUTPUT: + bcm2835_gpio_set_bit(pc, arg ? GPSET0 : GPCLR0, pin); + break; + + default: + return -EINVAL; + + } /* switch param type */ + } /* for each config */ + + return 0; +} + +static const struct pinconf_ops bcm2835_pinconf_ops = { + .pin_config_get = bcm2835_pinconf_get, + .pin_config_set = bcm2835_pinconf_set, +}; + +static struct pinctrl_desc bcm2835_pinctrl_desc = { + .name = MODULE_NAME, + .pins = bcm2835_gpio_pins, + .npins = ARRAY_SIZE(bcm2835_gpio_pins), + .pctlops = &bcm2835_pctl_ops, + .pmxops = &bcm2835_pmx_ops, + .confops = &bcm2835_pinconf_ops, + .owner = THIS_MODULE, +}; + +static struct pinctrl_gpio_range bcm2835_pinctrl_gpio_range = { + .name = MODULE_NAME, + .npins = BCM2835_NUM_GPIOS, +}; + +static int bcm2835_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct bcm2835_pinctrl *pc; + struct resource iomem; + int err, i; + BUILD_BUG_ON(ARRAY_SIZE(bcm2835_gpio_pins) != BCM2835_NUM_GPIOS); + BUILD_BUG_ON(ARRAY_SIZE(bcm2835_gpio_groups) != BCM2835_NUM_GPIOS); + + pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL); + if (!pc) + return -ENOMEM; + + platform_set_drvdata(pdev, pc); + pc->dev = dev; + + err = of_address_to_resource(np, 0, &iomem); + if (err) { + dev_err(dev, "could not get IO memory\n"); + return err; + } + + pc->base = devm_ioremap_resource(dev, &iomem); + if (IS_ERR(pc->base)) + return PTR_ERR(pc->base); + + pc->gpio_chip = bcm2835_gpio_chip; + pc->gpio_chip.parent = dev; + pc->gpio_chip.of_node = np; + + for (i = 0; i < BCM2835_NUM_BANKS; i++) { + unsigned long events; + unsigned offset; + + /* clear event detection flags */ + bcm2835_gpio_wr(pc, GPREN0 + i * 4, 0); + bcm2835_gpio_wr(pc, GPFEN0 + i * 4, 0); + bcm2835_gpio_wr(pc, GPHEN0 + i * 4, 0); + bcm2835_gpio_wr(pc, GPLEN0 + i * 4, 0); + bcm2835_gpio_wr(pc, GPAREN0 + i * 4, 0); + bcm2835_gpio_wr(pc, GPAFEN0 + i * 4, 0); + + /* clear all the events */ + events = bcm2835_gpio_rd(pc, GPEDS0 + i * 4); + for_each_set_bit(offset, &events, 32) + bcm2835_gpio_wr(pc, GPEDS0 + i * 4, BIT(offset)); + + raw_spin_lock_init(&pc->irq_lock[i]); + } + + err = gpiochip_add_data(&pc->gpio_chip, pc); + if (err) { + dev_err(dev, "could not add GPIO chip\n"); + return err; + } + + err = gpiochip_irqchip_add(&pc->gpio_chip, &bcm2835_gpio_irq_chip, + 0, handle_level_irq, IRQ_TYPE_NONE); + if (err) { + dev_info(dev, "could not add irqchip\n"); + return err; + } + + for (i = 0; i < BCM2835_NUM_IRQS; i++) { + pc->irq[i] = irq_of_parse_and_map(np, i); + + if (pc->irq[i] == 0) + continue; + + /* + * Use the same handler for all groups: this is necessary + * since we use one gpiochip to cover all lines - the + * irq handler then needs to figure out which group and + * bank that was firing the IRQ and look up the per-group + * and bank data. + */ + gpiochip_set_chained_irqchip(&pc->gpio_chip, + &bcm2835_gpio_irq_chip, + pc->irq[i], + bcm2835_gpio_irq_handler); + } + + pc->pctl_dev = devm_pinctrl_register(dev, &bcm2835_pinctrl_desc, pc); + if (IS_ERR(pc->pctl_dev)) { + gpiochip_remove(&pc->gpio_chip); + return PTR_ERR(pc->pctl_dev); + } + + pc->gpio_range = bcm2835_pinctrl_gpio_range; + pc->gpio_range.base = pc->gpio_chip.base; + pc->gpio_range.gc = &pc->gpio_chip; + pinctrl_add_gpio_range(pc->pctl_dev, &pc->gpio_range); + + return 0; +} + +static const struct of_device_id bcm2835_pinctrl_match[] = { + { .compatible = "brcm,bcm2835-gpio" }, + {} +}; + +static struct platform_driver bcm2835_pinctrl_driver = { + .probe = bcm2835_pinctrl_probe, + .driver = { + .name = MODULE_NAME, + .of_match_table = bcm2835_pinctrl_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(bcm2835_pinctrl_driver); diff --git a/drivers/pinctrl/bcm/pinctrl-cygnus-mux.c b/drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file mode 100644 index 000000000..44df35942 --- /dev/null +++ b/drivers/pinctrl/bcm/pinctrl-cygnus-mux.c @@ -0,0 +1,1022 @@ +/* + * Copyright (C) 2014-2017 Broadcom + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * Broadcom Cygnus IOMUX driver + * + * This file contains the Cygnus IOMUX driver that supports group based PINMUX + * configuration. Although PINMUX configuration is mainly group based, the + * Cygnus IOMUX controller allows certain pins to be individually muxed to GPIO + * function, and therefore be controlled by the Cygnus ASIU GPIO controller + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include "../core.h" +#include "../pinctrl-utils.h" + +#define CYGNUS_NUM_IOMUX_REGS 8 +#define CYGNUS_NUM_MUX_PER_REG 8 +#define CYGNUS_NUM_IOMUX (CYGNUS_NUM_IOMUX_REGS * \ + CYGNUS_NUM_MUX_PER_REG) + +/* + * Cygnus IOMUX register description + * + * @offset: register offset for mux configuration of a group + * @shift: bit shift for mux configuration of a group + * @alt: alternate function to set to + */ +struct cygnus_mux { + unsigned int offset; + unsigned int shift; + unsigned int alt; +}; + +/* + * Keep track of Cygnus IOMUX configuration and prevent double configuration + * + * @cygnus_mux: Cygnus IOMUX register description + * @is_configured: flag to indicate whether a mux setting has already been + * configured + */ +struct cygnus_mux_log { + struct cygnus_mux mux; + bool is_configured; +}; + +/* + * Group based IOMUX configuration + * + * @name: name of the group + * @pins: array of pins used by this group + * @num_pins: total number of pins used by this group + * @mux: Cygnus group based IOMUX configuration + */ +struct cygnus_pin_group { + const char *name; + const unsigned *pins; + unsigned num_pins; + struct cygnus_mux mux; +}; + +/* + * Cygnus mux function and supported pin groups + * + * @name: name of the function + * @groups: array of groups that can be supported by this function + * @num_groups: total number of groups that can be supported by this function + */ +struct cygnus_pin_function { + const char *name; + const char * const *groups; + unsigned num_groups; +}; + +/* + * Cygnus IOMUX pinctrl core + * + * @pctl: pointer to pinctrl_dev + * @dev: pointer to device + * @base0: first I/O register base of the Cygnus IOMUX controller + * @base1: second I/O register base + * @groups: pointer to array of groups + * @num_groups: total number of groups + * @functions: pointer to array of functions + * @num_functions: total number of functions + * @mux_log: pointer to the array of mux logs + * @lock: lock to protect register access + */ +struct cygnus_pinctrl { + struct pinctrl_dev *pctl; + struct device *dev; + void __iomem *base0; + void __iomem *base1; + + const struct cygnus_pin_group *groups; + unsigned num_groups; + + const struct cygnus_pin_function *functions; + unsigned num_functions; + + struct cygnus_mux_log *mux_log; + + spinlock_t lock; +}; + +/* + * Certain pins can be individually muxed to GPIO function + * + * @is_supported: flag to indicate GPIO mux is supported for this pin + * @offset: register offset for GPIO mux override of a pin + * @shift: bit shift for GPIO mux override of a pin + */ +struct cygnus_gpio_mux { + int is_supported; + unsigned int offset; + unsigned int shift; +}; + +/* + * Description of a pin in Cygnus + * + * @pin: pin number + * @name: pin name + * @gpio_mux: GPIO override related information + */ +struct cygnus_pin { + unsigned pin; + char *name; + struct cygnus_gpio_mux gpio_mux; +}; + +#define CYGNUS_PIN_DESC(p, n, i, o, s) \ +{ \ + .pin = p, \ + .name = n, \ + .gpio_mux = { \ + .is_supported = i, \ + .offset = o, \ + .shift = s, \ + }, \ +} + +/* + * List of pins in Cygnus + */ +static struct cygnus_pin cygnus_pins[] = { + CYGNUS_PIN_DESC(0, "ext_device_reset_n", 0, 0, 0), + CYGNUS_PIN_DESC(1, "chip_mode0", 0, 0, 0), + CYGNUS_PIN_DESC(2, "chip_mode1", 0, 0, 0), + CYGNUS_PIN_DESC(3, "chip_mode2", 0, 0, 0), + CYGNUS_PIN_DESC(4, "chip_mode3", 0, 0, 0), + CYGNUS_PIN_DESC(5, "chip_mode4", 0, 0, 0), + CYGNUS_PIN_DESC(6, "bsc0_scl", 0, 0, 0), + CYGNUS_PIN_DESC(7, "bsc0_sda", 0, 0, 0), + CYGNUS_PIN_DESC(8, "bsc1_scl", 0, 0, 0), + CYGNUS_PIN_DESC(9, "bsc1_sda", 0, 0, 0), + CYGNUS_PIN_DESC(10, "d1w_dq", 1, 0x28, 0), + CYGNUS_PIN_DESC(11, "d1wowstz_l", 1, 0x4, 28), + CYGNUS_PIN_DESC(12, "gpio0", 0, 0, 0), + CYGNUS_PIN_DESC(13, "gpio1", 0, 0, 0), + CYGNUS_PIN_DESC(14, "gpio2", 0, 0, 0), + CYGNUS_PIN_DESC(15, "gpio3", 0, 0, 0), + CYGNUS_PIN_DESC(16, "gpio4", 0, 0, 0), + CYGNUS_PIN_DESC(17, "gpio5", 0, 0, 0), + CYGNUS_PIN_DESC(18, "gpio6", 0, 0, 0), + CYGNUS_PIN_DESC(19, "gpio7", 0, 0, 0), + CYGNUS_PIN_DESC(20, "gpio8", 0, 0, 0), + CYGNUS_PIN_DESC(21, "gpio9", 0, 0, 0), + CYGNUS_PIN_DESC(22, "gpio10", 0, 0, 0), + CYGNUS_PIN_DESC(23, "gpio11", 0, 0, 0), + CYGNUS_PIN_DESC(24, "gpio12", 0, 0, 0), + CYGNUS_PIN_DESC(25, "gpio13", 0, 0, 0), + CYGNUS_PIN_DESC(26, "gpio14", 0, 0, 0), + CYGNUS_PIN_DESC(27, "gpio15", 0, 0, 0), + CYGNUS_PIN_DESC(28, "gpio16", 0, 0, 0), + CYGNUS_PIN_DESC(29, "gpio17", 0, 0, 0), + CYGNUS_PIN_DESC(30, "gpio18", 0, 0, 0), + CYGNUS_PIN_DESC(31, "gpio19", 0, 0, 0), + CYGNUS_PIN_DESC(32, "gpio20", 0, 0, 0), + CYGNUS_PIN_DESC(33, "gpio21", 0, 0, 0), + CYGNUS_PIN_DESC(34, "gpio22", 0, 0, 0), + CYGNUS_PIN_DESC(35, "gpio23", 0, 0, 0), + CYGNUS_PIN_DESC(36, "mdc", 0, 0, 0), + CYGNUS_PIN_DESC(37, "mdio", 0, 0, 0), + CYGNUS_PIN_DESC(38, "pwm0", 1, 0x10, 30), + CYGNUS_PIN_DESC(39, "pwm1", 1, 0x10, 28), + CYGNUS_PIN_DESC(40, "pwm2", 1, 0x10, 26), + CYGNUS_PIN_DESC(41, "pwm3", 1, 0x10, 24), + CYGNUS_PIN_DESC(42, "sc0_clk", 1, 0x10, 22), + CYGNUS_PIN_DESC(43, "sc0_cmdvcc_l", 1, 0x10, 20), + CYGNUS_PIN_DESC(44, "sc0_detect", 1, 0x10, 18), + CYGNUS_PIN_DESC(45, "sc0_fcb", 1, 0x10, 16), + CYGNUS_PIN_DESC(46, "sc0_io", 1, 0x10, 14), + CYGNUS_PIN_DESC(47, "sc0_rst_l", 1, 0x10, 12), + CYGNUS_PIN_DESC(48, "sc1_clk", 1, 0x10, 10), + CYGNUS_PIN_DESC(49, "sc1_cmdvcc_l", 1, 0x10, 8), + CYGNUS_PIN_DESC(50, "sc1_detect", 1, 0x10, 6), + CYGNUS_PIN_DESC(51, "sc1_fcb", 1, 0x10, 4), + CYGNUS_PIN_DESC(52, "sc1_io", 1, 0x10, 2), + CYGNUS_PIN_DESC(53, "sc1_rst_l", 1, 0x10, 0), + CYGNUS_PIN_DESC(54, "spi0_clk", 1, 0x18, 10), + CYGNUS_PIN_DESC(55, "spi0_mosi", 1, 0x18, 6), + CYGNUS_PIN_DESC(56, "spi0_miso", 1, 0x18, 8), + CYGNUS_PIN_DESC(57, "spi0_ss", 1, 0x18, 4), + CYGNUS_PIN_DESC(58, "spi1_clk", 1, 0x18, 2), + CYGNUS_PIN_DESC(59, "spi1_mosi", 1, 0x1c, 30), + CYGNUS_PIN_DESC(60, "spi1_miso", 1, 0x18, 0), + CYGNUS_PIN_DESC(61, "spi1_ss", 1, 0x1c, 28), + CYGNUS_PIN_DESC(62, "spi2_clk", 1, 0x1c, 26), + CYGNUS_PIN_DESC(63, "spi2_mosi", 1, 0x1c, 22), + CYGNUS_PIN_DESC(64, "spi2_miso", 1, 0x1c, 24), + CYGNUS_PIN_DESC(65, "spi2_ss", 1, 0x1c, 20), + CYGNUS_PIN_DESC(66, "spi3_clk", 1, 0x1c, 18), + CYGNUS_PIN_DESC(67, "spi3_mosi", 1, 0x1c, 14), + CYGNUS_PIN_DESC(68, "spi3_miso", 1, 0x1c, 16), + CYGNUS_PIN_DESC(69, "spi3_ss", 1, 0x1c, 12), + CYGNUS_PIN_DESC(70, "uart0_cts", 1, 0x1c, 10), + CYGNUS_PIN_DESC(71, "uart0_rts", 1, 0x1c, 8), + CYGNUS_PIN_DESC(72, "uart0_rx", 1, 0x1c, 6), + CYGNUS_PIN_DESC(73, "uart0_tx", 1, 0x1c, 4), + CYGNUS_PIN_DESC(74, "uart1_cts", 1, 0x1c, 2), + CYGNUS_PIN_DESC(75, "uart1_dcd", 1, 0x1c, 0), + CYGNUS_PIN_DESC(76, "uart1_dsr", 1, 0x20, 14), + CYGNUS_PIN_DESC(77, "uart1_dtr", 1, 0x20, 12), + CYGNUS_PIN_DESC(78, "uart1_ri", 1, 0x20, 10), + CYGNUS_PIN_DESC(79, "uart1_rts", 1, 0x20, 8), + CYGNUS_PIN_DESC(80, "uart1_rx", 1, 0x20, 6), + CYGNUS_PIN_DESC(81, "uart1_tx", 1, 0x20, 4), + CYGNUS_PIN_DESC(82, "uart3_rx", 1, 0x20, 2), + CYGNUS_PIN_DESC(83, "uart3_tx", 1, 0x20, 0), + CYGNUS_PIN_DESC(84, "sdio1_clk_sdcard", 1, 0x14, 6), + CYGNUS_PIN_DESC(85, "sdio1_cmd", 1, 0x14, 4), + CYGNUS_PIN_DESC(86, "sdio1_data0", 1, 0x14, 2), + CYGNUS_PIN_DESC(87, "sdio1_data1", 1, 0x14, 0), + CYGNUS_PIN_DESC(88, "sdio1_data2", 1, 0x18, 30), + CYGNUS_PIN_DESC(89, "sdio1_data3", 1, 0x18, 28), + CYGNUS_PIN_DESC(90, "sdio1_wp_n", 1, 0x18, 24), + CYGNUS_PIN_DESC(91, "sdio1_card_rst", 1, 0x14, 10), + CYGNUS_PIN_DESC(92, "sdio1_led_on", 1, 0x18, 26), + CYGNUS_PIN_DESC(93, "sdio1_cd", 1, 0x14, 8), + CYGNUS_PIN_DESC(94, "sdio0_clk_sdcard", 1, 0x14, 26), + CYGNUS_PIN_DESC(95, "sdio0_cmd", 1, 0x14, 24), + CYGNUS_PIN_DESC(96, "sdio0_data0", 1, 0x14, 22), + CYGNUS_PIN_DESC(97, "sdio0_data1", 1, 0x14, 20), + CYGNUS_PIN_DESC(98, "sdio0_data2", 1, 0x14, 18), + CYGNUS_PIN_DESC(99, "sdio0_data3", 1, 0x14, 16), + CYGNUS_PIN_DESC(100, "sdio0_wp_n", 1, 0x14, 12), + CYGNUS_PIN_DESC(101, "sdio0_card_rst", 1, 0x14, 30), + CYGNUS_PIN_DESC(102, "sdio0_led_on", 1, 0x14, 14), + CYGNUS_PIN_DESC(103, "sdio0_cd", 1, 0x14, 28), + CYGNUS_PIN_DESC(104, "sflash_clk", 1, 0x18, 22), + CYGNUS_PIN_DESC(105, "sflash_cs_l", 1, 0x18, 20), + CYGNUS_PIN_DESC(106, "sflash_mosi", 1, 0x18, 14), + CYGNUS_PIN_DESC(107, "sflash_miso", 1, 0x18, 16), + CYGNUS_PIN_DESC(108, "sflash_wp_n", 1, 0x18, 12), + CYGNUS_PIN_DESC(109, "sflash_hold_n", 1, 0x18, 18), + CYGNUS_PIN_DESC(110, "nand_ale", 1, 0xc, 30), + CYGNUS_PIN_DESC(111, "nand_ce0_l", 1, 0xc, 28), + CYGNUS_PIN_DESC(112, "nand_ce1_l", 1, 0xc, 26), + CYGNUS_PIN_DESC(113, "nand_cle", 1, 0xc, 24), + CYGNUS_PIN_DESC(114, "nand_dq0", 1, 0xc, 22), + CYGNUS_PIN_DESC(115, "nand_dq1", 1, 0xc, 20), + CYGNUS_PIN_DESC(116, "nand_dq2", 1, 0xc, 18), + CYGNUS_PIN_DESC(117, "nand_dq3", 1, 0xc, 16), + CYGNUS_PIN_DESC(118, "nand_dq4", 1, 0xc, 14), + CYGNUS_PIN_DESC(119, "nand_dq5", 1, 0xc, 12), + CYGNUS_PIN_DESC(120, "nand_dq6", 1, 0xc, 10), + CYGNUS_PIN_DESC(121, "nand_dq7", 1, 0xc, 8), + CYGNUS_PIN_DESC(122, "nand_rb_l", 1, 0xc, 6), + CYGNUS_PIN_DESC(123, "nand_re_l", 1, 0xc, 4), + CYGNUS_PIN_DESC(124, "nand_we_l", 1, 0xc, 2), + CYGNUS_PIN_DESC(125, "nand_wp_l", 1, 0xc, 0), + CYGNUS_PIN_DESC(126, "lcd_clac", 1, 0x4, 26), + CYGNUS_PIN_DESC(127, "lcd_clcp", 1, 0x4, 24), + CYGNUS_PIN_DESC(128, "lcd_cld0", 1, 0x4, 22), + CYGNUS_PIN_DESC(129, "lcd_cld1", 1, 0x4, 0), + CYGNUS_PIN_DESC(130, "lcd_cld10", 1, 0x4, 20), + CYGNUS_PIN_DESC(131, "lcd_cld11", 1, 0x4, 18), + CYGNUS_PIN_DESC(132, "lcd_cld12", 1, 0x4, 16), + CYGNUS_PIN_DESC(133, "lcd_cld13", 1, 0x4, 14), + CYGNUS_PIN_DESC(134, "lcd_cld14", 1, 0x4, 12), + CYGNUS_PIN_DESC(135, "lcd_cld15", 1, 0x4, 10), + CYGNUS_PIN_DESC(136, "lcd_cld16", 1, 0x4, 8), + CYGNUS_PIN_DESC(137, "lcd_cld17", 1, 0x4, 6), + CYGNUS_PIN_DESC(138, "lcd_cld18", 1, 0x4, 4), + CYGNUS_PIN_DESC(139, "lcd_cld19", 1, 0x4, 2), + CYGNUS_PIN_DESC(140, "lcd_cld2", 1, 0x8, 22), + CYGNUS_PIN_DESC(141, "lcd_cld20", 1, 0x8, 30), + CYGNUS_PIN_DESC(142, "lcd_cld21", 1, 0x8, 28), + CYGNUS_PIN_DESC(143, "lcd_cld22", 1, 0x8, 26), + CYGNUS_PIN_DESC(144, "lcd_cld23", 1, 0x8, 24), + CYGNUS_PIN_DESC(145, "lcd_cld3", 1, 0x8, 20), + CYGNUS_PIN_DESC(146, "lcd_cld4", 1, 0x8, 18), + CYGNUS_PIN_DESC(147, "lcd_cld5", 1, 0x8, 16), + CYGNUS_PIN_DESC(148, "lcd_cld6", 1, 0x8, 14), + CYGNUS_PIN_DESC(149, "lcd_cld7", 1, 0x8, 12), + CYGNUS_PIN_DESC(150, "lcd_cld8", 1, 0x8, 10), + CYGNUS_PIN_DESC(151, "lcd_cld9", 1, 0x8, 8), + CYGNUS_PIN_DESC(152, "lcd_clfp", 1, 0x8, 6), + CYGNUS_PIN_DESC(153, "lcd_clle", 1, 0x8, 4), + CYGNUS_PIN_DESC(154, "lcd_cllp", 1, 0x8, 2), + CYGNUS_PIN_DESC(155, "lcd_clpower", 1, 0x8, 0), + CYGNUS_PIN_DESC(156, "camera_vsync", 1, 0x4, 30), + CYGNUS_PIN_DESC(157, "camera_trigger", 1, 0x0, 0), + CYGNUS_PIN_DESC(158, "camera_strobe", 1, 0x0, 2), + CYGNUS_PIN_DESC(159, "camera_standby", 1, 0x0, 4), + CYGNUS_PIN_DESC(160, "camera_reset_n", 1, 0x0, 6), + CYGNUS_PIN_DESC(161, "camera_pixdata9", 1, 0x0, 8), + CYGNUS_PIN_DESC(162, "camera_pixdata8", 1, 0x0, 10), + CYGNUS_PIN_DESC(163, "camera_pixdata7", 1, 0x0, 12), + CYGNUS_PIN_DESC(164, "camera_pixdata6", 1, 0x0, 14), + CYGNUS_PIN_DESC(165, "camera_pixdata5", 1, 0x0, 16), + CYGNUS_PIN_DESC(166, "camera_pixdata4", 1, 0x0, 18), + CYGNUS_PIN_DESC(167, "camera_pixdata3", 1, 0x0, 20), + CYGNUS_PIN_DESC(168, "camera_pixdata2", 1, 0x0, 22), + CYGNUS_PIN_DESC(169, "camera_pixdata1", 1, 0x0, 24), + CYGNUS_PIN_DESC(170, "camera_pixdata0", 1, 0x0, 26), + CYGNUS_PIN_DESC(171, "camera_pixclk", 1, 0x0, 28), + CYGNUS_PIN_DESC(172, "camera_hsync", 1, 0x0, 30), + CYGNUS_PIN_DESC(173, "camera_pll_ref_clk", 0, 0, 0), + CYGNUS_PIN_DESC(174, "usb_id_indication", 0, 0, 0), + CYGNUS_PIN_DESC(175, "usb_vbus_indication", 0, 0, 0), + CYGNUS_PIN_DESC(176, "gpio0_3p3", 0, 0, 0), + CYGNUS_PIN_DESC(177, "gpio1_3p3", 0, 0, 0), + CYGNUS_PIN_DESC(178, "gpio2_3p3", 0, 0, 0), + CYGNUS_PIN_DESC(179, "gpio3_3p3", 0, 0, 0), +}; + +/* + * List of groups of pins + */ +static const unsigned bsc1_pins[] = { 8, 9 }; +static const unsigned pcie_clkreq_pins[] = { 8, 9 }; + +static const unsigned i2s2_0_pins[] = { 12 }; +static const unsigned i2s2_1_pins[] = { 13 }; +static const unsigned i2s2_2_pins[] = { 14 }; +static const unsigned i2s2_3_pins[] = { 15 }; +static const unsigned i2s2_4_pins[] = { 16 }; + +static const unsigned pwm4_pins[] = { 17 }; +static const unsigned pwm5_pins[] = { 18 }; + +static const unsigned key0_pins[] = { 20 }; +static const unsigned key1_pins[] = { 21 }; +static const unsigned key2_pins[] = { 22 }; +static const unsigned key3_pins[] = { 23 }; +static const unsigned key4_pins[] = { 24 }; +static const unsigned key5_pins[] = { 25 }; + +static const unsigned key6_pins[] = { 26 }; +static const unsigned audio_dte0_pins[] = { 26 }; + +static const unsigned key7_pins[] = { 27 }; +static const unsigned audio_dte1_pins[] = { 27 }; + +static const unsigned key8_pins[] = { 28 }; +static const unsigned key9_pins[] = { 29 }; +static const unsigned key10_pins[] = { 30 }; +static const unsigned key11_pins[] = { 31 }; +static const unsigned key12_pins[] = { 32 }; +static const unsigned key13_pins[] = { 33 }; + +static const unsigned key14_pins[] = { 34 }; +static const unsigned audio_dte2_pins[] = { 34 }; + +static const unsigned key15_pins[] = { 35 }; +static const unsigned audio_dte3_pins[] = { 35 }; + +static const unsigned pwm0_pins[] = { 38 }; +static const unsigned pwm1_pins[] = { 39 }; +static const unsigned pwm2_pins[] = { 40 }; +static const unsigned pwm3_pins[] = { 41 }; + +static const unsigned sdio0_pins[] = { 94, 95, 96, 97, 98, 99 }; + +static const unsigned smart_card0_pins[] = { 42, 43, 44, 46, 47 }; +static const unsigned i2s0_0_pins[] = { 42, 43, 44, 46 }; +static const unsigned spdif_pins[] = { 47 }; + +static const unsigned smart_card1_pins[] = { 48, 49, 50, 52, 53 }; +static const unsigned i2s1_0_pins[] = { 48, 49, 50, 52 }; + +static const unsigned spi0_pins[] = { 54, 55, 56, 57 }; + +static const unsigned spi1_pins[] = { 58, 59, 60, 61 }; + +static const unsigned spi2_pins[] = { 62, 63, 64, 65 }; + +static const unsigned spi3_pins[] = { 66, 67, 68, 69 }; +static const unsigned sw_led0_0_pins[] = { 66, 67, 68, 69 }; + +static const unsigned d1w_pins[] = { 10, 11 }; +static const unsigned uart4_pins[] = { 10, 11 }; +static const unsigned sw_led2_0_pins[] = { 10, 11 }; + +static const unsigned lcd_pins[] = { 126, 127, 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 }; +static const unsigned sram_0_pins[] = { 126, 127, 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 }; +static const unsigned spi5_pins[] = { 141, 142, 143, 144 }; + +static const unsigned uart0_pins[] = { 70, 71, 72, 73 }; +static const unsigned sw_led0_1_pins[] = { 70, 71, 72, 73 }; + +static const unsigned uart1_dte_pins[] = { 75, 76, 77, 78 }; +static const unsigned uart2_pins[] = { 75, 76, 77, 78 }; + +static const unsigned uart1_pins[] = { 74, 79, 80, 81 }; + +static const unsigned uart3_pins[] = { 82, 83 }; + +static const unsigned qspi_0_pins[] = { 104, 105, 106, 107 }; + +static const unsigned nand_pins[] = { 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125 }; + +static const unsigned sdio0_cd_pins[] = { 103 }; + +static const unsigned sdio0_mmc_pins[] = { 100, 101, 102 }; + +static const unsigned sdio1_data_0_pins[] = { 86, 87 }; +static const unsigned can0_pins[] = { 86, 87 }; +static const unsigned spi4_0_pins[] = { 86, 87 }; + +static const unsigned sdio1_data_1_pins[] = { 88, 89 }; +static const unsigned can1_pins[] = { 88, 89 }; +static const unsigned spi4_1_pins[] = { 88, 89 }; + +static const unsigned sdio1_cd_pins[] = { 93 }; + +static const unsigned sdio1_led_pins[] = { 84, 85 }; +static const unsigned sw_led2_1_pins[] = { 84, 85 }; + +static const unsigned sdio1_mmc_pins[] = { 90, 91, 92 }; + +static const unsigned cam_led_pins[] = { 156, 157, 158, 159, 160 }; +static const unsigned sw_led1_pins[] = { 156, 157, 158, 159 }; + +static const unsigned cam_0_pins[] = { 169, 170, 171, 169, 170 }; + +static const unsigned cam_1_pins[] = { 161, 162, 163, 164, 165, 166, 167, + 168 }; +static const unsigned sram_1_pins[] = { 161, 162, 163, 164, 165, 166, 167, + 168 }; + +static const unsigned qspi_1_pins[] = { 108, 109 }; + +static const unsigned smart_card0_fcb_pins[] = { 45 }; +static const unsigned i2s0_1_pins[] = { 45 }; + +static const unsigned smart_card1_fcb_pins[] = { 51 }; +static const unsigned i2s1_1_pins[] = { 51 }; + +static const unsigned gpio0_3p3_pins[] = { 176 }; +static const unsigned usb0_oc_pins[] = { 176 }; + +static const unsigned gpio1_3p3_pins[] = { 177 }; +static const unsigned usb1_oc_pins[] = { 177 }; + +static const unsigned gpio2_3p3_pins[] = { 178 }; +static const unsigned usb2_oc_pins[] = { 178 }; + +#define CYGNUS_PIN_GROUP(group_name, off, sh, al) \ +{ \ + .name = __stringify(group_name) "_grp", \ + .pins = group_name ## _pins, \ + .num_pins = ARRAY_SIZE(group_name ## _pins), \ + .mux = { \ + .offset = off, \ + .shift = sh, \ + .alt = al, \ + } \ +} + +/* + * List of Cygnus pin groups + */ +static const struct cygnus_pin_group cygnus_pin_groups[] = { + CYGNUS_PIN_GROUP(i2s2_0, 0x0, 0, 2), + CYGNUS_PIN_GROUP(i2s2_1, 0x0, 4, 2), + CYGNUS_PIN_GROUP(i2s2_2, 0x0, 8, 2), + CYGNUS_PIN_GROUP(i2s2_3, 0x0, 12, 2), + CYGNUS_PIN_GROUP(i2s2_4, 0x0, 16, 2), + CYGNUS_PIN_GROUP(pwm4, 0x0, 20, 0), + CYGNUS_PIN_GROUP(pwm5, 0x0, 24, 2), + CYGNUS_PIN_GROUP(key0, 0x4, 0, 1), + CYGNUS_PIN_GROUP(key1, 0x4, 4, 1), + CYGNUS_PIN_GROUP(key2, 0x4, 8, 1), + CYGNUS_PIN_GROUP(key3, 0x4, 12, 1), + CYGNUS_PIN_GROUP(key4, 0x4, 16, 1), + CYGNUS_PIN_GROUP(key5, 0x4, 20, 1), + CYGNUS_PIN_GROUP(key6, 0x4, 24, 1), + CYGNUS_PIN_GROUP(audio_dte0, 0x4, 24, 2), + CYGNUS_PIN_GROUP(key7, 0x4, 28, 1), + CYGNUS_PIN_GROUP(audio_dte1, 0x4, 28, 2), + CYGNUS_PIN_GROUP(key8, 0x8, 0, 1), + CYGNUS_PIN_GROUP(key9, 0x8, 4, 1), + CYGNUS_PIN_GROUP(key10, 0x8, 8, 1), + CYGNUS_PIN_GROUP(key11, 0x8, 12, 1), + CYGNUS_PIN_GROUP(key12, 0x8, 16, 1), + CYGNUS_PIN_GROUP(key13, 0x8, 20, 1), + CYGNUS_PIN_GROUP(key14, 0x8, 24, 1), + CYGNUS_PIN_GROUP(audio_dte2, 0x8, 24, 2), + CYGNUS_PIN_GROUP(key15, 0x8, 28, 1), + CYGNUS_PIN_GROUP(audio_dte3, 0x8, 28, 2), + CYGNUS_PIN_GROUP(pwm0, 0xc, 0, 0), + CYGNUS_PIN_GROUP(pwm1, 0xc, 4, 0), + CYGNUS_PIN_GROUP(pwm2, 0xc, 8, 0), + CYGNUS_PIN_GROUP(pwm3, 0xc, 12, 0), + CYGNUS_PIN_GROUP(sdio0, 0xc, 16, 0), + CYGNUS_PIN_GROUP(smart_card0, 0xc, 20, 0), + CYGNUS_PIN_GROUP(i2s0_0, 0xc, 20, 1), + CYGNUS_PIN_GROUP(spdif, 0xc, 20, 1), + CYGNUS_PIN_GROUP(smart_card1, 0xc, 24, 0), + CYGNUS_PIN_GROUP(i2s1_0, 0xc, 24, 1), + CYGNUS_PIN_GROUP(spi0, 0x10, 0, 0), + CYGNUS_PIN_GROUP(spi1, 0x10, 4, 0), + CYGNUS_PIN_GROUP(spi2, 0x10, 8, 0), + CYGNUS_PIN_GROUP(spi3, 0x10, 12, 0), + CYGNUS_PIN_GROUP(sw_led0_0, 0x10, 12, 2), + CYGNUS_PIN_GROUP(d1w, 0x10, 16, 0), + CYGNUS_PIN_GROUP(uart4, 0x10, 16, 1), + CYGNUS_PIN_GROUP(sw_led2_0, 0x10, 16, 2), + CYGNUS_PIN_GROUP(lcd, 0x10, 20, 0), + CYGNUS_PIN_GROUP(sram_0, 0x10, 20, 1), + CYGNUS_PIN_GROUP(spi5, 0x10, 20, 2), + CYGNUS_PIN_GROUP(uart0, 0x14, 0, 0), + CYGNUS_PIN_GROUP(sw_led0_1, 0x14, 0, 2), + CYGNUS_PIN_GROUP(uart1_dte, 0x14, 4, 0), + CYGNUS_PIN_GROUP(uart2, 0x14, 4, 1), + CYGNUS_PIN_GROUP(uart1, 0x14, 8, 0), + CYGNUS_PIN_GROUP(uart3, 0x14, 12, 0), + CYGNUS_PIN_GROUP(qspi_0, 0x14, 16, 0), + CYGNUS_PIN_GROUP(nand, 0x14, 20, 0), + CYGNUS_PIN_GROUP(sdio0_cd, 0x18, 0, 0), + CYGNUS_PIN_GROUP(sdio0_mmc, 0x18, 4, 0), + CYGNUS_PIN_GROUP(sdio1_data_0, 0x18, 8, 0), + CYGNUS_PIN_GROUP(can0, 0x18, 8, 1), + CYGNUS_PIN_GROUP(spi4_0, 0x18, 8, 2), + CYGNUS_PIN_GROUP(sdio1_data_1, 0x18, 12, 0), + CYGNUS_PIN_GROUP(can1, 0x18, 12, 1), + CYGNUS_PIN_GROUP(spi4_1, 0x18, 12, 2), + CYGNUS_PIN_GROUP(sdio1_cd, 0x18, 16, 0), + CYGNUS_PIN_GROUP(sdio1_led, 0x18, 20, 0), + CYGNUS_PIN_GROUP(sw_led2_1, 0x18, 20, 2), + CYGNUS_PIN_GROUP(sdio1_mmc, 0x18, 24, 0), + CYGNUS_PIN_GROUP(cam_led, 0x1c, 0, 0), + CYGNUS_PIN_GROUP(sw_led1, 0x1c, 0, 1), + CYGNUS_PIN_GROUP(cam_0, 0x1c, 4, 0), + CYGNUS_PIN_GROUP(cam_1, 0x1c, 8, 0), + CYGNUS_PIN_GROUP(sram_1, 0x1c, 8, 1), + CYGNUS_PIN_GROUP(qspi_1, 0x1c, 12, 0), + CYGNUS_PIN_GROUP(bsc1, 0x1c, 16, 0), + CYGNUS_PIN_GROUP(pcie_clkreq, 0x1c, 16, 1), + CYGNUS_PIN_GROUP(smart_card0_fcb, 0x20, 0, 0), + CYGNUS_PIN_GROUP(i2s0_1, 0x20, 0, 1), + CYGNUS_PIN_GROUP(smart_card1_fcb, 0x20, 4, 0), + CYGNUS_PIN_GROUP(i2s1_1, 0x20, 4, 1), + CYGNUS_PIN_GROUP(gpio0_3p3, 0x28, 0, 0), + CYGNUS_PIN_GROUP(usb0_oc, 0x28, 0, 1), + CYGNUS_PIN_GROUP(gpio1_3p3, 0x28, 4, 0), + CYGNUS_PIN_GROUP(usb1_oc, 0x28, 4, 1), + CYGNUS_PIN_GROUP(gpio2_3p3, 0x28, 8, 0), + CYGNUS_PIN_GROUP(usb2_oc, 0x28, 8, 1), +}; + +/* + * List of groups supported by functions + */ +static const char * const i2s0_grps[] = { "i2s0_0_grp", "i2s0_1_grp" }; +static const char * const i2s1_grps[] = { "i2s1_0_grp", "i2s1_1_grp" }; +static const char * const i2s2_grps[] = { "i2s2_0_grp", "i2s2_1_grp", + "i2s2_2_grp", "i2s2_3_grp", "i2s2_4_grp" }; +static const char * const spdif_grps[] = { "spdif_grp" }; +static const char * const pwm0_grps[] = { "pwm0_grp" }; +static const char * const pwm1_grps[] = { "pwm1_grp" }; +static const char * const pwm2_grps[] = { "pwm2_grp" }; +static const char * const pwm3_grps[] = { "pwm3_grp" }; +static const char * const pwm4_grps[] = { "pwm4_grp" }; +static const char * const pwm5_grps[] = { "pwm5_grp" }; +static const char * const key_grps[] = { "key0_grp", "key1_grp", "key2_grp", + "key3_grp", "key4_grp", "key5_grp", "key6_grp", "key7_grp", "key8_grp", + "key9_grp", "key10_grp", "key11_grp", "key12_grp", "key13_grp", + "key14_grp", "key15_grp" }; +static const char * const audio_dte_grps[] = { "audio_dte0_grp", + "audio_dte1_grp", "audio_dte2_grp", "audio_dte3_grp" }; +static const char * const smart_card0_grps[] = { "smart_card0_grp", + "smart_card0_fcb_grp" }; +static const char * const smart_card1_grps[] = { "smart_card1_grp", + "smart_card1_fcb_grp" }; +static const char * const spi0_grps[] = { "spi0_grp" }; +static const char * const spi1_grps[] = { "spi1_grp" }; +static const char * const spi2_grps[] = { "spi2_grp" }; +static const char * const spi3_grps[] = { "spi3_grp" }; +static const char * const spi4_grps[] = { "spi4_0_grp", "spi4_1_grp" }; +static const char * const spi5_grps[] = { "spi5_grp" }; + +static const char * const sw_led0_grps[] = { "sw_led0_0_grp", + "sw_led0_1_grp" }; +static const char * const sw_led1_grps[] = { "sw_led1_grp" }; +static const char * const sw_led2_grps[] = { "sw_led2_0_grp", + "sw_led2_1_grp" }; +static const char * const d1w_grps[] = { "d1w_grp" }; +static const char * const lcd_grps[] = { "lcd_grp" }; +static const char * const sram_grps[] = { "sram_0_grp", "sram_1_grp" }; + +static const char * const uart0_grps[] = { "uart0_grp" }; +static const char * const uart1_grps[] = { "uart1_grp", "uart1_dte_grp" }; +static const char * const uart2_grps[] = { "uart2_grp" }; +static const char * const uart3_grps[] = { "uart3_grp" }; +static const char * const uart4_grps[] = { "uart4_grp" }; +static const char * const qspi_grps[] = { "qspi_0_grp", "qspi_1_grp" }; +static const char * const nand_grps[] = { "nand_grp" }; +static const char * const sdio0_grps[] = { "sdio0_grp", "sdio0_cd_grp", + "sdio0_mmc_grp" }; +static const char * const sdio1_grps[] = { "sdio1_data_0_grp", + "sdio1_data_1_grp", "sdio1_cd_grp", "sdio1_led_grp", "sdio1_mmc_grp" }; +static const char * const can0_grps[] = { "can0_grp" }; +static const char * const can1_grps[] = { "can1_grp" }; +static const char * const cam_grps[] = { "cam_led_grp", "cam_0_grp", + "cam_1_grp" }; +static const char * const bsc1_grps[] = { "bsc1_grp" }; +static const char * const pcie_clkreq_grps[] = { "pcie_clkreq_grp" }; +static const char * const usb0_oc_grps[] = { "usb0_oc_grp" }; +static const char * const usb1_oc_grps[] = { "usb1_oc_grp" }; +static const char * const usb2_oc_grps[] = { "usb2_oc_grp" }; + +#define CYGNUS_PIN_FUNCTION(func) \ +{ \ + .name = #func, \ + .groups = func ## _grps, \ + .num_groups = ARRAY_SIZE(func ## _grps), \ +} + +/* + * List of supported functions in Cygnus + */ +static const struct cygnus_pin_function cygnus_pin_functions[] = { + CYGNUS_PIN_FUNCTION(i2s0), + CYGNUS_PIN_FUNCTION(i2s1), + CYGNUS_PIN_FUNCTION(i2s2), + CYGNUS_PIN_FUNCTION(spdif), + CYGNUS_PIN_FUNCTION(pwm0), + CYGNUS_PIN_FUNCTION(pwm1), + CYGNUS_PIN_FUNCTION(pwm2), + CYGNUS_PIN_FUNCTION(pwm3), + CYGNUS_PIN_FUNCTION(pwm4), + CYGNUS_PIN_FUNCTION(pwm5), + CYGNUS_PIN_FUNCTION(key), + CYGNUS_PIN_FUNCTION(audio_dte), + CYGNUS_PIN_FUNCTION(smart_card0), + CYGNUS_PIN_FUNCTION(smart_card1), + CYGNUS_PIN_FUNCTION(spi0), + CYGNUS_PIN_FUNCTION(spi1), + CYGNUS_PIN_FUNCTION(spi2), + CYGNUS_PIN_FUNCTION(spi3), + CYGNUS_PIN_FUNCTION(spi4), + CYGNUS_PIN_FUNCTION(spi5), + CYGNUS_PIN_FUNCTION(sw_led0), + CYGNUS_PIN_FUNCTION(sw_led1), + CYGNUS_PIN_FUNCTION(sw_led2), + CYGNUS_PIN_FUNCTION(d1w), + CYGNUS_PIN_FUNCTION(lcd), + CYGNUS_PIN_FUNCTION(sram), + CYGNUS_PIN_FUNCTION(uart0), + CYGNUS_PIN_FUNCTION(uart1), + CYGNUS_PIN_FUNCTION(uart2), + CYGNUS_PIN_FUNCTION(uart3), + CYGNUS_PIN_FUNCTION(uart4), + CYGNUS_PIN_FUNCTION(qspi), + CYGNUS_PIN_FUNCTION(nand), + CYGNUS_PIN_FUNCTION(sdio0), + CYGNUS_PIN_FUNCTION(sdio1), + CYGNUS_PIN_FUNCTION(can0), + CYGNUS_PIN_FUNCTION(can1), + CYGNUS_PIN_FUNCTION(cam), + CYGNUS_PIN_FUNCTION(bsc1), + CYGNUS_PIN_FUNCTION(pcie_clkreq), + CYGNUS_PIN_FUNCTION(usb0_oc), + CYGNUS_PIN_FUNCTION(usb1_oc), + CYGNUS_PIN_FUNCTION(usb2_oc), +}; + +static int cygnus_get_groups_count(struct pinctrl_dev *pctrl_dev) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->num_groups; +} + +static const char *cygnus_get_group_name(struct pinctrl_dev *pctrl_dev, + unsigned selector) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->groups[selector].name; +} + +static int cygnus_get_group_pins(struct pinctrl_dev *pctrl_dev, + unsigned selector, const unsigned **pins, + unsigned *num_pins) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + *pins = pinctrl->groups[selector].pins; + *num_pins = pinctrl->groups[selector].num_pins; + + return 0; +} + +static void cygnus_pin_dbg_show(struct pinctrl_dev *pctrl_dev, + struct seq_file *s, unsigned offset) +{ + seq_printf(s, " %s", dev_name(pctrl_dev->dev)); +} + +static const struct pinctrl_ops cygnus_pinctrl_ops = { + .get_groups_count = cygnus_get_groups_count, + .get_group_name = cygnus_get_group_name, + .get_group_pins = cygnus_get_group_pins, + .pin_dbg_show = cygnus_pin_dbg_show, + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int cygnus_get_functions_count(struct pinctrl_dev *pctrl_dev) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->num_functions; +} + +static const char *cygnus_get_function_name(struct pinctrl_dev *pctrl_dev, + unsigned selector) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->functions[selector].name; +} + +static int cygnus_get_function_groups(struct pinctrl_dev *pctrl_dev, + unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + *groups = pinctrl->functions[selector].groups; + *num_groups = pinctrl->functions[selector].num_groups; + + return 0; +} + +static int cygnus_pinmux_set(struct cygnus_pinctrl *pinctrl, + const struct cygnus_pin_function *func, + const struct cygnus_pin_group *grp, + struct cygnus_mux_log *mux_log) +{ + const struct cygnus_mux *mux = &grp->mux; + int i; + u32 val, mask = 0x7; + unsigned long flags; + + for (i = 0; i < CYGNUS_NUM_IOMUX; i++) { + if (mux->offset != mux_log[i].mux.offset || + mux->shift != mux_log[i].mux.shift) + continue; + + /* match found if we reach here */ + + /* if this is a new configuration, just do it! */ + if (!mux_log[i].is_configured) + break; + + /* + * IOMUX has been configured previously and one is trying to + * configure it to a different function + */ + if (mux_log[i].mux.alt != mux->alt) { + dev_err(pinctrl->dev, + "double configuration error detected!\n"); + dev_err(pinctrl->dev, "func:%s grp:%s\n", + func->name, grp->name); + return -EINVAL; + } else { + /* + * One tries to configure it to the same function. + * Just quit and don't bother + */ + return 0; + } + } + + mux_log[i].mux.alt = mux->alt; + mux_log[i].is_configured = true; + + spin_lock_irqsave(&pinctrl->lock, flags); + + val = readl(pinctrl->base0 + grp->mux.offset); + val &= ~(mask << grp->mux.shift); + val |= grp->mux.alt << grp->mux.shift; + writel(val, pinctrl->base0 + grp->mux.offset); + + spin_unlock_irqrestore(&pinctrl->lock, flags); + + return 0; +} + +static int cygnus_pinmux_set_mux(struct pinctrl_dev *pctrl_dev, + unsigned func_select, unsigned grp_select) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + const struct cygnus_pin_function *func = + &pinctrl->functions[func_select]; + const struct cygnus_pin_group *grp = &pinctrl->groups[grp_select]; + + dev_dbg(pctrl_dev->dev, "func:%u name:%s grp:%u name:%s\n", + func_select, func->name, grp_select, grp->name); + + dev_dbg(pctrl_dev->dev, "offset:0x%08x shift:%u alt:%u\n", + grp->mux.offset, grp->mux.shift, grp->mux.alt); + + return cygnus_pinmux_set(pinctrl, func, grp, pinctrl->mux_log); +} + +static int cygnus_gpio_request_enable(struct pinctrl_dev *pctrl_dev, + struct pinctrl_gpio_range *range, + unsigned pin) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + const struct cygnus_gpio_mux *mux = pctrl_dev->desc->pins[pin].drv_data; + u32 val; + unsigned long flags; + + /* not all pins support GPIO pinmux override */ + if (!mux->is_supported) + return -ENOTSUPP; + + spin_lock_irqsave(&pinctrl->lock, flags); + + val = readl(pinctrl->base1 + mux->offset); + val |= 0x3 << mux->shift; + writel(val, pinctrl->base1 + mux->offset); + + spin_unlock_irqrestore(&pinctrl->lock, flags); + + dev_dbg(pctrl_dev->dev, + "gpio request enable pin=%u offset=0x%x shift=%u\n", + pin, mux->offset, mux->shift); + + return 0; +} + +static void cygnus_gpio_disable_free(struct pinctrl_dev *pctrl_dev, + struct pinctrl_gpio_range *range, + unsigned pin) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + struct cygnus_gpio_mux *mux = pctrl_dev->desc->pins[pin].drv_data; + u32 val; + unsigned long flags; + + if (!mux->is_supported) + return; + + spin_lock_irqsave(&pinctrl->lock, flags); + + val = readl(pinctrl->base1 + mux->offset); + val &= ~(0x3 << mux->shift); + writel(val, pinctrl->base1 + mux->offset); + + spin_unlock_irqrestore(&pinctrl->lock, flags); + + dev_err(pctrl_dev->dev, + "gpio disable free pin=%u offset=0x%x shift=%u\n", + pin, mux->offset, mux->shift); +} + +static const struct pinmux_ops cygnus_pinmux_ops = { + .get_functions_count = cygnus_get_functions_count, + .get_function_name = cygnus_get_function_name, + .get_function_groups = cygnus_get_function_groups, + .set_mux = cygnus_pinmux_set_mux, + .gpio_request_enable = cygnus_gpio_request_enable, + .gpio_disable_free = cygnus_gpio_disable_free, +}; + +static struct pinctrl_desc cygnus_pinctrl_desc = { + .name = "cygnus-pinmux", + .pctlops = &cygnus_pinctrl_ops, + .pmxops = &cygnus_pinmux_ops, +}; + +static int cygnus_mux_log_init(struct cygnus_pinctrl *pinctrl) +{ + struct cygnus_mux_log *log; + unsigned int i, j; + + pinctrl->mux_log = devm_kcalloc(pinctrl->dev, CYGNUS_NUM_IOMUX, + sizeof(struct cygnus_mux_log), + GFP_KERNEL); + if (!pinctrl->mux_log) + return -ENOMEM; + + log = pinctrl->mux_log; + for (i = 0; i < CYGNUS_NUM_IOMUX_REGS; i++) { + for (j = 0; j < CYGNUS_NUM_MUX_PER_REG; j++) { + log = &pinctrl->mux_log[i * CYGNUS_NUM_MUX_PER_REG + + j]; + log->mux.offset = i * 4; + log->mux.shift = j * 4; + log->mux.alt = 0; + log->is_configured = false; + } + } + + return 0; +} + +static int cygnus_pinmux_probe(struct platform_device *pdev) +{ + struct cygnus_pinctrl *pinctrl; + struct resource *res; + int i, ret; + struct pinctrl_pin_desc *pins; + unsigned num_pins = ARRAY_SIZE(cygnus_pins); + + pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL); + if (!pinctrl) + return -ENOMEM; + + pinctrl->dev = &pdev->dev; + platform_set_drvdata(pdev, pinctrl); + spin_lock_init(&pinctrl->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pinctrl->base0 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pinctrl->base0)) { + dev_err(&pdev->dev, "unable to map I/O space\n"); + return PTR_ERR(pinctrl->base0); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + pinctrl->base1 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pinctrl->base1)) { + dev_err(&pdev->dev, "unable to map I/O space\n"); + return PTR_ERR(pinctrl->base1); + } + + ret = cygnus_mux_log_init(pinctrl); + if (ret) { + dev_err(&pdev->dev, "unable to initialize IOMUX log\n"); + return ret; + } + + pins = devm_kcalloc(&pdev->dev, num_pins, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + for (i = 0; i < num_pins; i++) { + pins[i].number = cygnus_pins[i].pin; + pins[i].name = cygnus_pins[i].name; + pins[i].drv_data = &cygnus_pins[i].gpio_mux; + } + + pinctrl->groups = cygnus_pin_groups; + pinctrl->num_groups = ARRAY_SIZE(cygnus_pin_groups); + pinctrl->functions = cygnus_pin_functions; + pinctrl->num_functions = ARRAY_SIZE(cygnus_pin_functions); + cygnus_pinctrl_desc.pins = pins; + cygnus_pinctrl_desc.npins = num_pins; + + pinctrl->pctl = devm_pinctrl_register(&pdev->dev, &cygnus_pinctrl_desc, + pinctrl); + if (IS_ERR(pinctrl->pctl)) { + dev_err(&pdev->dev, "unable to register Cygnus IOMUX pinctrl\n"); + return PTR_ERR(pinctrl->pctl); + } + + return 0; +} + +static const struct of_device_id cygnus_pinmux_of_match[] = { + { .compatible = "brcm,cygnus-pinmux" }, + { } +}; + +static struct platform_driver cygnus_pinmux_driver = { + .driver = { + .name = "cygnus-pinmux", + .of_match_table = cygnus_pinmux_of_match, + .suppress_bind_attrs = true, + }, + .probe = cygnus_pinmux_probe, +}; + +static int __init cygnus_pinmux_init(void) +{ + return platform_driver_register(&cygnus_pinmux_driver); +} +arch_initcall(cygnus_pinmux_init); diff --git a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c new file mode 100644 index 000000000..20b9864ad --- /dev/null +++ b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c @@ -0,0 +1,909 @@ +/* + * Copyright (C) 2014-2017 Broadcom + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * This file contains the Broadcom Iproc GPIO driver that supports 3 + * GPIO controllers on Iproc including the ASIU GPIO controller, the + * chipCommonG GPIO controller, and the always-on GPIO controller. Basic + * PINCONF such as bias pull up/down, and drive strength are also supported + * in this driver. + * + * It provides the functionality where pins from the GPIO can be + * individually muxed to GPIO function, if individual pad + * configuration is supported, through the interaction with respective + * SoCs IOMUX controller. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/gpio/driver.h> +#include <linux/ioport.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> + +#include "../pinctrl-utils.h" + +#define IPROC_GPIO_DATA_IN_OFFSET 0x00 +#define IPROC_GPIO_DATA_OUT_OFFSET 0x04 +#define IPROC_GPIO_OUT_EN_OFFSET 0x08 +#define IPROC_GPIO_INT_TYPE_OFFSET 0x0c +#define IPROC_GPIO_INT_DE_OFFSET 0x10 +#define IPROC_GPIO_INT_EDGE_OFFSET 0x14 +#define IPROC_GPIO_INT_MSK_OFFSET 0x18 +#define IPROC_GPIO_INT_STAT_OFFSET 0x1c +#define IPROC_GPIO_INT_MSTAT_OFFSET 0x20 +#define IPROC_GPIO_INT_CLR_OFFSET 0x24 +#define IPROC_GPIO_PAD_RES_OFFSET 0x34 +#define IPROC_GPIO_RES_EN_OFFSET 0x38 + +/* drive strength control for ASIU GPIO */ +#define IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58 + +/* pinconf for CCM GPIO */ +#define IPROC_GPIO_PULL_DN_OFFSET 0x10 +#define IPROC_GPIO_PULL_UP_OFFSET 0x14 + +/* pinconf for CRMU(aon) GPIO and CCM GPIO*/ +#define IPROC_GPIO_DRV_CTRL_OFFSET 0x00 + +#define GPIO_BANK_SIZE 0x200 +#define NGPIOS_PER_BANK 32 +#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK) + +#define IPROC_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg)) +#define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK) + +#define GPIO_DRV_STRENGTH_BIT_SHIFT 20 +#define GPIO_DRV_STRENGTH_BITS 3 +#define GPIO_DRV_STRENGTH_BIT_MASK ((1 << GPIO_DRV_STRENGTH_BITS) - 1) + +enum iproc_pinconf_param { + IPROC_PINCONF_DRIVE_STRENGTH = 0, + IPROC_PINCONF_BIAS_DISABLE, + IPROC_PINCONF_BIAS_PULL_UP, + IPROC_PINCONF_BIAS_PULL_DOWN, + IPROC_PINCON_MAX, +}; + +enum iproc_pinconf_ctrl_type { + IOCTRL_TYPE_AON = 1, + IOCTRL_TYPE_CDRU, + IOCTRL_TYPE_INVALID, +}; + +/* + * Iproc GPIO core + * + * @dev: pointer to device + * @base: I/O register base for Iproc GPIO controller + * @io_ctrl: I/O register base for certain type of Iproc GPIO controller that + * has the PINCONF support implemented outside of the GPIO block + * @lock: lock to protect access to I/O registers + * @gc: GPIO chip + * @num_banks: number of GPIO banks, each bank supports up to 32 GPIOs + * @pinmux_is_supported: flag to indicate this GPIO controller contains pins + * that can be individually muxed to GPIO + * @pinconf_disable: contains a list of PINCONF parameters that need to be + * disabled + * @nr_pinconf_disable: total number of PINCONF parameters that need to be + * disabled + * @pctl: pointer to pinctrl_dev + * @pctldesc: pinctrl descriptor + */ +struct iproc_gpio { + struct device *dev; + + void __iomem *base; + void __iomem *io_ctrl; + enum iproc_pinconf_ctrl_type io_ctrl_type; + + raw_spinlock_t lock; + + struct gpio_chip gc; + unsigned num_banks; + + bool pinmux_is_supported; + + enum pin_config_param *pinconf_disable; + unsigned int nr_pinconf_disable; + + struct pinctrl_dev *pctl; + struct pinctrl_desc pctldesc; +}; + +/* + * Mapping from PINCONF pins to GPIO pins is 1-to-1 + */ +static inline unsigned iproc_pin_to_gpio(unsigned pin) +{ + return pin; +} + +/** + * iproc_set_bit - set or clear one bit (corresponding to the GPIO pin) in a + * Iproc GPIO register + * + * @iproc_gpio: Iproc GPIO device + * @reg: register offset + * @gpio: GPIO pin + * @set: set or clear + */ +static inline void iproc_set_bit(struct iproc_gpio *chip, unsigned int reg, + unsigned gpio, bool set) +{ + unsigned int offset = IPROC_GPIO_REG(gpio, reg); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); + u32 val; + + val = readl(chip->base + offset); + if (set) + val |= BIT(shift); + else + val &= ~BIT(shift); + writel(val, chip->base + offset); +} + +static inline bool iproc_get_bit(struct iproc_gpio *chip, unsigned int reg, + unsigned gpio) +{ + unsigned int offset = IPROC_GPIO_REG(gpio, reg); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); + + return !!(readl(chip->base + offset) & BIT(shift)); +} + +static void iproc_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct iproc_gpio *chip = gpiochip_get_data(gc); + struct irq_chip *irq_chip = irq_desc_get_chip(desc); + int i, bit; + + chained_irq_enter(irq_chip, desc); + + /* go through the entire GPIO banks and handle all interrupts */ + for (i = 0; i < chip->num_banks; i++) { + unsigned long val = readl(chip->base + (i * GPIO_BANK_SIZE) + + IPROC_GPIO_INT_MSTAT_OFFSET); + + for_each_set_bit(bit, &val, NGPIOS_PER_BANK) { + unsigned pin = NGPIOS_PER_BANK * i + bit; + int child_irq = irq_find_mapping(gc->irq.domain, pin); + + /* + * Clear the interrupt before invoking the + * handler, so we do not leave any window + */ + writel(BIT(bit), chip->base + (i * GPIO_BANK_SIZE) + + IPROC_GPIO_INT_CLR_OFFSET); + + generic_handle_irq(child_irq); + } + } + + chained_irq_exit(irq_chip, desc); +} + + +static void iproc_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio *chip = gpiochip_get_data(gc); + unsigned gpio = d->hwirq; + unsigned int offset = IPROC_GPIO_REG(gpio, + IPROC_GPIO_INT_CLR_OFFSET); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); + u32 val = BIT(shift); + + writel(val, chip->base + offset); +} + +/** + * iproc_gpio_irq_set_mask - mask/unmask a GPIO interrupt + * + * @d: IRQ chip data + * @unmask: mask/unmask GPIO interrupt + */ +static void iproc_gpio_irq_set_mask(struct irq_data *d, bool unmask) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio *chip = gpiochip_get_data(gc); + unsigned gpio = d->hwirq; + + iproc_set_bit(chip, IPROC_GPIO_INT_MSK_OFFSET, gpio, unmask); +} + +static void iproc_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio *chip = gpiochip_get_data(gc); + unsigned long flags; + + raw_spin_lock_irqsave(&chip->lock, flags); + iproc_gpio_irq_set_mask(d, false); + raw_spin_unlock_irqrestore(&chip->lock, flags); +} + +static void iproc_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio *chip = gpiochip_get_data(gc); + unsigned long flags; + + raw_spin_lock_irqsave(&chip->lock, flags); + iproc_gpio_irq_set_mask(d, true); + raw_spin_unlock_irqrestore(&chip->lock, flags); +} + +static int iproc_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio *chip = gpiochip_get_data(gc); + unsigned gpio = d->hwirq; + bool level_triggered = false; + bool dual_edge = false; + bool rising_or_high = false; + unsigned long flags; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + rising_or_high = true; + break; + + case IRQ_TYPE_EDGE_FALLING: + break; + + case IRQ_TYPE_EDGE_BOTH: + dual_edge = true; + break; + + case IRQ_TYPE_LEVEL_HIGH: + level_triggered = true; + rising_or_high = true; + break; + + case IRQ_TYPE_LEVEL_LOW: + level_triggered = true; + break; + + default: + dev_err(chip->dev, "invalid GPIO IRQ type 0x%x\n", + type); + return -EINVAL; + } + + raw_spin_lock_irqsave(&chip->lock, flags); + iproc_set_bit(chip, IPROC_GPIO_INT_TYPE_OFFSET, gpio, + level_triggered); + iproc_set_bit(chip, IPROC_GPIO_INT_DE_OFFSET, gpio, dual_edge); + iproc_set_bit(chip, IPROC_GPIO_INT_EDGE_OFFSET, gpio, + rising_or_high); + raw_spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, + "gpio:%u level_triggered:%d dual_edge:%d rising_or_high:%d\n", + gpio, level_triggered, dual_edge, rising_or_high); + + return 0; +} + +static struct irq_chip iproc_gpio_irq_chip = { + .name = "bcm-iproc-gpio", + .irq_ack = iproc_gpio_irq_ack, + .irq_mask = iproc_gpio_irq_mask, + .irq_unmask = iproc_gpio_irq_unmask, + .irq_set_type = iproc_gpio_irq_set_type, +}; + +/* + * Request the Iproc IOMUX pinmux controller to mux individual pins to GPIO + */ +static int iproc_gpio_request(struct gpio_chip *gc, unsigned offset) +{ + struct iproc_gpio *chip = gpiochip_get_data(gc); + unsigned gpio = gc->base + offset; + + /* not all Iproc GPIO pins can be muxed individually */ + if (!chip->pinmux_is_supported) + return 0; + + return pinctrl_gpio_request(gpio); +} + +static void iproc_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + struct iproc_gpio *chip = gpiochip_get_data(gc); + unsigned gpio = gc->base + offset; + + if (!chip->pinmux_is_supported) + return; + + pinctrl_gpio_free(gpio); +} + +static int iproc_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) +{ + struct iproc_gpio *chip = gpiochip_get_data(gc); + unsigned long flags; + + raw_spin_lock_irqsave(&chip->lock, flags); + iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, false); + raw_spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set input\n", gpio); + + return 0; +} + +static int iproc_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, + int val) +{ + struct iproc_gpio *chip = gpiochip_get_data(gc); + unsigned long flags; + + raw_spin_lock_irqsave(&chip->lock, flags); + iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, true); + iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); + raw_spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set output, value:%d\n", gpio, val); + + return 0; +} + +static void iproc_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) +{ + struct iproc_gpio *chip = gpiochip_get_data(gc); + unsigned long flags; + + raw_spin_lock_irqsave(&chip->lock, flags); + iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); + raw_spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, val); +} + +static int iproc_gpio_get(struct gpio_chip *gc, unsigned gpio) +{ + struct iproc_gpio *chip = gpiochip_get_data(gc); + unsigned int offset = IPROC_GPIO_REG(gpio, + IPROC_GPIO_DATA_IN_OFFSET); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); + + return !!(readl(chip->base + offset) & BIT(shift)); +} + +/* + * Mapping of the iProc PINCONF parameters to the generic pin configuration + * parameters + */ +static const enum pin_config_param iproc_pinconf_disable_map[] = { + [IPROC_PINCONF_DRIVE_STRENGTH] = PIN_CONFIG_DRIVE_STRENGTH, + [IPROC_PINCONF_BIAS_DISABLE] = PIN_CONFIG_BIAS_DISABLE, + [IPROC_PINCONF_BIAS_PULL_UP] = PIN_CONFIG_BIAS_PULL_UP, + [IPROC_PINCONF_BIAS_PULL_DOWN] = PIN_CONFIG_BIAS_PULL_DOWN, +}; + +static bool iproc_pinconf_param_is_disabled(struct iproc_gpio *chip, + enum pin_config_param param) +{ + unsigned int i; + + if (!chip->nr_pinconf_disable) + return false; + + for (i = 0; i < chip->nr_pinconf_disable; i++) + if (chip->pinconf_disable[i] == param) + return true; + + return false; +} + +static int iproc_pinconf_disable_map_create(struct iproc_gpio *chip, + unsigned long disable_mask) +{ + unsigned int map_size = ARRAY_SIZE(iproc_pinconf_disable_map); + unsigned int bit, nbits = 0; + + /* figure out total number of PINCONF parameters to disable */ + for_each_set_bit(bit, &disable_mask, map_size) + nbits++; + + if (!nbits) + return 0; + + /* + * Allocate an array to store PINCONF parameters that need to be + * disabled + */ + chip->pinconf_disable = devm_kcalloc(chip->dev, nbits, + sizeof(*chip->pinconf_disable), + GFP_KERNEL); + if (!chip->pinconf_disable) + return -ENOMEM; + + chip->nr_pinconf_disable = nbits; + + /* now store these parameters */ + nbits = 0; + for_each_set_bit(bit, &disable_mask, map_size) + chip->pinconf_disable[nbits++] = iproc_pinconf_disable_map[bit]; + + return 0; +} + +static int iproc_get_groups_count(struct pinctrl_dev *pctldev) +{ + return 1; +} + +/* + * Only one group: "gpio_grp", since this local pinctrl device only performs + * GPIO specific PINCONF configurations + */ +static const char *iproc_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return "gpio_grp"; +} + +static const struct pinctrl_ops iproc_pctrl_ops = { + .get_groups_count = iproc_get_groups_count, + .get_group_name = iproc_get_group_name, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int iproc_gpio_set_pull(struct iproc_gpio *chip, unsigned gpio, + bool disable, bool pull_up) +{ + void __iomem *base; + unsigned long flags; + unsigned int shift; + u32 val_1, val_2; + + raw_spin_lock_irqsave(&chip->lock, flags); + if (chip->io_ctrl_type == IOCTRL_TYPE_CDRU) { + base = chip->io_ctrl; + shift = IPROC_GPIO_SHIFT(gpio); + + val_1 = readl(base + IPROC_GPIO_PULL_UP_OFFSET); + val_2 = readl(base + IPROC_GPIO_PULL_DN_OFFSET); + if (disable) { + /* no pull-up or pull-down */ + val_1 &= ~BIT(shift); + val_2 &= ~BIT(shift); + } else if (pull_up) { + val_1 |= BIT(shift); + val_2 &= ~BIT(shift); + } else { + val_1 &= ~BIT(shift); + val_2 |= BIT(shift); + } + writel(val_1, base + IPROC_GPIO_PULL_UP_OFFSET); + writel(val_2, base + IPROC_GPIO_PULL_DN_OFFSET); + } else { + if (disable) { + iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, + false); + } else { + iproc_set_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio, + pull_up); + iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, + true); + } + } + + raw_spin_unlock_irqrestore(&chip->lock, flags); + dev_dbg(chip->dev, "gpio:%u set pullup:%d\n", gpio, pull_up); + + return 0; +} + +static void iproc_gpio_get_pull(struct iproc_gpio *chip, unsigned gpio, + bool *disable, bool *pull_up) +{ + void __iomem *base; + unsigned long flags; + unsigned int shift; + u32 val_1, val_2; + + raw_spin_lock_irqsave(&chip->lock, flags); + if (chip->io_ctrl_type == IOCTRL_TYPE_CDRU) { + base = chip->io_ctrl; + shift = IPROC_GPIO_SHIFT(gpio); + + val_1 = readl(base + IPROC_GPIO_PULL_UP_OFFSET) & BIT(shift); + val_2 = readl(base + IPROC_GPIO_PULL_DN_OFFSET) & BIT(shift); + + *pull_up = val_1 ? true : false; + *disable = (val_1 | val_2) ? false : true; + + } else { + *disable = !iproc_get_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio); + *pull_up = iproc_get_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio); + } + raw_spin_unlock_irqrestore(&chip->lock, flags); +} + +#define DRV_STRENGTH_OFFSET(gpio, bit, type) ((type) == IOCTRL_TYPE_AON ? \ + ((2 - (bit)) * 4 + IPROC_GPIO_DRV_CTRL_OFFSET) : \ + ((type) == IOCTRL_TYPE_CDRU) ? \ + ((bit) * 4 + IPROC_GPIO_DRV_CTRL_OFFSET) : \ + ((bit) * 4 + IPROC_GPIO_REG(gpio, IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET))) + +static int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio, + unsigned strength) +{ + void __iomem *base; + unsigned int i, offset, shift; + u32 val; + unsigned long flags; + + /* make sure drive strength is supported */ + if (strength < 2 || strength > 16 || (strength % 2)) + return -ENOTSUPP; + + if (chip->io_ctrl) { + base = chip->io_ctrl; + } else { + base = chip->base; + } + + shift = IPROC_GPIO_SHIFT(gpio); + + dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio, + strength); + + raw_spin_lock_irqsave(&chip->lock, flags); + strength = (strength / 2) - 1; + for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { + offset = DRV_STRENGTH_OFFSET(gpio, i, chip->io_ctrl_type); + val = readl(base + offset); + val &= ~BIT(shift); + val |= ((strength >> i) & 0x1) << shift; + writel(val, base + offset); + } + raw_spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int iproc_gpio_get_strength(struct iproc_gpio *chip, unsigned gpio, + u16 *strength) +{ + void __iomem *base; + unsigned int i, offset, shift; + u32 val; + unsigned long flags; + + if (chip->io_ctrl) { + base = chip->io_ctrl; + } else { + base = chip->base; + } + + shift = IPROC_GPIO_SHIFT(gpio); + + raw_spin_lock_irqsave(&chip->lock, flags); + *strength = 0; + for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { + offset = DRV_STRENGTH_OFFSET(gpio, i, chip->io_ctrl_type); + val = readl(base + offset) & BIT(shift); + val >>= shift; + *strength += (val << i); + } + + /* convert to mA */ + *strength = (*strength + 1) * 2; + raw_spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int iproc_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) +{ + struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + unsigned gpio = iproc_pin_to_gpio(pin); + u16 arg; + bool disable, pull_up; + int ret; + + if (iproc_pinconf_param_is_disabled(chip, param)) + return -ENOTSUPP; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); + if (disable) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_BIAS_PULL_UP: + iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); + if (!disable && pull_up) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_BIAS_PULL_DOWN: + iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); + if (!disable && !pull_up) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_DRIVE_STRENGTH: + ret = iproc_gpio_get_strength(chip, gpio, &arg); + if (ret) + return ret; + *config = pinconf_to_config_packed(param, arg); + + return 0; + + default: + return -ENOTSUPP; + } + + return -ENOTSUPP; +} + +static int iproc_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *configs, unsigned num_configs) +{ + struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param; + u32 arg; + unsigned i, gpio = iproc_pin_to_gpio(pin); + int ret = -ENOTSUPP; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + + if (iproc_pinconf_param_is_disabled(chip, param)) + return -ENOTSUPP; + + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + ret = iproc_gpio_set_pull(chip, gpio, true, false); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_BIAS_PULL_UP: + ret = iproc_gpio_set_pull(chip, gpio, false, true); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + ret = iproc_gpio_set_pull(chip, gpio, false, false); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + ret = iproc_gpio_set_strength(chip, gpio, arg); + if (ret < 0) + goto out; + break; + + default: + dev_err(chip->dev, "invalid configuration\n"); + return -ENOTSUPP; + } + } /* for each config */ + +out: + return ret; +} + +static const struct pinconf_ops iproc_pconf_ops = { + .is_generic = true, + .pin_config_get = iproc_pin_config_get, + .pin_config_set = iproc_pin_config_set, +}; + +/* + * Iproc GPIO controller supports some PINCONF related configurations such as + * pull up, pull down, and drive strength, when the pin is configured to GPIO + * + * Here a local pinctrl device is created with simple 1-to-1 pin mapping to the + * local GPIO pins + */ +static int iproc_gpio_register_pinconf(struct iproc_gpio *chip) +{ + struct pinctrl_desc *pctldesc = &chip->pctldesc; + struct pinctrl_pin_desc *pins; + struct gpio_chip *gc = &chip->gc; + int i; + + pins = devm_kcalloc(chip->dev, gc->ngpio, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + for (i = 0; i < gc->ngpio; i++) { + pins[i].number = i; + pins[i].name = devm_kasprintf(chip->dev, GFP_KERNEL, + "gpio-%d", i); + if (!pins[i].name) + return -ENOMEM; + } + + pctldesc->name = dev_name(chip->dev); + pctldesc->pctlops = &iproc_pctrl_ops; + pctldesc->pins = pins; + pctldesc->npins = gc->ngpio; + pctldesc->confops = &iproc_pconf_ops; + + chip->pctl = devm_pinctrl_register(chip->dev, pctldesc, chip); + if (IS_ERR(chip->pctl)) { + dev_err(chip->dev, "unable to register pinctrl device\n"); + return PTR_ERR(chip->pctl); + } + + return 0; +} + +static const struct of_device_id iproc_gpio_of_match[] = { + { .compatible = "brcm,iproc-gpio" }, + { .compatible = "brcm,cygnus-ccm-gpio" }, + { .compatible = "brcm,cygnus-asiu-gpio" }, + { .compatible = "brcm,cygnus-crmu-gpio" }, + { .compatible = "brcm,iproc-nsp-gpio" }, + { .compatible = "brcm,iproc-stingray-gpio" }, + { /* sentinel */ } +}; + +static int iproc_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct iproc_gpio *chip; + struct gpio_chip *gc; + u32 ngpios, pinconf_disable_mask = 0; + int irq, ret; + bool no_pinconf = false; + enum iproc_pinconf_ctrl_type io_ctrl_type = IOCTRL_TYPE_INVALID; + + /* NSP does not support drive strength config */ + if (of_device_is_compatible(dev->of_node, "brcm,iproc-nsp-gpio")) + pinconf_disable_mask = BIT(IPROC_PINCONF_DRIVE_STRENGTH); + /* Stingray does not support pinconf in this controller */ + else if (of_device_is_compatible(dev->of_node, + "brcm,iproc-stingray-gpio")) + no_pinconf = true; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = dev; + platform_set_drvdata(pdev, chip); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->base = devm_ioremap_resource(dev, res); + if (IS_ERR(chip->base)) { + dev_err(dev, "unable to map I/O memory\n"); + return PTR_ERR(chip->base); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + chip->io_ctrl = devm_ioremap_resource(dev, res); + if (IS_ERR(chip->io_ctrl)) { + dev_err(dev, "unable to map I/O memory\n"); + return PTR_ERR(chip->io_ctrl); + } + if (of_device_is_compatible(dev->of_node, + "brcm,cygnus-ccm-gpio")) + io_ctrl_type = IOCTRL_TYPE_CDRU; + else + io_ctrl_type = IOCTRL_TYPE_AON; + } + + chip->io_ctrl_type = io_ctrl_type; + + if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) { + dev_err(&pdev->dev, "missing ngpios DT property\n"); + return -ENODEV; + } + + raw_spin_lock_init(&chip->lock); + + gc = &chip->gc; + gc->base = -1; + gc->ngpio = ngpios; + chip->num_banks = (ngpios + NGPIOS_PER_BANK - 1) / NGPIOS_PER_BANK; + gc->label = dev_name(dev); + gc->parent = dev; + gc->of_node = dev->of_node; + gc->request = iproc_gpio_request; + gc->free = iproc_gpio_free; + gc->direction_input = iproc_gpio_direction_input; + gc->direction_output = iproc_gpio_direction_output; + gc->set = iproc_gpio_set; + gc->get = iproc_gpio_get; + + chip->pinmux_is_supported = of_property_read_bool(dev->of_node, + "gpio-ranges"); + + ret = gpiochip_add_data(gc, chip); + if (ret < 0) { + dev_err(dev, "unable to add GPIO chip\n"); + return ret; + } + + if (!no_pinconf) { + ret = iproc_gpio_register_pinconf(chip); + if (ret) { + dev_err(dev, "unable to register pinconf\n"); + goto err_rm_gpiochip; + } + + if (pinconf_disable_mask) { + ret = iproc_pinconf_disable_map_create(chip, + pinconf_disable_mask); + if (ret) { + dev_err(dev, + "unable to create pinconf disable map\n"); + goto err_rm_gpiochip; + } + } + } + + /* optional GPIO interrupt support */ + irq = platform_get_irq(pdev, 0); + if (irq) { + ret = gpiochip_irqchip_add(gc, &iproc_gpio_irq_chip, 0, + handle_simple_irq, IRQ_TYPE_NONE); + if (ret) { + dev_err(dev, "no GPIO irqchip\n"); + goto err_rm_gpiochip; + } + + gpiochip_set_chained_irqchip(gc, &iproc_gpio_irq_chip, irq, + iproc_gpio_irq_handler); + } + + return 0; + +err_rm_gpiochip: + gpiochip_remove(gc); + + return ret; +} + +static struct platform_driver iproc_gpio_driver = { + .driver = { + .name = "iproc-gpio", + .of_match_table = iproc_gpio_of_match, + }, + .probe = iproc_gpio_probe, +}; + +static int __init iproc_gpio_init(void) +{ + return platform_driver_register(&iproc_gpio_driver); +} +arch_initcall_sync(iproc_gpio_init); diff --git a/drivers/pinctrl/bcm/pinctrl-ns2-mux.c b/drivers/pinctrl/bcm/pinctrl-ns2-mux.c new file mode 100644 index 000000000..951090faa --- /dev/null +++ b/drivers/pinctrl/bcm/pinctrl-ns2-mux.c @@ -0,0 +1,1113 @@ +/* Copyright (C) 2016 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This file contains the Northstar2 IOMUX driver that supports group + * based PINMUX configuration. The PWM is functional only when the + * corresponding mfio pin group is selected as gpio. + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "../core.h" +#include "../pinctrl-utils.h" + +#define NS2_NUM_IOMUX 19 +#define NS2_NUM_PWM_MUX 4 + +#define NS2_PIN_MUX_BASE0 0x00 +#define NS2_PIN_MUX_BASE1 0x01 +#define NS2_PIN_CONF_BASE 0x02 +#define NS2_MUX_PAD_FUNC1_OFFSET 0x04 + +#define NS2_PIN_SRC_MASK 0x01 +#define NS2_PIN_PULL_MASK 0x03 +#define NS2_PIN_DRIVE_STRENGTH_MASK 0x07 + +#define NS2_PIN_PULL_UP 0x01 +#define NS2_PIN_PULL_DOWN 0x02 + +#define NS2_PIN_INPUT_EN_MASK 0x01 + +/* + * Northstar2 IOMUX register description + * + * @base: base address number + * @offset: register offset for mux configuration of a group + * @shift: bit shift for mux configuration of a group + * @mask: mask bits + * @alt: alternate function to set to + */ +struct ns2_mux { + unsigned int base; + unsigned int offset; + unsigned int shift; + unsigned int mask; + unsigned int alt; +}; + +/* + * Keep track of Northstar2 IOMUX configuration and prevent double + * configuration + * + * @ns2_mux: Northstar2 IOMUX register description + * @is_configured: flag to indicate whether a mux setting has already + * been configured + */ +struct ns2_mux_log { + struct ns2_mux mux; + bool is_configured; +}; + +/* + * Group based IOMUX configuration + * + * @name: name of the group + * @pins: array of pins used by this group + * @num_pins: total number of pins used by this group + * @mux: Northstar2 group based IOMUX configuration + */ +struct ns2_pin_group { + const char *name; + const unsigned int *pins; + const unsigned int num_pins; + const struct ns2_mux mux; +}; + +/* + * Northstar2 mux function and supported pin groups + * + * @name: name of the function + * @groups: array of groups that can be supported by this function + * @num_groups: total number of groups that can be supported by function + */ +struct ns2_pin_function { + const char *name; + const char * const *groups; + const unsigned int num_groups; +}; + +/* + * Northstar2 IOMUX pinctrl core + * + * @pctl: pointer to pinctrl_dev + * @dev: pointer to device + * @base0: first IOMUX register base + * @base1: second IOMUX register base + * @pinconf_base: configuration register base + * @groups: pointer to array of groups + * @num_groups: total number of groups + * @functions: pointer to array of functions + * @num_functions: total number of functions + * @mux_log: pointer to the array of mux logs + * @lock: lock to protect register access + */ +struct ns2_pinctrl { + struct pinctrl_dev *pctl; + struct device *dev; + void __iomem *base0; + void __iomem *base1; + void __iomem *pinconf_base; + + const struct ns2_pin_group *groups; + unsigned int num_groups; + + const struct ns2_pin_function *functions; + unsigned int num_functions; + + struct ns2_mux_log *mux_log; + + spinlock_t lock; +}; + +/* + * Pin configuration info + * + * @base: base address number + * @offset: register offset from base + * @src_shift: slew rate control bit shift in the register + * @input_en: input enable control bit shift + * @pull_shift: pull-up/pull-down control bit shift in the register + * @drive_shift: drive strength control bit shift in the register + */ +struct ns2_pinconf { + unsigned int base; + unsigned int offset; + unsigned int src_shift; + unsigned int input_en; + unsigned int pull_shift; + unsigned int drive_shift; +}; + +/* + * Description of a pin in Northstar2 + * + * @pin: pin number + * @name: pin name + * @pin_conf: pin configuration structure + */ +struct ns2_pin { + unsigned int pin; + char *name; + struct ns2_pinconf pin_conf; +}; + +#define NS2_PIN_DESC(p, n, b, o, s, i, pu, d) \ +{ \ + .pin = p, \ + .name = n, \ + .pin_conf = { \ + .base = b, \ + .offset = o, \ + .src_shift = s, \ + .input_en = i, \ + .pull_shift = pu, \ + .drive_shift = d, \ + } \ +} + +/* + * List of pins in Northstar2 + */ +static struct ns2_pin ns2_pins[] = { + NS2_PIN_DESC(0, "mfio_0", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(1, "mfio_1", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(2, "mfio_2", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(3, "mfio_3", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(4, "mfio_4", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(5, "mfio_5", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(6, "mfio_6", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(7, "mfio_7", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(8, "mfio_8", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(9, "mfio_9", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(10, "mfio_10", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(11, "mfio_11", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(12, "mfio_12", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(13, "mfio_13", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(14, "mfio_14", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(15, "mfio_15", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(16, "mfio_16", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(17, "mfio_17", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(18, "mfio_18", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(19, "mfio_19", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(20, "mfio_20", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(21, "mfio_21", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(22, "mfio_22", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(23, "mfio_23", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(24, "mfio_24", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(25, "mfio_25", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(26, "mfio_26", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(27, "mfio_27", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(28, "mfio_28", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(29, "mfio_29", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(30, "mfio_30", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(31, "mfio_31", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(32, "mfio_32", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(33, "mfio_33", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(34, "mfio_34", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(35, "mfio_35", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(36, "mfio_36", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(37, "mfio_37", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(38, "mfio_38", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(39, "mfio_39", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(40, "mfio_40", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(41, "mfio_41", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(42, "mfio_42", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(43, "mfio_43", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(44, "mfio_44", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(45, "mfio_45", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(46, "mfio_46", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(47, "mfio_47", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(48, "mfio_48", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(49, "mfio_49", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(50, "mfio_50", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(51, "mfio_51", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(52, "mfio_52", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(53, "mfio_53", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(54, "mfio_54", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(55, "mfio_55", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(56, "mfio_56", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(57, "mfio_57", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(58, "mfio_58", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(59, "mfio_59", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(60, "mfio_60", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(61, "mfio_61", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(62, "mfio_62", -1, 0, 0, 0, 0, 0), + NS2_PIN_DESC(63, "qspi_wp", 2, 0x0, 31, 30, 27, 24), + NS2_PIN_DESC(64, "qspi_hold", 2, 0x0, 23, 22, 19, 16), + NS2_PIN_DESC(65, "qspi_cs", 2, 0x0, 15, 14, 11, 8), + NS2_PIN_DESC(66, "qspi_sck", 2, 0x0, 7, 6, 3, 0), + NS2_PIN_DESC(67, "uart3_sin", 2, 0x04, 31, 30, 27, 24), + NS2_PIN_DESC(68, "uart3_sout", 2, 0x04, 23, 22, 19, 16), + NS2_PIN_DESC(69, "qspi_mosi", 2, 0x04, 15, 14, 11, 8), + NS2_PIN_DESC(70, "qspi_miso", 2, 0x04, 7, 6, 3, 0), + NS2_PIN_DESC(71, "spi0_fss", 2, 0x08, 31, 30, 27, 24), + NS2_PIN_DESC(72, "spi0_rxd", 2, 0x08, 23, 22, 19, 16), + NS2_PIN_DESC(73, "spi0_txd", 2, 0x08, 15, 14, 11, 8), + NS2_PIN_DESC(74, "spi0_sck", 2, 0x08, 7, 6, 3, 0), + NS2_PIN_DESC(75, "spi1_fss", 2, 0x0c, 31, 30, 27, 24), + NS2_PIN_DESC(76, "spi1_rxd", 2, 0x0c, 23, 22, 19, 16), + NS2_PIN_DESC(77, "spi1_txd", 2, 0x0c, 15, 14, 11, 8), + NS2_PIN_DESC(78, "spi1_sck", 2, 0x0c, 7, 6, 3, 0), + NS2_PIN_DESC(79, "sdio0_data7", 2, 0x10, 31, 30, 27, 24), + NS2_PIN_DESC(80, "sdio0_emmc_rst", 2, 0x10, 23, 22, 19, 16), + NS2_PIN_DESC(81, "sdio0_led_on", 2, 0x10, 15, 14, 11, 8), + NS2_PIN_DESC(82, "sdio0_wp", 2, 0x10, 7, 6, 3, 0), + NS2_PIN_DESC(83, "sdio0_data3", 2, 0x14, 31, 30, 27, 24), + NS2_PIN_DESC(84, "sdio0_data4", 2, 0x14, 23, 22, 19, 16), + NS2_PIN_DESC(85, "sdio0_data5", 2, 0x14, 15, 14, 11, 8), + NS2_PIN_DESC(86, "sdio0_data6", 2, 0x14, 7, 6, 3, 0), + NS2_PIN_DESC(87, "sdio0_cmd", 2, 0x18, 31, 30, 27, 24), + NS2_PIN_DESC(88, "sdio0_data0", 2, 0x18, 23, 22, 19, 16), + NS2_PIN_DESC(89, "sdio0_data1", 2, 0x18, 15, 14, 11, 8), + NS2_PIN_DESC(90, "sdio0_data2", 2, 0x18, 7, 6, 3, 0), + NS2_PIN_DESC(91, "sdio1_led_on", 2, 0x1c, 31, 30, 27, 24), + NS2_PIN_DESC(92, "sdio1_wp", 2, 0x1c, 23, 22, 19, 16), + NS2_PIN_DESC(93, "sdio0_cd_l", 2, 0x1c, 15, 14, 11, 8), + NS2_PIN_DESC(94, "sdio0_clk", 2, 0x1c, 7, 6, 3, 0), + NS2_PIN_DESC(95, "sdio1_data5", 2, 0x20, 31, 30, 27, 24), + NS2_PIN_DESC(96, "sdio1_data6", 2, 0x20, 23, 22, 19, 16), + NS2_PIN_DESC(97, "sdio1_data7", 2, 0x20, 15, 14, 11, 8), + NS2_PIN_DESC(98, "sdio1_emmc_rst", 2, 0x20, 7, 6, 3, 0), + NS2_PIN_DESC(99, "sdio1_data1", 2, 0x24, 31, 30, 27, 24), + NS2_PIN_DESC(100, "sdio1_data2", 2, 0x24, 23, 22, 19, 16), + NS2_PIN_DESC(101, "sdio1_data3", 2, 0x24, 15, 14, 11, 8), + NS2_PIN_DESC(102, "sdio1_data4", 2, 0x24, 7, 6, 3, 0), + NS2_PIN_DESC(103, "sdio1_cd_l", 2, 0x28, 31, 30, 27, 24), + NS2_PIN_DESC(104, "sdio1_clk", 2, 0x28, 23, 22, 19, 16), + NS2_PIN_DESC(105, "sdio1_cmd", 2, 0x28, 15, 14, 11, 8), + NS2_PIN_DESC(106, "sdio1_data0", 2, 0x28, 7, 6, 3, 0), + NS2_PIN_DESC(107, "ext_mdio_0", 2, 0x2c, 15, 14, 11, 8), + NS2_PIN_DESC(108, "ext_mdc_0", 2, 0x2c, 7, 6, 3, 0), + NS2_PIN_DESC(109, "usb3_p1_vbus_ppc", 2, 0x34, 31, 30, 27, 24), + NS2_PIN_DESC(110, "usb3_p1_overcurrent", 2, 0x34, 23, 22, 19, 16), + NS2_PIN_DESC(111, "usb3_p0_vbus_ppc", 2, 0x34, 15, 14, 11, 8), + NS2_PIN_DESC(112, "usb3_p0_overcurrent", 2, 0x34, 7, 6, 3, 0), + NS2_PIN_DESC(113, "usb2_presence_indication", 2, 0x38, 31, 30, 27, 24), + NS2_PIN_DESC(114, "usb2_vbus_present", 2, 0x38, 23, 22, 19, 16), + NS2_PIN_DESC(115, "usb2_vbus_ppc", 2, 0x38, 15, 14, 11, 8), + NS2_PIN_DESC(116, "usb2_overcurrent", 2, 0x38, 7, 6, 3, 0), + NS2_PIN_DESC(117, "sata_led1", 2, 0x3c, 15, 14, 11, 8), + NS2_PIN_DESC(118, "sata_led0", 2, 0x3c, 7, 6, 3, 0), +}; + +/* + * List of groups of pins + */ + +static const unsigned int nand_pins[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; +static const unsigned int nor_data_pins[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}; + +static const unsigned int gpio_0_1_pins[] = {24, 25}; +static const unsigned int pwm_0_pins[] = {24}; +static const unsigned int pwm_1_pins[] = {25}; + +static const unsigned int uart1_ext_clk_pins[] = {26}; +static const unsigned int nor_adv_pins[] = {26}; + +static const unsigned int gpio_2_5_pins[] = {27, 28, 29, 30}; +static const unsigned int pcie_ab1_clk_wak_pins[] = {27, 28, 29, 30}; +static const unsigned int nor_addr_0_3_pins[] = {27, 28, 29, 30}; +static const unsigned int pwm_2_pins[] = {27}; +static const unsigned int pwm_3_pins[] = {28}; + +static const unsigned int gpio_6_7_pins[] = {31, 32}; +static const unsigned int pcie_a3_clk_wak_pins[] = {31, 32}; +static const unsigned int nor_addr_4_5_pins[] = {31, 32}; + +static const unsigned int gpio_8_9_pins[] = {33, 34}; +static const unsigned int pcie_b3_clk_wak_pins[] = {33, 34}; +static const unsigned int nor_addr_6_7_pins[] = {33, 34}; + +static const unsigned int gpio_10_11_pins[] = {35, 36}; +static const unsigned int pcie_b2_clk_wak_pins[] = {35, 36}; +static const unsigned int nor_addr_8_9_pins[] = {35, 36}; + +static const unsigned int gpio_12_13_pins[] = {37, 38}; +static const unsigned int pcie_a2_clk_wak_pins[] = {37, 38}; +static const unsigned int nor_addr_10_11_pins[] = {37, 38}; + +static const unsigned int gpio_14_17_pins[] = {39, 40, 41, 42}; +static const unsigned int uart0_modem_pins[] = {39, 40, 41, 42}; +static const unsigned int nor_addr_12_15_pins[] = {39, 40, 41, 42}; + +static const unsigned int gpio_18_19_pins[] = {43, 44}; +static const unsigned int uart0_rts_cts_pins[] = {43, 44}; + +static const unsigned int gpio_20_21_pins[] = {45, 46}; +static const unsigned int uart0_in_out_pins[] = {45, 46}; + +static const unsigned int gpio_22_23_pins[] = {47, 48}; +static const unsigned int uart1_dcd_dsr_pins[] = {47, 48}; + +static const unsigned int gpio_24_25_pins[] = {49, 50}; +static const unsigned int uart1_ri_dtr_pins[] = {49, 50}; + +static const unsigned int gpio_26_27_pins[] = {51, 52}; +static const unsigned int uart1_rts_cts_pins[] = {51, 52}; + +static const unsigned int gpio_28_29_pins[] = {53, 54}; +static const unsigned int uart1_in_out_pins[] = {53, 54}; + +static const unsigned int gpio_30_31_pins[] = {55, 56}; +static const unsigned int uart2_rts_cts_pins[] = {55, 56}; + +#define NS2_PIN_GROUP(group_name, ba, off, sh, ma, al) \ +{ \ + .name = __stringify(group_name) "_grp", \ + .pins = group_name ## _pins, \ + .num_pins = ARRAY_SIZE(group_name ## _pins), \ + .mux = { \ + .base = ba, \ + .offset = off, \ + .shift = sh, \ + .mask = ma, \ + .alt = al, \ + } \ +} + +/* + * List of Northstar2 pin groups + */ +static const struct ns2_pin_group ns2_pin_groups[] = { + NS2_PIN_GROUP(nand, 0, 0, 31, 1, 0), + NS2_PIN_GROUP(nor_data, 0, 0, 31, 1, 1), + NS2_PIN_GROUP(gpio_0_1, 0, 0, 31, 1, 0), + + NS2_PIN_GROUP(uart1_ext_clk, 0, 4, 30, 3, 1), + NS2_PIN_GROUP(nor_adv, 0, 4, 30, 3, 2), + + NS2_PIN_GROUP(gpio_2_5, 0, 4, 28, 3, 0), + NS2_PIN_GROUP(pcie_ab1_clk_wak, 0, 4, 28, 3, 1), + NS2_PIN_GROUP(nor_addr_0_3, 0, 4, 28, 3, 2), + + NS2_PIN_GROUP(gpio_6_7, 0, 4, 26, 3, 0), + NS2_PIN_GROUP(pcie_a3_clk_wak, 0, 4, 26, 3, 1), + NS2_PIN_GROUP(nor_addr_4_5, 0, 4, 26, 3, 2), + + NS2_PIN_GROUP(gpio_8_9, 0, 4, 24, 3, 0), + NS2_PIN_GROUP(pcie_b3_clk_wak, 0, 4, 24, 3, 1), + NS2_PIN_GROUP(nor_addr_6_7, 0, 4, 24, 3, 2), + + NS2_PIN_GROUP(gpio_10_11, 0, 4, 22, 3, 0), + NS2_PIN_GROUP(pcie_b2_clk_wak, 0, 4, 22, 3, 1), + NS2_PIN_GROUP(nor_addr_8_9, 0, 4, 22, 3, 2), + + NS2_PIN_GROUP(gpio_12_13, 0, 4, 20, 3, 0), + NS2_PIN_GROUP(pcie_a2_clk_wak, 0, 4, 20, 3, 1), + NS2_PIN_GROUP(nor_addr_10_11, 0, 4, 20, 3, 2), + + NS2_PIN_GROUP(gpio_14_17, 0, 4, 18, 3, 0), + NS2_PIN_GROUP(uart0_modem, 0, 4, 18, 3, 1), + NS2_PIN_GROUP(nor_addr_12_15, 0, 4, 18, 3, 2), + + NS2_PIN_GROUP(gpio_18_19, 0, 4, 16, 3, 0), + NS2_PIN_GROUP(uart0_rts_cts, 0, 4, 16, 3, 1), + + NS2_PIN_GROUP(gpio_20_21, 0, 4, 14, 3, 0), + NS2_PIN_GROUP(uart0_in_out, 0, 4, 14, 3, 1), + + NS2_PIN_GROUP(gpio_22_23, 0, 4, 12, 3, 0), + NS2_PIN_GROUP(uart1_dcd_dsr, 0, 4, 12, 3, 1), + + NS2_PIN_GROUP(gpio_24_25, 0, 4, 10, 3, 0), + NS2_PIN_GROUP(uart1_ri_dtr, 0, 4, 10, 3, 1), + + NS2_PIN_GROUP(gpio_26_27, 0, 4, 8, 3, 0), + NS2_PIN_GROUP(uart1_rts_cts, 0, 4, 8, 3, 1), + + NS2_PIN_GROUP(gpio_28_29, 0, 4, 6, 3, 0), + NS2_PIN_GROUP(uart1_in_out, 0, 4, 6, 3, 1), + + NS2_PIN_GROUP(gpio_30_31, 0, 4, 4, 3, 0), + NS2_PIN_GROUP(uart2_rts_cts, 0, 4, 4, 3, 1), + + NS2_PIN_GROUP(pwm_0, 1, 0, 0, 1, 1), + NS2_PIN_GROUP(pwm_1, 1, 0, 1, 1, 1), + NS2_PIN_GROUP(pwm_2, 1, 0, 2, 1, 1), + NS2_PIN_GROUP(pwm_3, 1, 0, 3, 1, 1), +}; + +/* + * List of groups supported by functions + */ + +static const char * const nand_grps[] = {"nand_grp"}; + +static const char * const nor_grps[] = {"nor_data_grp", "nor_adv_grp", + "nor_addr_0_3_grp", "nor_addr_4_5_grp", "nor_addr_6_7_grp", + "nor_addr_8_9_grp", "nor_addr_10_11_grp", "nor_addr_12_15_grp"}; + +static const char * const gpio_grps[] = {"gpio_0_1_grp", "gpio_2_5_grp", + "gpio_6_7_grp", "gpio_8_9_grp", "gpio_10_11_grp", "gpio_12_13_grp", + "gpio_14_17_grp", "gpio_18_19_grp", "gpio_20_21_grp", "gpio_22_23_grp", + "gpio_24_25_grp", "gpio_26_27_grp", "gpio_28_29_grp", + "gpio_30_31_grp"}; + +static const char * const pcie_grps[] = {"pcie_ab1_clk_wak_grp", + "pcie_a3_clk_wak_grp", "pcie_b3_clk_wak_grp", "pcie_b2_clk_wak_grp", + "pcie_a2_clk_wak_grp"}; + +static const char * const uart0_grps[] = {"uart0_modem_grp", + "uart0_rts_cts_grp", "uart0_in_out_grp"}; + +static const char * const uart1_grps[] = {"uart1_ext_clk_grp", + "uart1_dcd_dsr_grp", "uart1_ri_dtr_grp", "uart1_rts_cts_grp", + "uart1_in_out_grp"}; + +static const char * const uart2_grps[] = {"uart2_rts_cts_grp"}; + +static const char * const pwm_grps[] = {"pwm_0_grp", "pwm_1_grp", + "pwm_2_grp", "pwm_3_grp"}; + +#define NS2_PIN_FUNCTION(func) \ +{ \ + .name = #func, \ + .groups = func ## _grps, \ + .num_groups = ARRAY_SIZE(func ## _grps), \ +} + +/* + * List of supported functions + */ +static const struct ns2_pin_function ns2_pin_functions[] = { + NS2_PIN_FUNCTION(nand), + NS2_PIN_FUNCTION(nor), + NS2_PIN_FUNCTION(gpio), + NS2_PIN_FUNCTION(pcie), + NS2_PIN_FUNCTION(uart0), + NS2_PIN_FUNCTION(uart1), + NS2_PIN_FUNCTION(uart2), + NS2_PIN_FUNCTION(pwm), +}; + +static int ns2_get_groups_count(struct pinctrl_dev *pctrl_dev) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->num_groups; +} + +static const char *ns2_get_group_name(struct pinctrl_dev *pctrl_dev, + unsigned int selector) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->groups[selector].name; +} + +static int ns2_get_group_pins(struct pinctrl_dev *pctrl_dev, + unsigned int selector, const unsigned int **pins, + unsigned int *num_pins) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + *pins = pinctrl->groups[selector].pins; + *num_pins = pinctrl->groups[selector].num_pins; + + return 0; +} + +static void ns2_pin_dbg_show(struct pinctrl_dev *pctrl_dev, + struct seq_file *s, unsigned int offset) +{ + seq_printf(s, " %s", dev_name(pctrl_dev->dev)); +} + +static const struct pinctrl_ops ns2_pinctrl_ops = { + .get_groups_count = ns2_get_groups_count, + .get_group_name = ns2_get_group_name, + .get_group_pins = ns2_get_group_pins, + .pin_dbg_show = ns2_pin_dbg_show, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int ns2_get_functions_count(struct pinctrl_dev *pctrl_dev) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->num_functions; +} + +static const char *ns2_get_function_name(struct pinctrl_dev *pctrl_dev, + unsigned int selector) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->functions[selector].name; +} + +static int ns2_get_function_groups(struct pinctrl_dev *pctrl_dev, + unsigned int selector, + const char * const **groups, + unsigned int * const num_groups) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + *groups = pinctrl->functions[selector].groups; + *num_groups = pinctrl->functions[selector].num_groups; + + return 0; +} + +static int ns2_pinmux_set(struct ns2_pinctrl *pinctrl, + const struct ns2_pin_function *func, + const struct ns2_pin_group *grp, + struct ns2_mux_log *mux_log) +{ + const struct ns2_mux *mux = &grp->mux; + int i; + u32 val, mask; + unsigned long flags; + void __iomem *base_address; + + for (i = 0; i < NS2_NUM_IOMUX; i++) { + if ((mux->shift != mux_log[i].mux.shift) || + (mux->base != mux_log[i].mux.base) || + (mux->offset != mux_log[i].mux.offset)) + continue; + + /* if this is a new configuration, just do it! */ + if (!mux_log[i].is_configured) + break; + + /* + * IOMUX has been configured previously and one is trying to + * configure it to a different function + */ + if (mux_log[i].mux.alt != mux->alt) { + dev_err(pinctrl->dev, + "double configuration error detected!\n"); + dev_err(pinctrl->dev, "func:%s grp:%s\n", + func->name, grp->name); + return -EINVAL; + } + + return 0; + } + if (i == NS2_NUM_IOMUX) + return -EINVAL; + + mask = mux->mask; + mux_log[i].mux.alt = mux->alt; + mux_log[i].is_configured = true; + + switch (mux->base) { + case NS2_PIN_MUX_BASE0: + base_address = pinctrl->base0; + break; + + case NS2_PIN_MUX_BASE1: + base_address = pinctrl->base1; + break; + + default: + return -EINVAL; + } + + spin_lock_irqsave(&pinctrl->lock, flags); + val = readl(base_address + grp->mux.offset); + val &= ~(mask << grp->mux.shift); + val |= grp->mux.alt << grp->mux.shift; + writel(val, (base_address + grp->mux.offset)); + spin_unlock_irqrestore(&pinctrl->lock, flags); + + return 0; +} + +static int ns2_pinmux_enable(struct pinctrl_dev *pctrl_dev, + unsigned int func_select, unsigned int grp_select) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + const struct ns2_pin_function *func; + const struct ns2_pin_group *grp; + + if (grp_select >= pinctrl->num_groups || + func_select >= pinctrl->num_functions) + return -EINVAL; + + func = &pinctrl->functions[func_select]; + grp = &pinctrl->groups[grp_select]; + + dev_dbg(pctrl_dev->dev, "func:%u name:%s grp:%u name:%s\n", + func_select, func->name, grp_select, grp->name); + + dev_dbg(pctrl_dev->dev, "offset:0x%08x shift:%u alt:%u\n", + grp->mux.offset, grp->mux.shift, grp->mux.alt); + + return ns2_pinmux_set(pinctrl, func, grp, pinctrl->mux_log); +} + +static int ns2_pin_set_enable(struct pinctrl_dev *pctrldev, unsigned int pin, + u16 enable) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrldev); + struct ns2_pin *pin_data = pctrldev->desc->pins[pin].drv_data; + unsigned long flags; + u32 val; + void __iomem *base_address; + + base_address = pinctrl->pinconf_base; + spin_lock_irqsave(&pinctrl->lock, flags); + val = readl(base_address + pin_data->pin_conf.offset); + val &= ~(NS2_PIN_SRC_MASK << pin_data->pin_conf.input_en); + + if (!enable) + val |= NS2_PIN_INPUT_EN_MASK << pin_data->pin_conf.input_en; + + writel(val, (base_address + pin_data->pin_conf.offset)); + spin_unlock_irqrestore(&pinctrl->lock, flags); + + dev_dbg(pctrldev->dev, "pin:%u set enable:%d\n", pin, enable); + return 0; +} + +static int ns2_pin_get_enable(struct pinctrl_dev *pctrldev, unsigned int pin) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrldev); + struct ns2_pin *pin_data = pctrldev->desc->pins[pin].drv_data; + unsigned long flags; + int enable; + + spin_lock_irqsave(&pinctrl->lock, flags); + enable = readl(pinctrl->pinconf_base + pin_data->pin_conf.offset); + enable = (enable >> pin_data->pin_conf.input_en) & + NS2_PIN_INPUT_EN_MASK; + spin_unlock_irqrestore(&pinctrl->lock, flags); + + if (!enable) + enable = NS2_PIN_INPUT_EN_MASK; + else + enable = 0; + + dev_dbg(pctrldev->dev, "pin:%u get disable:%d\n", pin, enable); + return enable; +} + +static int ns2_pin_set_slew(struct pinctrl_dev *pctrldev, unsigned int pin, + u32 slew) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrldev); + struct ns2_pin *pin_data = pctrldev->desc->pins[pin].drv_data; + unsigned long flags; + u32 val; + void __iomem *base_address; + + base_address = pinctrl->pinconf_base; + spin_lock_irqsave(&pinctrl->lock, flags); + val = readl(base_address + pin_data->pin_conf.offset); + val &= ~(NS2_PIN_SRC_MASK << pin_data->pin_conf.src_shift); + + if (slew) + val |= NS2_PIN_SRC_MASK << pin_data->pin_conf.src_shift; + + writel(val, (base_address + pin_data->pin_conf.offset)); + spin_unlock_irqrestore(&pinctrl->lock, flags); + + dev_dbg(pctrldev->dev, "pin:%u set slew:%d\n", pin, slew); + return 0; +} + +static int ns2_pin_get_slew(struct pinctrl_dev *pctrldev, unsigned int pin, + u16 *slew) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrldev); + struct ns2_pin *pin_data = pctrldev->desc->pins[pin].drv_data; + unsigned long flags; + u32 val; + + spin_lock_irqsave(&pinctrl->lock, flags); + val = readl(pinctrl->pinconf_base + pin_data->pin_conf.offset); + *slew = (val >> pin_data->pin_conf.src_shift) & NS2_PIN_SRC_MASK; + spin_unlock_irqrestore(&pinctrl->lock, flags); + + dev_dbg(pctrldev->dev, "pin:%u get slew:%d\n", pin, *slew); + return 0; +} + +static int ns2_pin_set_pull(struct pinctrl_dev *pctrldev, unsigned int pin, + bool pull_up, bool pull_down) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrldev); + struct ns2_pin *pin_data = pctrldev->desc->pins[pin].drv_data; + unsigned long flags; + u32 val; + void __iomem *base_address; + + base_address = pinctrl->pinconf_base; + spin_lock_irqsave(&pinctrl->lock, flags); + val = readl(base_address + pin_data->pin_conf.offset); + val &= ~(NS2_PIN_PULL_MASK << pin_data->pin_conf.pull_shift); + + if (pull_up == true) + val |= NS2_PIN_PULL_UP << pin_data->pin_conf.pull_shift; + if (pull_down == true) + val |= NS2_PIN_PULL_DOWN << pin_data->pin_conf.pull_shift; + writel(val, (base_address + pin_data->pin_conf.offset)); + spin_unlock_irqrestore(&pinctrl->lock, flags); + + dev_dbg(pctrldev->dev, "pin:%u set pullup:%d pulldown: %d\n", + pin, pull_up, pull_down); + return 0; +} + +static void ns2_pin_get_pull(struct pinctrl_dev *pctrldev, + unsigned int pin, bool *pull_up, + bool *pull_down) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrldev); + struct ns2_pin *pin_data = pctrldev->desc->pins[pin].drv_data; + unsigned long flags; + u32 val; + + spin_lock_irqsave(&pinctrl->lock, flags); + val = readl(pinctrl->pinconf_base + pin_data->pin_conf.offset); + val = (val >> pin_data->pin_conf.pull_shift) & NS2_PIN_PULL_MASK; + *pull_up = false; + *pull_down = false; + + if (val == NS2_PIN_PULL_UP) + *pull_up = true; + + if (val == NS2_PIN_PULL_DOWN) + *pull_down = true; + spin_unlock_irqrestore(&pinctrl->lock, flags); +} + +static int ns2_pin_set_strength(struct pinctrl_dev *pctrldev, unsigned int pin, + u32 strength) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrldev); + struct ns2_pin *pin_data = pctrldev->desc->pins[pin].drv_data; + u32 val; + unsigned long flags; + void __iomem *base_address; + + /* make sure drive strength is supported */ + if (strength < 2 || strength > 16 || (strength % 2)) + return -ENOTSUPP; + + base_address = pinctrl->pinconf_base; + spin_lock_irqsave(&pinctrl->lock, flags); + val = readl(base_address + pin_data->pin_conf.offset); + val &= ~(NS2_PIN_DRIVE_STRENGTH_MASK << pin_data->pin_conf.drive_shift); + val |= ((strength / 2) - 1) << pin_data->pin_conf.drive_shift; + writel(val, (base_address + pin_data->pin_conf.offset)); + spin_unlock_irqrestore(&pinctrl->lock, flags); + + dev_dbg(pctrldev->dev, "pin:%u set drive strength:%d mA\n", + pin, strength); + return 0; +} + +static int ns2_pin_get_strength(struct pinctrl_dev *pctrldev, unsigned int pin, + u16 *strength) +{ + struct ns2_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrldev); + struct ns2_pin *pin_data = pctrldev->desc->pins[pin].drv_data; + u32 val; + unsigned long flags; + + spin_lock_irqsave(&pinctrl->lock, flags); + val = readl(pinctrl->pinconf_base + pin_data->pin_conf.offset); + *strength = (val >> pin_data->pin_conf.drive_shift) & + NS2_PIN_DRIVE_STRENGTH_MASK; + *strength = (*strength + 1) * 2; + spin_unlock_irqrestore(&pinctrl->lock, flags); + + dev_dbg(pctrldev->dev, "pin:%u get drive strength:%d mA\n", + pin, *strength); + return 0; +} + +static int ns2_pin_config_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + struct ns2_pin *pin_data = pctldev->desc->pins[pin].drv_data; + enum pin_config_param param = pinconf_to_config_param(*config); + bool pull_up, pull_down; + u16 arg = 0; + int ret; + + if (pin_data->pin_conf.base == -1) + return -ENOTSUPP; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + ns2_pin_get_pull(pctldev, pin, &pull_up, &pull_down); + if ((pull_up == false) && (pull_down == false)) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_BIAS_PULL_UP: + ns2_pin_get_pull(pctldev, pin, &pull_up, &pull_down); + if (pull_up) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_BIAS_PULL_DOWN: + ns2_pin_get_pull(pctldev, pin, &pull_up, &pull_down); + if (pull_down) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_DRIVE_STRENGTH: + ret = ns2_pin_get_strength(pctldev, pin, &arg); + if (ret) + return ret; + *config = pinconf_to_config_packed(param, arg); + return 0; + + case PIN_CONFIG_SLEW_RATE: + ret = ns2_pin_get_slew(pctldev, pin, &arg); + if (ret) + return ret; + *config = pinconf_to_config_packed(param, arg); + return 0; + + case PIN_CONFIG_INPUT_ENABLE: + ret = ns2_pin_get_enable(pctldev, pin); + if (ret) + return 0; + else + return -EINVAL; + + default: + return -ENOTSUPP; + } +} + +static int ns2_pin_config_set(struct pinctrl_dev *pctrldev, unsigned int pin, + unsigned long *configs, unsigned int num_configs) +{ + struct ns2_pin *pin_data = pctrldev->desc->pins[pin].drv_data; + enum pin_config_param param; + unsigned int i; + u32 arg; + int ret = -ENOTSUPP; + + if (pin_data->pin_conf.base == -1) + return -ENOTSUPP; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + ret = ns2_pin_set_pull(pctrldev, pin, false, false); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_BIAS_PULL_UP: + ret = ns2_pin_set_pull(pctrldev, pin, true, false); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + ret = ns2_pin_set_pull(pctrldev, pin, false, true); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + ret = ns2_pin_set_strength(pctrldev, pin, arg); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_SLEW_RATE: + ret = ns2_pin_set_slew(pctrldev, pin, arg); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_INPUT_ENABLE: + ret = ns2_pin_set_enable(pctrldev, pin, arg); + if (ret < 0) + goto out; + break; + + default: + dev_err(pctrldev->dev, "invalid configuration\n"); + return -ENOTSUPP; + } + } +out: + return ret; +} +static const struct pinmux_ops ns2_pinmux_ops = { + .get_functions_count = ns2_get_functions_count, + .get_function_name = ns2_get_function_name, + .get_function_groups = ns2_get_function_groups, + .set_mux = ns2_pinmux_enable, +}; + +static const struct pinconf_ops ns2_pinconf_ops = { + .is_generic = true, + .pin_config_get = ns2_pin_config_get, + .pin_config_set = ns2_pin_config_set, +}; + +static struct pinctrl_desc ns2_pinctrl_desc = { + .name = "ns2-pinmux", + .pctlops = &ns2_pinctrl_ops, + .pmxops = &ns2_pinmux_ops, + .confops = &ns2_pinconf_ops, +}; + +static int ns2_mux_log_init(struct ns2_pinctrl *pinctrl) +{ + struct ns2_mux_log *log; + unsigned int i; + + pinctrl->mux_log = devm_kcalloc(pinctrl->dev, NS2_NUM_IOMUX, + sizeof(struct ns2_mux_log), + GFP_KERNEL); + if (!pinctrl->mux_log) + return -ENOMEM; + + for (i = 0; i < NS2_NUM_IOMUX; i++) + pinctrl->mux_log[i].is_configured = false; + /* Group 0 uses bit 31 in the IOMUX_PAD_FUNCTION_0 register */ + log = &pinctrl->mux_log[0]; + log->mux.base = NS2_PIN_MUX_BASE0; + log->mux.offset = 0; + log->mux.shift = 31; + log->mux.alt = 0; + + /* + * Groups 1 through 14 use two bits each in the + * IOMUX_PAD_FUNCTION_1 register starting with + * bit position 30. + */ + for (i = 1; i < (NS2_NUM_IOMUX - NS2_NUM_PWM_MUX); i++) { + log = &pinctrl->mux_log[i]; + log->mux.base = NS2_PIN_MUX_BASE0; + log->mux.offset = NS2_MUX_PAD_FUNC1_OFFSET; + log->mux.shift = 32 - (i * 2); + log->mux.alt = 0; + } + + /* + * Groups 15 through 18 use one bit each in the + * AUX_SEL register. + */ + for (i = 0; i < NS2_NUM_PWM_MUX; i++) { + log = &pinctrl->mux_log[(NS2_NUM_IOMUX - NS2_NUM_PWM_MUX) + i]; + log->mux.base = NS2_PIN_MUX_BASE1; + log->mux.offset = 0; + log->mux.shift = i; + log->mux.alt = 0; + } + return 0; +} + +static int ns2_pinmux_probe(struct platform_device *pdev) +{ + struct ns2_pinctrl *pinctrl; + struct resource *res; + int i, ret; + struct pinctrl_pin_desc *pins; + unsigned int num_pins = ARRAY_SIZE(ns2_pins); + + pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL); + if (!pinctrl) + return -ENOMEM; + + pinctrl->dev = &pdev->dev; + platform_set_drvdata(pdev, pinctrl); + spin_lock_init(&pinctrl->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pinctrl->base0 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pinctrl->base0)) + return PTR_ERR(pinctrl->base0); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + pinctrl->base1 = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!pinctrl->base1) { + dev_err(&pdev->dev, "unable to map I/O space\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + pinctrl->pinconf_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pinctrl->pinconf_base)) + return PTR_ERR(pinctrl->pinconf_base); + + ret = ns2_mux_log_init(pinctrl); + if (ret) { + dev_err(&pdev->dev, "unable to initialize IOMUX log\n"); + return ret; + } + + pins = devm_kcalloc(&pdev->dev, num_pins, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + for (i = 0; i < num_pins; i++) { + pins[i].number = ns2_pins[i].pin; + pins[i].name = ns2_pins[i].name; + pins[i].drv_data = &ns2_pins[i]; + } + + pinctrl->groups = ns2_pin_groups; + pinctrl->num_groups = ARRAY_SIZE(ns2_pin_groups); + pinctrl->functions = ns2_pin_functions; + pinctrl->num_functions = ARRAY_SIZE(ns2_pin_functions); + ns2_pinctrl_desc.pins = pins; + ns2_pinctrl_desc.npins = num_pins; + + pinctrl->pctl = pinctrl_register(&ns2_pinctrl_desc, &pdev->dev, + pinctrl); + if (IS_ERR(pinctrl->pctl)) { + dev_err(&pdev->dev, "unable to register IOMUX pinctrl\n"); + return PTR_ERR(pinctrl->pctl); + } + + return 0; +} + +static const struct of_device_id ns2_pinmux_of_match[] = { + {.compatible = "brcm,ns2-pinmux"}, + { } +}; + +static struct platform_driver ns2_pinmux_driver = { + .driver = { + .name = "ns2-pinmux", + .of_match_table = ns2_pinmux_of_match, + }, + .probe = ns2_pinmux_probe, +}; + +static int __init ns2_pinmux_init(void) +{ + return platform_driver_register(&ns2_pinmux_driver); +} +arch_initcall(ns2_pinmux_init); diff --git a/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c b/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c new file mode 100644 index 000000000..e67ae5202 --- /dev/null +++ b/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c @@ -0,0 +1,731 @@ +/* + * Copyright (C) 2014-2017 Broadcom + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * This file contains the Broadcom Northstar Plus (NSP) GPIO driver that + * supports the chipCommonA GPIO controller. Basic PINCONF such as bias, + * pull up/down, slew and drive strength are also supported in this driver. + * + * Pins from the chipCommonA GPIO can be individually muxed to GPIO function, + * through the interaction with the NSP IOMUX controller. + */ + +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/slab.h> + +#include "../pinctrl-utils.h" + +#define NSP_CHIP_A_INT_STATUS 0x00 +#define NSP_CHIP_A_INT_MASK 0x04 +#define NSP_GPIO_DATA_IN 0x40 +#define NSP_GPIO_DATA_OUT 0x44 +#define NSP_GPIO_OUT_EN 0x48 +#define NSP_GPIO_INT_POLARITY 0x50 +#define NSP_GPIO_INT_MASK 0x54 +#define NSP_GPIO_EVENT 0x58 +#define NSP_GPIO_EVENT_INT_MASK 0x5c +#define NSP_GPIO_EVENT_INT_POLARITY 0x64 +#define NSP_CHIP_A_GPIO_INT_BIT 0x01 + +/* I/O parameters offset for chipcommon A GPIO */ +#define NSP_GPIO_DRV_CTRL 0x00 +#define NSP_GPIO_HYSTERESIS_EN 0x10 +#define NSP_GPIO_SLEW_RATE_EN 0x14 +#define NSP_PULL_UP_EN 0x18 +#define NSP_PULL_DOWN_EN 0x1c +#define GPIO_DRV_STRENGTH_BITS 0x03 + +/* + * nsp GPIO core + * + * @dev: pointer to device + * @base: I/O register base for nsp GPIO controller + * @io_ctrl: I/O register base for PINCONF support outside the GPIO block + * @gc: GPIO chip + * @pctl: pointer to pinctrl_dev + * @pctldesc: pinctrl descriptor + * @irq_domain: pointer to irq domain + * @lock: lock to protect access to I/O registers + */ +struct nsp_gpio { + struct device *dev; + void __iomem *base; + void __iomem *io_ctrl; + struct gpio_chip gc; + struct pinctrl_dev *pctl; + struct pinctrl_desc pctldesc; + struct irq_domain *irq_domain; + raw_spinlock_t lock; +}; + +enum base_type { + REG, + IO_CTRL +}; + +/* + * Mapping from PINCONF pins to GPIO pins is 1-to-1 + */ +static inline unsigned nsp_pin_to_gpio(unsigned pin) +{ + return pin; +} + +/* + * nsp_set_bit - set or clear one bit (corresponding to the GPIO pin) in a + * nsp GPIO register + * + * @nsp_gpio: nsp GPIO device + * @base_type: reg base to modify + * @reg: register offset + * @gpio: GPIO pin + * @set: set or clear + */ +static inline void nsp_set_bit(struct nsp_gpio *chip, enum base_type address, + unsigned int reg, unsigned gpio, bool set) +{ + u32 val; + void __iomem *base_address; + + if (address == IO_CTRL) + base_address = chip->io_ctrl; + else + base_address = chip->base; + + val = readl(base_address + reg); + if (set) + val |= BIT(gpio); + else + val &= ~BIT(gpio); + + writel(val, base_address + reg); +} + +/* + * nsp_get_bit - get one bit (corresponding to the GPIO pin) in a + * nsp GPIO register + */ +static inline bool nsp_get_bit(struct nsp_gpio *chip, enum base_type address, + unsigned int reg, unsigned gpio) +{ + if (address == IO_CTRL) + return !!(readl(chip->io_ctrl + reg) & BIT(gpio)); + else + return !!(readl(chip->base + reg) & BIT(gpio)); +} + +static irqreturn_t nsp_gpio_irq_handler(int irq, void *data) +{ + struct nsp_gpio *chip = (struct nsp_gpio *)data; + struct gpio_chip gc = chip->gc; + int bit; + unsigned long int_bits = 0; + u32 int_status; + + /* go through the entire GPIOs and handle all interrupts */ + int_status = readl(chip->base + NSP_CHIP_A_INT_STATUS); + if (int_status & NSP_CHIP_A_GPIO_INT_BIT) { + unsigned int event, level; + + /* Get level and edge interrupts */ + event = readl(chip->base + NSP_GPIO_EVENT_INT_MASK) & + readl(chip->base + NSP_GPIO_EVENT); + level = readl(chip->base + NSP_GPIO_DATA_IN) ^ + readl(chip->base + NSP_GPIO_INT_POLARITY); + level &= readl(chip->base + NSP_GPIO_INT_MASK); + int_bits = level | event; + + for_each_set_bit(bit, &int_bits, gc.ngpio) { + /* + * Clear the interrupt before invoking the + * handler, so we do not leave any window + */ + writel(BIT(bit), chip->base + NSP_GPIO_EVENT); + generic_handle_irq( + irq_linear_revmap(chip->irq_domain, bit)); + } + } + + return int_bits ? IRQ_HANDLED : IRQ_NONE; +} + +static void nsp_gpio_irq_ack(struct irq_data *d) +{ + struct nsp_gpio *chip = irq_data_get_irq_chip_data(d); + unsigned gpio = d->hwirq; + u32 val = BIT(gpio); + u32 trigger_type; + + trigger_type = irq_get_trigger_type(d->irq); + if (trigger_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) + nsp_set_bit(chip, REG, NSP_GPIO_EVENT, gpio, val); +} + +/* + * nsp_gpio_irq_set_mask - mask/unmask a GPIO interrupt + * + * @d: IRQ chip data + * @unmask: mask/unmask GPIO interrupt + */ +static void nsp_gpio_irq_set_mask(struct irq_data *d, bool unmask) +{ + struct nsp_gpio *chip = irq_data_get_irq_chip_data(d); + unsigned gpio = d->hwirq; + u32 trigger_type; + + trigger_type = irq_get_trigger_type(d->irq); + if (trigger_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) + nsp_set_bit(chip, REG, NSP_GPIO_EVENT_INT_MASK, gpio, unmask); + else + nsp_set_bit(chip, REG, NSP_GPIO_INT_MASK, gpio, unmask); +} + +static void nsp_gpio_irq_mask(struct irq_data *d) +{ + struct nsp_gpio *chip = irq_data_get_irq_chip_data(d); + unsigned long flags; + + raw_spin_lock_irqsave(&chip->lock, flags); + nsp_gpio_irq_set_mask(d, false); + raw_spin_unlock_irqrestore(&chip->lock, flags); +} + +static void nsp_gpio_irq_unmask(struct irq_data *d) +{ + struct nsp_gpio *chip = irq_data_get_irq_chip_data(d); + unsigned long flags; + + raw_spin_lock_irqsave(&chip->lock, flags); + nsp_gpio_irq_set_mask(d, true); + raw_spin_unlock_irqrestore(&chip->lock, flags); +} + +static int nsp_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct nsp_gpio *chip = irq_data_get_irq_chip_data(d); + unsigned gpio = d->hwirq; + bool level_low; + bool falling; + unsigned long flags; + + raw_spin_lock_irqsave(&chip->lock, flags); + falling = nsp_get_bit(chip, REG, NSP_GPIO_EVENT_INT_POLARITY, gpio); + level_low = nsp_get_bit(chip, REG, NSP_GPIO_INT_POLARITY, gpio); + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + falling = false; + break; + + case IRQ_TYPE_EDGE_FALLING: + falling = true; + break; + + case IRQ_TYPE_LEVEL_HIGH: + level_low = false; + break; + + case IRQ_TYPE_LEVEL_LOW: + level_low = true; + break; + + default: + dev_err(chip->dev, "invalid GPIO IRQ type 0x%x\n", + type); + raw_spin_unlock_irqrestore(&chip->lock, flags); + return -EINVAL; + } + + nsp_set_bit(chip, REG, NSP_GPIO_EVENT_INT_POLARITY, gpio, falling); + nsp_set_bit(chip, REG, NSP_GPIO_INT_POLARITY, gpio, level_low); + raw_spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u level_low:%s falling:%s\n", gpio, + level_low ? "true" : "false", falling ? "true" : "false"); + return 0; +} + +static struct irq_chip nsp_gpio_irq_chip = { + .name = "gpio-a", + .irq_enable = nsp_gpio_irq_unmask, + .irq_disable = nsp_gpio_irq_mask, + .irq_ack = nsp_gpio_irq_ack, + .irq_mask = nsp_gpio_irq_mask, + .irq_unmask = nsp_gpio_irq_unmask, + .irq_set_type = nsp_gpio_irq_set_type, +}; + +static int nsp_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) +{ + struct nsp_gpio *chip = gpiochip_get_data(gc); + unsigned long flags; + + raw_spin_lock_irqsave(&chip->lock, flags); + nsp_set_bit(chip, REG, NSP_GPIO_OUT_EN, gpio, false); + raw_spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set input\n", gpio); + return 0; +} + +static int nsp_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, + int val) +{ + struct nsp_gpio *chip = gpiochip_get_data(gc); + unsigned long flags; + + raw_spin_lock_irqsave(&chip->lock, flags); + nsp_set_bit(chip, REG, NSP_GPIO_OUT_EN, gpio, true); + nsp_set_bit(chip, REG, NSP_GPIO_DATA_OUT, gpio, !!(val)); + raw_spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set output, value:%d\n", gpio, val); + return 0; +} + +static void nsp_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) +{ + struct nsp_gpio *chip = gpiochip_get_data(gc); + unsigned long flags; + + raw_spin_lock_irqsave(&chip->lock, flags); + nsp_set_bit(chip, REG, NSP_GPIO_DATA_OUT, gpio, !!(val)); + raw_spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, val); +} + +static int nsp_gpio_get(struct gpio_chip *gc, unsigned gpio) +{ + struct nsp_gpio *chip = gpiochip_get_data(gc); + + return !!(readl(chip->base + NSP_GPIO_DATA_IN) & BIT(gpio)); +} + +static int nsp_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct nsp_gpio *chip = gpiochip_get_data(gc); + + return irq_linear_revmap(chip->irq_domain, offset); +} + +static int nsp_get_groups_count(struct pinctrl_dev *pctldev) +{ + return 1; +} + +/* + * Only one group: "gpio_grp", since this local pinctrl device only performs + * GPIO specific PINCONF configurations + */ +static const char *nsp_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return "gpio_grp"; +} + +static const struct pinctrl_ops nsp_pctrl_ops = { + .get_groups_count = nsp_get_groups_count, + .get_group_name = nsp_get_group_name, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int nsp_gpio_set_slew(struct nsp_gpio *chip, unsigned gpio, u32 slew) +{ + if (slew) + nsp_set_bit(chip, IO_CTRL, NSP_GPIO_SLEW_RATE_EN, gpio, true); + else + nsp_set_bit(chip, IO_CTRL, NSP_GPIO_SLEW_RATE_EN, gpio, false); + + return 0; +} + +static int nsp_gpio_set_pull(struct nsp_gpio *chip, unsigned gpio, + bool pull_up, bool pull_down) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&chip->lock, flags); + nsp_set_bit(chip, IO_CTRL, NSP_PULL_DOWN_EN, gpio, pull_down); + nsp_set_bit(chip, IO_CTRL, NSP_PULL_UP_EN, gpio, pull_up); + raw_spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set pullup:%d pulldown: %d\n", + gpio, pull_up, pull_down); + return 0; +} + +static void nsp_gpio_get_pull(struct nsp_gpio *chip, unsigned gpio, + bool *pull_up, bool *pull_down) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&chip->lock, flags); + *pull_up = nsp_get_bit(chip, IO_CTRL, NSP_PULL_UP_EN, gpio); + *pull_down = nsp_get_bit(chip, IO_CTRL, NSP_PULL_DOWN_EN, gpio); + raw_spin_unlock_irqrestore(&chip->lock, flags); +} + +static int nsp_gpio_set_strength(struct nsp_gpio *chip, unsigned gpio, + u32 strength) +{ + u32 offset, shift, i; + u32 val; + unsigned long flags; + + /* make sure drive strength is supported */ + if (strength < 2 || strength > 16 || (strength % 2)) + return -ENOTSUPP; + + shift = gpio; + offset = NSP_GPIO_DRV_CTRL; + dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio, + strength); + raw_spin_lock_irqsave(&chip->lock, flags); + strength = (strength / 2) - 1; + for (i = GPIO_DRV_STRENGTH_BITS; i > 0; i--) { + val = readl(chip->io_ctrl + offset); + val &= ~BIT(shift); + val |= ((strength >> (i-1)) & 0x1) << shift; + writel(val, chip->io_ctrl + offset); + offset += 4; + } + raw_spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int nsp_gpio_get_strength(struct nsp_gpio *chip, unsigned gpio, + u16 *strength) +{ + unsigned int offset, shift; + u32 val; + unsigned long flags; + int i; + + offset = NSP_GPIO_DRV_CTRL; + shift = gpio; + + raw_spin_lock_irqsave(&chip->lock, flags); + *strength = 0; + for (i = (GPIO_DRV_STRENGTH_BITS - 1); i >= 0; i--) { + val = readl(chip->io_ctrl + offset) & BIT(shift); + val >>= shift; + *strength += (val << i); + offset += 4; + } + + /* convert to mA */ + *strength = (*strength + 1) * 2; + raw_spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int nsp_pin_config_group_get(struct pinctrl_dev *pctldev, + unsigned selector, + unsigned long *config) +{ + return 0; +} + +static int nsp_pin_config_group_set(struct pinctrl_dev *pctldev, + unsigned selector, + unsigned long *configs, unsigned num_configs) +{ + return 0; +} + +static int nsp_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) +{ + struct nsp_gpio *chip = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + unsigned int gpio; + u16 arg = 0; + bool pull_up, pull_down; + int ret; + + gpio = nsp_pin_to_gpio(pin); + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + nsp_gpio_get_pull(chip, gpio, &pull_up, &pull_down); + if ((pull_up == false) && (pull_down == false)) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_BIAS_PULL_UP: + nsp_gpio_get_pull(chip, gpio, &pull_up, &pull_down); + if (pull_up) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_BIAS_PULL_DOWN: + nsp_gpio_get_pull(chip, gpio, &pull_up, &pull_down); + if (pull_down) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_DRIVE_STRENGTH: + ret = nsp_gpio_get_strength(chip, gpio, &arg); + if (ret) + return ret; + *config = pinconf_to_config_packed(param, arg); + return 0; + + default: + return -ENOTSUPP; + } +} + +static int nsp_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *configs, unsigned num_configs) +{ + struct nsp_gpio *chip = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param; + u32 arg; + unsigned int i, gpio; + int ret = -ENOTSUPP; + + gpio = nsp_pin_to_gpio(pin); + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + ret = nsp_gpio_set_pull(chip, gpio, false, false); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_BIAS_PULL_UP: + ret = nsp_gpio_set_pull(chip, gpio, true, false); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + ret = nsp_gpio_set_pull(chip, gpio, false, true); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + ret = nsp_gpio_set_strength(chip, gpio, arg); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_SLEW_RATE: + ret = nsp_gpio_set_slew(chip, gpio, arg); + if (ret < 0) + goto out; + break; + + default: + dev_err(chip->dev, "invalid configuration\n"); + return -ENOTSUPP; + } + } + +out: + return ret; +} + +static const struct pinconf_ops nsp_pconf_ops = { + .is_generic = true, + .pin_config_get = nsp_pin_config_get, + .pin_config_set = nsp_pin_config_set, + .pin_config_group_get = nsp_pin_config_group_get, + .pin_config_group_set = nsp_pin_config_group_set, +}; + +/* + * NSP GPIO controller supports some PINCONF related configurations such as + * pull up, pull down, slew and drive strength, when the pin is configured + * to GPIO. + * + * Here a local pinctrl device is created with simple 1-to-1 pin mapping to the + * local GPIO pins + */ +static int nsp_gpio_register_pinconf(struct nsp_gpio *chip) +{ + struct pinctrl_desc *pctldesc = &chip->pctldesc; + struct pinctrl_pin_desc *pins; + struct gpio_chip *gc = &chip->gc; + int i; + + pins = devm_kcalloc(chip->dev, gc->ngpio, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + for (i = 0; i < gc->ngpio; i++) { + pins[i].number = i; + pins[i].name = devm_kasprintf(chip->dev, GFP_KERNEL, + "gpio-%d", i); + if (!pins[i].name) + return -ENOMEM; + } + pctldesc->name = dev_name(chip->dev); + pctldesc->pctlops = &nsp_pctrl_ops; + pctldesc->pins = pins; + pctldesc->npins = gc->ngpio; + pctldesc->confops = &nsp_pconf_ops; + + chip->pctl = devm_pinctrl_register(chip->dev, pctldesc, chip); + if (IS_ERR(chip->pctl)) { + dev_err(chip->dev, "unable to register pinctrl device\n"); + return PTR_ERR(chip->pctl); + } + + return 0; +} + +static const struct of_device_id nsp_gpio_of_match[] = { + {.compatible = "brcm,nsp-gpio-a",}, + {} +}; + +static int nsp_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct nsp_gpio *chip; + struct gpio_chip *gc; + u32 val, count; + int irq, ret; + + if (of_property_read_u32(pdev->dev.of_node, "ngpios", &val)) { + dev_err(&pdev->dev, "Missing ngpios OF property\n"); + return -ENODEV; + } + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = dev; + platform_set_drvdata(pdev, chip); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->base = devm_ioremap_resource(dev, res); + if (IS_ERR(chip->base)) { + dev_err(dev, "unable to map I/O memory\n"); + return PTR_ERR(chip->base); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + chip->io_ctrl = devm_ioremap_resource(dev, res); + if (IS_ERR(chip->io_ctrl)) { + dev_err(dev, "unable to map I/O memory\n"); + return PTR_ERR(chip->io_ctrl); + } + + raw_spin_lock_init(&chip->lock); + gc = &chip->gc; + gc->base = -1; + gc->can_sleep = false; + gc->ngpio = val; + gc->label = dev_name(dev); + gc->parent = dev; + gc->of_node = dev->of_node; + gc->request = gpiochip_generic_request; + gc->free = gpiochip_generic_free; + gc->direction_input = nsp_gpio_direction_input; + gc->direction_output = nsp_gpio_direction_output; + gc->set = nsp_gpio_set; + gc->get = nsp_gpio_get; + gc->to_irq = nsp_gpio_to_irq; + + /* optional GPIO interrupt support */ + irq = platform_get_irq(pdev, 0); + if (irq > 0) { + /* Create irq domain so that each pin can be assigned an IRQ.*/ + chip->irq_domain = irq_domain_add_linear(gc->of_node, gc->ngpio, + &irq_domain_simple_ops, + chip); + if (!chip->irq_domain) { + dev_err(&pdev->dev, "Couldn't allocate IRQ domain\n"); + return -ENXIO; + } + + /* Map each gpio to an IRQ and set the handler for gpiolib. */ + for (count = 0; count < gc->ngpio; count++) { + int irq = irq_create_mapping(chip->irq_domain, count); + + irq_set_chip_and_handler(irq, &nsp_gpio_irq_chip, + handle_simple_irq); + irq_set_chip_data(irq, chip); + } + + /* Install ISR for this GPIO controller. */ + ret = devm_request_irq(&pdev->dev, irq, nsp_gpio_irq_handler, + IRQF_SHARED, "gpio-a", chip); + if (ret) { + dev_err(&pdev->dev, "Unable to request IRQ%d: %d\n", + irq, ret); + goto err_rm_gpiochip; + } + + val = readl(chip->base + NSP_CHIP_A_INT_MASK); + val = val | NSP_CHIP_A_GPIO_INT_BIT; + writel(val, (chip->base + NSP_CHIP_A_INT_MASK)); + } + + ret = gpiochip_add_data(gc, chip); + if (ret < 0) { + dev_err(dev, "unable to add GPIO chip\n"); + return ret; + } + + ret = nsp_gpio_register_pinconf(chip); + if (ret) { + dev_err(dev, "unable to register pinconf\n"); + goto err_rm_gpiochip; + } + + return 0; + +err_rm_gpiochip: + gpiochip_remove(gc); + + return ret; +} + +static struct platform_driver nsp_gpio_driver = { + .driver = { + .name = "nsp-gpio-a", + .of_match_table = nsp_gpio_of_match, + }, + .probe = nsp_gpio_probe, +}; + +static int __init nsp_gpio_init(void) +{ + return platform_driver_register(&nsp_gpio_driver); +} +arch_initcall_sync(nsp_gpio_init); diff --git a/drivers/pinctrl/bcm/pinctrl-nsp-mux.c b/drivers/pinctrl/bcm/pinctrl-nsp-mux.c new file mode 100644 index 000000000..87618a4e9 --- /dev/null +++ b/drivers/pinctrl/bcm/pinctrl-nsp-mux.c @@ -0,0 +1,644 @@ +/* Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This file contains the Northstar plus (NSP) IOMUX driver that supports + * group based PINMUX configuration. The Northstar plus IOMUX controller + * allows pins to be individually muxed to GPIO function. The NAND and MMC is + * a group based selection. The gpio_a 8 - 11 are muxed with gpio_b and pwm. + * To select PWM, one need to enable the corresponding gpio_b as well. + * + * gpio_a (8 - 11) + * +---------- + * | + * gpio_a (8-11) | gpio_b (0 - 3) + * ------------------------+-------+---------- + * | + * | pwm (0 - 3) + * +---------- + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "../core.h" +#include "../pinctrl-utils.h" + +#define NSP_MUX_BASE0 0x00 +#define NSP_MUX_BASE1 0x01 +#define NSP_MUX_BASE2 0x02 +/* + * nsp IOMUX register description + * + * @base: base 0 or base 1 + * @shift: bit shift for mux configuration of a group + * @mask: bit mask of the function + * @alt: alternate function to set to + */ +struct nsp_mux { + unsigned int base; + unsigned int shift; + unsigned int mask; + unsigned int alt; +}; + +/* + * Keep track of nsp IOMUX configuration and prevent double configuration + * + * @nsp_mux: nsp IOMUX register description + * @is_configured: flag to indicate whether a mux setting has already been + * configured + */ +struct nsp_mux_log { + struct nsp_mux mux; + bool is_configured; +}; + +/* + * Group based IOMUX configuration + * + * @name: name of the group + * @pins: array of pins used by this group + * @num_pins: total number of pins used by this group + * @mux: nsp group based IOMUX configuration + */ +struct nsp_pin_group { + const char *name; + const unsigned int *pins; + const unsigned int num_pins; + const struct nsp_mux mux; +}; + +/* + * nsp mux function and supported pin groups + * + * @name: name of the function + * @groups: array of groups that can be supported by this function + * @num_groups: total number of groups that can be supported by this function + */ +struct nsp_pin_function { + const char *name; + const char * const *groups; + const unsigned int num_groups; +}; + +/* + * nsp IOMUX pinctrl core + * + * @pctl: pointer to pinctrl_dev + * @dev: pointer to device + * @base0: first mux register + * @base1: second mux register + * @base2: third mux register + * @groups: pointer to array of groups + * @num_groups: total number of groups + * @functions: pointer to array of functions + * @num_functions: total number of functions + * @mux_log: pointer to the array of mux logs + * @lock: lock to protect register access + */ +struct nsp_pinctrl { + struct pinctrl_dev *pctl; + struct device *dev; + void __iomem *base0; + void __iomem *base1; + void __iomem *base2; + const struct nsp_pin_group *groups; + unsigned int num_groups; + const struct nsp_pin_function *functions; + unsigned int num_functions; + struct nsp_mux_log *mux_log; + spinlock_t lock; +}; + +/* + * Description of a pin in nsp + * + * @pin: pin number + * @name: pin name + * @gpio_select: reg data to select GPIO + */ +struct nsp_pin { + unsigned int pin; + char *name; + unsigned int gpio_select; +}; + +#define NSP_PIN_DESC(p, n, g) \ +{ \ + .pin = p, \ + .name = n, \ + .gpio_select = g, \ +} + +/* + * List of muxable pins in nsp + */ +static struct nsp_pin nsp_pins[] = { + NSP_PIN_DESC(0, "spi_clk", 1), + NSP_PIN_DESC(1, "spi_ss", 1), + NSP_PIN_DESC(2, "spi_mosi", 1), + NSP_PIN_DESC(3, "spi_miso", 1), + NSP_PIN_DESC(4, "scl", 1), + NSP_PIN_DESC(5, "sda", 1), + NSP_PIN_DESC(6, "mdc", 1), + NSP_PIN_DESC(7, "mdio", 1), + NSP_PIN_DESC(8, "pwm0", 1), + NSP_PIN_DESC(9, "pwm1", 1), + NSP_PIN_DESC(10, "pwm2", 1), + NSP_PIN_DESC(11, "pwm3", 1), + NSP_PIN_DESC(12, "uart1_rx", 1), + NSP_PIN_DESC(13, "uart1_tx", 1), + NSP_PIN_DESC(14, "uart1_cts", 1), + NSP_PIN_DESC(15, "uart1_rts", 1), + NSP_PIN_DESC(16, "uart2_rx", 1), + NSP_PIN_DESC(17, "uart2_tx", 1), + NSP_PIN_DESC(18, "synce", 0), + NSP_PIN_DESC(19, "sata0_led", 0), + NSP_PIN_DESC(20, "sata1_led", 0), + NSP_PIN_DESC(21, "xtal_out", 1), + NSP_PIN_DESC(22, "sdio_pwr", 1), + NSP_PIN_DESC(23, "sdio_en_1p8v", 1), + NSP_PIN_DESC(24, "gpio_24", 1), + NSP_PIN_DESC(25, "gpio_25", 1), + NSP_PIN_DESC(26, "p5_led0", 0), + NSP_PIN_DESC(27, "p5_led1", 0), + NSP_PIN_DESC(28, "gpio_28", 1), + NSP_PIN_DESC(29, "gpio_29", 1), + NSP_PIN_DESC(30, "gpio_30", 1), + NSP_PIN_DESC(31, "gpio_31", 1), + NSP_PIN_DESC(32, "nand_ale", 0), + NSP_PIN_DESC(33, "nand_ce0", 0), + NSP_PIN_DESC(34, "nand_r/b", 0), + NSP_PIN_DESC(35, "nand_dq0", 0), + NSP_PIN_DESC(36, "nand_dq1", 0), + NSP_PIN_DESC(37, "nand_dq2", 0), + NSP_PIN_DESC(38, "nand_dq3", 0), + NSP_PIN_DESC(39, "nand_dq4", 0), + NSP_PIN_DESC(40, "nand_dq5", 0), + NSP_PIN_DESC(41, "nand_dq6", 0), + NSP_PIN_DESC(42, "nand_dq7", 0), +}; + +/* + * List of groups of pins + */ + +static const unsigned int spi_pins[] = {0, 1, 2, 3}; +static const unsigned int i2c_pins[] = {4, 5}; +static const unsigned int mdio_pins[] = {6, 7}; +static const unsigned int pwm0_pins[] = {8}; +static const unsigned int gpio_b_0_pins[] = {8}; +static const unsigned int pwm1_pins[] = {9}; +static const unsigned int gpio_b_1_pins[] = {9}; +static const unsigned int pwm2_pins[] = {10}; +static const unsigned int gpio_b_2_pins[] = {10}; +static const unsigned int pwm3_pins[] = {11}; +static const unsigned int gpio_b_3_pins[] = {11}; +static const unsigned int uart1_pins[] = {12, 13, 14, 15}; +static const unsigned int uart2_pins[] = {16, 17}; +static const unsigned int synce_pins[] = {18}; +static const unsigned int sata0_led_pins[] = {19}; +static const unsigned int sata1_led_pins[] = {20}; +static const unsigned int xtal_out_pins[] = {21}; +static const unsigned int sdio_pwr_pins[] = {22}; +static const unsigned int sdio_1p8v_pins[] = {23}; +static const unsigned int switch_p05_led0_pins[] = {26}; +static const unsigned int switch_p05_led1_pins[] = {27}; +static const unsigned int nand_pins[] = {32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42}; +static const unsigned int emmc_pins[] = {32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42}; + +#define NSP_PIN_GROUP(group_name, ba, sh, ma, al) \ +{ \ + .name = __stringify(group_name) "_grp", \ + .pins = group_name ## _pins, \ + .num_pins = ARRAY_SIZE(group_name ## _pins), \ + .mux = { \ + .base = ba, \ + .shift = sh, \ + .mask = ma, \ + .alt = al, \ + } \ +} + +/* + * List of nsp pin groups + */ +static const struct nsp_pin_group nsp_pin_groups[] = { + NSP_PIN_GROUP(spi, NSP_MUX_BASE0, 0, 0x0f, 0x00), + NSP_PIN_GROUP(i2c, NSP_MUX_BASE0, 3, 0x03, 0x00), + NSP_PIN_GROUP(mdio, NSP_MUX_BASE0, 5, 0x03, 0x00), + NSP_PIN_GROUP(gpio_b_0, NSP_MUX_BASE0, 7, 0x01, 0x00), + NSP_PIN_GROUP(pwm0, NSP_MUX_BASE1, 0, 0x01, 0x01), + NSP_PIN_GROUP(gpio_b_1, NSP_MUX_BASE0, 8, 0x01, 0x00), + NSP_PIN_GROUP(pwm1, NSP_MUX_BASE1, 1, 0x01, 0x01), + NSP_PIN_GROUP(gpio_b_2, NSP_MUX_BASE0, 9, 0x01, 0x00), + NSP_PIN_GROUP(pwm2, NSP_MUX_BASE1, 2, 0x01, 0x01), + NSP_PIN_GROUP(gpio_b_3, NSP_MUX_BASE0, 10, 0x01, 0x00), + NSP_PIN_GROUP(pwm3, NSP_MUX_BASE1, 3, 0x01, 0x01), + NSP_PIN_GROUP(uart1, NSP_MUX_BASE0, 11, 0x0f, 0x00), + NSP_PIN_GROUP(uart2, NSP_MUX_BASE0, 15, 0x03, 0x00), + NSP_PIN_GROUP(synce, NSP_MUX_BASE0, 17, 0x01, 0x01), + NSP_PIN_GROUP(sata0_led, NSP_MUX_BASE0, 18, 0x01, 0x01), + NSP_PIN_GROUP(sata1_led, NSP_MUX_BASE0, 19, 0x01, 0x01), + NSP_PIN_GROUP(xtal_out, NSP_MUX_BASE0, 20, 0x01, 0x00), + NSP_PIN_GROUP(sdio_pwr, NSP_MUX_BASE0, 21, 0x01, 0x00), + NSP_PIN_GROUP(sdio_1p8v, NSP_MUX_BASE0, 22, 0x01, 0x00), + NSP_PIN_GROUP(switch_p05_led0, NSP_MUX_BASE0, 26, 0x01, 0x01), + NSP_PIN_GROUP(switch_p05_led1, NSP_MUX_BASE0, 27, 0x01, 0x01), + NSP_PIN_GROUP(nand, NSP_MUX_BASE2, 0, 0x01, 0x00), + NSP_PIN_GROUP(emmc, NSP_MUX_BASE2, 0, 0x01, 0x01) +}; + +/* + * List of groups supported by functions + */ + +static const char * const spi_grps[] = {"spi_grp"}; +static const char * const i2c_grps[] = {"i2c_grp"}; +static const char * const mdio_grps[] = {"mdio_grp"}; +static const char * const pwm_grps[] = {"pwm0_grp", "pwm1_grp", "pwm2_grp" + , "pwm3_grp"}; +static const char * const gpio_b_grps[] = {"gpio_b_0_grp", "gpio_b_1_grp", + "gpio_b_2_grp", "gpio_b_3_grp"}; +static const char * const uart1_grps[] = {"uart1_grp"}; +static const char * const uart2_grps[] = {"uart2_grp"}; +static const char * const synce_grps[] = {"synce_grp"}; +static const char * const sata_led_grps[] = {"sata0_led_grp", "sata1_led_grp"}; +static const char * const xtal_out_grps[] = {"xtal_out_grp"}; +static const char * const sdio_grps[] = {"sdio_pwr_grp", "sdio_1p8v_grp"}; +static const char * const switch_led_grps[] = {"switch_p05_led0_grp", + "switch_p05_led1_grp"}; +static const char * const nand_grps[] = {"nand_grp"}; +static const char * const emmc_grps[] = {"emmc_grp"}; + +#define NSP_PIN_FUNCTION(func) \ +{ \ + .name = #func, \ + .groups = func ## _grps, \ + .num_groups = ARRAY_SIZE(func ## _grps), \ +} + +/* + * List of supported functions in nsp + */ +static const struct nsp_pin_function nsp_pin_functions[] = { + NSP_PIN_FUNCTION(spi), + NSP_PIN_FUNCTION(i2c), + NSP_PIN_FUNCTION(mdio), + NSP_PIN_FUNCTION(pwm), + NSP_PIN_FUNCTION(gpio_b), + NSP_PIN_FUNCTION(uart1), + NSP_PIN_FUNCTION(uart2), + NSP_PIN_FUNCTION(synce), + NSP_PIN_FUNCTION(sata_led), + NSP_PIN_FUNCTION(xtal_out), + NSP_PIN_FUNCTION(sdio), + NSP_PIN_FUNCTION(switch_led), + NSP_PIN_FUNCTION(nand), + NSP_PIN_FUNCTION(emmc) +}; + +static int nsp_get_groups_count(struct pinctrl_dev *pctrl_dev) +{ + struct nsp_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->num_groups; +} + +static const char *nsp_get_group_name(struct pinctrl_dev *pctrl_dev, + unsigned int selector) +{ + struct nsp_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->groups[selector].name; +} + +static int nsp_get_group_pins(struct pinctrl_dev *pctrl_dev, + unsigned int selector, const unsigned int **pins, + unsigned int *num_pins) +{ + struct nsp_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + *pins = pinctrl->groups[selector].pins; + *num_pins = pinctrl->groups[selector].num_pins; + + return 0; +} + +static void nsp_pin_dbg_show(struct pinctrl_dev *pctrl_dev, + struct seq_file *s, unsigned int offset) +{ + seq_printf(s, " %s", dev_name(pctrl_dev->dev)); +} + +static const struct pinctrl_ops nsp_pinctrl_ops = { + .get_groups_count = nsp_get_groups_count, + .get_group_name = nsp_get_group_name, + .get_group_pins = nsp_get_group_pins, + .pin_dbg_show = nsp_pin_dbg_show, + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int nsp_get_functions_count(struct pinctrl_dev *pctrl_dev) +{ + struct nsp_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->num_functions; +} + +static const char *nsp_get_function_name(struct pinctrl_dev *pctrl_dev, + unsigned int selector) +{ + struct nsp_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->functions[selector].name; +} + +static int nsp_get_function_groups(struct pinctrl_dev *pctrl_dev, + unsigned int selector, + const char * const **groups, + unsigned * const num_groups) +{ + struct nsp_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + *groups = pinctrl->functions[selector].groups; + *num_groups = pinctrl->functions[selector].num_groups; + + return 0; +} + +static int nsp_pinmux_set(struct nsp_pinctrl *pinctrl, + const struct nsp_pin_function *func, + const struct nsp_pin_group *grp, + struct nsp_mux_log *mux_log) +{ + const struct nsp_mux *mux = &grp->mux; + int i; + u32 val, mask; + unsigned long flags; + void __iomem *base_address; + + for (i = 0; i < pinctrl->num_groups; i++) { + if ((mux->shift != mux_log[i].mux.shift) || + (mux->base != mux_log[i].mux.base)) + continue; + + /* if this is a new configuration, just do it! */ + if (!mux_log[i].is_configured) + break; + + /* + * IOMUX has been configured previously and one is trying to + * configure it to a different function + */ + if (mux_log[i].mux.alt != mux->alt) { + dev_err(pinctrl->dev, + "double configuration error detected!\n"); + dev_err(pinctrl->dev, "func:%s grp:%s\n", + func->name, grp->name); + return -EINVAL; + } + + return 0; + } + if (i == pinctrl->num_groups) + return -EINVAL; + + mask = mux->mask; + mux_log[i].mux.alt = mux->alt; + mux_log[i].is_configured = true; + + switch (mux->base) { + case NSP_MUX_BASE0: + base_address = pinctrl->base0; + break; + + case NSP_MUX_BASE1: + base_address = pinctrl->base1; + break; + + case NSP_MUX_BASE2: + base_address = pinctrl->base2; + break; + + default: + return -EINVAL; + } + + spin_lock_irqsave(&pinctrl->lock, flags); + val = readl(base_address); + val &= ~(mask << grp->mux.shift); + val |= grp->mux.alt << grp->mux.shift; + writel(val, base_address); + spin_unlock_irqrestore(&pinctrl->lock, flags); + + return 0; +} + +static int nsp_pinmux_enable(struct pinctrl_dev *pctrl_dev, + unsigned int func_select, unsigned int grp_select) +{ + struct nsp_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + const struct nsp_pin_function *func; + const struct nsp_pin_group *grp; + + if (grp_select >= pinctrl->num_groups || + func_select >= pinctrl->num_functions) + return -EINVAL; + + func = &pinctrl->functions[func_select]; + grp = &pinctrl->groups[grp_select]; + + dev_dbg(pctrl_dev->dev, "func:%u name:%s grp:%u name:%s\n", + func_select, func->name, grp_select, grp->name); + + dev_dbg(pctrl_dev->dev, "shift:%u alt:%u\n", grp->mux.shift, + grp->mux.alt); + + return nsp_pinmux_set(pinctrl, func, grp, pinctrl->mux_log); +} + + +static int nsp_gpio_request_enable(struct pinctrl_dev *pctrl_dev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct nsp_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + u32 *gpio_select = pctrl_dev->desc->pins[pin].drv_data; + u32 val; + unsigned long flags; + + spin_lock_irqsave(&pinctrl->lock, flags); + val = readl(pinctrl->base0); + if ((val & BIT(pin)) != (*gpio_select << pin)) { + val &= ~BIT(pin); + val |= *gpio_select << pin; + writel(val, pinctrl->base0); + } + spin_unlock_irqrestore(&pinctrl->lock, flags); + + return 0; +} + +static void nsp_gpio_disable_free(struct pinctrl_dev *pctrl_dev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct nsp_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + u32 *gpio_select = pctrl_dev->desc->pins[pin].drv_data; + u32 val; + unsigned long flags; + + spin_lock_irqsave(&pinctrl->lock, flags); + val = readl(pinctrl->base0); + if ((val & (1 << pin)) == (*gpio_select << pin)) { + val &= ~(1 << pin); + if (!(*gpio_select)) + val |= (1 << pin); + writel(val, pinctrl->base0); + } + spin_unlock_irqrestore(&pinctrl->lock, flags); +} + +static const struct pinmux_ops nsp_pinmux_ops = { + .get_functions_count = nsp_get_functions_count, + .get_function_name = nsp_get_function_name, + .get_function_groups = nsp_get_function_groups, + .set_mux = nsp_pinmux_enable, + .gpio_request_enable = nsp_gpio_request_enable, + .gpio_disable_free = nsp_gpio_disable_free, +}; + +static struct pinctrl_desc nsp_pinctrl_desc = { + .name = "nsp-pinmux", + .pctlops = &nsp_pinctrl_ops, + .pmxops = &nsp_pinmux_ops, +}; + +static int nsp_mux_log_init(struct nsp_pinctrl *pinctrl) +{ + struct nsp_mux_log *log; + unsigned int i; + u32 no_of_groups = ARRAY_SIZE(nsp_pin_groups); + + pinctrl->mux_log = devm_kcalloc(pinctrl->dev, no_of_groups, + sizeof(struct nsp_mux_log), + GFP_KERNEL); + if (!pinctrl->mux_log) + return -ENOMEM; + + for (i = 0; i < no_of_groups; i++) { + log = &pinctrl->mux_log[i]; + log->mux.base = nsp_pin_groups[i].mux.base; + log->mux.shift = nsp_pin_groups[i].mux.shift; + log->mux.alt = 0; + log->is_configured = false; + } + + return 0; +} + +static int nsp_pinmux_probe(struct platform_device *pdev) +{ + struct nsp_pinctrl *pinctrl; + struct resource *res; + int i, ret; + struct pinctrl_pin_desc *pins; + unsigned int num_pins = ARRAY_SIZE(nsp_pins); + + pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL); + if (!pinctrl) + return -ENOMEM; + pinctrl->dev = &pdev->dev; + platform_set_drvdata(pdev, pinctrl); + spin_lock_init(&pinctrl->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pinctrl->base0 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pinctrl->base0)) + return PTR_ERR(pinctrl->base0); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -EINVAL; + pinctrl->base1 = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!pinctrl->base1) { + dev_err(&pdev->dev, "unable to map I/O space\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + pinctrl->base2 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pinctrl->base2)) + return PTR_ERR(pinctrl->base2); + + ret = nsp_mux_log_init(pinctrl); + if (ret) { + dev_err(&pdev->dev, "unable to initialize IOMUX log\n"); + return ret; + } + + pins = devm_kcalloc(&pdev->dev, num_pins, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + for (i = 0; i < num_pins; i++) { + pins[i].number = nsp_pins[i].pin; + pins[i].name = nsp_pins[i].name; + pins[i].drv_data = &nsp_pins[i].gpio_select; + } + + pinctrl->groups = nsp_pin_groups; + pinctrl->num_groups = ARRAY_SIZE(nsp_pin_groups); + pinctrl->functions = nsp_pin_functions; + pinctrl->num_functions = ARRAY_SIZE(nsp_pin_functions); + nsp_pinctrl_desc.pins = pins; + nsp_pinctrl_desc.npins = num_pins; + + pinctrl->pctl = devm_pinctrl_register(&pdev->dev, &nsp_pinctrl_desc, + pinctrl); + if (IS_ERR(pinctrl->pctl)) { + dev_err(&pdev->dev, "unable to register nsp IOMUX pinctrl\n"); + return PTR_ERR(pinctrl->pctl); + } + + return 0; +} + +static const struct of_device_id nsp_pinmux_of_match[] = { + { .compatible = "brcm,nsp-pinmux" }, + { } +}; + +static struct platform_driver nsp_pinmux_driver = { + .driver = { + .name = "nsp-pinmux", + .of_match_table = nsp_pinmux_of_match, + }, + .probe = nsp_pinmux_probe, +}; + +static int __init nsp_pinmux_init(void) +{ + return platform_driver_register(&nsp_pinmux_driver); +} +arch_initcall(nsp_pinmux_init); |