ipmi: attempt to register multiple SIs of the same type
[firefly-linux-kernel-4.4.55.git] / drivers / char / ipmi / ipmi_si_intf.c
index 4462b113ba3f1e134a1eb77b650af5e2732eb9af..6503d995b727cfacb7326fffe5b08fa1dbb66300 100644 (file)
@@ -107,6 +107,14 @@ enum si_type {
 };
 static char *si_to_str[] = { "kcs", "smic", "bt" };
 
+enum ipmi_addr_src {
+       SI_INVALID = 0, SI_HOTMOD, SI_HARDCODED, SI_SPMI, SI_ACPI, SI_SMBIOS,
+       SI_PCI, SI_DEVICETREE, SI_DEFAULT
+};
+static char *ipmi_addr_src_to_str[] = { NULL, "hotmod", "hardcoded", "SPMI",
+                                       "ACPI", "SMBIOS", "PCI",
+                                       "device-tree", "default" };
+
 #define DEVICE_NAME "ipmi_si"
 
 static struct platform_driver ipmi_driver = {
@@ -188,7 +196,7 @@ 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. */
+       enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
        void (*addr_source_cleanup)(struct smi_info *info);
        void *addr_source_data;
 
@@ -300,6 +308,7 @@ static int num_max_busy_us;
 
 static int unload_when_empty = 1;
 
+static int add_smi(struct smi_info *smi);
 static int try_smi_init(struct smi_info *smi);
 static void cleanup_one_si(struct smi_info *to_clean);
 
@@ -445,6 +454,9 @@ static inline void disable_si_irq(struct smi_info *smi_info)
        if ((smi_info->irq) && (!smi_info->interrupt_disabled)) {
                start_disable_irq(smi_info);
                smi_info->interrupt_disabled = 1;
+               if (!atomic_read(&smi_info->stop_operation))
+                       mod_timer(&smi_info->si_timer,
+                                 jiffies + SI_TIMEOUT_JIFFIES);
        }
 }
 
@@ -697,6 +709,8 @@ static void handle_transaction_done(struct smi_info *smi_info)
                        printk(KERN_WARNING
                               "ipmi_si: Could not enable interrupts"
                               ", failed set, using polled mode.\n");
+               } else {
+                       smi_info->interrupt_disabled = 0;
                }
                smi_info->si_state = SI_NORMAL;
                break;
@@ -877,6 +891,11 @@ static void sender(void                *send_info,
        printk("**Enqueue: %d.%9.9d\n", t.tv_sec, t.tv_usec);
 #endif
 
+       mod_timer(&smi_info->si_timer, jiffies + SI_TIMEOUT_JIFFIES);
+
+       if (smi_info->thread)
+               wake_up_process(smi_info->thread);
+
        if (smi_info->run_to_completion) {
                /*
                 * If we are running to completion, then throw it in
@@ -997,6 +1016,8 @@ static int ipmi_thread(void *data)
                        ; /* do nothing */
                else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait)
                        schedule();
+               else if (smi_result == SI_SM_IDLE)
+                       schedule_timeout_interruptible(100);
                else
                        schedule_timeout_interruptible(0);
        }
@@ -1039,6 +1060,7 @@ static void smi_timeout(unsigned long data)
        unsigned long     flags;
        unsigned long     jiffies_now;
        long              time_diff;
+       long              timeout;
 #ifdef DEBUG_TIMING
        struct timeval    t;
 #endif
@@ -1059,9 +1081,9 @@ static void smi_timeout(unsigned long data)
 
        if ((smi_info->irq) && (!smi_info->interrupt_disabled)) {
                /* Running with interrupts, only do long timeouts. */
-               smi_info->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES;
+               timeout = jiffies + SI_TIMEOUT_JIFFIES;
                smi_inc_stat(smi_info, long_timeouts);
-               goto do_add_timer;
+               goto do_mod_timer;
        }
 
        /*
@@ -1070,14 +1092,15 @@ static void smi_timeout(unsigned long data)
         */
        if (smi_result == SI_SM_CALL_WITH_DELAY) {
                smi_inc_stat(smi_info, short_timeouts);
-               smi_info->si_timer.expires = jiffies + 1;
+               timeout = jiffies + 1;
        } else {
                smi_inc_stat(smi_info, long_timeouts);
-               smi_info->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES;
+               timeout = jiffies + SI_TIMEOUT_JIFFIES;
        }
 
- do_add_timer:
-       add_timer(&(smi_info->si_timer));
+ do_mod_timer:
+       if (smi_result != SI_SM_IDLE)
+               mod_timer(&(smi_info->si_timer), timeout);
 }
 
 static irqreturn_t si_irq_handler(int irq, void *data)
