162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2019,2023 NXP 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Implementation of the SCU IRQ functions using MU. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <dt-bindings/firmware/imx/rsrc.h> 1062306a36Sopenharmony_ci#include <linux/firmware/imx/ipc.h> 1162306a36Sopenharmony_ci#include <linux/firmware/imx/sci.h> 1262306a36Sopenharmony_ci#include <linux/kobject.h> 1362306a36Sopenharmony_ci#include <linux/mailbox_client.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/suspend.h> 1662306a36Sopenharmony_ci#include <linux/sysfs.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define IMX_SC_IRQ_FUNC_ENABLE 1 1962306a36Sopenharmony_ci#define IMX_SC_IRQ_FUNC_STATUS 2 2062306a36Sopenharmony_ci#define IMX_SC_IRQ_NUM_GROUP 9 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic u32 mu_resource_id; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct imx_sc_msg_irq_get_status { 2562306a36Sopenharmony_ci struct imx_sc_rpc_msg hdr; 2662306a36Sopenharmony_ci union { 2762306a36Sopenharmony_ci struct { 2862306a36Sopenharmony_ci u16 resource; 2962306a36Sopenharmony_ci u8 group; 3062306a36Sopenharmony_ci u8 reserved; 3162306a36Sopenharmony_ci } __packed req; 3262306a36Sopenharmony_ci struct { 3362306a36Sopenharmony_ci u32 status; 3462306a36Sopenharmony_ci } resp; 3562306a36Sopenharmony_ci } data; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct imx_sc_msg_irq_enable { 3962306a36Sopenharmony_ci struct imx_sc_rpc_msg hdr; 4062306a36Sopenharmony_ci u32 mask; 4162306a36Sopenharmony_ci u16 resource; 4262306a36Sopenharmony_ci u8 group; 4362306a36Sopenharmony_ci u8 enable; 4462306a36Sopenharmony_ci} __packed; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct scu_wakeup { 4762306a36Sopenharmony_ci u32 mask; 4862306a36Sopenharmony_ci u32 wakeup_src; 4962306a36Sopenharmony_ci bool valid; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* Sysfs functions */ 5362306a36Sopenharmony_cistatic struct kobject *wakeup_obj; 5462306a36Sopenharmony_cistatic ssize_t wakeup_source_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); 5562306a36Sopenharmony_cistatic struct kobj_attribute wakeup_source_attr = 5662306a36Sopenharmony_ci __ATTR(wakeup_src, 0660, wakeup_source_show, NULL); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic struct scu_wakeup scu_irq_wakeup[IMX_SC_IRQ_NUM_GROUP]; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic struct imx_sc_ipc *imx_sc_irq_ipc_handle; 6162306a36Sopenharmony_cistatic struct work_struct imx_sc_irq_work; 6262306a36Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(imx_scu_irq_notifier_chain); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciint imx_scu_irq_register_notifier(struct notifier_block *nb) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci return blocking_notifier_chain_register( 6762306a36Sopenharmony_ci &imx_scu_irq_notifier_chain, nb); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ciEXPORT_SYMBOL(imx_scu_irq_register_notifier); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciint imx_scu_irq_unregister_notifier(struct notifier_block *nb) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return blocking_notifier_chain_unregister( 7462306a36Sopenharmony_ci &imx_scu_irq_notifier_chain, nb); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ciEXPORT_SYMBOL(imx_scu_irq_unregister_notifier); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int imx_scu_irq_notifier_call_chain(unsigned long status, u8 *group) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci return blocking_notifier_call_chain(&imx_scu_irq_notifier_chain, 8162306a36Sopenharmony_ci status, (void *)group); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic void imx_scu_irq_work_handler(struct work_struct *work) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci u32 irq_status; 8762306a36Sopenharmony_ci int ret; 8862306a36Sopenharmony_ci u8 i; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) { 9162306a36Sopenharmony_ci if (scu_irq_wakeup[i].mask) { 9262306a36Sopenharmony_ci scu_irq_wakeup[i].valid = false; 9362306a36Sopenharmony_ci scu_irq_wakeup[i].wakeup_src = 0; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci ret = imx_scu_irq_get_status(i, &irq_status); 9762306a36Sopenharmony_ci if (ret) { 9862306a36Sopenharmony_ci pr_err("get irq group %d status failed, ret %d\n", 9962306a36Sopenharmony_ci i, ret); 10062306a36Sopenharmony_ci return; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (!irq_status) 10462306a36Sopenharmony_ci continue; 10562306a36Sopenharmony_ci if (scu_irq_wakeup[i].mask & irq_status) { 10662306a36Sopenharmony_ci scu_irq_wakeup[i].valid = true; 10762306a36Sopenharmony_ci scu_irq_wakeup[i].wakeup_src = irq_status & scu_irq_wakeup[i].mask; 10862306a36Sopenharmony_ci } else { 10962306a36Sopenharmony_ci scu_irq_wakeup[i].wakeup_src = irq_status; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci pm_system_wakeup(); 11362306a36Sopenharmony_ci imx_scu_irq_notifier_call_chain(irq_status, &i); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciint imx_scu_irq_get_status(u8 group, u32 *irq_status) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct imx_sc_msg_irq_get_status msg; 12062306a36Sopenharmony_ci struct imx_sc_rpc_msg *hdr = &msg.hdr; 12162306a36Sopenharmony_ci int ret; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci hdr->ver = IMX_SC_RPC_VERSION; 12462306a36Sopenharmony_ci hdr->svc = IMX_SC_RPC_SVC_IRQ; 12562306a36Sopenharmony_ci hdr->func = IMX_SC_IRQ_FUNC_STATUS; 12662306a36Sopenharmony_ci hdr->size = 2; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci msg.data.req.resource = mu_resource_id; 12962306a36Sopenharmony_ci msg.data.req.group = group; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); 13262306a36Sopenharmony_ci if (ret) 13362306a36Sopenharmony_ci return ret; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (irq_status) 13662306a36Sopenharmony_ci *irq_status = msg.data.resp.status; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ciEXPORT_SYMBOL(imx_scu_irq_get_status); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ciint imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct imx_sc_msg_irq_enable msg; 14562306a36Sopenharmony_ci struct imx_sc_rpc_msg *hdr = &msg.hdr; 14662306a36Sopenharmony_ci int ret; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (!imx_sc_irq_ipc_handle) 14962306a36Sopenharmony_ci return -EPROBE_DEFER; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci hdr->ver = IMX_SC_RPC_VERSION; 15262306a36Sopenharmony_ci hdr->svc = IMX_SC_RPC_SVC_IRQ; 15362306a36Sopenharmony_ci hdr->func = IMX_SC_IRQ_FUNC_ENABLE; 15462306a36Sopenharmony_ci hdr->size = 3; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci msg.resource = mu_resource_id; 15762306a36Sopenharmony_ci msg.group = group; 15862306a36Sopenharmony_ci msg.mask = mask; 15962306a36Sopenharmony_ci msg.enable = enable; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); 16262306a36Sopenharmony_ci if (ret) 16362306a36Sopenharmony_ci pr_err("enable irq failed, group %d, mask %d, ret %d\n", 16462306a36Sopenharmony_ci group, mask, ret); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (enable) 16762306a36Sopenharmony_ci scu_irq_wakeup[group].mask |= mask; 16862306a36Sopenharmony_ci else 16962306a36Sopenharmony_ci scu_irq_wakeup[group].mask &= ~mask; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return ret; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ciEXPORT_SYMBOL(imx_scu_irq_group_enable); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic void imx_scu_irq_callback(struct mbox_client *c, void *msg) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci schedule_work(&imx_sc_irq_work); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic ssize_t wakeup_source_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci int i; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) { 18562306a36Sopenharmony_ci if (!scu_irq_wakeup[i].wakeup_src) 18662306a36Sopenharmony_ci continue; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (scu_irq_wakeup[i].valid) 18962306a36Sopenharmony_ci sprintf(buf, "Wakeup source group = %d, irq = 0x%x\n", 19062306a36Sopenharmony_ci i, scu_irq_wakeup[i].wakeup_src); 19162306a36Sopenharmony_ci else 19262306a36Sopenharmony_ci sprintf(buf, "Spurious SCU wakeup, group = %d, irq = 0x%x\n", 19362306a36Sopenharmony_ci i, scu_irq_wakeup[i].wakeup_src); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return strlen(buf); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciint imx_scu_enable_general_irq_channel(struct device *dev) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct of_phandle_args spec; 20262306a36Sopenharmony_ci struct mbox_client *cl; 20362306a36Sopenharmony_ci struct mbox_chan *ch; 20462306a36Sopenharmony_ci int ret = 0, i = 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ret = imx_scu_get_handle(&imx_sc_irq_ipc_handle); 20762306a36Sopenharmony_ci if (ret) 20862306a36Sopenharmony_ci return ret; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci cl = devm_kzalloc(dev, sizeof(*cl), GFP_KERNEL); 21162306a36Sopenharmony_ci if (!cl) 21262306a36Sopenharmony_ci return -ENOMEM; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci cl->dev = dev; 21562306a36Sopenharmony_ci cl->rx_callback = imx_scu_irq_callback; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* SCU general IRQ uses general interrupt channel 3 */ 21862306a36Sopenharmony_ci ch = mbox_request_channel_byname(cl, "gip3"); 21962306a36Sopenharmony_ci if (IS_ERR(ch)) { 22062306a36Sopenharmony_ci ret = PTR_ERR(ch); 22162306a36Sopenharmony_ci dev_err(dev, "failed to request mbox chan gip3, ret %d\n", ret); 22262306a36Sopenharmony_ci devm_kfree(dev, cl); 22362306a36Sopenharmony_ci return ret; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci INIT_WORK(&imx_sc_irq_work, imx_scu_irq_work_handler); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (!of_parse_phandle_with_args(dev->of_node, "mboxes", 22962306a36Sopenharmony_ci "#mbox-cells", 0, &spec)) 23062306a36Sopenharmony_ci i = of_alias_get_id(spec.np, "mu"); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* use mu1 as general mu irq channel if failed */ 23362306a36Sopenharmony_ci if (i < 0) 23462306a36Sopenharmony_ci i = 1; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci mu_resource_id = IMX_SC_R_MU_0A + i; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Create directory under /sysfs/firmware */ 23962306a36Sopenharmony_ci wakeup_obj = kobject_create_and_add("scu_wakeup_source", firmware_kobj); 24062306a36Sopenharmony_ci if (!wakeup_obj) { 24162306a36Sopenharmony_ci ret = -ENOMEM; 24262306a36Sopenharmony_ci goto free_ch; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ret = sysfs_create_file(wakeup_obj, &wakeup_source_attr.attr); 24662306a36Sopenharmony_ci if (ret) { 24762306a36Sopenharmony_ci dev_err(dev, "Cannot create wakeup source src file......\n"); 24862306a36Sopenharmony_ci kobject_put(wakeup_obj); 24962306a36Sopenharmony_ci goto free_ch; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cifree_ch: 25562306a36Sopenharmony_ci mbox_free_channel(ch); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return ret; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ciEXPORT_SYMBOL(imx_scu_enable_general_irq_channel); 260