blob: 2099bb7bf699e5807106817f0267b38bbaf4b3a6 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* The code in this file has been heavily based on the article "Writing a TPM
* Device Driver" published on http://ptgmedia.pearsoncmg.com and the
* submission by Stefan Berger on Qemu-devel mailing list.
*
* One principal difference is that in the simplest config the other than 0
* TPM localities do not get mapped by some devices (for instance, by
* Infineon slb9635), so this driver provides access to locality 0 only.
*/
#include <commonlib/helpers.h>
#include <string.h>
#include <delay.h>
#include <device/mmio.h>
#include <acpi/acpi.h>
#include <acpi/acpigen.h>
#include <acpi/acpi_device.h>
#include <device/device.h>
#include <console/console.h>
#include <security/tpm/tis.h>
#include <security/tpm/tss.h>
#include <device/pnp.h>
#include <drivers/tpm/tpm_ppi.h>
#include <timer.h>
#include "chip.h"
#define PREFIX "lpc_tpm: "
/* coreboot wrapper for TPM driver (start) */
#define TPM_DEBUG(fmt, args...) \
if (CONFIG(DEBUG_TPM)) { \
printk(BIOS_DEBUG, PREFIX); \
printk(BIOS_DEBUG, fmt, ##args); \
}
#define TPM_DEBUG_IO_READ(reg_, val_) \
TPM_DEBUG("Read reg %#x returns %#x\n", (reg_), (val_))
#define TPM_DEBUG_IO_WRITE(reg_, val_) \
TPM_DEBUG("Write reg %#x with %#x\n", (reg_), (val_))
#define printf(x...) printk(BIOS_ERR, x)
/* coreboot wrapper for TPM driver (end) */
/* the macro accepts the locality value, but only locality 0 is operational */
#define TIS_REG(LOCALITY, REG) \
(void *)(uintptr_t)(CONFIG_TPM_TIS_BASE_ADDRESS + (LOCALITY << 12) + REG)
/* hardware registers' offsets */
#define TIS_REG_ACCESS 0x0
#define TIS_REG_INT_ENABLE 0x8
#define TIS_REG_INT_VECTOR 0xc
#define TIS_REG_INT_STATUS 0x10
#define TIS_REG_INTF_CAPABILITY 0x14
#define TIS_REG_STS 0x18
#define TIS_REG_BURST_COUNT 0x19
#define TIS_REG_DATA_FIFO 0x24
#define TIS_REG_DID_VID 0xf00
#define TIS_REG_RID 0xf04
/* Some registers' bit field definitions */
#define TIS_STS_VALID (1 << 7) /* 0x80 */
#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */
#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */
#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */
#define TIS_STS_EXPECT (1 << 3) /* 0x08 */
#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */
#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */
#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */
#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */
#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */
#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */
#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */
#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */
/* 1 second is plenty for anything TPM does.*/
#define MAX_DELAY_US USECS_PER_SEC
/*
* Structures defined below allow creating descriptions of TPM vendor/device
* ID information for run time discovery. The only device the system knows
* about at this time is Infineon slb9635
*/
struct device_name {
u16 dev_id;
const char *const dev_name;
};
struct vendor_name {
u16 vendor_id;
const char *vendor_name;
const struct device_name *dev_names;
};
static const struct device_name atmel_devices[] = {
{0x3204, "AT97SC3204"},
{0xffff}
};
static const struct device_name infineon_devices[] = {
{0x000b, "SLB9635 TT 1.2"},
#if CONFIG(TPM2)
{0x001a, "SLB9665 TT 2.0"},
{0x001b, "SLB9670 TT 2.0"},
{0x001d, "SLB9672 TT 2.0"},
#else
{0x001a, "SLB9660 TT 1.2"},
{0x001b, "SLB9670 TT 1.2"},
#endif
{0xffff}
};
static const struct device_name nuvoton_devices[] = {
{0x00fe, "NPCT420AA V2"},
{0xffff}
};
static const struct device_name stmicro_devices[] = {
{0x0000, "ST33ZP24" },
{0xffff}
};
static const struct device_name swtpm_devices[] = {
#if CONFIG(TPM2)
{0x0001, "SwTPM 2.0" },
#endif
{0xffff}
};
static const struct vendor_name vendor_names[] = {
{0x1114, "Atmel", atmel_devices},
{0x15d1, "Infineon", infineon_devices},
{0x1050, "Nuvoton", nuvoton_devices},
{0x1014, "TPM Emulator", swtpm_devices},
{0x104a, "ST Microelectronics", stmicro_devices},
};
/*
* Cached vendor/device ID pair to indicate that the device has been already
* discovered
*/
static u32 vendor_dev_id;
static inline u8 tpm_read_status(int locality)
{
u8 value = read8(TIS_REG(locality, TIS_REG_STS));
TPM_DEBUG_IO_READ(TIS_REG_STS, value);
return value;
}
static inline void tpm_write_status(u8 sts, int locality)
{
TPM_DEBUG_IO_WRITE(TIS_REG_STS, sts);
write8(TIS_REG(locality, TIS_REG_STS), sts);
}
static inline u8 tpm_read_data(int locality)
{
u8 value = read8(TIS_REG(locality, TIS_REG_DATA_FIFO));
TPM_DEBUG_IO_READ(TIS_REG_DATA_FIFO, value);
return value;
}
static inline void tpm_write_data(u8 data, int locality)
{
TPM_DEBUG_IO_WRITE(TIS_REG_DATA_FIFO, data);
write8(TIS_REG(locality, TIS_REG_DATA_FIFO), data);
}
static inline u16 tpm_read_burst_count(int locality)
{
u16 count;
count = read8(TIS_REG(locality, TIS_REG_BURST_COUNT));
count |= read8(TIS_REG(locality, TIS_REG_BURST_COUNT + 1)) << 8;
TPM_DEBUG_IO_READ(TIS_REG_BURST_COUNT, count);
return count;
}
static inline u8 tpm_read_access(int locality)
{
u8 value = read8(TIS_REG(locality, TIS_REG_ACCESS));
TPM_DEBUG_IO_READ(TIS_REG_ACCESS, value);
return value;
}
static inline void tpm_write_access(u8 data, int locality)
{
TPM_DEBUG_IO_WRITE(TIS_REG_ACCESS, data);
write8(TIS_REG(locality, TIS_REG_ACCESS), data);
}
static inline u32 tpm_read_did_vid(int locality)
{
u32 value = read32(TIS_REG(locality, TIS_REG_DID_VID));
TPM_DEBUG_IO_READ(TIS_REG_DID_VID, value);
return value;
}
static inline void tpm_write_int_vector(int vector, int locality)
{
TPM_DEBUG_IO_WRITE(TIS_REG_INT_VECTOR, vector);
write8(TIS_REG(locality, TIS_REG_INT_VECTOR), vector & 0xf);
}
static inline u8 tpm_read_int_vector(int locality)
{
u8 value = read8(TIS_REG(locality, TIS_REG_INT_VECTOR));
TPM_DEBUG_IO_READ(TIS_REG_INT_VECTOR, value);
return value;
}
static inline void tpm_write_int_polarity(int polarity, int locality)
{
/* Set polarity and leave all other bits at 0 */
u32 value = (polarity & 0x3) << 3;
TPM_DEBUG_IO_WRITE(TIS_REG_INT_ENABLE, value);
write32(TIS_REG(locality, TIS_REG_INT_ENABLE), value);
}
static inline u32 tpm_read_int_polarity(int locality)
{
/* Get polarity and leave all other bits */
u32 value = read8(TIS_REG(locality, TIS_REG_INT_ENABLE));
value = (value >> 3) & 0x3;
TPM_DEBUG_IO_READ(TIS_REG_INT_ENABLE, value);
return value;
}
/*
* tis_wait_sts()
*
* Wait for at most a second for a status to change its state to match the
* expected state. Normally the transition happens within microseconds.
*
* @locality - locality
* @mask - bitmask for the bitfield(s) to watch
* @expected - value the field(s) are supposed to be set to
*
* Returns TPM_SUCCESS on success or TPM_CB_TIMEOUT on timeout.
*/
static tpm_result_t tis_wait_sts(int locality, u8 mask, u8 expected)
{
struct stopwatch sw;
stopwatch_init_usecs_expire(&sw, MAX_DELAY_US);
do {
u8 value = tpm_read_status(locality);
if ((value & mask) == expected)
return TPM_SUCCESS;
udelay(1);
} while (!stopwatch_expired(&sw));
return TPM_CB_TIMEOUT;
}
static inline tpm_result_t tis_wait_ready(int locality)
{
return tis_wait_sts(locality, TIS_STS_COMMAND_READY,
TIS_STS_COMMAND_READY);
}
static inline tpm_result_t tis_wait_valid(int locality)
{
return tis_wait_sts(locality, TIS_STS_VALID, TIS_STS_VALID);
}
static inline tpm_result_t tis_wait_valid_data(int locality)
{
const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
return tis_wait_sts(locality, has_data, has_data);
}
static inline int tis_has_valid_data(int locality)
{
const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
return (tpm_read_status(locality) & has_data) == has_data;
}
static inline int tis_expect_data(int locality)
{
return !!(tpm_read_status(locality) & TIS_STS_EXPECT);
}
/*
* tis_wait_access()
*
* Wait for at most a second for a access to change its state to match the
* expected state. Normally the transition happens within microseconds.
*
* @locality - locality
* @mask - bitmask for the bitfield(s) to watch
* @expected - value the field(s) are supposed to be set to
*
* Returns TPM_SUCCESS on success or TPM_CB_TIMEOUT on timeout.
*/
static tpm_result_t tis_wait_access(int locality, u8 mask, u8 expected)
{
struct stopwatch sw;
stopwatch_init_usecs_expire(&sw, MAX_DELAY_US);
do {
u8 value = tpm_read_access(locality);
if ((value & mask) == expected)
return TPM_SUCCESS;
udelay(1);
} while (!stopwatch_expired(&sw));
return TPM_CB_TIMEOUT;
}
static inline tpm_result_t tis_wait_received_access(int locality)
{
return tis_wait_access(locality, TIS_ACCESS_ACTIVE_LOCALITY,
TIS_ACCESS_ACTIVE_LOCALITY);
}
static inline int tis_has_access(int locality)
{
return !!(tpm_read_access(locality) & TIS_ACCESS_ACTIVE_LOCALITY);
}
static inline void tis_request_access(int locality)
{
tpm_write_access(TIS_ACCESS_REQUEST_USE, locality);
}
/*
* PC Client Specific TPM Interface Specification section 11.2.12:
*
* Software must be prepared to send two writes of a "1" to command ready
* field: the first to indicate successful read of all the data, thus
* clearing the data from the ReadFIFO and freeing the TPM's resources,
* and the second to indicate to the TPM it is about to send a new command.
*
* In practice not all TPMs behave the same so it is necessary to be
* flexible when trying to set command ready.
*
*/
static tpm_result_t tis_command_ready(u8 locality)
{
u32 status;
/* 1st attempt to set command ready */
tpm_write_status(TIS_STS_COMMAND_READY, locality);
/* Wait for response */
status = tpm_read_status(locality);
/* Check if command ready is set yet */
if (status & TIS_STS_COMMAND_READY)
return TPM_SUCCESS;
/* 2nd attempt to set command ready */
tpm_write_status(TIS_STS_COMMAND_READY, locality);
return tis_wait_ready(locality);
}
/*
* pc80_tis_probe()
*
* Probe the TPM device and try determining its manufacturer/device name.
*
* Returns TPM_SUCCESS on success (the device is found or was found during
* an earlier invocation) or TPM_CB_FAIL if the device is not found.
*/
static tpm_result_t pc80_tis_probe(void)
{
const char *device_name = "unknown";
const char *vendor_name = device_name;
const struct device_name *dev;
u32 didvid;
u16 vid, did;
int i;
if (vendor_dev_id)
return TPM_SUCCESS; /* Already probed. */
didvid = tpm_read_did_vid(0);
if (!didvid || (didvid == 0xffffffff)) {
printf("%s: No TPM device found\n", __func__);
return TPM_CB_FAIL;
}
vendor_dev_id = didvid;
vid = didvid & 0xffff;
did = (didvid >> 16) & 0xffff;
for (i = 0; i < ARRAY_SIZE(vendor_names); i++) {
int j = 0;
u16 known_did;
if (vid == vendor_names[i].vendor_id) {
vendor_name = vendor_names[i].vendor_name;
} else {
continue;
}
dev = &vendor_names[i].dev_names[j];
while ((known_did = dev->dev_id) != 0xffff) {
if (known_did == did) {
device_name = dev->dev_name;
break;
}
j++;
dev = &vendor_names[i].dev_names[j];
}
break;
}
/* this will have to be converted into debug printout */
printk(BIOS_INFO, "Found TPM %s by %s\n", device_name, vendor_name);
return TPM_SUCCESS;
}
/*
* tis_senddata()
*
* send the passed in data to the TPM device.
*
* @data - address of the data to send, byte by byte
* @len - length of the data to send
*
* Returns TPM_SUCCESS on success, TPM_CB_FAIL on error (in case the device does
* not accept the entire command).
*/
static tpm_result_t tis_senddata(const u8 *const data, u32 len)
{
u32 offset = 0;
u16 burst = 0;
u8 locality = 0;
tpm_result_t rc = TPM_SUCCESS;
rc = tis_wait_ready(locality);
if (rc) {
printf("%s:%d - failed to get 'command_ready' status with error %#x\n",
__FILE__, __LINE__, rc);
return rc;
}
burst = tpm_read_burst_count(locality);
while (1) {
unsigned int count;
struct stopwatch sw;
/* Wait till the device is ready to accept more data. */
stopwatch_init_usecs_expire(&sw, MAX_DELAY_US);
while (!burst) {
if (stopwatch_expired(&sw)) {
printf("%s:%d failed to feed %u bytes of %u\n",
__FILE__, __LINE__, len - offset, len);
return TPM_CB_TIMEOUT;
}
udelay(1);
burst = tpm_read_burst_count(locality);
}
/*
* Calculate number of bytes the TPM is ready to accept in one
* shot.
*
* We want to send the last byte outside of the loop (hence
* the -1 below) to make sure that the 'expected' status bit
* changes to zero exactly after the last byte is fed into the
* FIFO.
*/
count = MIN(burst, len - offset - 1);
while (count--)
tpm_write_data(data[offset++], locality);
rc = tis_wait_valid(locality);
if (rc || !tis_expect_data(locality)) {
printf("%s:%d TPM command feed overflow with error %#x\n",
__FILE__, __LINE__, rc);
return rc ? rc : TPM_CB_FAIL;
}
burst = tpm_read_burst_count(locality);
if ((offset == (len - 1)) && burst)
/*
* We need to be able to send the last byte to the
* device, so burst size must be nonzero before we
* break out.
*/
break;
}
/* Send the last byte. */
tpm_write_data(data[offset++], locality);
/*
* Verify that TPM does not expect any more data as part of this
* command.
*/
rc = tis_wait_valid(locality);
if (rc || tis_expect_data(locality)) {
printf("%s:%d unexpected TPM error %#x with status %#x\n",
__FILE__, __LINE__, rc, tpm_read_status(locality));
return rc ? rc : TPM_CB_FAIL;
}
/* OK, sitting pretty, let's start the command execution. */
tpm_write_status(TIS_STS_TPM_GO, locality);
return TPM_SUCCESS;
}
/*
* tis_readresponse()
*
* read the TPM device response after a command was issued.
*
* @buffer - address where to read the response, byte by byte.
* @len - pointer to the size of buffer
*
* On success stores the number of received bytes to len and returns
* TPM_SUCCESS. On errors (misformatted TPM data or synchronization
* problems) returns TPM_CB_FAIL.
*/
static tpm_result_t tis_readresponse(u8 *buffer, size_t *len)
{
u16 burst_count;
u32 offset = 0;
u8 locality = 0;
u32 expected_count = *len;
int max_cycles = 0;
tpm_result_t rc = TPM_SUCCESS;
/* Wait for the TPM to process the command */
rc = tis_wait_valid_data(locality);
if (rc) {
printf("%s:%d failed processing command with error %#x\n",
__FILE__, __LINE__, rc);
return rc;
}
do {
while ((burst_count = tpm_read_burst_count(locality)) == 0) {
if (max_cycles++ == MAX_DELAY_US) {
printf("%s:%d TPM stuck on read\n",
__FILE__, __LINE__);
return TPM_CB_FAIL;
}
udelay(1);
}
max_cycles = 0;
while (burst_count-- && (offset < expected_count)) {
buffer[offset++] = tpm_read_data(locality);
if (offset == 6) {
/*
* We got the first six bytes of the reply,
* let's figure out how many bytes to expect
* total - it is stored as a 4 byte number in
* network order, starting with offset 2 into
* the body of the reply.
*/
u32 real_length;
memcpy(&real_length,
buffer + 2,
sizeof(real_length));
expected_count = be32_to_cpu(real_length);
if ((expected_count < offset) ||
(expected_count > *len)) {
printf("%s:%d bad response size %u\n",
__FILE__, __LINE__,
expected_count);
return TPM_CB_FAIL;
}
}
}
/* Wait for the next portion */
rc = tis_wait_valid(locality);
if (rc) {
printf("%s:%d failed to read response with error %#x\n",
__FILE__, __LINE__, rc);
return rc;
}
if (offset == expected_count)
break; /* We got all we need */
/*
* Certain TPMs seem to need some delay between tis_wait_valid()
* and tis_has_valid_data(), or some race-condition-related
* issue will occur.
*/
if (CONFIG(TPM_RDRESP_NEED_DELAY))
udelay(10);
} while (tis_has_valid_data(locality));
/* * Make sure we indeed read all there was. */
if (tis_has_valid_data(locality)) {
printf("%s:%d wrong receive status: %#x %u bytes left\n",
__FILE__, __LINE__, tpm_read_status(locality),
tpm_read_burst_count(locality));
return TPM_CB_FAIL;
}
/* Tell the TPM that we are done. */
rc = tis_command_ready(locality);
if (rc)
return rc;
*len = offset;
return TPM_SUCCESS;
}
/*
* pc80_tis_open()
*
* Requests access to locality 0 for the caller.
*
* Returns TPM_SUCCESS on success, TSS Error on failure.
*/
static tpm_result_t pc80_tis_open(void)
{
u8 locality = 0; /* we use locality zero for everything */
tpm_result_t rc = TPM_SUCCESS;
if (!tis_has_access(locality)) {
/* request access to locality */
tis_request_access(locality);
/* did we get a lock? */
rc = tis_wait_received_access(locality);
if (rc) {
printf("%s:%d - failed to lock locality %u with error %#x\n",
__FILE__, __LINE__, locality, rc);
return rc;
}
/* Certain TPMs seem to need some delay here or they hang... */
udelay(10);
}
return tis_command_ready(locality);
}
/*
* tis_sendrecv()
*
* Send the requested data to the TPM and then try to get its response
*
* @sendbuf - buffer of the data to send
* @send_size size of the data to send
* @recvbuf - memory to save the response to
* @recv_len - pointer to the size of the response buffer
*
* Returns TPM_SUCCESS on success (and places the number of response bytes
* at recv_len) or TPM_CB_FAIL on failure.
*/
static tpm_result_t pc80_tpm_sendrecv(const uint8_t *sendbuf, size_t send_size,
uint8_t *recvbuf, size_t *recv_len)
{
tpm_result_t rc = tis_senddata(sendbuf, send_size);
if (rc) {
printf("%s:%d failed sending data to TPM with error %#x\n",
__FILE__, __LINE__, rc);
return rc;
}
return tis_readresponse(recvbuf, recv_len);
}
/*
* tis_probe()
*
* Probe for the TPM device and set it up for use within locality 0. Returns
* pointer to send-receive function on success or NULL on failure.
*/
tis_sendrecv_fn tis_probe(void)
{
if (pc80_tis_probe())
return NULL;
if (pc80_tis_open())
return NULL;
return &pc80_tpm_sendrecv;
}
/*
* tis_setup_interrupt()
*
* Set up the interrupt vector and polarity for locality 0 and
* disable all interrupts so they are unused in firmware but can
* be enabled by the OS.
*
* The values used here must match what is passed in the TPM ACPI
* device if ACPI is used on the platform.
*
* @vector - TPM interrupt vector
* @polarity - TPM interrupt polarity
*
* Returns TPM_SUCCESS on success, TPM_CB_FAIL on failure.
*/
static tpm_result_t tis_setup_interrupt(int vector, int polarity)
{
u8 locality = 0;
tpm_result_t rc = tlcl_lib_init();
if (rc)
return rc;
/* Set TPM interrupt vector */
tpm_write_int_vector(vector, locality);
/* Set TPM interrupt polarity and disable interrupts */
tpm_write_int_polarity(polarity, locality);
return TPM_SUCCESS;
}
static void lpc_tpm_read_resources(struct device *dev)
{
/* Static 5K memory region specified in Kconfig */
mmio_range(dev, 0, CONFIG_TPM_TIS_BASE_ADDRESS, 0x5000);
}
static void lpc_tpm_set_resources(struct device *dev)
{
tpm_config_t *config = (tpm_config_t *)dev->chip_info;
DEVTREE_CONST struct resource *res;
for (res = dev->resource_list; res; res = res->next) {
if (!(res->flags & IORESOURCE_ASSIGNED))
continue;
if (res->flags & IORESOURCE_IRQ) {
/* Set interrupt vector */
tis_setup_interrupt((int)res->base,
config->irq_polarity);
} else {
continue;
}
#if !DEVTREE_EARLY
res->flags |= IORESOURCE_STORED;
report_resource_stored(dev, res, " <tpm>");
#endif
}
}
#if CONFIG(HAVE_ACPI_TABLES)
static void lpc_tpm_fill_ssdt(const struct device *dev)
{
/* Windows 11 requires the following path for TPM to be detected */
const char *path = "\\_SB_.PCI0";
/* Device */
acpigen_write_scope(path);
acpigen_write_device(acpi_device_name(dev));
if (CONFIG(TPM2)) {
acpigen_write_name_string("_HID", "MSFT0101");
acpigen_write_name_string("_CID", "MSFT0101");
} else {
acpigen_write_name("_HID");
acpigen_emit_eisaid("PNP0C31");
acpigen_write_name("_CID");
acpigen_emit_eisaid("PNP0C31");
}
acpi_device_write_uid(dev);
u32 did_vid = tpm_read_did_vid(0);
if (did_vid > 0 && did_vid < 0xffffffff)
acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
else
acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_OFF);
u16 port = dev->path.pnp.port;
/* Resources */
acpigen_write_name("_CRS");
acpigen_write_resourcetemplate_header();
acpigen_write_mem32fixed(1, CONFIG_TPM_TIS_BASE_ADDRESS, 0x5000);
if (port)
acpigen_write_io16(port, port, 1, 2, 1);
if (CONFIG_TPM_PIRQ) {
/*
* PIRQ: Update interrupt vector with configured PIRQ
* Active-Low Level-Triggered Shared
*/
struct acpi_irq tpm_irq_a = ACPI_IRQ_LEVEL_LOW(CONFIG_TPM_PIRQ);
acpi_device_write_interrupt(&tpm_irq_a);
} else if (tpm_read_int_vector(0) > 0) {
u8 int_vec = tpm_read_int_vector(0);
u8 int_pol = tpm_read_int_polarity(0);
struct acpi_irq tpm_irq = ACPI_IRQ_LEVEL_LOW(int_vec);
if (int_pol & 1)
tpm_irq.polarity = ACPI_IRQ_ACTIVE_LOW;
else
tpm_irq.polarity = ACPI_IRQ_ACTIVE_HIGH;
if (int_pol & 2)
tpm_irq.mode = ACPI_IRQ_EDGE_TRIGGERED;
else
tpm_irq.mode = ACPI_IRQ_LEVEL_TRIGGERED;
acpi_device_write_interrupt(&tpm_irq);
}
acpigen_write_resourcetemplate_footer();
if (!CONFIG(CHROMEOS))
tpm_ppi_acpi_fill_ssdt(dev);
acpigen_pop_len(); /* Device */
acpigen_pop_len(); /* Scope */
#if !DEVTREE_EARLY
printk(BIOS_INFO, "%s.%s: %s %s\n", path, acpi_device_name(dev),
dev->chip_ops->name, dev_path(dev));
#endif
}
static const char *lpc_tpm_acpi_name(const struct device *dev)
{
return "TPM";
}
#endif
static struct device_operations lpc_tpm_ops = {
.read_resources = lpc_tpm_read_resources,
.set_resources = lpc_tpm_set_resources,
#if CONFIG(HAVE_ACPI_TABLES)
.acpi_name = lpc_tpm_acpi_name,
.acpi_fill_ssdt = lpc_tpm_fill_ssdt,
#endif
};
static struct device_operations noop_tpm_ops = {
.read_resources = noop_read_resources,
.set_resources = noop_set_resources,
};
static struct pnp_info pnp_dev_info[] = {
{ .flags = PNP_IRQ0 }
};
static void enable_dev(struct device *dev)
{
if (CONFIG(TPM))
pnp_enable_devices(dev, &lpc_tpm_ops,
ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
else
pnp_enable_devices(dev, &noop_tpm_ops, ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
}
struct chip_operations drivers_pc80_tpm_ops = {
.name = "LPC TPM",
.enable_dev = enable_dev
};