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:
parent
095f97b58f
commit
e58e6f2adf
|
@ -8,6 +8,7 @@
|
||||||
#include <intelblocks/gpio.h>
|
#include <intelblocks/gpio.h>
|
||||||
#include <gpio.h>
|
#include <gpio.h>
|
||||||
#include <intelblocks/itss.h>
|
#include <intelblocks/itss.h>
|
||||||
|
#include <intelblocks/p2sb.h>
|
||||||
#include <intelblocks/pcr.h>
|
#include <intelblocks/pcr.h>
|
||||||
#include <soc/pm.h>
|
#include <soc/pm.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -446,6 +447,84 @@ int gpio_get(gpio_t gpio_num)
|
||||||
return !!(reg & PAD_CFG0_RX_STATE);
|
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)
|
void gpio_set(gpio_t gpio_num, int value)
|
||||||
{
|
{
|
||||||
const struct pad_community *comm = gpio_get_community(gpio_num);
|
const struct pad_community *comm = gpio_get_community(gpio_num);
|
||||||
|
|
|
@ -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_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 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_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
|
uint8_t gpi_status_offset; /* specifies offset in struct
|
||||||
gpi_status */
|
gpi_status */
|
||||||
uint8_t port; /* PCR Port ID */
|
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);
|
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
|
* Returns the pmc_gpe to gpio_gpe mapping table
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue