diff options
Diffstat (limited to 'roms/skiboot/libstb/drivers')
-rw-r--r-- | roms/skiboot/libstb/drivers/Makefile.inc | 11 | ||||
-rw-r--r-- | roms/skiboot/libstb/drivers/tpm_i2c_interface.c | 50 | ||||
-rw-r--r-- | roms/skiboot/libstb/drivers/tpm_i2c_interface.h | 15 | ||||
-rw-r--r-- | roms/skiboot/libstb/drivers/tpm_i2c_nuvoton.c | 627 | ||||
-rw-r--r-- | roms/skiboot/libstb/drivers/tpm_i2c_nuvoton.h | 9 |
5 files changed, 712 insertions, 0 deletions
diff --git a/roms/skiboot/libstb/drivers/Makefile.inc b/roms/skiboot/libstb/drivers/Makefile.inc new file mode 100644 index 000000000..80356eb36 --- /dev/null +++ b/roms/skiboot/libstb/drivers/Makefile.inc @@ -0,0 +1,11 @@ +# -*-Makefile-*- + +DRIVERS_DIR = libstb/drivers + +SUBDIRS += $(DRIVERS_DIR) + +DRIVERS_SRCS = tpm_i2c_interface.c tpm_i2c_nuvoton.c +DRIVERS_OBJS = $(DRIVERS_SRCS:%.c=%.o) +DRIVERS = $(DRIVERS_DIR)/built-in.a + +$(DRIVERS): $(DRIVERS_OBJS:%=$(DRIVERS_DIR)/%) diff --git a/roms/skiboot/libstb/drivers/tpm_i2c_interface.c b/roms/skiboot/libstb/drivers/tpm_i2c_interface.c new file mode 100644 index 000000000..84cbab5bb --- /dev/null +++ b/roms/skiboot/libstb/drivers/tpm_i2c_interface.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* Copyright 2013-2018 IBM Corp. */ + +#include <skiboot.h> +#include <opal-api.h> +#include <i2c.h> + +#include "tpm_i2c_interface.h" +#include "../status_codes.h" + +/* TPMs can clock strech I2C operations for a LOOOOOOONG */ +#define I2C_BYTE_TIMEOUT_MS 2000 /* 2000ms/byte timeout */ + +/** + * tpm_i2c_request_send - send request to i2c bus + * @tpm_bus_id: i2c bus id + * @tpm_dev_addr: address of the tpm device + * @read_write: SMBUS_READ or SMBUS_WRITE + * @offset: any of the I2C interface offset defined + * @offset_bytes: offset size in bytes + * @buf: data to be read or written + * @buflen: buf length + * + * This interacts with skiboot i2c API to send an I2C request to the tpm + * device + * + * Returns: Zero on success otherwise a negative error code + */ +int tpm_i2c_request_send(struct tpm_dev *tpm_device, int read_write, + uint32_t offset, uint32_t offset_bytes, void* buf, + size_t buflen) +{ + int rc, timeout; + + /* + * Set the request timeout to 30ms per byte. Otherwise, we get + * an I2C master timeout for all requests sent to the device + * since the I2C master's timeout is too short (1ms per byte). + */ + timeout = (buflen + offset_bytes + 2) * I2C_BYTE_TIMEOUT_MS; + + rc = i2c_request_send(tpm_device->bus_id, tpm_device->i2c_addr, + read_write, offset, offset_bytes, buf, buflen, + timeout); + if (rc == OPAL_PARAMETER) + return STB_ARG_ERROR; + else if (rc < 0) + return STB_DRIVER_ERROR; + return 0; +} diff --git a/roms/skiboot/libstb/drivers/tpm_i2c_interface.h b/roms/skiboot/libstb/drivers/tpm_i2c_interface.h new file mode 100644 index 000000000..514d8f835 --- /dev/null +++ b/roms/skiboot/libstb/drivers/tpm_i2c_interface.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* Copyright 2013-2018 IBM Corp. */ + +#ifndef __TPM_I2C_H +#define __TPM_I2C_H + +#include <i2c.h> +#include <stdlib.h> + +#include "../tpm_chip.h" + +extern int tpm_i2c_request_send(struct tpm_dev *tpm_device, int read_write, + uint32_t offset, uint32_t offset_bytes, void* buf, + size_t buflen); +#endif /* __TPM_I2C_H */ diff --git a/roms/skiboot/libstb/drivers/tpm_i2c_nuvoton.c b/roms/skiboot/libstb/drivers/tpm_i2c_nuvoton.c new file mode 100644 index 000000000..0aa9711db --- /dev/null +++ b/roms/skiboot/libstb/drivers/tpm_i2c_nuvoton.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* Copyright 2013-2018 IBM Corp. */ + +#include <timebase.h> +#include <skiboot.h> +#include <device.h> +#include <i2c.h> +#include "../status_codes.h" +#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, ...) + +#define DRIVER_NAME "i2c_tpm_nuvoton" + +/* I2C interface offsets */ +#define TPM_STS 0x00 +#define TPM_BURST_COUNT 0x01 +#define TPM_DATA_FIFO_W 0x20 +#define TPM_DATA_FIFO_R 0x40 +#define TPM_VID_DID 0x60 + +/* Bit masks for the TPM STATUS register */ +#define TPM_STS_VALID 0x80 +#define TPM_STS_COMMAND_READY 0x40 +#define TPM_STS_GO 0x20 +#define TPM_STS_DATA_AVAIL 0x10 +#define TPM_STS_EXPECT 0x08 + + +/* TPM Driver values */ +#define MAX_STSVALID_POLLS 5 +#define TPM_TIMEOUT_INTERVAL 10 +#define TPM_NUVOTON_VID 0x5010FE00 +#define TPM_VENDOR_ID_MASK 0xFFFFFF00 + +static struct tpm_dev *tpm_device = NULL; + +static int tpm_status_write_byte(uint8_t byte) +{ + uint8_t value = byte; + return tpm_i2c_request_send(tpm_device, SMBUS_WRITE, TPM_STS, 1, &value, + sizeof(value)); +} + +static int tpm_status_read_byte(uint8_t offset, uint8_t *byte) +{ + return tpm_i2c_request_send(tpm_device, SMBUS_READ, offset, 1, byte, + sizeof(uint8_t)); +} + +static bool tpm_check_status(uint8_t status, uint8_t mask, uint8_t expected) +{ + return ((status & mask) == expected); +} + +static int tpm_wait_for_command_ready(void) +{ + uint64_t start, stop, now; + int rc; + uint8_t status; + + start = mftb(); + stop = start + msecs_to_tb(TPM_TIMEOUT_B); + + do { + now = mftb(); + rc = tpm_status_read_byte(TPM_STS, &status); + if (rc < 0) { + /** + * @fwts-label TPMReadCmdReady + * @fwts-advice Either the tpm device or the tpm-i2c + * interface doesn't seem to be working properly. Check + * the return code (rc) for further details. + */ + prlog(PR_ERR, "NUVOTON: fail to read sts.commandReady, " + "rc=%d\n", rc); + return STB_DRIVER_ERROR; + } + if (tpm_check_status(status, + TPM_STS_COMMAND_READY, + TPM_STS_COMMAND_READY)) { + DBG("--- Command ready, delay=%lu/%d\n", + tb_to_msecs(now-start), TPM_TIMEOUT_B); + return 0; + } + if (tb_compare(now, stop) == TB_ABEFOREB) + time_wait_ms(TPM_TIMEOUT_INTERVAL); + else + break; + } while (1); + + return STB_TPM_TIMEOUT; +} + +static int tpm_set_command_ready(void) +{ + int rc, retries; + /* + * The first write to command ready may just abort an + * outstanding command, so we poll twice + */ + for (retries = 0; retries < 2; retries++) { + rc = tpm_status_write_byte(TPM_STS_COMMAND_READY); + if (rc < 0) { + /** + * @fwts-label TPMWriteCmdReady + * @fwts-advice Either the tpm device or the tpm-i2c + * interface doesn't seem to be working properly. Check + * the return code (rc) for further details. + */ + prlog(PR_ERR, "NUVOTON: fail to write sts.commandReady, " + "rc=%d\n", rc); + return STB_DRIVER_ERROR; + } + rc = tpm_wait_for_command_ready(); + if (rc == STB_TPM_TIMEOUT) + continue; + return rc; + } + /** + * @fwts-label TPMCmdReadyTimeout + * @fwts-advice The command ready bit of the tpm status register is + * taking longer to be settled. Either the wait time need to be + * increased or the TPM device is not functional. + */ + prlog(PR_ERR, "NUVOTON: timeout on sts.commandReady, delay > %d\n", + 2*TPM_TIMEOUT_B); + return STB_TPM_TIMEOUT; +} + +static int tpm_wait_for_fifo_status(uint8_t mask, uint8_t expected) +{ + int retries, rc; + uint8_t status; + + for(retries = 0; retries <= MAX_STSVALID_POLLS; retries++) { + rc = tpm_status_read_byte(TPM_STS, &status); + if (rc < 0) { + /** + * @fwts-label TPMReadFifoStatus + * @fwts-advice Either the tpm device or the tpm-i2c + * interface doesn't seem to be working properly. Check + * the return code (rc) for further details. + */ + prlog(PR_ERR, "NUVOTON: fail to read fifo status: " + "mask %x, expected %x, rc=%d\n", mask, expected, + rc); + return STB_DRIVER_ERROR; + } + if (tpm_check_status(status, mask, expected)) + return 0; + /* Wait TPM STS register be settled */ + time_wait_ms(5); + } + return STB_TPM_TIMEOUT; +} + +static int tpm_wait_for_data_avail(void) +{ + uint64_t start, stop, now; + uint8_t status; + int rc; + + start = mftb(); + stop = start + msecs_to_tb(TPM_TIMEOUT_A); + + do { + now = mftb(); + rc = tpm_status_read_byte(TPM_STS, &status); + if (rc < 0) { + /** + * @fwts-label TPMReadDataAvail + * @fwts-advice Either the tpm device or the tpm-i2c + * interface doesn't seem to be working properly. Check + * the return code (rc) for further details. + */ + prlog(PR_ERR, "NUVOTON: fail to read sts.dataAvail, " + "rc=%d\n", rc); + return STB_DRIVER_ERROR; + } + if (tpm_check_status(status, + TPM_STS_VALID | TPM_STS_DATA_AVAIL, + TPM_STS_VALID | TPM_STS_DATA_AVAIL)) { + DBG("---- Data available. delay=%lu/%d\n", + tb_to_msecs(now-start), TPM_TIMEOUT_A); + return 0; + } + if (tb_compare(now, stop) == TB_ABEFOREB) + time_wait_ms(TPM_TIMEOUT_INTERVAL); + else + break; + } while (1); + /** + * @fwts-label TPMDataAvailBitTimeout + * @fwts-advice The data avail bit of the tpm status register is taking + * longer to be settled. Either the wait time need to be increased or + * the TPM device is not functional. + */ + prlog(PR_ERR, "NUVOTON: timeout on sts.dataAvail, delay=%lu/%d\n", + tb_to_msecs(now-start), TPM_TIMEOUT_A); + return STB_TPM_TIMEOUT; +} + +static int tpm_read_burst_count(void) +{ + uint64_t start, stop, now; + uint8_t burst_count; + int rc; + + start = mftb(); + stop = start + msecs_to_tb(TPM_TIMEOUT_D); + burst_count = 0; + + do { + now = mftb(); + /* In i2C, burstCount is 1 byte */ + rc = tpm_status_read_byte(TPM_BURST_COUNT, &burst_count); + if (rc == 0 && burst_count > 0) { + DBG("---- burst_count=%d, delay=%lu/%d\n", burst_count, + tb_to_msecs(now-start), TPM_TIMEOUT_D); + return (int) burst_count; + } + if (rc < 0) { + /** + * @fwts-label TPMReadBurstCount + * @fwts-advice Either the tpm device or the tpm-i2c + * interface doesn't seem to be working properly. Check + * the return code (rc) for further details. + */ + prlog(PR_ERR, "NUVOTON: fail to read sts.burstCount, " + "rc=%d\n", rc); + return STB_DRIVER_ERROR; + } + if (tb_compare(now, stop) == TB_ABEFOREB) + time_wait_ms(TPM_TIMEOUT_INTERVAL); + else + break; + } while (1); + + /** + * @fwts-label TPMBurstCountTimeout + * @fwts-advice The burstcount bit of the tpm status register is + * taking longer to be settled. Either the wait time need to be + * increased or the TPM device is not functional. + */ + prlog(PR_ERR, "NUVOTON: timeout on sts.burstCount, delay=%lu/%d\n", + tb_to_msecs(now-start), TPM_TIMEOUT_D); + return STB_TPM_TIMEOUT; +} + +static int tpm_write_fifo(uint8_t* buf, size_t buflen) +{ + int rc, burst_count; + size_t count, bytes; + + /* + * We will transfer the command except for the last byte + * that will be transfered separately to allow for + * overflow checking + */ + count = 0; + do { + burst_count = tpm_read_burst_count(); + if (burst_count < 0) + return burst_count; + + bytes = (count + burst_count > buflen - 1 ? + (buflen - 1 - count) : burst_count); + + rc = tpm_i2c_request_send(tpm_device, + SMBUS_WRITE, TPM_DATA_FIFO_W, + 1, &buf[count], bytes); + count += bytes; + DBG("%s FIFO: %zd bytes written, count=%zd, rc=%d\n", + (rc) ? "!!!!" : "----", bytes, count, rc); + if (rc < 0) { + /** + * @fwts-label TPMWriteFifo + * @fwts-advice Either the tpm device or the tpm-i2c + * interface doesn't seem to be working properly. Check + * the return code (rc) for further details. + */ + prlog(PR_ERR, "NUVOTON: fail to write fifo, " + "count=%zd, rc=%d\n", count, rc); + return STB_DRIVER_ERROR; + } + + rc = tpm_wait_for_fifo_status(TPM_STS_VALID | TPM_STS_EXPECT, + TPM_STS_VALID | TPM_STS_EXPECT); + if (rc == STB_DRIVER_ERROR) + return rc; + if (rc == STB_TPM_TIMEOUT) { + /** + * @fwts-label TPMWriteFifoNotExpecting + * @fwts-advice The write to the TPM FIFO overflowed, + * the TPM is not expecting more data. This indicates a + * bug in the TPM device driver. + */ + prlog(PR_ERR, "NUVOTON: write FIFO overflow, not expecting " + "more data\n"); + return STB_TPM_OVERFLOW; + } + } while (count < buflen - 1); + + /* + * Write the last byte + */ + burst_count = tpm_read_burst_count(); + if (burst_count < 0) + return burst_count; + + rc = tpm_i2c_request_send(tpm_device, + SMBUS_WRITE, + TPM_DATA_FIFO_W, 1, + &buf[count], 1); + count++; + DBG("%s FIFO: last byte written, count=%zd, rc=%d\n", + (rc) ? "!!!!" : "----", count, rc); + + if (rc < 0) { + /** + * @fwts-label TPMWriteFifoLastByte + * @fwts-advice Either the tpm device or the tpm-i2c interface + * doesn't seem to be working properly. Check the return code + * (rc) for further details. + */ + prlog(PR_ERR, "NUVOTON: fail to write fifo (last byte), " + "count=%zd, rc=%d\n", count, rc); + return STB_DRIVER_ERROR; + } + rc = tpm_wait_for_fifo_status(TPM_STS_VALID | TPM_STS_EXPECT, + TPM_STS_VALID | TPM_STS_EXPECT); + if (rc == STB_DRIVER_ERROR) + return rc; + if (rc == 0) { + /** + * @fwts-label TPMWriteFifoExpecting + * @fwts-advice The write to the TPM FIFO overflowed. + * It is expecting more data even though we think we + * are done. This indicates a bug in the TPM device + * driver. + */ + prlog(PR_ERR, "TPM: write FIFO overflow, expecting " + "more data\n"); + return STB_TPM_OVERFLOW; + } + return 0; +} + +static int tpm_read_fifo(uint8_t* buf, size_t* buflen) +{ + int rc, burst_count; + size_t count; + + rc = tpm_wait_for_data_avail(); + if (rc < 0) + goto error; + + count = 0; + do { + burst_count = tpm_read_burst_count(); + if (burst_count < 0) { + rc = burst_count; + goto error; + } + if (count + burst_count > *buflen) { + /** + * @fwts-label TPMReadFifoOverflow + * @fwts-advice The read from TPM FIFO overflowed. It is + * expecting more data even though we think we are done. + * This indicates a bug in the TPM device driver. + */ + prlog(PR_ERR, "NUVOTON: overflow on fifo read, c=%zd, " + "bc=%d, bl=%zd\n", count, burst_count, *buflen); + rc = STB_TPM_OVERFLOW; + goto error; + } + rc = tpm_i2c_request_send(tpm_device, + SMBUS_READ, + TPM_DATA_FIFO_R, 1, + &buf[count], burst_count); + count += burst_count; + DBG("%s FIFO: %d bytes read, count=%zd, rc=%d\n", + (rc) ? "!!!!" : "----", burst_count, count, rc); + if (rc < 0) { + /** + * @fwts-label TPMReadFifo + * @fwts-advice Either the tpm device or the tpm-i2c interface + * doesn't seem to be working properly. Check the return code + * (rc) for further details. + */ + prlog(PR_ERR, "NUVOTON: fail to read fifo, count=%zd, " + "rc=%d\n", count, rc); + rc = STB_DRIVER_ERROR; + goto error; + } + rc = tpm_wait_for_fifo_status( + TPM_STS_VALID | TPM_STS_DATA_AVAIL, + TPM_STS_VALID | TPM_STS_DATA_AVAIL); + if (rc == STB_DRIVER_ERROR) + goto error; + } while (rc == 0); + + *buflen = count; + return 0; + +error: + *buflen = 0; + return rc; +} + +static int tpm_transmit(struct tpm_dev *dev, uint8_t* buf, size_t cmdlen, + size_t* buflen) +{ + int rc = 0; + if (!dev) { + /** + * @fwts-label TPMDeviceNotInitialized + * @fwts-advice TPM device is not initialized. This indicates a + * bug in the tpm_transmit() caller + */ + prlog(PR_ERR, "TPM: tpm device not initialized\n"); + return STB_ARG_ERROR; + } + tpm_device = dev; + DBG("**** %s: dev %#x/%#x buf %016llx cmdlen %zu" + " buflen %zu ****\n", + __func__, dev->bus_id, dev->i2c_addr, *(uint64_t *) buf, + cmdlen, *buflen); + + DBG("step 1/5: set command ready\n"); + rc = tpm_set_command_ready(); + if (rc < 0) + goto out; + + DBG("step 2/5: write FIFO\n"); + rc = tpm_write_fifo(buf, cmdlen); + if (rc < 0) + goto out; + + DBG("step 3/5: write sts.go\n"); + rc = tpm_status_write_byte(TPM_STS_GO); + if (rc < 0) { + /** + * @fwts-label TPMWriteGo + * @fwts-advice Either the tpm device or the tpm-i2c interface + * doesn't seem to be working properly. Check the return code + * (rc) for further details. + */ + prlog(PR_ERR, "NUVOTON: fail to write sts.go, rc=%d\n", rc); + rc = STB_DRIVER_ERROR; + goto out; + } + + DBG("step 4/5: read FIFO\n"); + rc = tpm_read_fifo(buf, buflen); + if (rc < 0) + goto out; + + DBG("step 5/5: release tpm\n"); + rc = tpm_status_write_byte(TPM_STS_COMMAND_READY); + if (rc < 0) { + /** + * @fwts-label TPMReleaseTpm + * @fwts-advice Either the tpm device or the tpm-i2c interface + * doesn't seem to be working properly. Check the return code + * (rc) for further details. + */ + prlog(PR_ERR, "NUVOTON: fail to release tpm, rc=%d\n", rc); + rc = STB_DRIVER_ERROR; + } + +out: + DBG("**** tpm_transmit %s, rc=%d ****\n", + (rc) ? "ERROR" : "SUCCESS", rc); + return rc; +} + +static struct tpm_driver tpm_i2c_nuvoton_driver = { + .name = DRIVER_NAME, + .transmit = tpm_transmit, +}; + +static int nuvoton_tpm_quirk(void *data, struct i2c_request *req, int *rc) +{ + struct tpm_dev *tpm_device = data; + struct dt_node *dev; + uint16_t addr; + bool found; + + /* + * The nuvoton TPM firmware has a problem where a single byte read or + * zero byte write to one of its I2C addresses causes the TPM to lock + * up the bus. Once locked up the bus can only be recovered by power + * cycling the TPM. Unfortunately, we don't have the ability to + * power cycle the TPM because allowing it to be reset at runtime + * would undermine the TPM's security model (we can reset it and + * send it whatever measurements we like to unlock it's secrets). + * So the best we can do here is try avoid triggering the problem + * in the first place. + * + * For a bit of added fun the TPM also appears to check for traffic + * on a few different I2C bus addresses. It does this even when not + * configured to respond on those addresses so you can trigger the + * bug by sending traffic... somwhere. To work around this we block + * sending I2C requests on the TPM's bus unless the DT explicitly + * tells us there is a device there. + */ + + /* first, check if this a known address */ + addr = req->dev_addr; + found = false; + + dt_for_each_child(req->bus->dt_node, dev) { + if (dt_prop_get_u32(dev, "reg") == addr) { + found = true; + break; + } + } + + if (!found) { + *rc = OPAL_I2C_TIMEOUT; + return 1; + } + + /* second, check if it's a bad transaction to the TPM */ + if (tpm_device->bus_id == req->bus->opal_id && + tpm_device->i2c_addr == 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; + const char *name; + uint32_t vendor = 0; + + dt_for_each_compatible(dt_root, node, "nuvoton,npct650") { + if (!dt_node_is_enabled(node)) + continue; + tpm_device = (struct tpm_dev*) malloc(sizeof(struct tpm_dev)); + assert(tpm_device); + /* + * Read TPM device address and bus id. Make sure the properties + * really exist if the default value is returned. + */ + tpm_device->i2c_addr = dt_prop_get_u32_def(node, "reg", 0); + if (!tpm_device->i2c_addr && !dt_find_property(node, "reg")) { + /* + * @fwts-label NuvotonRegNotFound + * @fwts-advice reg property not found. This indicates + * a Hostboot bug if the property really doesn't exist + * in the tpm node. + */ + prlog(PR_ERR, "NUVOTON: reg property not found, " + "tpm node %p\n", node); + goto disable; + } + tpm_device->bus_id = dt_prop_get_u32_def(node->parent, + "ibm,opal-id", 0); + if (!tpm_device->bus_id && + !dt_find_property(node->parent, "ibm,opal-id")) { + /* + * @fwts-label NuvotonIbmOpalIdNotFound + * @fwts-advice ibm,opal-id property not found. This + * indicates a Hostboot bug if the property really + * doesn't exist in the tpm node. + */ + prlog(PR_ERR, "NUVOTON: ibm,opal-id property not " + "found, tpm node parent %p\n", node->parent); + goto disable; + } + /* ensure there's really the TPM we expect at that address */ + if (tpm_i2c_request_send(tpm_device, SMBUS_READ, TPM_VID_DID, + 1, &vendor, sizeof(vendor))) { + prlog(PR_ERR, "NUVOTON: i2c device inaccessible\n"); + goto disable; + } + if ((vendor & TPM_VENDOR_ID_MASK) != TPM_NUVOTON_VID) { + prlog(PR_ERR, "NUVOTON: expected vendor id mismatch\n"); + goto disable; + } + if (tpm_register_chip(node, tpm_device, + &tpm_i2c_nuvoton_driver)) { + free(tpm_device); + continue; + } + tss_tpm_register(tpm_device, &tpm_i2c_nuvoton_driver); + 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; + name = dt_prop_get(node->parent, "ibm,port-name"); + + prlog(PR_NOTICE, "NUVOTON: TPM I2C workaround applied to %s\n", + name); + + /* + * Tweak for linux. It doesn't have a driver compatible + * with "nuvoton,npct650" + */ + if (!dt_node_is_compatible(node, "nuvoton,npct601")) { + dt_check_del_prop(node, "compatible"); + dt_add_property_strings(node, "compatible", + "nuvoton,npct650", "nuvoton,npct601"); + } + } + return; +disable: + dt_add_property_string(node, "status", "disabled"); + prlog(PR_NOTICE, "TPM: tpm node %p disabled\n", node); + free(tpm_device); +} diff --git a/roms/skiboot/libstb/drivers/tpm_i2c_nuvoton.h b/roms/skiboot/libstb/drivers/tpm_i2c_nuvoton.h new file mode 100644 index 000000000..53cf1e1da --- /dev/null +++ b/roms/skiboot/libstb/drivers/tpm_i2c_nuvoton.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* Copyright 2013-2016 IBM Corp. */ + +#ifndef __TPM_I2C_NUVOTON_H +#define __TPM_I2C_NUVOTON_H + +extern void tpm_i2c_nuvoton_probe(void); + +#endif /* __TPM_I2C_NUVOTON_H */ |