soc/intel/common/block/gpio: Add `gpio_lock_pad()`

This commit adds a method for locking a GPIO pad configuration and its
TX state.  When the configuration is locked, the following registers
become Read-Only and software writes to these registers have no effect.

  Pad Configuration registers
  GPI_NMI_EN
  GPI_SMI_EN
  GPI_GPE_EN

Note that this is only effective if the pad is owned by the host (set in
the PAD_OWN register).

Intel platforms that wish to leverage this function need to define the
PADCFGLOCK offset for their platform.

BUG=b:191189275
BRANCH=None
TEST=With some other code, call gpio_lock_pad() against a pad and verify
that the pad configuration is locked and the state of the pad cannot be
changed from the OS.

Signed-off-by: Aseda Aboagye <aaboagye@google.com>
Change-Id: Id3c0da2f6942099c0289ca1e33a33c176f49d380
Reviewed-on: https://review.coreboot.org/c/coreboot/+/55557
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Furquan Shaikh <furquan@google.com>
Reviewed-by: Karthik Ramasubramanian <kramasub@google.com>
This commit is contained in:
Aseda Aboagye 2021-06-15 23:11:41 -07:00 committed by Karthik Ramasubramanian
parent 095f97b58f
commit e58e6f2adf
2 changed files with 112 additions and 0 deletions

View File

@ -8,6 +8,7 @@
#include <intelblocks/gpio.h>
#include <gpio.h>
#include <intelblocks/itss.h>
#include <intelblocks/p2sb.h>
#include <intelblocks/pcr.h>
#include <soc/pm.h>
#include <stdlib.h>
@ -446,6 +447,84 @@ int gpio_get(gpio_t gpio_num)
return !!(reg & PAD_CFG0_RX_STATE);
}
int gpio_lock_pad(const gpio_t pad, enum gpio_lock_action action)
{
const struct pad_community *comm = gpio_get_community(pad);
size_t rel_pad;
uint16_t offset;
uint32_t data;
uint8_t response;
int status;
/*
* FSP-S will unlock all the GPIO pads and hide the P2SB device. With
* the device hidden, we will not be able to send the sideband interface
* message to lock the GPIO configuration. Therefore, we need to unhide
* the P2SB device which can only be done in SMM requiring that this
* function is called from SMM.
*/
if (!ENV_SMM) {
printk(BIOS_ERR, "%s: Error: must be called from SMM!\n", __func__);
return -1;
}
if (!(action & GPIO_LOCK_FULL)) {
printk(BIOS_ERR, "%s: Error: no action specified!\n", __func__);
return -1;
}
rel_pad = relative_pad_in_comm(comm, pad);
offset = comm->pad_cfg_lock_offset;
if (!offset) {
printk(BIOS_ERR, "%s: Error: offset is not defined!\n", __func__);
return -1;
}
offset += gpio_group_index_scaled(comm, rel_pad, 2 * sizeof(uint32_t));
/* We must use the sideband interface in order to lock the pad. */
struct pcr_sbi_msg msg = {
.pid = comm->port,
.offset = offset,
.opcode = GPIO_LOCK_UNLOCK,
.is_posted = false,
.fast_byte_enable = 0xF,
.bar = 0,
.fid = 0,
};
p2sb_unhide();
data = gpio_bitmask_within_group(comm, rel_pad);
if (action & GPIO_LOCK_CONFIG) {
printk(BIOS_INFO, "%s: Locking pad %d configuration\n",
__func__, pad);
status = pcr_execute_sideband_msg(&msg, &data, &response);
if (status || response) {
printk(BIOS_ERR, "%s: error status=%x response=%x\n", __func__, status,
response);
p2sb_hide();
return status == -1 ? -1 : response;
}
}
if (action & GPIO_LOCK_TX) {
printk(BIOS_INFO, "%s: Locking pad %d TX state\n", __func__,
pad);
msg.offset = msg.offset + 4;
status = pcr_execute_sideband_msg(&msg, &data, &response);
if (status || response) {
printk(BIOS_ERR, "%s: error status=%x response=%x\n", __func__, status,
response);
p2sb_hide();
return status == -1 ? -1 : response;
}
}
p2sb_hide();
return 0;
}
void gpio_set(gpio_t gpio_num, int value)
{
const struct pad_community *comm = gpio_get_community(gpio_num);

View File

@ -121,6 +121,7 @@ struct pad_community {
uint16_t gpi_nmi_sts_reg_0; /* offset to GPI NMI STS Reg 0 */
uint16_t gpi_nmi_en_reg_0; /* offset to GPI NMI EN Reg 0 */
uint16_t pad_cfg_base; /* offset to first PAD_GFG_DW0 Reg */
uint16_t pad_cfg_lock_offset; /* offset to first PADCFGLOCK Reg */
uint8_t gpi_status_offset; /* specifies offset in struct
gpi_status */
uint8_t port; /* PCR Port ID */
@ -197,6 +198,38 @@ void gpio_configure_pads_with_override(const struct pad_config *base_cfg,
*/
void *gpio_dwx_address(const gpio_t pad);
enum gpio_lock_action {
GPIO_LOCK_CONFIG = 0x1,
GPIO_LOCK_TX = 0x2,
GPIO_LOCK_FULL = GPIO_LOCK_CONFIG | GPIO_LOCK_TX,
};
/*
* Lock a GPIO's configuration.
*
* The caller may specify if they wish to only lock the pad configuration, only
* the TX state, or both. When the configuration is locked, the following
* registers become Read-Only and software writes to these registers have no
* effect.
*
* Pad Configuration registers,
* GPI_NMI_EN,
* GPI_SMI_EN,
* GPI_GPE_EN
*
* Note that this is only effective if the pad is owned by the host and this
* function may only be called in SMM.
*
* @param pad: GPIO pad number
* @param action: Which register to lock.
* @return 0 if successful,
* 1 - unsuccessful
* 2 - powered down
* 3 - multi-cast mixed
* -1 - sideband message failed or other error
*/
int gpio_lock_pad(const gpio_t pad, enum gpio_lock_action action);
/*
* Returns the pmc_gpe to gpio_gpe mapping table
*