@@ -1755,7 +1778,7 @@ static int hotmod_handler(const char *val, struct kernel_param *kp)
                                goto out;
                        }
 
-                       info->addr_source = "hotmod";
+                       info->addr_source = SI_HOTMOD;
                        info->si_type = si_type;
                        info->io.addr_data = addr;
                        info->io.addr_type = addr_space;
@@ -1777,7 +1800,9 @@ static int hotmod_handler(const char *val, struct kernel_param *kp)
                                info->irq_setup = std_irq_setup;
                        info->slave_addr = ipmb;
 
-                       try_smi_init(info);
+                       if (!add_smi(info))
+                               if (try_smi_init(info))
+                                       cleanup_one_si(info);
                } else {
                        /* remove */
                        struct smi_info *e, *tmp_e;
@@ -1813,7 +1838,7 @@ static __devinit void hardcode_find_bmc(void)
                if (!info)
                        return;
 
-               info->addr_source = "hardcoded";
+               info->addr_source = SI_HARDCODED;
 
                if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) {
                        info->si_type = SI_KCS;
@@ -1863,7 +1888,9 @@ static __devinit void hardcode_find_bmc(void)
                        info->irq_setup = std_irq_setup;
                info->slave_addr = slave_addrs[i];
 
-               try_smi_init(info);
+               if (!add_smi(info))
+                       if (try_smi_init(info))
+                               cleanup_one_si(info);
        }
 }
 
@@ -2004,7 +2031,7 @@ static __devinit int try_init_spmi(struct SPMITable *spmi)
                return -ENOMEM;
        }
 
-       info->addr_source = "SPMI";
+       info->addr_source = SI_SPMI;
 
        /* Figure out the interface type. */
        switch (spmi->InterfaceType) {
@@ -2061,7 +2088,7 @@ static __devinit int try_init_spmi(struct SPMITable *spmi)
        }
        info->io.addr_data = spmi->addr.address;
 
-       try_smi_init(info);
+       add_smi(info);
 
        return 0;
 }
@@ -2105,7 +2132,7 @@ static int __devinit ipmi_pnp_probe(struct pnp_dev *dev,
        if (!info)
                return -ENOMEM;
 
-       info->addr_source = "ACPI";
+       info->addr_source = SI_ACPI;
 
        handle = acpi_dev->handle;
 
@@ -2159,7 +2186,7 @@ static int __devinit ipmi_pnp_probe(struct pnp_dev *dev,
        info->dev = &acpi_dev->dev;
        pnp_set_drvdata(dev, info);
 
-       return try_smi_init(info);
+       return add_smi(info);
 
 err_free:
        kfree(info);
@@ -2269,7 +2296,7 @@ static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data)
                return;
        }
 
-       info->addr_source = "SMBIOS";
+       info->addr_source = SI_SMBIOS;
 
        switch (ipmi_data->type) {
        case 0x01: /* KCS */
@@ -2318,7 +2345,7 @@ static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data)
        if (info->irq)
                info->irq_setup = std_irq_setup;
 
-       try_smi_init(info);
+       add_smi(info);
 }
 
 static void __devinit dmi_find_bmc(void)
@@ -2368,7 +2395,7 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
        if (!info)
                return -ENOMEM;
 
-       info->addr_source = "PCI";
+       info->addr_source = SI_PCI;
 
        switch (class_type) {
        case PCI_ERMC_CLASSCODE_TYPE_SMIC:
@@ -2421,7 +2448,7 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
        info->dev = &pdev->dev;
        pci_set_drvdata(pdev, info);
 
-       return try_smi_init(info);
+       return add_smi(info);
 }
 
 static void __devexit ipmi_pci_remove(struct pci_dev *pdev)
