/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include // Community PadOwnOffset HostOwnOffset // GpiIsOffset // GpiIeOffset GpiGpeStsOffset GpiGpeEnOffset // SmiStsOffset // SmiEnOffset NmiStsOffset NmiEnOffset // PadCfgLockOffset // PadCfgLockTxOffset PadCfgOffset PadPerGroup static const struct GPIO_GROUP_INFO mGpioGroupInfo[] = { {PID_GPIOCOM0, R_PCH_PCR_GPIO_NC_PAD_OWN, R_PCH_PCR_GPIO_NC_HOSTSW_OWN, R_PCH_PCR_GPIO_NC_GPI_IS, R_PCH_PCR_GPIO_NC_GPI_IE, R_PCH_PCR_GPIO_NC_GPI_GPE_STS, R_PCH_PCR_GPIO_NC_GPI_GPE_EN, R_PCH_PCR_GPIO_NC_SMI_STS, R_PCH_PCR_GPIO_NC_SMI_EN, R_PCH_PCR_GPIO_NC_NMI_STS, R_PCH_PCR_GPIO_NC_NMI_EN, R_PCH_PCR_GPIO_NC_PADCFGLOCK, R_PCH_PCR_GPIO_NC_PADCFGLOCKTX, R_PCH_PCR_GPIO_NC_PADCFG_OFFSET, V_PCH_GPIO_NC_PAD_MAX}, // DNV NORTH_ALL {PID_GPIOCOM1, R_PCH_PCR_GPIO_SC_DFX_PAD_OWN, R_PCH_PCR_GPIO_SC_DFX_HOSTSW_OWN, R_PCH_PCR_GPIO_SC_DFX_GPI_IS, R_PCH_PCR_GPIO_SC_DFX_GPI_IE, R_PCH_PCR_GPIO_SC_DFX_GPI_GPE_STS, R_PCH_PCR_GPIO_SC_DFX_GPI_GPE_EN, NO_REGISTER_FOR_PROPERTY, NO_REGISTER_FOR_PROPERTY, NO_REGISTER_FOR_PROPERTY, NO_REGISTER_FOR_PROPERTY, R_PCH_PCR_GPIO_SC_DFX_PADCFGLOCK, R_PCH_PCR_GPIO_SC_DFX_PADCFGLOCKTX, R_PCH_PCR_GPIO_SC_DFX_PADCFG_OFFSET, V_PCH_GPIO_SC_DFX_PAD_MAX}, // DNV SOUTH_DFX {PID_GPIOCOM1, R_PCH_PCR_GPIO_SC0_PAD_OWN, R_PCH_PCR_GPIO_SC0_HOSTSW_OWN, R_PCH_PCR_GPIO_SC0_GPI_IS, R_PCH_PCR_GPIO_SC0_GPI_IE, R_PCH_PCR_GPIO_SC0_GPI_GPE_STS, R_PCH_PCR_GPIO_SC0_GPI_GPE_EN, R_PCH_PCR_GPIO_SC0_SMI_STS, R_PCH_PCR_GPIO_SC0_SMI_EN, R_PCH_PCR_GPIO_SC0_NMI_STS, R_PCH_PCR_GPIO_SC0_NMI_EN, R_PCH_PCR_GPIO_SC0_PADCFGLOCK, R_PCH_PCR_GPIO_SC0_PADCFGLOCKTX, R_PCH_PCR_GPIO_SC0_PADCFG_OFFSET, V_PCH_GPIO_SC0_PAD_MAX}, // DNV South Community 0 {PID_GPIOCOM1, R_PCH_PCR_GPIO_SC1_PAD_OWN, R_PCH_PCR_GPIO_SC1_HOSTSW_OWN, R_PCH_PCR_GPIO_SC1_GPI_IS, R_PCH_PCR_GPIO_SC1_GPI_IE, R_PCH_PCR_GPIO_SC1_GPI_GPE_STS, R_PCH_PCR_GPIO_SC1_GPI_GPE_EN, R_PCH_PCR_GPIO_SC1_SMI_STS, R_PCH_PCR_GPIO_SC1_SMI_EN, R_PCH_PCR_GPIO_SC1_NMI_STS, R_PCH_PCR_GPIO_SC1_NMI_EN, R_PCH_PCR_GPIO_SC1_PADCFGLOCK, R_PCH_PCR_GPIO_SC1_PADCFGLOCKTX, R_PCH_PCR_GPIO_SC1_PADCFG_OFFSET, V_PCH_GPIO_SC1_PAD_MAX}, // DNV South Community 1 }; /* Retrieve address and length of GPIO info table */ static struct GPIO_GROUP_INFO * GpioGetGroupInfoTable(uint32_t *GpioGroupInfoTableLength) { *GpioGroupInfoTableLength = sizeof(mGpioGroupInfo) / sizeof(struct GPIO_GROUP_INFO); return (struct GPIO_GROUP_INFO *)mGpioGroupInfo; } /* Get Gpio Pad Ownership */ static void GpioGetPadOwnership(GPIO_PAD GpioPad, GPIO_PAD_OWN *PadOwnVal) { uint32_t Mask; uint32_t RegOffset; uint32_t GroupIndex; uint32_t PadNumber; struct GPIO_GROUP_INFO *GpioGroupInfo; uint32_t GpioGroupInfoLength; uint32_t PadOwnRegValue; GroupIndex = GPIO_GET_GROUP_INDEX_FROM_PAD(GpioPad); PadNumber = GPIO_GET_PAD_NUMBER(GpioPad); GpioGroupInfo = GpioGetGroupInfoTable(&GpioGroupInfoLength); // // Check if group argument exceeds GPIO GROUP INFO array // if ((uint32_t)GroupIndex >= GpioGroupInfoLength) { printk(BIOS_ERR, "GPIO ERROR: Group argument (%d) exceeds GPIO " "group range\n", GroupIndex); return; } // // Check if legal pin number // if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup) { printk(BIOS_ERR, "GPIO ERROR: Pin number (%d) exceeds possible " "range for this group\n", PadNumber); return; } // // Calculate RegOffset using Pad Ownership offset and GPIO Pad number. // One DWord register contains information for 8 pads. // RegOffset = GpioGroupInfo[GroupIndex].PadOwnOffset + (PadNumber >> 3) * 0x4; // // Calculate pad bit position within DWord register // PadNumber %= 8; Mask = ((1 << 1) | (1 << 0)) << (PadNumber * 4); PadOwnRegValue = read32((void *)PCH_PCR_ADDRESS( GpioGroupInfo[GroupIndex].Community, RegOffset)); *PadOwnVal = (GPIO_PAD_OWN)((PadOwnRegValue & Mask) >> (PadNumber * 4)); } void gpio_configure_dnv_pads(const struct dnv_pad_config *gpio, size_t num) { /* Return if gpio not valid */ if ((gpio == NULL) || (num == 0)) return; uint32_t Index; uint32_t Dw0Reg; uint32_t Dw0RegMask; uint32_t Dw1Reg; uint32_t Dw1RegMask; uint32_t PadCfgReg; uint64_t HostSoftOwnReg[V_PCH_GPIO_GROUP_MAX]; uint64_t HostSoftOwnRegMask[V_PCH_GPIO_GROUP_MAX]; uint64_t GpiGpeEnReg[V_PCH_GPIO_GROUP_MAX]; uint64_t GpiGpeEnRegMask[V_PCH_GPIO_GROUP_MAX]; struct GPIO_GROUP_INFO *GpioGroupInfo; uint32_t GpioGroupInfoLength; GPIO_PAD GpioGroupOffset; uint32_t NumberOfGroups; GPIO_PAD_OWN PadOwnVal; struct dnv_pad_config *GpioData; GPIO_PAD Group; uint32_t GroupIndex; uint32_t PadNumber; uint32_t FinalValue; uint32_t Data32; uint32_t PadMode1, PadMode2; PadOwnVal = GpioPadOwnHost; memset(HostSoftOwnReg, 0, sizeof(HostSoftOwnReg)); memset(HostSoftOwnRegMask, 0, sizeof(HostSoftOwnRegMask)); memset(GpiGpeEnReg, 0, sizeof(GpiGpeEnReg)); memset(GpiGpeEnRegMask, 0, sizeof(GpiGpeEnRegMask)); GpioGroupInfo = GpioGetGroupInfoTable(&GpioGroupInfoLength); GpioGroupOffset = GPIO_DNV_GROUP_MIN; NumberOfGroups = V_PCH_GPIO_GROUP_MAX; for (Index = 0; Index < (uint32_t)num; Index++) { Dw0RegMask = 0; Dw0Reg = 0; Dw1RegMask = 0; Dw1Reg = 0; GpioData = (struct dnv_pad_config *)&(gpio[Index]); Group = GPIO_GET_GROUP_FROM_PAD(GpioData->GpioPad); GroupIndex = GPIO_GET_GROUP_INDEX_FROM_PAD(GpioData->GpioPad); PadNumber = GPIO_GET_PAD_NUMBER(GpioData->GpioPad); // // Check if group index argument exceeds GPIO group index range // if (GroupIndex >= V_PCH_GPIO_GROUP_MAX) { printk(BIOS_ERR, "GPIO ERROR: Invalid Group Index " "(GroupIndex=%d, Pad=%d)!\n", GroupIndex, PadNumber); continue; } // // Check if group argument exceeds GPIO group range // if ((Group < GpioGroupOffset) || (Group >= NumberOfGroups + GpioGroupOffset)) { printk(BIOS_ERR, "GPIO ERROR: Invalid Group (Group=%d)!\n", Group); return; } // // Check if legal pin number // if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup) { printk(BIOS_ERR, "GPIO ERROR: Invalid PadNumber " "(PadNumber=%d)!\n", PadNumber); return; } // // Check if selected GPIO Pad is not owned by CSME/ISH // GpioGetPadOwnership(GpioData->GpioPad, &PadOwnVal); if (PadOwnVal != GpioPadOwnHost) { printk(BIOS_ERR, "GPIO WARNING: Accessing pad not " "owned by host (Group=%d, Pad=%d)!", GroupIndex, PadNumber); if (PadOwnVal == GpioPadOwnCsme) printk(BIOS_ERR, "The owner is CSME\n"); else if (PadOwnVal == GpioPadOwnIsh) printk(BIOS_ERR, "The owner is ISH\n"); printk(BIOS_ERR, "** Please make sure the GPIO usage " "in sync between CSME/ISH and Host IA " "FW configuration.\n"); printk(BIOS_ERR, "** All the GPIO occupied by CSME/ISH " "should not do any configuration by " "Host IA FW.\n"); continue; } // // Configure Reset Type (PadRstCfg) // Dw0RegMask |= ((((GpioData->GpioConfig.PowerConfig & GPIO_CONF_RESET_MASK) >> GPIO_CONF_RESET_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_RST_CONF); Dw0Reg |= (((GpioData->GpioConfig.PowerConfig & GPIO_CONF_RESET_MASK) >> (GPIO_CONF_RESET_BIT_POS + 1)) << N_PCH_GPIO_RST_CONF); // // Configure how interrupt is triggered (RxEvCfg) // Dw0RegMask |= ((((GpioData->GpioConfig.InterruptConfig & GPIO_CONF_INT_TRIG_MASK) >> GPIO_CONF_INT_TRIG_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_RX_LVL_EDG); Dw0Reg |= (((GpioData->GpioConfig.InterruptConfig & GPIO_CONF_INT_TRIG_MASK) >> (GPIO_CONF_INT_TRIG_BIT_POS + 1)) << N_PCH_GPIO_RX_LVL_EDG); // // Configure interrupt generation (GPIRoutIOxAPIC/SCI/SMI/NMI) // Dw0RegMask |= ((((GpioData->GpioConfig.InterruptConfig & GPIO_CONF_INT_ROUTE_MASK) >> GPIO_CONF_INT_ROUTE_BIT_POS) == GpioHardwareDefault) ? 0x0 : (B_PCH_GPIO_RX_NMI_ROUTE | B_PCH_GPIO_RX_SCI_ROUTE | B_PCH_GPIO_RX_SMI_ROUTE | B_PCH_GPIO_RX_APIC_ROUTE)); Dw0Reg |= (((GpioData->GpioConfig.InterruptConfig & GPIO_CONF_INT_ROUTE_MASK) >> (GPIO_CONF_INT_ROUTE_BIT_POS + 1)) << N_PCH_GPIO_RX_NMI_ROUTE); // If CFIO is not Working as GPIO mode, Don't move TxDisable and // RxDisable if (GpioData->GpioConfig.PadMode == GpioPadModeGpio) { // // Configure GPIO direction (GPIORxDis and GPIOTxDis) // Dw0RegMask |= ((((GpioData->GpioConfig.Direction & GPIO_CONF_DIR_MASK) >> GPIO_CONF_DIR_BIT_POS) == GpioHardwareDefault) ? 0x0 : (B_PCH_GPIO_RXDIS | B_PCH_GPIO_TXDIS)); Dw0Reg |= (((GpioData->GpioConfig.Direction & GPIO_CONF_DIR_MASK) >> (GPIO_CONF_DIR_BIT_POS + 1)) << N_PCH_GPIO_TXDIS); } // // Configure GPIO input inversion (RXINV) // Dw0RegMask |= ((((GpioData->GpioConfig.Direction & GPIO_CONF_INV_MASK) >> GPIO_CONF_INV_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_RXINV); Dw0Reg |= (((GpioData->GpioConfig.Direction & GPIO_CONF_INV_MASK) >> (GPIO_CONF_INV_BIT_POS + 1)) << N_PCH_GPIO_RXINV); // // Configure GPIO output state (GPIOTxState) // Dw0RegMask |= ((((GpioData->GpioConfig.OutputState & GPIO_CONF_OUTPUT_MASK) >> GPIO_CONF_OUTPUT_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_TX_STATE); Dw0Reg |= (((GpioData->GpioConfig.OutputState & GPIO_CONF_OUTPUT_MASK) >> (GPIO_CONF_OUTPUT_BIT_POS + 1)) << N_PCH_GPIO_TX_STATE); // // Configure GPIO RX raw override to '1' (RXRAW1) // Dw0RegMask |= ((((GpioData->GpioConfig.OtherSettings & GPIO_CONF_RXRAW_MASK) >> GPIO_CONF_RXRAW_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_RX_RAW1); Dw0Reg |= (((GpioData->GpioConfig.OtherSettings & GPIO_CONF_RXRAW_MASK) >> (GPIO_CONF_RXRAW_BIT_POS + 1)) << N_PCH_GPIO_RX_RAW1); // // Configure GPIO Pad Mode (PMode) // Dw0RegMask |= ((((GpioData->GpioConfig.PadMode & GPIO_CONF_PAD_MODE_MASK) >> GPIO_CONF_PAD_MODE_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_PAD_MODE); Dw0Reg |= (((GpioData->GpioConfig.PadMode & GPIO_CONF_PAD_MODE_MASK) >> (GPIO_CONF_PAD_MODE_BIT_POS + 1)) << N_PCH_GPIO_PAD_MODE); // // Configure GPIO termination (Term) // Dw1RegMask |= ((((GpioData->GpioConfig.ElectricalConfig & GPIO_CONF_TERM_MASK) >> GPIO_CONF_TERM_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_TERM); Dw1Reg |= (((GpioData->GpioConfig.ElectricalConfig & GPIO_CONF_TERM_MASK) >> (GPIO_CONF_TERM_BIT_POS + 1)) << N_PCH_GPIO_TERM); // // Configure GPIO pad tolerance (padtol) // Dw1RegMask |= ((((GpioData->GpioConfig.ElectricalConfig & GPIO_CONF_PADTOL_MASK) >> GPIO_CONF_PADTOL_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_PADTOL); Dw1Reg |= (((GpioData->GpioConfig.ElectricalConfig & GPIO_CONF_PADTOL_MASK) >> (GPIO_CONF_PADTOL_BIT_POS + 1)) << N_PCH_GPIO_PADTOL); // // Check for additional requirements on setting PADCFG register // // // Create PADCFG register offset using group and pad number // PadCfgReg = 0x8 * PadNumber + GpioGroupInfo[GroupIndex].PadCfgOffset; Data32 = read32((void *)PCH_PCR_ADDRESS( GpioGroupInfo[GroupIndex].Community, PadCfgReg)); FinalValue = ((Data32 & (~Dw0RegMask)) | Dw0Reg); PadMode1 = (Data32 & B_PCH_GPIO_PAD_MODE) >> N_PCH_GPIO_PAD_MODE; PadMode2 = (Dw0Reg & B_PCH_GPIO_PAD_MODE) >> N_PCH_GPIO_PAD_MODE; if (((Data32 & B_PCH_GPIO_PAD_MODE) != (FinalValue & B_PCH_GPIO_PAD_MODE)) || (PadMode2 == 0)) { printk(BIOS_DEBUG, "Changing GpioPad PID: %x Offset: " "0x%x PadModeP1: %d P2: %d ", GpioGroupInfo[GroupIndex].Community, PadCfgReg, PadMode1, PadMode2); printk(BIOS_DEBUG, "R: 0x%08x Fx%08x !\n", Data32, FinalValue); // // Write PADCFG DW0 register`` // mmio_andthenor32( (void *)(uint32_t)PCH_PCR_ADDRESS( GpioGroupInfo[GroupIndex].Community, PadCfgReg), ~(uint32_t)Dw0RegMask, (uint32_t)Dw0Reg); } Data32 = read32((void *)PCH_PCR_ADDRESS( GpioGroupInfo[GroupIndex].Community, PadCfgReg + 0x4)); FinalValue = ((Data32 & (~Dw1RegMask)) | Dw1Reg); if (Data32 != FinalValue) { // // Write PADCFG DW1 register // mmio_andthenor32( (void *)(uint32_t)PCH_PCR_ADDRESS( GpioGroupInfo[GroupIndex].Community, PadCfgReg + 0x4), ~(uint32_t)Dw1RegMask, (uint32_t)Dw1Reg); } // // Update value to be programmed in HOSTSW_OWN register // HostSoftOwnRegMask[GroupIndex] |= LShiftU64( (uint64_t)GpioData->GpioConfig.HostSoftPadOwn & 0x1, PadNumber); HostSoftOwnReg[GroupIndex] |= LShiftU64( (uint64_t)GpioData->GpioConfig.HostSoftPadOwn >> 0x1, PadNumber); // // Update value to be programmed in GPI_GPE_EN register // GpiGpeEnRegMask[GroupIndex] |= LShiftU64( (uint64_t)(GpioData->GpioConfig.InterruptConfig & 0x1), PadNumber); GpiGpeEnReg[GroupIndex] |= LShiftU64( (uint64_t)(GpioData->GpioConfig.InterruptConfig & GpioIntSci) >> 3, PadNumber); } for (Index = 0; Index < NumberOfGroups; Index++) { // // Write HOSTSW_OWN registers // if (GpioGroupInfo[Index].HostOwnOffset != NO_REGISTER_FOR_PROPERTY) { mmio_andthenor32( (void *)PCH_PCR_ADDRESS( GpioGroupInfo[Index].Community, GpioGroupInfo[Index].HostOwnOffset), ~(uint32_t)(HostSoftOwnRegMask[Index] & 0xFFFFFFFF), (uint32_t)(HostSoftOwnReg[Index] & 0xFFFFFFFF)); mmio_andthenor32( (void *)PCH_PCR_ADDRESS( GpioGroupInfo[Index].Community, GpioGroupInfo[Index].HostOwnOffset + 0x4), ~(uint32_t)(RShiftU64(HostSoftOwnRegMask[Index], 32)), (uint32_t)( RShiftU64(HostSoftOwnReg[Index], 32))); } // // Write GPI_GPE_EN registers // if (GpioGroupInfo[Index].GpiGpeEnOffset != NO_REGISTER_FOR_PROPERTY) { mmio_andthenor32( (void *)PCH_PCR_ADDRESS( GpioGroupInfo[Index].Community, GpioGroupInfo[Index].GpiGpeEnOffset), ~(uint32_t)(GpiGpeEnRegMask[Index] & 0xFFFFFFFF), (uint32_t)(GpiGpeEnReg[Index] & 0xFFFFFFFF)); mmio_andthenor32( (void *)PCH_PCR_ADDRESS( GpioGroupInfo[Index].Community, GpioGroupInfo[Index].GpiGpeEnOffset + 0x4), ~(uint32_t)( RShiftU64(GpiGpeEnRegMask[Index], 32)), (uint32_t)(RShiftU64(GpiGpeEnReg[Index], 32))); } } }