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 3f2a4900fe18d9323ac17743a1065f5cec33217f..6503d995b727cfacb7326fffe5b08fa1dbb66300 100644 (file)
@@ -454,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);
        }
 }
 
@@ -706,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;
@@ -886,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
@@ -1006,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);
        }
@@ -1048,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
@@ -1068,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;
        }
 
        /*
@@ -1079,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)
@@ -3117,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++;
@@ -3264,6 +3278,7 @@ static __devinit int init_ipmi_si(void)
        char *str;
        int  rv;
        struct smi_info *e;
+       enum ipmi_addr_src type = SI_INVALID;
 
        if (initialized)
                return 0;
@@ -3298,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);
@@ -3317,17 +3329,59 @@ 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) {
-               if (!e->si_sm)
-                       try_smi_init(e);
+               /* 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)) {