Skip to content

Commit ffdbae2

Browse files
Tejas PatelMichal Simek
authored andcommitted
drivers: soc: xilinx: Use mailbox IPI callback
Add support for init suspend callback through mailbox IPI callback. Signed-off-by: Tejas Patel <[email protected]> Signed-off-by: Rajan Vaja <[email protected]> Signed-off-by: Michal Simek <[email protected]>
1 parent a117daa commit ffdbae2

File tree

2 files changed

+112
-14
lines changed

2 files changed

+112
-14
lines changed

drivers/soc/xilinx/Kconfig

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@ config ZYNQMP_POWER
2121
bool "Enable Xilinx Zynq MPSoC Power Management driver"
2222
depends on PM && ARCH_ZYNQMP
2323
default y
24+
select MAILBOX
25+
select ZYNQMP_IPI_MBOX
2426
help
2527
Say yes to enable power management support for ZyqnMP SoC.
2628
This driver uses firmware driver as an interface for power
2729
management request to firmware. It registers isr to handle
28-
power management callbacks from firmware.
30+
power management callbacks from firmware. It registers mailbox client
31+
to handle power management callbacks from firmware.
32+
2933
If in doubt, say N.
3034

3135
config ZYNQMP_PM_DOMAINS

drivers/soc/xilinx/zynqmp_power.c

Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/*
33
* Xilinx Zynq MPSoC Power Management
44
*
5-
* Copyright (C) 2014-2018 Xilinx, Inc.
5+
* Copyright (C) 2014-2019 Xilinx, Inc.
66
*
77
* Davorin Mista <[email protected]>
88
* Jolly Shah <[email protected]>
@@ -16,6 +16,21 @@
1616
#include <linux/suspend.h>
1717

1818
#include <linux/firmware/xlnx-zynqmp.h>
19+
#include <linux/mailbox/zynqmp-ipi-message.h>
20+
21+
/**
22+
* struct zynqmp_pm_work_struct - Wrapper for struct work_struct
23+
* @callback_work: Work structure
24+
* @args: Callback arguments
25+
*/
26+
struct zynqmp_pm_work_struct {
27+
struct work_struct callback_work;
28+
u32 args[CB_ARG_CNT];
29+
};
30+
31+
static struct zynqmp_pm_work_struct *zynqmp_pm_init_suspend_work;
32+
static struct mbox_chan *rx_chan;
33+
static const struct zynqmp_eemi_ops *eemi_ops;
1934

