powerpc/powernv: Add interfaces for flash device access
[firefly-linux-kernel-4.4.55.git] / arch / powerpc / platforms / powernv / opal.c
index 45f0d9aa37331a830a9ec3fe2f707a22b298883a..2241565b0739ff3bc6dc84f9bc6c63c70edf8b8c 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/kobject.h>
 #include <linux/delay.h>
 #include <linux/memblock.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
 
 #include <asm/machdep.h>
 #include <asm/opal.h>
@@ -58,6 +60,7 @@ 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)
 {
@@ -302,23 +305,26 @@ void opal_notifier_disable(void)
  * Opal message notifier based on message type. Allow subscribers to get
  * notified for specific messgae type.
  */
-int opal_message_notifier_register(enum OpalMessageType msg_type,
+int opal_message_notifier_register(enum opal_msg_type msg_type,
                                        struct notifier_block *nb)
 {
-       if (!nb) {
-               pr_warning("%s: Invalid argument (%p)\n",
-                          __func__, nb);
-               return -EINVAL;
-       }
-       if (msg_type > OPAL_MSG_TYPE_MAX) {
-               pr_warning("%s: Invalid message type argument (%d)\n",
+       if (!nb || msg_type >= OPAL_MSG_TYPE_MAX) {
+               pr_warning("%s: Invalid arguments, msg_type:%d\n",
                           __func__, msg_type);
                return -EINVAL;
        }
+
        return atomic_notifier_chain_register(
                                &opal_msg_notifier_head[msg_type], nb);
 }
 
+int opal_message_notifier_unregister(enum opal_msg_type msg_type,
+                                    struct notifier_block *nb)
+{
+       return atomic_notifier_chain_unregister(
+                       &opal_msg_notifier_head[msg_type], nb);
+}
+
 static void opal_message_do_notify(uint32_t msg_type, void *msg)
 {
        /* notify subscribers */
@@ -351,7 +357,7 @@ static void opal_handle_message(void)
        type = be32_to_cpu(msg.msg_type);
 
        /* Sanity check */
-       if (type > OPAL_MSG_TYPE_MAX) {
+       if (type >= OPAL_MSG_TYPE_MAX) {
                pr_warning("%s: Unknown message type: %u\n", __func__, type);
                return;
        }
@@ -687,6 +693,15 @@ static void __init opal_dump_region_init(void)
                        "rc = %d\n", rc);
 }
 
+static void opal_flash_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-flash"))
+                       of_platform_device_create(np, NULL, NULL);
+}
+
 static void opal_ipmi_init(struct device_node *opal_node)
 {
        struct device_node *np;
@@ -744,6 +759,29 @@ static void __init opal_irq_init(struct device_node *dn)
        }
 }
 
+static int kopald(void *unused)
+{
+       set_freezable();
+       do {
+               try_to_freeze();
+               opal_poll_events(NULL);
+               msleep_interruptible(opal_heartbeat);
+       } while (!kthread_should_stop());
+
+       return 0;
+}
+
+static void opal_init_heartbeat(void)
+{
+       /* Old firwmware, we assume the HVC heartbeat is sufficient */
+       if (of_property_read_u32(opal_node, "ibm,heartbeat-ms",
+                                &opal_heartbeat) != 0)
+               opal_heartbeat = 0;
+
+       if (opal_heartbeat)
+               kthread_run(kopald, NULL, "kopald");
+}
+
 static int __init opal_init(void)
 {
        struct device_node *np, *consoles;
@@ -772,6 +810,9 @@ static int __init opal_init(void)
        /* 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);
 
@@ -785,7 +826,7 @@ static int __init opal_init(void)
                /* Setup error log interface */
                rc = opal_elog_init();
                /* Setup code update interface */
-               opal_flash_init();
+               opal_flash_update_init();
                /* Setup platform dump extract interface */
                opal_platform_dump_init();
                /* Setup system parameters interface */
@@ -794,8 +835,11 @@ static int __init opal_init(void)
                opal_msglog_init();
        }
 
+       /* Initialize OPAL IPMI backend */
        opal_ipmi_init(opal_node);
 
+       opal_flash_init(opal_node);
+
        return 0;
 }
 machine_subsys_initcall(powernv, opal_init);
@@ -834,6 +878,9 @@ void opal_shutdown(void)
 EXPORT_SYMBOL_GPL(opal_invalid_call);
 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);
 
 /* Convert a region of vmalloc memory to an opal sg list */
 struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr,
@@ -898,6 +945,25 @@ void opal_free_sg_list(struct opal_sg_list *sg)
        }
 }
 
+int opal_error_code(int rc)
+{
+       switch (rc) {
+       case OPAL_SUCCESS:              return 0;
+
+       case OPAL_PARAMETER:            return -EINVAL;
+       case OPAL_ASYNC_COMPLETION:     return -EINPROGRESS;
+       case OPAL_BUSY_EVENT:           return -EBUSY;
+       case OPAL_NO_MEM:               return -ENOMEM;
+
+       case OPAL_UNSUPPORTED:          return -EIO;
+       case OPAL_HARDWARE:             return -EIO;
+       case OPAL_INTERNAL_ERROR:       return -EIO;
+       default:
+               pr_err("%s: unexpected OPAL error %d\n", __func__, rc);
+               return -EIO;
+       }
+}
+
 EXPORT_SYMBOL_GPL(opal_poll_events);
 EXPORT_SYMBOL_GPL(opal_rtc_read);
 EXPORT_SYMBOL_GPL(opal_rtc_write);