X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fchar%2Fipmi%2Fipmi_si_intf.c;h=78e1b962fe35e4c83f6a0516709008d66fd40db5;hb=21ba0f88ae56da82a3a15fe54d729208b64c4f4b;hp=a7b33d2f5991c84fd332ff15dab1145161c1c089;hpb=b22364c8eec89e6b0c081a237f3b6348df87796f;p=linux-2.6-omap-h63xx.git diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index a7b33d2f599..78e1b962fe3 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -9,6 +9,7 @@ * source@mvista.com * * Copyright 2002 MontaVista Software Inc. + * Copyright 2006 IBM Corp., Christian Krafft * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -64,6 +65,11 @@ #include #include +#ifdef CONFIG_PPC_OF +#include +#include +#endif + #define PFX "ipmi_si: " /* Measure times between events in the driver. */ @@ -76,6 +82,12 @@ #define SI_SHORT_TIMEOUT_USEC 250 /* .25ms when the SM request a short timeout */ +/* Bit for BMC global enables. */ +#define IPMI_BMC_RCV_MSG_INTR 0x01 +#define IPMI_BMC_EVT_MSG_INTR 0x02 +#define IPMI_BMC_EVT_MSG_BUFF 0x04 +#define IPMI_BMC_SYS_LOG 0x08 + enum si_intf_state { SI_NORMAL, SI_GETTING_FLAGS, @@ -84,7 +96,9 @@ enum si_intf_state { SI_CLEARING_FLAGS_THEN_SET_IRQ, SI_GETTING_MESSAGES, SI_ENABLE_INTERRUPTS1, - SI_ENABLE_INTERRUPTS2 + SI_ENABLE_INTERRUPTS2, + SI_DISABLE_INTERRUPTS1, + SI_DISABLE_INTERRUPTS2 /* FIXME - add watchdog stuff. */ }; @@ -333,6 +347,17 @@ static void start_enable_irq(struct smi_info *smi_info) smi_info->si_state = SI_ENABLE_INTERRUPTS1; } +static void start_disable_irq(struct smi_info *smi_info) +{ + unsigned char msg[2]; + + msg[0] = (IPMI_NETFN_APP_REQUEST << 2); + msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; + + smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2); + smi_info->si_state = SI_DISABLE_INTERRUPTS1; +} + static void start_clear_flags(struct smi_info *smi_info) { unsigned char msg[3]; @@ -353,7 +378,7 @@ static void start_clear_flags(struct smi_info *smi_info) static inline void disable_si_irq(struct smi_info *smi_info) { if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { - disable_irq_nosync(smi_info->irq); + start_disable_irq(smi_info); smi_info->interrupt_disabled = 1; } } @@ -361,7 +386,7 @@ static inline void disable_si_irq(struct smi_info *smi_info) static inline void enable_si_irq(struct smi_info *smi_info) { if ((smi_info->irq) && (smi_info->interrupt_disabled)) { - enable_irq(smi_info->irq); + start_enable_irq(smi_info); smi_info->interrupt_disabled = 0; } } @@ -583,7 +608,9 @@ static void handle_transaction_done(struct smi_info *smi_info) } else { msg[0] = (IPMI_NETFN_APP_REQUEST << 2); msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; - msg[2] = msg[3] | 1; /* enable msg queue int */ + msg[2] = (msg[3] | + IPMI_BMC_RCV_MSG_INTR | + IPMI_BMC_EVT_MSG_INTR); smi_info->handlers->start_transaction( smi_info->si_sm, msg, 3); smi_info->si_state = SI_ENABLE_INTERRUPTS2; @@ -605,6 +632,45 @@ static void handle_transaction_done(struct smi_info *smi_info) smi_info->si_state = SI_NORMAL; break; } + + case SI_DISABLE_INTERRUPTS1: + { + unsigned char msg[4]; + + /* We got the flags from the SMI, now handle them. */ + smi_info->handlers->get_result(smi_info->si_sm, msg, 4); + if (msg[2] != 0) { + printk(KERN_WARNING + "ipmi_si: Could not disable interrupts" + ", failed get.\n"); + smi_info->si_state = SI_NORMAL; + } else { + msg[0] = (IPMI_NETFN_APP_REQUEST << 2); + msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; + msg[2] = (msg[3] & + ~(IPMI_BMC_RCV_MSG_INTR | + IPMI_BMC_EVT_MSG_INTR)); + smi_info->handlers->start_transaction( + smi_info->si_sm, msg, 3); + smi_info->si_state = SI_DISABLE_INTERRUPTS2; + } + break; + } + + case SI_DISABLE_INTERRUPTS2: + { + unsigned char msg[4]; + + /* We got the flags from the SMI, now handle them. */ + smi_info->handlers->get_result(smi_info->si_sm, msg, 4); + if (msg[2] != 0) { + printk(KERN_WARNING + "ipmi_si: Could not disable interrupts" + ", failed set.\n"); + } + smi_info->si_state = SI_NORMAL; + break; + } } } @@ -858,9 +924,6 @@ static void smi_timeout(unsigned long data) struct timeval t; #endif - if (atomic_read(&smi_info->stop_operation)) - return; - spin_lock_irqsave(&(smi_info->si_lock), flags); #ifdef DEBUG_TIMING do_gettimeofday(&t); @@ -916,15 +979,11 @@ static irqreturn_t si_irq_handler(int irq, void *data) smi_info->interrupts++; spin_unlock(&smi_info->count_lock); - if (atomic_read(&smi_info->stop_operation)) - goto out; - #ifdef DEBUG_TIMING do_gettimeofday(&t); printk("**Interrupt: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif smi_event_handler(smi_info, 0); - out: spin_unlock_irqrestore(&(smi_info->si_lock), flags); return IRQ_HANDLED; } @@ -1006,6 +1065,7 @@ static DEFINE_MUTEX(smi_infos_lock); static int smi_num; /* Used to sequence the SMIs */ #define DEFAULT_REGSPACING 1 +#define DEFAULT_REGSIZE 1 static int si_trydefaults = 1; static char *si_type[SI_MAX_PARMS]; @@ -1111,7 +1171,7 @@ static int std_irq_setup(struct smi_info *info) if (info->si_type == SI_BT) { rv = request_irq(info->irq, si_bt_irq_handler, - IRQF_DISABLED, + IRQF_SHARED | IRQF_DISABLED, DEVICE_NAME, info); if (!rv) @@ -1121,7 +1181,7 @@ static int std_irq_setup(struct smi_info *info) } else rv = request_irq(info->irq, si_irq_handler, - IRQF_DISABLED, + IRQF_SHARED | IRQF_DISABLED, DEVICE_NAME, info); if (rv) { @@ -1701,15 +1761,11 @@ static u32 ipmi_acpi_gpe(void *context) smi_info->interrupts++; spin_unlock(&smi_info->count_lock); - if (atomic_read(&smi_info->stop_operation)) - goto out; - #ifdef DEBUG_TIMING do_gettimeofday(&t); printk("**ACPI_GPE: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif smi_event_handler(smi_info, 0); - out: spin_unlock_irqrestore(&(smi_info->si_lock), flags); return ACPI_INTERRUPT_HANDLED; @@ -1859,10 +1915,10 @@ static __devinit int try_init_acpi(struct SPMITable *spmi) if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { info->io_setup = mem_setup; - info->io.addr_type = IPMI_IO_ADDR_SPACE; + info->io.addr_type = IPMI_MEM_ADDR_SPACE; } else if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_IO) { info->io_setup = port_setup; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; + info->io.addr_type = IPMI_IO_ADDR_SPACE; } else { kfree(info); printk("ipmi_si: Unknown ACPI I/O Address type\n"); @@ -2133,12 +2189,15 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, info->irq_setup = std_irq_setup; info->dev = &pdev->dev; + pci_set_drvdata(pdev, info); return try_smi_init(info); } static void __devexit ipmi_pci_remove(struct pci_dev *pdev) { + struct smi_info *info = pci_get_drvdata(pdev); + cleanup_one_si(info); } #ifdef CONFIG_PM @@ -2172,6 +2231,99 @@ static struct pci_driver ipmi_pci_driver = { #endif /* CONFIG_PCI */ +#ifdef CONFIG_PPC_OF +static int __devinit ipmi_of_probe(struct of_device *dev, + const struct of_device_id *match) +{ + struct smi_info *info; + struct resource resource; + const int *regsize, *regspacing, *regshift; + struct device_node *np = dev->node; + int ret; + int proplen; + + dev_info(&dev->dev, PFX "probing via device tree\n"); + + ret = of_address_to_resource(np, 0, &resource); + if (ret) { + dev_warn(&dev->dev, PFX "invalid address from OF\n"); + return ret; + } + + regsize = get_property(np, "reg-size", &proplen); + if (regsize && proplen != 4) { + dev_warn(&dev->dev, PFX "invalid regsize from OF\n"); + return -EINVAL; + } + + regspacing = get_property(np, "reg-spacing", &proplen); + if (regspacing && proplen != 4) { + dev_warn(&dev->dev, PFX "invalid regspacing from OF\n"); + return -EINVAL; + } + + regshift = get_property(np, "reg-shift", &proplen); + if (regshift && proplen != 4) { + dev_warn(&dev->dev, PFX "invalid regshift from OF\n"); + return -EINVAL; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + + if (!info) { + dev_err(&dev->dev, + PFX "could not allocate memory for OF probe\n"); + return -ENOMEM; + } + + info->si_type = (enum si_type) match->data; + info->addr_source = "device-tree"; + info->io_setup = mem_setup; + info->irq_setup = std_irq_setup; + + info->io.addr_type = IPMI_MEM_ADDR_SPACE; + info->io.addr_data = resource.start; + + info->io.regsize = regsize ? *regsize : DEFAULT_REGSIZE; + info->io.regspacing = regspacing ? *regspacing : DEFAULT_REGSPACING; + info->io.regshift = regshift ? *regshift : 0; + + info->irq = irq_of_parse_and_map(dev->node, 0); + info->dev = &dev->dev; + + dev_dbg(&dev->dev, "addr 0x%lx regsize %ld spacing %ld irq %x\n", + info->io.addr_data, info->io.regsize, info->io.regspacing, + info->irq); + + dev->dev.driver_data = (void*) info; + + return try_smi_init(info); +} + +static int __devexit ipmi_of_remove(struct of_device *dev) +{ + cleanup_one_si(dev->dev.driver_data); + return 0; +} + +static struct of_device_id ipmi_match[] = +{ + { .type = "ipmi", .compatible = "ipmi-kcs", .data = (void *)(unsigned long) SI_KCS }, + { .type = "ipmi", .compatible = "ipmi-smic", .data = (void *)(unsigned long) SI_SMIC }, + { .type = "ipmi", .compatible = "ipmi-bt", .data = (void *)(unsigned long) SI_BT }, + {}, +}; + +static struct of_platform_driver ipmi_of_platform_driver = +{ + .name = "ipmi", + .match_table = ipmi_match, + .probe = ipmi_of_probe, + .remove = __devexit_p(ipmi_of_remove), +}; +#endif /* CONFIG_PPC_OF */ + + static int try_get_dev_id(struct smi_info *smi_info) { unsigned char msg[2]; @@ -2478,6 +2630,11 @@ static __devinit void default_find_bmc(void) if (!info) return; +#ifdef CONFIG_PPC_MERGE + if (check_legacy_ioport(ipmi_defaults[i].port)) + continue; +#endif + info->addr_source = NULL; info->si_type = ipmi_defaults[i].type; @@ -2796,6 +2953,10 @@ static __devinit int init_ipmi_si(void) } #endif +#ifdef CONFIG_PPC_OF + of_register_platform_driver(&ipmi_of_platform_driver); +#endif + if (si_trydefaults) { mutex_lock(&smi_infos_lock); if (list_empty(&smi_infos)) { @@ -2813,6 +2974,10 @@ static __devinit int init_ipmi_si(void) #ifdef CONFIG_PCI pci_unregister_driver(&ipmi_pci_driver); #endif + +#ifdef CONFIG_PPC_OF + of_unregister_platform_driver(&ipmi_of_platform_driver); +#endif driver_unregister(&ipmi_driver); printk("ipmi_si: Unable to find any System Interface(s)\n"); return -ENODEV; @@ -2833,28 +2998,33 @@ static void cleanup_one_si(struct smi_info *to_clean) list_del(&to_clean->link); - /* Tell the timer and interrupt handlers that we are shutting - down. */ - spin_lock_irqsave(&(to_clean->si_lock), flags); - spin_lock(&(to_clean->msg_lock)); - + /* Tell the driver that we are shutting down. */ atomic_inc(&to_clean->stop_operation); - if (to_clean->irq_cleanup) - to_clean->irq_cleanup(to_clean); - - spin_unlock(&(to_clean->msg_lock)); - spin_unlock_irqrestore(&(to_clean->si_lock), flags); - - /* Wait until we know that we are out of any interrupt - handlers might have been running before we freed the - interrupt. */ - synchronize_sched(); - + /* Make sure the timer and thread are stopped and will not run + again. */ wait_for_timer_and_thread(to_clean); - /* Interrupts and timeouts are stopped, now make sure the - interface is in a clean state. */ + /* Timeouts are stopped, now make sure the interrupts are off + for the device. A little tricky with locks to make sure + there are no races. */ + spin_lock_irqsave(&to_clean->si_lock, flags); + while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { + spin_unlock_irqrestore(&to_clean->si_lock, flags); + poll(to_clean); + schedule_timeout_uninterruptible(1); + spin_lock_irqsave(&to_clean->si_lock, flags); + } + disable_si_irq(to_clean); + spin_unlock_irqrestore(&to_clean->si_lock, flags); + while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { + poll(to_clean); + schedule_timeout_uninterruptible(1); + } + + /* Clean up interrupts and make sure that everything is done. */ + if (to_clean->irq_cleanup) + to_clean->irq_cleanup(to_clean); while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { poll(to_clean); schedule_timeout_uninterruptible(1); @@ -2893,6 +3063,10 @@ static __exit void cleanup_ipmi_si(void) pci_unregister_driver(&ipmi_pci_driver); #endif +#ifdef CONFIG_PPC_OF + of_unregister_platform_driver(&ipmi_of_platform_driver); +#endif + mutex_lock(&smi_infos_lock); list_for_each_entry_safe(e, tmp_e, &smi_infos, link) cleanup_one_si(e);