tpmdd-devel.lists.sourceforge.net archive mirror
 help / color / mirror / Atom feed
From: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
To: Christophe Ricard <christophe.ricard@gmail.com>
Cc: jgunthorpe@obsidianresearch.com, peterhuewe@gmx.de,
	ashley@ashleylai.com, tpmdd@selhorst.net,
	tpmdd-devel@lists.sourceforge.net, christophe-h.ricard@st.com,
	jean-luc.blanc@st.com, benoit.houyere@st.com,
	Alexander.Steffen@infineon.com,
	linux-security-module@vger.kernel.org,
	Peter Huewe <peter.huewe@infineon.com>
Subject: Re: [PATCH v8 6/7] tpm/tpm_tis: Split tpm_tis driver into a core and TCG TIS compliant phy
Date: Tue, 24 May 2016 00:24:33 +0300	[thread overview]
Message-ID: <20160523212433.GD30456@intel.com> (raw)
In-Reply-To: <1463610953-2766-7-git-send-email-christophe-h.ricard@st.com>

On Thu, May 19, 2016 at 12:35:52AM +0200, Christophe Ricard wrote:
> From: Christophe Ricard <christophe.ricard@gmail.com>
> 
> To avoid code duplication between the old tpm_tis and the new and future
> native tcg tis driver(ie: spi, i2c...), the tpm_tis driver was reworked,
> so that all common logic is extracted and can be reused from all drivers.
> 
> The core methods can also be used from other TIS like drivers.
> 
> itpm workaround is now managed with a specific tis flag
> TPM_TIS_ITPM_POSSIBLE.
> 
> Signed-off-by: Peter Huewe <peter.huewe@infineon.com>
> Signed-off-by: Christophe Ricard <christophe-h.ricard@st.com>

LGTM, have to test this though first.

/Jarkko