@@ -2469,7 +2496,7 @@ static int __devinit ipmi_of_probe(struct of_device *dev,
        struct smi_info *info;
        struct resource resource;
        const int *regsize, *regspacing, *regshift;
-       struct device_node *np = dev->node;
+       struct device_node *np = dev->dev.of_node;
        int ret;
        int proplen;
 
@@ -2508,7 +2535,7 @@ static int __devinit ipmi_of_probe(struct of_device *dev,
        }
 
        info->si_type           = (enum si_type) match->data;
-       info->addr_source       = "device-tree";
+       info->addr_source       = SI_DEVICETREE;
        info->irq_setup         = std_irq_setup;
 
        if (resource.flags & IORESOURCE_IO) {
@@ -2525,7 +2552,7 @@ static int __devinit ipmi_of_probe(struct of_device *dev,
        info->io.regspacing     = regspacing ? *regspacing : DEFAULT_REGSPACING;
        info->io.regshift       = regshift ? *regshift : 0;
 
-       info->irq               = irq_of_parse_and_map(dev->node, 0);
+       info->irq               = irq_of_parse_and_map(dev->dev.of_node, 0);
        info->dev               = &dev->dev;
 
        dev_dbg(&dev->dev, "addr 0x%lx regsize %d spacing %d irq %x\n",
@@ -2534,7 +2561,7 @@ static int __devinit ipmi_of_probe(struct of_device *dev,
 
        dev_set_drvdata(&dev->dev, info);
 
-       return try_smi_init(info);
+       return add_smi(info);
 }
 
 static int __devexit ipmi_of_remove(struct of_device *dev)
@@ -2555,8 +2582,11 @@ static struct of_device_id ipmi_match[] =
 };
 
 static struct of_platform_driver ipmi_of_platform_driver = {
-       .name           = "ipmi",
-       .match_table    = ipmi_match,
+       .driver = {
+               .name = "ipmi",
+               .owner = THIS_MODULE,
+               .of_match_table = ipmi_match,
+       },
        .probe          = ipmi_of_probe,
        .remove         = __devexit_p(ipmi_of_remove),
 };
@@ -2948,7 +2978,7 @@ static __devinit void default_find_bmc(void)
                if (!info)
                        return;
 
-               info->addr_source = NULL;
+               info->addr_source = SI_DEFAULT;
 
                info->si_type = ipmi_defaults[i].type;
                info->io_setup = port_setup;
@@ -2960,14 +2990,16 @@ static __devinit void default_find_bmc(void)
                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;
+               if (add_smi(info) == 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);
+                       } else
+                               cleanup_one_si(info);
                }
        }
 }
@@ -2986,34 +3018,48 @@ static int is_new_interface(struct smi_info *info)
        return 1;
 }
 
-static int try_smi_init(struct smi_info *new_smi)
+static int add_smi(struct smi_info *new_smi)
 {
-       int rv;
-       int i;
-
-       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);
-       }
+       int rv = 0;
 
+       printk(KERN_INFO "ipmi_si: Adding %s-specified %s state machine",
+                       ipmi_addr_src_to_str[new_smi->addr_source],
+                       si_to_str[new_smi->si_type]);
        mutex_lock(&smi_infos_lock);
        if (!is_new_interface(new_smi)) {
-               printk(KERN_WARNING "ipmi_si: duplicate interface\n");
+               printk(KERN_CONT ": duplicate interface\n");
                rv = -EBUSY;
                goto out_err;
        }
 
+       printk(KERN_CONT "\n");
+
        /* 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;
 
+       list_add_tail(&new_smi->link, &smi_infos);
+
+out_err:
+       mutex_unlock(&smi_infos_lock);
+       return rv;
+}
+
+static int try_smi_init(struct smi_info *new_smi)
+{
+       int rv = 0;
+       int i;
+
+       printk(KERN_INFO "ipmi_si: Trying %s-specified %s state"
+              " machine at %s address 0x%lx, slave address 0x%x,"
+              " irq %d\n",
+              ipmi_addr_src_to_str[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);
+
        switch (new_smi->si_type) {
        case SI_KCS:
                new_smi->handlers = &kcs_smi_handlers;
@@ -3085,7 +3131,7 @@ static int try_smi_init(struct smi_info *new_smi)
        for (i = 0; i < SI_NUM_STATS; i++)
                atomic_set(&new_smi->stats[i], 0);
 
-       new_smi->interrupt_disabled = 0;
+       new_smi->interrupt_disabled = 1;
        atomic_set(&new_smi->stop_operation, 0);
        new_smi->intf_num = smi_num;
        smi_num++;
@@ -3174,10 +3220,6 @@ 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);
-
        printk(KERN_INFO "IPMI %s interface initialized\n",
               si_to_str[new_smi->si_type]);
 
@@ -3188,11 +3230,17 @@ static int try_smi_init(struct smi_info *new_smi)
        wait_for_timer_and_thread(new_smi);
 
  out_err:
-       if (new_smi->intf)
+       new_smi->interrupt_disabled = 1;
+
+       if (new_smi->intf) {
                ipmi_unregister_smi(new_smi->intf);
+               new_smi->intf = NULL;
+       }
 
-       if (new_smi->irq_cleanup)
+       if (new_smi->irq_cleanup) {
                new_smi->irq_cleanup(new_smi);
+               new_smi->irq_cleanup = NULL;
+       }
 
        /*
         * Wait until we know that we are out of any interrupt
@@ -3205,18 +3253,21 @@ static int try_smi_init(struct smi_info *new_smi)
                if (new_smi->handlers)
                        new_smi->handlers->cleanup(new_smi->si_sm);
                kfree(new_smi->si_sm);
+               new_smi->si_sm = NULL;
        }
-       if (new_smi->addr_source_cleanup)
+       if (new_smi->addr_source_cleanup) {
                new_smi->addr_source_cleanup(new_smi);
-       if (new_smi->io_cleanup)
+               new_smi->addr_source_cleanup = NULL;
+       }
+       if (new_smi->io_cleanup) {
                new_smi->io_cleanup(new_smi);
+               new_smi->io_cleanup = NULL;
+       }
 
-       if (new_smi->dev_registered)
+       if (new_smi->dev_registered) {
                platform_device_unregister(new_smi->pdev);
-
-       kfree(new_smi);
-
-       mutex_unlock(&smi_infos_lock);
+               new_smi->dev_registered = 0;
+       }
 
        return rv;
 }
@@ -3226,6 +3277,8 @@ static __devinit int init_ipmi_si(void)
        int  i;
        char *str;
        int  rv;
+       struct smi_info *e;
+       enum ipmi_addr_src type = SI_INVALID;
 
        if (initialized)
                return 0;
@@ -3260,16 +3313,13 @@ static __devinit int init_ipmi_si(void)
 
        hardcode_find_bmc();
 
-#ifdef CONFIG_DMI
-       dmi_find_bmc();
-#endif
-
-#ifdef CONFIG_ACPI
-       spmi_find_bmc();
-#endif
-#ifdef CONFIG_ACPI
-       pnp_register_driver(&ipmi_pnp_driver);
-#endif
+       /* If the user gave us a device, they presumably want us to use it */
+       mutex_lock(&smi_infos_lock);
+       if (!list_empty(&smi_infos)) {
+               mutex_unlock(&smi_infos_lock);
+               return 0;
+       }
+       mutex_unlock(&smi_infos_lock);
 
 #ifdef CONFIG_PCI
        rv = pci_register_driver(&ipmi_pci_driver);
