From 157468aeb16e9f50551ac9bc7d46887cba0e011f Mon Sep 17 00:00:00 2001 From: Stewart Smith Date: Fri, 2 Dec 2016 13:20:18 +1100 Subject: i2c: Add nuvoton quirk, disallowing i2cdetect as it locks TPM In TPM 2.0 Firmware 1.3.0.1 and 1.3.1.0 (at least) there exists a bug where if you send the wrong thing to the TPM it may lock the bus, with no way of recovery except powering the TPM off/on. On our current systems, the only way to power the TPM off/on is to pull the power on the system (*NOT* just power off/on to host from BMC). So, this patch adds the ability to do things to the i2c request really early on, well before it hits any hardware, such as quickly drop it. Signed-off-by: Stewart Smith --- libstb/drivers/tpm_i2c_nuvoton.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'libstb/drivers') diff --git a/libstb/drivers/tpm_i2c_nuvoton.c b/libstb/drivers/tpm_i2c_nuvoton.c index e355ffe..4cb017e 100644 --- a/libstb/drivers/tpm_i2c_nuvoton.c +++ b/libstb/drivers/tpm_i2c_nuvoton.c @@ -22,6 +22,7 @@ #include "../tpm_chip.h" #include "tpm_i2c_interface.h" #include "tpm_i2c_nuvoton.h" +#include //#define DBG(fmt, ...) prlog(PR_DEBUG, fmt, ##__VA_ARGS__) #define DBG(fmt, ...) @@ -507,10 +508,33 @@ static struct tpm_driver tpm_i2c_nuvoton_driver = { .transmit = tpm_transmit, }; +static int nuvoton_tpm_quirk(void *data, struct i2c_request *req, int *rc) +{ + struct tpm_dev *tpm_device = data; + + /* If we're doing i2cdetect on the TPM, pretent we just NACKed + * it due to errata in nuvoton firmware where if we let this + * request go through, it would steal the bus and you'd end up + * in a nice world of pain. + */ + if (tpm_device->bus_id == req->bus->opal_id && + tpm_device->xscom_base == req->dev_addr && + ((req->op == I2C_READ && req->rw_len == 1) || + (req->op == I2C_WRITE && req->rw_len == 0))) { + *rc = OPAL_I2C_TIMEOUT; + prlog(PR_DEBUG,"NUVOTON: Squashed i2c probe to avoid locking " + "I2C bus\n"); + return 1; + } + + return 0; +} + void tpm_i2c_nuvoton_probe(void) { struct tpm_dev *tpm_device = NULL; struct dt_node *node = NULL; + struct i2c_bus *bus; dt_for_each_compatible(dt_root, node, "nuvoton,npct650") { if (!dt_node_is_enabled(node)) @@ -551,6 +575,10 @@ void tpm_i2c_nuvoton_probe(void) if (tpm_register_chip(node, tpm_device, &tpm_i2c_nuvoton_driver)) free(tpm_device); + bus = i2c_find_bus_by_id(tpm_device->bus_id); + assert(bus->check_quirk == NULL); + bus->check_quirk = nuvoton_tpm_quirk; + bus->check_quirk_data = tpm_device; } return; disable: -- cgit v1.1