Skip to content

Commit

Permalink
tpm_tis: Fix the probing for interrupts
Browse files Browse the repository at this point in the history
This patch fixes several aspects of the probing for interrupts.

This patch reads the TPM's timeouts before probing for the interrupts. The
tpm_get_timeouts() function is invoked in polling mode and gets the proper
timeouts from the TPM so that we don't need to fall back to 2 minutes timeouts
for short duration commands while the interrupt probing is happening.

This patch introduces a variable probed_irq into the vendor structure that gets
the irq number if an interrupt is received while the the tpm_gen_interrupt()
function is run in polling mode during interrupt probing. Previously some
parts of tpm_gen_interrupt() were run in polling mode, then the irq variable
was set in the interrupt handler when an interrupt was received and execution
of tpm_gen_interrupt() ended up switching over to interrupt mode.
tpm_gen_interrupt() execution ended up on an event queue where it eventually
timed out since the probing handler doesn't wake any queues.

Before calling into free_irq() clear all interrupt flags that may have
been set by the TPM. The reason is that free_irq() will call into the probing
interrupt handler and may otherwise fool us into thinking that a real interrupt
happened (because we see the flags as being set) while the TPM's interrupt line
is not even connected to anything on the motherboard. This solves a problem
on one machine I did testing on (Thinkpad T60).

If a TPM claims to use a specifc interrupt, the probing is done as well
to verify that the interrupt is actually working. If a TPM indicates
that it does not use a specific interrupt (returns '0'), probe all interrupts
from 3 to 15.

Signed-off-by: Stefan Berger <[email protected]>
Signed-off-by: Rajiv Andrade <[email protected]>
  • Loading branch information
stefanberger authored and srajiv committed Jul 12, 2011
1 parent 20b87bb commit a7b6682
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 6 deletions.
1 change: 1 addition & 0 deletions drivers/char/tpm/tpm.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ struct tpm_vendor_specific {
unsigned long base; /* TPM base address */

int irq;
int probed_irq;

int region_size;
int have_region;
Expand Down
33 changes: 27 additions & 6 deletions drivers/char/tpm/tpm_tis.c
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ static irqreturn_t tis_int_probe(int irq, void *dev_id)
if (interrupt == 0)
return IRQ_NONE;

chip->vendor.irq = irq;
chip->vendor.probed_irq = irq;

/* Clear interrupts handled with TPM_EOI */
iowrite32(interrupt,
Expand Down Expand Up @@ -486,7 +486,7 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
resource_size_t len, unsigned int irq)
{
u32 vendor, intfcaps, intmask;
int rc, i;
int rc, i, irq_s, irq_e;
struct tpm_chip *chip;

if (!(chip = tpm_register_hardware(dev, &tpm_tis)))
Expand Down Expand Up @@ -544,6 +544,9 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
dev_dbg(dev, "\tData Avail Int Support\n");

/* get the timeouts before testing for irqs */
tpm_get_timeouts(chip);

/* INTERRUPT Setup */
init_waitqueue_head(&chip->vendor.read_queue);
init_waitqueue_head(&chip->vendor.int_queue);
Expand All @@ -562,13 +565,19 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
if (interrupts)
chip->vendor.irq = irq;
if (interrupts && !chip->vendor.irq) {
chip->vendor.irq =
irq_s =
ioread8(chip->vendor.iobase +
TPM_INT_VECTOR(chip->vendor.locality));
if (irq_s) {
irq_e = irq_s;
} else {
irq_s = 3;
irq_e = 15;
}

for (i = 3; i < 16 && chip->vendor.irq == 0; i++) {
for (i = irq_s; i <= irq_e && chip->vendor.irq == 0; i++) {
iowrite8(i, chip->vendor.iobase +
TPM_INT_VECTOR(chip->vendor.locality));
TPM_INT_VECTOR(chip->vendor.locality));
if (request_irq
(i, tis_int_probe, IRQF_SHARED,
chip->vendor.miscdev.name, chip) != 0) {
Expand All @@ -590,9 +599,22 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
chip->vendor.iobase +
TPM_INT_ENABLE(chip->vendor.locality));

chip->vendor.probed_irq = 0;

/* Generate Interrupts */
tpm_gen_interrupt(chip);

chip->vendor.irq = chip->vendor.probed_irq;

/* free_irq will call into tis_int_probe;
clear all irqs we haven't seen while doing
tpm_gen_interrupt */
iowrite32(ioread32
(chip->vendor.iobase +
TPM_INT_STATUS(chip->vendor.locality)),
chip->vendor.iobase +
TPM_INT_STATUS(chip->vendor.locality));

/* Turn off */
iowrite32(intmask,
chip->vendor.iobase +
Expand Down Expand Up @@ -631,7 +653,6 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
list_add(&chip->vendor.list, &tis_chips);
spin_unlock(&tis_lock);

tpm_get_timeouts(chip);
tpm_continue_selftest(chip);

return 0;
Expand Down

0 comments on commit a7b6682

Please sign in to comment.