aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-07-29 17:38:46 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2016-07-29 17:38:46 -0700
commit7a1e8b80fb1e8ead4cec15d1fc494ed290e4d2e9 (patch)
tree55a36d4256f1ae793b5c8e88c0f158737447193f /drivers
parentMerge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace (diff)
parentapparmor: fix SECURITY_APPARMOR_HASH_DEFAULT parameter handling (diff)
downloadlinux-dev-7a1e8b80fb1e8ead4cec15d1fc494ed290e4d2e9.tar.xz
linux-dev-7a1e8b80fb1e8ead4cec15d1fc494ed290e4d2e9.zip
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "Highlights: - TPM core and driver updates/fixes - IPv6 security labeling (CALIPSO) - Lots of Apparmor fixes - Seccomp: remove 2-phase API, close hole where ptrace can change syscall #" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (156 commits) apparmor: fix SECURITY_APPARMOR_HASH_DEFAULT parameter handling tpm: Add TPM 2.0 support to the Nuvoton i2c driver (NPCT6xx family) tpm: Factor out common startup code tpm: use devm_add_action_or_reset tpm2_i2c_nuvoton: add irq validity check tpm: read burstcount from TPM_STS in one 32-bit transaction tpm: fix byte-order for the value read by tpm2_get_tpm_pt tpm_tis_core: convert max timeouts from msec to jiffies apparmor: fix arg_size computation for when setprocattr is null terminated apparmor: fix oops, validate buffer size in apparmor_setprocattr() apparmor: do not expose kernel stack apparmor: fix module parameters can be changed after policy is locked apparmor: fix oops in profile_unpack() when policy_db is not present apparmor: don't check for vmalloc_addr if kvzalloc() failed apparmor: add missing id bounds check on dfa verification apparmor: allow SYS_CAP_RESOURCE to be sufficient to prlimit another task apparmor: use list_next_entry instead of list_entry_next apparmor: fix refcount race when finding a child profile apparmor: fix ref count leak when profile sha1 hash is read apparmor: check that xindex is in trans_table bounds ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/tpm/Kconfig30
-rw-r--r--drivers/char/tpm/Makefile3
-rw-r--r--drivers/char/tpm/st33zp24/Kconfig11
-rw-r--r--drivers/char/tpm/st33zp24/i2c.c70
-rw-r--r--drivers/char/tpm/st33zp24/spi.c184
-rw-r--r--drivers/char/tpm/st33zp24/st33zp24.c129
-rw-r--r--drivers/char/tpm/st33zp24/st33zp24.h14
-rw-r--r--drivers/char/tpm/tpm-chip.c299
-rw-r--r--drivers/char/tpm/tpm-dev.c15
-rw-r--r--drivers/char/tpm/tpm-interface.c132
-rw-r--r--drivers/char/tpm/tpm-sysfs.c78
-rw-r--r--drivers/char/tpm/tpm.h82
-rw-r--r--drivers/char/tpm/tpm2-cmd.c59
-rw-r--r--drivers/char/tpm/tpm_atmel.c63
-rw-r--r--drivers/char/tpm/tpm_atmel.h16
-rw-r--r--drivers/char/tpm/tpm_crb.c85
-rw-r--r--drivers/char/tpm/tpm_eventlog.c2
-rw-r--r--drivers/char/tpm/tpm_eventlog.h4
-rw-r--r--drivers/char/tpm/tpm_i2c_atmel.c45
-rw-r--r--drivers/char/tpm/tpm_i2c_infineon.c59
-rw-r--r--drivers/char/tpm/tpm_i2c_nuvoton.c131
-rw-r--r--drivers/char/tpm/tpm_ibmvtpm.c38
-rw-r--r--drivers/char/tpm/tpm_infineon.c22
-rw-r--r--drivers/char/tpm/tpm_nsc.c84
-rw-r--r--drivers/char/tpm/tpm_tis.c829
-rw-r--r--drivers/char/tpm/tpm_tis_core.c835
-rw-r--r--drivers/char/tpm/tpm_tis_core.h156
-rw-r--r--drivers/char/tpm/tpm_tis_spi.c272
-rw-r--r--drivers/char/tpm/tpm_vtpm_proxy.c637
-rw-r--r--drivers/char/tpm/xen-tpmfront.c36
30 files changed, 2968 insertions, 1452 deletions
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index 3b84a8b1bfbe..9faa0b1e7766 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -24,9 +24,16 @@ menuconfig TCG_TPM
if TCG_TPM
+config TCG_TIS_CORE
+ tristate
+ ---help---
+ TCG TIS TPM core driver. It implements the TPM TCG TIS logic and hooks
+ into the TPM kernel APIs. Physical layers will register against it.
+
config TCG_TIS
tristate "TPM Interface Specification 1.2 Interface / TPM 2.0 FIFO Interface"
depends on X86
+ select TCG_TIS_CORE
---help---
If you have a TPM security chip that is compliant with the
TCG TIS 1.2 TPM specification (TPM1.2) or the TCG PTP FIFO
@@ -34,6 +41,18 @@ config TCG_TIS
within Linux. To compile this driver as a module, choose M here;
the module will be called tpm_tis.
+config TCG_TIS_SPI
+ tristate "TPM Interface Specification 1.3 Interface / TPM 2.0 FIFO Interface - (SPI)"
+ depends on SPI
+ select TCG_TIS_CORE
+ ---help---
+ If you have a TPM security chip which is connected to a regular,
+ non-tcg SPI master (i.e. most embedded platforms) that is compliant with the
+ TCG TIS 1.3 TPM specification (TPM1.2) or the TCG PTP FIFO
+ specification (TPM2.0) say Yes and it will be accessible from
+ within Linux. To compile this driver as a module, choose M here;
+ the module will be called tpm_tis_spi.
+
config TCG_TIS_I2C_ATMEL
tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)"
depends on I2C
@@ -122,5 +141,16 @@ config TCG_CRB
from within Linux. To compile this driver as a module, choose
M here; the module will be called tpm_crb.
+config TCG_VTPM_PROXY
+ tristate "VTPM Proxy Interface"
+ depends on TCG_TPM
+ select ANON_INODES
+ ---help---
+ This driver proxies for an emulated TPM (vTPM) running in userspace.
+ A device /dev/vtpmx is provided that creates a device pair
+ /dev/vtpmX and a server-side file descriptor on which the vTPM
+ can receive commands.
+
+
source "drivers/char/tpm/st33zp24/Kconfig"
endif # TCG_TPM
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 56e8f1f3dc7e..a385fb8c17de 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -12,7 +12,9 @@ ifdef CONFIG_TCG_IBMVTPM
tpm-y += tpm_eventlog.o tpm_of.o
endif
endif
+obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o
obj-$(CONFIG_TCG_TIS) += tpm_tis.o
+obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi.o
obj-$(CONFIG_TCG_TIS_I2C_ATMEL) += tpm_i2c_atmel.o
obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o
@@ -23,3 +25,4 @@ obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/
obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o
obj-$(CONFIG_TCG_CRB) += tpm_crb.o
+obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
diff --git a/drivers/char/tpm/st33zp24/Kconfig b/drivers/char/tpm/st33zp24/Kconfig
index 19c007461d1c..e74c6f29fc85 100644
--- a/drivers/char/tpm/st33zp24/Kconfig
+++ b/drivers/char/tpm/st33zp24/Kconfig
@@ -1,6 +1,5 @@
config TCG_TIS_ST33ZP24
- tristate "STMicroelectronics TPM Interface Specification 1.2 Interface"
- depends on GPIOLIB || COMPILE_TEST
+ tristate
---help---
STMicroelectronics ST33ZP24 core driver. It implements the core
TPM1.2 logic and hooks into the TPM kernel APIs. Physical layers will
@@ -10,9 +9,9 @@ config TCG_TIS_ST33ZP24
tpm_st33zp24.
config TCG_TIS_ST33ZP24_I2C
- tristate "TPM 1.2 ST33ZP24 I2C support"
- depends on TCG_TIS_ST33ZP24
+ tristate "STMicroelectronics TPM Interface Specification 1.2 Interface (I2C)"
depends on I2C
+ select TCG_TIS_ST33ZP24
---help---
This module adds support for the STMicroelectronics TPM security chip
ST33ZP24 with i2c interface.
@@ -20,9 +19,9 @@ config TCG_TIS_ST33ZP24_I2C
called tpm_st33zp24_i2c.
config TCG_TIS_ST33ZP24_SPI
- tristate "TPM 1.2 ST33ZP24 SPI support"
- depends on TCG_TIS_ST33ZP24
+ tristate "STMicroelectronics TPM Interface Specification 1.2 Interface (SPI)"
depends on SPI
+ select TCG_TIS_ST33ZP24
---help---
This module adds support for the STMicroelectronics TPM security chip
ST33ZP24 with spi interface.
diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c
index 309d2767c6a1..028a9cd76b63 100644
--- a/drivers/char/tpm/st33zp24/i2c.c
+++ b/drivers/char/tpm/st33zp24/i2c.c
@@ -1,6 +1,6 @@
/*
* STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24
- * Copyright (C) 2009 - 2015 STMicroelectronics
+ * Copyright (C) 2009 - 2016 STMicroelectronics
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,11 +19,14 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
+#include <linux/acpi.h>
#include <linux/tpm.h>
#include <linux/platform_data/st33zp24.h>
+#include "../tpm.h"
#include "st33zp24.h"
#define TPM_DUMMY_BYTE 0xAA
@@ -108,11 +111,40 @@ static const struct st33zp24_phy_ops i2c_phy_ops = {
.recv = st33zp24_i2c_recv,
};
-#ifdef CONFIG_OF
-static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy)
+static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client)
{
+ struct tpm_chip *chip = i2c_get_clientdata(client);
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
+ struct st33zp24_i2c_phy *phy = tpm_dev->phy_id;
+ struct gpio_desc *gpiod_lpcpd;
+ struct device *dev = &client->dev;
+
+ /* Get LPCPD GPIO from ACPI */
+ gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod_lpcpd)) {
+ dev_err(&client->dev,
+ "Failed to retrieve lpcpd-gpios from acpi.\n");
+ phy->io_lpcpd = -1;
+ /*
+ * lpcpd pin is not specified. This is not an issue as
+ * power management can be also managed by TPM specific
+ * commands. So leave with a success status code.
+ */
+ return 0;
+ }
+
+ phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd);
+
+ return 0;
+}
+
+static int st33zp24_i2c_of_request_resources(struct i2c_client *client)
+{
+ struct tpm_chip *chip = i2c_get_clientdata(client);
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
+ struct st33zp24_i2c_phy *phy = tpm_dev->phy_id;
struct device_node *pp;
- struct i2c_client *client = phy->client;
int gpio;
int ret;
@@ -146,16 +178,12 @@ static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy)
return 0;
}
-#else
-static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy)
-{
- return -ENODEV;
-}
-#endif
-static int st33zp24_i2c_request_resources(struct i2c_client *client,
- struct st33zp24_i2c_phy *phy)
+static int st33zp24_i2c_request_resources(struct i2c_client *client)
{
+ struct tpm_chip *chip = i2c_get_clientdata(client);
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
+ struct st33zp24_i2c_phy *phy = tpm_dev->phy_id;
struct st33zp24_platform_data *pdata;
int ret;
@@ -212,13 +240,18 @@ static int st33zp24_i2c_probe(struct i2c_client *client,
return -ENOMEM;
phy->client = client;
+
pdata = client->dev.platform_data;
if (!pdata && client->dev.of_node) {
- ret = st33zp24_i2c_of_request_resources(phy);
+ ret = st33zp24_i2c_of_request_resources(client);
if (ret)
return ret;
} else if (pdata) {
- ret = st33zp24_i2c_request_resources(client, phy);
+ ret = st33zp24_i2c_request_resources(client);
+ if (ret)
+ return ret;
+ } else if (ACPI_HANDLE(&client->dev)) {
+ ret = st33zp24_i2c_acpi_request_resources(client);
if (ret)
return ret;
}
@@ -245,13 +278,17 @@ static const struct i2c_device_id st33zp24_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, st33zp24_i2c_id);
-#ifdef CONFIG_OF
static const struct of_device_id of_st33zp24_i2c_match[] = {
{ .compatible = "st,st33zp24-i2c", },
{}
};
MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match);
-#endif
+
+static const struct acpi_device_id st33zp24_i2c_acpi_match[] = {
+ {"SMO3324"},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, st33zp24_i2c_acpi_match);
static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend,
st33zp24_pm_resume);
@@ -261,6 +298,7 @@ static struct i2c_driver st33zp24_i2c_driver = {
.name = TPM_ST33_I2C,
.pm = &st33zp24_i2c_ops,
.of_match_table = of_match_ptr(of_st33zp24_i2c_match),
+ .acpi_match_table = ACPI_PTR(st33zp24_i2c_acpi_match),
},
.probe = st33zp24_i2c_probe,
.remove = st33zp24_i2c_remove,
diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c
index f974c945c97a..9f5a0117098c 100644
--- a/drivers/char/tpm/st33zp24/spi.c
+++ b/drivers/char/tpm/st33zp24/spi.c
@@ -1,6 +1,6 @@
/*
* STMicroelectronics TPM SPI Linux driver for TPM ST33ZP24
- * Copyright (C) 2009 - 2015 STMicroelectronics
+ * Copyright (C) 2009 - 2016 STMicroelectronics
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,11 +19,14 @@
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
+#include <linux/acpi.h>
#include <linux/tpm.h>
#include <linux/platform_data/st33zp24.h>
+#include "../tpm.h"
#include "st33zp24.h"
#define TPM_DATA_FIFO 0x24
@@ -66,7 +69,7 @@
struct st33zp24_spi_phy {
struct spi_device *spi_device;
- struct spi_transfer spi_xfer;
+
u8 tx_buf[ST33ZP24_SPI_BUFFER_SIZE];
u8 rx_buf[ST33ZP24_SPI_BUFFER_SIZE];
@@ -110,43 +113,39 @@ static int st33zp24_status_to_errno(u8 code)
static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data,
int tpm_size)
{
- u8 data = 0;
- int total_length = 0, nbr_dummy_bytes = 0, ret = 0;
+ int total_length = 0, ret = 0;
struct st33zp24_spi_phy *phy = phy_id;
struct spi_device *dev = phy->spi_device;
- u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf;
- u8 *rx_buf = phy->spi_xfer.rx_buf;
+ struct spi_transfer spi_xfer = {
+ .tx_buf = phy->tx_buf,
+ .rx_buf = phy->rx_buf,
+ };
/* Pre-Header */
- data = TPM_WRITE_DIRECTION | LOCALITY0;
- memcpy(tx_buf + total_length, &data, sizeof(data));
- total_length++;
- data = tpm_register;
- memcpy(tx_buf + total_length, &data, sizeof(data));
- total_length++;
+ phy->tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0;
+ phy->tx_buf[total_length++] = tpm_register;
if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) {
- tx_buf[total_length++] = tpm_size >> 8;
- tx_buf[total_length++] = tpm_size;
+ phy->tx_buf[total_length++] = tpm_size >> 8;
+ phy->tx_buf[total_length++] = tpm_size;
}
- memcpy(&tx_buf[total_length], tpm_data, tpm_size);
+ memcpy(&phy->tx_buf[total_length], tpm_data, tpm_size);
total_length += tpm_size;
- nbr_dummy_bytes = phy->latency;
- memset(&tx_buf[total_length], TPM_DUMMY_BYTE, nbr_dummy_bytes);
+ memset(&phy->tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency);
- phy->spi_xfer.len = total_length + nbr_dummy_bytes;
+ spi_xfer.len = total_length + phy->latency;
- ret = spi_sync_transfer(dev, &phy->spi_xfer, 1);
+ ret = spi_sync_transfer(dev, &spi_xfer, 1);
if (ret == 0)
- ret = rx_buf[total_length + nbr_dummy_bytes - 1];
+ ret = phy->rx_buf[total_length + phy->latency - 1];
return st33zp24_status_to_errno(ret);
} /* st33zp24_spi_send() */
/*
- * read8_recv
+ * st33zp24_spi_read8_recv
* Recv byte from the TIS register according to the ST33ZP24 SPI protocol.
* @param: phy_id, the phy description
* @param: tpm_register, the tpm tis register where the data should be read
@@ -154,40 +153,37 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data,
* @param: tpm_size, tpm TPM response size to read.
* @return: should be zero if success else a negative error code.
*/
-static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size)
+static int st33zp24_spi_read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data,
+ int tpm_size)
{
- u8 data = 0;
- int total_length = 0, nbr_dummy_bytes, ret;
+ int total_length = 0, ret;
struct st33zp24_spi_phy *phy = phy_id;
struct spi_device *dev = phy->spi_device;
- u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf;
- u8 *rx_buf = phy->spi_xfer.rx_buf;
+ struct spi_transfer spi_xfer = {
+ .tx_buf = phy->tx_buf,
+ .rx_buf = phy->rx_buf,
+ };
/* Pre-Header */
- data = LOCALITY0;
- memcpy(tx_buf + total_length, &data, sizeof(data));
- total_length++;
- data = tpm_register;
- memcpy(tx_buf + total_length, &data, sizeof(data));
- total_length++;
+ phy->tx_buf[total_length++] = LOCALITY0;
+ phy->tx_buf[total_length++] = tpm_register;
- nbr_dummy_bytes = phy->latency;
- memset(&tx_buf[total_length], TPM_DUMMY_BYTE,
- nbr_dummy_bytes + tpm_size);
+ memset(&phy->tx_buf[total_length], TPM_DUMMY_BYTE,
+ phy->latency + tpm_size);
- phy->spi_xfer.len = total_length + nbr_dummy_bytes + tpm_size;
+ spi_xfer.len = total_length + phy->latency + tpm_size;
/* header + status byte + size of the data + status byte */
- ret = spi_sync_transfer(dev, &phy->spi_xfer, 1);
+ ret = spi_sync_transfer(dev, &spi_xfer, 1);
if (tpm_size > 0 && ret == 0) {
- ret = rx_buf[total_length + nbr_dummy_bytes - 1];
+ ret = phy->rx_buf[total_length + phy->latency - 1];
- memcpy(tpm_data, rx_buf + total_length + nbr_dummy_bytes,
+ memcpy(tpm_data, phy->rx_buf + total_length + phy->latency,
tpm_size);
}
return ret;
-} /* read8_reg() */
+} /* st33zp24_spi_read8_reg() */
/*
* st33zp24_spi_recv
@@ -203,13 +199,13 @@ static int st33zp24_spi_recv(void *phy_id, u8 tpm_register, u8 *tpm_data,
{
int ret;
- ret = read8_reg(phy_id, tpm_register, tpm_data, tpm_size);
+ ret = st33zp24_spi_read8_reg(phy_id, tpm_register, tpm_data, tpm_size);
if (!st33zp24_status_to_errno(ret))
return tpm_size;
return ret;
} /* st33zp24_spi_recv() */
-static int evaluate_latency(void *phy_id)
+static int st33zp24_spi_evaluate_latency(void *phy_id)
{
struct st33zp24_spi_phy *phy = phy_id;
int latency = 1, status = 0;
@@ -217,9 +213,15 @@ static int evaluate_latency(void *phy_id)
while (!status && latency < MAX_SPI_LATENCY) {
phy->latency = latency;
- status = read8_reg(phy_id, TPM_INTF_CAPABILITY, &data, 1);
+ status = st33zp24_spi_read8_reg(phy_id, TPM_INTF_CAPABILITY,
+ &data, 1);
latency++;
}
+ if (status < 0)
+ return status;
+ if (latency == MAX_SPI_LATENCY)
+ return -ENODEV;
+
return latency - 1;
} /* evaluate_latency() */
@@ -228,24 +230,52 @@ static const struct st33zp24_phy_ops spi_phy_ops = {
.recv = st33zp24_spi_recv,
};
-#ifdef CONFIG_OF
-static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy)
+static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev)
{
+ struct tpm_chip *chip = spi_get_drvdata(spi_dev);
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
+ struct st33zp24_spi_phy *phy = tpm_dev->phy_id;
+ struct gpio_desc *gpiod_lpcpd;
+ struct device *dev = &spi_dev->dev;
+
+ /* Get LPCPD GPIO from ACPI */
+ gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod_lpcpd)) {
+ dev_err(dev, "Failed to retrieve lpcpd-gpios from acpi.\n");
+ phy->io_lpcpd = -1;
+ /*
+ * lpcpd pin is not specified. This is not an issue as
+ * power management can be also managed by TPM specific
+ * commands. So leave with a success status code.
+ */
+ return 0;
+ }
+
+ phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd);
+
+ return 0;
+}
+
+static int st33zp24_spi_of_request_resources(struct spi_device *spi_dev)
+{
+ struct tpm_chip *chip = spi_get_drvdata(spi_dev);
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
+ struct st33zp24_spi_phy *phy = tpm_dev->phy_id;
struct device_node *pp;
- struct spi_device *dev = phy->spi_device;
int gpio;
int ret;
- pp = dev->dev.of_node;
+ pp = spi_dev->dev.of_node;
if (!pp) {
- dev_err(&dev->dev, "No platform data\n");
+ dev_err(&spi_dev->dev, "No platform data\n");
return -ENODEV;
}
/* Get GPIO from device tree */
gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0);
if (gpio < 0) {
- dev_err(&dev->dev,
+ dev_err(&spi_dev->dev,
"Failed to retrieve lpcpd-gpios from dts.\n");
phy->io_lpcpd = -1;
/*
@@ -256,26 +286,22 @@ static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy)
return 0;
}
/* GPIO request and configuration */
- ret = devm_gpio_request_one(&dev->dev, gpio,
+ ret = devm_gpio_request_one(&spi_dev->dev, gpio,
GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD");
if (ret) {
- dev_err(&dev->dev, "Failed to request lpcpd pin\n");
+ dev_err(&spi_dev->dev, "Failed to request lpcpd pin\n");
return -ENODEV;
}
phy->io_lpcpd = gpio;
return 0;
}
-#else
-static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy)
-{
- return -ENODEV;
-}
-#endif
-static int tpm_stm_spi_request_resources(struct spi_device *dev,
- struct st33zp24_spi_phy *phy)
+static int st33zp24_spi_request_resources(struct spi_device *dev)
{
+ struct tpm_chip *chip = spi_get_drvdata(dev);
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
+ struct st33zp24_spi_phy *phy = tpm_dev->phy_id;
struct st33zp24_platform_data *pdata;
int ret;
@@ -303,13 +329,12 @@ static int tpm_stm_spi_request_resources(struct spi_device *dev,
}
/*
- * tpm_st33_spi_probe initialize the TPM device
+ * st33zp24_spi_probe initialize the TPM device
* @param: dev, the spi_device drescription (TPM SPI description).
* @return: 0 in case of success.
* or a negative value describing the error.
*/
-static int
-tpm_st33_spi_probe(struct spi_device *dev)
+static int st33zp24_spi_probe(struct spi_device *dev)
{
int ret;
struct st33zp24_platform_data *pdata;
@@ -328,21 +353,23 @@ tpm_st33_spi_probe(struct spi_device *dev)
return -ENOMEM;
phy->spi_device = dev;
+
pdata = dev->dev.platform_data;
if (!pdata && dev->dev.of_node) {
- ret = tpm_stm_spi_of_request_resources(phy);
+ ret = st33zp24_spi_of_request_resources(dev);
if (ret)
return ret;
} else if (pdata) {
- ret = tpm_stm_spi_request_resources(dev, phy);
+ ret = st33zp24_spi_request_resources(dev);
+ if (ret)
+ return ret;
+ } else if (ACPI_HANDLE(&dev->dev)) {
+ ret = st33zp24_spi_acpi_request_resources(dev);
if (ret)
return ret;
}
- phy->spi_xfer.tx_buf = phy->tx_buf;
- phy->spi_xfer.rx_buf = phy->rx_buf;
-
- phy->latency = evaluate_latency(phy);
+ phy->latency = st33zp24_spi_evaluate_latency(phy);
if (phy->latency <= 0)
return -ENODEV;
@@ -351,11 +378,11 @@ tpm_st33_spi_probe(struct spi_device *dev)
}
/*
- * tpm_st33_spi_remove remove the TPM device
+ * st33zp24_spi_remove remove the TPM device
* @param: client, the spi_device drescription (TPM SPI description).
* @return: 0 in case of success.
*/
-static int tpm_st33_spi_remove(struct spi_device *dev)
+static int st33zp24_spi_remove(struct spi_device *dev)
{
struct tpm_chip *chip = spi_get_drvdata(dev);
@@ -368,29 +395,34 @@ static const struct spi_device_id st33zp24_spi_id[] = {
};
MODULE_DEVICE_TABLE(spi, st33zp24_spi_id);
-#ifdef CONFIG_OF
static const struct of_device_id of_st33zp24_spi_match[] = {
{ .compatible = "st,st33zp24-spi", },
{}
};
MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match);
-#endif
+
+static const struct acpi_device_id st33zp24_spi_acpi_match[] = {
+ {"SMO3324"},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, st33zp24_spi_acpi_match);
static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend,
st33zp24_pm_resume);
-static struct spi_driver tpm_st33_spi_driver = {
+static struct spi_driver st33zp24_spi_driver = {
.driver = {
.name = TPM_ST33_SPI,
.pm = &st33zp24_spi_ops,
.of_match_table = of_match_ptr(of_st33zp24_spi_match),
+ .acpi_match_table = ACPI_PTR(st33zp24_spi_acpi_match),
},
- .probe = tpm_st33_spi_probe,
- .remove = tpm_st33_spi_remove,
+ .probe = st33zp24_spi_probe,
+ .remove = st33zp24_spi_remove,
.id_table = st33zp24_spi_id,
};
-module_spi_driver(tpm_st33_spi_driver);
+module_spi_driver(st33zp24_spi_driver);
MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)");
MODULE_DESCRIPTION("STM TPM 1.2 SPI ST33 Driver");
diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c
index 8d626784cd8d..c2ee30451e41 100644
--- a/drivers/char/tpm/st33zp24/st33zp24.c
+++ b/drivers/char/tpm/st33zp24/st33zp24.c
@@ -1,6 +1,6 @@
/*
* STMicroelectronics TPM Linux driver for TPM ST33ZP24
- * Copyright (C) 2009 - 2015 STMicroelectronics
+ * Copyright (C) 2009 - 2016 STMicroelectronics
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -73,14 +73,6 @@ enum tis_defaults {
TIS_LONG_TIMEOUT = 2000,
};
-struct st33zp24_dev {
- struct tpm_chip *chip;
- void *phy_id;
- const struct st33zp24_phy_ops *ops;
- u32 intrs;
- int io_lpcpd;
-};
-
/*
* clear_interruption clear the pending interrupt.
* @param: tpm_dev, the tpm device device.
@@ -102,11 +94,9 @@ static u8 clear_interruption(struct st33zp24_dev *tpm_dev)
*/
static void st33zp24_cancel(struct tpm_chip *chip)
{
- struct st33zp24_dev *tpm_dev;
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u8 data;
- tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
-
data = TPM_STS_COMMAND_READY;
tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1);
} /* st33zp24_cancel() */
@@ -118,11 +108,9 @@ static void st33zp24_cancel(struct tpm_chip *chip)
*/
static u8 st33zp24_status(struct tpm_chip *chip)
{
- struct st33zp24_dev *tpm_dev;
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u8 data;
- tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
-
tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS, &data, 1);
return data;
} /* st33zp24_status() */
@@ -134,17 +122,15 @@ static u8 st33zp24_status(struct tpm_chip *chip)
*/
static int check_locality(struct tpm_chip *chip)
{
- struct st33zp24_dev *tpm_dev;
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u8 data;
u8 status;
- tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
-
status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
if (status && (data &
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
- return chip->vendor.locality;
+ return tpm_dev->locality;
return -EACCES;
} /* check_locality() */
@@ -156,27 +142,25 @@ static int check_locality(struct tpm_chip *chip)
*/
static int request_locality(struct tpm_chip *chip)
{
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
unsigned long stop;
long ret;
- struct st33zp24_dev *tpm_dev;
u8 data;
- if (check_locality(chip) == chip->vendor.locality)
- return chip->vendor.locality;
-
- tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
+ if (check_locality(chip) == tpm_dev->locality)
+ return tpm_dev->locality;
data = TPM_ACCESS_REQUEST_USE;
ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
if (ret < 0)
return ret;
- stop = jiffies + chip->vendor.timeout_a;
+ stop = jiffies + chip->timeout_a;
/* Request locality is usually effective after the request */
do {
if (check_locality(chip) >= 0)
- return chip->vendor.locality;
+ return tpm_dev->locality;
msleep(TPM_TIMEOUT);
} while (time_before(jiffies, stop));
@@ -190,10 +174,9 @@ static int request_locality(struct tpm_chip *chip)
*/
static void release_locality(struct tpm_chip *chip)
{
- struct st33zp24_dev *tpm_dev;
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u8 data;
- tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
data = TPM_ACCESS_ACTIVE_LOCALITY;
tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1);
@@ -206,23 +189,21 @@ static void release_locality(struct tpm_chip *chip)
*/
static int get_burstcount(struct tpm_chip *chip)
{
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
unsigned long stop;
int burstcnt, status;
- u8 tpm_reg, temp;
- struct st33zp24_dev *tpm_dev;
-
- tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
+ u8 temp;
- stop = jiffies + chip->vendor.timeout_d;
+ stop = jiffies + chip->timeout_d;
do {
- tpm_reg = TPM_STS + 1;
- status = tpm_dev->ops->recv(tpm_dev->phy_id, tpm_reg, &temp, 1);
+ status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS + 1,
+ &temp, 1);
if (status < 0)
return -EBUSY;
- tpm_reg = TPM_STS + 2;
burstcnt = temp;
- status = tpm_dev->ops->recv(tpm_dev->phy_id, tpm_reg, &temp, 1);
+ status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS + 2,
+ &temp, 1);
if (status < 0)
return -EBUSY;
@@ -271,15 +252,13 @@ static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
wait_queue_head_t *queue, bool check_cancel)
{
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
unsigned long stop;
int ret = 0;
bool canceled = false;
bool condition;
u32 cur_intrs;
u8 status;
- struct st33zp24_dev *tpm_dev;
-
- tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
/* check current status */
status = st33zp24_status(chip);
@@ -288,10 +267,10 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
stop = jiffies + timeout;
- if (chip->vendor.irq) {
+ if (chip->flags & TPM_CHIP_FLAG_IRQ) {
cur_intrs = tpm_dev->intrs;
clear_interruption(tpm_dev);
- enable_irq(chip->vendor.irq);
+ enable_irq(tpm_dev->irq);
do {
if (ret == -ERESTARTSYS && freezing(current))
@@ -314,7 +293,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
}
} while (ret == -ERESTARTSYS && freezing(current));
- disable_irq_nosync(chip->vendor.irq);
+ disable_irq_nosync(tpm_dev->irq);
} else {
do {
@@ -337,16 +316,14 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
*/
static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
{
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
int size = 0, burstcnt, len, ret;
- struct st33zp24_dev *tpm_dev;
-
- tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
while (size < count &&
wait_for_stat(chip,
TPM_STS_DATA_AVAIL | TPM_STS_VALID,
- chip->vendor.timeout_c,
- &chip->vendor.read_queue, true) == 0) {
+ chip->timeout_c,
+ &tpm_dev->read_queue, true) == 0) {
burstcnt = get_burstcount(chip);
if (burstcnt < 0)
return burstcnt;
@@ -370,13 +347,11 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
{
struct tpm_chip *chip = dev_id;
- struct st33zp24_dev *tpm_dev;
-
- tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
tpm_dev->intrs++;
- wake_up_interruptible(&chip->vendor.read_queue);
- disable_irq_nosync(chip->vendor.irq);
+ wake_up_interruptible(&tpm_dev->read_queue);
+ disable_irq_nosync(tpm_dev->irq);
return IRQ_HANDLED;
} /* tpm_ioserirq_handler() */
@@ -393,19 +368,17 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf,
size_t len)
{
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
u32 status, i, size, ordinal;
int burstcnt = 0;
int ret;
u8 data;
- struct st33zp24_dev *tpm_dev;
if (!chip)
return -EBUSY;
if (len < TPM_HEADER_SIZE)
return -EBUSY;
- tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
-
ret = request_locality(chip);
if (ret < 0)
return ret;
@@ -414,8 +387,8 @@ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf,
if ((status & TPM_STS_COMMAND_READY) == 0) {
st33zp24_cancel(chip);
if (wait_for_stat
- (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
- &chip->vendor.read_queue, false) < 0) {
+ (chip, TPM_STS_COMMAND_READY, chip->timeout_b,
+ &tpm_dev->read_queue, false) < 0) {
ret = -ETIME;
goto out_err;
}
@@ -456,12 +429,12 @@ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf,
if (ret < 0)
goto out_err;
- if (chip->vendor.irq) {
+ if (chip->flags & TPM_CHIP_FLAG_IRQ) {
ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
ret = wait_for_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
tpm_calc_ordinal_duration(chip, ordinal),
- &chip->vendor.read_queue, false);
+ &tpm_dev->read_queue, false);
if (ret < 0)
goto out_err;
}
@@ -532,6 +505,7 @@ static bool st33zp24_req_canceled(struct tpm_chip *chip, u8 status)
}
static const struct tpm_class_ops st33zp24_tpm = {
+ .flags = TPM_OPS_AUTO_STARTUP,
.send = st33zp24_send,
.recv = st33zp24_recv,
.cancel = st33zp24_cancel,
@@ -565,20 +539,20 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
if (!tpm_dev)
return -ENOMEM;
- TPM_VPRIV(chip) = tpm_dev;
tpm_dev->phy_id = phy_id;
tpm_dev->ops = ops;
+ dev_set_drvdata(&chip->dev, tpm_dev);
- chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
- chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
- chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
- chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ chip->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+ chip->timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
- chip->vendor.locality = LOCALITY0;
+ tpm_dev->locality = LOCALITY0;
if (irq) {
/* INTERRUPT Setup */
- init_waitqueue_head(&chip->vendor.read_queue);
+ init_waitqueue_head(&tpm_dev->read_queue);
tpm_dev->intrs = 0;
if (request_locality(chip) != LOCALITY0) {
@@ -611,16 +585,14 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops,
if (ret < 0)
goto _tpm_clean_answer;
- chip->vendor.irq = irq;
+ tpm_dev->irq = irq;
+ chip->flags |= TPM_CHIP_FLAG_IRQ;
- disable_irq_nosync(chip->vendor.irq);
+ disable_irq_nosync(tpm_dev->irq);
tpm_gen_interrupt(chip);
}
- tpm_get_timeouts(chip);
- tpm_do_selftest(chip);
-
return tpm_chip_register(chip);
_tpm_clean_answer:
dev_info(&chip->dev, "TPM initialization fail\n");
@@ -650,10 +622,9 @@ EXPORT_SYMBOL(st33zp24_remove);
int st33zp24_pm_suspend(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
- struct st33zp24_dev *tpm_dev;
- int ret = 0;
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
- tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
+ int ret = 0;
if (gpio_is_valid(tpm_dev->io_lpcpd))
gpio_set_value(tpm_dev->io_lpcpd, 0);
@@ -672,16 +643,14 @@ EXPORT_SYMBOL(st33zp24_pm_suspend);
int st33zp24_pm_resume(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
- struct st33zp24_dev *tpm_dev;
+ struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev);
int ret = 0;
- tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip);
-
if (gpio_is_valid(tpm_dev->io_lpcpd)) {
gpio_set_value(tpm_dev->io_lpcpd, 1);
ret = wait_for_stat(chip,
- TPM_STS_VALID, chip->vendor.timeout_b,
- &chip->vendor.read_queue, false);
+ TPM_STS_VALID, chip->timeout_b,
+ &tpm_dev->read_queue, false);
} else {
ret = tpm_pm_resume(dev);
if (!ret)
diff --git a/drivers/char/tpm/st33zp24/st33zp24.h b/drivers/char/tpm/st33zp24/st33zp24.h
index c207cebf67dd..6f4a4198af6a 100644
--- a/drivers/char/tpm/st33zp24/st33zp24.h
+++ b/drivers/char/tpm/st33zp24/st33zp24.h
@@ -1,6 +1,6 @@
/*
* STMicroelectronics TPM Linux driver for TPM ST33ZP24
- * Copyright (C) 2009 - 2015 STMicroelectronics
+ * Copyright (C) 2009 - 2016 STMicroelectronics
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -21,6 +21,18 @@
#define TPM_WRITE_DIRECTION 0x80
#define TPM_BUFSIZE 2048
+struct st33zp24_dev {
+ struct tpm_chip *chip;
+ void *phy_id;
+ const struct st33zp24_phy_ops *ops;
+ int locality;
+ int irq;
+ u32 intrs;
+ int io_lpcpd;
+ wait_queue_head_t read_queue;
+};
+
+
struct st33zp24_phy_ops {
int (*send)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size);
int (*recv)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size);
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 274dd0123237..e5950131bd90 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -29,33 +29,88 @@
#include "tpm.h"
#include "tpm_eventlog.h"
-static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
-static LIST_HEAD(tpm_chip_list);
-static DEFINE_SPINLOCK(driver_lock);
+DEFINE_IDR(dev_nums_idr);
+static DEFINE_MUTEX(idr_lock);
struct class *tpm_class;
dev_t tpm_devt;
-/*
- * tpm_chip_find_get - return tpm_chip for a given chip number
- * @chip_num the device number for the chip
+/**
+ * tpm_try_get_ops() - Get a ref to the tpm_chip
+ * @chip: Chip to ref
+ *
+ * The caller must already have some kind of locking to ensure that chip is
+ * valid. This function will lock the chip so that the ops member can be
+ * accessed safely. The locking prevents tpm_chip_unregister from
+ * completing, so it should not be held for long periods.
+ *
+ * Returns -ERRNO if the chip could not be got.
*/
-struct tpm_chip *tpm_chip_find_get(int chip_num)
+int tpm_try_get_ops(struct tpm_chip *chip)
{
- struct tpm_chip *pos, *chip = NULL;
+ int rc = -EIO;
- rcu_read_lock();
- list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
- if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num)
- continue;
+ get_device(&chip->dev);
- if (try_module_get(pos->pdev->driver->owner)) {
- chip = pos;
- break;
- }
+ down_read(&chip->ops_sem);
+ if (!chip->ops)
+ goto out_lock;
+
+ return 0;
+out_lock:
+ up_read(&chip->ops_sem);
+ put_device(&chip->dev);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_try_get_ops);
+
+/**
+ * tpm_put_ops() - Release a ref to the tpm_chip
+ * @chip: Chip to put
+ *
+ * This is the opposite pair to tpm_try_get_ops(). After this returns chip may
+ * be kfree'd.
+ */
+void tpm_put_ops(struct tpm_chip *chip)
+{
+ up_read(&chip->ops_sem);
+ put_device(&chip->dev);
+}
+EXPORT_SYMBOL_GPL(tpm_put_ops);
+
+/**
+ * tpm_chip_find_get() - return tpm_chip for a given chip number
+ * @chip_num: id to find
+ *
+ * The return'd chip has been tpm_try_get_ops'd and must be released via
+ * tpm_put_ops
+ */
+struct tpm_chip *tpm_chip_find_get(int chip_num)
+{
+ struct tpm_chip *chip, *res = NULL;
+ int chip_prev;
+
+ mutex_lock(&idr_lock);
+
+ if (chip_num == TPM_ANY_NUM) {
+ chip_num = 0;
+ do {
+ chip_prev = chip_num;
+ chip = idr_get_next(&dev_nums_idr, &chip_num);
+ if (chip && !tpm_try_get_ops(chip)) {
+ res = chip;
+ break;
+ }
+ } while (chip_prev != chip_num);
+ } else {
+ chip = idr_find_slowpath(&dev_nums_idr, chip_num);
+ if (chip && !tpm_try_get_ops(chip))
+ res = chip;
}
- rcu_read_unlock();
- return chip;
+
+ mutex_unlock(&idr_lock);
+
+ return res;
}
/**
@@ -68,24 +123,25 @@ static void tpm_dev_release(struct device *dev)
{
struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev);
- spin_lock(&driver_lock);
- clear_bit(chip->dev_num, dev_mask);
- spin_unlock(&driver_lock);
+ mutex_lock(&idr_lock);
+ idr_remove(&dev_nums_idr, chip->dev_num);
+ mutex_unlock(&idr_lock);
+
kfree(chip);
}
/**
- * tpmm_chip_alloc() - allocate a new struct tpm_chip instance
- * @dev: device to which the chip is associated
+ * tpm_chip_alloc() - allocate a new struct tpm_chip instance
+ * @pdev: device to which the chip is associated
+ * At this point pdev mst be initialized, but does not have to
+ * be registered
* @ops: struct tpm_class_ops instance
*
* Allocates a new struct tpm_chip instance and assigns a free
- * device number for it. Caller does not have to worry about
- * freeing the allocated resources. When the devices is removed
- * devres calls tpmm_chip_remove() to do the job.
+ * device number for it. Must be paired with put_device(&chip->dev).
*/
-struct tpm_chip *tpmm_chip_alloc(struct device *dev,
- const struct tpm_class_ops *ops)
+struct tpm_chip *tpm_chip_alloc(struct device *dev,
+ const struct tpm_class_ops *ops)
{
struct tpm_chip *chip;
int rc;
@@ -95,53 +151,75 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev,
return ERR_PTR(-ENOMEM);
mutex_init(&chip->tpm_mutex);
- INIT_LIST_HEAD(&chip->list);
+ init_rwsem(&chip->ops_sem);
chip->ops = ops;
- spin_lock(&driver_lock);
- chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES);
- spin_unlock(&driver_lock);
-
- if (chip->dev_num >= TPM_NUM_DEVICES) {
+ mutex_lock(&idr_lock);
+ rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL);
+ mutex_unlock(&idr_lock);
+ if (rc < 0) {
dev_err(dev, "No available tpm device numbers\n");
kfree(chip);
- return ERR_PTR(-ENOMEM);
+ return ERR_PTR(rc);
}
+ chip->dev_num = rc;
- set_bit(chip->dev_num, dev_mask);
-
- scnprintf(chip->devname, sizeof(chip->devname), "tpm%d", chip->dev_num);
-
- chip->pdev = dev;
-
- dev_set_drvdata(dev, chip);
+ device_initialize(&chip->dev);
chip->dev.class = tpm_class;
chip->dev.release = tpm_dev_release;
- chip->dev.parent = chip->pdev;
-#ifdef CONFIG_ACPI
+ chip->dev.parent = dev;
chip->dev.groups = chip->groups;
-#endif
if (chip->dev_num == 0)
chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR);
else
chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num);
- dev_set_name(&chip->dev, "%s", chip->devname);
+ rc = dev_set_name(&chip->dev, "tpm%d", chip->dev_num);
+ if (rc)
+ goto out;
- device_initialize(&chip->dev);
+ if (!dev)
+ chip->flags |= TPM_CHIP_FLAG_VIRTUAL;
cdev_init(&chip->cdev, &tpm_fops);
- chip->cdev.owner = chip->pdev->driver->owner;
+ chip->cdev.owner = THIS_MODULE;
chip->cdev.kobj.parent = &chip->dev.kobj;
- rc = devm_add_action(dev, (void (*)(void *)) put_device, &chip->dev);
- if (rc) {
- put_device(&chip->dev);
+ return chip;
+
+out:
+ put_device(&chip->dev);
+ return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(tpm_chip_alloc);
+
+/**
+ * tpmm_chip_alloc() - allocate a new struct tpm_chip instance
+ * @pdev: parent device to which the chip is associated
+ * @ops: struct tpm_class_ops instance
+ *
+ * Same as tpm_chip_alloc except devm is used to do the put_device
+ */
+struct tpm_chip *tpmm_chip_alloc(struct device *pdev,
+ const struct tpm_class_ops *ops)
+{
+ struct tpm_chip *chip;
+ int rc;
+
+ chip = tpm_chip_alloc(pdev, ops);
+ if (IS_ERR(chip))
+ return chip;
+
+ rc = devm_add_action_or_reset(pdev,
+ (void (*)(void *)) put_device,
+ &chip->dev);
+ if (rc)
return ERR_PTR(rc);
- }
+
+ dev_set_drvdata(pdev, chip);
return chip;
}
@@ -155,7 +233,7 @@ static int tpm_add_char_device(struct tpm_chip *chip)
if (rc) {
dev_err(&chip->dev,
"unable to cdev_add() %s, major %d, minor %d, err=%d\n",
- chip->devname, MAJOR(chip->dev.devt),
+ dev_name(&chip->dev), MAJOR(chip->dev.devt),
MINOR(chip->dev.devt), rc);
return rc;
@@ -165,13 +243,18 @@ static int tpm_add_char_device(struct tpm_chip *chip)
if (rc) {
dev_err(&chip->dev,
"unable to device_register() %s, major %d, minor %d, err=%d\n",
- chip->devname, MAJOR(chip->dev.devt),
+ dev_name(&chip->dev), MAJOR(chip->dev.devt),
MINOR(chip->dev.devt), rc);
cdev_del(&chip->cdev);
return rc;
}
+ /* Make the chip available. */
+ mutex_lock(&idr_lock);
+ idr_replace(&dev_nums_idr, chip, chip->dev_num);
+ mutex_unlock(&idr_lock);
+
return rc;
}
@@ -179,20 +262,28 @@ static void tpm_del_char_device(struct tpm_chip *chip)
{
cdev_del(&chip->cdev);
device_del(&chip->dev);
+
+ /* Make the chip unavailable. */
+ mutex_lock(&idr_lock);
+ idr_replace(&dev_nums_idr, NULL, chip->dev_num);
+ mutex_unlock(&idr_lock);
+
+ /* Make the driver uncallable. */
+ down_write(&chip->ops_sem);
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ tpm2_shutdown(chip, TPM2_SU_CLEAR);
+ chip->ops = NULL;
+ up_write(&chip->ops_sem);
}
static int tpm1_chip_register(struct tpm_chip *chip)
{
- int rc;
-
if (chip->flags & TPM_CHIP_FLAG_TPM2)
return 0;
- rc = tpm_sysfs_add_device(chip);
- if (rc)
- return rc;
+ tpm_sysfs_add_device(chip);
- chip->bios_dir = tpm_bios_log_setup(chip->devname);
+ chip->bios_dir = tpm_bios_log_setup(dev_name(&chip->dev));
return 0;
}
@@ -204,10 +295,50 @@ static void tpm1_chip_unregister(struct tpm_chip *chip)
if (chip->bios_dir)
tpm_bios_log_teardown(chip->bios_dir);
+}
+
+static void tpm_del_legacy_sysfs(struct tpm_chip *chip)
+{
+ struct attribute **i;
+
+ if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL))
+ return;
+
+ sysfs_remove_link(&chip->dev.parent->kobj, "ppi");
- tpm_sysfs_del_device(chip);
+ for (i = chip->groups[0]->attrs; *i != NULL; ++i)
+ sysfs_remove_link(&chip->dev.parent->kobj, (*i)->name);
}
+/* For compatibility with legacy sysfs paths we provide symlinks from the
+ * parent dev directory to selected names within the tpm chip directory. Old
+ * kernel versions created these files directly under the parent.
+ */
+static int tpm_add_legacy_sysfs(struct tpm_chip *chip)
+{
+ struct attribute **i;
+ int rc;
+
+ if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL))
+ return 0;
+
+ rc = __compat_only_sysfs_link_entry_to_kobj(
+ &chip->dev.parent->kobj, &chip->dev.kobj, "ppi");
+ if (rc && rc != -ENOENT)
+ return rc;
+
+ /* All the names from tpm-sysfs */
+ for (i = chip->groups[0]->attrs; *i != NULL; ++i) {
+ rc = __compat_only_sysfs_link_entry_to_kobj(
+ &chip->dev.parent->kobj, &chip->dev.kobj, (*i)->name);
+ if (rc) {
+ tpm_del_legacy_sysfs(chip);
+ return rc;
+ }
+ }
+
+ return 0;
+}
/*
* tpm_chip_register() - create a character device for the TPM chip
* @chip: TPM chip to use.
@@ -223,6 +354,15 @@ int tpm_chip_register(struct tpm_chip *chip)
{
int rc;
+ if (chip->ops->flags & TPM_OPS_AUTO_STARTUP) {
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ rc = tpm2_auto_startup(chip);
+ else
+ rc = tpm1_auto_startup(chip);
+ if (rc)
+ return rc;
+ }
+
rc = tpm1_chip_register(chip);
if (rc)
return rc;
@@ -230,30 +370,20 @@ int tpm_chip_register(struct tpm_chip *chip)
tpm_add_ppi(chip);
rc = tpm_add_char_device(chip);
- if (rc)
- goto out_err;
-
- /* Make the chip available. */
- spin_lock(&driver_lock);
- list_add_tail_rcu(&chip->list, &tpm_chip_list);
- spin_unlock(&driver_lock);
+ if (rc) {
+ tpm1_chip_unregister(chip);
+ return rc;
+ }
chip->flags |= TPM_CHIP_FLAG_REGISTERED;
- if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
- rc = __compat_only_sysfs_link_entry_to_kobj(&chip->pdev->kobj,
- &chip->dev.kobj,
- "ppi");
- if (rc && rc != -ENOENT) {
- tpm_chip_unregister(chip);
- return rc;
- }
+ rc = tpm_add_legacy_sysfs(chip);
+ if (rc) {
+ tpm_chip_unregister(chip);
+ return rc;
}
return 0;
-out_err:
- tpm1_chip_unregister(chip);
- return rc;
}
EXPORT_SYMBOL_GPL(tpm_chip_register);
@@ -264,6 +394,9 @@ EXPORT_SYMBOL_GPL(tpm_chip_register);
* Takes the chip first away from the list of available TPM chips and then
* cleans up all the resources reserved by tpm_chip_register().
*
+ * Once this function returns the driver call backs in 'op's will not be
+ * running and will no longer start.
+ *
* NOTE: This function should be only called before deinitializing chip
* resources.
*/
@@ -272,13 +405,7 @@ void tpm_chip_unregister(struct tpm_chip *chip)
if (!(chip->flags & TPM_CHIP_FLAG_REGISTERED))
return;
- spin_lock(&driver_lock);
- list_del_rcu(&chip->list);
- spin_unlock(&driver_lock);
- synchronize_rcu();
-
- if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
- sysfs_remove_link(&chip->pdev->kobj, "ppi");
+ tpm_del_legacy_sysfs(chip);
tpm1_chip_unregister(chip);
tpm_del_char_device(chip);
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index de0337ebd658..f5d452151c6b 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -61,7 +61,7 @@ static int tpm_open(struct inode *inode, struct file *file)
* by the check of is_open variable, which is protected
* by driver_lock. */
if (test_and_set_bit(0, &chip->is_open)) {
- dev_dbg(chip->pdev, "Another process owns this TPM\n");
+ dev_dbg(&chip->dev, "Another process owns this TPM\n");
return -EBUSY;
}
@@ -79,7 +79,6 @@ static int tpm_open(struct inode *inode, struct file *file)
INIT_WORK(&priv->work, timeout_work);
file->private_data = priv;
- get_device(chip->pdev);
return 0;
}
@@ -137,9 +136,18 @@ static ssize_t tpm_write(struct file *file, const char __user *buf,
return -EFAULT;
}
- /* atomic tpm command send and result receive */
+ /* atomic tpm command send and result receive. We only hold the ops
+ * lock during this period so that the tpm can be unregistered even if
+ * the char dev is held open.
+ */
+ if (tpm_try_get_ops(priv->chip)) {
+ mutex_unlock(&priv->buffer_mutex);
+ return -EPIPE;
+ }
out_size = tpm_transmit(priv->chip, priv->data_buffer,
sizeof(priv->data_buffer));
+
+ tpm_put_ops(priv->chip);
if (out_size < 0) {
mutex_unlock(&priv->buffer_mutex);
return out_size;
@@ -166,7 +174,6 @@ static int tpm_release(struct inode *inode, struct file *file)
file->private_data = NULL;
atomic_set(&priv->data_pending, 0);
clear_bit(0, &priv->chip->is_open);
- put_device(priv->chip->pdev);
kfree(priv);
return 0;
}
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index e2fa89c88304..1abe2d7a2610 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -319,7 +319,7 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
duration_idx = tpm_ordinal_duration[ordinal];
if (duration_idx != TPM_UNDEFINED)
- duration = chip->vendor.duration[duration_idx];
+ duration = chip->duration[duration_idx];
if (duration <= 0)
return 2 * 60 * HZ;
else
@@ -345,7 +345,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
if (count == 0)
return -ENODATA;
if (count > bufsiz) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"invalid count value %x %zx\n", count, bufsiz);
return -E2BIG;
}
@@ -354,12 +354,12 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
rc = chip->ops->send(chip, (u8 *) buf, count);
if (rc < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"tpm_transmit: tpm_send: error %zd\n", rc);
goto out;
}
- if (chip->vendor.irq)
+ if (chip->flags & TPM_CHIP_FLAG_IRQ)
goto out_recv;
if (chip->flags & TPM_CHIP_FLAG_TPM2)
@@ -373,7 +373,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
goto out_recv;
if (chip->ops->req_canceled(chip, status)) {
- dev_err(chip->pdev, "Operation Canceled\n");
+ dev_err(&chip->dev, "Operation Canceled\n");
rc = -ECANCELED;
goto out;
}
@@ -383,14 +383,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
} while (time_before(jiffies, stop));
chip->ops->cancel(chip);
- dev_err(chip->pdev, "Operation Timed out\n");
+ dev_err(&chip->dev, "Operation Timed out\n");
rc = -ETIME;
goto out;
out_recv:
rc = chip->ops->recv(chip, (u8 *) buf, bufsiz);
if (rc < 0)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"tpm_transmit: tpm_recv: error %zd\n", rc);
out:
mutex_unlock(&chip->tpm_mutex);
@@ -416,7 +416,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd,
err = be32_to_cpu(header->return_code);
if (err != 0 && desc)
- dev_err(chip->pdev, "A TPM error (%d) occurred %s\n", err,
+ dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err,
desc);
return err;
@@ -432,12 +432,11 @@ static const struct tpm_input_header tpm_getcap_header = {
.ordinal = TPM_ORD_GET_CAP
};
-ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
+ssize_t tpm_getcap(struct tpm_chip *chip, __be32 subcap_id, cap_t *cap,
const char *desc)
{
struct tpm_cmd_t tpm_cmd;
int rc;
- struct tpm_chip *chip = dev_get_drvdata(dev);
tpm_cmd.header.in = tpm_getcap_header;
if (subcap_id == CAP_VERSION_1_1 || subcap_id == CAP_VERSION_1_2) {
@@ -505,15 +504,15 @@ int tpm_get_timeouts(struct tpm_chip *chip)
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
/* Fixed timeouts for TPM2 */
- chip->vendor.timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
- chip->vendor.timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B);
- chip->vendor.timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C);
- chip->vendor.timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D);
- chip->vendor.duration[TPM_SHORT] =
+ chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
+ chip->timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B);
+ chip->timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C);
+ chip->timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D);
+ chip->duration[TPM_SHORT] =
msecs_to_jiffies(TPM2_DURATION_SHORT);
- chip->vendor.duration[TPM_MEDIUM] =
+ chip->duration[TPM_MEDIUM] =
msecs_to_jiffies(TPM2_DURATION_MEDIUM);
- chip->vendor.duration[TPM_LONG] =
+ chip->duration[TPM_LONG] =
msecs_to_jiffies(TPM2_DURATION_LONG);
return 0;
}
@@ -527,7 +526,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
if (rc == TPM_ERR_INVALID_POSTINIT) {
/* The TPM is not started, we are the first to talk to it.
Execute a startup command. */
- dev_info(chip->pdev, "Issuing TPM_STARTUP");
+ dev_info(&chip->dev, "Issuing TPM_STARTUP");
if (tpm_startup(chip, TPM_ST_CLEAR))
return rc;
@@ -539,7 +538,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
NULL);
}
if (rc) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"A TPM error (%zd) occurred attempting to determine the timeouts\n",
rc);
goto duration;
@@ -561,10 +560,10 @@ int tpm_get_timeouts(struct tpm_chip *chip)
* of misreporting.
*/
if (chip->ops->update_timeouts != NULL)
- chip->vendor.timeout_adjusted =
+ chip->timeout_adjusted =
chip->ops->update_timeouts(chip, new_timeout);
- if (!chip->vendor.timeout_adjusted) {
+ if (!chip->timeout_adjusted) {
/* Don't overwrite default if value is 0 */
if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
int i;
@@ -572,13 +571,13 @@ int tpm_get_timeouts(struct tpm_chip *chip)
/* timeouts in msec rather usec */
for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
new_timeout[i] *= 1000;
- chip->vendor.timeout_adjusted = true;
+ chip->timeout_adjusted = true;
}
}
/* Report adjusted timeouts */
- if (chip->vendor.timeout_adjusted) {
- dev_info(chip->pdev,
+ if (chip->timeout_adjusted) {
+ dev_info(&chip->dev,
HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
old_timeout[0], new_timeout[0],
old_timeout[1], new_timeout[1],
@@ -586,10 +585,10 @@ int tpm_get_timeouts(struct tpm_chip *chip)
old_timeout[3], new_timeout[3]);
}
- chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
- chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
- chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
- chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
+ chip->timeout_a = usecs_to_jiffies(new_timeout[0]);
+ chip->timeout_b = usecs_to_jiffies(new_timeout[1]);
+ chip->timeout_c = usecs_to_jiffies(new_timeout[2]);
+ chip->timeout_d = usecs_to_jiffies(new_timeout[3]);
duration:
tpm_cmd.header.in = tpm_getcap_header;
@@ -608,11 +607,11 @@ duration:
return -EINVAL;
duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
- chip->vendor.duration[TPM_SHORT] =
+ chip->duration[TPM_SHORT] =
usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
- chip->vendor.duration[TPM_MEDIUM] =
+ chip->duration[TPM_MEDIUM] =
usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
- chip->vendor.duration[TPM_LONG] =
+ chip->duration[TPM_LONG] =
usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
@@ -620,12 +619,12 @@ duration:
* fix up the resulting too-small TPM_SHORT value to make things work.
* We also scale the TPM_MEDIUM and -_LONG values by 1000.
*/
- if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) {
- chip->vendor.duration[TPM_SHORT] = HZ;
- chip->vendor.duration[TPM_MEDIUM] *= 1000;
- chip->vendor.duration[TPM_LONG] *= 1000;
- chip->vendor.duration_adjusted = true;
- dev_info(chip->pdev, "Adjusting TPM timeout parameters.");
+ if (chip->duration[TPM_SHORT] < (HZ / 100)) {
+ chip->duration[TPM_SHORT] = HZ;
+ chip->duration[TPM_MEDIUM] *= 1000;
+ chip->duration[TPM_LONG] *= 1000;
+ chip->duration_adjusted = true;
+ dev_info(&chip->dev, "Adjusting TPM timeout parameters.");
}
return 0;
}
@@ -700,7 +699,7 @@ int tpm_is_tpm2(u32 chip_num)
rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0;
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
@@ -729,7 +728,7 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
rc = tpm2_pcr_read(chip, pcr_idx, res_buf);
else
rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_pcr_read);
@@ -764,7 +763,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
rc = tpm2_pcr_extend(chip, pcr_idx, hash);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
@@ -774,7 +773,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
"attempting extend a PCR value");
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_pcr_extend);
@@ -815,7 +814,9 @@ int tpm_do_selftest(struct tpm_chip *chip)
* around 300ms while the self test is ongoing, keep trying
* until the self test duration expires. */
if (rc == -ETIME) {
- dev_info(chip->pdev, HW_ERR "TPM command timed out during continue self test");
+ dev_info(
+ &chip->dev, HW_ERR
+ "TPM command timed out during continue self test");
msleep(delay_msec);
continue;
}
@@ -825,7 +826,7 @@ int tpm_do_selftest(struct tpm_chip *chip)
rc = be32_to_cpu(cmd.header.out.return_code);
if (rc == TPM_ERR_DISABLED || rc == TPM_ERR_DEACTIVATED) {
- dev_info(chip->pdev,
+ dev_info(&chip->dev,
"TPM is disabled/deactivated (0x%X)\n", rc);
/* TPM is disabled and/or deactivated; driver can
* proceed and TPM does handle commands for
@@ -842,6 +843,33 @@ int tpm_do_selftest(struct tpm_chip *chip)
}
EXPORT_SYMBOL_GPL(tpm_do_selftest);
+/**
+ * tpm1_auto_startup - Perform the standard automatic TPM initialization
+ * sequence
+ * @chip: TPM chip to use
+ *
+ * Returns 0 on success, < 0 in case of fatal error.
+ */
+int tpm1_auto_startup(struct tpm_chip *chip)
+{
+ int rc;
+
+ rc = tpm_get_timeouts(chip);
+ if (rc)
+ goto out;
+ rc = tpm_do_selftest(chip);
+ if (rc) {
+ dev_err(&chip->dev, "TPM self test failed\n");
+ goto out;
+ }
+
+ return rc;
+out:
+ if (rc > 0)
+ rc = -ENODEV;
+ return rc;
+}
+
int tpm_send(u32 chip_num, void *cmd, size_t buflen)
{
struct tpm_chip *chip;
@@ -853,7 +881,7 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen)
rc = tpm_transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd");
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_send);
@@ -888,7 +916,7 @@ int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
stop = jiffies + timeout;
- if (chip->vendor.irq) {
+ if (chip->flags & TPM_CHIP_FLAG_IRQ) {
again:
timeout = stop - jiffies;
if ((long)timeout <= 0)
@@ -978,10 +1006,10 @@ int tpm_pm_suspend(struct device *dev)
}
if (rc)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"Error (%d) sending savestate before suspend\n", rc);
else if (try > 0)
- dev_warn(chip->pdev, "TPM savestate took %dms\n",
+ dev_warn(&chip->dev, "TPM savestate took %dms\n",
try * TPM_TIMEOUT_RETRY);
return rc;
@@ -1035,7 +1063,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
err = tpm2_get_random(chip, out, max);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return err;
}
@@ -1057,7 +1085,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
num_bytes -= recd;
} while (retries-- && total < max);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return total ? total : -EIO;
}
EXPORT_SYMBOL_GPL(tpm_get_random);
@@ -1083,7 +1111,7 @@ int tpm_seal_trusted(u32 chip_num, struct trusted_key_payload *payload,
rc = tpm2_seal_trusted(chip, payload, options);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_seal_trusted);
@@ -1109,7 +1137,8 @@ int tpm_unseal_trusted(u32 chip_num, struct trusted_key_payload *payload,
rc = tpm2_unseal_trusted(chip, payload, options);
- tpm_chip_put(chip);
+ tpm_put_ops(chip);
+
return rc;
}
EXPORT_SYMBOL_GPL(tpm_unseal_trusted);
@@ -1136,6 +1165,7 @@ static int __init tpm_init(void)
static void __exit tpm_exit(void)
{
+ idr_destroy(&dev_nums_idr);
class_destroy(tpm_class);
unregister_chrdev_region(tpm_devt, TPM_NUM_DEVICES);
}
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index ee66fd4673f3..b46cf70c8b16 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -36,7 +36,7 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
int i, rc;
char *str = buf;
- struct tpm_chip *chip = dev_get_drvdata(dev);
+ struct tpm_chip *chip = to_tpm_chip(dev);
tpm_cmd.header.in = tpm_readpubek_header;
err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
@@ -92,9 +92,9 @@ static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr,
ssize_t rc;
int i, j, num_pcrs;
char *str = buf;
- struct tpm_chip *chip = dev_get_drvdata(dev);
+ struct tpm_chip *chip = to_tpm_chip(dev);
- rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap,
+ rc = tpm_getcap(chip, TPM_CAP_PROP_PCR, &cap,
"attempting to determine the number of PCRS");
if (rc)
return 0;
@@ -119,8 +119,8 @@ static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
cap_t cap;
ssize_t rc;
- rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
- "attempting to determine the permanent enabled state");
+ rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap,
+ "attempting to determine the permanent enabled state");
if (rc)
return 0;
@@ -135,8 +135,8 @@ static ssize_t active_show(struct device *dev, struct device_attribute *attr,
cap_t cap;
ssize_t rc;
- rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
- "attempting to determine the permanent active state");
+ rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap,
+ "attempting to determine the permanent active state");
if (rc)
return 0;
@@ -151,8 +151,8 @@ static ssize_t owned_show(struct device *dev, struct device_attribute *attr,
cap_t cap;
ssize_t rc;
- rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap,
- "attempting to determine the owner state");
+ rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_PROP_OWNER, &cap,
+ "attempting to determine the owner state");
if (rc)
return 0;
@@ -167,8 +167,8 @@ static ssize_t temp_deactivated_show(struct device *dev,
cap_t cap;
ssize_t rc;
- rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap,
- "attempting to determine the temporary state");
+ rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_VOL, &cap,
+ "attempting to determine the temporary state");
if (rc)
return 0;
@@ -180,11 +180,12 @@ static DEVICE_ATTR_RO(temp_deactivated);
static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
+ struct tpm_chip *chip = to_tpm_chip(dev);
cap_t cap;
ssize_t rc;
char *str = buf;
- rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
+ rc = tpm_getcap(chip, TPM_CAP_PROP_MANUFACTURER, &cap,
"attempting to determine the manufacturer");
if (rc)
return 0;
@@ -192,8 +193,8 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
be32_to_cpu(cap.manufacturer_id));
/* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */
- rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap,
- "attempting to determine the 1.2 version");
+ rc = tpm_getcap(chip, CAP_VERSION_1_2, &cap,
+ "attempting to determine the 1.2 version");
if (!rc) {
str += sprintf(str,
"TCG version: %d.%d\nFirmware version: %d.%d\n",
@@ -203,7 +204,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
cap.tpm_version_1_2.revMinor);
} else {
/* Otherwise just use TPM_STRUCT_VER */
- rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
+ rc = tpm_getcap(chip, CAP_VERSION_1_1, &cap,
"attempting to determine the 1.1 version");
if (rc)
return 0;
@@ -222,7 +223,7 @@ static DEVICE_ATTR_RO(caps);
static ssize_t cancel_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct tpm_chip *chip = dev_get_drvdata(dev);
+ struct tpm_chip *chip = to_tpm_chip(dev);
if (chip == NULL)
return 0;
@@ -234,16 +235,16 @@ static DEVICE_ATTR_WO(cancel);
static ssize_t durations_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct tpm_chip *chip = dev_get_drvdata(dev);
+ struct tpm_chip *chip = to_tpm_chip(dev);
- if (chip->vendor.duration[TPM_LONG] == 0)
+ if (chip->duration[TPM_LONG] == 0)
return 0;
return sprintf(buf, "%d %d %d [%s]\n",
- jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
- jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
- jiffies_to_usecs(chip->vendor.duration[TPM_LONG]),
- chip->vendor.duration_adjusted
+ jiffies_to_usecs(chip->duration[TPM_SHORT]),
+ jiffies_to_usecs(chip->duration[TPM_MEDIUM]),
+ jiffies_to_usecs(chip->duration[TPM_LONG]),
+ chip->duration_adjusted
? "adjusted" : "original");
}
static DEVICE_ATTR_RO(durations);
@@ -251,14 +252,14 @@ static DEVICE_ATTR_RO(durations);
static ssize_t timeouts_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct tpm_chip *chip = dev_get_drvdata(dev);
+ struct tpm_chip *chip = to_tpm_chip(dev);
return sprintf(buf, "%d %d %d %d [%s]\n",
- jiffies_to_usecs(chip->vendor.timeout_a),
- jiffies_to_usecs(chip->vendor.timeout_b),
- jiffies_to_usecs(chip->vendor.timeout_c),
- jiffies_to_usecs(chip->vendor.timeout_d),
- chip->vendor.timeout_adjusted
+ jiffies_to_usecs(chip->timeout_a),
+ jiffies_to_usecs(chip->timeout_b),
+ jiffies_to_usecs(chip->timeout_c),
+ jiffies_to_usecs(chip->timeout_d),
+ chip->timeout_adjusted
? "adjusted" : "original");
}
static DEVICE_ATTR_RO(timeouts);
@@ -281,19 +282,12 @@ static const struct attribute_group tpm_dev_group = {
.attrs = tpm_dev_attrs,
};
-int tpm_sysfs_add_device(struct tpm_chip *chip)
+void tpm_sysfs_add_device(struct tpm_chip *chip)
{
- int err;
- err = sysfs_create_group(&chip->pdev->kobj,
- &tpm_dev_group);
-
- if (err)
- dev_err(chip->pdev,
- "failed to create sysfs attributes, %d\n", err);
- return err;
-}
-
-void tpm_sysfs_del_device(struct tpm_chip *chip)
-{
- sysfs_remove_group(&chip->pdev->kobj, &tpm_dev_group);
+ /* The sysfs routines rely on an implicit tpm_try_get_ops, device_del
+ * is called before ops is null'd and the sysfs core synchronizes this
+ * removal so that no callbacks are running or can run again
+ */
+ WARN_ON(chip->groups_cnt != 0);
+ chip->groups[chip->groups_cnt++] = &tpm_dev_group;
}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 28b477e8da6a..3e32d5bd2dc6 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -19,6 +19,10 @@
* License.
*
*/
+
+#ifndef __TPM_H__
+#define __TPM_H__
+
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/fs.h>
@@ -34,7 +38,7 @@
enum tpm_const {
TPM_MINOR = 224, /* officially assigned */
TPM_BUFSIZE = 4096,
- TPM_NUM_DEVICES = 256,
+ TPM_NUM_DEVICES = 65536,
TPM_RETRY = 50, /* 5 seconds */
};
@@ -128,33 +132,6 @@ enum tpm2_startup_types {
TPM2_SU_STATE = 0x0001,
};
-struct tpm_chip;
-
-struct tpm_vendor_specific {
- void __iomem *iobase; /* ioremapped address */
- unsigned long base; /* TPM base address */
-
- int irq;
-
- int region_size;
- int have_region;
-
- struct list_head list;
- int locality;
- unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */
- bool timeout_adjusted;
- unsigned long duration[3]; /* jiffies */
- bool duration_adjusted;
- void *priv;
-
- wait_queue_head_t read_queue;
- wait_queue_head_t int_queue;
-
- u16 manufacturer_id;
-};
-
-#define TPM_VPRIV(c) ((c)->vendor.priv)
-
#define TPM_VID_INTEL 0x8086
#define TPM_VID_WINBOND 0x1050
#define TPM_VID_STM 0x104A
@@ -164,44 +141,48 @@ struct tpm_vendor_specific {
enum tpm_chip_flags {
TPM_CHIP_FLAG_REGISTERED = BIT(0),
TPM_CHIP_FLAG_TPM2 = BIT(1),
+ TPM_CHIP_FLAG_IRQ = BIT(2),
+ TPM_CHIP_FLAG_VIRTUAL = BIT(3),
};
struct tpm_chip {
- struct device *pdev; /* Device stuff */
struct device dev;
struct cdev cdev;
+ /* A driver callback under ops cannot be run unless ops_sem is held
+ * (sometimes implicitly, eg for the sysfs code). ops becomes null
+ * when the driver is unregistered, see tpm_try_get_ops.
+ */
+ struct rw_semaphore ops_sem;
const struct tpm_class_ops *ops;
+
unsigned int flags;
int dev_num; /* /dev/tpm# */
- char devname[7];
unsigned long is_open; /* only one allowed */
- int time_expired;
struct mutex tpm_mutex; /* tpm is processing */
- struct tpm_vendor_specific vendor;
+ unsigned long timeout_a; /* jiffies */
+ unsigned long timeout_b; /* jiffies */
+ unsigned long timeout_c; /* jiffies */
+ unsigned long timeout_d; /* jiffies */
+ bool timeout_adjusted;
+ unsigned long duration[3]; /* jiffies */
+ bool duration_adjusted;
struct dentry **bios_dir;
-#ifdef CONFIG_ACPI
- const struct attribute_group *groups[2];
+ const struct attribute_group *groups[3];
unsigned int groups_cnt;
+#ifdef CONFIG_ACPI
acpi_handle acpi_dev_handle;
char ppi_version[TPM_PPI_VERSION_LEN + 1];
#endif /* CONFIG_ACPI */
-
- struct list_head list;
};
#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
-static inline void tpm_chip_put(struct tpm_chip *chip)
-{
- module_put(chip->pdev->driver->owner);
-}
-
static inline int tpm_read_index(int base, int index)
{
outb(index, base);
@@ -493,14 +474,17 @@ static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
extern struct class *tpm_class;
extern dev_t tpm_devt;
extern const struct file_operations tpm_fops;
+extern struct idr dev_nums_idr;
-ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *);
+ssize_t tpm_getcap(struct tpm_chip *chip, __be32 subcap_id, cap_t *cap,
+ const char *desc);
ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
size_t bufsiz);
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd, int len,
const char *desc);
extern int tpm_get_timeouts(struct tpm_chip *);
extern void tpm_gen_interrupt(struct tpm_chip *);
+int tpm1_auto_startup(struct tpm_chip *chip);
extern int tpm_do_selftest(struct tpm_chip *);
extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32);
extern int tpm_pm_suspend(struct device *);
@@ -509,13 +493,17 @@ extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
wait_queue_head_t *, bool);
struct tpm_chip *tpm_chip_find_get(int chip_num);
-extern struct tpm_chip *tpmm_chip_alloc(struct device *dev,
+__must_check int tpm_try_get_ops(struct tpm_chip *chip);
+void tpm_put_ops(struct tpm_chip *chip);
+
+extern struct tpm_chip *tpm_chip_alloc(struct device *dev,
+ const struct tpm_class_ops *ops);
+extern struct tpm_chip *tpmm_chip_alloc(struct device *pdev,
const struct tpm_class_ops *ops);
extern int tpm_chip_register(struct tpm_chip *chip);
extern void tpm_chip_unregister(struct tpm_chip *chip);
-int tpm_sysfs_add_device(struct tpm_chip *chip);
-void tpm_sysfs_del_device(struct tpm_chip *chip);
+void tpm_sysfs_add_device(struct tpm_chip *chip);
int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf);
@@ -539,9 +527,9 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
u32 *value, const char *desc);
-extern int tpm2_startup(struct tpm_chip *chip, u16 startup_type);
+int tpm2_auto_startup(struct tpm_chip *chip);
extern void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type);
extern unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *, u32);
-extern int tpm2_do_selftest(struct tpm_chip *chip);
extern int tpm2_gen_interrupt(struct tpm_chip *chip);
extern int tpm2_probe(struct tpm_chip *chip);
+#endif
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index b28e4da3d2cf..08c7e23ed535 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -597,7 +597,7 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
if (rc) {
- dev_warn(chip->pdev, "0x%08x was not flushed, out of memory\n",
+ dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
handle);
return;
}
@@ -606,7 +606,7 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "flushing context");
if (rc)
- dev_warn(chip->pdev, "0x%08x was not flushed, rc=%d\n", handle,
+ dev_warn(&chip->dev, "0x%08x was not flushed, rc=%d\n", handle,
rc);
tpm_buf_destroy(&buf);
@@ -703,7 +703,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), desc);
if (!rc)
- *value = cmd.params.get_tpm_pt_out.value;
+ *value = be32_to_cpu(cmd.params.get_tpm_pt_out.value);
return rc;
}
@@ -728,7 +728,7 @@ static const struct tpm_input_header tpm2_startup_header = {
* returned it remarks a POSIX error code. If a positive number is returned
* it remarks a TPM error.
*/
-int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
+static int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
{
struct tpm2_cmd cmd;
@@ -738,7 +738,6 @@ int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
return tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
"attempting to start the TPM");
}
-EXPORT_SYMBOL_GPL(tpm2_startup);
#define TPM2_SHUTDOWN_IN_SIZE \
(sizeof(struct tpm_input_header) + \
@@ -770,10 +769,9 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
* except print the error code on a system failure.
*/
if (rc < 0)
- dev_warn(chip->pdev, "transmit returned %d while stopping the TPM",
+ dev_warn(&chip->dev, "transmit returned %d while stopping the TPM",
rc);
}
-EXPORT_SYMBOL_GPL(tpm2_shutdown);
/*
* tpm2_calc_ordinal_duration() - maximum duration for a command
@@ -793,7 +791,7 @@ unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
index = tpm2_ordinal_duration[ordinal - TPM2_CC_FIRST];
if (index != TPM_UNDEFINED)
- duration = chip->vendor.duration[index];
+ duration = chip->duration[index];
if (duration <= 0)
duration = 2 * 60 * HZ;
@@ -837,7 +835,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
* immediately. This is a workaround for that.
*/
if (rc == TPM2_RC_TESTING) {
- dev_warn(chip->pdev, "Got RC_TESTING, ignoring\n");
+ dev_warn(&chip->dev, "Got RC_TESTING, ignoring\n");
rc = 0;
}
@@ -855,7 +853,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
* returned it remarks a POSIX error code. If a positive number is returned
* it remarks a TPM error.
*/
-int tpm2_do_selftest(struct tpm_chip *chip)
+static int tpm2_do_selftest(struct tpm_chip *chip)
{
int rc;
unsigned int loops;
@@ -895,7 +893,6 @@ int tpm2_do_selftest(struct tpm_chip *chip)
return rc;
}
-EXPORT_SYMBOL_GPL(tpm2_do_selftest);
/**
* tpm2_gen_interrupt() - generate an interrupt
@@ -943,3 +940,43 @@ int tpm2_probe(struct tpm_chip *chip)
return 0;
}
EXPORT_SYMBOL_GPL(tpm2_probe);
+
+/**
+ * tpm2_auto_startup - Perform the standard automatic TPM initialization
+ * sequence
+ * @chip: TPM chip to use
+ *
+ * Returns 0 on success, < 0 in case of fatal error.
+ */
+int tpm2_auto_startup(struct tpm_chip *chip)
+{
+ int rc;
+
+ rc = tpm_get_timeouts(chip);
+ if (rc)
+ goto out;
+
+ rc = tpm2_do_selftest(chip);
+ if (rc != TPM2_RC_INITIALIZE) {
+ dev_err(&chip->dev, "TPM self test failed\n");
+ goto out;
+ }
+
+ if (rc == TPM2_RC_INITIALIZE) {
+ rc = tpm2_startup(chip, TPM2_SU_CLEAR);
+ if (rc)
+ goto out;
+
+ rc = tpm2_do_selftest(chip);
+ if (rc) {
+ dev_err(&chip->dev, "TPM self test failed\n");
+ goto out;
+ }
+ }
+
+ return rc;
+out:
+ if (rc > 0)
+ rc = -ENODEV;
+ return rc;
+}
diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c
index dfadad0916a1..0d322ab11faa 100644
--- a/drivers/char/tpm/tpm_atmel.c
+++ b/drivers/char/tpm/tpm_atmel.c
@@ -37,6 +37,7 @@ enum tpm_atmel_read_status {
static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
+ struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
u8 status, *hdr = buf;
u32 size;
int i;
@@ -47,12 +48,12 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
return -EIO;
for (i = 0; i < 6; i++) {
- status = ioread8(chip->vendor.iobase + 1);
+ status = ioread8(priv->iobase + 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
- dev_err(chip->pdev, "error reading header\n");
+ dev_err(&chip->dev, "error reading header\n");
return -EIO;
}
- *buf++ = ioread8(chip->vendor.iobase);
+ *buf++ = ioread8(priv->iobase);
}
/* size of the data received */
@@ -60,12 +61,12 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
size = be32_to_cpu(*native_size);
if (count < size) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"Recv size(%d) less than available space\n", size);
for (; i < size; i++) { /* clear the waiting data anyway */
- status = ioread8(chip->vendor.iobase + 1);
+ status = ioread8(priv->iobase + 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
- dev_err(chip->pdev, "error reading data\n");
+ dev_err(&chip->dev, "error reading data\n");
return -EIO;
}
}
@@ -74,19 +75,19 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
/* read all the data available */
for (; i < size; i++) {
- status = ioread8(chip->vendor.iobase + 1);
+ status = ioread8(priv->iobase + 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
- dev_err(chip->pdev, "error reading data\n");
+ dev_err(&chip->dev, "error reading data\n");
return -EIO;
}
- *buf++ = ioread8(chip->vendor.iobase);
+ *buf++ = ioread8(priv->iobase);
}
/* make sure data available is gone */
- status = ioread8(chip->vendor.iobase + 1);
+ status = ioread8(priv->iobase + 1);
if (status & ATML_STATUS_DATA_AVAIL) {
- dev_err(chip->pdev, "data available is stuck\n");
+ dev_err(&chip->dev, "data available is stuck\n");
return -EIO;
}
@@ -95,12 +96,13 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
{
+ struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
int i;
- dev_dbg(chip->pdev, "tpm_atml_send:\n");
+ dev_dbg(&chip->dev, "tpm_atml_send:\n");
for (i = 0; i < count; i++) {
- dev_dbg(chip->pdev, "%d 0x%x(%d)\n", i, buf[i], buf[i]);
- iowrite8(buf[i], chip->vendor.iobase);
+ dev_dbg(&chip->dev, "%d 0x%x(%d)\n", i, buf[i], buf[i]);
+ iowrite8(buf[i], priv->iobase);
}
return count;
@@ -108,12 +110,16 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
static void tpm_atml_cancel(struct tpm_chip *chip)
{
- iowrite8(ATML_STATUS_ABORT, chip->vendor.iobase + 1);
+ struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
+
+ iowrite8(ATML_STATUS_ABORT, priv->iobase + 1);
}
static u8 tpm_atml_status(struct tpm_chip *chip)
{
- return ioread8(chip->vendor.iobase + 1);
+ struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
+
+ return ioread8(priv->iobase + 1);
}
static bool tpm_atml_req_canceled(struct tpm_chip *chip, u8 status)
@@ -136,13 +142,13 @@ static struct platform_device *pdev;
static void atml_plat_remove(void)
{
struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
+ struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev);
if (chip) {
tpm_chip_unregister(chip);
- if (chip->vendor.have_region)
- atmel_release_region(chip->vendor.base,
- chip->vendor.region_size);
- atmel_put_base_addr(chip->vendor.iobase);
+ if (priv->have_region)
+ atmel_release_region(priv->base, priv->region_size);
+ atmel_put_base_addr(priv->iobase);
platform_device_unregister(pdev);
}
}
@@ -163,6 +169,7 @@ static int __init init_atmel(void)
int have_region, region_size;
unsigned long base;
struct tpm_chip *chip;
+ struct tpm_atmel_priv *priv;
rc = platform_driver_register(&atml_drv);
if (rc)
@@ -183,16 +190,24 @@ static int __init init_atmel(void)
goto err_rel_reg;
}
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ rc = -ENOMEM;
+ goto err_unreg_dev;
+ }
+
+ priv->iobase = iobase;
+ priv->base = base;
+ priv->have_region = have_region;
+ priv->region_size = region_size;
+
chip = tpmm_chip_alloc(&pdev->dev, &tpm_atmel);
if (IS_ERR(chip)) {
rc = PTR_ERR(chip);
goto err_unreg_dev;
}
- chip->vendor.iobase = iobase;
- chip->vendor.base = base;
- chip->vendor.have_region = have_region;
- chip->vendor.region_size = region_size;
+ dev_set_drvdata(&chip->dev, priv);
rc = tpm_chip_register(chip);
if (rc)
diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h
index 6c831f9466b7..4f96d80cdce9 100644
--- a/drivers/char/tpm/tpm_atmel.h
+++ b/drivers/char/tpm/tpm_atmel.h
@@ -22,12 +22,19 @@
*
*/
+struct tpm_atmel_priv {
+ int region_size;
+ int have_region;
+ unsigned long base;
+ void __iomem *iobase;
+};
+
#ifdef CONFIG_PPC64
#include <asm/prom.h>
-#define atmel_getb(chip, offset) readb(chip->vendor->iobase + offset);
-#define atmel_putb(val, chip, offset) writeb(val, chip->vendor->iobase + offset)
+#define atmel_getb(priv, offset) readb(priv->iobase + offset)
+#define atmel_putb(val, priv, offset) writeb(val, priv->iobase + offset)
#define atmel_request_region request_mem_region
#define atmel_release_region release_mem_region
@@ -78,8 +85,9 @@ static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size)
return ioremap(*base, *region_size);
}
#else
-#define atmel_getb(chip, offset) inb(chip->vendor->base + offset)
-#define atmel_putb(val, chip, offset) outb(val, chip->vendor->base + offset)
+#define atmel_getb(chip, offset) inb(atmel_get_priv(chip)->base + offset)
+#define atmel_putb(val, chip, offset) \
+ outb(val, atmel_get_priv(chip)->base + offset)
#define atmel_request_region request_region
#define atmel_release_region release_region
/* Atmel definitions */
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index a12b31940344..018c382554ba 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -77,7 +77,6 @@ enum crb_flags {
struct crb_priv {
unsigned int flags;
- struct resource res;
void __iomem *iobase;
struct crb_control_area __iomem *cca;
u8 __iomem *cmd;
@@ -88,7 +87,7 @@ static SIMPLE_DEV_PM_OPS(crb_pm, tpm_pm_suspend, tpm_pm_resume);
static u8 crb_status(struct tpm_chip *chip)
{
- struct crb_priv *priv = chip->vendor.priv;
+ struct crb_priv *priv = dev_get_drvdata(&chip->dev);
u8 sts = 0;
if ((ioread32(&priv->cca->start) & CRB_START_INVOKE) !=
@@ -100,7 +99,7 @@ static u8 crb_status(struct tpm_chip *chip)
static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
- struct crb_priv *priv = chip->vendor.priv;
+ struct crb_priv *priv = dev_get_drvdata(&chip->dev);
unsigned int expected;
/* sanity check */
@@ -140,7 +139,7 @@ static int crb_do_acpi_start(struct tpm_chip *chip)
static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
{
- struct crb_priv *priv = chip->vendor.priv;
+ struct crb_priv *priv = dev_get_drvdata(&chip->dev);
int rc = 0;
if (len > ioread32(&priv->cca->cmd_size)) {
@@ -167,7 +166,7 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
static void crb_cancel(struct tpm_chip *chip)
{
- struct crb_priv *priv = chip->vendor.priv;
+ struct crb_priv *priv = dev_get_drvdata(&chip->dev);
iowrite32(cpu_to_le32(CRB_CANCEL_INVOKE), &priv->cca->cancel);
@@ -182,13 +181,14 @@ static void crb_cancel(struct tpm_chip *chip)
static bool crb_req_canceled(struct tpm_chip *chip, u8 status)
{
- struct crb_priv *priv = chip->vendor.priv;
+ struct crb_priv *priv = dev_get_drvdata(&chip->dev);
u32 cancel = ioread32(&priv->cca->cancel);
return (cancel & CRB_CANCEL_INVOKE) == CRB_CANCEL_INVOKE;
}
static const struct tpm_class_ops tpm_crb = {
+ .flags = TPM_OPS_AUTO_STARTUP,
.status = crb_status,
.recv = crb_recv,
.send = crb_send,
@@ -201,42 +201,33 @@ static const struct tpm_class_ops tpm_crb = {
static int crb_init(struct acpi_device *device, struct crb_priv *priv)
{
struct tpm_chip *chip;
- int rc;
chip = tpmm_chip_alloc(&device->dev, &tpm_crb);
if (IS_ERR(chip))
return PTR_ERR(chip);
- chip->vendor.priv = priv;
+ dev_set_drvdata(&chip->dev, priv);
chip->acpi_dev_handle = device->handle;
chip->flags = TPM_CHIP_FLAG_TPM2;
- rc = tpm_get_timeouts(chip);
- if (rc)
- return rc;
-
- rc = tpm2_do_selftest(chip);
- if (rc)
- return rc;
-
return tpm_chip_register(chip);
}
static int crb_check_resource(struct acpi_resource *ares, void *data)
{
- struct crb_priv *priv = data;
+ struct resource *io_res = data;
struct resource res;
if (acpi_dev_resource_memory(ares, &res)) {
- priv->res = res;
- priv->res.name = NULL;
+ *io_res = res;
+ io_res->name = NULL;
}
return 1;
}
static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv,
- u64 start, u32 size)
+ struct resource *io_res, u64 start, u32 size)
{
struct resource new_res = {
.start = start,
@@ -246,53 +237,74 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv,
/* Detect a 64 bit address on a 32 bit system */
if (start != new_res.start)
- return ERR_PTR(-EINVAL);
+ return (void __iomem *) ERR_PTR(-EINVAL);
- if (!resource_contains(&priv->res, &new_res))
+ if (!resource_contains(io_res, &new_res))
return devm_ioremap_resource(dev, &new_res);
- return priv->iobase + (new_res.start - priv->res.start);
+ return priv->iobase + (new_res.start - io_res->start);
}
static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
struct acpi_table_tpm2 *buf)
{
struct list_head resources;
+ struct resource io_res;
struct device *dev = &device->dev;
- u64 pa;
+ u64 cmd_pa;
+ u32 cmd_size;
+ u64 rsp_pa;
+ u32 rsp_size;
int ret;
INIT_LIST_HEAD(&resources);
ret = acpi_dev_get_resources(device, &resources, crb_check_resource,
- priv);
+ &io_res);
if (ret < 0)
return ret;
acpi_dev_free_resource_list(&resources);
- if (resource_type(&priv->res) != IORESOURCE_MEM) {
+ if (resource_type(&io_res) != IORESOURCE_MEM) {
dev_err(dev,
FW_BUG "TPM2 ACPI table does not define a memory resource\n");
return -EINVAL;
}
- priv->iobase = devm_ioremap_resource(dev, &priv->res);
+ priv->iobase = devm_ioremap_resource(dev, &io_res);
if (IS_ERR(priv->iobase))
return PTR_ERR(priv->iobase);
- priv->cca = crb_map_res(dev, priv, buf->control_address, 0x1000);
+ priv->cca = crb_map_res(dev, priv, &io_res, buf->control_address,
+ sizeof(struct crb_control_area));
if (IS_ERR(priv->cca))
return PTR_ERR(priv->cca);
- pa = ((u64) ioread32(&priv->cca->cmd_pa_high) << 32) |
- (u64) ioread32(&priv->cca->cmd_pa_low);
- priv->cmd = crb_map_res(dev, priv, pa, ioread32(&priv->cca->cmd_size));
+ cmd_pa = ((u64) ioread32(&priv->cca->cmd_pa_high) << 32) |
+ (u64) ioread32(&priv->cca->cmd_pa_low);
+ cmd_size = ioread32(&priv->cca->cmd_size);
+ priv->cmd = crb_map_res(dev, priv, &io_res, cmd_pa, cmd_size);
if (IS_ERR(priv->cmd))
return PTR_ERR(priv->cmd);
- memcpy_fromio(&pa, &priv->cca->rsp_pa, 8);
- pa = le64_to_cpu(pa);
- priv->rsp = crb_map_res(dev, priv, pa, ioread32(&priv->cca->rsp_size));
- return PTR_ERR_OR_ZERO(priv->rsp);
+ memcpy_fromio(&rsp_pa, &priv->cca->rsp_pa, 8);
+ rsp_pa = le64_to_cpu(rsp_pa);
+ rsp_size = ioread32(&priv->cca->rsp_size);
+
+ if (cmd_pa != rsp_pa) {
+ priv->rsp = crb_map_res(dev, priv, &io_res, rsp_pa, rsp_size);
+ return PTR_ERR_OR_ZERO(priv->rsp);
+ }
+
+ /* According to the PTP specification, overlapping command and response
+ * buffer sizes must be identical.
+ */
+ if (cmd_size != rsp_size) {
+ dev_err(dev, FW_BUG "overlapping command and response buffer sizes are not identical");
+ return -EINVAL;
+ }
+
+ priv->rsp = priv->cmd;
+ return 0;
}
static int crb_acpi_add(struct acpi_device *device)
@@ -344,9 +356,6 @@ static int crb_acpi_remove(struct acpi_device *device)
struct device *dev = &device->dev;
struct tpm_chip *chip = dev_get_drvdata(dev);
- if (chip->flags & TPM_CHIP_FLAG_TPM2)
- tpm2_shutdown(chip, TPM2_SU_CLEAR);
-
tpm_chip_unregister(chip);
return 0;
diff --git a/drivers/char/tpm/tpm_eventlog.c b/drivers/char/tpm/tpm_eventlog.c
index 4e6940acf639..e7228863290e 100644
--- a/drivers/char/tpm/tpm_eventlog.c
+++ b/drivers/char/tpm/tpm_eventlog.c
@@ -403,7 +403,7 @@ static int is_bad(void *p)
return 0;
}
-struct dentry **tpm_bios_log_setup(char *name)
+struct dentry **tpm_bios_log_setup(const char *name)
{
struct dentry **ret = NULL, *tpm_dir, *bin_file, *ascii_file;
diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h
index 267bfbd1b7bb..8de62b09be51 100644
--- a/drivers/char/tpm/tpm_eventlog.h
+++ b/drivers/char/tpm/tpm_eventlog.h
@@ -77,10 +77,10 @@ int read_log(struct tpm_bios_log *log);
#if defined(CONFIG_TCG_IBMVTPM) || defined(CONFIG_TCG_IBMVTPM_MODULE) || \
defined(CONFIG_ACPI)
-extern struct dentry **tpm_bios_log_setup(char *);
+extern struct dentry **tpm_bios_log_setup(const char *);
extern void tpm_bios_log_teardown(struct dentry **);
#else
-static inline struct dentry **tpm_bios_log_setup(char *name)
+static inline struct dentry **tpm_bios_log_setup(const char *name)
{
return NULL;
}
diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c
index 8dfb88b9739c..95ce2e9ccdc6 100644
--- a/drivers/char/tpm/tpm_i2c_atmel.c
+++ b/drivers/char/tpm/tpm_i2c_atmel.c
@@ -51,8 +51,8 @@ struct priv_data {
static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
{
- struct priv_data *priv = chip->vendor.priv;
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct priv_data *priv = dev_get_drvdata(&chip->dev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
s32 status;
priv->len = 0;
@@ -62,7 +62,7 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
status = i2c_master_send(client, buf, len);
- dev_dbg(chip->pdev,
+ dev_dbg(&chip->dev,
"%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__,
(int)min_t(size_t, 64, len), buf, len, status);
return status;
@@ -70,8 +70,8 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
- struct priv_data *priv = chip->vendor.priv;
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct priv_data *priv = dev_get_drvdata(&chip->dev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
struct tpm_output_header *hdr =
(struct tpm_output_header *)priv->buffer;
u32 expected_len;
@@ -88,7 +88,7 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
return -ENOMEM;
if (priv->len >= expected_len) {
- dev_dbg(chip->pdev,
+ dev_dbg(&chip->dev,
"%s early(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
(int)min_t(size_t, 64, expected_len), buf, count,
expected_len);
@@ -97,7 +97,7 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
}
rc = i2c_master_recv(client, buf, expected_len);
- dev_dbg(chip->pdev,
+ dev_dbg(&chip->dev,
"%s reread(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
(int)min_t(size_t, 64, expected_len), buf, count,
expected_len);
@@ -106,13 +106,13 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
static void i2c_atmel_cancel(struct tpm_chip *chip)
{
- dev_err(chip->pdev, "TPM operation cancellation was requested, but is not supported");
+ dev_err(&chip->dev, "TPM operation cancellation was requested, but is not supported");
}
static u8 i2c_atmel_read_status(struct tpm_chip *chip)
{
- struct priv_data *priv = chip->vendor.priv;
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct priv_data *priv = dev_get_drvdata(&chip->dev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
int rc;
/* The TPM fails the I2C read until it is ready, so we do the entire
@@ -125,7 +125,7 @@ static u8 i2c_atmel_read_status(struct tpm_chip *chip)
/* Once the TPM has completed the command the command remains readable
* until another command is issued. */
rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer));
- dev_dbg(chip->pdev,
+ dev_dbg(&chip->dev,
"%s: sts=%d", __func__, rc);
if (rc <= 0)
return 0;
@@ -141,6 +141,7 @@ static bool i2c_atmel_req_canceled(struct tpm_chip *chip, u8 status)
}
static const struct tpm_class_ops i2c_atmel = {
+ .flags = TPM_OPS_AUTO_STARTUP,
.status = i2c_atmel_read_status,
.recv = i2c_atmel_recv,
.send = i2c_atmel_send,
@@ -155,6 +156,7 @@ static int i2c_atmel_probe(struct i2c_client *client,
{
struct tpm_chip *chip;
struct device *dev = &client->dev;
+ struct priv_data *priv;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
@@ -163,26 +165,21 @@ static int i2c_atmel_probe(struct i2c_client *client,
if (IS_ERR(chip))
return PTR_ERR(chip);
- chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data),
- GFP_KERNEL);
- if (!chip->vendor.priv)
+ priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL);
+ if (!priv)
return -ENOMEM;
/* Default timeouts */
- chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
- chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
- chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
- chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
- chip->vendor.irq = 0;
+ chip->timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+ chip->timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
+ chip->timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+ chip->timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+
+ dev_set_drvdata(&chip->dev, priv);
/* There is no known way to probe for this device, and all version
* information seems to be read via TPM commands. Thus we rely on the
* TPM startup process in the common code to detect the device. */
- if (tpm_get_timeouts(chip))
- return -ENODEV;
-
- if (tpm_do_selftest(chip))
- return -ENODEV;
return tpm_chip_register(chip);
}
diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c
index 63d5d22e9e60..62ee44e57ddc 100644
--- a/drivers/char/tpm/tpm_i2c_infineon.c
+++ b/drivers/char/tpm/tpm_i2c_infineon.c
@@ -66,6 +66,7 @@ enum i2c_chip_type {
/* Structure to store I2C TPM specific stuff */
struct tpm_inf_dev {
struct i2c_client *client;
+ int locality;
u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */
struct tpm_chip *chip;
enum i2c_chip_type chip_type;
@@ -288,7 +289,7 @@ static int check_locality(struct tpm_chip *chip, int loc)
if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
- chip->vendor.locality = loc;
+ tpm_dev.locality = loc;
return loc;
}
@@ -320,7 +321,7 @@ static int request_locality(struct tpm_chip *chip, int loc)
iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
/* wait for burstcount */
- stop = jiffies + chip->vendor.timeout_a;
+ stop = jiffies + chip->timeout_a;
do {
if (check_locality(chip, loc) >= 0)
return loc;
@@ -337,7 +338,7 @@ static u8 tpm_tis_i2c_status(struct tpm_chip *chip)
u8 i = 0;
do {
- if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)
+ if (iic_tpm_read(TPM_STS(tpm_dev.locality), &buf, 1) < 0)
return 0;
i++;
@@ -351,7 +352,7 @@ static void tpm_tis_i2c_ready(struct tpm_chip *chip)
{
/* this causes the current command to be aborted */
u8 buf = TPM_STS_COMMAND_READY;
- iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);
+ iic_tpm_write_long(TPM_STS(tpm_dev.locality), &buf, 1);
}
static ssize_t get_burstcount(struct tpm_chip *chip)
@@ -362,10 +363,10 @@ static ssize_t get_burstcount(struct tpm_chip *chip)
/* wait for burstcount */
/* which timeout value, spec has 2 answers (c & d) */
- stop = jiffies + chip->vendor.timeout_d;
+ stop = jiffies + chip->timeout_d;
do {
/* Note: STS is little endian */
- if (iic_tpm_read(TPM_STS(chip->vendor.locality)+1, buf, 3) < 0)
+ if (iic_tpm_read(TPM_STS(tpm_dev.locality)+1, buf, 3) < 0)
burstcnt = 0;
else
burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
@@ -419,7 +420,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
if (burstcnt > (count - size))
burstcnt = count - size;
- rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality),
+ rc = iic_tpm_read(TPM_DATA_FIFO(tpm_dev.locality),
&(buf[size]), burstcnt);
if (rc == 0)
size += burstcnt;
@@ -446,7 +447,7 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
/* read first 10 bytes, including tag, paramsize, and result */
size = recv_data(chip, buf, TPM_HEADER_SIZE);
if (size < TPM_HEADER_SIZE) {
- dev_err(chip->pdev, "Unable to read header\n");
+ dev_err(&chip->dev, "Unable to read header\n");
goto out;
}
@@ -459,14 +460,14 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
size += recv_data(chip, &buf[TPM_HEADER_SIZE],
expected - TPM_HEADER_SIZE);
if (size < expected) {
- dev_err(chip->pdev, "Unable to read remainder of result\n");
+ dev_err(&chip->dev, "Unable to read remainder of result\n");
size = -ETIME;
goto out;
}
- wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
+ wait_for_stat(chip, TPM_STS_VALID, chip->timeout_c, &status);
if (status & TPM_STS_DATA_AVAIL) { /* retry? */
- dev_err(chip->pdev, "Error left over data\n");
+ dev_err(&chip->dev, "Error left over data\n");
size = -EIO;
goto out;
}
@@ -477,7 +478,7 @@ out:
* so we sleep rather than keeping the bus busy
*/
usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
- release_locality(chip, chip->vendor.locality, 0);
+ release_locality(chip, tpm_dev.locality, 0);
return size;
}
@@ -500,7 +501,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
tpm_tis_i2c_ready(chip);
if (wait_for_stat
(chip, TPM_STS_COMMAND_READY,
- chip->vendor.timeout_b, &status) < 0) {
+ chip->timeout_b, &status) < 0) {
rc = -ETIME;
goto out_err;
}
@@ -516,7 +517,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
if (burstcnt > (len - 1 - count))
burstcnt = len - 1 - count;
- rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality),
+ rc = iic_tpm_write(TPM_DATA_FIFO(tpm_dev.locality),
&(buf[count]), burstcnt);
if (rc == 0)
count += burstcnt;
@@ -530,7 +531,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
}
wait_for_stat(chip, TPM_STS_VALID,
- chip->vendor.timeout_c, &status);
+ chip->timeout_c, &status);
if ((status & TPM_STS_DATA_EXPECT) == 0) {
rc = -EIO;
@@ -539,15 +540,15 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
}
/* write last byte */
- iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);
- wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
+ iic_tpm_write(TPM_DATA_FIFO(tpm_dev.locality), &(buf[count]), 1);
+ wait_for_stat(chip, TPM_STS_VALID, chip->timeout_c, &status);
if ((status & TPM_STS_DATA_EXPECT) != 0) {
rc = -EIO;
goto out_err;
}
/* go and do it */
- iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1);
+ iic_tpm_write(TPM_STS(tpm_dev.locality), &sts, 1);
return len;
out_err:
@@ -556,7 +557,7 @@ out_err:
* so we sleep rather than keeping the bus busy
*/
usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
- release_locality(chip, chip->vendor.locality, 0);
+ release_locality(chip, tpm_dev.locality, 0);
return rc;
}
@@ -566,6 +567,7 @@ static bool tpm_tis_i2c_req_canceled(struct tpm_chip *chip, u8 status)
}
static const struct tpm_class_ops tpm_tis_i2c = {
+ .flags = TPM_OPS_AUTO_STARTUP,
.status = tpm_tis_i2c_status,
.recv = tpm_tis_i2c_recv,
.send = tpm_tis_i2c_send,
@@ -585,14 +587,11 @@ static int tpm_tis_i2c_init(struct device *dev)
if (IS_ERR(chip))
return PTR_ERR(chip);
- /* Disable interrupts */
- chip->vendor.irq = 0;
-
/* Default timeouts */
- chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
- chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
- chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
- chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ chip->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+ chip->timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+ chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
if (request_locality(chip, 0) != 0) {
dev_err(dev, "could not request locality\n");
@@ -619,15 +618,11 @@ static int tpm_tis_i2c_init(struct device *dev)
dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16);
- INIT_LIST_HEAD(&chip->vendor.list);
tpm_dev.chip = chip;
- tpm_get_timeouts(chip);
- tpm_do_selftest(chip);
-
return tpm_chip_register(chip);
out_release:
- release_locality(chip, chip->vendor.locality, 1);
+ release_locality(chip, tpm_dev.locality, 1);
tpm_dev.client = NULL;
out_err:
return rc;
@@ -699,7 +694,7 @@ static int tpm_tis_i2c_remove(struct i2c_client *client)
struct tpm_chip *chip = tpm_dev.chip;
tpm_chip_unregister(chip);
- release_locality(chip, chip->vendor.locality, 1);
+ release_locality(chip, tpm_dev.locality, 1);
tpm_dev.client = NULL;
return 0;
diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c
index 847f1597fe9b..e3a9155ee671 100644
--- a/drivers/char/tpm/tpm_i2c_nuvoton.c
+++ b/drivers/char/tpm/tpm_i2c_nuvoton.c
@@ -1,5 +1,5 @@
-/******************************************************************************
- * Nuvoton TPM I2C Device Driver Interface for WPCT301/NPCT501,
+ /******************************************************************************
+ * Nuvoton TPM I2C Device Driver Interface for WPCT301/NPCT501/NPCT6XX,
* based on the TCG TPM Interface Spec version 1.2.
* Specifications at www.trustedcomputinggroup.org
*
@@ -31,6 +31,7 @@
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/i2c.h>
+#include <linux/of_device.h>
#include "tpm.h"
/* I2C interface offsets */
@@ -52,10 +53,13 @@
#define TPM_I2C_RETRY_DELAY_SHORT 2 /* msec */
#define TPM_I2C_RETRY_DELAY_LONG 10 /* msec */
-#define I2C_DRIVER_NAME "tpm_i2c_nuvoton"
+#define OF_IS_TPM2 ((void *)1)
+#define I2C_IS_TPM2 1
struct priv_data {
+ int irq;
unsigned int intrs;
+ wait_queue_head_t read_queue;
};
static s32 i2c_nuvoton_read_buf(struct i2c_client *client, u8 offset, u8 size,
@@ -96,13 +100,13 @@ static s32 i2c_nuvoton_write_buf(struct i2c_client *client, u8 offset, u8 size,
/* read TPM_STS register */
static u8 i2c_nuvoton_read_status(struct tpm_chip *chip)
{
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
s32 status;
u8 data;
status = i2c_nuvoton_read_buf(client, TPM_STS, 1, &data);
if (status <= 0) {
- dev_err(chip->pdev, "%s() error return %d\n", __func__,
+ dev_err(&chip->dev, "%s() error return %d\n", __func__,
status);
data = TPM_STS_ERR_VAL;
}
@@ -127,13 +131,13 @@ static s32 i2c_nuvoton_write_status(struct i2c_client *client, u8 data)
/* write commandReady to TPM_STS register */
static void i2c_nuvoton_ready(struct tpm_chip *chip)
{
- struct i2c_client *client = to_i2c_client(chip->pdev);
+ struct i2c_client *client = to_i2c_client(chip->dev.parent);
s32 status;
/* this causes the current command to be aborted */
status = i2c_nuvoton_write_status(client, TPM_STS_COMMAND_READY);
if (status < 0)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"%s() fail to write TPM_STS.commandReady\n", __func__);
}
@@ -142,7 +146,7 @@ static void i2c_nuvoton_ready(struct tpm_chip *chip)
static int i2c_nuvoton_get_burstcount(struct i2c_client *client,
struct tpm_chip *chip)
{
- unsigned long stop = jiffies + chip->vendor.timeout_d;
+ unsigned long stop = jiffies + chip->timeout_d;
s32 status;
int burst_count = -1;
u8 data;
@@ -163,7 +167,7 @@ static int i2c_nuvoton_get_burstcount(struct i2c_client *client,
}
/*
- * WPCT301/NPCT501 SINT# supports only dataAvail
+ * WPCT301/NPCT501/NPCT6XX SINT# supports only dataAvail
* any call to this function which is not waiting for dataAvail will
* set queue to NULL to avoid waiting for interrupt
*/
@@ -176,12 +180,12 @@ static bool i2c_nuvoton_check_status(struct tpm_chip *chip, u8 mask, u8 value)
static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value,
u32 timeout, wait_queue_head_t *queue)
{
- if (chip->vendor.irq && queue) {
+ if ((chip->flags & TPM_CHIP_FLAG_IRQ) && queue) {
s32 rc;
- struct priv_data *priv = chip->vendor.priv;
+ struct priv_data *priv = dev_get_drvdata(&chip->dev);
unsigned int cur_intrs = priv->intrs;
- enable_irq(chip->vendor.irq);
+ enable_irq(priv->irq);
rc = wait_event_interruptible_timeout(*queue,
cur_intrs != priv->intrs,
timeout);
@@ -212,7 +216,7 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value,
return 0;
} while (time_before(jiffies, stop));
}
- dev_err(chip->pdev, "%s(%02x, %02x) -> timeout\n", __func__, mask,
+ dev_err(&chip->dev, "%s(%02x, %02x) -> timeout\n", __func__, mask,
value);
return -ETIMEDOUT;
}
@@ -231,16 +235,17 @@ static int i2c_nuvoton_wait_for_data_avail(struct tpm_chip *chip, u32 timeout,
static int i2c_nuvoton_recv_data(struct i2c_client *client,
struct tpm_chip *chip, u8 *buf, size_t count)
{
+ struct priv_data *priv = dev_get_drvdata(&chip->dev);
s32 rc;
int burst_count, bytes2read, size = 0;
while (size < count &&
i2c_nuvoton_wait_for_data_avail(chip,
- chip->vendor.timeout_c,
- &chip->vendor.read_queue) == 0) {
+ chip->timeout_c,
+ &priv->read_queue) == 0) {
burst_count = i2c_nuvoton_get_burstcount(client, chip);
if (burst_count < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"%s() fail to read burstCount=%d\n", __func__,
burst_count);
return -EIO;
@@ -249,12 +254,12 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client,
rc = i2c_nuvoton_read_buf(client, TPM_DATA_FIFO_R,
bytes2read, &buf[size]);
if (rc < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"%s() fail on i2c_nuvoton_read_buf()=%d\n",
__func__, rc);
return -EIO;
}
- dev_dbg(chip->pdev, "%s(%d):", __func__, bytes2read);
+ dev_dbg(&chip->dev, "%s(%d):", __func__, bytes2read);
size += bytes2read;
}
@@ -264,7 +269,8 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client,
/* Read TPM command results */
static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
- struct device *dev = chip->pdev;
+ struct priv_data *priv = dev_get_drvdata(&chip->dev);
+ struct device *dev = chip->dev.parent;
struct i2c_client *client = to_i2c_client(dev);
s32 rc;
int expected, status, burst_count, retries, size = 0;
@@ -285,7 +291,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
* tag, paramsize, and result
*/
status = i2c_nuvoton_wait_for_data_avail(
- chip, chip->vendor.timeout_c, &chip->vendor.read_queue);
+ chip, chip->timeout_c, &priv->read_queue);
if (status != 0) {
dev_err(dev, "%s() timeout on dataAvail\n", __func__);
size = -ETIMEDOUT;
@@ -325,7 +331,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
}
if (i2c_nuvoton_wait_for_stat(
chip, TPM_STS_VALID | TPM_STS_DATA_AVAIL,
- TPM_STS_VALID, chip->vendor.timeout_c,
+ TPM_STS_VALID, chip->timeout_c,
NULL)) {
dev_err(dev, "%s() error left over data\n", __func__);
size = -ETIMEDOUT;
@@ -334,7 +340,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
break;
}
i2c_nuvoton_ready(chip);
- dev_dbg(chip->pdev, "%s() -> %d\n", __func__, size);
+ dev_dbg(&chip->dev, "%s() -> %d\n", __func__, size);
return size;
}
@@ -347,7 +353,8 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
*/
static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
{
- struct device *dev = chip->pdev;
+ struct priv_data *priv = dev_get_drvdata(&chip->dev);
+ struct device *dev = chip->dev.parent;
struct i2c_client *client = to_i2c_client(dev);
u32 ordinal;
size_t count = 0;
@@ -357,7 +364,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
i2c_nuvoton_ready(chip);
if (i2c_nuvoton_wait_for_stat(chip, TPM_STS_COMMAND_READY,
TPM_STS_COMMAND_READY,
- chip->vendor.timeout_b, NULL)) {
+ chip->timeout_b, NULL)) {
dev_err(dev, "%s() timeout on commandReady\n",
__func__);
rc = -EIO;
@@ -389,7 +396,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
TPM_STS_EXPECT,
TPM_STS_VALID |
TPM_STS_EXPECT,
- chip->vendor.timeout_c,
+ chip->timeout_c,
NULL);
if (rc < 0) {
dev_err(dev, "%s() timeout on Expect\n",
@@ -414,7 +421,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
rc = i2c_nuvoton_wait_for_stat(chip,
TPM_STS_VALID | TPM_STS_EXPECT,
TPM_STS_VALID,
- chip->vendor.timeout_c, NULL);
+ chip->timeout_c, NULL);
if (rc) {
dev_err(dev, "%s() timeout on Expect to clear\n",
__func__);
@@ -439,7 +446,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
rc = i2c_nuvoton_wait_for_data_avail(chip,
tpm_calc_ordinal_duration(chip,
ordinal),
- &chip->vendor.read_queue);
+ &priv->read_queue);
if (rc) {
dev_err(dev, "%s() timeout command duration\n", __func__);
i2c_nuvoton_ready(chip);
@@ -456,6 +463,7 @@ static bool i2c_nuvoton_req_canceled(struct tpm_chip *chip, u8 status)
}
static const struct tpm_class_ops tpm_i2c = {
+ .flags = TPM_OPS_AUTO_STARTUP,
.status = i2c_nuvoton_read_status,
.recv = i2c_nuvoton_recv,
.send = i2c_nuvoton_send,
@@ -473,11 +481,11 @@ static const struct tpm_class_ops tpm_i2c = {
static irqreturn_t i2c_nuvoton_int_handler(int dummy, void *dev_id)
{
struct tpm_chip *chip = dev_id;
- struct priv_data *priv = chip->vendor.priv;
+ struct priv_data *priv = dev_get_drvdata(&chip->dev);
priv->intrs++;
- wake_up(&chip->vendor.read_queue);
- disable_irq_nosync(chip->vendor.irq);
+ wake_up(&priv->read_queue);
+ disable_irq_nosync(priv->irq);
return IRQ_HANDLED;
}
@@ -521,6 +529,7 @@ static int i2c_nuvoton_probe(struct i2c_client *client,
int rc;
struct tpm_chip *chip;
struct device *dev = &client->dev;
+ struct priv_data *priv;
u32 vid = 0;
rc = get_vid(client, &vid);
@@ -534,46 +543,56 @@ static int i2c_nuvoton_probe(struct i2c_client *client,
if (IS_ERR(chip))
return PTR_ERR(chip);
- chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data),
- GFP_KERNEL);
- if (!chip->vendor.priv)
+ priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL);
+ if (!priv)
return -ENOMEM;
- init_waitqueue_head(&chip->vendor.read_queue);
- init_waitqueue_head(&chip->vendor.int_queue);
+ if (dev->of_node) {
+ const struct of_device_id *of_id;
+
+ of_id = of_match_device(dev->driver->of_match_table, dev);
+ if (of_id && of_id->data == OF_IS_TPM2)
+ chip->flags |= TPM_CHIP_FLAG_TPM2;
+ } else
+ if (id->driver_data == I2C_IS_TPM2)
+ chip->flags |= TPM_CHIP_FLAG_TPM2;
+
+ init_waitqueue_head(&priv->read_queue);
/* Default timeouts */
- chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
- chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
- chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
- chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+ chip->timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+ chip->timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
+ chip->timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+ chip->timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
+
+ dev_set_drvdata(&chip->dev, priv);
/*
* I2C intfcaps (interrupt capabilitieis) in the chip are hard coded to:
* TPM_INTF_INT_LEVEL_LOW | TPM_INTF_DATA_AVAIL_INT
* The IRQ should be set in the i2c_board_info (which is done
* automatically in of_i2c_register_devices, for device tree users */
- chip->vendor.irq = client->irq;
-
- if (chip->vendor.irq) {
- dev_dbg(dev, "%s() chip-vendor.irq\n", __func__);
- rc = devm_request_irq(dev, chip->vendor.irq,
+ priv->irq = client->irq;
+ if (client->irq) {
+ dev_dbg(dev, "%s() priv->irq\n", __func__);
+ rc = devm_request_irq(dev, client->irq,
i2c_nuvoton_int_handler,
IRQF_TRIGGER_LOW,
- chip->devname,
+ dev_name(&chip->dev),
chip);
if (rc) {
dev_err(dev, "%s() Unable to request irq: %d for use\n",
- __func__, chip->vendor.irq);
- chip->vendor.irq = 0;
+ __func__, priv->irq);
+ priv->irq = 0;
} else {
+ chip->flags |= TPM_CHIP_FLAG_IRQ;
/* Clear any pending interrupt */
i2c_nuvoton_ready(chip);
/* - wait for TPM_STS==0xA0 (stsValid, commandReady) */
rc = i2c_nuvoton_wait_for_stat(chip,
TPM_STS_COMMAND_READY,
TPM_STS_COMMAND_READY,
- chip->vendor.timeout_b,
+ chip->timeout_b,
NULL);
if (rc == 0) {
/*
@@ -601,25 +620,20 @@ static int i2c_nuvoton_probe(struct i2c_client *client,
}
}
- if (tpm_get_timeouts(chip))
- return -ENODEV;
-
- if (tpm_do_selftest(chip))
- return -ENODEV;
-
return tpm_chip_register(chip);
}
static int i2c_nuvoton_remove(struct i2c_client *client)
{
- struct device *dev = &(client->dev);
- struct tpm_chip *chip = dev_get_drvdata(dev);
+ struct tpm_chip *chip = i2c_get_clientdata(client);
+
tpm_chip_unregister(chip);
return 0;
}
static const struct i2c_device_id i2c_nuvoton_id[] = {
- {I2C_DRIVER_NAME, 0},
+ {"tpm_i2c_nuvoton"},
+ {"tpm2_i2c_nuvoton", .driver_data = I2C_IS_TPM2},
{}
};
MODULE_DEVICE_TABLE(i2c, i2c_nuvoton_id);
@@ -628,6 +642,7 @@ MODULE_DEVICE_TABLE(i2c, i2c_nuvoton_id);
static const struct of_device_id i2c_nuvoton_of_match[] = {
{.compatible = "nuvoton,npct501"},
{.compatible = "winbond,wpct301"},
+ {.compatible = "nuvoton,npct601", .data = OF_IS_TPM2},
{},
};
MODULE_DEVICE_TABLE(of, i2c_nuvoton_of_match);
@@ -640,7 +655,7 @@ static struct i2c_driver i2c_nuvoton_driver = {
.probe = i2c_nuvoton_probe,
.remove = i2c_nuvoton_remove,
.driver = {
- .name = I2C_DRIVER_NAME,
+ .name = "tpm_i2c_nuvoton",
.pm = &i2c_nuvoton_pm_ops,
.of_match_table = of_match_ptr(i2c_nuvoton_of_match),
},
diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c
index b0a9a9e34241..946025a7413b 100644
--- a/drivers/char/tpm/tpm_ibmvtpm.c
+++ b/drivers/char/tpm/tpm_ibmvtpm.c
@@ -54,21 +54,6 @@ static int ibmvtpm_send_crq(struct vio_dev *vdev, u64 w1, u64 w2)
}
/**
- * ibmvtpm_get_data - Retrieve ibm vtpm data
- * @dev: device struct
- *
- * Return value:
- * vtpm device struct
- */
-static struct ibmvtpm_dev *ibmvtpm_get_data(const struct device *dev)
-{
- struct tpm_chip *chip = dev_get_drvdata(dev);
- if (chip)
- return (struct ibmvtpm_dev *)TPM_VPRIV(chip);
- return NULL;
-}
-
-/**
* tpm_ibmvtpm_recv - Receive data after send
* @chip: tpm chip struct
* @buf: buffer to read
@@ -79,12 +64,10 @@ static struct ibmvtpm_dev *ibmvtpm_get_data(const struct device *dev)
*/
static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
- struct ibmvtpm_dev *ibmvtpm;
+ struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
u16 len;
int sig;
- ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip);
-
if (!ibmvtpm->rtce_buf) {
dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
return 0;
@@ -122,13 +105,11 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
*/
static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
{
- struct ibmvtpm_dev *ibmvtpm;
+ struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
struct ibmvtpm_crq crq;
__be64 *word = (__be64 *)&crq;
int rc, sig;
- ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip);
-
if (!ibmvtpm->rtce_buf) {
dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
return 0;
@@ -289,8 +270,8 @@ static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm)
*/
static int tpm_ibmvtpm_remove(struct vio_dev *vdev)
{
- struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev);
- struct tpm_chip *chip = dev_get_drvdata(ibmvtpm->dev);
+ struct tpm_chip *chip = dev_get_drvdata(&vdev->dev);
+ struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
int rc = 0;
tpm_chip_unregister(chip);
@@ -327,7 +308,8 @@ static int tpm_ibmvtpm_remove(struct vio_dev *vdev)
*/
static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev)
{
- struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev);
+ struct tpm_chip *chip = dev_get_drvdata(&vdev->dev);
+ struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
/* ibmvtpm initializes at probe time, so the data we are
* asking for may not be set yet. Estimate that 4K required
@@ -348,7 +330,8 @@ static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev)
*/
static int tpm_ibmvtpm_suspend(struct device *dev)
{
- struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev);
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+ struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
struct ibmvtpm_crq crq;
u64 *buf = (u64 *) &crq;
int rc = 0;
@@ -400,7 +383,8 @@ static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm)
*/
static int tpm_ibmvtpm_resume(struct device *dev)
{
- struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev);
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+ struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
int rc = 0;
do {
@@ -643,7 +627,7 @@ static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
crq_q->index = 0;
- TPM_VPRIV(chip) = (void *)ibmvtpm;
+ dev_set_drvdata(&chip->dev, ibmvtpm);
spin_lock_init(&ibmvtpm->rtce_lock);
diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c
index 6c488e635fdd..e3cf9f3545c5 100644
--- a/drivers/char/tpm/tpm_infineon.c
+++ b/drivers/char/tpm/tpm_infineon.c
@@ -195,9 +195,9 @@ static int wait(struct tpm_chip *chip, int wait_for_bit)
}
if (i == TPM_MAX_TRIES) { /* timeout occurs */
if (wait_for_bit == STAT_XFE)
- dev_err(chip->pdev, "Timeout in wait(STAT_XFE)\n");
+ dev_err(&chip->dev, "Timeout in wait(STAT_XFE)\n");
if (wait_for_bit == STAT_RDA)
- dev_err(chip->pdev, "Timeout in wait(STAT_RDA)\n");
+ dev_err(&chip->dev, "Timeout in wait(STAT_RDA)\n");
return -EIO;
}
return 0;
@@ -220,7 +220,7 @@ static void wait_and_send(struct tpm_chip *chip, u8 sendbyte)
static void tpm_wtx(struct tpm_chip *chip)
{
number_of_wtx++;
- dev_info(chip->pdev, "Granting WTX (%02d / %02d)\n",
+ dev_info(&chip->dev, "Granting WTX (%02d / %02d)\n",
number_of_wtx, TPM_MAX_WTX_PACKAGES);
wait_and_send(chip, TPM_VL_VER);
wait_and_send(chip, TPM_CTRL_WTX);
@@ -231,7 +231,7 @@ static void tpm_wtx(struct tpm_chip *chip)
static void tpm_wtx_abort(struct tpm_chip *chip)
{
- dev_info(chip->pdev, "Aborting WTX\n");
+ dev_info(&chip->dev, "Aborting WTX\n");
wait_and_send(chip, TPM_VL_VER);
wait_and_send(chip, TPM_CTRL_WTX_ABORT);
wait_and_send(chip, 0x00);
@@ -257,7 +257,7 @@ recv_begin:
}
if (buf[0] != TPM_VL_VER) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"Wrong transport protocol implementation!\n");
return -EIO;
}
@@ -272,7 +272,7 @@ recv_begin:
}
if ((size == 0x6D00) && (buf[1] == 0x80)) {
- dev_err(chip->pdev, "Error handling on vendor layer!\n");
+ dev_err(&chip->dev, "Error handling on vendor layer!\n");
return -EIO;
}
@@ -284,7 +284,7 @@ recv_begin:
}
if (buf[1] == TPM_CTRL_WTX) {
- dev_info(chip->pdev, "WTX-package received\n");
+ dev_info(&chip->dev, "WTX-package received\n");
if (number_of_wtx < TPM_MAX_WTX_PACKAGES) {
tpm_wtx(chip);
goto recv_begin;
@@ -295,14 +295,14 @@ recv_begin:
}
if (buf[1] == TPM_CTRL_WTX_ABORT_ACK) {
- dev_info(chip->pdev, "WTX-abort acknowledged\n");
+ dev_info(&chip->dev, "WTX-abort acknowledged\n");
return size;
}
if (buf[1] == TPM_CTRL_ERROR) {
- dev_err(chip->pdev, "ERROR-package received:\n");
+ dev_err(&chip->dev, "ERROR-package received:\n");
if (buf[4] == TPM_INF_NAK)
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"-> Negative acknowledgement"
" - retransmit command!\n");
return -EIO;
@@ -321,7 +321,7 @@ static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count)
ret = empty_fifo(chip, 1);
if (ret) {
- dev_err(chip->pdev, "Timeout while clearing FIFO\n");
+ dev_err(&chip->dev, "Timeout while clearing FIFO\n");
return -EIO;
}
diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c
index 289389ecef84..9ff0e072c476 100644
--- a/drivers/char/tpm/tpm_nsc.c
+++ b/drivers/char/tpm/tpm_nsc.c
@@ -64,15 +64,21 @@ enum tpm_nsc_cmd_mode {
NSC_COMMAND_EOC = 0x03,
NSC_COMMAND_CANCEL = 0x22
};
+
+struct tpm_nsc_priv {
+ unsigned long base;
+};
+
/*
* Wait for a certain status to appear
*/
static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
{
+ struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
unsigned long stop;
/* status immediately available check */
- *data = inb(chip->vendor.base + NSC_STATUS);
+ *data = inb(priv->base + NSC_STATUS);
if ((*data & mask) == val)
return 0;
@@ -80,7 +86,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
stop = jiffies + 10 * HZ;
do {
msleep(TPM_TIMEOUT);
- *data = inb(chip->vendor.base + 1);
+ *data = inb(priv->base + 1);
if ((*data & mask) == val)
return 0;
}
@@ -91,13 +97,14 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
static int nsc_wait_for_ready(struct tpm_chip *chip)
{
+ struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
int status;
unsigned long stop;
/* status immediately available check */
- status = inb(chip->vendor.base + NSC_STATUS);
+ status = inb(priv->base + NSC_STATUS);
if (status & NSC_STATUS_OBF)
- status = inb(chip->vendor.base + NSC_DATA);
+ status = inb(priv->base + NSC_DATA);
if (status & NSC_STATUS_RDY)
return 0;
@@ -105,21 +112,22 @@ static int nsc_wait_for_ready(struct tpm_chip *chip)
stop = jiffies + 100;
do {
msleep(TPM_TIMEOUT);
- status = inb(chip->vendor.base + NSC_STATUS);
+ status = inb(priv->base + NSC_STATUS);
if (status & NSC_STATUS_OBF)
- status = inb(chip->vendor.base + NSC_DATA);
+ status = inb(priv->base + NSC_DATA);
if (status & NSC_STATUS_RDY)
return 0;
}
while (time_before(jiffies, stop));
- dev_info(chip->pdev, "wait for ready failed\n");
+ dev_info(&chip->dev, "wait for ready failed\n");
return -EBUSY;
}
static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
{
+ struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
u8 *buffer = buf;
u8 data, *p;
u32 size;
@@ -129,12 +137,13 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
return -EIO;
if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
- dev_err(chip->pdev, "F0 timeout\n");
+ dev_err(&chip->dev, "F0 timeout\n");
return -EIO;
}
- if ((data =
- inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
- dev_err(chip->pdev, "not in normal mode (0x%x)\n",
+
+ data = inb(priv->base + NSC_DATA);
+ if (data != NSC_COMMAND_NORMAL) {
+ dev_err(&chip->dev, "not in normal mode (0x%x)\n",
data);
return -EIO;
}
@@ -143,22 +152,24 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
for (p = buffer; p < &buffer[count]; p++) {
if (wait_for_stat
(chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"OBF timeout (while reading data)\n");
return -EIO;
}
if (data & NSC_STATUS_F0)
break;
- *p = inb(chip->vendor.base + NSC_DATA);
+ *p = inb(priv->base + NSC_DATA);
}
if ((data & NSC_STATUS_F0) == 0 &&
(wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0)) {
- dev_err(chip->pdev, "F0 not set\n");
+ dev_err(&chip->dev, "F0 not set\n");
return -EIO;
}
- if ((data = inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_EOC) {
- dev_err(chip->pdev,
+
+ data = inb(priv->base + NSC_DATA);
+ if (data != NSC_COMMAND_EOC) {
+ dev_err(&chip->dev,
"expected end of command(0x%x)\n", data);
return -EIO;
}
@@ -174,6 +185,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
{
+ struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
u8 data;
int i;
@@ -183,48 +195,52 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
* fix it. Not sure why this is needed, we followed the flow
* chart in the manual to the letter.
*/
- outb(NSC_COMMAND_CANCEL, chip->vendor.base + NSC_COMMAND);
+ outb(NSC_COMMAND_CANCEL, priv->base + NSC_COMMAND);
if (nsc_wait_for_ready(chip) != 0)
return -EIO;
if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
- dev_err(chip->pdev, "IBF timeout\n");
+ dev_err(&chip->dev, "IBF timeout\n");
return -EIO;
}
- outb(NSC_COMMAND_NORMAL, chip->vendor.base + NSC_COMMAND);
+ outb(NSC_COMMAND_NORMAL, priv->base + NSC_COMMAND);
if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
- dev_err(chip->pdev, "IBR timeout\n");
+ dev_err(&chip->dev, "IBR timeout\n");
return -EIO;
}
for (i = 0; i < count; i++) {
if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
- dev_err(chip->pdev,
+ dev_err(&chip->dev,
"IBF timeout (while writing data)\n");
return -EIO;
}
- outb(buf[i], chip->vendor.base + NSC_DATA);
+ outb(buf[i], priv->base + NSC_DATA);
}
if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
- dev_err(chip->pdev, "IBF timeout\n");
+ dev_err(&chip->dev, "IBF timeout\n");
return -EIO;
}
- outb(NSC_COMMAND_EOC, chip->vendor.base + NSC_COMMAND);
+ outb(NSC_COMMAND_EOC, priv->base + NSC_COMMAND);
return count;
}
static void tpm_nsc_cancel(struct tpm_chip *chip)
{
- outb(NSC_COMMAND_CANCEL, chip->vendor.base + NSC_COMMAND);
+ struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
+
+ outb(NSC_COMMAND_CANCEL, priv->base + NSC_COMMAND);
}
static u8 tpm_nsc_status(struct tpm_chip *chip)
{
- return inb(chip->vendor.base + NSC_STATUS);
+ struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
+
+ return inb(priv->base + NSC_STATUS);
}
static bool tpm_nsc_req_canceled(struct tpm_chip *chip, u8 status)
@@ -247,9 +263,10 @@ static struct platform_device *pdev = NULL;
static void tpm_nsc_remove(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
+ struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
tpm_chip_unregister(chip);
- release_region(chip->vendor.base, 2);
+ release_region(priv->base, 2);
}
static SIMPLE_DEV_PM_OPS(tpm_nsc_pm, tpm_pm_suspend, tpm_pm_resume);
@@ -268,6 +285,7 @@ static int __init init_nsc(void)
int nscAddrBase = TPM_ADDR;
struct tpm_chip *chip;
unsigned long base;
+ struct tpm_nsc_priv *priv;
/* verify that it is a National part (SID) */
if (tpm_read_index(TPM_ADDR, NSC_SID_INDEX) != 0xEF) {
@@ -301,6 +319,14 @@ static int __init init_nsc(void)
if ((rc = platform_device_add(pdev)) < 0)
goto err_put_dev;
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ rc = -ENOMEM;
+ goto err_del_dev;
+ }
+
+ priv->base = base;
+
if (request_region(base, 2, "tpm_nsc0") == NULL ) {
rc = -EBUSY;
goto err_del_dev;
@@ -312,6 +338,8 @@ static int __init init_nsc(void)
goto err_rel_reg;
}
+ dev_set_drvdata(&chip->dev, priv);
+
rc = tpm_chip_register(chip);
if (rc)
goto err_rel_reg;
@@ -349,8 +377,6 @@ static int __init init_nsc(void)
"NSC TPM revision %d\n",
tpm_read_index(nscAddrBase, 0x27) & 0x1F);
- chip->vendor.base = base;
-
return 0;
err_rel_reg:
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index a507006728e0..eaf5730d79eb 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -29,40 +29,7 @@
#include <linux/acpi.h>
#include <linux/freezer.h>
#include "tpm.h"
-
-enum tis_access {
- TPM_ACCESS_VALID = 0x80,
- TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
- TPM_ACCESS_REQUEST_PENDING = 0x04,
- TPM_ACCESS_REQUEST_USE = 0x02,
-};
-
-enum tis_status {
- TPM_STS_VALID = 0x80,
- TPM_STS_COMMAND_READY = 0x40,
- TPM_STS_GO = 0x20,
- TPM_STS_DATA_AVAIL = 0x10,
- TPM_STS_DATA_EXPECT = 0x08,
-};
-
-enum tis_int_flags {
- TPM_GLOBAL_INT_ENABLE = 0x80000000,
- TPM_INTF_BURST_COUNT_STATIC = 0x100,
- TPM_INTF_CMD_READY_INT = 0x080,
- TPM_INTF_INT_EDGE_FALLING = 0x040,
- TPM_INTF_INT_EDGE_RISING = 0x020,
- TPM_INTF_INT_LEVEL_LOW = 0x010,
- TPM_INTF_INT_LEVEL_HIGH = 0x008,
- TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
- TPM_INTF_STS_VALID_INT = 0x002,
- TPM_INTF_DATA_AVAIL_INT = 0x001,
-};
-
-enum tis_defaults {
- TIS_MEM_LEN = 0x5000,
- TIS_SHORT_TIMEOUT = 750, /* ms */
- TIS_LONG_TIMEOUT = 2000, /* 2 sec */
-};
+#include "tpm_tis_core.h"
struct tpm_info {
struct resource res;
@@ -73,30 +40,30 @@ struct tpm_info {
int irq;
};
-/* Some timeout values are needed before it is known whether the chip is
- * TPM 1.0 or TPM 2.0.
- */
-#define TIS_TIMEOUT_A_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A)
-#define TIS_TIMEOUT_B_MAX max(TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B)
-#define TIS_TIMEOUT_C_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C)
-#define TIS_TIMEOUT_D_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D)
-
-#define TPM_ACCESS(l) (0x0000 | ((l) << 12))
-#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12))
-#define TPM_INT_VECTOR(l) (0x000C | ((l) << 12))
-#define TPM_INT_STATUS(l) (0x0010 | ((l) << 12))
-#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12))
-#define TPM_STS(l) (0x0018 | ((l) << 12))
-#define TPM_STS3(l) (0x001b | ((l) << 12))
-#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12))
-
-#define TPM_DID_VID(l) (0x0F00 | ((l) << 12))
-#define TPM_RID(l) (0x0F04 | ((l) << 12))
-
-struct priv_data {
- bool irq_tested;
+struct tpm_tis_tcg_phy {
+ struct tpm_tis_data priv;
+ void __iomem *iobase;
};
+static inline struct tpm_tis_tcg_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *data)
+{
+ return container_of(data, struct tpm_tis_tcg_phy, priv);
+}
+
+static bool interrupts = true;
+module_param(interrupts, bool, 0444);
+MODULE_PARM_DESC(interrupts, "Enable interrupts");
+
+static bool itpm;
+module_param(itpm, bool, 0444);
+MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
+
+static bool force;
+#ifdef CONFIG_X86
+module_param(force, bool, 0444);
+MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry");
+#endif
+
#if defined(CONFIG_PNP) && defined(CONFIG_ACPI)
static int has_hid(struct acpi_device *dev, const char *hid)
{
@@ -120,744 +87,82 @@ static inline int is_itpm(struct acpi_device *dev)
}
#endif
-/* Before we attempt to access the TPM we must see that the valid bit is set.
- * The specification says that this bit is 0 at reset and remains 0 until the
- * 'TPM has gone through its self test and initialization and has established
- * correct values in the other bits.' */
-static int wait_startup(struct tpm_chip *chip, int l)
-{
- unsigned long stop = jiffies + chip->vendor.timeout_a;
- do {
- if (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
- TPM_ACCESS_VALID)
- return 0;
- msleep(TPM_TIMEOUT);
- } while (time_before(jiffies, stop));
- return -1;
-}
-
-static int check_locality(struct tpm_chip *chip, int l)
-{
- if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
- (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
- (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
- return chip->vendor.locality = l;
-
- return -1;
-}
-
-static void release_locality(struct tpm_chip *chip, int l, int force)
-{
- if (force || (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
- (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
- (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID))
- iowrite8(TPM_ACCESS_ACTIVE_LOCALITY,
- chip->vendor.iobase + TPM_ACCESS(l));
-}
-
-static int request_locality(struct tpm_chip *chip, int l)
-{
- unsigned long stop, timeout;
- long rc;
-
- if (check_locality(chip, l) >= 0)
- return l;
-
- iowrite8(TPM_ACCESS_REQUEST_USE,
- chip->vendor.iobase + TPM_ACCESS(l));
-
- stop = jiffies + chip->vendor.timeout_a;
-
- if (chip->vendor.irq) {
-again:
- timeout = stop - jiffies;
- if ((long)timeout <= 0)
- return -1;
- rc = wait_event_interruptible_timeout(chip->vendor.int_queue,
- (check_locality
- (chip, l) >= 0),
- timeout);
- if (rc > 0)
- return l;
- if (rc == -ERESTARTSYS && freezing(current)) {
- clear_thread_flag(TIF_SIGPENDING);
- goto again;
- }
- } else {
- /* wait for burstcount */
- do {
- if (check_locality(chip, l) >= 0)
- return l;
- msleep(TPM_TIMEOUT);
- }
- while (time_before(jiffies, stop));
- }
- return -1;
-}
-
-static u8 tpm_tis_status(struct tpm_chip *chip)
-{
- return ioread8(chip->vendor.iobase +
- TPM_STS(chip->vendor.locality));
-}
-
-static void tpm_tis_ready(struct tpm_chip *chip)
-{
- /* this causes the current command to be aborted */
- iowrite8(TPM_STS_COMMAND_READY,
- chip->vendor.iobase + TPM_STS(chip->vendor.locality));
-}
-
-static int get_burstcount(struct tpm_chip *chip)
-{
- unsigned long stop;
- int burstcnt;
-
- /* wait for burstcount */
- /* which timeout value, spec has 2 answers (c & d) */
- stop = jiffies + chip->vendor.timeout_d;
- do {
- burstcnt = ioread8(chip->vendor.iobase +
- TPM_STS(chip->vendor.locality) + 1);
- burstcnt += ioread8(chip->vendor.iobase +
- TPM_STS(chip->vendor.locality) +
- 2) << 8;
- if (burstcnt)
- return burstcnt;
- msleep(TPM_TIMEOUT);
- } while (time_before(jiffies, stop));
- return -EBUSY;
-}
-
-static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
+static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
+ u8 *result)
{
- int size = 0, burstcnt;
- while (size < count &&
- wait_for_tpm_stat(chip,
- TPM_STS_DATA_AVAIL | TPM_STS_VALID,
- chip->vendor.timeout_c,
- &chip->vendor.read_queue, true)
- == 0) {
- burstcnt = get_burstcount(chip);
- for (; burstcnt > 0 && size < count; burstcnt--)
- buf[size++] = ioread8(chip->vendor.iobase +
- TPM_DATA_FIFO(chip->vendor.
- locality));
- }
- return size;
-}
-
-static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
-{
- int size = 0;
- int expected, status;
-
- if (count < TPM_HEADER_SIZE) {
- size = -EIO;
- goto out;
- }
-
- /* read first 10 bytes, including tag, paramsize, and result */
- if ((size =
- recv_data(chip, buf, TPM_HEADER_SIZE)) < TPM_HEADER_SIZE) {
- dev_err(chip->pdev, "Unable to read header\n");
- goto out;
- }
-
- expected = be32_to_cpu(*(__be32 *) (buf + 2));
- if (expected > count) {
- size = -EIO;
- goto out;
- }
-
- if ((size +=
- recv_data(chip, &buf[TPM_HEADER_SIZE],
- expected - TPM_HEADER_SIZE)) < expected) {
- dev_err(chip->pdev, "Unable to read remainder of result\n");
- size = -ETIME;
- goto out;
- }
-
- wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
- &chip->vendor.int_queue, false);
- status = tpm_tis_status(chip);
- if (status & TPM_STS_DATA_AVAIL) { /* retry? */
- dev_err(chip->pdev, "Error left over data\n");
- size = -EIO;
- goto out;
- }
-
-out:
- tpm_tis_ready(chip);
- release_locality(chip, chip->vendor.locality, 0);
- return size;
-}
-
-static bool itpm;
-module_param(itpm, bool, 0444);
-MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
-
-/*
- * If interrupts are used (signaled by an irq set in the vendor structure)
- * tpm.c can skip polling for the data to be available as the interrupt is
- * waited for here
- */
-static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
-{
- int rc, status, burstcnt;
- size_t count = 0;
-
- if (request_locality(chip, 0) < 0)
- return -EBUSY;
-
- status = tpm_tis_status(chip);
- if ((status & TPM_STS_COMMAND_READY) == 0) {
- tpm_tis_ready(chip);
- if (wait_for_tpm_stat
- (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
- &chip->vendor.int_queue, false) < 0) {
- rc = -ETIME;
- goto out_err;
- }
- }
-
- while (count < len - 1) {
- burstcnt = get_burstcount(chip);
- for (; burstcnt > 0 && count < len - 1; burstcnt--) {
- iowrite8(buf[count], chip->vendor.iobase +
- TPM_DATA_FIFO(chip->vendor.locality));
- count++;
- }
-
- wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
- &chip->vendor.int_queue, false);
- status = tpm_tis_status(chip);
- if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
- rc = -EIO;
- goto out_err;
- }
- }
-
- /* write last byte */
- iowrite8(buf[count],
- chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
- wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
- &chip->vendor.int_queue, false);
- status = tpm_tis_status(chip);
- if ((status & TPM_STS_DATA_EXPECT) != 0) {
- rc = -EIO;
- goto out_err;
- }
+ struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
+ while (len--)
+ *result++ = ioread8(phy->iobase + addr);
return 0;
-
-out_err:
- tpm_tis_ready(chip);
- release_locality(chip, chip->vendor.locality, 0);
- return rc;
-}
-
-static void disable_interrupts(struct tpm_chip *chip)
-{
- u32 intmask;
-
- intmask =
- ioread32(chip->vendor.iobase +
- TPM_INT_ENABLE(chip->vendor.locality));
- intmask &= ~TPM_GLOBAL_INT_ENABLE;
- iowrite32(intmask,
- chip->vendor.iobase +
- TPM_INT_ENABLE(chip->vendor.locality));
- devm_free_irq(chip->pdev, chip->vendor.irq, chip);
- chip->vendor.irq = 0;
-}
-
-/*
- * If interrupts are used (signaled by an irq set in the vendor structure)
- * tpm.c can skip polling for the data to be available as the interrupt is
- * waited for here
- */
-static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len)
-{
- int rc;
- u32 ordinal;
- unsigned long dur;
-
- rc = tpm_tis_send_data(chip, buf, len);
- if (rc < 0)
- return rc;
-
- /* go and do it */
- iowrite8(TPM_STS_GO,
- chip->vendor.iobase + TPM_STS(chip->vendor.locality));
-
- if (chip->vendor.irq) {
- ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
-
- if (chip->flags & TPM_CHIP_FLAG_TPM2)
- dur = tpm2_calc_ordinal_duration(chip, ordinal);
- else
- dur = tpm_calc_ordinal_duration(chip, ordinal);
-
- if (wait_for_tpm_stat
- (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, dur,
- &chip->vendor.read_queue, false) < 0) {
- rc = -ETIME;
- goto out_err;
- }
- }
- return len;
-out_err:
- tpm_tis_ready(chip);
- release_locality(chip, chip->vendor.locality, 0);
- return rc;
-}
-
-static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
-{
- int rc, irq;
- struct priv_data *priv = chip->vendor.priv;
-
- if (!chip->vendor.irq || priv->irq_tested)
- return tpm_tis_send_main(chip, buf, len);
-
- /* Verify receipt of the expected IRQ */
- irq = chip->vendor.irq;
- chip->vendor.irq = 0;
- rc = tpm_tis_send_main(chip, buf, len);
- chip->vendor.irq = irq;
- if (!priv->irq_tested)
- msleep(1);
- if (!priv->irq_tested)
- disable_interrupts(chip);
- priv->irq_tested = true;
- return rc;
}
-struct tis_vendor_timeout_override {
- u32 did_vid;
- unsigned long timeout_us[4];
-};
-
-static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
- /* Atmel 3204 */
- { 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
- (TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
-};
-
-static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
- unsigned long *timeout_cap)
+static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
+ u8 *value)
{
- int i;
- u32 did_vid;
+ struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
- did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
-
- for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
- if (vendor_timeout_overrides[i].did_vid != did_vid)
- continue;
- memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
- sizeof(vendor_timeout_overrides[i].timeout_us));
- return true;
- }
-
- return false;
+ while (len--)
+ iowrite8(*value++, phy->iobase + addr);
+ return 0;
}
-/*
- * Early probing for iTPM with STS_DATA_EXPECT flaw.
- * Try sending command without itpm flag set and if that
- * fails, repeat with itpm flag set.
- */
-static int probe_itpm(struct tpm_chip *chip)
+static int tpm_tcg_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
{
- int rc = 0;
- u8 cmd_getticks[] = {
- 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a,
- 0x00, 0x00, 0x00, 0xf1
- };
- size_t len = sizeof(cmd_getticks);
- bool rem_itpm = itpm;
- u16 vendor = ioread16(chip->vendor.iobase + TPM_DID_VID(0));
-
- /* probe only iTPMS */
- if (vendor != TPM_VID_INTEL)
- return 0;
-
- itpm = false;
-
- rc = tpm_tis_send_data(chip, cmd_getticks, len);
- if (rc == 0)
- goto out;
-
- tpm_tis_ready(chip);
- release_locality(chip, chip->vendor.locality, 0);
+ struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
- itpm = true;
-
- rc = tpm_tis_send_data(chip, cmd_getticks, len);
- if (rc == 0) {
- dev_info(chip->pdev, "Detected an iTPM.\n");
- rc = 1;
- } else
- rc = -EFAULT;
-
-out:
- itpm = rem_itpm;
- tpm_tis_ready(chip);
- release_locality(chip, chip->vendor.locality, 0);
-
- return rc;
+ *result = ioread16(phy->iobase + addr);
+ return 0;
}
-static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status)
+static int tpm_tcg_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
{
- switch (chip->vendor.manufacturer_id) {
- case TPM_VID_WINBOND:
- return ((status == TPM_STS_VALID) ||
- (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY)));
- case TPM_VID_STM:
- return (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY));
- default:
- return (status == TPM_STS_COMMAND_READY);
- }
-}
-
-static const struct tpm_class_ops tpm_tis = {
- .status = tpm_tis_status,
- .recv = tpm_tis_recv,
- .send = tpm_tis_send,
- .cancel = tpm_tis_ready,
- .update_timeouts = tpm_tis_update_timeouts,
- .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
- .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
- .req_canceled = tpm_tis_req_canceled,
-};
+ struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
-static irqreturn_t tis_int_handler(int dummy, void *dev_id)
-{
- struct tpm_chip *chip = dev_id;
- u32 interrupt;
- int i;
-
- interrupt = ioread32(chip->vendor.iobase +
- TPM_INT_STATUS(chip->vendor.locality));
-
- if (interrupt == 0)
- return IRQ_NONE;
-
- ((struct priv_data *)chip->vendor.priv)->irq_tested = true;
- if (interrupt & TPM_INTF_DATA_AVAIL_INT)
- wake_up_interruptible(&chip->vendor.read_queue);
- if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT)
- for (i = 0; i < 5; i++)
- if (check_locality(chip, i) >= 0)
- break;
- if (interrupt &
- (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT |
- TPM_INTF_CMD_READY_INT))
- wake_up_interruptible(&chip->vendor.int_queue);
-
- /* Clear interrupts handled with TPM_EOI */
- iowrite32(interrupt,
- chip->vendor.iobase +
- TPM_INT_STATUS(chip->vendor.locality));
- ioread32(chip->vendor.iobase + TPM_INT_STATUS(chip->vendor.locality));
- return IRQ_HANDLED;
+ *result = ioread32(phy->iobase + addr);
+ return 0;
}
-/* Register the IRQ and issue a command that will cause an interrupt. If an
- * irq is seen then leave the chip setup for IRQ operation, otherwise reverse
- * everything and leave in polling mode. Returns 0 on success.
- */
-static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask,
- int flags, int irq)
+static int tpm_tcg_write32(struct tpm_tis_data *data, u32 addr, u32 value)
{
- struct priv_data *priv = chip->vendor.priv;
- u8 original_int_vec;
-
- if (devm_request_irq(chip->pdev, irq, tis_int_handler, flags,
- chip->devname, chip) != 0) {
- dev_info(chip->pdev, "Unable to request irq: %d for probe\n",
- irq);
- return -1;
- }
- chip->vendor.irq = irq;
-
- original_int_vec = ioread8(chip->vendor.iobase +
- TPM_INT_VECTOR(chip->vendor.locality));
- iowrite8(irq,
- chip->vendor.iobase + TPM_INT_VECTOR(chip->vendor.locality));
-
- /* Clear all existing */
- iowrite32(ioread32(chip->vendor.iobase +
- TPM_INT_STATUS(chip->vendor.locality)),
- chip->vendor.iobase + TPM_INT_STATUS(chip->vendor.locality));
-
- /* Turn on */
- iowrite32(intmask | TPM_GLOBAL_INT_ENABLE,
- chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
-
- priv->irq_tested = false;
-
- /* Generate an interrupt by having the core call through to
- * tpm_tis_send
- */
- if (chip->flags & TPM_CHIP_FLAG_TPM2)
- tpm2_gen_interrupt(chip);
- else
- tpm_gen_interrupt(chip);
-
- /* tpm_tis_send will either confirm the interrupt is working or it
- * will call disable_irq which undoes all of the above.
- */
- if (!chip->vendor.irq) {
- iowrite8(original_int_vec,
- chip->vendor.iobase +
- TPM_INT_VECTOR(chip->vendor.locality));
- return 1;
- }
+ struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
+ iowrite32(value, phy->iobase + addr);
return 0;
}
-/* Try to find the IRQ the TPM is using. This is for legacy x86 systems that
- * do not have ACPI/etc. We typically expect the interrupt to be declared if
- * present.
- */
-static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask)
-{
- u8 original_int_vec;
- int i;
-
- original_int_vec = ioread8(chip->vendor.iobase +
- TPM_INT_VECTOR(chip->vendor.locality));
-
- if (!original_int_vec) {
- if (IS_ENABLED(CONFIG_X86))
- for (i = 3; i <= 15; i++)
- if (!tpm_tis_probe_irq_single(chip, intmask, 0,
- i))
- return;
- } else if (!tpm_tis_probe_irq_single(chip, intmask, 0,
- original_int_vec))
- return;
-}
-
-static bool interrupts = true;
-module_param(interrupts, bool, 0444);
-MODULE_PARM_DESC(interrupts, "Enable interrupts");
-
-static void tpm_tis_remove(struct tpm_chip *chip)
-{
- if (chip->flags & TPM_CHIP_FLAG_TPM2)
- tpm2_shutdown(chip, TPM2_SU_CLEAR);
-
- iowrite32(~TPM_GLOBAL_INT_ENABLE &
- ioread32(chip->vendor.iobase +
- TPM_INT_ENABLE(chip->vendor.
- locality)),
- chip->vendor.iobase +
- TPM_INT_ENABLE(chip->vendor.locality));
- release_locality(chip, chip->vendor.locality, 1);
-}
+static const struct tpm_tis_phy_ops tpm_tcg = {
+ .read_bytes = tpm_tcg_read_bytes,
+ .write_bytes = tpm_tcg_write_bytes,
+ .read16 = tpm_tcg_read16,
+ .read32 = tpm_tcg_read32,
+ .write32 = tpm_tcg_write32,
+};
static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
acpi_handle acpi_dev_handle)
{
- u32 vendor, intfcaps, intmask;
- int rc, probe;
- struct tpm_chip *chip;
- struct priv_data *priv;
+ struct tpm_tis_tcg_phy *phy;
+ int irq = -1;
- priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL);
- if (priv == NULL)
+ phy = devm_kzalloc(dev, sizeof(struct tpm_tis_tcg_phy), GFP_KERNEL);
+ if (phy == NULL)
return -ENOMEM;
- chip = tpmm_chip_alloc(dev, &tpm_tis);
- if (IS_ERR(chip))
- return PTR_ERR(chip);
-
- chip->vendor.priv = priv;
-#ifdef CONFIG_ACPI
- chip->acpi_dev_handle = acpi_dev_handle;
-#endif
+ phy->iobase = devm_ioremap_resource(dev, &tpm_info->res);
+ if (IS_ERR(phy->iobase))
+ return PTR_ERR(phy->iobase);
- chip->vendor.iobase = devm_ioremap_resource(dev, &tpm_info->res);
- if (IS_ERR(chip->vendor.iobase))
- return PTR_ERR(chip->vendor.iobase);
-
- /* Maximum timeouts */
- chip->vendor.timeout_a = TIS_TIMEOUT_A_MAX;
- chip->vendor.timeout_b = TIS_TIMEOUT_B_MAX;
- chip->vendor.timeout_c = TIS_TIMEOUT_C_MAX;
- chip->vendor.timeout_d = TIS_TIMEOUT_D_MAX;
-
- if (wait_startup(chip, 0) != 0) {
- rc = -ENODEV;
- goto out_err;
- }
-
- /* Take control of the TPM's interrupt hardware and shut it off */
- intmask = ioread32(chip->vendor.iobase +
- TPM_INT_ENABLE(chip->vendor.locality));
- intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT |
- TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT;
- intmask &= ~TPM_GLOBAL_INT_ENABLE;
- iowrite32(intmask,
- chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
-
- if (request_locality(chip, 0) != 0) {
- rc = -ENODEV;
- goto out_err;
- }
-
- rc = tpm2_probe(chip);
- if (rc)
- goto out_err;
-
- vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
- chip->vendor.manufacturer_id = vendor;
-
- dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n",
- (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
- vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
-
- if (!itpm) {
- probe = probe_itpm(chip);
- if (probe < 0) {
- rc = -ENODEV;
- goto out_err;
- }
- itpm = !!probe;
- }
+ if (interrupts)
+ irq = tpm_info->irq;
if (itpm)
- dev_info(dev, "Intel iTPM workaround enabled\n");
-
-
- /* Figure out the capabilities */
- intfcaps =
- ioread32(chip->vendor.iobase +
- TPM_INTF_CAPS(chip->vendor.locality));
- dev_dbg(dev, "TPM interface capabilities (0x%x):\n",
- intfcaps);
- if (intfcaps & TPM_INTF_BURST_COUNT_STATIC)
- dev_dbg(dev, "\tBurst Count Static\n");
- if (intfcaps & TPM_INTF_CMD_READY_INT)
- dev_dbg(dev, "\tCommand Ready Int Support\n");
- if (intfcaps & TPM_INTF_INT_EDGE_FALLING)
- dev_dbg(dev, "\tInterrupt Edge Falling\n");
- if (intfcaps & TPM_INTF_INT_EDGE_RISING)
- dev_dbg(dev, "\tInterrupt Edge Rising\n");
- if (intfcaps & TPM_INTF_INT_LEVEL_LOW)
- dev_dbg(dev, "\tInterrupt Level Low\n");
- if (intfcaps & TPM_INTF_INT_LEVEL_HIGH)
- dev_dbg(dev, "\tInterrupt Level High\n");
- if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT)
- dev_dbg(dev, "\tLocality Change Int Support\n");
- if (intfcaps & TPM_INTF_STS_VALID_INT)
- dev_dbg(dev, "\tSts Valid Int Support\n");
- if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
- dev_dbg(dev, "\tData Avail Int Support\n");
-
- /* Very early on issue a command to the TPM in polling mode to make
- * sure it works. May as well use that command to set the proper
- * timeouts for the driver.
- */
- if (tpm_get_timeouts(chip)) {
- dev_err(dev, "Could not get TPM timeouts and durations\n");
- rc = -ENODEV;
- goto out_err;
- }
-
- /* INTERRUPT Setup */
- init_waitqueue_head(&chip->vendor.read_queue);
- init_waitqueue_head(&chip->vendor.int_queue);
- if (interrupts && tpm_info->irq != -1) {
- if (tpm_info->irq) {
- tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED,
- tpm_info->irq);
- if (!chip->vendor.irq)
- dev_err(chip->pdev, FW_BUG
- "TPM interrupt not working, polling instead\n");
- } else
- tpm_tis_probe_irq(chip, intmask);
- }
+ phy->priv.flags |= TPM_TIS_ITPM_POSSIBLE;
- if (chip->flags & TPM_CHIP_FLAG_TPM2) {
- rc = tpm2_do_selftest(chip);
- if (rc == TPM2_RC_INITIALIZE) {
- dev_warn(dev, "Firmware has not started TPM\n");
- rc = tpm2_startup(chip, TPM2_SU_CLEAR);
- if (!rc)
- rc = tpm2_do_selftest(chip);
- }
-
- if (rc) {
- dev_err(dev, "TPM self test failed\n");
- if (rc > 0)
- rc = -ENODEV;
- goto out_err;
- }
- } else {
- if (tpm_do_selftest(chip)) {
- dev_err(dev, "TPM self test failed\n");
- rc = -ENODEV;
- goto out_err;
- }
- }
-
- return tpm_chip_register(chip);
-out_err:
- tpm_tis_remove(chip);
- return rc;
+ return tpm_tis_core_init(dev, &phy->priv, irq, &tpm_tcg,
+ acpi_dev_handle);
}
-#ifdef CONFIG_PM_SLEEP
-static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
-{
- u32 intmask;
-
- /* reenable interrupts that device may have lost or
- BIOS/firmware may have disabled */
- iowrite8(chip->vendor.irq, chip->vendor.iobase +
- TPM_INT_VECTOR(chip->vendor.locality));
-
- intmask =
- ioread32(chip->vendor.iobase +
- TPM_INT_ENABLE(chip->vendor.locality));
-
- intmask |= TPM_INTF_CMD_READY_INT
- | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
- | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE;
-
- iowrite32(intmask,
- chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
-}
-
-static int tpm_tis_resume(struct device *dev)
-{
- struct tpm_chip *chip = dev_get_drvdata(dev);
- int ret;
-
- if (chip->vendor.irq)
- tpm_tis_reenable_interrupts(chip);
-
- ret = tpm_pm_resume(dev);
- if (ret)
- return ret;
-
- /* TPM 1.2 requires self-test on resume. This function actually returns
- * an error code but for unknown reason it isn't handled.
- */
- if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
- tpm_do_selftest(chip);
-
- return 0;
-}
-#endif
-
static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
@@ -1058,12 +363,6 @@ static struct platform_driver tis_drv = {
},
};
-static bool force;
-#ifdef CONFIG_X86
-module_param(force, bool, 0444);
-MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry");
-#endif
-
static int tpm_tis_force_device(void)
{
struct platform_device *pdev;
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
new file mode 100644
index 000000000000..d66f51b3648e
--- /dev/null
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -0,0 +1,835 @@
+/*
+ * Copyright (C) 2005, 2006 IBM Corporation
+ * Copyright (C) 2014, 2015 Intel Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd-devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * This device driver implements the TPM interface as defined in
+ * the TCG TPM Interface Spec version 1.2, revision 1.0.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pnp.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/acpi.h>
+#include <linux/freezer.h>
+#include "tpm.h"
+#include "tpm_tis_core.h"
+
+/* Before we attempt to access the TPM we must see that the valid bit is set.
+ * The specification says that this bit is 0 at reset and remains 0 until the
+ * 'TPM has gone through its self test and initialization and has established
+ * correct values in the other bits.'
+ */
+static int wait_startup(struct tpm_chip *chip, int l)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ unsigned long stop = jiffies + chip->timeout_a;
+
+ do {
+ int rc;
+ u8 access;
+
+ rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access);
+ if (rc < 0)
+ return rc;
+
+ if (access & TPM_ACCESS_VALID)
+ return 0;
+ msleep(TPM_TIMEOUT);
+ } while (time_before(jiffies, stop));
+ return -1;
+}
+
+static int check_locality(struct tpm_chip *chip, int l)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ int rc;
+ u8 access;
+
+ rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access);
+ if (rc < 0)
+ return rc;
+
+ if ((access & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
+ (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
+ return priv->locality = l;
+
+ return -1;
+}
+
+static void release_locality(struct tpm_chip *chip, int l, int force)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ int rc;
+ u8 access;
+
+ rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access);
+ if (rc < 0)
+ return;
+
+ if (force || (access &
+ (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
+ (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID))
+ tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
+
+}
+
+static int request_locality(struct tpm_chip *chip, int l)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ unsigned long stop, timeout;
+ long rc;
+
+ if (check_locality(chip, l) >= 0)
+ return l;
+
+ rc = tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_REQUEST_USE);
+ if (rc < 0)
+ return rc;
+
+ stop = jiffies + chip->timeout_a;
+
+ if (chip->flags & TPM_CHIP_FLAG_IRQ) {
+again:
+ timeout = stop - jiffies;
+ if ((long)timeout <= 0)
+ return -1;
+ rc = wait_event_interruptible_timeout(priv->int_queue,
+ (check_locality
+ (chip, l) >= 0),
+ timeout);
+ if (rc > 0)
+ return l;
+ if (rc == -ERESTARTSYS && freezing(current)) {
+ clear_thread_flag(TIF_SIGPENDING);
+ goto again;
+ }
+ } else {
+ /* wait for burstcount */
+ do {
+ if (check_locality(chip, l) >= 0)
+ return l;
+ msleep(TPM_TIMEOUT);
+ } while (time_before(jiffies, stop));
+ }
+ return -1;
+}
+
+static u8 tpm_tis_status(struct tpm_chip *chip)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ int rc;
+ u8 status;
+
+ rc = tpm_tis_read8(priv, TPM_STS(priv->locality), &status);
+ if (rc < 0)
+ return 0;
+
+ return status;
+}
+
+static void tpm_tis_ready(struct tpm_chip *chip)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+
+ /* this causes the current command to be aborted */
+ tpm_tis_write8(priv, TPM_STS(priv->locality), TPM_STS_COMMAND_READY);
+}
+
+static int get_burstcount(struct tpm_chip *chip)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ unsigned long stop;
+ int burstcnt, rc;
+ u32 value;
+
+ /* wait for burstcount */
+ /* which timeout value, spec has 2 answers (c & d) */
+ stop = jiffies + chip->timeout_d;
+ do {
+ rc = tpm_tis_read32(priv, TPM_STS(priv->locality), &value);
+ if (rc < 0)
+ return rc;
+
+ burstcnt = (value >> 8) & 0xFFFF;
+ if (burstcnt)
+ return burstcnt;
+ msleep(TPM_TIMEOUT);
+ } while (time_before(jiffies, stop));
+ return -EBUSY;
+}
+
+static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ int size = 0, burstcnt, rc;
+
+ while (size < count &&
+ wait_for_tpm_stat(chip,
+ TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ chip->timeout_c,
+ &priv->read_queue, true) == 0) {
+ burstcnt = min_t(int, get_burstcount(chip), count - size);
+
+ rc = tpm_tis_read_bytes(priv, TPM_DATA_FIFO(priv->locality),
+ burstcnt, buf + size);
+ if (rc < 0)
+ return rc;
+
+ size += burstcnt;
+ }
+ return size;
+}
+
+static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ int size = 0;
+ int expected, status;
+
+ if (count < TPM_HEADER_SIZE) {
+ size = -EIO;
+ goto out;
+ }
+
+ size = recv_data(chip, buf, TPM_HEADER_SIZE);
+ /* read first 10 bytes, including tag, paramsize, and result */
+ if (size < TPM_HEADER_SIZE) {
+ dev_err(&chip->dev, "Unable to read header\n");
+ goto out;
+ }
+
+ expected = be32_to_cpu(*(__be32 *) (buf + 2));
+ if (expected > count) {
+ size = -EIO;
+ goto out;
+ }
+
+ size += recv_data(chip, &buf[TPM_HEADER_SIZE],
+ expected - TPM_HEADER_SIZE);
+ if (size < expected) {
+ dev_err(&chip->dev, "Unable to read remainder of result\n");
+ size = -ETIME;
+ goto out;
+ }
+
+ wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
+ &priv->int_queue, false);
+ status = tpm_tis_status(chip);
+ if (status & TPM_STS_DATA_AVAIL) { /* retry? */
+ dev_err(&chip->dev, "Error left over data\n");
+ size = -EIO;
+ goto out;
+ }
+
+out:
+ tpm_tis_ready(chip);
+ release_locality(chip, priv->locality, 0);
+ return size;
+}
+
+/*
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tpm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ int rc, status, burstcnt;
+ size_t count = 0;
+ bool itpm = priv->flags & TPM_TIS_ITPM_POSSIBLE;
+
+ if (request_locality(chip, 0) < 0)
+ return -EBUSY;
+
+ status = tpm_tis_status(chip);
+ if ((status & TPM_STS_COMMAND_READY) == 0) {
+ tpm_tis_ready(chip);
+ if (wait_for_tpm_stat
+ (chip, TPM_STS_COMMAND_READY, chip->timeout_b,
+ &priv->int_queue, false) < 0) {
+ rc = -ETIME;
+ goto out_err;
+ }
+ }
+
+ while (count < len - 1) {
+ burstcnt = min_t(int, get_burstcount(chip), len - count - 1);
+ rc = tpm_tis_write_bytes(priv, TPM_DATA_FIFO(priv->locality),
+ burstcnt, buf + count);
+ if (rc < 0)
+ goto out_err;
+
+ count += burstcnt;
+
+ wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
+ &priv->int_queue, false);
+ status = tpm_tis_status(chip);
+ if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
+ rc = -EIO;
+ goto out_err;
+ }
+ }
+
+ /* write last byte */
+ rc = tpm_tis_write8(priv, TPM_DATA_FIFO(priv->locality), buf[count]);
+ if (rc < 0)
+ goto out_err;
+
+ wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
+ &priv->int_queue, false);
+ status = tpm_tis_status(chip);
+ if (!itpm && (status & TPM_STS_DATA_EXPECT) != 0) {
+ rc = -EIO;
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ tpm_tis_ready(chip);
+ release_locality(chip, priv->locality, 0);
+ return rc;
+}
+
+static void disable_interrupts(struct tpm_chip *chip)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ u32 intmask;
+ int rc;
+
+ rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
+ if (rc < 0)
+ intmask = 0;
+
+ intmask &= ~TPM_GLOBAL_INT_ENABLE;
+ rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
+
+ devm_free_irq(chip->dev.parent, priv->irq, chip);
+ priv->irq = 0;
+ chip->flags &= ~TPM_CHIP_FLAG_IRQ;
+}
+
+/*
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tpm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ int rc;
+ u32 ordinal;
+ unsigned long dur;
+
+ rc = tpm_tis_send_data(chip, buf, len);
+ if (rc < 0)
+ return rc;
+
+ /* go and do it */
+ rc = tpm_tis_write8(priv, TPM_STS(priv->locality), TPM_STS_GO);
+ if (rc < 0)
+ goto out_err;
+
+ if (chip->flags & TPM_CHIP_FLAG_IRQ) {
+ ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
+
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ dur = tpm2_calc_ordinal_duration(chip, ordinal);
+ else
+ dur = tpm_calc_ordinal_duration(chip, ordinal);
+
+ if (wait_for_tpm_stat
+ (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, dur,
+ &priv->read_queue, false) < 0) {
+ rc = -ETIME;
+ goto out_err;
+ }
+ }
+ return len;
+out_err:
+ tpm_tis_ready(chip);
+ release_locality(chip, priv->locality, 0);
+ return rc;
+}
+
+static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+ int rc, irq;
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+
+ if (!(chip->flags & TPM_CHIP_FLAG_IRQ) || priv->irq_tested)
+ return tpm_tis_send_main(chip, buf, len);
+
+ /* Verify receipt of the expected IRQ */
+ irq = priv->irq;
+ priv->irq = 0;
+ chip->flags &= ~TPM_CHIP_FLAG_IRQ;
+ rc = tpm_tis_send_main(chip, buf, len);
+ priv->irq = irq;
+ chip->flags |= TPM_CHIP_FLAG_IRQ;
+ if (!priv->irq_tested)
+ msleep(1);
+ if (!priv->irq_tested)
+ disable_interrupts(chip);
+ priv->irq_tested = true;
+ return rc;
+}
+
+struct tis_vendor_timeout_override {
+ u32 did_vid;
+ unsigned long timeout_us[4];
+};
+
+static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
+ /* Atmel 3204 */
+ { 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
+ (TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
+};
+
+static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
+ unsigned long *timeout_cap)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ int i, rc;
+ u32 did_vid;
+
+ rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid);
+ if (rc < 0)
+ return rc;
+
+ for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
+ if (vendor_timeout_overrides[i].did_vid != did_vid)
+ continue;
+ memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
+ sizeof(vendor_timeout_overrides[i].timeout_us));
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Early probing for iTPM with STS_DATA_EXPECT flaw.
+ * Try sending command without itpm flag set and if that
+ * fails, repeat with itpm flag set.
+ */
+static int probe_itpm(struct tpm_chip *chip)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ int rc = 0;
+ u8 cmd_getticks[] = {
+ 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a,
+ 0x00, 0x00, 0x00, 0xf1
+ };
+ size_t len = sizeof(cmd_getticks);
+ bool itpm;
+ u16 vendor;
+
+ rc = tpm_tis_read16(priv, TPM_DID_VID(0), &vendor);
+ if (rc < 0)
+ return rc;
+
+ /* probe only iTPMS */
+ if (vendor != TPM_VID_INTEL)
+ return 0;
+
+ itpm = false;
+
+ rc = tpm_tis_send_data(chip, cmd_getticks, len);
+ if (rc == 0)
+ goto out;
+
+ tpm_tis_ready(chip);
+ release_locality(chip, priv->locality, 0);
+
+ itpm = true;
+
+ rc = tpm_tis_send_data(chip, cmd_getticks, len);
+ if (rc == 0) {
+ dev_info(&chip->dev, "Detected an iTPM.\n");
+ rc = 1;
+ } else
+ rc = -EFAULT;
+
+out:
+ tpm_tis_ready(chip);
+ release_locality(chip, priv->locality, 0);
+
+ return rc;
+}
+
+static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+
+ switch (priv->manufacturer_id) {
+ case TPM_VID_WINBOND:
+ return ((status == TPM_STS_VALID) ||
+ (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY)));
+ case TPM_VID_STM:
+ return (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY));
+ default:
+ return (status == TPM_STS_COMMAND_READY);
+ }
+}
+
+static irqreturn_t tis_int_handler(int dummy, void *dev_id)
+{
+ struct tpm_chip *chip = dev_id;
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ u32 interrupt;
+ int i, rc;
+
+ rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt);
+ if (rc < 0)
+ return IRQ_NONE;
+
+ if (interrupt == 0)
+ return IRQ_NONE;
+
+ priv->irq_tested = true;
+ if (interrupt & TPM_INTF_DATA_AVAIL_INT)
+ wake_up_interruptible(&priv->read_queue);
+ if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT)
+ for (i = 0; i < 5; i++)
+ if (check_locality(chip, i) >= 0)
+ break;
+ if (interrupt &
+ (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT |
+ TPM_INTF_CMD_READY_INT))
+ wake_up_interruptible(&priv->int_queue);
+
+ /* Clear interrupts handled with TPM_EOI */
+ rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), interrupt);
+ if (rc < 0)
+ return IRQ_NONE;
+
+ tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt);
+ return IRQ_HANDLED;
+}
+
+/* Register the IRQ and issue a command that will cause an interrupt. If an
+ * irq is seen then leave the chip setup for IRQ operation, otherwise reverse
+ * everything and leave in polling mode. Returns 0 on success.
+ */
+static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask,
+ int flags, int irq)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ u8 original_int_vec;
+ int rc;
+ u32 int_status;
+
+ if (devm_request_irq(chip->dev.parent, irq, tis_int_handler, flags,
+ dev_name(&chip->dev), chip) != 0) {
+ dev_info(&chip->dev, "Unable to request irq: %d for probe\n",
+ irq);
+ return -1;
+ }
+ priv->irq = irq;
+
+ rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality),
+ &original_int_vec);
+ if (rc < 0)
+ return rc;
+
+ rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), irq);
+ if (rc < 0)
+ return rc;
+
+ rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &int_status);
+ if (rc < 0)
+ return rc;
+
+ /* Clear all existing */
+ rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), int_status);
+ if (rc < 0)
+ return rc;
+
+ /* Turn on */
+ rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality),
+ intmask | TPM_GLOBAL_INT_ENABLE);
+ if (rc < 0)
+ return rc;
+
+ priv->irq_tested = false;
+
+ /* Generate an interrupt by having the core call through to
+ * tpm_tis_send
+ */
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ tpm2_gen_interrupt(chip);
+ else
+ tpm_gen_interrupt(chip);
+
+ /* tpm_tis_send will either confirm the interrupt is working or it
+ * will call disable_irq which undoes all of the above.
+ */
+ if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) {
+ rc = tpm_tis_write8(priv, original_int_vec,
+ TPM_INT_VECTOR(priv->locality));
+ if (rc < 0)
+ return rc;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Try to find the IRQ the TPM is using. This is for legacy x86 systems that
+ * do not have ACPI/etc. We typically expect the interrupt to be declared if
+ * present.
+ */
+static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ u8 original_int_vec;
+ int i, rc;
+
+ rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality),
+ &original_int_vec);
+ if (rc < 0)
+ return;
+
+ if (!original_int_vec) {
+ if (IS_ENABLED(CONFIG_X86))
+ for (i = 3; i <= 15; i++)
+ if (!tpm_tis_probe_irq_single(chip, intmask, 0,
+ i))
+ return;
+ } else if (!tpm_tis_probe_irq_single(chip, intmask, 0,
+ original_int_vec))
+ return;
+}
+
+void tpm_tis_remove(struct tpm_chip *chip)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ u32 reg = TPM_INT_ENABLE(priv->locality);
+ u32 interrupt;
+ int rc;
+
+ rc = tpm_tis_read32(priv, reg, &interrupt);
+ if (rc < 0)
+ interrupt = 0;
+
+ tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt);
+ release_locality(chip, priv->locality, 1);
+}
+EXPORT_SYMBOL_GPL(tpm_tis_remove);
+
+static const struct tpm_class_ops tpm_tis = {
+ .flags = TPM_OPS_AUTO_STARTUP,
+ .status = tpm_tis_status,
+ .recv = tpm_tis_recv,
+ .send = tpm_tis_send,
+ .cancel = tpm_tis_ready,
+ .update_timeouts = tpm_tis_update_timeouts,
+ .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ .req_canceled = tpm_tis_req_canceled,
+};
+
+int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
+ const struct tpm_tis_phy_ops *phy_ops,
+ acpi_handle acpi_dev_handle)
+{
+ u32 vendor, intfcaps, intmask;
+ u8 rid;
+ int rc, probe;
+ struct tpm_chip *chip;
+
+ chip = tpmm_chip_alloc(dev, &tpm_tis);
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+#ifdef CONFIG_ACPI
+ chip->acpi_dev_handle = acpi_dev_handle;
+#endif
+
+ /* Maximum timeouts */
+ chip->timeout_a = msecs_to_jiffies(TIS_TIMEOUT_A_MAX);
+ chip->timeout_b = msecs_to_jiffies(TIS_TIMEOUT_B_MAX);
+ chip->timeout_c = msecs_to_jiffies(TIS_TIMEOUT_C_MAX);
+ chip->timeout_d = msecs_to_jiffies(TIS_TIMEOUT_D_MAX);
+ priv->phy_ops = phy_ops;
+ dev_set_drvdata(&chip->dev, priv);
+
+ if (wait_startup(chip, 0) != 0) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ /* Take control of the TPM's interrupt hardware and shut it off */
+ rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
+ if (rc < 0)
+ goto out_err;
+
+ intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT |
+ TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT;
+ intmask &= ~TPM_GLOBAL_INT_ENABLE;
+ tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
+
+ if (request_locality(chip, 0) != 0) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ rc = tpm2_probe(chip);
+ if (rc)
+ goto out_err;
+
+ rc = tpm_tis_read32(priv, TPM_DID_VID(0), &vendor);
+ if (rc < 0)
+ goto out_err;
+
+ priv->manufacturer_id = vendor;
+
+ rc = tpm_tis_read8(priv, TPM_RID(0), &rid);
+ if (rc < 0)
+ goto out_err;
+
+ dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n",
+ (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
+ vendor >> 16, rid);
+
+ if (!(priv->flags & TPM_TIS_ITPM_POSSIBLE)) {
+ probe = probe_itpm(chip);
+ if (probe < 0) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ if (!!probe)
+ priv->flags |= TPM_TIS_ITPM_POSSIBLE;
+ }
+
+ /* Figure out the capabilities */
+ rc = tpm_tis_read32(priv, TPM_INTF_CAPS(priv->locality), &intfcaps);
+ if (rc < 0)
+ goto out_err;
+
+ dev_dbg(dev, "TPM interface capabilities (0x%x):\n",
+ intfcaps);
+ if (intfcaps & TPM_INTF_BURST_COUNT_STATIC)
+ dev_dbg(dev, "\tBurst Count Static\n");
+ if (intfcaps & TPM_INTF_CMD_READY_INT)
+ dev_dbg(dev, "\tCommand Ready Int Support\n");
+ if (intfcaps & TPM_INTF_INT_EDGE_FALLING)
+ dev_dbg(dev, "\tInterrupt Edge Falling\n");
+ if (intfcaps & TPM_INTF_INT_EDGE_RISING)
+ dev_dbg(dev, "\tInterrupt Edge Rising\n");
+ if (intfcaps & TPM_INTF_INT_LEVEL_LOW)
+ dev_dbg(dev, "\tInterrupt Level Low\n");
+ if (intfcaps & TPM_INTF_INT_LEVEL_HIGH)
+ dev_dbg(dev, "\tInterrupt Level High\n");
+ if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT)
+ dev_dbg(dev, "\tLocality Change Int Support\n");
+ if (intfcaps & TPM_INTF_STS_VALID_INT)
+ dev_dbg(dev, "\tSts Valid Int Support\n");
+ if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
+ dev_dbg(dev, "\tData Avail Int Support\n");
+
+ /* Very early on issue a command to the TPM in polling mode to make
+ * sure it works. May as well use that command to set the proper
+ * timeouts for the driver.
+ */
+ if (tpm_get_timeouts(chip)) {
+ dev_err(dev, "Could not get TPM timeouts and durations\n");
+ rc = -ENODEV;
+ goto out_err;
+ }
+
+ /* INTERRUPT Setup */
+ init_waitqueue_head(&priv->read_queue);
+ init_waitqueue_head(&priv->int_queue);
+ if (irq != -1) {
+ if (irq) {
+ tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED,
+ irq);
+ if (!(chip->flags & TPM_CHIP_FLAG_IRQ))
+ dev_err(&chip->dev, FW_BUG
+ "TPM interrupt not working, polling instead\n");
+ } else {
+ tpm_tis_probe_irq(chip, intmask);
+ }
+ }
+
+ return tpm_chip_register(chip);
+out_err:
+ tpm_tis_remove(chip);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_tis_core_init);
+
+#ifdef CONFIG_PM_SLEEP
+static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
+{
+ struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ u32 intmask;
+ int rc;
+
+ /* reenable interrupts that device may have lost or
+ * BIOS/firmware may have disabled
+ */
+ rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), priv->irq);
+ if (rc < 0)
+ return;
+
+ rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
+ if (rc < 0)
+ return;
+
+ intmask |= TPM_INTF_CMD_READY_INT
+ | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
+ | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE;
+
+ tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
+}
+
+int tpm_tis_resume(struct device *dev)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+ int ret;
+
+ if (chip->flags & TPM_CHIP_FLAG_IRQ)
+ tpm_tis_reenable_interrupts(chip);
+
+ ret = tpm_pm_resume(dev);
+ if (ret)
+ return ret;
+
+ /* TPM 1.2 requires self-test on resume. This function actually returns
+ * an error code but for unknown reason it isn't handled.
+ */
+ if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
+ tpm_do_selftest(chip);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tpm_tis_resume);
+#endif
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h
new file mode 100644
index 000000000000..9191aabbf9c2
--- /dev/null
+++ b/drivers/char/tpm/tpm_tis_core.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2005, 2006 IBM Corporation
+ * Copyright (C) 2014, 2015 Intel Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd-devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * This device driver implements the TPM interface as defined in
+ * the TCG TPM Interface Spec version 1.2, revision 1.0.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#ifndef __TPM_TIS_CORE_H__
+#define __TPM_TIS_CORE_H__
+
+#include "tpm.h"
+
+enum tis_access {
+ TPM_ACCESS_VALID = 0x80,
+ TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
+ TPM_ACCESS_REQUEST_PENDING = 0x04,
+ TPM_ACCESS_REQUEST_USE = 0x02,
+};
+
+enum tis_status {
+ TPM_STS_VALID = 0x80,
+ TPM_STS_COMMAND_READY = 0x40,
+ TPM_STS_GO = 0x20,
+ TPM_STS_DATA_AVAIL = 0x10,
+ TPM_STS_DATA_EXPECT = 0x08,
+};
+
+enum tis_int_flags {
+ TPM_GLOBAL_INT_ENABLE = 0x80000000,
+ TPM_INTF_BURST_COUNT_STATIC = 0x100,
+ TPM_INTF_CMD_READY_INT = 0x080,
+ TPM_INTF_INT_EDGE_FALLING = 0x040,
+ TPM_INTF_INT_EDGE_RISING = 0x020,
+ TPM_INTF_INT_LEVEL_LOW = 0x010,
+ TPM_INTF_INT_LEVEL_HIGH = 0x008,
+ TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
+ TPM_INTF_STS_VALID_INT = 0x002,
+ TPM_INTF_DATA_AVAIL_INT = 0x001,
+};
+
+enum tis_defaults {
+ TIS_MEM_LEN = 0x5000,
+ TIS_SHORT_TIMEOUT = 750, /* ms */
+ TIS_LONG_TIMEOUT = 2000, /* 2 sec */
+};
+
+/* Some timeout values are needed before it is known whether the chip is
+ * TPM 1.0 or TPM 2.0.
+ */
+#define TIS_TIMEOUT_A_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A)
+#define TIS_TIMEOUT_B_MAX max(TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B)
+#define TIS_TIMEOUT_C_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C)
+#define TIS_TIMEOUT_D_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D)
+
+#define TPM_ACCESS(l) (0x0000 | ((l) << 12))
+#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12))
+#define TPM_INT_VECTOR(l) (0x000C | ((l) << 12))
+#define TPM_INT_STATUS(l) (0x0010 | ((l) << 12))
+#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12))
+#define TPM_STS(l) (0x0018 | ((l) << 12))
+#define TPM_STS3(l) (0x001b | ((l) << 12))
+#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12))
+
+#define TPM_DID_VID(l) (0x0F00 | ((l) << 12))
+#define TPM_RID(l) (0x0F04 | ((l) << 12))
+
+enum tpm_tis_flags {
+ TPM_TIS_ITPM_POSSIBLE = BIT(0),
+};
+
+struct tpm_tis_data {
+ u16 manufacturer_id;
+ int locality;
+ int irq;
+ bool irq_tested;
+ unsigned int flags;
+ wait_queue_head_t int_queue;
+ wait_queue_head_t read_queue;
+ const struct tpm_tis_phy_ops *phy_ops;
+};
+
+struct tpm_tis_phy_ops {
+ int (*read_bytes)(struct tpm_tis_data *data, u32 addr, u16 len,
+ u8 *result);
+ int (*write_bytes)(struct tpm_tis_data *data, u32 addr, u16 len,
+ u8 *value);
+ int (*read16)(struct tpm_tis_data *data, u32 addr, u16 *result);
+ int (*read32)(struct tpm_tis_data *data, u32 addr, u32 *result);
+ int (*write32)(struct tpm_tis_data *data, u32 addr, u32 src);
+};
+
+static inline int tpm_tis_read_bytes(struct tpm_tis_data *data, u32 addr,
+ u16 len, u8 *result)
+{
+ return data->phy_ops->read_bytes(data, addr, len, result);
+}
+
+static inline int tpm_tis_read8(struct tpm_tis_data *data, u32 addr, u8 *result)
+{
+ return data->phy_ops->read_bytes(data, addr, 1, result);
+}
+
+static inline int tpm_tis_read16(struct tpm_tis_data *data, u32 addr,
+ u16 *result)
+{
+ return data->phy_ops->read16(data, addr, result);
+}
+
+static inline int tpm_tis_read32(struct tpm_tis_data *data, u32 addr,
+ u32 *result)
+{
+ return data->phy_ops->read32(data, addr, result);
+}
+
+static inline int tpm_tis_write_bytes(struct tpm_tis_data *data, u32 addr,
+ u16 len, u8 *value)
+{
+ return data->phy_ops->write_bytes(data, addr, len, value);
+}
+
+static inline int tpm_tis_write8(struct tpm_tis_data *data, u32 addr, u8 value)
+{
+ return data->phy_ops->write_bytes(data, addr, 1, &value);
+}
+
+static inline int tpm_tis_write32(struct tpm_tis_data *data, u32 addr,
+ u32 value)
+{
+ return data->phy_ops->write32(data, addr, value);
+}
+
+void tpm_tis_remove(struct tpm_chip *chip);
+int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
+ const struct tpm_tis_phy_ops *phy_ops,
+ acpi_handle acpi_dev_handle);
+
+#ifdef CONFIG_PM_SLEEP
+int tpm_tis_resume(struct device *dev);
+#endif
+
+#endif
diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c
new file mode 100644
index 000000000000..dbaad9c681e3
--- /dev/null
+++ b/drivers/char/tpm/tpm_tis_spi.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2015 Infineon Technologies AG
+ * Copyright (C) 2016 STMicroelectronics SAS
+ *
+ * Authors:
+ * Peter Huewe <peter.huewe@infineon.com>
+ * Christophe Ricard <christophe-h.ricard@st.com>
+ *
+ * Maintained by: <tpmdd-devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * This device driver implements the TPM interface as defined in
+ * the TCG TPM Interface Spec version 1.3, revision 27 via _raw/native
+ * SPI access_.
+ *
+ * It is based on the original tpm_tis device driver from Leendert van
+ * Dorn and Kyleen Hall and Jarko Sakkinnen.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/acpi.h>
+#include <linux/freezer.h>
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/tpm.h>
+#include "tpm.h"
+#include "tpm_tis_core.h"
+
+#define MAX_SPI_FRAMESIZE 64
+
+struct tpm_tis_spi_phy {
+ struct tpm_tis_data priv;
+ struct spi_device *spi_device;
+
+ u8 tx_buf[MAX_SPI_FRAMESIZE + 4];
+ u8 rx_buf[MAX_SPI_FRAMESIZE + 4];
+};
+
+static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data)
+{
+ return container_of(data, struct tpm_tis_spi_phy, priv);
+}
+
+static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
+ u16 len, u8 *result)
+{
+ struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
+ int ret, i;
+ struct spi_message m;
+ struct spi_transfer spi_xfer = {
+ .tx_buf = phy->tx_buf,
+ .rx_buf = phy->rx_buf,
+ .len = 4,
+ };
+
+ if (len > MAX_SPI_FRAMESIZE)
+ return -ENOMEM;
+
+ phy->tx_buf[0] = 0x80 | (len - 1);
+ phy->tx_buf[1] = 0xd4;
+ phy->tx_buf[2] = (addr >> 8) & 0xFF;
+ phy->tx_buf[3] = addr & 0xFF;
+
+ spi_xfer.cs_change = 1;
+ spi_message_init(&m);
+ spi_message_add_tail(&spi_xfer, &m);
+
+ spi_bus_lock(phy->spi_device->master);
+ ret = spi_sync_locked(phy->spi_device, &m);
+ if (ret < 0)
+ goto exit;
+
+ memset(phy->tx_buf, 0, len);
+
+ /* According to TCG PTP specification, if there is no TPM present at
+ * all, then the design has a weak pull-up on MISO. If a TPM is not
+ * present, a pull-up on MISO means that the SB controller sees a 1,
+ * and will latch in 0xFF on the read.
+ */
+ for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) {
+ spi_xfer.len = 1;
+ spi_message_init(&m);
+ spi_message_add_tail(&spi_xfer, &m);
+ ret = spi_sync_locked(phy->spi_device, &m);
+ if (ret < 0)
+ goto exit;
+ }
+
+ spi_xfer.cs_change = 0;
+ spi_xfer.len = len;
+ spi_xfer.rx_buf = result;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&spi_xfer, &m);
+ ret = spi_sync_locked(phy->spi_device, &m);
+
+exit:
+ spi_bus_unlock(phy->spi_device->master);
+ return ret;
+}
+
+static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr,
+ u16 len, u8 *value)
+{
+ struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
+ int ret, i;
+ struct spi_message m;
+ struct spi_transfer spi_xfer = {
+ .tx_buf = phy->tx_buf,
+ .rx_buf = phy->rx_buf,
+ .len = 4,
+ };
+
+ if (len > MAX_SPI_FRAMESIZE)
+ return -ENOMEM;
+
+ phy->tx_buf[0] = len - 1;
+ phy->tx_buf[1] = 0xd4;
+ phy->tx_buf[2] = (addr >> 8) & 0xFF;
+ phy->tx_buf[3] = addr & 0xFF;
+
+ spi_xfer.cs_change = 1;
+ spi_message_init(&m);
+ spi_message_add_tail(&spi_xfer, &m);
+
+ spi_bus_lock(phy->spi_device->master);
+ ret = spi_sync_locked(phy->spi_device, &m);
+ if (ret < 0)
+ goto exit;
+
+ memset(phy->tx_buf, 0, len);
+
+ /* According to TCG PTP specification, if there is no TPM present at
+ * all, then the design has a weak pull-up on MISO. If a TPM is not
+ * present, a pull-up on MISO means that the SB controller sees a 1,
+ * and will latch in 0xFF on the read.
+ */
+ for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) {
+ spi_xfer.len = 1;
+ spi_message_init(&m);
+ spi_message_add_tail(&spi_xfer, &m);
+ ret = spi_sync_locked(phy->spi_device, &m);
+ if (ret < 0)
+ goto exit;
+ }
+
+ spi_xfer.len = len;
+ spi_xfer.tx_buf = value;
+ spi_xfer.cs_change = 0;
+ spi_xfer.tx_buf = value;
+ spi_message_init(&m);
+ spi_message_add_tail(&spi_xfer, &m);
+ ret = spi_sync_locked(phy->spi_device, &m);
+
+exit:
+ spi_bus_unlock(phy->spi_device->master);
+ return ret;
+}
+
+static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
+{
+ int rc;
+
+ rc = data->phy_ops->read_bytes(data, addr, sizeof(u16), (u8 *)result);
+ if (!rc)
+ *result = le16_to_cpu(*result);
+ return rc;
+}
+
+static int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
+{
+ int rc;
+
+ rc = data->phy_ops->read_bytes(data, addr, sizeof(u32), (u8 *)result);
+ if (!rc)
+ *result = le32_to_cpu(*result);
+ return rc;
+}
+
+static int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value)
+{
+ value = cpu_to_le32(value);
+ return data->phy_ops->write_bytes(data, addr, sizeof(u32),
+ (u8 *)&value);
+}
+
+static const struct tpm_tis_phy_ops tpm_spi_phy_ops = {
+ .read_bytes = tpm_tis_spi_read_bytes,
+ .write_bytes = tpm_tis_spi_write_bytes,
+ .read16 = tpm_tis_spi_read16,
+ .read32 = tpm_tis_spi_read32,
+ .write32 = tpm_tis_spi_write32,
+};
+
+static int tpm_tis_spi_probe(struct spi_device *dev)
+{
+ struct tpm_tis_spi_phy *phy;
+
+ phy = devm_kzalloc(&dev->dev, sizeof(struct tpm_tis_spi_phy),
+ GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ phy->spi_device = dev;
+
+ return tpm_tis_core_init(&dev->dev, &phy->priv, -1, &tpm_spi_phy_ops,
+ NULL);
+}
+
+static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
+
+static int tpm_tis_spi_remove(struct spi_device *dev)
+{
+ struct tpm_chip *chip = spi_get_drvdata(dev);
+
+ tpm_chip_unregister(chip);
+ tpm_tis_remove(chip);
+ return 0;
+}
+
+static const struct spi_device_id tpm_tis_spi_id[] = {
+ {"tpm_tis_spi", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, tpm_tis_spi_id);
+
+static const struct of_device_id of_tis_spi_match[] = {
+ { .compatible = "st,st33htpm-spi", },
+ { .compatible = "infineon,slb9670", },
+ { .compatible = "tcg,tpm_tis-spi", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_tis_spi_match);
+
+static const struct acpi_device_id acpi_tis_spi_match[] = {
+ {"SMO0768", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, acpi_tis_spi_match);
+
+static struct spi_driver tpm_tis_spi_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tpm_tis_spi",
+ .pm = &tpm_tis_pm,
+ .of_match_table = of_match_ptr(of_tis_spi_match),
+ .acpi_match_table = ACPI_PTR(acpi_tis_spi_match),
+ },
+ .probe = tpm_tis_spi_probe,
+ .remove = tpm_tis_spi_remove,
+ .id_table = tpm_tis_spi_id,
+};
+module_spi_driver(tpm_tis_spi_driver);
+
+MODULE_DESCRIPTION("TPM Driver for native SPI access");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
new file mode 100644
index 000000000000..9a940332c157
--- /dev/null
+++ b/drivers/char/tpm/tpm_vtpm_proxy.c
@@ -0,0 +1,637 @@
+/*
+ * Copyright (C) 2015, 2016 IBM Corporation
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ *
+ * Maintained by: <tpmdd-devel@lists.sourceforge.net>
+ *
+ * Device driver for vTPM (vTPM proxy driver)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/vtpm_proxy.h>
+#include <linux/file.h>
+#include <linux/anon_inodes.h>
+#include <linux/poll.h>
+#include <linux/compat.h>
+
+#include "tpm.h"
+
+#define VTPM_PROXY_REQ_COMPLETE_FLAG BIT(0)
+
+struct proxy_dev {
+ struct tpm_chip *chip;
+
+ u32 flags; /* public API flags */
+
+ wait_queue_head_t wq;
+
+ struct mutex buf_lock; /* protect buffer and flags */
+
+ long state; /* internal state */
+#define STATE_OPENED_FLAG BIT(0)
+#define STATE_WAIT_RESPONSE_FLAG BIT(1) /* waiting for emulator response */
+
+ size_t req_len; /* length of queued TPM request */
+ size_t resp_len; /* length of queued TPM response */
+ u8 buffer[TPM_BUFSIZE]; /* request/response buffer */
+
+ struct work_struct work; /* task that retrieves TPM timeouts */
+};
+
+/* all supported flags */
+#define VTPM_PROXY_FLAGS_ALL (VTPM_PROXY_FLAG_TPM2)
+
+static struct workqueue_struct *workqueue;
+
+static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev);
+
+/*
+ * Functions related to 'server side'
+ */
+
+/**
+ * vtpm_proxy_fops_read - Read TPM commands on 'server side'
+ *
+ * Return value:
+ * Number of bytes read or negative error code
+ */
+static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *off)
+{
+ struct proxy_dev *proxy_dev = filp->private_data;
+ size_t len;
+ int sig, rc;
+
+ sig = wait_event_interruptible(proxy_dev->wq,
+ proxy_dev->req_len != 0 ||
+ !(proxy_dev->state & STATE_OPENED_FLAG));
+ if (sig)
+ return -EINTR;
+
+ mutex_lock(&proxy_dev->buf_lock);
+
+ if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ return -EPIPE;
+ }
+
+ len = proxy_dev->req_len;
+
+ if (count < len) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ pr_debug("Invalid size in recv: count=%zd, req_len=%zd\n",
+ count, len);
+ return -EIO;
+ }
+
+ rc = copy_to_user(buf, proxy_dev->buffer, len);
+ memset(proxy_dev->buffer, 0, len);
+ proxy_dev->req_len = 0;
+
+ if (!rc)
+ proxy_dev->state |= STATE_WAIT_RESPONSE_FLAG;
+
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ if (rc)
+ return -EFAULT;
+
+ return len;
+}
+
+/**
+ * vtpm_proxy_fops_write - Write TPM responses on 'server side'
+ *
+ * Return value:
+ * Number of bytes read or negative error value
+ */
+static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *off)
+{
+ struct proxy_dev *proxy_dev = filp->private_data;
+
+ mutex_lock(&proxy_dev->buf_lock);
+
+ if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ return -EPIPE;
+ }
+
+ if (count > sizeof(proxy_dev->buffer) ||
+ !(proxy_dev->state & STATE_WAIT_RESPONSE_FLAG)) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ return -EIO;
+ }
+
+ proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG;
+
+ proxy_dev->req_len = 0;
+
+ if (copy_from_user(proxy_dev->buffer, buf, count)) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ return -EFAULT;
+ }
+
+ proxy_dev->resp_len = count;
+
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ wake_up_interruptible(&proxy_dev->wq);
+
+ return count;
+}
+
+/*
+ * vtpm_proxy_fops_poll: Poll status on 'server side'
+ *
+ * Return value:
+ * Poll flags
+ */
+static unsigned int vtpm_proxy_fops_poll(struct file *filp, poll_table *wait)
+{
+ struct proxy_dev *proxy_dev = filp->private_data;
+ unsigned ret;
+
+ poll_wait(filp, &proxy_dev->wq, wait);
+
+ ret = POLLOUT;
+
+ mutex_lock(&proxy_dev->buf_lock);
+
+ if (proxy_dev->req_len)
+ ret |= POLLIN | POLLRDNORM;
+
+ if (!(proxy_dev->state & STATE_OPENED_FLAG))
+ ret |= POLLHUP;
+
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ return ret;
+}
+
+/*
+ * vtpm_proxy_fops_open - Open vTPM device on 'server side'
+ *
+ * Called when setting up the anonymous file descriptor
+ */
+static void vtpm_proxy_fops_open(struct file *filp)
+{
+ struct proxy_dev *proxy_dev = filp->private_data;
+
+ proxy_dev->state |= STATE_OPENED_FLAG;
+}
+
+/**
+ * vtpm_proxy_fops_undo_open - counter-part to vtpm_fops_open
+ *
+ * Call to undo vtpm_proxy_fops_open
+ */
+static void vtpm_proxy_fops_undo_open(struct proxy_dev *proxy_dev)
+{
+ mutex_lock(&proxy_dev->buf_lock);
+
+ proxy_dev->state &= ~STATE_OPENED_FLAG;
+
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ /* no more TPM responses -- wake up anyone waiting for them */
+ wake_up_interruptible(&proxy_dev->wq);
+}
+
+/*
+ * vtpm_proxy_fops_release: Close 'server side'
+ *
+ * Return value:
+ * Always returns 0.
+ */
+static int vtpm_proxy_fops_release(struct inode *inode, struct file *filp)
+{
+ struct proxy_dev *proxy_dev = filp->private_data;
+
+ filp->private_data = NULL;
+
+ vtpm_proxy_delete_device(proxy_dev);
+
+ return 0;
+}
+
+static const struct file_operations vtpm_proxy_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = vtpm_proxy_fops_read,
+ .write = vtpm_proxy_fops_write,
+ .poll = vtpm_proxy_fops_poll,
+ .release = vtpm_proxy_fops_release,
+};
+
+/*
+ * Functions invoked by the core TPM driver to send TPM commands to
+ * 'server side' and receive responses from there.
+ */
+
+/*
+ * Called when core TPM driver reads TPM responses from 'server side'
+ *
+ * Return value:
+ * Number of TPM response bytes read, negative error value otherwise
+ */
+static int vtpm_proxy_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
+ size_t len;
+
+ /* process gone ? */
+ mutex_lock(&proxy_dev->buf_lock);
+
+ if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ return -EPIPE;
+ }
+
+ len = proxy_dev->resp_len;
+ if (count < len) {
+ dev_err(&chip->dev,
+ "Invalid size in recv: count=%zd, resp_len=%zd\n",
+ count, len);
+ len = -EIO;
+ goto out;
+ }
+
+ memcpy(buf, proxy_dev->buffer, len);
+ proxy_dev->resp_len = 0;
+
+out:
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ return len;
+}
+
+/*
+ * Called when core TPM driver forwards TPM requests to 'server side'.
+ *
+ * Return value:
+ * 0 in case of success, negative error value otherwise.
+ */
+static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+ struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
+ int rc = 0;
+
+ if (count > sizeof(proxy_dev->buffer)) {
+ dev_err(&chip->dev,
+ "Invalid size in send: count=%zd, buffer size=%zd\n",
+ count, sizeof(proxy_dev->buffer));
+ return -EIO;
+ }
+
+ mutex_lock(&proxy_dev->buf_lock);
+
+ if (!(proxy_dev->state & STATE_OPENED_FLAG)) {
+ mutex_unlock(&proxy_dev->buf_lock);
+ return -EPIPE;
+ }
+
+ proxy_dev->resp_len = 0;
+
+ proxy_dev->req_len = count;
+ memcpy(proxy_dev->buffer, buf, count);
+
+ proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG;
+
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ wake_up_interruptible(&proxy_dev->wq);
+
+ return rc;
+}
+
+static void vtpm_proxy_tpm_op_cancel(struct tpm_chip *chip)
+{
+ /* not supported */
+}
+
+static u8 vtpm_proxy_tpm_op_status(struct tpm_chip *chip)
+{
+ struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
+
+ if (proxy_dev->resp_len)
+ return VTPM_PROXY_REQ_COMPLETE_FLAG;
+
+ return 0;
+}
+
+static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip *chip, u8 status)
+{
+ struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
+ bool ret;
+
+ mutex_lock(&proxy_dev->buf_lock);
+
+ ret = !(proxy_dev->state & STATE_OPENED_FLAG);
+
+ mutex_unlock(&proxy_dev->buf_lock);
+
+ return ret;
+}
+
+static const struct tpm_class_ops vtpm_proxy_tpm_ops = {
+ .flags = TPM_OPS_AUTO_STARTUP,
+ .recv = vtpm_proxy_tpm_op_recv,
+ .send = vtpm_proxy_tpm_op_send,
+ .cancel = vtpm_proxy_tpm_op_cancel,
+ .status = vtpm_proxy_tpm_op_status,
+ .req_complete_mask = VTPM_PROXY_REQ_COMPLETE_FLAG,
+ .req_complete_val = VTPM_PROXY_REQ_COMPLETE_FLAG,
+ .req_canceled = vtpm_proxy_tpm_req_canceled,
+};
+
+/*
+ * Code related to the startup of the TPM 2 and startup of TPM 1.2 +
+ * retrieval of timeouts and durations.
+ */
+
+static void vtpm_proxy_work(struct work_struct *work)
+{
+ struct proxy_dev *proxy_dev = container_of(work, struct proxy_dev,
+ work);
+ int rc;
+
+ rc = tpm_chip_register(proxy_dev->chip);
+ if (rc)
+ goto err;
+
+ return;
+
+err:
+ vtpm_proxy_fops_undo_open(proxy_dev);
+}
+
+/*
+ * vtpm_proxy_work_stop: make sure the work has finished
+ *
+ * This function is useful when user space closed the fd
+ * while the driver still determines timeouts.
+ */
+static void vtpm_proxy_work_stop(struct proxy_dev *proxy_dev)
+{
+ vtpm_proxy_fops_undo_open(proxy_dev);
+ flush_work(&proxy_dev->work);
+}
+
+/*
+ * vtpm_proxy_work_start: Schedule the work for TPM 1.2 & 2 initialization
+ */
+static inline void vtpm_proxy_work_start(struct proxy_dev *proxy_dev)
+{
+ queue_work(workqueue, &proxy_dev->work);
+}
+
+/*
+ * Code related to creation and deletion of device pairs
+ */
+static struct proxy_dev *vtpm_proxy_create_proxy_dev(void)
+{
+ struct proxy_dev *proxy_dev;
+ struct tpm_chip *chip;
+ int err;
+
+ proxy_dev = kzalloc(sizeof(*proxy_dev), GFP_KERNEL);
+ if (proxy_dev == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ init_waitqueue_head(&proxy_dev->wq);
+ mutex_init(&proxy_dev->buf_lock);
+ INIT_WORK(&proxy_dev->work, vtpm_proxy_work);
+
+ chip = tpm_chip_alloc(NULL, &vtpm_proxy_tpm_ops);
+ if (IS_ERR(chip)) {
+ err = PTR_ERR(chip);
+ goto err_proxy_dev_free;
+ }
+ dev_set_drvdata(&chip->dev, proxy_dev);
+
+ proxy_dev->chip = chip;
+
+ return proxy_dev;
+
+err_proxy_dev_free:
+ kfree(proxy_dev);
+
+ return ERR_PTR(err);
+}
+
+/*
+ * Undo what has been done in vtpm_create_proxy_dev
+ */
+static inline void vtpm_proxy_delete_proxy_dev(struct proxy_dev *proxy_dev)
+{
+ put_device(&proxy_dev->chip->dev); /* frees chip */
+ kfree(proxy_dev);
+}
+
+/*
+ * Create a /dev/tpm%d and 'server side' file descriptor pair
+ *
+ * Return value:
+ * Returns file pointer on success, an error value otherwise
+ */
+static struct file *vtpm_proxy_create_device(
+ struct vtpm_proxy_new_dev *vtpm_new_dev)
+{
+ struct proxy_dev *proxy_dev;
+ int rc, fd;
+ struct file *file;
+
+ if (vtpm_new_dev->flags & ~VTPM_PROXY_FLAGS_ALL)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ proxy_dev = vtpm_proxy_create_proxy_dev();
+ if (IS_ERR(proxy_dev))
+ return ERR_CAST(proxy_dev);
+
+ proxy_dev->flags = vtpm_new_dev->flags;
+
+ /* setup an anonymous file for the server-side */
+ fd = get_unused_fd_flags(O_RDWR);
+ if (fd < 0) {
+ rc = fd;
+ goto err_delete_proxy_dev;
+ }
+
+ file = anon_inode_getfile("[vtpms]", &vtpm_proxy_fops, proxy_dev,
+ O_RDWR);
+ if (IS_ERR(file)) {
+ rc = PTR_ERR(file);
+ goto err_put_unused_fd;
+ }
+
+ /* from now on we can unwind with put_unused_fd() + fput() */
+ /* simulate an open() on the server side */
+ vtpm_proxy_fops_open(file);
+
+ if (proxy_dev->flags & VTPM_PROXY_FLAG_TPM2)
+ proxy_dev->chip->flags |= TPM_CHIP_FLAG_TPM2;
+
+ vtpm_proxy_work_start(proxy_dev);
+
+ vtpm_new_dev->fd = fd;
+ vtpm_new_dev->major = MAJOR(proxy_dev->chip->dev.devt);
+ vtpm_new_dev->minor = MINOR(proxy_dev->chip->dev.devt);
+ vtpm_new_dev->tpm_num = proxy_dev->chip->dev_num;
+
+ return file;
+
+err_put_unused_fd:
+ put_unused_fd(fd);
+
+err_delete_proxy_dev:
+ vtpm_proxy_delete_proxy_dev(proxy_dev);
+
+ return ERR_PTR(rc);
+}
+
+/*
+ * Counter part to vtpm_create_device.
+ */
+static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev)
+{
+ vtpm_proxy_work_stop(proxy_dev);
+
+ /*
+ * A client may hold the 'ops' lock, so let it know that the server
+ * side shuts down before we try to grab the 'ops' lock when
+ * unregistering the chip.
+ */
+ vtpm_proxy_fops_undo_open(proxy_dev);
+
+ tpm_chip_unregister(proxy_dev->chip);
+
+ vtpm_proxy_delete_proxy_dev(proxy_dev);
+}
+
+/*
+ * Code related to the control device /dev/vtpmx
+ */
+
+/*
+ * vtpmx_fops_ioctl: ioctl on /dev/vtpmx
+ *
+ * Return value:
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct vtpm_proxy_new_dev __user *vtpm_new_dev_p;
+ struct vtpm_proxy_new_dev vtpm_new_dev;
+ struct file *file;
+
+ switch (ioctl) {
+ case VTPM_PROXY_IOC_NEW_DEV:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ vtpm_new_dev_p = argp;
+ if (copy_from_user(&vtpm_new_dev, vtpm_new_dev_p,
+ sizeof(vtpm_new_dev)))
+ return -EFAULT;
+ file = vtpm_proxy_create_device(&vtpm_new_dev);
+ if (IS_ERR(file))
+ return PTR_ERR(file);
+ if (copy_to_user(vtpm_new_dev_p, &vtpm_new_dev,
+ sizeof(vtpm_new_dev))) {
+ put_unused_fd(vtpm_new_dev.fd);
+ fput(file);
+ return -EFAULT;
+ }
+
+ fd_install(vtpm_new_dev.fd, file);
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static long vtpmx_fops_compat_ioctl(struct file *f, unsigned int ioctl,
+ unsigned long arg)
+{
+ return vtpmx_fops_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations vtpmx_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = vtpmx_fops_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vtpmx_fops_compat_ioctl,
+#endif
+ .llseek = noop_llseek,
+};
+
+static struct miscdevice vtpmx_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "vtpmx",
+ .fops = &vtpmx_fops,
+};
+
+static int vtpmx_init(void)
+{
+ return misc_register(&vtpmx_miscdev);
+}
+
+static void vtpmx_cleanup(void)
+{
+ misc_deregister(&vtpmx_miscdev);
+}
+
+static int __init vtpm_module_init(void)
+{
+ int rc;
+
+ rc = vtpmx_init();
+ if (rc) {
+ pr_err("couldn't create vtpmx device\n");
+ return rc;
+ }
+
+ workqueue = create_workqueue("tpm-vtpm");
+ if (!workqueue) {
+ pr_err("couldn't create workqueue\n");
+ rc = -ENOMEM;
+ goto err_vtpmx_cleanup;
+ }
+
+ return 0;
+
+err_vtpmx_cleanup:
+ vtpmx_cleanup();
+
+ return rc;
+}
+
+static void __exit vtpm_module_exit(void)
+{
+ destroy_workqueue(workqueue);
+ vtpmx_cleanup();
+}
+
+module_init(vtpm_module_init);
+module_exit(vtpm_module_exit);
+
+MODULE_AUTHOR("Stefan Berger (stefanb@us.ibm.com)");
+MODULE_DESCRIPTION("vTPM Driver");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c
index 3111f2778079..62028f483bba 100644
--- a/drivers/char/tpm/xen-tpmfront.c
+++ b/drivers/char/tpm/xen-tpmfront.c
@@ -28,6 +28,8 @@ struct tpm_private {
unsigned int evtchn;
int ring_ref;
domid_t backend_id;
+ int irq;
+ wait_queue_head_t read_queue;
};
enum status_bits {
@@ -39,7 +41,7 @@ enum status_bits {
static u8 vtpm_status(struct tpm_chip *chip)
{
- struct tpm_private *priv = TPM_VPRIV(chip);
+ struct tpm_private *priv = dev_get_drvdata(&chip->dev);
switch (priv->shr->state) {
case VTPM_STATE_IDLE:
return VTPM_STATUS_IDLE | VTPM_STATUS_CANCELED;
@@ -60,7 +62,7 @@ static bool vtpm_req_canceled(struct tpm_chip *chip, u8 status)
static void vtpm_cancel(struct tpm_chip *chip)
{
- struct tpm_private *priv = TPM_VPRIV(chip);
+ struct tpm_private *priv = dev_get_drvdata(&chip->dev);
priv->shr->state = VTPM_STATE_CANCEL;
wmb();
notify_remote_via_evtchn(priv->evtchn);
@@ -73,7 +75,7 @@ static unsigned int shr_data_offset(struct vtpm_shared_page *shr)
static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
{
- struct tpm_private *priv = TPM_VPRIV(chip);
+ struct tpm_private *priv = dev_get_drvdata(&chip->dev);
struct vtpm_shared_page *shr = priv->shr;
unsigned int offset = shr_data_offset(shr);
@@ -87,8 +89,8 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
return -EINVAL;
/* Wait for completion of any existing command or cancellation */
- if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, chip->vendor.timeout_c,
- &chip->vendor.read_queue, true) < 0) {
+ if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, chip->timeout_c,
+ &priv->read_queue, true) < 0) {
vtpm_cancel(chip);
return -ETIME;
}
@@ -104,7 +106,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
duration = tpm_calc_ordinal_duration(chip, ordinal);
if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, duration,
- &chip->vendor.read_queue, true) < 0) {
+ &priv->read_queue, true) < 0) {
/* got a signal or timeout, try to cancel */
vtpm_cancel(chip);
return -ETIME;
@@ -115,7 +117,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
{
- struct tpm_private *priv = TPM_VPRIV(chip);
+ struct tpm_private *priv = dev_get_drvdata(&chip->dev);
struct vtpm_shared_page *shr = priv->shr;
unsigned int offset = shr_data_offset(shr);
size_t length = shr->length;
@@ -124,8 +126,8 @@ static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
return -ECANCELED;
/* In theory the wait at the end of _send makes this one unnecessary */
- if (wait_for_tpm_stat(chip, VTPM_STATUS_RESULT, chip->vendor.timeout_c,
- &chip->vendor.read_queue, true) < 0) {
+ if (wait_for_tpm_stat(chip, VTPM_STATUS_RESULT, chip->timeout_c,
+ &priv->read_queue, true) < 0) {
vtpm_cancel(chip);
return -ETIME;
}
@@ -161,7 +163,7 @@ static irqreturn_t tpmif_interrupt(int dummy, void *dev_id)
switch (priv->shr->state) {
case VTPM_STATE_IDLE:
case VTPM_STATE_FINISH:
- wake_up_interruptible(&priv->chip->vendor.read_queue);
+ wake_up_interruptible(&priv->read_queue);
break;
case VTPM_STATE_SUBMIT:
case VTPM_STATE_CANCEL:
@@ -179,10 +181,10 @@ static int setup_chip(struct device *dev, struct tpm_private *priv)
if (IS_ERR(chip))
return PTR_ERR(chip);
- init_waitqueue_head(&chip->vendor.read_queue);
+ init_waitqueue_head(&priv->read_queue);
priv->chip = chip;
- TPM_VPRIV(chip) = priv;
+ dev_set_drvdata(&chip->dev, priv);
return 0;
}
@@ -217,7 +219,7 @@ static int setup_ring(struct xenbus_device *dev, struct tpm_private *priv)
xenbus_dev_fatal(dev, rv, "allocating TPM irq");
return rv;
}
- priv->chip->vendor.irq = rv;
+ priv->irq = rv;
again:
rv = xenbus_transaction_start(&xbt);
@@ -277,8 +279,8 @@ static void ring_free(struct tpm_private *priv)
else
free_page((unsigned long)priv->shr);
- if (priv->chip && priv->chip->vendor.irq)
- unbind_from_irqhandler(priv->chip->vendor.irq, priv);
+ if (priv->irq)
+ unbind_from_irqhandler(priv->irq, priv);
kfree(priv);
}
@@ -318,10 +320,10 @@ static int tpmfront_probe(struct xenbus_device *dev,
static int tpmfront_remove(struct xenbus_device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(&dev->dev);
- struct tpm_private *priv = TPM_VPRIV(chip);
+ struct tpm_private *priv = dev_get_drvdata(&chip->dev);
tpm_chip_unregister(chip);
ring_free(priv);
- TPM_VPRIV(chip) = NULL;
+ dev_set_drvdata(&chip->dev, NULL);
return 0;
}