summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/firmware/imx/imx-scu-irq.c60
-rw-r--r--include/linux/firmware/imx/sci.h24
2 files changed, 81 insertions, 3 deletions
diff --git a/drivers/firmware/imx/imx-scu-irq.c b/drivers/firmware/imx/imx-scu-irq.c
index 369621c14105..b4272a75a315 100644
--- a/drivers/firmware/imx/imx-scu-irq.c
+++ b/drivers/firmware/imx/imx-scu-irq.c
@@ -10,10 +10,11 @@
#include <linux/firmware/imx/sci.h>
#include <linux/mailbox_client.h>
#include <linux/suspend.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
#define IMX_SC_IRQ_FUNC_ENABLE 1
#define IMX_SC_IRQ_FUNC_STATUS 2
-#define IMX_SC_IRQ_NUM_GROUP 7
static u32 mu_resource_id;
@@ -39,6 +40,20 @@ struct imx_sc_msg_irq_enable {
u8 enable;
} __packed;
+struct scu_wakeup {
+ u32 mask;
+ u32 wakeup_src;
+ bool valid;
+};
+
+/* Sysfs functions */
+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);
@@ -70,6 +85,10 @@ 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",
@@ -79,7 +98,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);
}
@@ -134,6 +158,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);
@@ -143,6 +172,24 @@ 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)
+{
+ u8 i = 0, size = 0;
+
+ for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) {
+ if (scu_irq_wakeup[i].wakeup_src != 0) {
+ if (scu_irq_wakeup[i].valid)
+ size += sprintf(buf + size, "Wakeup source group = %d, irq = 0x%x\n",
+ i, scu_irq_wakeup[i].wakeup_src);
+ else
+ size += sprintf(buf + size, "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;
@@ -182,6 +229,15 @@ 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 (sysfs_create_file(wakeup_obj, &wakeup_source_attr.attr)) {
+ pr_err("Cannot create sysfs file......\n");
+ kobject_put(wakeup_obj);
+ sysfs_remove_file(firmware_kobj, &wakeup_source_attr.attr);
+ }
+
return ret;
}
EXPORT_SYMBOL(imx_scu_enable_general_irq_channel);
diff --git a/include/linux/firmware/imx/sci.h b/include/linux/firmware/imx/sci.h
index e749594436bb..70fe789aa842 100644
--- a/include/linux/firmware/imx/sci.h
+++ b/include/linux/firmware/imx/sci.h
@@ -17,12 +17,34 @@
#include <linux/firmware/imx/svc/rm.h>
#include <linux/firmware/imx/svc/seco.h>
-#define IMX_SC_IRQ_GROUP_WAKE 3U /* Wakeup interrupts */
+#define IMX_SC_IRQ_NUM_GROUP 9
+
+#define IMX_SC_IRQ_GROUP_TEMP 0 /* Temp interrupts */
+#define IMX_SC_IRQ_GROUP_WDOG 1 /* Watchdog interrupts */
+#define IMX_SC_IRQ_GROUP_RTC 2 /* RTC interrupts */
+#define IMX_SC_IRQ_GROUP_WAKE 3 /* Wakeup interrupts */
+#define IMX_SC_IRQ_GROUP_SYSCTR 4 /* System counter interrupts */
+#define IMX_SC_IRQ_GROUP_REBOOTED 5 /* Partition reboot complete */
+#define IMX_SC_IRQ_GROUP_REBOOT 6 /* Partition reboot starting */
+#define IMX_SC_IRQ_GROUP_OFFED 7 /* Partition off complete */
+#define IMX_SC_IRQ_GROUP_OFF 8 /* Partition off starting */
+
+#define IMX_SC_IRQ_RTC BIT(0) /* RTC interrupt */
+#define IMX_SC_IRQ_WDOG BIT(0) /* Watch Dog interrupt */
+#define IMX_SC_IRQ_SYSCTR BIT(0) /* System Counter interrupt */
+#define IMX_SC_IRQ_BUTTON BIT(0) /* Button interrupt */
+#define IMX_SC_IRQ_PAD BIT(1) /* Pad wakeup */
+#define IMX_SC_IRQ_USR1 BIT(2) /* User defined 1 */
+#define IMX_SC_IRQ_USR2 BIT(3) /* User defined 2 */
+#define IMX_SC_IRQ_BC_PAD BIT(4) /* Pad wakeup (broadcast to all partitions) */
+#define IMX_SC_IRQ_SW_WAKE BIT(5) /* Software requested wake */
#define IMX_SC_IRQ_SECVIO BIT(6) /* Security violation */
+#define IMX_SC_IRQ_V2X_RESET BIT(7) /* V2X reset */
int imx_scu_enable_general_irq_channel(struct device *dev);
int imx_scu_irq_register_notifier(struct notifier_block *nb);
int imx_scu_irq_unregister_notifier(struct notifier_block *nb);
int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable);
int imx_scu_irq_get_status(u8 group, u32 *irq_status);
+
#endif /* _SC_SCI_H */