/* * gpio-intelce2700.c * * GPL LICENSE SUMMARY * * Copyright(c) 2015-2016 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Corporation * 2200 Mission College Blvd. * Santa Clara, CA 97052 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FAMILY0_PAD_REGS_OFF 0x4400 #define FAMILY_PAD_REGS_SIZE 0x400 #define MAX_FAMILY_PAD_GPIO_NO 16 #define GPIO_REGS_SIZE 8 #define CE2700_GPIO_PADCTRL0_REG 0x000 #define CE2700_GPIO_PADCTRL1_REG 0x004 #define CE2700_GPIO_INT_STAT_REG 0x300 #define CE2700_GPIO_INT_MASK_REG 0x380 #define CE2700_GPIO_GPIO_RX_STAT BIT(0) #define CE2700_GPIO_GPIO_TX_STAT BIT(1) #define CE2700_GPIO_GPIO_EN BIT(15) #define CE2700_GPIO_GPIO_PULL BIT(23) #define CE2700_GPIO_CFG_LOCK_MASK BIT(31) #define CE2700_GPIO_INT_CFG_MASK (BIT(0) | BIT(1) | BIT(2)) #define CE2700_GPIO_PAD_MODE_MASK (0xF << 16) #define CE2700_GPIO_GPIO_CFG_MASK (BIT(8) | BIT(9) | BIT(10)) #define CE2700_GPIO_GPIO_TXRX_EN (0 << 8) #define CE2700_GPIO_GPIO_TX_EN (1 << 8) #define CE2700_GPIO_GPIO_RX_EN (2 << 8) #define CE2700_GPIO_GPIO_HZ (3 << 8) #define CE2700_GPIO_INV_RX_DATA BIT(6) #define CE2700_GPIO_INT_SEL_MASK (0xF << 28) #define CE2700_GPIO_PAD_MODE_MASK (0xF << 16) #define CE2700_GPIO_GPIO_PULL_MODE (0xF << 20) #define CE2700_GPIO_GPIO_PULL_STRENGTH_MASK (0x7 << 20) #define MAX_INTR_LINE_NUM 16 #if defined(CONFIG_AVM_ENHANCED) #include #include #include #include /* proc_mkdir */ #endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ /* When Pad Cfg is locked, driver can only change GPIOTXState or GPIORXState */ #define PAD_CFG_LOCKED(offset) (intelce2700_readl(intelce2700_gpio_reg(&cg->chip, \ offset, CE2700_GPIO_PADCTRL1_REG)) & CE2700_GPIO_CFG_LOCK_MASK) /*northeast pin list*/ enum { SPARE0_1P8 = 0, // 0x4400 NGPIO15_1P8 , // 0x4408 NGPIO14_1P8 , // 0x4410 UART1_TXD_SPI2_MOSI_1P8 , // 0x4418 UART1_RXD_SPI2_MISO_1P8 , // 0x4428 UART1_RTS_B_SPI2_CS0_B_1P8 , // 0x4438 UART1_CTS_B_SPI2_CLK_1P8 , // 0x4448 PROCHOT_B_1P8 , // 0x4800 SUSPWRDNACK_1P8 , // 0x4808 FSPI_D0_BBUC_SPI_DO_1P8 , // 0x4810 PMU_PWRBTN_B , // 0x4818 PMU_WAKE_B_1P8 , // 0x4820 FSPI_D1_BBUC_SPI_DI_1P8 , // 0x4830 PMU_SLP_S4_B_1P8 , // 0x4838 FSPI_CLK_BBUC_SPI_CLK_1P8 , // 0x4840 PMU_SLP_S0IX_B_1P8 , // 0x4848 PMU_AC_PRESENT_1P8 , // 0x4850 FSPI_CS1_B_BBUC_SPI_CS1_B_1P8 , // 0x4860 FSPI_CS0_B_BBUC_SPI_CS0_B_1P8 , // 0x4870 PMU_RESETBUTTON_B_1P8 , // 0x4878 PMU_PLTRST_B_1P8 , // 0x4880 SPARE4_1P8 , // 0x4C00 MMC3_1P8_EN_1P8 , // 0x4C08 PMIC_INT_1P8 , // 0x4C10 SPARE5_1P8 , // 0x4C18 MMC3_PWR_EN_1P8 , // 0x4C20 NGPIO28_1P8 , // 0x4C28 NGPIO31_1P8 , // 0x4C30 SPARE6_1P8 , // 0x4C38 I2C4_SCL_1P8 , // 0x4C40 I2C4_SDA_1P8 , // 0x4C48 NGPIO30_1P8 , // 0x4C50 NGPIO29_1P8 , // 0x4C58 NGPIO21_BBU_ADC_SPI_CS0_B_OSC_CLK_CTRL_1P8 , // 0x5000 NGPIO22_BBU_ADC_SPI_DO_SEC_MSTR_TCK_1P8 , // 0x5008 NGPIO20_BBU_ADC_SPI_CLK_1P8 , // 0x5010 NGPIO23_BBU_ADC_SPI_DI_SEC_MSTR_TDI_1P8 , // 0x5018 NGPIO27_1P8 , // 0x5020 NGPIO24_SEC_MSTR_TDO_1P8 , // 0x5028 UART2_RXD_NUART3_RXD_NFC_INT_1P8 , // 0x5030 NGPIO25_SEC_MSTR_TMS_1P8 , // 0x5038 UART2_TXD_NUART3_TXD_1P8 , // 0x5040 NGPIO26_SEC_MSTR_TRST_B_1P8 , // 0x5048 UART2_CTS_B_NUART3_CTS_B_I2C_NFC_SCL_1P8 , // 0x5050 UART2_RTS_B_NUART3_RTS_B_I2C_NFC_SDA_1P8 , // 0x5058 }; /*north west pin list*/ enum { MOCA_SPI_CS1N_1P8 = 0, // 0x4408 RF_FE_RST_NGPIO4_1P8 , // 0x4418 EXT_INTR_05_1P8 , // 0x4420 EXT_INTR_01_NGPIO7_1P8 , // 0x4428 NGPIO1_1P8 , // 0x4430 RF_FE_SPI_CLK_1P8 , // 0x4438 NGPIO3_SGMII1_INT_1P8 , // 0x4440 RF_FE_SPI_MISO_1P8 , // 0x4448 MOCA_SPI_CS0N_1P8 , // 0x4450 NGPIO2_SGMII0_INT_1P8 , // 0x4458 NGPIO0_1P8 , // 0x4460 RF_FE_SPI_CS0_1P8 , // 0x4468 EXT_INTR_00_NGPIO6_1P8 , // 0x4470 RF_FE_SPI_MOSI_1P8 , // 0x4478 MOCA_RESET_NGPIO5_1P8 , // 0x4480 I2C1_SDA__1P8 , // 0x4800 I2C1_SCL_CCU_PLL_LOCKEN_1P8 , // 0x4808 UART0_TXD_1P8 , // 0x4810 EPGA_CLK_1P8 , // 0x4818 UART0_RXD_1P8 , // 0x4820 EPGA_PWDN_N_1P8 , // 0x4828 EPGA_DATA_1P8 , // 0x4838 EPGA_WE_N_1P8 , // 0x4848 I2C0_SCL_NGPIO13_1P8 , // 0x4850 I2C0_SDA_NGPIO12_1P8 , // 0x4858 BBU_DCIN_DETECT_3P3 , // 0x4C00 BBU_EPROM2_3P3 , // 0x4C08 BBU_PWM0_3P3 , // 0x4C10 BBU_EPROM1_3P3 , // 0x4C20 BBU_ENABLE2_3P3 , // 0x4C28 BBU_UART_TX_NGPIO16_3P3 , // 0x4C30 BBU_UART_RX_NGPIO17_3P3 , // 0x4C40 BBU_ENABLE1_3P3 , // 0x4C48 BBU_UART_RTS_B_PWM2_3P3 , // 0x4C50 BBU_UART_CTS_B_PWM1_3P3 , // 0x4C58 }; /*south east pin list*/ enum { PCIE_WAKE0_B_3P3 = 0, // 0x4400 PCIE_WAKE1_B_AVB_SNAPSHOT_3P3 , // 0x4408 PCIE_CLKREQ0B_3P3 , // 0x4418 PCIE_CLKREQ3B_NGPIO10_MMC3_WP_3P3 , // 0x4428 PCIE_CLKREQ1B_BCFG_AVB_SNAPSHOT_3P3 , // 0x4438 PCIE_CLKREQ2B_NGPIO9_3P3 , // 0x4448 DOCSIS_UART_RXD_3P3 , // 0x4800 DOCSIS_UART_TXD_3P3 , // 0x4808 EXT_INT2_NGPIO8_I2C6_SCL_3P3 , // 0x4810 DJTAG_TRSTB_3P3 , // 0x4818 EXT_INT3_NGPIO9_I2C6_SDA_3P3 , // 0x4820 DJTAG_TMS_3P3 , // 0x4828 EXT_INT4_ARM11_RTCK_3P3 , // 0x4830 DJTAG_TCK_3P3 , // 0x4838 DJTAG_EMU0_3P3 , // 0x4840 DJTAG_TDI_3P3 , // 0x4848 DJTAG_EMU1_3P3 , // 0x4850 DJTAG_TDO_3P3 , // 0x4858 CODEC_CS0_N_3P3 , // 0x4C00 DECT_UART_RTS_B_DECT_SPI_CS_3P3 , // 0x4C08 TDM0_CLK_TDM1_CLK_3P3 , // 0x4C18 DECT_UART_CTS_B_DECT_SPI_CLK_3P3 , // 0x4C20 CODEC_CLK_3P3 , // 0x4C28 TDM0_FS_TDM1_FS_3P3 , // 0x4C30 DECT_UART_RX_DECT_SPI_MISO_3P3 , // 0x4C38 TDM0_TX_TDM1_TX_3P3 , // 0x4C48 DECT_UART_TX_DECT_SPI_MOSI_3P3 , // 0x4C50 CODEC_DATA_OUT_3P3 , // 0x4C58 TDM0_RX_TDM1_RX_3P3 , // 0x4C60 DECT_IRQ_3P3 , // 0x4C68 CODEC_DATA_IN_3P3 , // 0x4C70 TS0_CLK_IN_OUT_NGPIO7_1P8 , // 0x5000 TS3_CLK_IN_OUT_BCFG_AVB_SNAPSHOT_1P8 , // 0x5008 TS0_SYNC_IN_OUT_NGPIO8_1P8 , // 0x5010 TS3_DAV_IN_OUT_SGMII0_INT_1P8 , // 0x5018 TS3_SYNC_IN_OUT_EXT_INT06_1P8 , // 0x5020 TS0_DAV_IN_OUT_NGPIO9_1P8 , // 0x5028 TS0_DATA_IN_OUT_NGPIO10_1P8 , // 0x5030 TS3_DATA__IN_OUT_SGMII1_INT_1P8 , // 0x5038 TS1_CLK_IN_OUT_NGPIO11_1P8 , // 0x5040 TS2_DATA_IN_OUT_EXT_INT07_1P8 , // 0x5048 TS1_SYNC_IN_OUT_NGPIO12_1P8 , // 0x5058 TS2_CLK_IN_OUT_NGPIO15_1P8 , // 0x5060 TS2_DAV_IN_OUT_NGPIO17_1P8 , // 0x5068 TS1_DAV_IN_OUT_NGPIO13_1P8 , // 0x5070 TS2_SYNC_IN_OUT_NGPIO16_1P8 , // 0x5078 TS1_DATA_IN_OUT_NGPIO14_1P8 , // 0x5080 TS4_SYNC_IN_OUT_NGPIO8_1P8_3P3 , // 0x5400 TS6_DATA_IN_OUT_BCFG_AVB_SNAPSHOT_1P8_3P3 , // 0x5408 TS4_DAV_IN_OUT_NGPIO9_1P8_3P3 , // 0x5410 TS6_CLK_IN_OUT_NGPIO15_1P8_3P3 , // 0x5418 TS7_DAV_IN_OUT_EXT_INT08_1P8_3P3 , // 0x5420 TS4_CLK_IN_OUT_NGPIO7_1P8_3P3 , // 0x5428 TS5_SYNC_IN_OUT_NGPIO12_1P8_3P3 , // 0x5430 TS7_CLK_IN_OUT_PCI_WAKE3_B_1P8_3P3 , // 0x5438 TS4_DATA_IN_OUT_NGPIO10_1P8_3P3 , // 0x5440 TS6_SYNC_IN_OUT_NGPIO16_1P8_3P3 , // 0x5448 TS7_DATA_IN_OUT_EXT_INT09_1P8_3P3 , // 0x5450 TS5_DAV_IN_OUT_NGPIO13_1P8_3P3 , // 0x5460 TS7_SYNC_IN_OUT_PCI_WAKE4_B_1P8_3P3 , // 0x5468 TS5_CLK_IN_OUT_NGPIO11_1P8_3P3 , // 0x5470 TS6_DAV_IN_OUT_NGPIO17_1P8_3P3 , // 0x5478 TS5_DATA_IN_OUT_NGPIO14_1P8_3P3 , // 0x5480 SC1_INS_3P3 , // 0x5800 SC1_VSEL_3P3 , // 0x5808 SPI1_MOSI_3P3 , // 0x5810 SC1_DIO_3P3 , // 0x5818 SPI1_CLK_3P3 , // 0x5820 SC1_VEN_3P3 , // 0x5828 SPI1_MISO_3P3 , // 0x5830 SC1_RST_3P3 , // 0x5838 SPI1_CS0_B_3P3 , // 0x5840 SC1_CLK_3P3 , // 0x5848 SPARE3_3P3 , // 0x5850 SC0_INS_3P3 , // 0x5C00 SC0_VSEL_3P3 , // 0x5C08 SC0_DIO_3P3 , // 0x5C10 SC0_CLK_3P3 , // 0x5C18 SC0_RST_3P3 , // 0x5C20 SC0_VEN_3P3 , // 0x5C28 SC1_AUX2_SPI1_CS1_B_3P3 , // 0x5C38 SC1_AUX1_3P3 , // 0x5C48 SC0_AUX2_3P3 , // 0x5C50 SC0_AUX1_3P3 , // 0x5C58 }; /*southwest pin list*/ enum { MMC1_D0_1P8 = 0, // 0x4400 MMC1_D5_1P8 , // 0x4408 MMC1_D6_1P8 , // 0x4410 MMC1_D2_1P8 , // 0x4418 MMC1_D7_1P8 , // 0x4420 MMC1_CLK_1P8 , // 0x4428 MMC1_D1_1P8 , // 0x4430 MMC1_D4_SD_WE_1P8 , // 0x4438 MMC1_CMD_1P8 , // 0x4440 MMC1_DS_1P8 , // 0x4448 MMC1_D3_CD_B_1P8 , // 0x4450 MMC2_D0_NGPIO11_1P8 , // 0x4800 MMC2_D5_NGPIO16_1P8 , // 0x4808 MMC2_D6_I2C2_SCL_NGPIO17_1P8 , // 0x4810 MMC2_D2_NGPIO13_1P8 , // 0x4818 MMC2_D7_I2C2_SDA_1P8 , // 0x4820 MMC2_CLK_NGPIO6_1P8 , // 0x4828 MMC2_D1_NGPIO12_1P8 , // 0x4830 MMC2_D4_SD_WE_NGPIO15_1P8 , // 0x4838 MMC2_CMD_NGPIO10_1P8 , // 0x4840 MMC2_DS_MMC2_RCLK_1P8 , // 0x4848 MMC2_D3_CD_B_NGPIO14_1P8 , // 0x4850 MMC3_D1_1P8_3P3 , // 0x4C00 MMC3_CLK_1P8_3P3 , // 0x4C08 MMC3_D3_I2C2_SDA_1P8_3P3 , // 0x4C10 MMC3_D2_I2C2_SCL_1P8_3P3 , // 0x4C18 MMC3_CMD_1P8_3P3 , // 0x4C20 MMC3_D0_1P8_3P3 , // 0x4C28 JTAG_TRST_B_1P8 , // 0x5000 CX_PREQ_B_1P8 , // 0x5008 CX_PRDY_B_1P8 , // 0x5010 JTAG_TDO_1P8 , // 0x5018 JTAG_TCK_1P8 , // 0x5020 JTAG_TMS_1P8 , // 0x5038 MMC1_RESET_B_1P8 , // 0x5040 JTAG_TDI_1P8 , // 0x5048 MMC2_RST_B_MMC3_WP_1P8 , // 0x5050 MMC3_CD_B_1P8 , // 0x5058 }; enum { VIRTUAL0 = 0, VIRTUAL1 , VIRTUAL2 , VIRTUAL3 , VIRTUAL4 , VIRTUAL5 , VIRTUAL6 , VIRTUAL7 , }; /* west pinlist */ enum { USB_OC1_B_1P8 = 0, // 0x4410 USB_OC0_B_1P8 , // 0x4428 I2C3_SCL_NGPIO11_I2C6_SCL_1P8 , // 0x4438 SGMII0_INT_1P8 , // 0x4440 I2C3_SDA_SGMII0_INT_I2C6_SDA_1P8 , // 0x4458 GBE_1_TXDATA_3_1P8_3P3 , // 0x4800 GBE_1_TXDATA_2_1P8_3P3 , // 0x4808 GBE_1_TXDATA_1_1P8_3P3 , // 0x4810 GBE_1_TXCLK_1P8_3P3 , // 0x4818 GBE_1_TXCTL_1P8_3P3 , // 0x4820 GBE_1_TXDATA_0_1P8_3P3 , // 0x4828 GBE_1_RXCLK_1P8_3P3 , // 0x4830 GBE_1_RXCTL_1P8_3P3 , // 0x4838 GBE_1_RXDATA_0_1P8_3P3 , // 0x4840 GBE_1_RXDATA_3_1P8_3P3 , // 0x4848 GBE_1_RXDATA_2_1P8_3P3 , // 0x4850 GBE_1_RXDATA_1_1P8_3P3 , // 0x4858 GBE_1_MDC_SGMII1_INT_1P8_3P3 , // 0x4860 GBE_1_MDIO_SGMII0_INT_1P8_3P3 , // 0x4868 SPARE2_1P8_3P3 , // 0x4870 GBE_1_INT_SGMII0_INT_1P8_3P3 , // 0x4878 GBE_1_RESET_NGPIO19_1P8_3P3 , // 0x4880 GBE_0_TXDATA_3_1P8_3P3 , // 0x4C00 GBE_0_TXDATA_2_1P8_3P3 , // 0x4C08 GBE_0_TXDATA_1_1P8_3P3 , // 0x4C10 GBE_0_TXCLK_1P8_3P3 , // 0x4C18 GBE_0_TXCTL_1P8_3P3 , // 0x4C20 GBE_0_TXDATA_0_1P8_3P3 , // 0x4C28 GBE_0_RXCLK_1P8_3P3 , // 0x4C30 GBE_0_RXCTL_1P8_3P3 , // 0x4C38 GBE_0_RXDATA_0_1P8_3P3 , // 0x4C40 GBE_0_RXDATA_3_1P8_3P3 , // 0x4C48 GBE_0_RXDATA_2_1P8_3P3 , // 0x4C50 GBE_0_RXDATA_1_1P8_3P3 , // 0x4C58 GBE_0_MDC_1P8_3P3 , // 0x4C60 GBE_0_MDIO_1P8_3P3 , // 0x4C68 SPARE1_1P8_3P3 , // 0x4C70 GBE_0_INT_1P8_3P3 , // 0x4C78 NGPIO18_1P8_3P3 , // 0x4C80 }; enum INTR_CFG { CE2700_GPIO_INTR_DISABLE, CE2700_GPIO_TRIG_EDGE_FALLING, CE2700_GPIO_TRIG_EDGE_RISING, CE2700_GPIO_TRIG_EDGE_BOTH, CE2700_GPIO_TRIG_LEVEL, }; struct gpio_pad_info { //int family; /* Family ID */ //int pad; /* Pad ID in this family */ int offset; /* Interrupt line selected (0~15), -1 if not interruptible. */ int interrupt_line; unsigned long init_value; }; struct gpio_bank { char *name; int base; int irq_base; int ngpio; struct gpio_pad_info *pads_info; struct intelce2700_gpio *cg; char *community_name; }; /* For invalid GPIO number(not found in GPIO list), * initialize all fields as -1. */ static struct gpio_pad_info northeast_pads_info[] = { [SPARE0_1P8 ] = {0x4400, -1}, [NGPIO15_1P8 ] = {0x4408, -1}, [NGPIO14_1P8 ] = {0x4410, -1}, [UART1_TXD_SPI2_MOSI_1P8 ] = {0x4418, -1}, [UART1_RXD_SPI2_MISO_1P8 ] = {0x4428, -1}, [UART1_RTS_B_SPI2_CS0_B_1P8 ] = {0x4438, -1}, [UART1_CTS_B_SPI2_CLK_1P8 ] = {0x4448, -1}, [PROCHOT_B_1P8 ] = {0x4800, -1}, [SUSPWRDNACK_1P8 ] = {0x4808, -1}, [FSPI_D0_BBUC_SPI_DO_1P8 ] = {0x4810, -1}, [PMU_PWRBTN_B ] = {0x4818, -1}, [PMU_WAKE_B_1P8 ] = {0x4820, -1}, [FSPI_D1_BBUC_SPI_DI_1P8 ] = {0x4830, -1}, [PMU_SLP_S4_B_1P8 ] = {0x4838, -1}, [FSPI_CLK_BBUC_SPI_CLK_1P8 ] = {0x4840, -1}, [PMU_SLP_S0IX_B_1P8 ] = {0x4848, -1}, [PMU_AC_PRESENT_1P8 ] = {0x4850, -1}, [FSPI_CS1_B_BBUC_SPI_CS1_B_1P8 ] = {0x4860, -1}, [FSPI_CS0_B_BBUC_SPI_CS0_B_1P8 ] = {0x4870, -1}, [PMU_RESETBUTTON_B_1P8 ] = {0x4878, -1}, [PMU_PLTRST_B_1P8 ] = {0x4880, -1}, [SPARE4_1P8 ] = {0x4C00, -1}, [MMC3_1P8_EN_1P8 ] = {0x4C08, -1}, [PMIC_INT_1P8 ] = {0x4C10, -1}, [SPARE5_1P8 ] = {0x4C18, -1}, [MMC3_PWR_EN_1P8 ] = {0x4C20, -1}, [NGPIO28_1P8 ] = {0x4C28, -1}, [NGPIO31_1P8 ] = {0x4C30, -1}, [SPARE6_1P8 ] = {0x4C38, -1}, [I2C4_SCL_1P8 ] = {0x4C40, -1}, [I2C4_SDA_1P8 ] = {0x4C48, -1}, [NGPIO30_1P8 ] = {0x4C50, -1}, [NGPIO29_1P8 ] = {0x4C58, -1}, [NGPIO21_BBU_ADC_SPI_CS0_B_OSC_CLK_CTRL_1P8] = {0x5000, -1}, [NGPIO22_BBU_ADC_SPI_DO_SEC_MSTR_TCK_1P8 ] = {0x5008, -1}, [NGPIO20_BBU_ADC_SPI_CLK_1P8 ] = {0x5010, -1}, [NGPIO23_BBU_ADC_SPI_DI_SEC_MSTR_TDI_1P8 ] = {0x5018, -1}, [NGPIO27_1P8 ] = {0x5020, -1}, [NGPIO24_SEC_MSTR_TDO_1P8 ] = {0x5028, -1}, [UART2_RXD_NUART3_RXD_NFC_INT_1P8 ] = {0x5030, -1}, [NGPIO25_SEC_MSTR_TMS_1P8 ] = {0x5038, -1}, [UART2_TXD_NUART3_TXD_1P8 ] = {0x5040, -1}, [NGPIO26_SEC_MSTR_TRST_B_1P8 ] = {0x5048, -1}, [UART2_CTS_B_NUART3_CTS_B_I2C_NFC_SCL_1P8 ] = {0x5050, -1}, [UART2_RTS_B_NUART3_RTS_B_I2C_NFC_SDA_1P8 ] = {0x5058, -1}, }; static struct gpio_pad_info northwest_pads_info[] = { [MOCA_SPI_CS1N_1P8 ] = {0x4408, -1}, [RF_FE_RST_NGPIO4_1P8 ] = {0x4418, -1}, [EXT_INTR_05_1P8 ] = {0x4420, -1}, [EXT_INTR_01_NGPIO7_1P8 ] = {0x4428, -1}, [NGPIO1_1P8 ] = {0x4430, -1}, [RF_FE_SPI_CLK_1P8 ] = {0x4438, -1}, [NGPIO3_SGMII1_INT_1P8 ] = {0x4440, -1}, [RF_FE_SPI_MISO_1P8 ] = {0x4448, -1}, [MOCA_SPI_CS0N_1P8 ] = {0x4450, -1}, [NGPIO2_SGMII0_INT_1P8 ] = {0x4458, -1}, [NGPIO0_1P8 ] = {0x4460, -1}, [RF_FE_SPI_CS0_1P8 ] = {0x4468, -1}, [EXT_INTR_00_NGPIO6_1P8 ] = {0x4470, -1}, [RF_FE_SPI_MOSI_1P8 ] = {0x4478, -1}, [MOCA_RESET_NGPIO5_1P8 ] = {0x4480, -1}, [I2C1_SDA__1P8 ] = {0x4800, -1}, [I2C1_SCL_CCU_PLL_LOCKEN_1P8] = {0x4808, -1}, [UART0_TXD_1P8 ] = {0x4810, -1}, [EPGA_CLK_1P8 ] = {0x4818, -1}, [UART0_RXD_1P8 ] = {0x4820, -1}, [EPGA_PWDN_N_1P8 ] = {0x4828, -1}, [EPGA_DATA_1P8 ] = {0x4838, -1}, [EPGA_WE_N_1P8 ] = {0x4848, -1}, [I2C0_SCL_NGPIO13_1P8 ] = {0x4850, -1}, [I2C0_SDA_NGPIO12_1P8 ] = {0x4858, -1}, [BBU_DCIN_DETECT_3P3 ] = {0x4C00, -1}, [BBU_EPROM2_3P3 ] = {0x4C08, -1}, [BBU_PWM0_3P3 ] = {0x4C10, -1}, [BBU_EPROM1_3P3 ] = {0x4C20, -1}, [BBU_ENABLE2_3P3 ] = {0x4C28, -1}, [BBU_UART_TX_NGPIO16_3P3 ] = {0x4C30, -1}, [BBU_UART_RX_NGPIO17_3P3 ] = {0x4C40, -1}, [BBU_ENABLE1_3P3 ] = {0x4C48, -1}, [BBU_UART_RTS_B_PWM2_3P3 ] = {0x4C50, -1}, [BBU_UART_CTS_B_PWM1_3P3 ] = {0x4C58, -1}, }; static struct gpio_pad_info southeast_pads_info[] = { [PCIE_WAKE0_B_3P3 ] = { 0x4400, -1}, [PCIE_WAKE1_B_AVB_SNAPSHOT_3P3 ] = { 0x4408, -1}, [PCIE_CLKREQ0B_3P3 ] = { 0x4418, -1}, [PCIE_CLKREQ3B_NGPIO10_MMC3_WP_3P3 ] = { 0x4428, -1}, [PCIE_CLKREQ1B_BCFG_AVB_SNAPSHOT_3P3 ] = { 0x4438, -1}, [PCIE_CLKREQ2B_NGPIO9_3P3 ] = { 0x4448, -1}, [DOCSIS_UART_RXD_3P3 ] = { 0x4800, -1}, [DOCSIS_UART_TXD_3P3 ] = { 0x4808, -1}, [EXT_INT2_NGPIO8_I2C6_SCL_3P3 ] = { 0x4810, -1}, [DJTAG_TRSTB_3P3 ] = { 0x4818, -1}, [EXT_INT3_NGPIO9_I2C6_SDA_3P3 ] = { 0x4820, -1}, [DJTAG_TMS_3P3 ] = { 0x4828, -1}, [EXT_INT4_ARM11_RTCK_3P3 ] = { 0x4830, -1}, [DJTAG_TCK_3P3 ] = { 0x4838, -1}, [DJTAG_EMU0_3P3 ] = { 0x4840, -1}, [DJTAG_TDI_3P3 ] = { 0x4848, -1}, [DJTAG_EMU1_3P3 ] = { 0x4850, -1}, [DJTAG_TDO_3P3 ] = { 0x4858, -1}, [CODEC_CS0_N_3P3 ] = { 0x4C00, -1}, [DECT_UART_RTS_B_DECT_SPI_CS_3P3 ] = { 0x4C08, -1}, [TDM0_CLK_TDM1_CLK_3P3 ] = { 0x4C18, -1}, [DECT_UART_CTS_B_DECT_SPI_CLK_3P3 ] = { 0x4C20, -1}, [CODEC_CLK_3P3 ] = { 0x4C28, -1}, [TDM0_FS_TDM1_FS_3P3 ] = { 0x4C30, -1}, [DECT_UART_RX_DECT_SPI_MISO_3P3 ] = { 0x4C38, -1}, [TDM0_TX_TDM1_TX_3P3 ] = { 0x4C48, -1}, [DECT_UART_TX_DECT_SPI_MOSI_3P3 ] = { 0x4C50, -1}, [CODEC_DATA_OUT_3P3 ] = { 0x4C58, -1}, [TDM0_RX_TDM1_RX_3P3 ] = { 0x4C60, -1}, [DECT_IRQ_3P3 ] = { 0x4C68, -1}, [CODEC_DATA_IN_3P3 ] = { 0x4C70, -1}, [TS0_CLK_IN_OUT_NGPIO7_1P8 ] = { 0x5000, -1}, [TS3_CLK_IN_OUT_BCFG_AVB_SNAPSHOT_1P8 ] = { 0x5008, -1}, [TS0_SYNC_IN_OUT_NGPIO8_1P8 ] = { 0x5010, -1}, [TS3_DAV_IN_OUT_SGMII0_INT_1P8 ] = { 0x5018, -1}, [TS3_SYNC_IN_OUT_EXT_INT06_1P8 ] = { 0x5020, -1}, [TS0_DAV_IN_OUT_NGPIO9_1P8 ] = { 0x5028, -1}, [TS0_DATA_IN_OUT_NGPIO10_1P8 ] = { 0x5030, -1}, [TS3_DATA__IN_OUT_SGMII1_INT_1P8 ] = { 0x5038, -1}, [TS1_CLK_IN_OUT_NGPIO11_1P8 ] = { 0x5040, -1}, [TS2_DATA_IN_OUT_EXT_INT07_1P8 ] = { 0x5048, -1}, [TS1_SYNC_IN_OUT_NGPIO12_1P8 ] = { 0x5058, -1}, [TS2_CLK_IN_OUT_NGPIO15_1P8 ] = { 0x5060, -1}, [TS2_DAV_IN_OUT_NGPIO17_1P8 ] = { 0x5068, -1}, [TS1_DAV_IN_OUT_NGPIO13_1P8 ] = { 0x5070, -1}, [TS2_SYNC_IN_OUT_NGPIO16_1P8 ] = { 0x5078, -1}, [TS1_DATA_IN_OUT_NGPIO14_1P8 ] = { 0x5080, -1}, [TS4_SYNC_IN_OUT_NGPIO8_1P8_3P3 ] = { 0x5400, -1}, [TS6_DATA_IN_OUT_BCFG_AVB_SNAPSHOT_1P8_3P3] = { 0x5408, -1}, [TS4_DAV_IN_OUT_NGPIO9_1P8_3P3 ] = { 0x5410, -1}, [TS6_CLK_IN_OUT_NGPIO15_1P8_3P3 ] = { 0x5418, -1}, [TS7_DAV_IN_OUT_EXT_INT08_1P8_3P3 ] = { 0x5420, -1}, [TS4_CLK_IN_OUT_NGPIO7_1P8_3P3 ] = { 0x5428, -1}, [TS5_SYNC_IN_OUT_NGPIO12_1P8_3P3 ] = { 0x5430, -1}, [TS7_CLK_IN_OUT_PCI_WAKE3_B_1P8_3P3 ] = { 0x5438, -1}, [TS4_DATA_IN_OUT_NGPIO10_1P8_3P3 ] = { 0x5440, -1}, [TS6_SYNC_IN_OUT_NGPIO16_1P8_3P3 ] = { 0x5448, -1}, [TS7_DATA_IN_OUT_EXT_INT09_1P8_3P3 ] = { 0x5450, -1}, [TS5_DAV_IN_OUT_NGPIO13_1P8_3P3 ] = { 0x5460, -1}, [TS7_SYNC_IN_OUT_PCI_WAKE4_B_1P8_3P3 ] = { 0x5468, -1}, [TS5_CLK_IN_OUT_NGPIO11_1P8_3P3 ] = { 0x5470, -1}, [TS6_DAV_IN_OUT_NGPIO17_1P8_3P3 ] = { 0x5478, -1}, [TS5_DATA_IN_OUT_NGPIO14_1P8_3P3 ] = { 0x5480, -1}, [SC1_INS_3P3 ] = { 0x5800, -1}, [SC1_VSEL_3P3 ] = { 0x5808, -1}, [SPI1_MOSI_3P3 ] = { 0x5810, -1}, [SC1_DIO_3P3 ] = { 0x5818, -1}, [SPI1_CLK_3P3 ] = { 0x5820, -1}, [SC1_VEN_3P3 ] = { 0x5828, -1}, [SPI1_MISO_3P3 ] = { 0x5830, -1}, [SC1_RST_3P3 ] = { 0x5838, -1}, [SPI1_CS0_B_3P3 ] = { 0x5840, -1}, [SC1_CLK_3P3 ] = { 0x5848, -1}, [SPARE3_3P3 ] = { 0x5850, -1}, [SC0_INS_3P3 ] = { 0x5C00, -1}, [SC0_VSEL_3P3 ] = { 0x5C08, -1}, [SC0_DIO_3P3 ] = { 0x5C10, -1}, [SC0_CLK_3P3 ] = { 0x5C18, -1}, [SC0_RST_3P3 ] = { 0x5C20, -1}, [SC0_VEN_3P3 ] = { 0x5C28, -1}, [SC1_AUX2_SPI1_CS1_B_3P3 ] = { 0x5C38, -1}, [SC1_AUX1_3P3 ] = { 0x5C48, -1}, [SC0_AUX2_3P3 ] = { 0x5C50, -1}, [SC0_AUX1_3P3 ] = { 0x5C58, -1}, }; static struct gpio_pad_info southwest_pads_info[] = { [MMC1_D0_1P8 ] = { 0x4400, -1}, [MMC1_D5_1P8 ] = { 0x4408, -1}, [MMC1_D6_1P8 ] = { 0x4410, -1}, [MMC1_D2_1P8 ] = { 0x4418, -1}, [MMC1_D7_1P8 ] = { 0x4420, -1}, [MMC1_CLK_1P8 ] = { 0x4428, -1}, [MMC1_D1_1P8 ] = { 0x4430, -1}, [MMC1_D4_SD_WE_1P8 ] = { 0x4438, -1}, [MMC1_CMD_1P8 ] = { 0x4440, -1}, [MMC1_DS_1P8 ] = { 0x4448, -1}, [MMC1_D3_CD_B_1P8 ] = { 0x4450, -1}, [MMC2_D0_NGPIO11_1P8 ] = { 0x4800, -1}, [MMC2_D5_NGPIO16_1P8 ] = { 0x4808, -1}, [MMC2_D6_I2C2_SCL_NGPIO17_1P8] = { 0x4810, -1}, [MMC2_D2_NGPIO13_1P8 ] = { 0x4818, -1}, [MMC2_D7_I2C2_SDA_1P8 ] = { 0x4820, -1}, [MMC2_CLK_NGPIO6_1P8 ] = { 0x4828, -1}, [MMC2_D1_NGPIO12_1P8 ] = { 0x4830, -1}, [MMC2_D4_SD_WE_NGPIO15_1P8 ] = { 0x4838, -1}, [MMC2_CMD_NGPIO10_1P8 ] = { 0x4840, -1}, [MMC2_DS_MMC2_RCLK_1P8 ] = { 0x4848, -1}, [MMC2_D3_CD_B_NGPIO14_1P8 ] = { 0x4850, -1}, [MMC3_D1_1P8_3P3 ] = { 0x4C00, -1}, [MMC3_CLK_1P8_3P3 ] = { 0x4C08, -1}, [MMC3_D3_I2C2_SDA_1P8_3P3 ] = { 0x4C10, -1}, [MMC3_D2_I2C2_SCL_1P8_3P3 ] = { 0x4C18, -1}, [MMC3_CMD_1P8_3P3 ] = { 0x4C20, -1}, [MMC3_D0_1P8_3P3 ] = { 0x4C28, -1}, [JTAG_TRST_B_1P8 ] = { 0x5000, -1}, [CX_PREQ_B_1P8 ] = { 0x5008, -1}, [CX_PRDY_B_1P8 ] = { 0x5010, -1}, [JTAG_TDO_1P8 ] = { 0x5018, -1}, [JTAG_TCK_1P8 ] = { 0x5020, -1}, [JTAG_TMS_1P8 ] = { 0x5038, -1}, [MMC1_RESET_B_1P8 ] = { 0x5040, -1}, [JTAG_TDI_1P8 ] = { 0x5048, -1}, [MMC2_RST_B_MMC3_WP_1P8 ] = { 0x5050, -1}, [MMC3_CD_B_1P8 ] = { 0x5058, -1}, }; static struct gpio_pad_info virtual_pads_info[] = { [VIRTUAL0] ={ 0x4400, -1}, [VIRTUAL1] ={ 0x4408, -1}, [VIRTUAL2] ={ 0x4410, -1}, [VIRTUAL3] ={ 0x4418, -1}, [VIRTUAL4] ={ 0x4420, -1}, [VIRTUAL5] ={ 0x4428, -1}, [VIRTUAL6] ={ 0x4430, -1}, [VIRTUAL7] ={ 0x4438, -1}, }; static struct gpio_pad_info west_pads_info[] = { [USB_OC1_B_1P8 ] = { 0x4410, -1}, [USB_OC0_B_1P8 ] = { 0x4428, -1}, [I2C3_SCL_NGPIO11_I2C6_SCL_1P8 ] = { 0x4438, -1}, [SGMII0_INT_1P8 ] = { 0x4440, -1}, [I2C3_SDA_SGMII0_INT_I2C6_SDA_1P8] = { 0x4458, -1}, [GBE_1_TXDATA_3_1P8_3P3 ] = { 0x4800, -1}, [GBE_1_TXDATA_2_1P8_3P3 ] = { 0x4808, -1}, [GBE_1_TXDATA_1_1P8_3P3 ] = { 0x4810, -1}, [GBE_1_TXCLK_1P8_3P3 ] = { 0x4818, -1}, [GBE_1_TXCTL_1P8_3P3 ] = { 0x4820, -1}, [GBE_1_TXDATA_0_1P8_3P3 ] = { 0x4828, -1}, [GBE_1_RXCLK_1P8_3P3 ] = { 0x4830, -1}, [GBE_1_RXCTL_1P8_3P3 ] = { 0x4838, -1}, [GBE_1_RXDATA_0_1P8_3P3 ] = { 0x4840, -1}, [GBE_1_RXDATA_3_1P8_3P3 ] = { 0x4848, -1}, [GBE_1_RXDATA_2_1P8_3P3 ] = { 0x4850, -1}, [GBE_1_RXDATA_1_1P8_3P3 ] = { 0x4858, -1}, [GBE_1_MDC_SGMII1_INT_1P8_3P3 ] = { 0x4860, -1}, [GBE_1_MDIO_SGMII0_INT_1P8_3P3 ] = { 0x4868, -1}, [SPARE2_1P8_3P3 ] = { 0x4870, -1}, [GBE_1_INT_SGMII0_INT_1P8_3P3 ] = { 0x4878, -1}, [GBE_1_RESET_NGPIO19_1P8_3P3 ] = { 0x4880, -1}, [GBE_0_TXDATA_3_1P8_3P3 ] = { 0x4C00, -1}, [GBE_0_TXDATA_2_1P8_3P3 ] = { 0x4C08, -1}, [GBE_0_TXDATA_1_1P8_3P3 ] = { 0x4C10, -1}, [GBE_0_TXCLK_1P8_3P3 ] = { 0x4C18, -1}, [GBE_0_TXCTL_1P8_3P3 ] = { 0x4C20, -1}, [GBE_0_TXDATA_0_1P8_3P3 ] = { 0x4C28, -1}, [GBE_0_RXCLK_1P8_3P3 ] = { 0x4C30, -1}, [GBE_0_RXCTL_1P8_3P3 ] = { 0x4C38, -1}, [GBE_0_RXDATA_0_1P8_3P3 ] = { 0x4C40, -1}, [GBE_0_RXDATA_3_1P8_3P3 ] = { 0x4C48, -1}, [GBE_0_RXDATA_2_1P8_3P3 ] = { 0x4C50, -1}, [GBE_0_RXDATA_1_1P8_3P3 ] = { 0x4C58, -1}, [GBE_0_MDC_1P8_3P3 ] = { 0x4C60, -1}, [GBE_0_MDIO_1P8_3P3 ] = { 0x4C68, -1}, [SPARE1_1P8_3P3 ] = { 0x4C70, -1}, [GBE_0_INT_1P8_3P3 ] = { 0x4C78, -1}, [NGPIO18_1P8_3P3 ] = { 0x4C80, -1}, }; /* gpio bases are rounded up to next multiple of ten and incremented by ten */ #define CE2700_GPIO_SOUTHWEST_BASE (0) #define CE2700_GPIO_NORTHEAST_BASE (CE2700_GPIO_SOUTHWEST_BASE + ((ARRAY_SIZE(southwest_pads_info)/10 + 2)*10)) #define CE2700_GPIO_NORTHWEST_BASE (CE2700_GPIO_NORTHEAST_BASE + ((ARRAY_SIZE(northeast_pads_info)/10 + 2)*10)) #define CE2700_GPIO_SOUTHEAST_BASE (CE2700_GPIO_NORTHWEST_BASE + ((ARRAY_SIZE(northwest_pads_info)/10 + 2)*10)) #define CE2700_GPIO_VIRTUAL_BASE (CE2700_GPIO_SOUTHEAST_BASE + ((ARRAY_SIZE(southeast_pads_info)/10 + 2)*10)) #define CE2700_GPIO_WEST_BASE (CE2700_GPIO_VIRTUAL_BASE + ((ARRAY_SIZE(virtual_pads_info)/10 + 2)*10)) #define CE2700_GPIO_SOUTHWEST_IRQBASE (256) #define CE2700_GPIO_NORTHEAST_IRQBASE (CE2700_GPIO_SOUTHWEST_IRQBASE + (ARRAY_SIZE(southwest_pads_info))) #define CE2700_GPIO_NORTHWEST_IRQBASE (CE2700_GPIO_NORTHEAST_IRQBASE + (ARRAY_SIZE(northeast_pads_info))) #define CE2700_GPIO_SOUTHEAST_IRQBASE (CE2700_GPIO_NORTHWEST_IRQBASE + (ARRAY_SIZE(northwest_pads_info))) #define CE2700_GPIO_VIRTUAL_IRQBASE (CE2700_GPIO_SOUTHEAST_IRQBASE + (ARRAY_SIZE(southeast_pads_info))) #define CE2700_GPIO_WEST_IRQBASE (CE2700_GPIO_VIRTUAL_IRQBASE + (ARRAY_SIZE(virtual_pads_info))) static struct gpio_bank intelce2700_banks_platform[] = { { .name = "INT33FF:00", .base = CE2700_GPIO_SOUTHWEST_BASE, .irq_base = CE2700_GPIO_SOUTHWEST_IRQBASE, .ngpio = ARRAY_SIZE(southwest_pads_info), .pads_info = southwest_pads_info, .community_name = "southwest", }, { .name = "INT33FF:01", .base = CE2700_GPIO_NORTHEAST_BASE, .irq_base = CE2700_GPIO_NORTHEAST_IRQBASE, .ngpio = ARRAY_SIZE(northeast_pads_info), .pads_info = northeast_pads_info, .community_name = "northeast", }, { .name = "INT33FF:02", .base = CE2700_GPIO_NORTHWEST_BASE, .irq_base = CE2700_GPIO_NORTHWEST_IRQBASE, .ngpio = ARRAY_SIZE(northwest_pads_info), .pads_info = northwest_pads_info, .community_name = "northwest", }, { .name = "INT33FF:03", .base = CE2700_GPIO_SOUTHEAST_BASE, .irq_base = CE2700_GPIO_SOUTHEAST_IRQBASE, .ngpio = ARRAY_SIZE(southeast_pads_info), .pads_info = southeast_pads_info, .community_name = "southeast", }, { .name = "INT33FF:04", .base = CE2700_GPIO_VIRTUAL_BASE, .irq_base = CE2700_GPIO_VIRTUAL_IRQBASE, .ngpio = ARRAY_SIZE(virtual_pads_info), .pads_info = virtual_pads_info, .community_name = "virtual", }, { .name = "INT33FF:05", .base = CE2700_GPIO_WEST_BASE, .irq_base = CE2700_GPIO_WEST_IRQBASE, .ngpio = ARRAY_SIZE(west_pads_info), .pads_info = west_pads_info, .community_name = "west", }, }; struct intelce2700_gpio { struct gpio_chip chip; struct platform_device *pdev; spinlock_t lock; void __iomem *reg_base; struct gpio_pad_info *pad_info; struct irq_domain *domain; int intr_lines[MAX_INTR_LINE_NUM]; int irq_base; char *community_name; }; #define to_intelce2700_priv(chip) container_of(chip, struct intelce2700_gpio, chip) static inline void __iomem *intelce2700_gpio_reg(struct gpio_chip *chip, unsigned offset, int reg) { struct intelce2700_gpio *cg = to_intelce2700_priv(chip); u32 reg_offset; void __iomem *ptr; if (reg == CE2700_GPIO_INT_STAT_REG || reg == CE2700_GPIO_INT_MASK_REG) reg_offset = 0; else reg_offset = cg->pad_info[offset].offset; //reg_offset = FAMILY0_PAD_REGS_OFF + // FAMILY_PAD_REGS_SIZE * (offset / MAX_FAMILY_PAD_GPIO_NO) + // GPIO_REGS_SIZE * (offset % MAX_FAMILY_PAD_GPIO_NO); ptr = (void __iomem *) (cg->reg_base + reg_offset + reg); return ptr; } static inline u32 intelce2700_readl(void __iomem *reg) { u32 value; rmb(); value = readl(reg); return value; } static inline void intelce2700_writel(u32 value, void __iomem *reg) { rmb(); writel(value, reg); /* simple readback to confirm the bus transferring done */ //readl(reg); } static int intelce2700_gpio_request(struct gpio_chip *chip, unsigned offset) { struct intelce2700_gpio *cg = to_intelce2700_priv(chip); unsigned long flags, value; void __iomem *reg; //if (cg->pad_info[offset].family < 0) if (offset >= cg->chip.ngpio) return -EINVAL; spin_lock_irqsave(&cg->lock, flags); reg = intelce2700_gpio_reg(chip, offset, CE2700_GPIO_PADCTRL0_REG); value = intelce2700_readl(reg); cg->pad_info[offset].init_value = value; value |= CE2700_GPIO_GPIO_EN; intelce2700_writel(value, reg); spin_unlock_irqrestore(&cg->lock, flags); return 0; } static void intelce2700_gpio_free(struct gpio_chip *chip, unsigned offset) { struct intelce2700_gpio *cg = to_intelce2700_priv(chip); unsigned long flags, value; void __iomem *reg; spin_lock_irqsave(&cg->lock, flags); reg = intelce2700_gpio_reg(chip, offset, CE2700_GPIO_PADCTRL0_REG); value = cg->pad_info[offset].init_value; intelce2700_writel(value, reg); spin_unlock_irqrestore(&cg->lock, flags); } static void intelce2700_update_irq_type(struct intelce2700_gpio *cg, unsigned type, void __iomem *reg) { u32 value; value = intelce2700_readl(reg); value &= ~CE2700_GPIO_INT_CFG_MASK; value &= ~CE2700_GPIO_INV_RX_DATA; if (type & IRQ_TYPE_EDGE_BOTH) { if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) value |= CE2700_GPIO_TRIG_EDGE_BOTH; else if (type & IRQ_TYPE_EDGE_RISING) value |= CE2700_GPIO_TRIG_EDGE_RISING; else if (type & IRQ_TYPE_EDGE_FALLING) value |= CE2700_GPIO_TRIG_EDGE_FALLING; } else if (type & IRQ_TYPE_LEVEL_MASK) { value |= CE2700_GPIO_TRIG_LEVEL; if (type & IRQ_TYPE_LEVEL_LOW) value |= CE2700_GPIO_INV_RX_DATA; } intelce2700_writel(value, reg); } /* BIOS programs IntSel bits for shared interrupt. * GPIO driver follows it. */ static void pad_intr_line_save(struct intelce2700_gpio *cg, unsigned offset) { u32 value; u32 intr_line; void __iomem *reg = intelce2700_gpio_reg(&cg->chip, offset, CE2700_GPIO_PADCTRL0_REG); struct gpio_pad_info *pad_info = cg->pad_info + offset; value = intelce2700_readl(reg); intr_line = (value & CE2700_GPIO_INT_SEL_MASK) >> 28; pad_info->interrupt_line = (int)intr_line; cg->intr_lines[intr_line] = (int)offset; } static int intelce2700_irq_type(struct irq_data *d, unsigned type) { struct intelce2700_gpio *cg = irq_data_get_irq_chip_data(d); u32 offset = irqd_to_hwirq(d); void __iomem *reg; unsigned long flags; int ret = 0; if (offset >= cg->chip.ngpio) return -EINVAL; //if (cg->pad_info[offset].family < 0) // return -EINVAL; spin_lock_irqsave(&cg->lock, flags); /* Pins which can be used as shared interrupt are configured in BIOS. * Driver trusts BIOS configurations and assigns different handler * according to the irq type. * * Driver needs to save the mapping between each pin and * its interrupt line. * 1. If the pin cfg is locked in BIOS: * Trust BIOS has programmed IntWakeCfg bits correctly, * driver just needs to save the mapping. * 2. If the pin cfg is not locked in BIOS: * Driver programs the IntWakeCfg bits and save the mapping. * */ if (!PAD_CFG_LOCKED(offset)) { reg = intelce2700_gpio_reg(&cg->chip, offset, CE2700_GPIO_PADCTRL1_REG); intelce2700_update_irq_type(cg, type, reg); } pad_intr_line_save(cg, offset); if (type & IRQ_TYPE_EDGE_BOTH) irq_set_handler_locked(d, handle_edge_irq); else if (type & IRQ_TYPE_LEVEL_MASK) irq_set_handler_locked(d, handle_level_irq); spin_unlock_irqrestore(&cg->lock, flags); return ret; } static int intelce2700_gpio_get(struct gpio_chip *chip, unsigned offset) { struct intelce2700_gpio *cg = to_intelce2700_priv(chip); void __iomem *reg; u32 value; //if (cg->pad_info[offset].family < 0) if (unlikely(offset >= cg->chip.ngpio)) return -EINVAL; reg = intelce2700_gpio_reg(chip, offset, CE2700_GPIO_PADCTRL0_REG); value = intelce2700_readl(reg); return value & CE2700_GPIO_GPIO_RX_STAT; } static void intelce2700_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct intelce2700_gpio *cg = to_intelce2700_priv(chip); void __iomem *reg; unsigned long flags; u32 old_val; //if (cg->pad_info[offset].family < 0) if (unlikely(offset >= cg->chip.ngpio)) { printk("GPIO set offset is greater ngpio %d\n", cg->chip.ngpio); return; } reg = intelce2700_gpio_reg(chip, offset, CE2700_GPIO_PADCTRL0_REG); spin_lock_irqsave(&cg->lock, flags); old_val = intelce2700_readl(reg); if (value) intelce2700_writel(old_val | CE2700_GPIO_GPIO_TX_STAT, reg); else intelce2700_writel(old_val & ~CE2700_GPIO_GPIO_TX_STAT, reg); spin_unlock_irqrestore(&cg->lock, flags); } static int intelce2700_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { struct intelce2700_gpio *cg = to_intelce2700_priv(chip); void __iomem *reg; unsigned long flags; u32 value; //if (cg->pad_info[offset].family < 0) if (offset >= cg->chip.ngpio) return -EINVAL; if (PAD_CFG_LOCKED(offset)) { printk("Set as input gpio pad locked \n"); return 0; } reg = intelce2700_gpio_reg(chip, offset, CE2700_GPIO_PADCTRL0_REG); spin_lock_irqsave(&cg->lock, flags); value = intelce2700_readl(reg) & (~CE2700_GPIO_GPIO_CFG_MASK); /* Disable TX and Enable RX */ value |= CE2700_GPIO_GPIO_RX_EN; intelce2700_writel(value, reg); spin_unlock_irqrestore(&cg->lock, flags); return 0; } static int intelce2700_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { struct intelce2700_gpio *cg = to_intelce2700_priv(chip); void __iomem *ctrl0, *ctrl1; unsigned long flags; u32 reg_val; //if (cg->pad_info[offset].family < 0) if (offset >= cg->chip.ngpio) return -EINVAL; if (PAD_CFG_LOCKED(offset)) { printk("gpio pad locked \n"); return 0; } ctrl0 = intelce2700_gpio_reg(chip, offset, CE2700_GPIO_PADCTRL0_REG); ctrl1 = intelce2700_gpio_reg(chip, offset, CE2700_GPIO_PADCTRL1_REG); spin_lock_irqsave(&cg->lock, flags); /* Make sure interrupt of this pad is disabled */ intelce2700_update_irq_type(cg, IRQ_TYPE_NONE, ctrl1); reg_val = intelce2700_readl(ctrl0) & (~CE2700_GPIO_GPIO_CFG_MASK); /* Enable both RX and TX, control TX State */ if (value) reg_val |= CE2700_GPIO_GPIO_TX_STAT; else reg_val &= ~CE2700_GPIO_GPIO_TX_STAT; intelce2700_writel(reg_val, ctrl0); spin_unlock_irqrestore(&cg->lock, flags); return 0; } static int intelce2700_get_gpio_direction(struct gpio_chip *chip, unsigned offset) { struct intelce2700_gpio *cg = to_intelce2700_priv(chip); void __iomem *ctrl0; u32 reg_val; //if (cg->pad_info[offset].family < 0) if (offset >= cg->chip.ngpio) return -EINVAL; ctrl0 = intelce2700_gpio_reg(chip, offset, CE2700_GPIO_PADCTRL0_REG); reg_val = intelce2700_readl(ctrl0) & CE2700_GPIO_GPIO_CFG_MASK; /* if output enabled, set the direction to out. * Otherwise, assume it as input*/ if (reg_val < CE2700_GPIO_GPIO_RX_EN) return 0; else return 1; } static void intelce2700_irq_unmask(struct irq_data *d) { struct intelce2700_gpio *cg = irq_data_get_irq_chip_data(d); u32 offset = irqd_to_hwirq(d); int interrupt_line; u32 value; void __iomem *reg = intelce2700_gpio_reg(&cg->chip, 0, CE2700_GPIO_INT_MASK_REG); unsigned long flags; struct gpio_pad_info *pad_info = cg->pad_info + offset; //if (cg->pad_info[offset].family < 0) if (offset >= cg->chip.ngpio) return; if (offset >= cg->chip.ngpio) return; spin_lock_irqsave(&cg->lock, flags); interrupt_line = pad_info->interrupt_line; /* Unmask if this GPIO has valid interrupt line */ if (interrupt_line >= 0) { value = intelce2700_readl(reg); value |= (1 << interrupt_line); intelce2700_writel(value, reg); } else { dev_warn(&cg->pdev->dev, "Trying to unmask GPIO intr which is not allocated\n"); } spin_unlock_irqrestore(&cg->lock, flags); } static void intelce2700_irq_mask(struct irq_data *d) { struct intelce2700_gpio *cg = irq_data_get_irq_chip_data(d); u32 offset = irqd_to_hwirq(d); int interrupt_line; u32 value; unsigned long flags; void __iomem *reg = intelce2700_gpio_reg(&cg->chip, 0, CE2700_GPIO_INT_MASK_REG); struct gpio_pad_info *pad_info = cg->pad_info + offset; //if (cg->pad_info[offset].family < 0) if (offset >= cg->chip.ngpio) return; if (offset >= cg->chip.ngpio) return; spin_lock_irqsave(&cg->lock, flags); interrupt_line = pad_info->interrupt_line; /* Mask if this GPIO has valid interrupt line */ if (interrupt_line >= 0) { value = intelce2700_readl(reg); value &= (~(1 << interrupt_line)); intelce2700_writel(value, reg); } else { dev_warn(&cg->pdev->dev, "Trying to mask GPIO intr which is not allocated\n"); } spin_unlock_irqrestore(&cg->lock, flags); } static int intelce2700_irq_wake(struct irq_data *d, unsigned on) { return 0; } static void intelce2700_irq_ack(struct irq_data *d) { struct intelce2700_gpio *cg = irq_data_get_irq_chip_data(d); u32 offset = irqd_to_hwirq(d); struct gpio_pad_info *pad_info = cg->pad_info + offset; void __iomem *stat_reg = intelce2700_gpio_reg(&cg->chip, 0, CE2700_GPIO_INT_STAT_REG); unsigned long flags; spin_lock_irqsave(&cg->lock, flags); /* Ack if this GPIO has valid interrupt line */ if (pad_info->interrupt_line >= 0) { intelce2700_writel(BIT(pad_info->interrupt_line), stat_reg); } else { dev_warn(&cg->pdev->dev, "Trying to ack GPIO intr which is not allocated\n"); } spin_unlock_irqrestore(&cg->lock, flags); } static void intelce2700_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { struct intelce2700_gpio *cg = container_of(chip, struct intelce2700_gpio, chip); int i; unsigned long flags; u32 ctrl0, ctrl1, offs; void __iomem *reg; const char *label; char *config; char *int_type; u32 value; spin_lock_irqsave(&cg->lock, flags); reg = intelce2700_gpio_reg(chip, 0, CE2700_GPIO_INT_STAT_REG); seq_printf(s, "\tCE2700_GPIO_INT_STAT_REG: 0x%x\n", intelce2700_readl(reg)); reg = intelce2700_gpio_reg(chip, 0, CE2700_GPIO_INT_MASK_REG); seq_printf(s, "\tCE2700_GPIO_INT_MASK_REG: 0x%x\n", intelce2700_readl(reg)); for (i = 0; i < 16; i++) seq_printf(s, "\tintline: %d, offset: %d\n", i, cg->intr_lines[i]); for (i = 0; i < chip->ngpio; i++) { //if (cg->pad_info[i].family < 0) { // seq_printf(s, "\tgpio-%-3d Invalid\n", i + chip->base); // continue; //} label = gpiochip_is_requested(chip, i); if (!label) label = "Unrequested"; offs = cg->pad_info[i].offset; ctrl0 = intelce2700_readl(intelce2700_gpio_reg(chip, i, CE2700_GPIO_PADCTRL0_REG)); ctrl1 = intelce2700_readl(intelce2700_gpio_reg(chip, i, CE2700_GPIO_PADCTRL1_REG)); value = ctrl0 & CE2700_GPIO_GPIO_CFG_MASK; if (value < CE2700_GPIO_GPIO_RX_EN) config = "out "; else if (value == CE2700_GPIO_GPIO_RX_EN) config = "in "; else if (value == CE2700_GPIO_GPIO_HZ) config = "Hi-Z"; else config = " "; value = ctrl1 & CE2700_GPIO_INT_CFG_MASK; if (value == CE2700_GPIO_INTR_DISABLE) int_type = "disabled "; else if (value == CE2700_GPIO_TRIG_EDGE_FALLING) int_type = "falling "; else if (value == CE2700_GPIO_TRIG_EDGE_RISING) int_type = "rising "; else if (value == CE2700_GPIO_TRIG_EDGE_BOTH) int_type = "both "; else if (value == CE2700_GPIO_TRIG_LEVEL) int_type = (ctrl1 & CE2700_GPIO_INV_RX_DATA) ? "level-low " : "level-high"; else int_type = " "; seq_printf(s, "\tgpio-%-3d (%-20.20s) %s-%-3d \t%s %s ", i + chip->base, label, cg->community_name, i, config, (ctrl0 & CE2700_GPIO_GPIO_RX_STAT) ? "high" : "low "); seq_printf(s, "offset:0x%03x mux:%d (%s)\t %s IntSel:%d ", offs, (ctrl0 & CE2700_GPIO_PAD_MODE_MASK) >> 16, (ctrl0 & CE2700_GPIO_GPIO_EN) ? "gpio" : "func", int_type, (ctrl0 & CE2700_GPIO_INT_SEL_MASK) >> 28); seq_printf(s, "\t ctrl0: 0x%-8x ctrl1: 0x%-8x\n", ctrl0, ctrl1); } spin_unlock_irqrestore(&cg->lock, flags); } static void intelce2700_irq_shutdown(struct irq_data *d) { struct intelce2700_gpio *cg = irq_data_get_irq_chip_data(d); u32 offset = irqd_to_hwirq(d); void __iomem *reg; unsigned long flags; //if (cg->pad_info[offset].family < 0) if (offset >= cg->chip.ngpio) return; reg = intelce2700_gpio_reg(&cg->chip, offset, CE2700_GPIO_PADCTRL1_REG); intelce2700_irq_mask(d); if (!PAD_CFG_LOCKED(offset)) { spin_lock_irqsave(&cg->lock, flags); intelce2700_update_irq_type(cg, IRQ_TYPE_NONE, reg); spin_unlock_irqrestore(&cg->lock, flags); } } static struct irq_chip intelce2700_irqchip = { .name = "CE2700-GPIO", .irq_mask = intelce2700_irq_mask, .irq_unmask = intelce2700_irq_unmask, .irq_set_type = intelce2700_irq_type, .irq_set_wake = intelce2700_irq_wake, .irq_ack = intelce2700_irq_ack, .irq_shutdown = intelce2700_irq_shutdown, }; static void intelce2700_gpio_irq_dispatch(struct intelce2700_gpio *cg) { u32 intr_line, mask; int offset; void __iomem *reg, *mask_reg; u32 pending; /* each GPIO controller has one INT_STAT reg */ reg = intelce2700_gpio_reg(&cg->chip, 0, CE2700_GPIO_INT_STAT_REG); mask_reg = intelce2700_gpio_reg(&cg->chip, 0, CE2700_GPIO_INT_MASK_REG); while ((pending = (intelce2700_readl(reg) & intelce2700_readl(mask_reg) & 0xFFFF))) { intr_line = __ffs(pending); offset = cg->intr_lines[intr_line]; if (unlikely(offset < 0)) { mask = BIT(intr_line); intelce2700_writel(mask, reg); dev_warn(&cg->pdev->dev, "unregistered shared irq\n"); continue; } generic_handle_irq(irq_find_mapping(cg->domain, offset)); } } static void intelce2700_gpio_irq_handler(struct irq_desc *desc) { struct irq_data *data = irq_desc_get_irq_data(desc); struct intelce2700_gpio *cg = irq_data_get_irq_handler_data(data); struct irq_chip *chip = irq_data_get_irq_chip(data); intelce2700_gpio_irq_dispatch(cg); chip->irq_eoi(data); } static void intelce2700_irq_init_hw(struct intelce2700_gpio *cg) { void __iomem *reg; reg = intelce2700_gpio_reg(&cg->chip, 0, CE2700_GPIO_INT_STAT_REG); intelce2700_writel(0xffff, reg); } static int intelce2700_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct intelce2700_gpio *cg = to_intelce2700_priv(chip); return irq_create_mapping(cg->domain, offset); } static int intelce2700_gpio_irq_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw) { struct intelce2700_gpio *cg = d->host_data; irq_set_chip_and_handler_name(virq, &intelce2700_irqchip, handle_simple_irq, "ce2700-gpio-irq"); irq_set_chip_data(virq, cg); return 0; } static const struct irq_domain_ops intelce2700_gpio_irq_ops = { .map = intelce2700_gpio_irq_map, .xlate = irq_domain_xlate_twocell, }; static int intelce2700_gpio_probe(struct platform_device *pdev) { int i; int irq; struct intelce2700_gpio *cg; struct gpio_chip *gc; struct resource *mem_rc; struct device *dev = &pdev->dev; struct gpio_bank *bank; int ret = 0; int nbanks = sizeof(intelce2700_banks_platform) / sizeof(struct gpio_bank); char path[32]; cg = devm_kzalloc(dev, sizeof(struct intelce2700_gpio), GFP_KERNEL); if (!cg) { dev_err(dev, "can't allocate intelce2700_gpio chip data\n"); return -ENOMEM; } cg->pdev = pdev; for (i = 0; i < nbanks; i++) { bank = intelce2700_banks_platform + i; if (!strcmp(pdev->name, bank->name)) { snprintf(path, sizeof(path), "/acpi/%s", pdev->name); strreplace(path, ':', '_'); dev->of_node = of_find_node_by_path(path); cg->chip.ngpio = bank->ngpio; cg->pad_info = bank->pads_info; cg->community_name = bank->community_name; bank->cg = cg; cg->irq_base = bank->irq_base; break; } } if (!bank || cg->chip.ngpio == 0) { dev_err(dev, "can't find %s\n", pdev->name); ret = -ENODEV; goto err; } mem_rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem_rc) { dev_err(dev, "missing MEM resource\n"); ret = -EINVAL; goto err; } irq = platform_get_irq(pdev, 0); if (irq <= 0) { dev_err(dev, "missing IRQ resource\n"); ret = irq; goto err; } cg->reg_base = devm_ioremap_resource(dev, mem_rc); if (IS_ERR(cg->reg_base)) { dev_err(dev, "error mapping resource\n"); ret = PTR_ERR(cg->reg_base); goto err; } spin_lock_init(&cg->lock); gc = &cg->chip; gc->label = dev_name(&pdev->dev); gc->owner = THIS_MODULE; gc->request = intelce2700_gpio_request; gc->free = intelce2700_gpio_free; gc->direction_input = intelce2700_gpio_direction_input; gc->direction_output = intelce2700_gpio_direction_output; gc->get = intelce2700_gpio_get; gc->set = intelce2700_gpio_set; gc->to_irq = intelce2700_gpio_to_irq; gc->dbg_show = intelce2700_gpio_dbg_show; gc->get_direction = intelce2700_get_gpio_direction; gc->base = bank->base; gc->can_sleep = 0; gc->parent = dev; /* Initialize interrupt lines array with negative value */ for (i = 0; i < MAX_INTR_LINE_NUM; i++) cg->intr_lines[i] = -1; cg->domain = irq_domain_add_simple(dev->of_node, cg->chip.ngpio, cg->irq_base, &intelce2700_gpio_irq_ops, cg); if (!cg->domain) { ret = -ENOMEM; goto err; } ret = gpiochip_add(gc); if (ret) { dev_err(dev, "failed adding intelce2700-gpio chip\n"); goto err; } intelce2700_irq_init_hw(cg); irq_set_handler_data(irq, cg); irq_set_chained_handler(irq, intelce2700_gpio_irq_handler); platform_set_drvdata(pdev, cg); dev_info(dev, "Intel CE2700 GPIO %s probed as Platform driver\n", pdev->name); return 0; err: devm_kfree(dev, cg); return ret; } static int intelce2700_gpio_remove(struct platform_device *pdev) { struct intelce2700_gpio *cg; if (!pdev || !platform_get_drvdata(pdev)) return -EINVAL; cg = platform_get_drvdata(pdev); gpiochip_remove(&cg->chip); return 0; } static const struct acpi_device_id intelce2700_gpio_acpi_match[] = { { "INT33FF", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, intelce2700_gpio_acpi_match); static struct platform_driver intelce2700_gpio_driver = { .probe = intelce2700_gpio_probe, .remove = intelce2700_gpio_remove, .driver = { .name = "intelce2700_gpio", .owner = THIS_MODULE, .acpi_match_table = ACPI_PTR(intelce2700_gpio_acpi_match), }, }; static int __init intelce2700_gpio_init(void) { return platform_driver_register(&intelce2700_gpio_driver); } static void __exit intelce2700_gpio_exit(void) { platform_driver_unregister(&intelce2700_gpio_driver); } #if defined(CONFIG_AVM_ENHANCED) #define DBG_TRC(args...) /*--- #define DBG_TRC(args...) printk(KERN_ERR args) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int x86_gpio_ctrl(unsigned int gpio_pin, enum _hw_gpio_function pin_mode, enum _hw_gpio_direction pin_dir) { struct gpio_chip *chip = gpio_to_chip(gpio_pin); unsigned int offset; struct intelce2700_gpio *cg; void __iomem *reg; unsigned long flags; u32 value; DBG_TRC("%s(%d) %s(mux=%d) %s\n", __func__, gpio_pin, pin_mode != GPIO_PIN ? "func" : "gpio", pin_mode, pin_dir == GPIO_OUTPUT_PIN ? "out" : "in"); if(chip == NULL) { printk(KERN_ERR"%s(%d) failed\n", __func__, gpio_pin); return -1; } cg = to_intelce2700_priv(chip); offset = gpio_pin - chip->base; if (offset >= chip->ngpio) { printk(KERN_ERR"%s(%d) false offset=%u - base=%u ngpio=%u\n", __func__, gpio_pin, offset, chip->base, chip->ngpio); return -EINVAL; } if(pin_dir == GPIO_OUTPUT_PIN) { intelce2700_gpio_direction_output(chip, offset, intelce2700_gpio_get(chip, offset)); } else { intelce2700_gpio_direction_input(chip, offset); } if (PAD_CFG_LOCKED(offset)) { printk(KERN_ERR"%s(%d) error: pad locked=\n", __func__, gpio_pin); return -1; } reg = intelce2700_gpio_reg(chip, offset, CE2700_GPIO_PADCTRL0_REG); spin_lock_irqsave(&cg->lock, flags); value = intelce2700_readl(reg); DBG_TRC("%s: gpio_pin=%u %x\n", __func__, gpio_pin, value); value &= ~CE2700_GPIO_PAD_MODE_MASK; if(pin_mode == GPIO_PIN) { value |= CE2700_GPIO_GPIO_EN; } else { value &= ~CE2700_GPIO_GPIO_EN; value |= (pin_mode << 16) & CE2700_GPIO_PAD_MODE_MASK; } intelce2700_writel(value, reg); value = intelce2700_readl(reg); DBG_TRC("%s: gpio_pin=%u padctrl0=%x mux=%u %s %s(%u) rxstat=%s cfg=%s\n", __func__, gpio_pin, value, (value & CE2700_GPIO_PAD_MODE_MASK) >> 16, (value & CE2700_GPIO_GPIO_EN) ? "gpio" : "func", (value & CE2700_GPIO_GPIO_PULL) ? "pu" : "pd", (value & CE2700_GPIO_GPIO_PULL_STRENGTH_MASK) >> 20, (value & CE2700_GPIO_GPIO_RX_STAT) ? "high" : "low ", (value & CE2700_GPIO_GPIO_CFG_MASK) < CE2700_GPIO_GPIO_RX_EN ? "out" : (value & CE2700_GPIO_GPIO_CFG_MASK) == CE2700_GPIO_GPIO_RX_EN ? "in" : (value & CE2700_GPIO_GPIO_CFG_MASK) == CE2700_GPIO_GPIO_HZ ? "hi-z" : "?" ); spin_unlock_irqrestore(&cg->lock, flags); return 0; } EXPORT_SYMBOL(x86_gpio_ctrl); /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ static const struct _pinctrl_config { unsigned int reg; unsigned int mask; unsigned int shift_value; } pinctrl_config[] = { [OPENDRAIN_PINCTRL] {.reg = CE2700_GPIO_PADCTRL1_REG, .mask = 0x1, .shift_value = 3 }, [PULLUP_PINCTRL] {.reg = CE2700_GPIO_PADCTRL0_REG, .mask = 0xF, .shift_value = 20 }, [PULLDOWN_PINCTRL] {.reg = CE2700_GPIO_PADCTRL0_REG, .mask = 0x1, .shift_value = 23 }, [CFG_TXRX_EN_PINCTRL] {.reg = CE2700_GPIO_PADCTRL0_REG, .mask = 0x7, .shift_value = 8 }, [CFG_TX_EN_PINCTRL] {.reg = CE2700_GPIO_PADCTRL0_REG, .mask = 0x7, .shift_value = 8 }, [CFG_RX_EN_PINCTRL] {.reg = CE2700_GPIO_PADCTRL0_REG, .mask = 0x7, .shift_value = 8 }, [CFG_HI_Z_PINCTRL] {.reg = CE2700_GPIO_PADCTRL0_REG, .mask = 0x7, .shift_value = 8 }, [LIGHTMODE_PINCTRL] {.reg = CE2700_GPIO_PADCTRL0_REG, .mask = 0x1, .shift_value = 7 }, [INTSEL_PINCTRL] {.reg = CE2700_GPIO_PADCTRL0_REG, .mask = 0xF, .shift_value = 28 }, [INTCFG_PINCTRL] {.reg = CE2700_GPIO_PADCTRL1_REG, .mask = 0x7, .shift_value = 0 }, [INVERT_RXTX_PINCTRL] {.reg = CE2700_GPIO_PADCTRL1_REG, .mask = 0xf, .shift_value = 4, }, }; /**--------------------------------------------------------------------------------**\ \**--------------------------------------------------------------------------------**/ int x86_gpio_pinctrl(unsigned int gpio_pin, enum _hw_gpio_pinctrl pinctrl, int input) { const struct _pinctrl_config *pcfg; struct gpio_chip *chip = gpio_to_chip(gpio_pin); unsigned int offset; struct intelce2700_gpio *cg; void __iomem *reg; unsigned long flags; u32 value, mask, set; DBG_TRC("%s(%d) pinctrl=%u input=%u\n", __func__, gpio_pin, pinctrl, input); if(chip == NULL) { printk(KERN_ERR"%s(%d) failed\n", __func__, gpio_pin); return -1; } cg = to_intelce2700_priv(chip); offset = gpio_pin - chip->base; if (offset >= chip->ngpio) { printk(KERN_ERR"%s(%d) false offset=%u - base=%u ngpio=%u\n", __func__, gpio_pin, offset, chip->base, chip->ngpio); return -EINVAL; } if (PAD_CFG_LOCKED(offset)) { printk(KERN_ERR"%s(%d) error: pad locked\n", __func__, gpio_pin); return -1; } if(pinctrl >= ARRAY_SIZE(pinctrl_config) || (pinctrl < 0)) { printk(KERN_ERR"%s(%d) error: invalid pinctrl=%u\n", __func__, gpio_pin, pinctrl); return -1; } pcfg = &pinctrl_config[pinctrl]; mask = pcfg->mask << pcfg->shift_value; switch(pinctrl) { case OPENDRAIN_PINCTRL: set = input ? (1 << pcfg->shift_value) : 0; break; case LIGHTMODE_PINCTRL: set = input ? (1 << pcfg->shift_value) : 0; break; case PULLUP_PINCTRL: set = (((0x1 << 3) | (1 << (input & 0x7))) & pcfg->mask) << pcfg->shift_value; break; case PULLDOWN_PINCTRL: set = (((0x0 << 3) | (1 << (input & 0x7))) & pcfg->mask) << pcfg->shift_value; break; case CFG_TXRX_EN_PINCTRL: set = 0 << pcfg->shift_value; break; case CFG_TX_EN_PINCTRL: set = 1 << pcfg->shift_value; break; case CFG_RX_EN_PINCTRL: set = 2 << pcfg->shift_value; break; case CFG_HI_Z_PINCTRL: set = 3 << pcfg->shift_value; break; case INTSEL_PINCTRL: set = (input & 0xF) << pcfg->shift_value; break; case INTCFG_PINCTRL: set = (input & 0x7) << pcfg->shift_value; break; case INVERT_RXTX_PINCTRL: set = (input & 0x7) << pcfg->shift_value; break; } reg = intelce2700_gpio_reg(chip, offset, pcfg->reg); spin_lock_irqsave(&cg->lock, flags); value = intelce2700_readl(reg); DBG_TRC("%s: gpio_pin=%u %x\n", __func__, gpio_pin, value); value &= ~mask; value |= set; DBG_TRC("%s: gpio_pin=%u erg=%x\n", __func__, gpio_pin, value); intelce2700_writel(value, reg); spin_unlock_irqrestore(&cg->lock, flags); return 0; } /* write complete pad-control-register */ static int x86_gpio_padctrl(unsigned int gpio_pin, unsigned int padctrl_reg, unsigned int value) { struct gpio_chip *chip = gpio_to_chip(gpio_pin); unsigned int offset; struct intelce2700_gpio *cg; void __iomem *reg; if (chip == NULL) { pr_err("%s(%d) failed\n", __func__, gpio_pin); return -1; } cg = to_intelce2700_priv(chip); offset = gpio_pin - chip->base; if (offset >= chip->ngpio) { pr_err("%s(%d) false offset=%u - base=%u ngpio=%u\n", __func__, gpio_pin, offset, chip->base, chip->ngpio); return -EINVAL; } if (PAD_CFG_LOCKED(offset)) { pr_err("%s(%d) error: pad locked\n", __func__, gpio_pin); return -1; } reg = intelce2700_gpio_reg(chip, offset, padctrl_reg); DBG_TRC("%s: gpio%u %s=0x%x\n", __func__, gpio_pin, padctrl_reg == CE2700_GPIO_PADCTRL0_REG ? "CE2700_GPIO_PADCTRL0_REG" : padctrl_reg == CE2700_GPIO_PADCTRL1_REG ? "CE2700_GPIO_PADCTRL1_REG" : "?", value); intelce2700_writel(value, reg); return 0; } struct _gpio_bitbang_ctrl { void __iomem *reg; unsigned int value; }; /**--------------------------------------------------------------------------------**\ * Do not change configuration after - * only use x86_gpio_bitbang_in_bit() or x86_gpio_bitbang_out_bit() * you know what you do ! \**--------------------------------------------------------------------------------**/ void *x86_gpio_get_bitbang_handle(unsigned int gpio_pin) { struct _gpio_bitbang_ctrl *pfa_gpio; struct gpio_chip *chip = gpio_to_chip(gpio_pin); struct intelce2700_gpio *cg; unsigned int offset; if (unlikely(chip == NULL)) { printk(KERN_ERR "%s(%d) failed\n", __func__, gpio_pin); return NULL; } cg = to_intelce2700_priv(chip); offset = gpio_pin - chip->base; if (unlikely(offset >= cg->chip.ngpio)) { return NULL; } pfa_gpio = kzalloc(sizeof(struct _gpio_bitbang_ctrl), GFP_ATOMIC); if (pfa_gpio == NULL) return NULL; pfa_gpio->reg = intelce2700_gpio_reg(chip, offset, CE2700_GPIO_PADCTRL0_REG); pfa_gpio->value = intelce2700_readl(pfa_gpio->reg); return pfa_gpio; } EXPORT_SYMBOL(x86_gpio_get_bitbang_handle); /**--------------------------------------------------------------------------------**\ * free handle \**--------------------------------------------------------------------------------**/ void x86_gpio_put_bitbang_handle(void *handle) { if (handle) kfree(handle); } EXPORT_SYMBOL(x86_gpio_put_bitbang_handle); /**--------------------------------------------------------------------------------**\ * Attention! unlocked and unsaved - you know what you do ! \**--------------------------------------------------------------------------------**/ int x86_gpio_bitbang_in_bit(void *handle) { struct _gpio_bitbang_ctrl *pfa_gpio = (struct _gpio_bitbang_ctrl *)handle; unsigned int value; value = intelce2700_readl(pfa_gpio->reg); return (value & CE2700_GPIO_GPIO_RX_STAT) ? 1 : 0; } EXPORT_SYMBOL(x86_gpio_bitbang_in_bit); /**--------------------------------------------------------------------------------**\ * Attention! unlocked and unsaved - you know what you do ! * background-knowledge: if you use intelce2700_readl() -> change -> intelce2700_writel() * the maximum gpio-setting rate reduced to 300 KHz instead 3 MHz \**--------------------------------------------------------------------------------**/ void x86_gpio_bitbang_out_bit(void *handle, unsigned int set) { struct _gpio_bitbang_ctrl *pfa_gpio = (struct _gpio_bitbang_ctrl *)handle; unsigned int val; if (set) val = pfa_gpio->value | CE2700_GPIO_GPIO_TX_STAT; else val = pfa_gpio->value & ~CE2700_GPIO_GPIO_TX_STAT; intelce2700_writel(val, pfa_gpio->reg); } EXPORT_SYMBOL(x86_gpio_bitbang_out_bit); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int x86_gpio_out_bit(unsigned int gpio_pin, int value) { struct gpio_chip *chip = gpio_to_chip(gpio_pin); unsigned int offset; DBG_TRC("%s(%d) set=%x\n", __func__, gpio_pin, value); if (unlikely(chip == NULL)) { printk(KERN_ERR"%s(%d) failed\n", __func__, gpio_pin); return -1; } offset = gpio_pin - chip->base; value = avm_gpio_interpret_value(gpio_pin, value); intelce2700_gpio_set(chip, offset, value); return 0; } EXPORT_SYMBOL(x86_gpio_out_bit); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int x86_gpio_in_bit(unsigned int gpio_pin) { struct gpio_chip *chip = gpio_to_chip(gpio_pin); unsigned int offset; int value; if (unlikely(chip == NULL)) { printk(KERN_ERR"%s(%d) failed\n", __func__, gpio_pin); return -1; } offset = gpio_pin - chip->base; value = intelce2700_gpio_get(chip, offset); value = avm_gpio_interpret_value(gpio_pin, value); return value; } EXPORT_SYMBOL(x86_gpio_in_bit); #ifdef CONFIG_PROC_FS /*--------------------------------------------------------------------------------*\ * ret: negval -> not found/no range/no match \*--------------------------------------------------------------------------------*/ static int generic_gpio_param_parse(const char *string, const char *match, int maxval, const char *matchstrg[], int matchnr) { const char *p = string; int i, ret = -1; if((p = strstr(string, match))) { p += strlen(match); while(*p == ' ' || *p == '\t') p++; for(i = 0; i < matchnr; i++) { if(strncmp(p, matchstrg[i], strlen(matchstrg[i])) == 0) { return i; } } if((matchnr == 0) && *p) { if((*p == '0') && *(p+1) == 'x') { sscanf(p, "0x%x", &ret); } else { sscanf(p, "%d", &ret); } if((maxval != -1) && (ret > maxval)) { ret = -1; } } } return ret; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int x86_gpio_set(char *string, void *priv) { int gpio, dir, set, mux, od, pu, pd, mode, intsel, intcfg, lightm, padcr0, padcr1; static const char *InOut[] = { [GPIO_OUTPUT_PIN] = "out", [GPIO_INPUT_PIN] = "in" }; static const char *Mode[] = {"txrx", "tx", "rx", "hiz"}; gpio = generic_gpio_param_parse(string, "gpio", ARCH_NR_GPIOS, NULL, 0); dir = generic_gpio_param_parse(string, "dir=", 1, InOut, ARRAY_SIZE(InOut)); set = generic_gpio_param_parse(string, "set=", 1, NULL, 0); mux = generic_gpio_param_parse(string, "mux=", 15, NULL, 0); od = generic_gpio_param_parse(string, "od=", 1, NULL, 0); pu = generic_gpio_param_parse(string, "pullup=", 7, NULL, 0); pd = generic_gpio_param_parse(string, "pulldown=", 7, NULL, 0); mode = generic_gpio_param_parse(string, "mode=", 1, Mode, ARRAY_SIZE(Mode)); intsel = generic_gpio_param_parse(string, "intsel=", 15, NULL, 0); intcfg = generic_gpio_param_parse(string, "intcfg=", 4, NULL, 0); lightm = generic_gpio_param_parse(string, "lightmode=", 1, NULL, 0); padcr0 = generic_gpio_param_parse(string, "padcr0=", -1, NULL, 0); padcr1 = generic_gpio_param_parse(string, "padcr1=", -1, NULL, 0); if ((gpio < 0) || (strstr(string, "help"))) { pr_err("use: gpio dir= intsel=<0-15> intcfg=<0..4> set=<0|1>> mux=<1..15> od=<0|1> lightmode=0|1" "pullup=<0-3> pulldown=<0-3> mode=txrx|tx|rx|hiz padcr0=0x padcr1=0x\n"); return 0; } if(padcr0 != -1) { x86_gpio_padctrl(gpio, CE2700_GPIO_PADCTRL0_REG, padcr0); } if(padcr1 != -1) { x86_gpio_padctrl(gpio, CE2700_GPIO_PADCTRL1_REG, padcr1); } if(dir >= 0 || mux >= 0) { x86_gpio_ctrl(gpio, mux < 0 ? GPIO_PIN : mux, dir); } if(set >= 0) { x86_gpio_out_bit(gpio, set); } if (lightm >= 0) x86_gpio_pinctrl(gpio, LIGHTMODE_PINCTRL, lightm); if(pd >= 0) { x86_gpio_pinctrl(gpio, PULLDOWN_PINCTRL, pd); } if(pu >= 0) { x86_gpio_pinctrl(gpio, PULLUP_PINCTRL, pu); } if(od >= 0) { x86_gpio_pinctrl(gpio, OPENDRAIN_PINCTRL, od); } if (intsel >= 0) { x86_gpio_pinctrl(gpio, INTSEL_PINCTRL, intsel); } if (intcfg >= 0) { x86_gpio_pinctrl(gpio, INTCFG_PINCTRL, intcfg); } switch(mode) { case 0: x86_gpio_pinctrl(gpio, CFG_TXRX_EN_PINCTRL, 0);break; case 1: x86_gpio_pinctrl(gpio, CFG_TX_EN_PINCTRL , 0);break; case 2: x86_gpio_pinctrl(gpio, CFG_RX_EN_PINCTRL , 0);break; case 3: x86_gpio_pinctrl(gpio, CFG_HI_Z_PINCTRL , 0);break; } return 0; } static void x86_gpio_list(struct seq_file *seq, void *priv) { struct gpio_chip *chip, *old_chip = NULL; unsigned int i; for (i = 0; i < ARCH_NR_GPIOS; i++) { chip = gpio_to_chip(i); if (!chip) continue; if (old_chip == chip) continue; seq_printf(seq, "GPIOs %d-%d\n", chip->base, chip->base + chip->ngpio - 1); if (chip->dbg_show) chip->dbg_show(seq, chip); else seq_printf(seq, " Chip %s has no debug support, sorry.\n", chip->label); old_chip = chip; i += chip->ngpio - 1; } } static struct proc_dir_entry *gpioprocdir; static int __init avm_gpioproc_init(void) { gpioprocdir = proc_mkdir("avm/gpio", NULL); if (gpioprocdir == NULL) return 0; add_simple_proc_file("avm/gpio/set", x86_gpio_set, NULL, NULL); add_simple_proc_file("avm/gpio/list", NULL, x86_gpio_list, NULL); return 0; } late_initcall(avm_gpioproc_init); #endif /* CONFIG_AVM_ENHANCED */ #endif /* CONFIG_PROC_FS */ subsys_initcall(intelce2700_gpio_init); module_exit(intelce2700_gpio_exit); /* vim: set ts=8 sw=8 noet cino=>8\:0l1(0: */