summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/char/tpm/tpm_tis.c184
1 files changed, 84 insertions, 100 deletions
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 2580893de023..c29ae69f1df6 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -99,7 +99,6 @@ static struct tpm_info tis_default_info = {
#define TPM_RID(l) (0x0F04 | ((l) << 12))
struct priv_data {
- bool irq_probing;
bool irq_tested;
};
@@ -462,12 +461,8 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
chip->vendor.irq = irq;
if (!priv->irq_tested)
msleep(1);
- if (!priv->irq_tested) {
+ if (!priv->irq_tested)
disable_interrupts(chip);
- if (!priv->irq_probing)
- dev_err(chip->pdev, FW_BUG
- "TPM interrupt not working, polling instead\n");
- }
priv->irq_tested = true;
return rc;
}
@@ -604,6 +599,80 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)
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 irq)
+{
+ struct priv_data *priv = chip->vendor.priv;
+ u8 original_int_vec;
+
+ if (devm_request_irq(chip->pdev, irq, tis_int_handler, IRQF_SHARED,
+ 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;
+ }
+
+ 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) {
+ for (i = 3; i <= 15; i++)
+ if (!tpm_tis_probe_irq_single(chip, intmask, i))
+ return;
+ } else if (!tpm_tis_probe_irq_single(chip, intmask, original_int_vec))
+ return;
+}
+
static bool interrupts = true;
module_param(interrupts, bool, 0444);
MODULE_PARM_DESC(interrupts, "Enable interrupts");
@@ -626,8 +695,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
acpi_handle acpi_dev_handle)
{
u32 vendor, intfcaps, intmask;
- int rc, i, irq_s, irq_e, probe;
- int irq_r = -1;
+ int rc, probe;
struct tpm_chip *chip;
struct priv_data *priv;
@@ -735,98 +803,14 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info,
/* INTERRUPT Setup */
init_waitqueue_head(&chip->vendor.read_queue);
init_waitqueue_head(&chip->vendor.int_queue);
-
- if (interrupts)
- chip->vendor.irq = tpm_info->irq;
- if (interrupts && !chip->vendor.irq) {
- irq_s =
- ioread8(chip->vendor.iobase +
- TPM_INT_VECTOR(chip->vendor.locality));
- irq_r = irq_s;
- if (irq_s) {
- irq_e = irq_s;
- } else {
- irq_s = 3;
- irq_e = 15;
- }
-
- for (i = irq_s; i <= irq_e && chip->vendor.irq == 0; i++) {
- iowrite8(i, chip->vendor.iobase +
- TPM_INT_VECTOR(chip->vendor.locality));
- if (devm_request_irq
- (dev, i, tis_int_handler, IRQF_SHARED,
- chip->devname, chip) != 0) {
- dev_info(chip->pdev,
- "Unable to request irq: %d for probe\n",
- i);
- continue;
- }
- chip->vendor.irq = i;
-
- /* 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;
- priv->irq_probing = true;
-
- /* Generate Interrupts */
- if (chip->flags & TPM_CHIP_FLAG_TPM2)
- tpm2_gen_interrupt(chip);
- else
- tpm_gen_interrupt(chip);
-
- priv->irq_probing = false;
-
- /* 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)
- break;
- }
- if (!chip->vendor.irq)
- iowrite8(irq_r, chip->vendor.iobase +
- TPM_INT_VECTOR(chip->vendor.locality));
- }
- if (chip->vendor.irq && !priv->irq_tested) {
- iowrite8(chip->vendor.irq,
- chip->vendor.iobase +
- TPM_INT_VECTOR(chip->vendor.locality));
- if (devm_request_irq
- (dev, chip->vendor.irq, tis_int_handler, IRQF_SHARED,
- chip->devname, chip) != 0) {
- dev_info(chip->pdev,
- "Unable to request irq: %d for use\n",
- chip->vendor.irq);
- chip->vendor.irq = 0;
- } else {
- /* 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));
- }
- }
-
- if (tpm_get_timeouts(chip)) {
- dev_err(dev, "Could not get TPM timeouts and durations\n");
- rc = -ENODEV;
- goto out_err;
+ if (interrupts) {
+ if (tpm_info->irq) {
+ tpm_tis_probe_irq_single(chip, intmask, 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);
}
if (chip->flags & TPM_CHIP_FLAG_TPM2) {