aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/i2c.c5
-rw-r--r--include/i2c.h9
-rw-r--r--libstb/drivers/tpm_i2c_nuvoton.c28
3 files changed, 42 insertions, 0 deletions
diff --git a/core/i2c.c b/core/i2c.c
index de74681..c164624 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 a10a179..2c4c6a1 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 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 <opal-api.h>
//#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: