From a404133547c98094a326f60b83e1576ba94b8c06 Mon Sep 17 00:00:00 2001 From: Mariusz Szafranski Date: Wed, 2 Aug 2017 17:28:17 +0200 Subject: soc/intel/denverton_ns: Add support for Intel Atom C3000 SoC This change adds support for Intel Atom C3000 SoC ("Denverton" and "Denverton-NS"). Code is partially based on Apollo Lake/Skylake code. Change-Id: I53d69aede3b92f1fe06b74a96cc40187fb9825f1 Signed-off-by: Mariusz Szafranski Reviewed-on: https://review.coreboot.org/20861 Tested-by: build bot (Jenkins) Reviewed-by: FEI WANG --- src/soc/intel/denverton_ns/gpio.c | 509 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 509 insertions(+) create mode 100644 src/soc/intel/denverton_ns/gpio.c (limited to 'src/soc/intel/denverton_ns/gpio.c') diff --git a/src/soc/intel/denverton_ns/gpio.c b/src/soc/intel/denverton_ns/gpio.c new file mode 100644 index 0000000000..3030fbb98b --- /dev/null +++ b/src/soc/intel/denverton_ns/gpio.c @@ -0,0 +1,509 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 - 2017 Intel 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 of the License. + * + * 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 +#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_pads(const struct 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 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 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 TxDisabe 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))); + } + } +} -- cgit v1.2.3