@@ -3279,19 +3329,67 @@ static __devinit int init_ipmi_si(void)
                       rv);
 #endif
 
+#ifdef CONFIG_ACPI
+       pnp_register_driver(&ipmi_pnp_driver);
+#endif
+
+#ifdef CONFIG_DMI
+       dmi_find_bmc();
+#endif
+
+#ifdef CONFIG_ACPI
+       spmi_find_bmc();
+#endif
+
 #ifdef CONFIG_PPC_OF
        of_register_platform_driver(&ipmi_of_platform_driver);
 #endif
 
+       /* We prefer devices with interrupts, but in the case of a machine
+          with multiple BMCs we assume that there will be several instances
+          of a given type so if we succeed in registering a type then also
+          try to register everything else of the same type */
+
+       mutex_lock(&smi_infos_lock);
+       list_for_each_entry(e, &smi_infos, link) {
+               /* Try to register a device if it has an IRQ and we either
+                  haven't successfully registered a device yet or this
+                  device has the same type as one we successfully registered */
+               if (e->irq && (!type || e->addr_source == type)) {
+                       if (!try_smi_init(e)) {
+                               type = e->addr_source;
+                       }
+               }
+       }
+
+       /* type will only have been set if we successfully registered an si */
+       if (type) {
+               mutex_unlock(&smi_infos_lock);
+               return 0;
+       }
+
+       /* Fall back to the preferred device */
+
+       list_for_each_entry(e, &smi_infos, link) {
+               if (!e->irq && (!type || e->addr_source == type)) {
+                       if (!try_smi_init(e)) {
+                               type = e->addr_source;
+                       }
+               }
+       }
+       mutex_unlock(&smi_infos_lock);
+
+       if (type)
+               return 0;
+
        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 {
+               } else
                        mutex_unlock(&smi_infos_lock);
-               }
        }
 
        mutex_lock(&smi_infos_lock);
@@ -3317,7 +3415,7 @@ module_init(init_ipmi_si);
 
 static void cleanup_one_si(struct smi_info *to_clean)
 {
-       int           rv;
+       int           rv = 0;
        unsigned long flags;
 
        if (!to_clean)
@@ -3361,14 +3459,17 @@ static void cleanup_one_si(struct smi_info *to_clean)
                schedule_timeout_uninterruptible(1);
        }
 
-       rv = ipmi_unregister_smi(to_clean->intf);
+       if (to_clean->intf)
+               rv = ipmi_unregister_smi(to_clean->intf);
+
        if (rv) {
                printk(KERN_ERR
                       "ipmi_si: Unable to unregister device: errno=%d\n",
                       rv);
        }
 
-       to_clean->handlers->cleanup(to_clean->si_sm);
+       if (to_clean->handlers)
+               to_clean->handlers->cleanup(to_clean->si_sm);
 
        kfree(to_clean->si_sm);