X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fchar%2Fipmi%2Fipmi_si_intf.c;fp=drivers%2Fchar%2Fipmi%2Fipmi_si_intf.c;h=fd63716767813cbd1806617d696432994a2f16d6;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=5292258cc4fc5b6a48ccb5f446edd02d8b56126b;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 5292258cc..fd6371676 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -38,6 +38,7 @@ * and drives the real SMI state machine. */ +#include #include #include #include @@ -51,9 +52,25 @@ #include #include #include -#include #include #include +#ifdef CONFIG_HIGH_RES_TIMERS +#include +# if defined(schedule_next_int) +/* Old high-res timer code, do translations. */ +# define get_arch_cycles(a) quick_update_jiffies_sub(a) +# define arch_cycles_per_jiffy cycles_per_jiffies +# endif +static inline void add_usec_to_timer(struct timer_list *t, long v) +{ + t->arch_cycle_expires += nsec_to_arch_cycle(v * 1000); + while (t->arch_cycle_expires >= arch_cycles_per_jiffy) + { + t->expires++; + t->arch_cycle_expires -= arch_cycles_per_jiffy; + } +} +#endif #include #include #include @@ -92,15 +109,21 @@ enum si_intf_state { enum si_type { SI_KCS, SI_SMIC, SI_BT }; -static char *si_to_str[] = { "KCS", "SMIC", "BT" }; - -#define DEVICE_NAME "ipmi_si" -static struct device_driver ipmi_driver = -{ - .name = DEVICE_NAME, - .bus = &platform_bus_type -}; +struct ipmi_device_id { + unsigned char device_id; + unsigned char device_revision; + unsigned char firmware_revision_1; + unsigned char firmware_revision_2; + unsigned char ipmi_version; + unsigned char additional_device_support; + unsigned char manufacturer_id[3]; + unsigned char product_id[2]; + unsigned char aux_firmware_revision[4]; +} __attribute__((packed)); + +#define ipmi_version_major(v) ((v)->ipmi_version & 0xf) +#define ipmi_version_minor(v) ((v)->ipmi_version >> 4) struct smi_info { @@ -124,9 +147,6 @@ struct smi_info int (*irq_setup)(struct smi_info *info); void (*irq_cleanup)(struct smi_info *info); unsigned int io_size; - char *addr_source; /* ACPI, PCI, SMBIOS, hardcode, default. */ - void (*addr_source_cleanup)(struct smi_info *info); - void *addr_source_data; /* Per-OEM handler, called from handle_flags(). Returns 1 when handle_flags() needs to be re-run @@ -183,17 +203,8 @@ struct smi_info interrupts. */ int interrupt_disabled; - /* From the get device id response... */ struct ipmi_device_id device_id; - /* Driver model stuff. */ - struct device *dev; - struct platform_device *pdev; - - /* True if we allocated the device, false if it came from - * someplace else (like PCI). */ - int dev_registered; - /* Slave address, could be reported from DMI. */ unsigned char slave_addr; @@ -213,18 +224,16 @@ struct smi_info unsigned long incoming_messages; struct task_struct *thread; - - struct list_head link; }; -static int try_smi_init(struct smi_info *smi); - -static ATOMIC_NOTIFIER_HEAD(xaction_notifier_list); +static struct notifier_block *xaction_notifier_list; static int register_xaction_notifier(struct notifier_block * nb) { - return atomic_notifier_chain_register(&xaction_notifier_list, nb); + return notifier_chain_register(&xaction_notifier_list, nb); } +static void si_restart_short_timer(struct smi_info *smi_info); + static void deliver_recv_msg(struct smi_info *smi_info, struct ipmi_smi_msg *msg) { @@ -262,13 +271,13 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) spin_lock(&(smi_info->msg_lock)); /* Pick the high priority queue first. */ - if (!list_empty(&(smi_info->hp_xmit_msgs))) { + if (! list_empty(&(smi_info->hp_xmit_msgs))) { entry = smi_info->hp_xmit_msgs.next; - } else if (!list_empty(&(smi_info->xmit_msgs))) { + } else if (! list_empty(&(smi_info->xmit_msgs))) { entry = smi_info->xmit_msgs.next; } - if (!entry) { + if (! entry) { smi_info->curr_msg = NULL; rv = SI_SM_IDLE; } else { @@ -282,8 +291,7 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) do_gettimeofday(&t); printk("**Start2: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif - err = atomic_notifier_call_chain(&xaction_notifier_list, - 0, smi_info); + err = notifier_call_chain(&xaction_notifier_list, 0, smi_info); if (err & NOTIFY_STOP_MASK) { rv = SI_SM_CALL_WITHOUT_DELAY; goto out; @@ -336,7 +344,7 @@ static void start_clear_flags(struct smi_info *smi_info) memory, we will re-enable the interrupt. */ static inline void disable_si_irq(struct smi_info *smi_info) { - if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { + if ((smi_info->irq) && (! smi_info->interrupt_disabled)) { disable_irq_nosync(smi_info->irq); smi_info->interrupt_disabled = 1; } @@ -367,7 +375,7 @@ static void handle_flags(struct smi_info *smi_info) } else if (smi_info->msg_flags & RECEIVE_MSG_AVAIL) { /* Messages available. */ smi_info->curr_msg = ipmi_alloc_smi_msg(); - if (!smi_info->curr_msg) { + if (! smi_info->curr_msg) { disable_si_irq(smi_info); smi_info->si_state = SI_NORMAL; return; @@ -386,7 +394,7 @@ static void handle_flags(struct smi_info *smi_info) } else if (smi_info->msg_flags & EVENT_MSG_BUFFER_FULL) { /* Events available. */ smi_info->curr_msg = ipmi_alloc_smi_msg(); - if (!smi_info->curr_msg) { + if (! smi_info->curr_msg) { disable_si_irq(smi_info); smi_info->si_state = SI_NORMAL; return; @@ -402,10 +410,10 @@ static void handle_flags(struct smi_info *smi_info) smi_info->curr_msg->data, smi_info->curr_msg->data_size); smi_info->si_state = SI_GETTING_EVENTS; - } else if (smi_info->msg_flags & OEM_DATA_AVAIL && - smi_info->oem_data_avail_handler) { - if (smi_info->oem_data_avail_handler(smi_info)) - goto retry; + } else if (smi_info->msg_flags & OEM_DATA_AVAIL) { + if (smi_info->oem_data_avail_handler) + if (smi_info->oem_data_avail_handler(smi_info)) + goto retry; } else { smi_info->si_state = SI_NORMAL; } @@ -422,7 +430,7 @@ static void handle_transaction_done(struct smi_info *smi_info) #endif switch (smi_info->si_state) { case SI_NORMAL: - if (!smi_info->curr_msg) + if (! smi_info->curr_msg) break; smi_info->curr_msg->rsp_size @@ -748,6 +756,7 @@ static void sender(void *send_info, && (smi_info->curr_msg == NULL)) { start_next_msg(smi_info); + si_restart_short_timer(smi_info); } spin_unlock_irqrestore(&(smi_info->si_lock), flags); } @@ -782,13 +791,13 @@ static int ipmi_thread(void *data) set_user_nice(current, 19); while (!kthread_should_stop()) { spin_lock_irqsave(&(smi_info->si_lock), flags); - smi_result = smi_event_handler(smi_info, 0); + smi_result=smi_event_handler(smi_info, 0); spin_unlock_irqrestore(&(smi_info->si_lock), flags); if (smi_result == SI_SM_CALL_WITHOUT_DELAY) { /* do nothing */ } else if (smi_result == SI_SM_CALL_WITH_DELAY) - schedule(); + udelay(1); else schedule_timeout_interruptible(1); } @@ -812,6 +821,37 @@ static void request_events(void *send_info) static int initialized = 0; +/* Must be called with interrupts off and with the si_lock held. */ +static void si_restart_short_timer(struct smi_info *smi_info) +{ +#if defined(CONFIG_HIGH_RES_TIMERS) + unsigned long flags; + unsigned long jiffies_now; + unsigned long seq; + + if (del_timer(&(smi_info->si_timer))) { + /* If we don't delete the timer, then it will go off + immediately, anyway. So we only process if we + actually delete the timer. */ + + do { + seq = read_seqbegin_irqsave(&xtime_lock, flags); + jiffies_now = jiffies; + smi_info->si_timer.expires = jiffies_now; + smi_info->si_timer.arch_cycle_expires + = get_arch_cycles(jiffies_now); + } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); + + add_usec_to_timer(&smi_info->si_timer, SI_SHORT_TIMEOUT_USEC); + + add_timer(&(smi_info->si_timer)); + spin_lock_irqsave(&smi_info->count_lock, flags); + smi_info->timeout_restarts++; + spin_unlock_irqrestore(&smi_info->count_lock, flags); + } +#endif +} + static void smi_timeout(unsigned long data) { struct smi_info *smi_info = (struct smi_info *) data; @@ -840,7 +880,7 @@ static void smi_timeout(unsigned long data) smi_info->last_timeout_jiffies = jiffies_now; - if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { + if ((smi_info->irq) && (! smi_info->interrupt_disabled)) { /* Running with interrupts, only do long timeouts. */ smi_info->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES; spin_lock_irqsave(&smi_info->count_lock, flags); @@ -852,15 +892,31 @@ static void smi_timeout(unsigned long data) /* If the state machine asks for a short delay, then shorten the timer timeout. */ if (smi_result == SI_SM_CALL_WITH_DELAY) { +#if defined(CONFIG_HIGH_RES_TIMERS) + unsigned long seq; +#endif spin_lock_irqsave(&smi_info->count_lock, flags); smi_info->short_timeouts++; spin_unlock_irqrestore(&smi_info->count_lock, flags); +#if defined(CONFIG_HIGH_RES_TIMERS) + do { + seq = read_seqbegin_irqsave(&xtime_lock, flags); + smi_info->si_timer.expires = jiffies; + smi_info->si_timer.arch_cycle_expires + = get_arch_cycles(smi_info->si_timer.expires); + } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); + add_usec_to_timer(&smi_info->si_timer, SI_SHORT_TIMEOUT_USEC); +#else smi_info->si_timer.expires = jiffies + 1; +#endif } else { spin_lock_irqsave(&smi_info->count_lock, flags); smi_info->long_timeouts++; spin_unlock_irqrestore(&smi_info->count_lock, flags); smi_info->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES; +#if defined(CONFIG_HIGH_RES_TIMERS) + smi_info->si_timer.arch_cycle_expires = 0; +#endif } do_add_timer: @@ -904,37 +960,10 @@ static irqreturn_t si_bt_irq_handler(int irq, void *data, struct pt_regs *regs) return si_irq_handler(irq, data, regs); } -static int smi_start_processing(void *send_info, - ipmi_smi_t intf) -{ - struct smi_info *new_smi = send_info; - - new_smi->intf = intf; - - /* Set up the timer that drives the interface. */ - setup_timer(&new_smi->si_timer, smi_timeout, (long)new_smi); - new_smi->last_timeout_jiffies = jiffies; - mod_timer(&new_smi->si_timer, jiffies + SI_TIMEOUT_JIFFIES); - - if (new_smi->si_type != SI_BT) { - new_smi->thread = kthread_run(ipmi_thread, new_smi, - "kipmi%d", new_smi->intf_num); - if (IS_ERR(new_smi->thread)) { - printk(KERN_NOTICE "ipmi_si_intf: Could not start" - " kernel thread due to error %ld, only using" - " timers to drive the interface\n", - PTR_ERR(new_smi->thread)); - new_smi->thread = NULL; - } - } - - return 0; -} static struct ipmi_smi_handlers handlers = { .owner = THIS_MODULE, - .start_processing = smi_start_processing, .sender = sender, .request_events = request_events, .set_run_to_completion = set_run_to_completion, @@ -945,10 +974,15 @@ static struct ipmi_smi_handlers handlers = a default IO port, and 1 ACPI/SPMI address. That sets SI_MAX_DRIVERS */ #define SI_MAX_PARMS 4 -static LIST_HEAD(smi_infos); -static DEFINE_MUTEX(smi_infos_lock); -static int smi_num; /* Used to sequence the SMIs */ +#define SI_MAX_DRIVERS ((SI_MAX_PARMS * 2) + 2) +static struct smi_info *smi_infos[SI_MAX_DRIVERS] = +{ NULL, NULL, NULL, NULL }; + +#define DEVICE_NAME "ipmi_si" +#define DEFAULT_KCS_IO_PORT 0xca2 +#define DEFAULT_SMIC_IO_PORT 0xca9 +#define DEFAULT_BT_IO_PORT 0xe4 #define DEFAULT_REGSPACING 1 static int si_trydefaults = 1; @@ -1019,39 +1053,54 @@ MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for" " by interface number."); -#define IPMI_IO_ADDR_SPACE 0 #define IPMI_MEM_ADDR_SPACE 1 -static char *addr_space_to_str[] = { "I/O", "memory" }; +#define IPMI_IO_ADDR_SPACE 2 -static void std_irq_cleanup(struct smi_info *info) +#if defined(CONFIG_ACPI) || defined(CONFIG_DMI) || defined(CONFIG_PCI) +static int is_new_interface(int intf, u8 addr_space, unsigned long base_addr) { - 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); + int i; + + for (i = 0; i < SI_MAX_PARMS; ++i) { + /* Don't check our address. */ + if (i == intf) + continue; + if (si_type[i] != NULL) { + if ((addr_space == IPMI_MEM_ADDR_SPACE && + base_addr == addrs[i]) || + (addr_space == IPMI_IO_ADDR_SPACE && + base_addr == ports[i])) + return 0; + } + else + break; + } + + return 1; } +#endif static int std_irq_setup(struct smi_info *info) { int rv; - if (!info->irq) + if (! info->irq) return 0; if (info->si_type == SI_BT) { rv = request_irq(info->irq, si_bt_irq_handler, - IRQF_DISABLED, + SA_INTERRUPT, DEVICE_NAME, info); - if (!rv) + 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, - IRQF_DISABLED, + SA_INTERRUPT, DEVICE_NAME, info); if (rv) { @@ -1061,77 +1110,88 @@ static int std_irq_setup(struct smi_info *info) DEVICE_NAME, info->irq); info->irq = 0; } else { - info->irq_cleanup = std_irq_cleanup; printk(" Using irq %d\n", info->irq); } return rv; } +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); +} + static unsigned char port_inb(struct si_sm_io *io, unsigned int offset) { - unsigned int addr = io->addr_data; + unsigned int *addr = io->info; - return inb(addr + (offset * io->regspacing)); + return inb((*addr)+(offset*io->regspacing)); } static void port_outb(struct si_sm_io *io, unsigned int offset, unsigned char b) { - unsigned int addr = io->addr_data; + unsigned int *addr = io->info; - outb(b, addr + (offset * io->regspacing)); + outb(b, (*addr)+(offset * io->regspacing)); } static unsigned char port_inw(struct si_sm_io *io, unsigned int offset) { - unsigned int addr = io->addr_data; + unsigned int *addr = io->info; - return (inw(addr + (offset * io->regspacing)) >> io->regshift) & 0xff; + return (inw((*addr)+(offset * io->regspacing)) >> io->regshift) & 0xff; } static void port_outw(struct si_sm_io *io, unsigned int offset, unsigned char b) { - unsigned int addr = io->addr_data; + unsigned int *addr = io->info; - outw(b << io->regshift, addr + (offset * io->regspacing)); + outw(b << io->regshift, (*addr)+(offset * io->regspacing)); } static unsigned char port_inl(struct si_sm_io *io, unsigned int offset) { - unsigned int addr = io->addr_data; + unsigned int *addr = io->info; - return (inl(addr + (offset * io->regspacing)) >> io->regshift) & 0xff; + return (inl((*addr)+(offset * io->regspacing)) >> io->regshift) & 0xff; } static void port_outl(struct si_sm_io *io, unsigned int offset, unsigned char b) { - unsigned int addr = io->addr_data; + unsigned int *addr = io->info; - outl(b << io->regshift, addr+(offset * io->regspacing)); + outl(b << io->regshift, (*addr)+(offset * io->regspacing)); } static void port_cleanup(struct smi_info *info) { - unsigned int addr = info->io.addr_data; - int idx; + unsigned int *addr = info->io.info; + int mapsize; - if (addr) { - for (idx = 0; idx < info->io_size; idx++) { - release_region(addr + idx * info->io.regspacing, - info->io.regsize); - } + if (addr && (*addr)) { + mapsize = ((info->io_size * info->io.regspacing) + - (info->io.regspacing - info->io.regsize)); + + release_region (*addr, mapsize); } + kfree(info); } static int port_setup(struct smi_info *info) { - unsigned int addr = info->io.addr_data; - int idx; + unsigned int *addr = info->io.info; + int mapsize; - if (!addr) + if (! addr || (! *addr)) return -ENODEV; info->io_cleanup = port_cleanup; @@ -1157,22 +1217,56 @@ static int port_setup(struct smi_info *info) return -EINVAL; } - /* Some BIOSes reserve disjoint I/O regions in their ACPI - * tables. This causes problems when trying to register the - * entire I/O region. Therefore we must register each I/O - * port separately. - */ - for (idx = 0; idx < info->io_size; idx++) { - if (request_region(addr + idx * info->io.regspacing, - info->io.regsize, DEVICE_NAME) == NULL) { - /* Undo allocations */ - while (idx--) { - release_region(addr + idx * info->io.regspacing, - info->io.regsize); - } - return -EIO; - } + /* Calculate the total amount of memory to claim. This is an + * unusual looking calculation, but it avoids claiming any + * more memory than it has to. It will claim everything + * between the first address to the end of the last full + * register. */ + mapsize = ((info->io_size * info->io.regspacing) + - (info->io.regspacing - info->io.regsize)); + + if (request_region(*addr, mapsize, DEVICE_NAME) == NULL) + return -EIO; + return 0; +} + +static int try_init_port(int intf_num, struct smi_info **new_info) +{ + struct smi_info *info; + + if (! ports[intf_num]) + return -ENODEV; + + if (! is_new_interface(intf_num, IPMI_IO_ADDR_SPACE, + ports[intf_num])) + return -ENODEV; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (! info) { + printk(KERN_ERR "ipmi_si: Could not allocate SI data (1)\n"); + return -ENOMEM; } + memset(info, 0, sizeof(*info)); + + info->io_setup = port_setup; + info->io.info = &(ports[intf_num]); + info->io.addr = NULL; + info->io.regspacing = regspacings[intf_num]; + if (! info->io.regspacing) + info->io.regspacing = DEFAULT_REGSPACING; + info->io.regsize = regsizes[intf_num]; + if (! info->io.regsize) + info->io.regsize = DEFAULT_REGSPACING; + info->io.regshift = regshifts[intf_num]; + info->irq = 0; + info->irq_setup = NULL; + *new_info = info; + + if (si_type[intf_num] == NULL) + si_type[intf_num] = "kcs"; + + printk("ipmi_si: Trying \"%s\" at I/O port 0x%x\n", + si_type[intf_num], ports[intf_num]); return 0; } @@ -1190,7 +1284,7 @@ static void intf_mem_outb(struct si_sm_io *io, unsigned int offset, static unsigned char intf_mem_inw(struct si_sm_io *io, unsigned int offset) { return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift) - && 0xff; + & 0xff; } static void intf_mem_outw(struct si_sm_io *io, unsigned int offset, @@ -1202,7 +1296,7 @@ static void intf_mem_outw(struct si_sm_io *io, unsigned int offset, static unsigned char intf_mem_inl(struct si_sm_io *io, unsigned int offset) { return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift) - && 0xff; + & 0xff; } static void intf_mem_outl(struct si_sm_io *io, unsigned int offset, @@ -1215,7 +1309,7 @@ static void intf_mem_outl(struct si_sm_io *io, unsigned int offset, static unsigned char mem_inq(struct si_sm_io *io, unsigned int offset) { return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift) - && 0xff; + & 0xff; } static void mem_outq(struct si_sm_io *io, unsigned int offset, @@ -1227,7 +1321,7 @@ static void mem_outq(struct si_sm_io *io, unsigned int offset, static void mem_cleanup(struct smi_info *info) { - unsigned long addr = info->io.addr_data; + unsigned long *addr = info->io.info; int mapsize; if (info->io.addr) { @@ -1236,16 +1330,17 @@ static void mem_cleanup(struct smi_info *info) mapsize = ((info->io_size * info->io.regspacing) - (info->io.regspacing - info->io.regsize)); - release_mem_region(addr, mapsize); + release_mem_region(*addr, mapsize); } + kfree(info); } static int mem_setup(struct smi_info *info) { - unsigned long addr = info->io.addr_data; + unsigned long *addr = info->io.info; int mapsize; - if (!addr) + if (! addr || (! *addr)) return -ENODEV; info->io_cleanup = mem_cleanup; @@ -1285,84 +1380,58 @@ static int mem_setup(struct smi_info *info) mapsize = ((info->io_size * info->io.regspacing) - (info->io.regspacing - info->io.regsize)); - if (request_mem_region(addr, mapsize, DEVICE_NAME) == NULL) + if (request_mem_region(*addr, mapsize, DEVICE_NAME) == NULL) return -EIO; - info->io.addr = ioremap(addr, mapsize); + info->io.addr = ioremap(*addr, mapsize); if (info->io.addr == NULL) { - release_mem_region(addr, mapsize); + release_mem_region(*addr, mapsize); return -EIO; } return 0; } - -static __devinit void hardcode_find_bmc(void) +static int try_init_mem(int intf_num, struct smi_info **new_info) { - int i; struct smi_info *info; - for (i = 0; i < SI_MAX_PARMS; i++) { - if (!ports[i] && !addrs[i]) - continue; - - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return; + if (! addrs[intf_num]) + return -ENODEV; - info->addr_source = "hardcoded"; + if (! is_new_interface(intf_num, IPMI_MEM_ADDR_SPACE, + addrs[intf_num])) + return -ENODEV; - if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) { - info->si_type = SI_KCS; - } else if (strcmp(si_type[i], "smic") == 0) { - info->si_type = SI_SMIC; - } else if (strcmp(si_type[i], "bt") == 0) { - info->si_type = SI_BT; - } else { - printk(KERN_WARNING - "ipmi_si: Interface type specified " - "for interface %d, was invalid: %s\n", - i, si_type[i]); - kfree(info); - continue; - } + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (! info) { + printk(KERN_ERR "ipmi_si: Could not allocate SI data (2)\n"); + return -ENOMEM; + } + memset(info, 0, sizeof(*info)); - if (ports[i]) { - /* An I/O port */ - info->io_setup = port_setup; - info->io.addr_data = ports[i]; - info->io.addr_type = IPMI_IO_ADDR_SPACE; - } else if (addrs[i]) { - /* A memory port */ - info->io_setup = mem_setup; - info->io.addr_data = addrs[i]; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; - } else { - printk(KERN_WARNING - "ipmi_si: Interface type specified " - "for interface %d, " - "but port and address were not set or " - "set to zero.\n", i); - kfree(info); - continue; - } + info->io_setup = mem_setup; + info->io.info = &addrs[intf_num]; + info->io.addr = NULL; + info->io.regspacing = regspacings[intf_num]; + if (! info->io.regspacing) + info->io.regspacing = DEFAULT_REGSPACING; + info->io.regsize = regsizes[intf_num]; + if (! info->io.regsize) + info->io.regsize = DEFAULT_REGSPACING; + info->io.regshift = regshifts[intf_num]; + info->irq = 0; + info->irq_setup = NULL; + *new_info = info; - info->io.addr = NULL; - info->io.regspacing = regspacings[i]; - if (!info->io.regspacing) - info->io.regspacing = DEFAULT_REGSPACING; - info->io.regsize = regsizes[i]; - if (!info->io.regsize) - info->io.regsize = DEFAULT_REGSPACING; - info->io.regshift = regshifts[i]; - info->irq = irqs[i]; - if (info->irq) - info->irq_setup = std_irq_setup; + if (si_type[intf_num] == NULL) + si_type[intf_num] = "kcs"; - try_smi_init(info); - } + printk("ipmi_si: Trying \"%s\" at memory address 0x%lx\n", + si_type[intf_num], addrs[intf_num]); + return 0; } + #ifdef CONFIG_ACPI #include @@ -1401,19 +1470,11 @@ static u32 ipmi_acpi_gpe(void *context) return ACPI_INTERRUPT_HANDLED; } -static void acpi_gpe_irq_cleanup(struct smi_info *info) -{ - if (!info->irq) - return; - - acpi_remove_gpe_handler(NULL, info->irq, &ipmi_acpi_gpe); -} - static int acpi_gpe_irq_setup(struct smi_info *info) { acpi_status status; - if (!info->irq) + if (! info->irq) return 0; /* FIXME - is level triggered right? */ @@ -1430,12 +1491,19 @@ static int acpi_gpe_irq_setup(struct smi_info *info) info->irq = 0; return -EINVAL; } else { - info->irq_cleanup = acpi_gpe_irq_cleanup; printk(" Using ACPI GPE %d\n", info->irq); return 0; } } +static void acpi_gpe_irq_cleanup(struct smi_info *info) +{ + if (! info->irq) + return; + + acpi_remove_gpe_handler(NULL, info->irq, &ipmi_acpi_gpe); +} + /* * Defined at * http://h21007.www2.hp.com/dspp/files/unprotected/devresource/Docs/TechPapers/IA64/hpspmi.pdf @@ -1478,12 +1546,28 @@ struct SPMITable { s8 spmi_id[1]; /* A '\0' terminated array starts here. */ }; -static __devinit int try_init_acpi(struct SPMITable *spmi) +static int try_init_acpi(int intf_num, struct smi_info **new_info) { struct smi_info *info; + acpi_status status; + struct SPMITable *spmi; char *io_type; u8 addr_space; + if (acpi_disabled) + return -ENODEV; + + if (acpi_failure) + return -ENODEV; + + status = acpi_get_firmware_table("SPMI", intf_num+1, + ACPI_LOGICAL_ADDRESSING, + (struct acpi_table_header **) &spmi); + if (status != AE_OK) { + acpi_failure = 1; + return -ENODEV; + } + if (spmi->IPMIlegacy != 1) { printk(KERN_INFO "IPMI: Bad SPMI legacy %d\n", spmi->IPMIlegacy); return -ENODEV; @@ -1493,42 +1577,47 @@ static __devinit int try_init_acpi(struct SPMITable *spmi) addr_space = IPMI_MEM_ADDR_SPACE; else addr_space = IPMI_IO_ADDR_SPACE; - - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - printk(KERN_ERR "ipmi_si: Could not allocate SI data (3)\n"); - return -ENOMEM; - } - - info->addr_source = "ACPI"; + if (! is_new_interface(-1, addr_space, spmi->addr.address)) + return -ENODEV; /* Figure out the interface type. */ switch (spmi->InterfaceType) { case 1: /* KCS */ - info->si_type = SI_KCS; + si_type[intf_num] = "kcs"; break; + case 2: /* SMIC */ - info->si_type = SI_SMIC; + si_type[intf_num] = "smic"; break; + case 3: /* BT */ - info->si_type = SI_BT; + si_type[intf_num] = "bt"; break; + default: printk(KERN_INFO "ipmi_si: Unknown ACPI/SPMI SI type %d\n", spmi->InterfaceType); - kfree(info); return -EIO; } + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (! info) { + printk(KERN_ERR "ipmi_si: Could not allocate SI data (3)\n"); + return -ENOMEM; + } + memset(info, 0, sizeof(*info)); + if (spmi->InterruptType & 1) { /* We've got a GPE interrupt. */ info->irq = spmi->GPE; info->irq_setup = acpi_gpe_irq_setup; + info->irq_cleanup = acpi_gpe_irq_cleanup; } else if (spmi->InterruptType & 2) { /* We've got an APIC/SAPIC interrupt. */ info->irq = spmi->GlobalSystemInterrupt; info->irq_setup = std_irq_setup; + info->irq_cleanup = std_irq_cleanup; } else { /* Use the default interrupt setting. */ info->irq = 0; @@ -1537,60 +1626,43 @@ static __devinit int try_init_acpi(struct SPMITable *spmi) 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 { + regspacings[intf_num] = DEFAULT_REGSPACING; info->io.regspacing = DEFAULT_REGSPACING; } - info->io.regsize = info->io.regspacing; - info->io.regshift = spmi->addr.register_bit_offset; + regsizes[intf_num] = regspacings[intf_num]; + info->io.regsize = regsizes[intf_num]; + regshifts[intf_num] = spmi->addr.register_bit_offset; + info->io.regshift = regshifts[intf_num]; if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { io_type = "memory"; info->io_setup = mem_setup; - info->io.addr_type = IPMI_IO_ADDR_SPACE; + addrs[intf_num] = spmi->addr.address; + info->io.info = &(addrs[intf_num]); } else if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_IO) { io_type = "I/O"; info->io_setup = port_setup; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; + ports[intf_num] = spmi->addr.address; + info->io.info = &(ports[intf_num]); } else { kfree(info); printk("ipmi_si: Unknown ACPI I/O Address type\n"); return -EIO; } - info->io.addr_data = spmi->addr.address; - try_smi_init(info); + *new_info = info; + printk("ipmi_si: ACPI/SPMI specifies \"%s\" %s SI @ 0x%lx\n", + si_type[intf_num], io_type, (unsigned long) spmi->addr.address); return 0; } - -static __devinit void acpi_find_bmc(void) -{ - acpi_status status; - struct SPMITable *spmi; - int i; - - if (acpi_disabled) - return; - - if (acpi_failure) - return; - - for (i = 0; ; i++) { - status = acpi_get_firmware_table("SPMI", i+1, - ACPI_LOGICAL_ADDRESSING, - (struct acpi_table_header **) - &spmi); - if (status != AE_OK) - return; - - try_init_acpi(spmi); - } -} #endif #ifdef CONFIG_DMI -struct dmi_ipmi_data +typedef struct dmi_ipmi_data { u8 type; u8 addr_space; @@ -1598,46 +1670,49 @@ struct dmi_ipmi_data u8 irq; u8 offset; u8 slave_addr; -}; +} dmi_ipmi_data_t; + +static dmi_ipmi_data_t dmi_data[SI_MAX_DRIVERS]; +static int dmi_data_entries; -static int __devinit decode_dmi(struct dmi_header *dm, - struct dmi_ipmi_data *dmi) +static int __init decode_dmi(struct dmi_header *dm, int intf_num) { u8 *data = (u8 *)dm; unsigned long base_addr; u8 reg_spacing; u8 len = dm->length; + dmi_ipmi_data_t *ipmi_data = dmi_data+intf_num; - dmi->type = data[4]; + ipmi_data->type = data[4]; memcpy(&base_addr, data+8, sizeof(unsigned long)); if (len >= 0x11) { if (base_addr & 1) { /* I/O */ base_addr &= 0xFFFE; - dmi->addr_space = IPMI_IO_ADDR_SPACE; + ipmi_data->addr_space = IPMI_IO_ADDR_SPACE; } else { /* Memory */ - dmi->addr_space = IPMI_MEM_ADDR_SPACE; + ipmi_data->addr_space = IPMI_MEM_ADDR_SPACE; } /* If bit 4 of byte 0x10 is set, then the lsb for the address is odd. */ - dmi->base_addr = base_addr | ((data[0x10] & 0x10) >> 4); + ipmi_data->base_addr = base_addr | ((data[0x10] & 0x10) >> 4); - dmi->irq = data[0x11]; + ipmi_data->irq = data[0x11]; /* The top two bits of byte 0x10 hold the register spacing. */ reg_spacing = (data[0x10] & 0xC0) >> 6; switch(reg_spacing){ case 0x00: /* Byte boundaries */ - dmi->offset = 1; + ipmi_data->offset = 1; break; case 0x01: /* 32-bit boundaries */ - dmi->offset = 4; + ipmi_data->offset = 4; break; case 0x02: /* 16-byte boundaries */ - dmi->offset = 16; + ipmi_data->offset = 16; break; default: /* Some other interface, just ignore it. */ @@ -1651,227 +1726,217 @@ static int __devinit decode_dmi(struct dmi_header *dm, * 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. */ - dmi->base_addr = base_addr & 0xfffe; - dmi->addr_space = IPMI_IO_ADDR_SPACE; - dmi->offset = 1; + ipmi_data->base_addr = base_addr & 0xfffe; + ipmi_data->addr_space = IPMI_IO_ADDR_SPACE; + ipmi_data->offset = 1; } - dmi->slave_addr = data[6]; + ipmi_data->slave_addr = data[6]; - return 0; + if (is_new_interface(-1, ipmi_data->addr_space,ipmi_data->base_addr)) { + dmi_data_entries++; + return 0; + } + + memset(ipmi_data, 0, sizeof(dmi_ipmi_data_t)); + + return -1; } -static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data) +static void __init dmi_find_bmc(void) { - struct smi_info *info; + struct dmi_device *dev = NULL; + int intf_num = 0; - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - printk(KERN_ERR - "ipmi_si: Could not allocate SI data\n"); - return; + while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) { + if (intf_num >= SI_MAX_DRIVERS) + break; + + decode_dmi((struct dmi_header *) dev->device_data, intf_num++); } +} + +static int try_init_smbios(int intf_num, struct smi_info **new_info) +{ + struct smi_info *info; + dmi_ipmi_data_t *ipmi_data = dmi_data+intf_num; + char *io_type; - info->addr_source = "SMBIOS"; + if (intf_num >= dmi_data_entries) + return -ENODEV; switch (ipmi_data->type) { - case 0x01: /* KCS */ - info->si_type = SI_KCS; - break; - case 0x02: /* SMIC */ - info->si_type = SI_SMIC; - break; - case 0x03: /* BT */ - info->si_type = SI_BT; - break; - default: - return; + case 0x01: /* KCS */ + si_type[intf_num] = "kcs"; + break; + case 0x02: /* SMIC */ + si_type[intf_num] = "smic"; + break; + case 0x03: /* BT */ + si_type[intf_num] = "bt"; + break; + default: + return -EIO; } - switch (ipmi_data->addr_space) { - case IPMI_MEM_ADDR_SPACE: - info->io_setup = mem_setup; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; - break; + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (! info) { + printk(KERN_ERR "ipmi_si: Could not allocate SI data (4)\n"); + return -ENOMEM; + } + memset(info, 0, sizeof(*info)); - case IPMI_IO_ADDR_SPACE: + if (ipmi_data->addr_space == 1) { + io_type = "memory"; + info->io_setup = mem_setup; + addrs[intf_num] = ipmi_data->base_addr; + info->io.info = &(addrs[intf_num]); + } else if (ipmi_data->addr_space == 2) { + io_type = "I/O"; info->io_setup = port_setup; - info->io.addr_type = IPMI_IO_ADDR_SPACE; - break; - - default: + ports[intf_num] = ipmi_data->base_addr; + info->io.info = &(ports[intf_num]); + } else { kfree(info); - printk(KERN_WARNING - "ipmi_si: Unknown SMBIOS I/O Address type: %d.\n", - ipmi_data->addr_space); - return; + printk("ipmi_si: Unknown SMBIOS I/O Address type.\n"); + return -EIO; } - info->io.addr_data = ipmi_data->base_addr; - info->io.regspacing = ipmi_data->offset; - if (!info->io.regspacing) + regspacings[intf_num] = ipmi_data->offset; + info->io.regspacing = regspacings[intf_num]; + if (! info->io.regspacing) info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = DEFAULT_REGSPACING; - info->io.regshift = 0; + info->io.regshift = regshifts[intf_num]; info->slave_addr = ipmi_data->slave_addr; - info->irq = ipmi_data->irq; - if (info->irq) - info->irq_setup = std_irq_setup; - - try_smi_init(info); -} + irqs[intf_num] = ipmi_data->irq; -static void __devinit dmi_find_bmc(void) -{ - struct dmi_device *dev = NULL; - struct dmi_ipmi_data data; - int rv; + *new_info = info; - while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) { - rv = decode_dmi((struct dmi_header *) dev->device_data, &data); - if (!rv) - try_init_dmi(&data); - } + printk("ipmi_si: Found SMBIOS-specified state machine at %s" + " address 0x%lx, slave address 0x%x\n", + io_type, (unsigned long)ipmi_data->base_addr, + ipmi_data->slave_addr); + return 0; } #endif /* CONFIG_DMI */ #ifdef CONFIG_PCI -#define PCI_ERMC_CLASSCODE 0x0C0700 -#define PCI_ERMC_CLASSCODE_MASK 0xffffff00 -#define PCI_ERMC_CLASSCODE_TYPE_MASK 0xff -#define PCI_ERMC_CLASSCODE_TYPE_SMIC 0x00 -#define PCI_ERMC_CLASSCODE_TYPE_KCS 0x01 -#define PCI_ERMC_CLASSCODE_TYPE_BT 0x02 - +#define PCI_ERMC_CLASSCODE 0x0C0700 #define PCI_HP_VENDOR_ID 0x103C #define PCI_MMC_DEVICE_ID 0x121A #define PCI_MMC_ADDR_CW 0x10 -static void ipmi_pci_cleanup(struct smi_info *info) -{ - struct pci_dev *pdev = info->addr_source_data; - - pci_disable_device(pdev); -} +/* Avoid more than one attempt to probe pci smic. */ +static int pci_smic_checked = 0; -static int __devinit ipmi_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int find_pci_smic(int intf_num, struct smi_info **new_info) { - int rv; - int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK; - struct smi_info *info; - int first_reg_offset = 0; - - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return ENOMEM; - - info->addr_source = "PCI"; + struct smi_info *info; + int error; + struct pci_dev *pci_dev = NULL; + u16 base_addr; + int fe_rmc = 0; - switch (class_type) { - case PCI_ERMC_CLASSCODE_TYPE_SMIC: - info->si_type = SI_SMIC; - break; + if (pci_smic_checked) + return -ENODEV; - case PCI_ERMC_CLASSCODE_TYPE_KCS: - info->si_type = SI_KCS; - break; + pci_smic_checked = 1; - case PCI_ERMC_CLASSCODE_TYPE_BT: - info->si_type = SI_BT; - break; + pci_dev = pci_get_device(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID, NULL); + if (! pci_dev) { + pci_dev = pci_get_class(PCI_ERMC_CLASSCODE, NULL); + if (pci_dev && (pci_dev->subsystem_vendor == PCI_HP_VENDOR_ID)) + fe_rmc = 1; + else + return -ENODEV; + } - default: - kfree(info); - printk(KERN_INFO "ipmi_si: %s: Unknown IPMI type: %d\n", - pci_name(pdev), class_type); - return ENOMEM; + error = pci_read_config_word(pci_dev, PCI_MMC_ADDR_CW, &base_addr); + if (error) + { + pci_dev_put(pci_dev); + printk(KERN_ERR + "ipmi_si: pci_read_config_word() failed (%d).\n", + error); + return -ENODEV; } - rv = pci_enable_device(pdev); - if (rv) { - printk(KERN_ERR "ipmi_si: %s: couldn't enable PCI device\n", - pci_name(pdev)); - kfree(info); - return rv; + /* Bit 0: 1 specifies programmed I/O, 0 specifies memory mapped I/O */ + if (! (base_addr & 0x0001)) + { + pci_dev_put(pci_dev); + printk(KERN_ERR + "ipmi_si: memory mapped I/O not supported for PCI" + " smic.\n"); + return -ENODEV; } - info->addr_source_cleanup = ipmi_pci_cleanup; - info->addr_source_data = pdev; + base_addr &= 0xFFFE; + if (! fe_rmc) + /* Data register starts at base address + 1 in eRMC */ + ++base_addr; - if (pdev->subsystem_vendor == PCI_HP_VENDOR_ID) - first_reg_offset = 1; + if (! is_new_interface(-1, IPMI_IO_ADDR_SPACE, base_addr)) { + pci_dev_put(pci_dev); + return -ENODEV; + } - if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) { - info->io_setup = port_setup; - info->io.addr_type = IPMI_IO_ADDR_SPACE; - } else { - info->io_setup = mem_setup; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (! info) { + pci_dev_put(pci_dev); + printk(KERN_ERR "ipmi_si: Could not allocate SI data (5)\n"); + return -ENOMEM; } - info->io.addr_data = pci_resource_start(pdev, 0); + memset(info, 0, sizeof(*info)); - info->io.regspacing = DEFAULT_REGSPACING; + info->io_setup = port_setup; + ports[intf_num] = base_addr; + info->io.info = &(ports[intf_num]); + info->io.regspacing = regspacings[intf_num]; + if (! info->io.regspacing) + info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = DEFAULT_REGSPACING; - info->io.regshift = 0; + info->io.regshift = regshifts[intf_num]; - info->irq = pdev->irq; - if (info->irq) - info->irq_setup = std_irq_setup; - - info->dev = &pdev->dev; + *new_info = info; - return try_smi_init(info); -} + irqs[intf_num] = pci_dev->irq; + si_type[intf_num] = "smic"; -static void __devexit ipmi_pci_remove(struct pci_dev *pdev) -{ -} + printk("ipmi_si: Found PCI SMIC at I/O address 0x%lx\n", + (long unsigned int) base_addr); -#ifdef CONFIG_PM -static int ipmi_pci_suspend(struct pci_dev *pdev, pm_message_t state) -{ + pci_dev_put(pci_dev); return 0; } +#endif /* CONFIG_PCI */ -static int ipmi_pci_resume(struct pci_dev *pdev) +static int try_init_plug_and_play(int intf_num, struct smi_info **new_info) { - return 0; -} +#ifdef CONFIG_PCI + if (find_pci_smic(intf_num, new_info) == 0) + return 0; #endif + /* Include other methods here. */ -static struct pci_device_id ipmi_pci_devices[] = { - { PCI_DEVICE(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID) }, - { PCI_DEVICE_CLASS(PCI_ERMC_CLASSCODE, PCI_ERMC_CLASSCODE_MASK) } -}; -MODULE_DEVICE_TABLE(pci, ipmi_pci_devices); - -static struct pci_driver ipmi_pci_driver = { - .name = DEVICE_NAME, - .id_table = ipmi_pci_devices, - .probe = ipmi_pci_probe, - .remove = __devexit_p(ipmi_pci_remove), -#ifdef CONFIG_PM - .suspend = ipmi_pci_suspend, - .resume = ipmi_pci_resume, -#endif -}; -#endif /* CONFIG_PCI */ + return -ENODEV; +} static int try_get_dev_id(struct smi_info *smi_info) { - unsigned char msg[2]; - unsigned char *resp; - unsigned long resp_len; - enum si_sm_result smi_result; - int rv = 0; + unsigned char msg[2]; + unsigned char *resp; + unsigned long resp_len; + enum si_sm_result smi_result; + int rv = 0; resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); - if (!resp) + if (! resp) return -ENOMEM; /* Do a Get Device ID command, since it comes back with some @@ -1907,7 +1972,7 @@ static int try_get_dev_id(struct smi_info *smi_info) /* Otherwise, we got some data. */ resp_len = smi_info->handlers->get_result(smi_info->si_sm, resp, IPMI_MAX_MSG_LENGTH); - if (resp_len < 14) { + if (resp_len < 6) { /* That's odd, it should be longer. */ rv = -EINVAL; goto out; @@ -1920,7 +1985,8 @@ static int try_get_dev_id(struct smi_info *smi_info) } /* Record info from the get device id, in case we need it. */ - ipmi_demangle_device_id(resp+3, resp_len-3, &smi_info->device_id); + memcpy(&smi_info->device_id, &resp[3], + min_t(unsigned long, resp_len-3, sizeof(smi_info->device_id))); out: kfree(resp); @@ -1952,7 +2018,7 @@ static int stat_file_read_proc(char *page, char **start, off_t off, struct smi_info *smi = data; out += sprintf(out, "interrupts_enabled: %d\n", - smi->irq && !smi->interrupt_disabled); + smi->irq && ! smi->interrupt_disabled); out += sprintf(out, "short_timeouts: %ld\n", smi->short_timeouts); out += sprintf(out, "long_timeouts: %ld\n", @@ -2023,14 +2089,15 @@ static int oem_data_avail_to_receive_msg_avail(struct smi_info *smi_info) #define DELL_POWEREDGE_8G_BMC_DEVICE_ID 0x20 #define DELL_POWEREDGE_8G_BMC_DEVICE_REV 0x80 #define DELL_POWEREDGE_8G_BMC_IPMI_VERSION 0x51 -#define DELL_IANA_MFR_ID 0x0002a2 +#define DELL_IANA_MFR_ID {0xA2, 0x02, 0x00} static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info) { struct ipmi_device_id *id = &smi_info->device_id; - if (id->manufacturer_id == DELL_IANA_MFR_ID) { + const char mfr[3]=DELL_IANA_MFR_ID; + if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr))) { if (id->device_id == DELL_POWEREDGE_8G_BMC_DEVICE_ID && id->device_revision == DELL_POWEREDGE_8G_BMC_DEVICE_REV && - id->ipmi_version == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) { + id->ipmi_version == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) { smi_info->oem_data_avail_handler = oem_data_avail_to_receive_msg_avail; } @@ -2102,7 +2169,8 @@ static void setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info) { struct ipmi_device_id *id = &smi_info->device_id; - if (id->manufacturer_id == DELL_IANA_MFR_ID && + const char mfr[3]=DELL_IANA_MFR_ID; + if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr)) && smi_info->si_type == SI_BT) register_xaction_notifier(&dell_poweredge_bt_xaction_notifier); } @@ -2127,119 +2195,67 @@ static void setup_xaction_handlers(struct smi_info *smi_info) static inline void wait_for_timer_and_thread(struct smi_info *smi_info) { - if (smi_info->intf) { - /* The timer and thread are only running if the - interface has been started up and registered. */ - if (smi_info->thread != NULL) - kthread_stop(smi_info->thread); - del_timer_sync(&smi_info->si_timer); - } + if (smi_info->thread != NULL && smi_info->thread != ERR_PTR(-ENOMEM)) + kthread_stop(smi_info->thread); + del_timer_sync(&smi_info->si_timer); } -static __devinitdata struct ipmi_default_vals +/* Returns 0 if initialized, or negative on an error. */ +static int init_one_smi(int intf_num, struct smi_info **smi) { - int type; - int port; -} ipmi_defaults[] = -{ - { .type = SI_KCS, .port = 0xca2 }, - { .type = SI_SMIC, .port = 0xca9 }, - { .type = SI_BT, .port = 0xe4 }, - { .port = 0 } -}; - -static __devinit void default_find_bmc(void) -{ - struct smi_info *info; - int i; + int rv; + struct smi_info *new_smi; - for (i = 0; ; i++) { - if (!ipmi_defaults[i].port) - break; - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return; - - info->addr_source = NULL; - - info->si_type = ipmi_defaults[i].type; - info->io_setup = port_setup; - info->io.addr_data = ipmi_defaults[i].port; - info->io.addr_type = IPMI_IO_ADDR_SPACE; - - info->io.addr = NULL; - info->io.regspacing = DEFAULT_REGSPACING; - info->io.regsize = DEFAULT_REGSPACING; - info->io.regshift = 0; - - if (try_smi_init(info) == 0) { - /* Found one... */ - printk(KERN_INFO "ipmi_si: Found default %s state" - " machine at %s address 0x%lx\n", - si_to_str[info->si_type], - addr_space_to_str[info->io.addr_type], - info->io.addr_data); - return; - } - } -} - -static int is_new_interface(struct smi_info *info) -{ - struct smi_info *e; - - list_for_each_entry(e, &smi_infos, link) { - if (e->io.addr_type != info->io.addr_type) - continue; - if (e->io.addr_data == info->io.addr_data) - return 0; - } - - return 1; -} - -static int try_smi_init(struct smi_info *new_smi) -{ - int rv; + rv = try_init_mem(intf_num, &new_smi); + if (rv) + rv = try_init_port(intf_num, &new_smi); +#ifdef CONFIG_ACPI + if (rv && si_trydefaults) + rv = try_init_acpi(intf_num, &new_smi); +#endif +#ifdef CONFIG_DMI + if (rv && si_trydefaults) + rv = try_init_smbios(intf_num, &new_smi); +#endif + if (rv && si_trydefaults) + rv = try_init_plug_and_play(intf_num, &new_smi); - if (new_smi->addr_source) { - printk(KERN_INFO "ipmi_si: Trying %s-specified %s state" - " machine at %s address 0x%lx, slave address 0x%x," - " irq %d\n", - new_smi->addr_source, - si_to_str[new_smi->si_type], - addr_space_to_str[new_smi->io.addr_type], - new_smi->io.addr_data, - new_smi->slave_addr, new_smi->irq); - } - - mutex_lock(&smi_infos_lock); - if (!is_new_interface(new_smi)) { - printk(KERN_WARNING "ipmi_si: duplicate interface\n"); - rv = -EBUSY; - goto out_err; - } + if (rv) + return rv; /* So we know not to free it unless we have allocated one. */ new_smi->intf = NULL; new_smi->si_sm = NULL; new_smi->handlers = NULL; - switch (new_smi->si_type) { - case SI_KCS: - new_smi->handlers = &kcs_smi_handlers; - break; + if (! new_smi->irq_setup) { + new_smi->irq = irqs[intf_num]; + new_smi->irq_setup = std_irq_setup; + new_smi->irq_cleanup = std_irq_cleanup; + } - case SI_SMIC: - new_smi->handlers = &smic_smi_handlers; - break; + /* Default to KCS if no type is specified. */ + if (si_type[intf_num] == NULL) { + if (si_trydefaults) + si_type[intf_num] = "kcs"; + else { + rv = -EINVAL; + goto out_err; + } + } - case SI_BT: + /* Set up the state machine to use. */ + if (strcmp(si_type[intf_num], "kcs") == 0) { + new_smi->handlers = &kcs_smi_handlers; + new_smi->si_type = SI_KCS; + } else if (strcmp(si_type[intf_num], "smic") == 0) { + new_smi->handlers = &smic_smi_handlers; + new_smi->si_type = SI_SMIC; + } else if (strcmp(si_type[intf_num], "bt") == 0) { new_smi->handlers = &bt_smi_handlers; - break; - - default: + new_smi->si_type = SI_BT; + } else { /* No support for anything else yet. */ rv = -EIO; goto out_err; @@ -2247,7 +2263,7 @@ static int try_smi_init(struct smi_info *new_smi) /* Allocate the state machine's data and initialize it. */ new_smi->si_sm = kmalloc(new_smi->handlers->size(), GFP_KERNEL); - if (!new_smi->si_sm) { + if (! new_smi->si_sm) { printk(" Could not allocate state machine memory\n"); rv = -ENOMEM; goto out_err; @@ -2268,29 +2284,21 @@ static int try_smi_init(struct smi_info *new_smi) /* Do low-level detection first. */ if (new_smi->handlers->detect(new_smi->si_sm)) { - if (new_smi->addr_source) - printk(KERN_INFO "ipmi_si: Interface detection" - " failed\n"); rv = -ENODEV; goto out_err; } /* Attempt a get device id command. If it fails, we probably - don't have a BMC here. */ + don't have a SMI here. */ rv = try_get_dev_id(new_smi); - if (rv) { - if (new_smi->addr_source) - printk(KERN_INFO "ipmi_si: There appears to be no BMC" - " at this location\n"); + if (rv) goto out_err; - } setup_oem_data_handler(new_smi); setup_xaction_handlers(new_smi); /* Try to claim any interrupts. */ - if (new_smi->irq_setup) - new_smi->irq_setup(new_smi); + new_smi->irq_setup(new_smi); INIT_LIST_HEAD(&(new_smi->xmit_msgs)); INIT_LIST_HEAD(&(new_smi->hp_xmit_msgs)); @@ -2300,8 +2308,7 @@ static int try_smi_init(struct smi_info *new_smi) new_smi->interrupt_disabled = 0; atomic_set(&new_smi->stop_operation, 0); - new_smi->intf_num = smi_num; - smi_num++; + new_smi->intf_num = intf_num; /* Start clearing the flags before we enable interrupts or the timer to avoid racing with the timer. */ @@ -2310,37 +2317,27 @@ static int try_smi_init(struct smi_info *new_smi) if (new_smi->irq) new_smi->si_state = SI_CLEARING_FLAGS_THEN_SET_IRQ; - if (!new_smi->dev) { - /* If we don't already have a device from something - * else (like PCI), then register a new one. */ - new_smi->pdev = platform_device_alloc("ipmi_si", - new_smi->intf_num); - if (rv) { - printk(KERN_ERR - "ipmi_si_intf:" - " Unable to allocate platform device\n"); - goto out_err; - } - new_smi->dev = &new_smi->pdev->dev; - new_smi->dev->driver = &ipmi_driver; + /* The ipmi_register_smi() code does some operations to + determine the channel information, so we must be ready to + handle operations before it is called. This means we have + to stop the timer if we get an error after this point. */ + init_timer(&(new_smi->si_timer)); + new_smi->si_timer.data = (long) new_smi; + new_smi->si_timer.function = smi_timeout; + new_smi->last_timeout_jiffies = jiffies; + new_smi->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES; - rv = platform_device_register(new_smi->pdev); - if (rv) { - printk(KERN_ERR - "ipmi_si_intf:" - " Unable to register system interface device:" - " %d\n", - rv); - goto out_err; - } - new_smi->dev_registered = 1; - } + add_timer(&(new_smi->si_timer)); + if (new_smi->si_type != SI_BT) + new_smi->thread = kthread_run(ipmi_thread, new_smi, + "kipmi%d", new_smi->intf_num); rv = ipmi_register_smi(&handlers, new_smi, - &new_smi->device_id, - new_smi->dev, - new_smi->slave_addr); + ipmi_version_major(&new_smi->device_id), + ipmi_version_minor(&new_smi->device_id), + new_smi->slave_addr, + &(new_smi->intf)); if (rv) { printk(KERN_ERR "ipmi_si: Unable to register device: error %d\n", @@ -2368,11 +2365,9 @@ static int try_smi_init(struct smi_info *new_smi) goto out_err_stop_timer; } - list_add_tail(&new_smi->link, &smi_infos); - - mutex_unlock(&smi_infos_lock); + *smi = new_smi; - printk(" IPMI %s interface initialized\n",si_to_str[new_smi->si_type]); + printk(" IPMI %s interface initialized\n", si_type[intf_num]); return 0; @@ -2384,8 +2379,7 @@ static int try_smi_init(struct smi_info *new_smi) if (new_smi->intf) ipmi_unregister_smi(new_smi->intf); - if (new_smi->irq_cleanup) - new_smi->irq_cleanup(new_smi); + new_smi->irq_cleanup(new_smi); /* Wait until we know that we are out of any interrupt handlers might have been running before we freed the @@ -2397,41 +2391,23 @@ static int try_smi_init(struct smi_info *new_smi) new_smi->handlers->cleanup(new_smi->si_sm); kfree(new_smi->si_sm); } - if (new_smi->addr_source_cleanup) - new_smi->addr_source_cleanup(new_smi); if (new_smi->io_cleanup) new_smi->io_cleanup(new_smi); - if (new_smi->dev_registered) - platform_device_unregister(new_smi->pdev); - - kfree(new_smi); - - mutex_unlock(&smi_infos_lock); - return rv; } -static __devinit int init_ipmi_si(void) +static __init int init_ipmi_si(void) { + int rv = 0; + int pos = 0; int i; char *str; - int rv; if (initialized) return 0; initialized = 1; - /* Register the device drivers. */ - rv = driver_register(&ipmi_driver); - if (rv) { - printk(KERN_ERR - "init_ipmi_si: Unable to register driver: %d\n", - rv); - return rv; - } - - /* Parse out the si_type string into its components. */ str = si_type_str; if (*str != '\0') { @@ -2449,67 +2425,63 @@ static __devinit int init_ipmi_si(void) printk(KERN_INFO "IPMI System Interface driver.\n"); - hardcode_find_bmc(); - #ifdef CONFIG_DMI dmi_find_bmc(); #endif -#ifdef CONFIG_ACPI - if (si_trydefaults) - acpi_find_bmc(); -#endif - -#ifdef CONFIG_PCI - pci_module_init(&ipmi_pci_driver); -#endif - - if (si_trydefaults) { - mutex_lock(&smi_infos_lock); - if (list_empty(&smi_infos)) { - /* No BMC was found, try defaults. */ - mutex_unlock(&smi_infos_lock); - default_find_bmc(); - } else { - mutex_unlock(&smi_infos_lock); + rv = init_one_smi(0, &(smi_infos[pos])); + if (rv && ! ports[0] && si_trydefaults) { + /* If we are trying defaults and the initial port is + not set, then set it. */ + si_type[0] = "kcs"; + ports[0] = DEFAULT_KCS_IO_PORT; + rv = init_one_smi(0, &(smi_infos[pos])); + if (rv) { + /* No KCS - try SMIC */ + si_type[0] = "smic"; + ports[0] = DEFAULT_SMIC_IO_PORT; + rv = init_one_smi(0, &(smi_infos[pos])); + } + if (rv) { + /* No SMIC - try BT */ + si_type[0] = "bt"; + ports[0] = DEFAULT_BT_IO_PORT; + rv = init_one_smi(0, &(smi_infos[pos])); } } + if (rv == 0) + pos++; - mutex_lock(&smi_infos_lock); - if (list_empty(&smi_infos)) { - mutex_unlock(&smi_infos_lock); -#ifdef CONFIG_PCI - pci_unregister_driver(&ipmi_pci_driver); -#endif - driver_unregister(&ipmi_driver); + for (i = 1; i < SI_MAX_PARMS; i++) { + rv = init_one_smi(i, &(smi_infos[pos])); + if (rv == 0) + pos++; + } + + if (smi_infos[0] == NULL) { printk("ipmi_si: Unable to find any System Interface(s)\n"); return -ENODEV; - } else { - mutex_unlock(&smi_infos_lock); - return 0; } + + return 0; } module_init(init_ipmi_si); -static void __devexit cleanup_one_si(struct smi_info *to_clean) +static void __exit cleanup_one_si(struct smi_info *to_clean) { int rv; unsigned long flags; - if (!to_clean) + if (! to_clean) return; - 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)); atomic_inc(&to_clean->stop_operation); - - if (to_clean->irq_cleanup) - to_clean->irq_cleanup(to_clean); + to_clean->irq_cleanup(to_clean); spin_unlock(&(to_clean->msg_lock)); spin_unlock_irqrestore(&(to_clean->si_lock), flags); @@ -2539,34 +2511,20 @@ static void __devexit cleanup_one_si(struct smi_info *to_clean) kfree(to_clean->si_sm); - if (to_clean->addr_source_cleanup) - to_clean->addr_source_cleanup(to_clean); if (to_clean->io_cleanup) to_clean->io_cleanup(to_clean); - - if (to_clean->dev_registered) - platform_device_unregister(to_clean->pdev); - - kfree(to_clean); } static __exit void cleanup_ipmi_si(void) { - struct smi_info *e, *tmp_e; + int i; - if (!initialized) + if (! initialized) return; -#ifdef CONFIG_PCI - pci_unregister_driver(&ipmi_pci_driver); -#endif - - mutex_lock(&smi_infos_lock); - list_for_each_entry_safe(e, tmp_e, &smi_infos, link) - cleanup_one_si(e); - mutex_unlock(&smi_infos_lock); - - driver_unregister(&ipmi_driver); + for (i = 0; i < SI_MAX_DRIVERS; i++) { + cleanup_one_si(smi_infos[i]); + } } module_exit(cleanup_ipmi_si);