aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnson Huang <anson.huang@nxp.com>2019-04-09 00:59:55 -0400
committerShawn Guo <shawnguo@kernel.org>2019-04-11 03:31:02 -0400
commit851826c7566e9bb4d03eb050634031ecc802affb (patch)
treee1a7fddbc34e3dab2899e98412c70591f20d5397
parent9f735c4e94fcbec66aa282eebb92c4aa3de88c6d (diff)
firmware: imx: enable imx scu general irq function
The System Controller Firmware (SCFW) controls RTC, thermal and WDOG etc., these resources' interrupt function are managed by SCU. When any IRQ pending, SCU will notify Linux via MU general interrupt channel #3, and Linux kernel needs to call SCU APIs to get IRQ status and notify each module to handle the interrupt. Since there is no data transmission for SCU IRQ notification, so doorbell mode is used for this MU channel, and SCU driver will use notifier mechanism to broadcast to every module which registers the SCU block notifier. Signed-off-by: Anson Huang <Anson.Huang@nxp.com> Reviewed-by: Dong Aisheng <aisheng.dong@nxp.com> Signed-off-by: Shawn Guo <shawnguo@kernel.org>
-rw-r--r--drivers/firmware/imx/Makefile2
-rw-r--r--drivers/firmware/imx/imx-scu-irq.c168
-rw-r--r--drivers/firmware/imx/imx-scu.c6
-rw-r--r--include/linux/firmware/imx/sci.h5
4 files changed, 180 insertions, 1 deletions
diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile
index 1b2e15b3c9ca..802c4ad8e8f9 100644
--- a/drivers/firmware/imx/Makefile
+++ b/drivers/firmware/imx/Makefile
@@ -1,3 +1,3 @@
1# SPDX-License-Identifier: GPL-2.0 1# SPDX-License-Identifier: GPL-2.0
2obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o 2obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o
3obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o 3obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o
diff --git a/drivers/firmware/imx/imx-scu-irq.c b/drivers/firmware/imx/imx-scu-irq.c
new file mode 100644
index 000000000000..043833ad3c1a
--- /dev/null
+++ b/drivers/firmware/imx/imx-scu-irq.c
@@ -0,0 +1,168 @@
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/mailbox_client.h>
12
13#define IMX_SC_IRQ_FUNC_ENABLE 1
14#define IMX_SC_IRQ_FUNC_STATUS 2
15#define IMX_SC_IRQ_NUM_GROUP 4
16
17static u32 mu_resource_id;
18
19struct imx_sc_msg_irq_get_status {
20 struct imx_sc_rpc_msg hdr;
21 union {
22 struct {
23 u16 resource;
24 u8 group;
25 u8 reserved;
26 } __packed req;
27 struct {
28 u32 status;
29 } resp;
30 } data;
31};
32
33struct imx_sc_msg_irq_enable {
34 struct imx_sc_rpc_msg hdr;
35 u32 mask;
36 u16 resource;
37 u8 group;
38 u8 enable;
39} __packed;
40
41static struct imx_sc_ipc *imx_sc_irq_ipc_handle;
42static struct work_struct imx_sc_irq_work;
43static ATOMIC_NOTIFIER_HEAD(imx_scu_irq_notifier_chain);
44
45int imx_scu_irq_register_notifier(struct notifier_block *nb)
46{
47 return atomic_notifier_chain_register(
48 &imx_scu_irq_notifier_chain, nb);
49}
50EXPORT_SYMBOL(imx_scu_irq_register_notifier);
51
52int imx_scu_irq_unregister_notifier(struct notifier_block *nb)
53{
54 return atomic_notifier_chain_unregister(
55 &imx_scu_irq_notifier_chain, nb);
56}
57EXPORT_SYMBOL(imx_scu_irq_unregister_notifier);
58
59static int imx_scu_irq_notifier_call_chain(unsigned long status, u8 *group)
60{
61 return atomic_notifier_call_chain(&imx_scu_irq_notifier_chain,
62 status, (void *)group);
63}
64
65static void imx_scu_irq_work_handler(struct work_struct *work)
66{
67 struct imx_sc_msg_irq_get_status msg;
68 struct imx_sc_rpc_msg *hdr = &msg.hdr;
69 u32 irq_status;
70 int ret;
71 u8 i;
72
73 for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) {
74 hdr->ver = IMX_SC_RPC_VERSION;
75 hdr->svc = IMX_SC_RPC_SVC_IRQ;
76 hdr->func = IMX_SC_IRQ_FUNC_STATUS;
77 hdr->size = 2;
78
79 msg.data.req.resource = mu_resource_id;
80 msg.data.req.group = i;
81
82 ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true);
83 if (ret) {
84 pr_err("get irq group %d status failed, ret %d\n",
85 i, ret);
86 return;
87 }
88
89 irq_status = msg.data.resp.status;
90 if (!irq_status)
91 continue;
92
93 imx_scu_irq_notifier_call_chain(irq_status, &i);
94 }
95}
96
97int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable)
98{
99 struct imx_sc_msg_irq_enable msg;
100 struct imx_sc_rpc_msg *hdr = &msg.hdr;
101 int ret;
102
103 hdr->ver = IMX_SC_RPC_VERSION;
104 hdr->svc = IMX_SC_RPC_SVC_IRQ;
105 hdr->func = IMX_SC_IRQ_FUNC_ENABLE;
106 hdr->size = 3;
107
108 msg.resource = mu_resource_id;
109 msg.group = group;
110 msg.mask = mask;
111 msg.enable = enable;
112
113 ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true);
114 if (ret)
115 pr_err("enable irq failed, group %d, mask %d, ret %d\n",
116 group, mask, ret);
117
118 return ret;
119}
120EXPORT_SYMBOL(imx_scu_irq_group_enable);
121
122static void imx_scu_irq_callback(struct mbox_client *c, void *msg)
123{
124 schedule_work(&imx_sc_irq_work);
125}
126
127int imx_scu_enable_general_irq_channel(struct device *dev)
128{
129 struct of_phandle_args spec;
130 struct mbox_client *cl;
131 struct mbox_chan *ch;
132 int ret = 0, i = 0;
133
134 ret = imx_scu_get_handle(&imx_sc_irq_ipc_handle);
135 if (ret)
136 return ret;
137
138 cl = devm_kzalloc(dev, sizeof(*cl), GFP_KERNEL);
139 if (!cl)
140 return -ENOMEM;
141
142 cl->dev = dev;
143 cl->rx_callback = imx_scu_irq_callback;
144
145 /* SCU general IRQ uses general interrupt channel 3 */
146 ch = mbox_request_channel_byname(cl, "gip3");
147 if (IS_ERR(ch)) {
148 ret = PTR_ERR(ch);
149 dev_err(dev, "failed to request mbox chan gip3, ret %d\n", ret);
150 devm_kfree(dev, cl);
151 return ret;
152 }
153
154 INIT_WORK(&imx_sc_irq_work, imx_scu_irq_work_handler);
155
156 if (!of_parse_phandle_with_args(dev->of_node, "mboxes",
157 "#mbox-cells", 0, &spec))
158 i = of_alias_get_id(spec.np, "mu");
159
160 /* use mu1 as general mu irq channel if failed */
161 if (i < 0)
162 i = 1;
163
164 mu_resource_id = IMX_SC_R_MU_0A + i;
165
166 return ret;
167}
168EXPORT_SYMBOL(imx_scu_enable_general_irq_channel);
diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c
index 2bb1a19c413f..04a24a863d6e 100644
--- a/drivers/firmware/imx/imx-scu.c
+++ b/drivers/firmware/imx/imx-scu.c
@@ -10,6 +10,7 @@
10#include <linux/err.h> 10#include <linux/err.h>
11#include <linux/firmware/imx/types.h> 11#include <linux/firmware/imx/types.h>
12#include <linux/firmware/imx/ipc.h> 12#include <linux/firmware/imx/ipc.h>
13#include <linux/firmware/imx/sci.h>
13#include <linux/interrupt.h> 14#include <linux/interrupt.h>
14#include <linux/irq.h> 15#include <linux/irq.h>
15#include <linux/kernel.h> 16#include <linux/kernel.h>
@@ -246,6 +247,11 @@ static int imx_scu_probe(struct platform_device *pdev)
246 247
247 imx_sc_ipc_handle = sc_ipc; 248 imx_sc_ipc_handle = sc_ipc;
248 249
250 ret = imx_scu_enable_general_irq_channel(dev);
251 if (ret)
252 dev_warn(dev,
253 "failed to enable general irq channel: %d\n", ret);
254
249 dev_info(dev, "NXP i.MX SCU Initialized\n"); 255 dev_info(dev, "NXP i.MX SCU Initialized\n");
250 256
251 return devm_of_platform_populate(dev); 257 return devm_of_platform_populate(dev);
diff --git a/include/linux/firmware/imx/sci.h b/include/linux/firmware/imx/sci.h
index ebc55098faee..17ba4e405129 100644
--- a/include/linux/firmware/imx/sci.h
+++ b/include/linux/firmware/imx/sci.h
@@ -15,4 +15,9 @@
15 15
16#include <linux/firmware/imx/svc/misc.h> 16#include <linux/firmware/imx/svc/misc.h>
17#include <linux/firmware/imx/svc/pm.h> 17#include <linux/firmware/imx/svc/pm.h>
18
19int imx_scu_enable_general_irq_channel(struct device *dev);
20int imx_scu_irq_register_notifier(struct notifier_block *nb);
21int imx_scu_irq_unregister_notifier(struct notifier_block *nb);
22int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable);
18#endif /* _SC_SCI_H */ 23#endif /* _SC_SCI_H */