These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / arch / powerpc / platforms / powernv / opal.c
index 2241565..ae29eaf 100644 (file)
@@ -53,13 +53,7 @@ static int mc_recoverable_range_len;
 
 struct device_node *opal_node;
 static DEFINE_SPINLOCK(opal_write_lock);
-static unsigned int *opal_irqs;
-static unsigned int opal_irq_count;
-static ATOMIC_NOTIFIER_HEAD(opal_notifier_head);
 static struct atomic_notifier_head opal_msg_notifier_head[OPAL_MSG_TYPE_MAX];
-static DEFINE_SPINLOCK(opal_notifier_lock);
-static uint64_t last_notified_mask = 0x0ul;
-static atomic_t opal_notifier_hold = ATOMIC_INIT(0);
 static uint32_t opal_heartbeat;
 
 static void opal_reinit_cores(void)
@@ -225,82 +219,6 @@ static int __init opal_register_exception_handlers(void)
 }
 machine_early_initcall(powernv, opal_register_exception_handlers);
 
-int opal_notifier_register(struct notifier_block *nb)
-{
-       if (!nb) {
-               pr_warning("%s: Invalid argument (%p)\n",
-                          __func__, nb);
-               return -EINVAL;
-       }
-
-       atomic_notifier_chain_register(&opal_notifier_head, nb);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(opal_notifier_register);
-
-int opal_notifier_unregister(struct notifier_block *nb)
-{
-       if (!nb) {
-               pr_warning("%s: Invalid argument (%p)\n",
-                          __func__, nb);
-               return -EINVAL;
-       }
-
-       atomic_notifier_chain_unregister(&opal_notifier_head, nb);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(opal_notifier_unregister);
-
-static void opal_do_notifier(uint64_t events)
-{
-       unsigned long flags;
-       uint64_t changed_mask;
-
-       if (atomic_read(&opal_notifier_hold))
-               return;
-
-       spin_lock_irqsave(&opal_notifier_lock, flags);
-       changed_mask = last_notified_mask ^ events;
-       last_notified_mask = events;
-       spin_unlock_irqrestore(&opal_notifier_lock, flags);
-
-       /*
-        * We feed with the event bits and changed bits for
-        * enough information to the callback.
-        */
-       atomic_notifier_call_chain(&opal_notifier_head,
-                                  events, (void *)changed_mask);
-}
-
-void opal_notifier_update_evt(uint64_t evt_mask,
-                             uint64_t evt_val)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&opal_notifier_lock, flags);
-       last_notified_mask &= ~evt_mask;
-       last_notified_mask |= evt_val;
-       spin_unlock_irqrestore(&opal_notifier_lock, flags);
-}
-
-void opal_notifier_enable(void)
-{
-       int64_t rc;
-       __be64 evt = 0;
-
-       atomic_set(&opal_notifier_hold, 0);
-
-       /* Process pending events */
-       rc = opal_poll_events(&evt);
-       if (rc == OPAL_SUCCESS && evt)
-               opal_do_notifier(be64_to_cpu(evt));
-}
-
-void opal_notifier_disable(void)
-{
-       atomic_set(&opal_notifier_hold, 1);
-}
-
 /*
  * Opal message notifier based on message type. Allow subscribers to get
  * notified for specific messgae type.
@@ -317,6 +235,7 @@ int opal_message_notifier_register(enum opal_msg_type msg_type,
        return atomic_notifier_chain_register(
                                &opal_msg_notifier_head[msg_type], nb);
 }
+EXPORT_SYMBOL_GPL(opal_message_notifier_register);
 
 int opal_message_notifier_unregister(enum opal_msg_type msg_type,
                                     struct notifier_block *nb)
@@ -324,6 +243,7 @@ int opal_message_notifier_unregister(enum opal_msg_type msg_type,
        return atomic_notifier_chain_unregister(
                        &opal_msg_notifier_head[msg_type], nb);
 }
+EXPORT_SYMBOL_GPL(opal_message_notifier_unregister);
 
 static void opal_message_do_notify(uint32_t msg_type, void *msg)
 {
@@ -358,42 +278,42 @@ static void opal_handle_message(void)
 
        /* Sanity check */
        if (type >= OPAL_MSG_TYPE_MAX) {
-               pr_warning("%s: Unknown message type: %u\n", __func__, type);
+               pr_warn_once("%s: Unknown message type: %u\n", __func__, type);
                return;
        }
        opal_message_do_notify(type, (void *)&msg);
 }
 
