forked from Minki/linux
tpm_tis: Fix the probing for interrupts
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 <stefanb@linux.vnet.ibm.com> Signed-off-by: Rajiv Andrade <srajiv@linux.vnet.ibm.com>
This commit is contained in:
parent
20b87bbfad
commit
a7b66822b2
@ -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;
|
||||
|
@ -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,
|
||||
@ -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)))
|
||||
@ -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);
|
||||
@ -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) {
|
||||
@ -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 +
|
||||
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user