> ---
>  drivers/char/tpm/Kconfig        |   7 +
>  drivers/char/tpm/Makefile       |   1 +
>  drivers/char/tpm/tpm_tis.c      | 902 +---------------------------------------
>  drivers/char/tpm/tpm_tis_core.c | 862 ++++++++++++++++++++++++++++++++++++++
>  drivers/char/tpm/tpm_tis_core.h |  68 +++
>  5 files changed, 958 insertions(+), 882 deletions(-)
>  create mode 100644 drivers/char/tpm/tpm_tis_core.c
> 
> diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
> index bfdc5c0..241ff4c 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
> diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
> index 98de5e6..662221f 100644
> --- a/drivers/char/tpm/Makefile
> +++ b/drivers/char/tpm/Makefile
> @@ -12,6 +12,7 @@ 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_I2C_ATMEL) += tpm_i2c_atmel.o
>  obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
> diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
> index 29254f0..eaf5730 100644
> --- a/drivers/char/tpm/tpm_tis.c
> +++ b/drivers/char/tpm/tpm_tis.c
> @@ -31,40 +31,6 @@
>  #include "tpm.h"
>  #include "tpm_tis_core.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 */
> -};
> -
>  struct tpm_info {
>  	struct resource res;
>  	/* irq > 0 means: use irq $irq;
> @@ -74,26 +40,6 @@ 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 tpm_tis_tcg_phy {
>  	struct tpm_tis_data priv;
>  	void __iomem *iobase;
> @@ -104,6 +50,20 @@ static inline struct tpm_tis_tcg_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *da
>  	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)
>  {
> @@ -127,484 +87,6 @@ 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)
> -{
> -	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
> -	unsigned long stop = jiffies + chip->timeout_a;
> -	int rc;
> -	u8 access;
> -
> -	do {
> -		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;
> -	u8 value;
> -
> -	/* wait for burstcount */
> -	/* which timeout value, spec has 2 answers (c & d) */
> -	stop = jiffies + chip->timeout_d;
> -	do {
> -		rc = tpm_tis_read8(priv, TPM_STS(priv->locality) + 1, &value);
> -		if (rc < 0)
> -			return rc;
> -
> -		burstcnt = value;
> -		rc = tpm_tis_read8(priv, TPM_STS(priv->locality) + 2, &value);
> -		if (rc < 0)
> -			return rc;
> -
> -		burstcnt += value << 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)
> -{
> -	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;
> -	}
> -
> -	/* read first 10 bytes, including tag, paramsize, and result */
> -	if ((size =
> -	     recv_data(chip, buf, TPM_HEADER_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;
> -	}
> -
> -	if ((size +=
> -	     recv_data(chip, &buf[TPM_HEADER_SIZE],
> -		       expected - TPM_HEADER_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;
> -}
> -
> -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)
> -{
> -	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
> -	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->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 ((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;
> -	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)
> -		return rc;
> -
> -	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)
> -{
> -	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
> -	int rc, irq;
> -
> -	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 rem_itpm = 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:
> -	itpm = rem_itpm;
> -	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 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,
> -};
> -
>  static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
>  			      u8 *result)
>  {
> @@ -657,367 +139,29 @@ static const struct tpm_tis_phy_ops tpm_tcg = {
>  	.write32 = tpm_tcg_write32,
>  };
>  
> -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, TPM_INT_VECTOR(priv->locality),
> -				original_int_vec);
> -		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;
> -}
> -
> -static bool interrupts = true;
> -module_param(interrupts, bool, 0444);
> -MODULE_PARM_DESC(interrupts, "Enable interrupts");
> -
> -static 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);
> -}
> -
>  static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
>  			acpi_handle acpi_dev_handle)
>  {
> -	u32 vendor, intfcaps, intmask;
> -	u8 rid;
> -	int rc, probe;
> -	struct tpm_chip *chip;
>  	struct tpm_tis_tcg_phy *phy;
> +	int irq = -1;
>  
>  	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);
> -
> -#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);
>  
> -	phy->priv.phy_ops = &tpm_tcg;
> -
> -	/* Maximum timeouts */
> -	chip->timeout_a = TIS_TIMEOUT_A_MAX;
> -	chip->timeout_b = TIS_TIMEOUT_B_MAX;
> -	chip->timeout_c = TIS_TIMEOUT_C_MAX;
> -	chip->timeout_d = TIS_TIMEOUT_D_MAX;
> -
> -	dev_set_drvdata(&chip->dev, &phy->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(&phy->priv, TPM_INT_ENABLE(phy->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(&phy->priv, TPM_INT_ENABLE(phy->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(&phy->priv, TPM_DID_VID(0), &vendor);
> -	if (rc < 0)
> -		goto out_err;
> -
> -	phy->priv.manufacturer_id = vendor;
> -
> -	rc = tpm_tis_read8(&phy->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 (!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 */
> -	rc = tpm_tis_read32(&phy->priv, TPM_INTF_CAPS(phy->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;
> -	}
> +		phy->priv.flags |= TPM_TIS_ITPM_POSSIBLE;
>  
> -	/* INTERRUPT Setup */
> -	init_waitqueue_head(&phy->priv.read_queue);
> -	init_waitqueue_head(&phy->priv.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->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);
> -	}
> -
> -	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;
> -}
> -
> -#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);
> -}
> -
> -static 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;
> +	return tpm_tis_core_init(dev, &phy->priv, irq, &tpm_tcg,
> +				 acpi_dev_handle);
>  }
> -#endif
>  
>  static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
>  
> @@ -1219,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 0000000..03a06b3
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_tis_core.c
> @@ -0,0 +1,862 @@
> +/*
> + * 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;
> +	u8 value;
> +
> +	/* wait for burstcount */
> +	/* which timeout value, spec has 2 answers (c & d) */
> +	stop = jiffies + chip->timeout_d;
> +	do {
> +		rc = tpm_tis_read8(priv, TPM_STS(priv->locality) + 1, &value);
> +		if (rc < 0)
> +			return rc;
> +
> +		burstcnt = value;
> +		rc = tpm_tis_read8(priv, TPM_STS(priv->locality) + 2, &value);
> +		if (rc < 0)
> +			return rc;
> +
> +		burstcnt += value << 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)
> +{
> +	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 = {
> +	.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 = TIS_TIMEOUT_A_MAX;
> +	chip->timeout_b = TIS_TIMEOUT_B_MAX;
> +	chip->timeout_c = TIS_TIMEOUT_C_MAX;
> +	chip->timeout_d = 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);
> +		}
> +	}
> +
> +	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;
> +}
> +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
> index b922100..9191aab 100644
> --- a/drivers/char/tpm/tpm_tis_core.h
> +++ b/drivers/char/tpm/tpm_tis_core.h
> @@ -25,11 +25,70 @@
>  
>  #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;
> @@ -85,4 +144,13 @@ static inline int tpm_tis_write32(struct tpm_tis_data *data, u32 addr,
>  	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
> -- 
> 2.1.4
> 

  reply	other threads:[~2016-05-23 21:24 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-05-18 22:35 [PATCH v8 0/7] Rework of tpm_tis to share common logic across phy's (lpc/spi/-i2c-) Christophe Ricard
2016-05-18 22:35 ` [PATCH v8 2/7] tpm: tpm_tis: Share common data between phys Christophe Ricard
2016-05-18 22:35 ` [PATCH v8 3/7] tpm_tis: Introduce intermediate layer for TPM access Christophe Ricard
     [not found]   ` <1463610953-2766-4-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
2016-05-23 21:20     ` Jarkko Sakkinen
2016-05-18 22:35 ` [PATCH v8 4/7] devicetree: Add infineon to vendor-prefix.txt Christophe Ricard
2016-05-23 21:21   ` Jarkko Sakkinen
     [not found] ` <1463610953-2766-1-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
2016-05-18 22:35   ` [PATCH v8 1/7] tpm: Add include guards in tpm.h Christophe Ricard
2016-05-18 22:35   ` [PATCH v8 5/7] devicetree: Add Trusted Computing Group to vendor-prefix.txt Christophe Ricard
     [not found]     ` <1463610953-2766-6-git-send-email-christophe-h.ricard-qxv4g6HH51o@public.gmane.org>
2016-05-23 21:22       ` Jarkko Sakkinen
2016-05-18 22:35   ` [PATCH v8 7/7] tpm/tpm_tis_spi: Add support for spi phy Christophe Ricard
2016-06-17 18:05   ` [PATCH v8 0/7] Rework of tpm_tis to share common logic across phy's (lpc/spi/-i2c-) Jarkko Sakkinen
2016-05-18 22:35 ` [PATCH v8 6/7] tpm/tpm_tis: Split tpm_tis driver into a core and TCG TIS compliant phy Christophe Ricard
2016-05-23 21:24   ` Jarkko Sakkinen [this message]
2016-05-24  6:58   ` Jarkko Sakkinen
2016-05-23 21:31 ` [PATCH v8 0/7] Rework of tpm_tis to share common logic across phy's (lpc/spi/-i2c-) Jarkko Sakkinen
2016-05-26 12:26 ` Jarkko Sakkinen
     [not found]   ` <20160526131932.C95EDC6047@b03ledav006.gho.boulder.ibm.com>
     [not found]     ` <20160526131932.C95EDC6047-YTr1IC4v7wD031Vs2vNS4j5RcGG8+4aCqyM6JfAXOaQ@public.gmane.org>
2016-05-26 16:05       ` Christophe Ricard
     [not found]   ` <20160526131932.30F57112056@b01ledav004.gho.pok.ibm.com>
2016-05-26 16:35     ` [tpmdd-devel] " Jarkko Sakkinen
     [not found]   ` <20160526122635.GA31557-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
2016-05-26 13:19     ` Stefan Berger
2016-05-28  8:59     ` Peter Huewe
2016-05-30 18:36       ` Jarkko Sakkinen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160523212433.GD30456@intel.com \
    --to=jarkko.sakkinen@linux.intel.com \
    --cc=Alexander.Steffen@infineon.com \
    --cc=ashley@ashleylai.com \
    --cc=benoit.houyere@st.com \
    --cc=christophe-h.ricard@st.com \
    --cc=christophe.ricard@gmail.com \
    --cc=jean-luc.blanc@st.com \
    --cc=jgunthorpe@obsidianresearch.com \
    --cc=linux-security-module@vger.kernel.org \
    --cc=peter.huewe@infineon.com \
    --cc=peterhuewe@gmx.de \
    --cc=tpmdd-devel@lists.sourceforge.net \
    --cc=tpmdd@selhorst.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).