diff options
Diffstat (limited to 'drivers/char/tpm/tpm_tis.c')
-rw-r--r-- | drivers/char/tpm/tpm_tis.c | 184 |
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) { |