-static int opal_message_notify(struct notifier_block *nb,
-                         unsigned long events, void *change)
+static irqreturn_t opal_message_notify(int irq, void *data)
 {
-       if (events & OPAL_EVENT_MSG_PENDING)
-               opal_handle_message();
-       return 0;
+       opal_handle_message();
+       return IRQ_HANDLED;
 }
 
-static struct notifier_block opal_message_nb = {
-       .notifier_call  = opal_message_notify,
-       .next           = NULL,
-       .priority       = 0,
-};
-
 static int __init opal_message_init(void)
 {
-       int ret, i;
+       int ret, i, irq;
 
        for (i = 0; i < OPAL_MSG_TYPE_MAX; i++)
                ATOMIC_INIT_NOTIFIER_HEAD(&opal_msg_notifier_head[i]);
 
-       ret = opal_notifier_register(&opal_message_nb);
+       irq = opal_event_request(ilog2(OPAL_EVENT_MSG_PENDING));
+       if (!irq) {
+               pr_err("%s: Can't register OPAL event irq (%d)\n",
+                      __func__, irq);
+               return irq;
+       }
+
+       ret = request_irq(irq, opal_message_notify,
+                       IRQ_TYPE_LEVEL_HIGH, "opal-msg", NULL);
        if (ret) {
-               pr_err("%s: Can't register OPAL event notifier (%d)\n",
+               pr_err("%s: Can't request OPAL event irq (%d)\n",
                       __func__, ret);
                return ret;
        }
+
        return 0;
 }
-machine_early_initcall(powernv, opal_message_init);
 
 int opal_get_chars(uint32_t vtermno, char *buf, int count)
 {
@@ -521,6 +441,7 @@ static int opal_recover_mce(struct pt_regs *regs,
 int opal_machine_check(struct pt_regs *regs)
 {
        struct machine_check_event evt;
+       int ret;
 
        if (!get_mce_event(&evt, MCE_EVENT_RELEASE))
                return 0;
@@ -535,6 +456,43 @@ int opal_machine_check(struct pt_regs *regs)
 
        if (opal_recover_mce(regs, &evt))
                return 1;
+
+       /*
+        * Unrecovered machine check, we are heading to panic path.
+        *
+        * We may have hit this MCE in very early stage of kernel
+        * initialization even before opal-prd has started running. If
+        * this is the case then this MCE error may go un-noticed or
+        * un-analyzed if we go down panic path. We need to inform
+        * BMC/OCC about this error so that they can collect relevant
+        * data for error analysis before rebooting.
+        * Use opal_cec_reboot2(OPAL_REBOOT_PLATFORM_ERROR) to do so.
+        * This function may not return on BMC based system.
+        */
+       ret = opal_cec_reboot2(OPAL_REBOOT_PLATFORM_ERROR,
+                       "Unrecoverable Machine Check exception");
+       if (ret == OPAL_UNSUPPORTED) {
+               pr_emerg("Reboot type %d not supported\n",
+                                       OPAL_REBOOT_PLATFORM_ERROR);
+       }
+
+       /*
+        * We reached here. There can be three possibilities:
+        * 1. We are running on a firmware level that do not support
+        *    opal_cec_reboot2()
+        * 2. We are running on a firmware level that do not support
+        *    OPAL_REBOOT_PLATFORM_ERROR reboot type.
+        * 3. We are running on FSP based system that does not need opal
+        *    to trigger checkstop explicitly for error analysis. The FSP
+        *    PRD component would have already got notified about this
+        *    error through other channels.
+        *
+        * If hardware marked this as an unrecoverable MCE, we are
+        * going to panic anyway. Even if it didn't, it's not safe to
+        * continue at this point, so we should explicitly panic.
+        */
+
+       panic("PowerNV Unrecovered Machine Check");
        return 0;
 }
 
@@ -573,7 +531,7 @@ int opal_handle_hmi_exception(struct pt_regs *regs)
        local_paca->hmi_event_available = 0;
        rc = opal_poll_events(&evt);
        if (rc == OPAL_SUCCESS && evt)
-               opal_do_notifier(be64_to_cpu(evt));
+               opal_handle_events(be64_to_cpu(evt));
 
        return 1;
 }
@@ -610,17 +568,6 @@ out:
        return !!recover_addr;
 }
 
-static irqreturn_t opal_interrupt(int irq, void *data)
-{
-       __be64 events;
-
-       opal_handle_interrupt(virq_to_hw(irq), &events);
-
-       opal_do_notifier(be64_to_cpu(events));
-
-       return IRQ_HANDLED;
-}
-
 static int opal_sysfs_init(void)
 {
        opal_kobj = kobject_create_and_add("opal", firmware_kobj);
@@ -693,21 +640,13 @@ static void __init opal_dump_region_init(void)
                        "rc = %d\n", rc);
 }
 
