diff --git a/core/i2c.c b/core/i2c.c index de7468108430..c164624917c0 100644 --- a/core/i2c.c +++ b/core/i2c.c @@ -118,6 +118,11 @@ static int opal_i2c_request(uint64_t async_token, uint32_t bus_id, req->user_data = (void *)(unsigned long)async_token; req->bus = bus; + if (i2c_check_quirk(req, &rc)) { + i2c_free_req(req); + return rc; + } + /* Finally, queue the OPAL i2c request and return */ rc = i2c_queue_req(req); if (rc) { diff --git a/include/i2c.h b/include/i2c.h index a10a179d284b..2c4c6a1d4bd2 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -29,6 +29,8 @@ struct i2c_bus { void (*set_req_timeout)(struct i2c_request *req, uint64_t duration); uint64_t (*run_req)(struct i2c_request *req); + int (*check_quirk)(void *data, struct i2c_request *req, int *rc); + void *check_quirk_data; }; /* @@ -97,6 +99,13 @@ static inline uint64_t i2c_run_req(struct i2c_request *req) return 0; } +static inline int i2c_check_quirk(struct i2c_request *req, int *rc) +{ + if (req->bus->check_quirk) + return req->bus->check_quirk(req->bus->check_quirk_data, req, rc); + return 0; +} + /* P8 implementation details */ extern void p8_i2c_init(void); extern void p8_i2c_interrupt(uint32_t chip_id); diff --git a/libstb/drivers/tpm_i2c_nuvoton.c b/libstb/drivers/tpm_i2c_nuvoton.c index e355ffe2437f..4cb017e45c85 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: