linux/drivers/firmware/imx/imx-scu-irq.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2019 NXP
   4 *
   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/mailbox_client.h>
  13#include <linux/suspend.h>
  14
  15#define IMX_SC_IRQ_FUNC_ENABLE  1
  16#define IMX_SC_IRQ_FUNC_STATUS  2
  17#define IMX_SC_IRQ_NUM_GROUP    4
  18
  19static u32 mu_resource_id;
  20
  21struct imx_sc_msg_irq_get_status {
  22        struct imx_sc_rpc_msg hdr;
  23        union {
  24                struct {
  25                        u16 resource;
  26                        u8 group;
  27                        u8 reserved;
  28                } __packed req;
  29                struct {
  30                        u32 status;
  31                } resp;
  32        } data;
  33};
  34
  35struct imx_sc_msg_irq_enable {
  36        struct imx_sc_rpc_msg hdr;
  37        u32 mask;
  38        u16 resource;
  39        u8 group;
  40        u8 enable;
  41} __packed;
  42
  43static struct imx_sc_ipc *imx_sc_irq_ipc_handle;
  44static struct work_struct imx_sc_irq_work;
  45static ATOMIC_NOTIFIER_HEAD(imx_scu_irq_notifier_chain);
  46
  47int imx_scu_irq_register_notifier(struct notifier_block *nb)
  48{
  49        return atomic_notifier_chain_register(
  50                &imx_scu_irq_notifier_chain, nb);
  51}
  52EXPORT_SYMBOL(imx_scu_irq_register_notifier);
  53
  54int imx_scu_irq_unregister_notifier(struct notifier_block *nb)
  55{
  56        return atomic_notifier_chain_unregister(
  57                &imx_scu_irq_notifier_chain, nb);
  58}
  59EXPORT_SYMBOL(imx_scu_irq_unregister_notifier);
  60
  61static int imx_scu_irq_notifier_call_chain(unsigned long status, u8 *group)
  62{
  63        return atomic_notifier_call_chain(&imx_scu_irq_notifier_chain,
  64                status, (void *)group);
  65}
  66
  67static void imx_scu_irq_work_handler(struct work_struct *work)
  68{
  69        struct imx_sc_msg_irq_get_status msg;
  70        struct imx_sc_rpc_msg *hdr = &msg.hdr;
  71        u32 irq_status;
  72        int ret;
  73        u8 i;
  74
  75        for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) {
  76                hdr->ver = IMX_SC_RPC_VERSION;
  77                hdr->svc = IMX_SC_RPC_SVC_IRQ;
  78                hdr->func = IMX_SC_IRQ_FUNC_STATUS;
  79                hdr->size = 2;
  80
  81                msg.data.req.resource = mu_resource_id;
  82                msg.data.req.group = i;
  83
  84                ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true);
  85                if (ret) {
  86                        pr_err("get irq group %d status failed, ret %d\n",
  87                               i, ret);
  88                        return;
  89                }
  90
  91                irq_status = msg.data.resp.status;
  92                if (!irq_status)
  93                        continue;
  94
  95                pm_system_wakeup();
  96                imx_scu_irq_notifier_call_chain(irq_status, &i);
  97        }
  98}
  99
 100int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable)
 101{
 102        struct imx_sc_msg_irq_enable msg;
 103        struct imx_sc_rpc_msg *hdr = &msg.hdr;
 104        int ret;
 105
 106        if (!imx_sc_irq_ipc_handle)
 107                return -EPROBE_DEFER;
 108
 109        hdr->ver = IMX_SC_RPC_VERSION;
 110        hdr->svc = IMX_SC_RPC_SVC_IRQ;
 111        hdr->func = IMX_SC_IRQ_FUNC_ENABLE;
 112        hdr->size = 3;
 113
 114        msg.resource = mu_resource_id;
 115        msg.group = group;
 116        msg.mask = mask;
 117        msg.enable = enable;
 118
 119        ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true);
 120        if (ret)
 121                pr_err("enable irq failed, group %d, mask %d, ret %d\n",
 122                        group, mask, ret);
 123
 124        return ret;
 125}
 126EXPORT_SYMBOL(imx_scu_irq_group_enable);
 127
 128static void imx_scu_irq_callback(struct mbox_client *c, void *msg)
 129{
 130        schedule_work(&imx_sc_irq_work);
 131}
 132
 133int imx_scu_enable_general_irq_channel(struct device *dev)
 134{
 135        struct of_phandle_args spec;
 136        struct mbox_client *cl;
 137        struct mbox_chan *ch;
 138        int ret = 0, i = 0;
 139
 140        ret = imx_scu_get_handle(&imx_sc_irq_ipc_handle);
 141        if (ret)
 142                return ret;
 143
 144        cl = devm_kzalloc(dev, sizeof(*cl), GFP_KERNEL);
 145        if (!cl)
 146                return -ENOMEM;
 147
 148        cl->dev = dev;
 149        cl->rx_callback = imx_scu_irq_callback;
 150
 151        /* SCU general IRQ uses general interrupt channel 3 */
 152        ch = mbox_request_channel_byname(cl, "gip3");
 153        if (IS_ERR(ch)) {
 154                ret = PTR_ERR(ch);
 155                dev_err(dev, "failed to request mbox chan gip3, ret %d\n", ret);
 156                devm_kfree(dev, cl);
 157                return ret;
 158        }
 159
 160        INIT_WORK(&imx_sc_irq_work, imx_scu_irq_work_handler);
 161
 162        if (!of_parse_phandle_with_args(dev->of_node, "mboxes",
 163                                       "#mbox-cells", 0, &spec))
 164                i = of_alias_get_id(spec.np, "mu");
 165
 166        /* use mu1 as general mu irq channel if failed */
 167        if (i < 0)
 168                i = 1;
 169
 170        mu_resource_id = IMX_SC_R_MU_0A + i;
 171
 172        return ret;
 173}
 174EXPORT_SYMBOL(imx_scu_enable_general_irq_channel);
 175