diff options
Diffstat (limited to 'drivers/char/ipmi/ipmi_si_intf.c')
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 65 |
1 files changed, 55 insertions, 10 deletions
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 29de259a981..5419440087f 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -100,6 +100,11 @@ enum si_intf_state { /* FIXME - add watchdog stuff. */ }; +/* Some BT-specific defines we need here. */ +#define IPMI_BT_INTMASK_REG 2 +#define IPMI_BT_INTMASK_CLEAR_IRQ_BIT 2 +#define IPMI_BT_INTMASK_ENABLE_IRQ_BIT 1 + enum si_type { SI_KCS, SI_SMIC, SI_BT }; @@ -875,6 +880,17 @@ static irqreturn_t si_irq_handler(int irq, void *data, struct pt_regs *regs) return IRQ_HANDLED; } +static irqreturn_t si_bt_irq_handler(int irq, void *data, struct pt_regs *regs) +{ + struct smi_info *smi_info = data; + /* We need to clear the IRQ flag for the BT interface. */ + smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG, + IPMI_BT_INTMASK_CLEAR_IRQ_BIT + | IPMI_BT_INTMASK_ENABLE_IRQ_BIT); + return si_irq_handler(irq, data, regs); +} + + static struct ipmi_smi_handlers handlers = { .owner = THIS_MODULE, @@ -1001,11 +1017,22 @@ static int std_irq_setup(struct smi_info *info) if (!info->irq) return 0; - rv = request_irq(info->irq, - si_irq_handler, - SA_INTERRUPT, - DEVICE_NAME, - info); + if (info->si_type == SI_BT) { + rv = request_irq(info->irq, + si_bt_irq_handler, + SA_INTERRUPT, + DEVICE_NAME, + info); + if (!rv) + /* Enable the interrupt in the BT interface. */ + info->io.outputb(&info->io, IPMI_BT_INTMASK_REG, + IPMI_BT_INTMASK_ENABLE_IRQ_BIT); + } else + rv = request_irq(info->irq, + si_irq_handler, + SA_INTERRUPT, + DEVICE_NAME, + info); if (rv) { printk(KERN_WARNING "ipmi_si: %s unable to claim interrupt %d," @@ -1024,6 +1051,9 @@ static void std_irq_cleanup(struct smi_info *info) if (!info->irq) return; + if (info->si_type == SI_BT) + /* Disable the interrupt in the BT interface. */ + info->io.outputb(&info->io, IPMI_BT_INTMASK_REG, 0); free_irq(info->irq, info); } @@ -1526,8 +1556,17 @@ static int try_init_acpi(int intf_num, struct smi_info **new_info) info->irq_setup = NULL; } - regspacings[intf_num] = spmi->addr.register_bit_width / 8; - info->io.regspacing = spmi->addr.register_bit_width / 8; + if (spmi->addr.register_bit_width) { + /* A (hopefully) properly formed register bit width. */ + regspacings[intf_num] = spmi->addr.register_bit_width / 8; + info->io.regspacing = spmi->addr.register_bit_width / 8; + } else { + /* Some broken systems get this wrong and set the value + * to zero. Assume it is the default spacing. If that + * is wrong, too bad, the vendor should fix the tables. */ + regspacings[intf_num] = DEFAULT_REGSPACING; + info->io.regspacing = DEFAULT_REGSPACING; + } regsizes[intf_num] = regspacings[intf_num]; info->io.regsize = regsizes[intf_num]; regshifts[intf_num] = spmi->addr.register_bit_offset; @@ -1623,7 +1662,13 @@ static int decode_dmi(dmi_header_t *dm, int intf_num) } } else { /* Old DMI spec. */ - ipmi_data->base_addr = base_addr; + /* Note that technically, the lower bit of the base + * address should be 1 if the address is I/O and 0 if + * the address is in memory. So many systems get that + * wrong (and all that I have seen are I/O) so we just + * ignore that bit and assume I/O. Systems that use + * memory should use the newer spec, anyway. */ + ipmi_data->base_addr = base_addr & 0xfffe; ipmi_data->addr_space = IPMI_IO_ADDR_SPACE; ipmi_data->offset = 1; } @@ -2199,7 +2244,7 @@ static int init_one_smi(int intf_num, struct smi_info **smi) /* Wait until we know that we are out of any interrupt handlers might have been running before we freed the interrupt. */ - synchronize_kernel(); + synchronize_sched(); if (new_smi->si_sm) { if (new_smi->handlers) @@ -2312,7 +2357,7 @@ static void __exit cleanup_one_si(struct smi_info *to_clean) /* Wait until we know that we are out of any interrupt handlers might have been running before we freed the interrupt. */ - synchronize_kernel(); + synchronize_sched(); /* Wait for the timer to stop. This avoids problems with race conditions removing the timer here. */ |