-static void opal_flash_init(struct device_node *opal_node)
+static void opal_pdev_init(struct device_node *opal_node,
+               const char *compatible)
 {
        struct device_node *np;
 
        for_each_child_of_node(opal_node, np)
-               if (of_device_is_compatible(np, "ibm,opal-flash"))
-                       of_platform_device_create(np, NULL, NULL);
-}
-
-static void opal_ipmi_init(struct device_node *opal_node)
-{
-       struct device_node *np;
-
-       for_each_child_of_node(opal_node, np)
-               if (of_device_is_compatible(np, "ibm,opal-ipmi"))
+               if (of_device_is_compatible(np, compatible))
                        of_platform_device_create(np, NULL, NULL);
 }
 
@@ -719,52 +658,15 @@ static void opal_i2c_create_devs(void)
                of_platform_device_create(np, NULL, NULL);
 }
 
-static void __init opal_irq_init(struct device_node *dn)
-{
-       const __be32 *irqs;
-       int i, irqlen;
-
-       /* Get interrupt property */
-       irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
-       opal_irq_count = irqs ? (irqlen / 4) : 0;
-       pr_debug("Found %d interrupts reserved for OPAL\n", opal_irq_count);
-       if (!opal_irq_count)
-               return;
-
-       /* Install interrupt handlers */
-       opal_irqs = kzalloc(opal_irq_count * sizeof(unsigned int), GFP_KERNEL);
-       for (i = 0; irqs && i < opal_irq_count; i++, irqs++) {
-               unsigned int irq, virq;
-               int rc;
-
-               /* Get hardware and virtual IRQ */
-               irq = be32_to_cpup(irqs);
-               virq = irq_create_mapping(NULL, irq);
-               if (virq == NO_IRQ) {
-                       pr_warn("Failed to map irq 0x%x\n", irq);
-                       continue;
-               }
-
-               /* Install interrupt handler */
-               rc = request_irq(virq, opal_interrupt, 0, "opal", NULL);
-               if (rc) {
-                       irq_dispose_mapping(virq);
-                       pr_warn("Error %d requesting irq %d (0x%x)\n",
-                                rc, virq, irq);
-                       continue;
-               }
-
-               /* Cache IRQ */
-               opal_irqs[i] = virq;
-       }
-}
-
 static int kopald(void *unused)
 {
+       __be64 events;
+
        set_freezable();
        do {
                try_to_freeze();
-               opal_poll_events(NULL);
+               opal_poll_events(&events);
+               opal_handle_events(be64_to_cpu(events));
                msleep_interruptible(opal_heartbeat);
        } while (!kthread_should_stop());
 
@@ -784,7 +686,7 @@ static void opal_init_heartbeat(void)
 
 static int __init opal_init(void)
 {
-       struct device_node *np, *consoles;
+       struct device_node *np, *consoles, *leds;
        int rc;
 
        opal_node = of_find_node_by_path("/ibm,opal");
@@ -807,14 +709,30 @@ static int __init opal_init(void)
                of_node_put(consoles);
        }
 
+       /* Initialise OPAL messaging system */
+       opal_message_init();
+
+       /* Initialise OPAL asynchronous completion interface */
+       opal_async_comp_init();
+
+       /* Initialise OPAL sensor interface */
+       opal_sensor_init();
+
+       /* Initialise OPAL hypervisor maintainence interrupt handling */
+       opal_hmi_handler_init();
+
        /* Create i2c platform devices */
        opal_i2c_create_devs();
 
        /* Setup a heatbeat thread if requested by OPAL */
        opal_init_heartbeat();
 
-       /* Find all OPAL interrupts and request them */
-       opal_irq_init(opal_node);
+       /* Create leds platform devices */
+       leds = of_find_node_by_path("/ibm,opal/leds");
+       if (leds) {
+               of_platform_device_create(leds, "opal_leds", NULL);
+               of_node_put(leds);
+       }
 
        /* Create "opal" kobject under /sys/firmware */
        rc = opal_sysfs_init();
@@ -835,10 +753,13 @@ static int __init opal_init(void)
                opal_msglog_init();
        }
 
