firmware: imx: scu-irq: support identifying SCU wakeup source from sysfs
[linux-2.6-microblaze.git] / drivers / firmware / imx / imx-scu-irq.c
index 8d902db..7cc0dec 100644 (file)
@@ -9,8 +9,10 @@
 #include <dt-bindings/firmware/imx/rsrc.h>
 #include <linux/firmware/imx/ipc.h>
 #include <linux/firmware/imx/sci.h>
+#include <linux/kobject.h>
 #include <linux/mailbox_client.h>
 #include <linux/suspend.h>
+#include <linux/sysfs.h>
 
 #define IMX_SC_IRQ_FUNC_ENABLE 1
 #define IMX_SC_IRQ_FUNC_STATUS 2
@@ -40,6 +42,20 @@ struct imx_sc_msg_irq_enable {
        u8 enable;
 } __packed;
 
+struct scu_wakeup {
+       u32 mask;
+       u32 wakeup_src;
+       bool valid;
+};
+
+/* Sysfs functions */
+static struct kobject *wakeup_obj;
+static ssize_t wakeup_source_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static struct kobj_attribute wakeup_source_attr =
+               __ATTR(wakeup_src, 0660, wakeup_source_show, NULL);
+
+static struct scu_wakeup scu_irq_wakeup[IMX_SC_IRQ_NUM_GROUP];
+
 static struct imx_sc_ipc *imx_sc_irq_ipc_handle;
 static struct work_struct imx_sc_irq_work;
 static BLOCKING_NOTIFIER_HEAD(imx_scu_irq_notifier_chain);
@@ -71,6 +87,11 @@ static void imx_scu_irq_work_handler(struct work_struct *work)
        u8 i;
 
        for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) {
+               if (scu_irq_wakeup[i].mask) {
+                       scu_irq_wakeup[i].valid = false;
+                       scu_irq_wakeup[i].wakeup_src = 0;
+               }
+
                ret = imx_scu_irq_get_status(i, &irq_status);
                if (ret) {
                        pr_err("get irq group %d status failed, ret %d\n",
@@ -80,6 +101,12 @@ static void imx_scu_irq_work_handler(struct work_struct *work)
 
                if (!irq_status)
                        continue;
+               if (scu_irq_wakeup[i].mask & irq_status) {
+                       scu_irq_wakeup[i].valid = true;
+                       scu_irq_wakeup[i].wakeup_src = irq_status & scu_irq_wakeup[i].mask;
+               } else {
+                       scu_irq_wakeup[i].wakeup_src = irq_status;
+               }
 
                pm_system_wakeup();
                imx_scu_irq_notifier_call_chain(irq_status, &i);
@@ -135,6 +162,11 @@ int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable)
                pr_err("enable irq failed, group %d, mask %d, ret %d\n",
                        group, mask, ret);
 
+       if (enable)
+               scu_irq_wakeup[group].mask |= mask;
+       else
+               scu_irq_wakeup[group].mask &= ~mask;
+
        return ret;
 }
 EXPORT_SYMBOL(imx_scu_irq_group_enable);
@@ -144,6 +176,25 @@ static void imx_scu_irq_callback(struct mbox_client *c, void *msg)
        schedule_work(&imx_sc_irq_work);
 }
 
+static ssize_t wakeup_source_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+       int i;
+
+       for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) {
+               if (!scu_irq_wakeup[i].wakeup_src)
+                       continue;
+
+               if (scu_irq_wakeup[i].valid)
+                       sprintf(buf, "Wakeup source group = %d, irq = 0x%x\n",
+                               i, scu_irq_wakeup[i].wakeup_src);
+               else
+                       sprintf(buf, "Spurious SCU wakeup, group = %d, irq = 0x%x\n",
+                               i, scu_irq_wakeup[i].wakeup_src);
+       }
+
+       return strlen(buf);
+}
+
 int imx_scu_enable_general_irq_channel(struct device *dev)
 {
        struct of_phandle_args spec;
@@ -183,6 +234,25 @@ int imx_scu_enable_general_irq_channel(struct device *dev)
 
        mu_resource_id = IMX_SC_R_MU_0A + i;
 
+       /* Create directory under /sysfs/firmware */
+       wakeup_obj = kobject_create_and_add("scu_wakeup_source", firmware_kobj);
+       if (!wakeup_obj) {
+               ret = -ENOMEM;
+               goto free_ch;
+       }
+
+       ret = sysfs_create_file(wakeup_obj, &wakeup_source_attr.attr);
+       if (ret) {
+               dev_err(dev, "Cannot create wakeup source src file......\n");
+               kobject_put(wakeup_obj);
+               goto free_ch;
+       }
+
+       return 0;
+
+free_ch:
+       mbox_free_channel(ch);
+
        return ret;
 }
 EXPORT_SYMBOL(imx_scu_enable_general_irq_channel);