coreboot/src/soc/intel/denverton_ns/gpio_dnv.c

495 lines
15 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
#include <stdint.h>
#include <string.h>
#include <device/mmio.h>
#include <console/console.h>
#include <device/device.h>
#include <device/pci.h>
#include <soc/iomap.h>
#include <soc/pcr.h>
#include <soc/soc_util.h>
#include <soc/gpio_dnv.h>
// 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 = ARRAY_SIZE(mGpioGroupInfo);
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)));
}
}
}