-       /* Initialize OPAL IPMI backend */
-       opal_ipmi_init(opal_node);
+       /* Initialize platform devices: IPMI backend, PRD & flash interface */
+       opal_pdev_init(opal_node, "ibm,opal-ipmi");
+       opal_pdev_init(opal_node, "ibm,opal-flash");
+       opal_pdev_init(opal_node, "ibm,opal-prd");
 
-       opal_flash_init(opal_node);
+       /* Initialise OPAL kmsg dumper for flushing console on panic */
+       opal_kmsg_init();
 
        return 0;
 }
@@ -846,15 +767,9 @@ machine_subsys_initcall(powernv, opal_init);
 
 void opal_shutdown(void)
 {
-       unsigned int i;
        long rc = OPAL_BUSY;
 
-       /* First free interrupts, which will also mask them */
-       for (i = 0; i < opal_irq_count; i++) {
-               if (opal_irqs[i])
-                       free_irq(opal_irqs[i], NULL);
-               opal_irqs[i] = 0;
-       }
+       opal_event_shutdown();
 
        /*
         * Then sync with OPAL which ensure anything that can
@@ -876,11 +791,14 @@ void opal_shutdown(void)
 
 /* Export this so that test modules can use it */
 EXPORT_SYMBOL_GPL(opal_invalid_call);
+EXPORT_SYMBOL_GPL(opal_xscom_read);
+EXPORT_SYMBOL_GPL(opal_xscom_write);
 EXPORT_SYMBOL_GPL(opal_ipmi_send);
 EXPORT_SYMBOL_GPL(opal_ipmi_recv);
 EXPORT_SYMBOL_GPL(opal_flash_read);
 EXPORT_SYMBOL_GPL(opal_flash_write);
 EXPORT_SYMBOL_GPL(opal_flash_erase);
+EXPORT_SYMBOL_GPL(opal_prd_msg);
 
 /* Convert a region of vmalloc memory to an opal sg list */
 struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr,
@@ -954,6 +872,7 @@ int opal_error_code(int rc)
        case OPAL_ASYNC_COMPLETION:     return -EINPROGRESS;
        case OPAL_BUSY_EVENT:           return -EBUSY;
        case OPAL_NO_MEM:               return -ENOMEM;
+       case OPAL_PERMISSION:           return -EPERM;
 
        case OPAL_UNSUPPORTED:          return -EIO;
        case OPAL_HARDWARE:             return -EIO;
@@ -970,3 +889,6 @@ EXPORT_SYMBOL_GPL(opal_rtc_write);
 EXPORT_SYMBOL_GPL(opal_tpo_read);
 EXPORT_SYMBOL_GPL(opal_tpo_write);
 EXPORT_SYMBOL_GPL(opal_i2c_request);
+/* Export these symbols for PowerNV LED class driver */
+EXPORT_SYMBOL_GPL(opal_leds_get_ind);
+EXPORT_SYMBOL_GPL(opal_leds_set_ind);