2035
enum pm_suspend_mode {
2136
PM_SUSPEND_MODE_FIRST = 0,
@@ -31,7 +46,6 @@ static const char *const suspend_modes[] = {
3146
};
3247

3348
static enum pm_suspend_mode suspend_mode = PM_SUSPEND_MODE_STD;
34-
static const struct zynqmp_eemi_ops *eemi_ops;
3549

3650
enum pm_api_cb_id {
3751
PM_INIT_SUSPEND_CB = 30,
@@ -68,6 +82,53 @@ static irqreturn_t zynqmp_pm_isr(int irq, void *data)
6882
return IRQ_HANDLED;
6983
}
7084

85+
static void ipi_receive_callback(struct mbox_client *cl, void *data)
86+
{
87+
struct zynqmp_ipi_message *msg = (struct zynqmp_ipi_message *)data;
88+
u32 payload[CB_PAYLOAD_SIZE];
89+
int ret;
90+
91+
memcpy(payload, msg->data, sizeof(msg->len));
92+
/* First element is callback API ID, others are callback arguments */
93+
if (payload[0] == PM_INIT_SUSPEND_CB) {
94+
if (work_pending(&zynqmp_pm_init_suspend_work->callback_work))
95+
return;
96+
97+
/* Copy callback arguments into work's structure */
98+
memcpy(zynqmp_pm_init_suspend_work->args, &payload[1],
99+
sizeof(zynqmp_pm_init_suspend_work->args));
100+
101+
queue_work(system_unbound_wq,
102+
&zynqmp_pm_init_suspend_work->callback_work);
103+
104+
/* Send NULL message to mbox controller to ack the message */
105+
ret = mbox_send_message(rx_chan, NULL);
106+
if (ret)
107+
pr_err("IPI ack failed. Error %d\n", ret);
108+
}
109+
}
110+
111+
/**
112+
* zynqmp_pm_init_suspend_work_fn - Initialize suspend
113+
* @work: Pointer to work_struct
114+
*
115+
* Bottom-half of PM callback IRQ handler.
116+
*/
117+
static void zynqmp_pm_init_suspend_work_fn(struct work_struct *work)
118+
{
119+
struct zynqmp_pm_work_struct *pm_work =
120+
container_of(work, struct zynqmp_pm_work_struct, callback_work);
121+
122+
if (pm_work->args[0] == SUSPEND_SYSTEM_SHUTDOWN) {
123+
orderly_poweroff(true);
124+
} else if (pm_work->args[0] == SUSPEND_POWER_REQUEST) {
125+
pm_suspend(PM_SUSPEND_MEM);
126+
} else {
127+
pr_err("%s Unsupported InitSuspendCb reason code %d.\n",
128+
__func__, pm_work->args[0]);
129+
}
130+
}
131+
71132
static ssize_t suspend_mode_show(struct device *dev,
72133
struct device_attribute *attr, char *buf)
73134
{
@@ -119,6 +180,7 @@ static int zynqmp_pm_probe(struct platform_device *pdev)
119180
{
120181
int ret, irq;
121182
u32 pm_api_version;
183+
struct mbox_client *client;
122184

123185
eemi_ops = zynqmp_pm_get_eemi_ops();
124186
if (IS_ERR(eemi_ops))
@@ -134,17 +196,46 @@ static int zynqmp_pm_probe(struct platform_device *pdev)
134196
if (pm_api_version < ZYNQMP_PM_VERSION)
135197
return -ENODEV;
136198

137-
irq = platform_get_irq(pdev, 0);
138-
if (irq <= 0)
139-
return -ENXIO;
140-
141-
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, zynqmp_pm_isr,
142-
IRQF_NO_SUSPEND | IRQF_ONESHOT,
143-
dev_name(&pdev->dev), &pdev->dev);
144-
if (ret) {
145-
dev_err(&pdev->dev, "devm_request_threaded_irq '%d' failed "
146-
"with %d\n", irq, ret);
147-
return ret;
199+
if (of_find_property(pdev->dev.of_node, "mboxes", NULL)) {
200+
zynqmp_pm_init_suspend_work =
201+
devm_kzalloc(&pdev->dev,
202+
sizeof(struct zynqmp_pm_work_struct),
203+
GFP_KERNEL);
204+
if (!zynqmp_pm_init_suspend_work)
205+
return -ENOMEM;
206+
207+
INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work,
208+
zynqmp_pm_init_suspend_work_fn);
209+
client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL);
210+
if (!client)
211+
return -ENOMEM;
212+
213+
client->dev = &pdev->dev;
214+
client->rx_callback = ipi_receive_callback;
215+
216+
rx_chan = mbox_request_channel_byname(client, "rx");
217+
if (IS_ERR(rx_chan)) {
218+
dev_err(&pdev->dev, "Failed to request rx channel\n");
219+
return IS_ERR(rx_chan);
220+
}
221+
} else if (of_find_property(pdev->dev.of_node, "interrupts", NULL)) {
222+
irq = platform_get_irq(pdev, 0);
223+
if (irq <= 0)
224+
return -ENXIO;
225+
226+
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
227+
zynqmp_pm_isr,
228+
IRQF_NO_SUSPEND | IRQF_ONESHOT,
229+
dev_name(&pdev->dev),
230+
&pdev->dev);
231+
if (ret) {
232+
dev_err(&pdev->dev, "devm_request_threaded_irq '%d' "
233+
"failed with %d\n", irq, ret);
234+
return ret;
235+
}
236+
} else {
237+
dev_err(&pdev->dev, "Required property not found in DT node\n");
238+
return -ENOENT;
148239
}
149240

150241
ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr);
@@ -160,6 +251,9 @@ static int zynqmp_pm_remove(struct platform_device *pdev)
160251
{
161252
sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr);
162253

254+
if (!rx_chan)
255+
mbox_free_channel(rx_chan);
256+
163257
return 0;
164258
}
165259

0 commit comments

Comments
 (0)