260 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright 2019,2023 NXP4 *5 * Implementation of the SCU IRQ functions using MU.6 *7 */8 9#include <dt-bindings/firmware/imx/rsrc.h>10#include <linux/firmware/imx/ipc.h>11#include <linux/firmware/imx/sci.h>12#include <linux/kobject.h>13#include <linux/mailbox_client.h>14#include <linux/of.h>15#include <linux/suspend.h>16#include <linux/sysfs.h>17 18#define IMX_SC_IRQ_FUNC_ENABLE 119#define IMX_SC_IRQ_FUNC_STATUS 220#define IMX_SC_IRQ_NUM_GROUP 921 22static u32 mu_resource_id;23 24struct imx_sc_msg_irq_get_status {25 struct imx_sc_rpc_msg hdr;26 union {27 struct {28 u16 resource;29 u8 group;30 u8 reserved;31 } __packed req;32 struct {33 u32 status;34 } resp;35 } data;36};37 38struct imx_sc_msg_irq_enable {39 struct imx_sc_rpc_msg hdr;40 u32 mask;41 u16 resource;42 u8 group;43 u8 enable;44} __packed;45 46struct scu_wakeup {47 u32 mask;48 u32 wakeup_src;49 bool valid;50};51 52/* Sysfs functions */53static struct kobject *wakeup_obj;54static ssize_t wakeup_source_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);55static struct kobj_attribute wakeup_source_attr =56 __ATTR(wakeup_src, 0660, wakeup_source_show, NULL);57 58static struct scu_wakeup scu_irq_wakeup[IMX_SC_IRQ_NUM_GROUP];59 60static struct imx_sc_ipc *imx_sc_irq_ipc_handle;61static struct work_struct imx_sc_irq_work;62static BLOCKING_NOTIFIER_HEAD(imx_scu_irq_notifier_chain);63 64int imx_scu_irq_register_notifier(struct notifier_block *nb)65{66 return blocking_notifier_chain_register(67 &imx_scu_irq_notifier_chain, nb);68}69EXPORT_SYMBOL(imx_scu_irq_register_notifier);70 71int imx_scu_irq_unregister_notifier(struct notifier_block *nb)72{73 return blocking_notifier_chain_unregister(74 &imx_scu_irq_notifier_chain, nb);75}76EXPORT_SYMBOL(imx_scu_irq_unregister_notifier);77 78static int imx_scu_irq_notifier_call_chain(unsigned long status, u8 *group)79{80 return blocking_notifier_call_chain(&imx_scu_irq_notifier_chain,81 status, (void *)group);82}83 84static void imx_scu_irq_work_handler(struct work_struct *work)85{86 u32 irq_status;87 int ret;88 u8 i;89 90 for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) {91 if (scu_irq_wakeup[i].mask) {92 scu_irq_wakeup[i].valid = false;93 scu_irq_wakeup[i].wakeup_src = 0;94 }95 96 ret = imx_scu_irq_get_status(i, &irq_status);97 if (ret) {98 pr_err("get irq group %d status failed, ret %d\n",99 i, ret);100 return;101 }102 103 if (!irq_status)104 continue;105 if (scu_irq_wakeup[i].mask & irq_status) {106 scu_irq_wakeup[i].valid = true;107 scu_irq_wakeup[i].wakeup_src = irq_status & scu_irq_wakeup[i].mask;108 } else {109 scu_irq_wakeup[i].wakeup_src = irq_status;110 }111 112 pm_system_wakeup();113 imx_scu_irq_notifier_call_chain(irq_status, &i);114 }115}116 117int imx_scu_irq_get_status(u8 group, u32 *irq_status)118{119 struct imx_sc_msg_irq_get_status msg;120 struct imx_sc_rpc_msg *hdr = &msg.hdr;121 int ret;122 123 hdr->ver = IMX_SC_RPC_VERSION;124 hdr->svc = IMX_SC_RPC_SVC_IRQ;125 hdr->func = IMX_SC_IRQ_FUNC_STATUS;126 hdr->size = 2;127 128 msg.data.req.resource = mu_resource_id;129 msg.data.req.group = group;130 131 ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true);132 if (ret)133 return ret;134 135 if (irq_status)136 *irq_status = msg.data.resp.status;137 138 return 0;139}140EXPORT_SYMBOL(imx_scu_irq_get_status);141 142int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable)143{144 struct imx_sc_msg_irq_enable msg;145 struct imx_sc_rpc_msg *hdr = &msg.hdr;146 int ret;147 148 if (!imx_sc_irq_ipc_handle)149 return -EPROBE_DEFER;150 151 hdr->ver = IMX_SC_RPC_VERSION;152 hdr->svc = IMX_SC_RPC_SVC_IRQ;153 hdr->func = IMX_SC_IRQ_FUNC_ENABLE;154 hdr->size = 3;155 156 msg.resource = mu_resource_id;157 msg.group = group;158 msg.mask = mask;159 msg.enable = enable;160 161 ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true);162 if (ret)163 pr_err("enable irq failed, group %d, mask %d, ret %d\n",164 group, mask, ret);165 166 if (enable)167 scu_irq_wakeup[group].mask |= mask;168 else169 scu_irq_wakeup[group].mask &= ~mask;170 171 return ret;172}173EXPORT_SYMBOL(imx_scu_irq_group_enable);174 175static void imx_scu_irq_callback(struct mbox_client *c, void *msg)176{177 schedule_work(&imx_sc_irq_work);178}179 180static ssize_t wakeup_source_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)181{182 int i;183 184 for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) {185 if (!scu_irq_wakeup[i].wakeup_src)186 continue;187 188 if (scu_irq_wakeup[i].valid)189 sprintf(buf, "Wakeup source group = %d, irq = 0x%x\n",190 i, scu_irq_wakeup[i].wakeup_src);191 else192 sprintf(buf, "Spurious SCU wakeup, group = %d, irq = 0x%x\n",193 i, scu_irq_wakeup[i].wakeup_src);194 }195 196 return strlen(buf);197}198 199int imx_scu_enable_general_irq_channel(struct device *dev)200{201 struct of_phandle_args spec;202 struct mbox_client *cl;203 struct mbox_chan *ch;204 int ret = 0, i = 0;205 206 ret = imx_scu_get_handle(&imx_sc_irq_ipc_handle);207 if (ret)208 return ret;209 210 cl = devm_kzalloc(dev, sizeof(*cl), GFP_KERNEL);211 if (!cl)212 return -ENOMEM;213 214 cl->dev = dev;215 cl->rx_callback = imx_scu_irq_callback;216 217 /* SCU general IRQ uses general interrupt channel 3 */218 ch = mbox_request_channel_byname(cl, "gip3");219 if (IS_ERR(ch)) {220 ret = PTR_ERR(ch);221 dev_err(dev, "failed to request mbox chan gip3, ret %d\n", ret);222 devm_kfree(dev, cl);223 return ret;224 }225 226 INIT_WORK(&imx_sc_irq_work, imx_scu_irq_work_handler);227 228 if (!of_parse_phandle_with_args(dev->of_node, "mboxes",229 "#mbox-cells", 0, &spec))230 i = of_alias_get_id(spec.np, "mu");231 232 /* use mu1 as general mu irq channel if failed */233 if (i < 0)234 i = 1;235 236 mu_resource_id = IMX_SC_R_MU_0A + i;237 238 /* Create directory under /sysfs/firmware */239 wakeup_obj = kobject_create_and_add("scu_wakeup_source", firmware_kobj);240 if (!wakeup_obj) {241 ret = -ENOMEM;242 goto free_ch;243 }244 245 ret = sysfs_create_file(wakeup_obj, &wakeup_source_attr.attr);246 if (ret) {247 dev_err(dev, "Cannot create wakeup source src file......\n");248 kobject_put(wakeup_obj);249 goto free_ch;250 }251 252 return 0;253 254free_ch:255 mbox_free_channel(ch);256 257 return ret;258}259EXPORT_SYMBOL(imx_scu_enable_general_irq_channel);260