Skip to content

Commit ce40329

Browse files
committed
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 <[email protected]> (cherry picked from commit 157468a) Signed-off-by: Stewart Smith <[email protected]>
1 parent 2924f7f commit ce40329

File tree

3 files changed

+42
-0
lines changed

3 files changed

+42
-0
lines changed

core/i2c.c

+5
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ static int opal_i2c_request(uint64_t async_token, uint32_t bus_id,
118118
req->user_data = (void *)(unsigned long)async_token;
119119
req->bus = bus;
120120

121+
if (i2c_check_quirk(req, &rc)) {
122+
i2c_free_req(req);
123+
return rc;
124+
}
125+
121126
/* Finally, queue the OPAL i2c request and return */
122127
rc = i2c_queue_req(req);
123128
if (rc) {

include/i2c.h

+9
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ struct i2c_bus {
2929
void (*set_req_timeout)(struct i2c_request *req,
3030
uint64_t duration);
3131
uint64_t (*run_req)(struct i2c_request *req);
32+
int (*check_quirk)(void *data, struct i2c_request *req, int *rc);
33+
void *check_quirk_data;
3234
};
3335

3436
/*
@@ -97,6 +99,13 @@ static inline uint64_t i2c_run_req(struct i2c_request *req)
9799
return 0;
98100
}
99101

102+
static inline int i2c_check_quirk(struct i2c_request *req, int *rc)
103+
{
104+
if (req->bus->check_quirk)
105+
return req->bus->check_quirk(req->bus->check_quirk_data, req, rc);
106+
return 0;
107+
}
108+
100109
/* P8 implementation details */
101110
extern void p8_i2c_init(void);
102111
extern void p8_i2c_interrupt(uint32_t chip_id);

libstb/drivers/tpm_i2c_nuvoton.c

+28
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "../tpm_chip.h"
2323
#include "tpm_i2c_interface.h"
2424
#include "tpm_i2c_nuvoton.h"
25+
#include <opal-api.h>
2526

2627
//#define DBG(fmt, ...) prlog(PR_DEBUG, fmt, ##__VA_ARGS__)
2728
#define DBG(fmt, ...)
@@ -507,10 +508,33 @@ static struct tpm_driver tpm_i2c_nuvoton_driver = {
507508
.transmit = tpm_transmit,
508509
};
509510

511+
static int nuvoton_tpm_quirk(void *data, struct i2c_request *req, int *rc)
512+
{
513+
struct tpm_dev *tpm_device = data;
514+
515+
/* If we're doing i2cdetect on the TPM, pretent we just NACKed
516+
* it due to errata in nuvoton firmware where if we let this
517+
* request go through, it would steal the bus and you'd end up
518+
* in a nice world of pain.
519+
*/
520+
if (tpm_device->bus_id == req->bus->opal_id &&
521+
tpm_device->xscom_base == req->dev_addr &&
522+
((req->op == I2C_READ && req->rw_len == 1) ||
523+
(req->op == I2C_WRITE && req->rw_len == 0))) {
524+
*rc = OPAL_I2C_TIMEOUT;
525+
prlog(PR_DEBUG,"NUVOTON: Squashed i2c probe to avoid locking "
526+
"I2C bus\n");
527+
return 1;
528+
}
529+
530+
return 0;
531+
}
532+
510533
void tpm_i2c_nuvoton_probe(void)
511534
{
512535
struct tpm_dev *tpm_device = NULL;
513536
struct dt_node *node = NULL;
537+
struct i2c_bus *bus;
514538

515539
dt_for_each_compatible(dt_root, node, "nuvoton,npct650") {
516540
if (!dt_node_is_enabled(node))
@@ -551,6 +575,10 @@ void tpm_i2c_nuvoton_probe(void)
551575
if (tpm_register_chip(node, tpm_device,
552576
&tpm_i2c_nuvoton_driver))
553577
free(tpm_device);
578+
bus = i2c_find_bus_by_id(tpm_device->bus_id);
579+
assert(bus->check_quirk == NULL);
580+
bus->check_quirk = nuvoton_tpm_quirk;
581+
bus->check_quirk_data = tpm_device;
554582
}
555583
return;
556584
disable:

0 commit comments

Comments
 (0)