Skip to content

Commit 88b4750

Browse files
committed
Merge tag 'zynqmp-soc-for-v5.6' of https://github.com/Xilinx/linux-xlnx into arm/drivers
arm64: soc: ZynqMP SoC changes for v5.6 - Extend firmware interface for feature checking - Use mailbox for communication with firmware for power management * tag 'zynqmp-soc-for-v5.6' of https://github.com/Xilinx/linux-xlnx: drivers: soc: xilinx: Use mailbox IPI callback dt-bindings: power: reset: xilinx: Add bindings for ipi mailbox drivers: firmware: xilinx: Add support for feature check Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Olof Johansson <[email protected]>
2 parents 684415d + ffdbae2 commit 88b4750

File tree

5 files changed

+201
-17
lines changed

5 files changed

+201
-17
lines changed

Documentation/devicetree/bindings/power/reset/xlnx,zynqmp-power.txt

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,41 @@ Required properties:
88
- compatible: Must contain: "xlnx,zynqmp-power"
99
- interrupts: Interrupt specifier
1010

11-
-------
12-
Example
13-
-------
11+
Optional properties:
12+
- mbox-names : Name given to channels seen in the 'mboxes' property.
13+
"tx" - Mailbox corresponding to transmit path
14+
"rx" - Mailbox corresponding to receive path
15+
- mboxes : Standard property to specify a Mailbox. Each value of
16+
the mboxes property should contain a phandle to the
17+
mailbox controller device node and an args specifier
18+
that will be the phandle to the intended sub-mailbox
19+
child node to be used for communication. See
20+
Documentation/devicetree/bindings/mailbox/mailbox.txt
21+
for more details about the generic mailbox controller
22+
and client driver bindings. Also see
23+
Documentation/devicetree/bindings/mailbox/ \
24+
xlnx,zynqmp-ipi-mailbox.txt for typical controller that
25+
is used to communicate with this System controllers.
26+
27+
--------
28+
Examples
29+
--------
30+
31+
Example with interrupt method:
32+
33+
firmware {
34+
zynqmp_firmware: zynqmp-firmware {
35+
compatible = "xlnx,zynqmp-firmware";
36+
method = "smc";
37+
38+
zynqmp_power: zynqmp-power {
39+
compatible = "xlnx,zynqmp-power";
40+
interrupts = <0 35 4>;
41+
};
42+
};
43+
};
44+
45+
Example with IPI mailbox method:
1446

1547
firmware {
1648
zynqmp_firmware: zynqmp-firmware {
@@ -19,7 +51,11 @@ firmware {
1951

2052
zynqmp_power: zynqmp-power {
2153
compatible = "xlnx,zynqmp-power";
54+
interrupt-parent = <&gic>;
2255
interrupts = <0 35 4>;
56+
mboxes = <&ipi_mailbox_pmu0 0>,
57+
<&ipi_mailbox_pmu0 1>;
58+
mbox-names = "tx", "rx";
2359
};
2460
};
2561
};

drivers/firmware/xilinx/zynqmp.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626

2727
static const struct zynqmp_eemi_ops *eemi_ops_tbl;
2828

29+
static bool feature_check_enabled;
30+
static u32 zynqmp_pm_features[PM_API_MAX];
31+
2932
static const struct mfd_cell firmware_devs[] = {
3033
{
3134
.name = "zynqmp_power_controller",
@@ -44,6 +47,8 @@ static int zynqmp_pm_ret_code(u32 ret_status)
4447
case XST_PM_SUCCESS:
4548
case XST_PM_DOUBLE_REQ:
4649
return 0;
50+
case XST_PM_NO_FEATURE:
51+
return -ENOTSUPP;
4752
case XST_PM_NO_ACCESS:
4853
return -EACCES;
4954
case XST_PM_ABORT_SUSPEND:
@@ -126,6 +131,39 @@ static noinline int do_fw_call_hvc(u64 arg0, u64 arg1, u64 arg2,
126131
return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
127132
}
128133

134+
/**
135+
* zynqmp_pm_feature() - Check weather given feature is supported or not
136+
* @api_id: API ID to check
137+
*
138+
* Return: Returns status, either success or error+reason
139+
*/
140+
static int zynqmp_pm_feature(u32 api_id)
141+
{
142+
int ret;
143+
u32 ret_payload[PAYLOAD_ARG_CNT];
144+
u64 smc_arg[2];
145+
146+
if (!feature_check_enabled)
147+
return 0;
148+
149+
/* Return value if feature is already checked */
150+
if (zynqmp_pm_features[api_id] != PM_FEATURE_UNCHECKED)
151+
return zynqmp_pm_features[api_id];
152+
153+
smc_arg[0] = PM_SIP_SVC | PM_FEATURE_CHECK;
154+
smc_arg[1] = api_id;
155+
156+
ret = do_fw_call(smc_arg[0], smc_arg[1], 0, ret_payload);
157+
if (ret) {
158+
zynqmp_pm_features[api_id] = PM_FEATURE_INVALID;
159+
return PM_FEATURE_INVALID;
160+
}
161+
162+
zynqmp_pm_features[api_id] = ret_payload[1];
163+
164+
return zynqmp_pm_features[api_id];
165+
}
166+
129167
/**
130168
* zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer
131169
* caller function depending on the configuration
@@ -160,6 +198,9 @@ int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1,
160198
*/
161199
u64 smc_arg[4];
162200

201+
if (zynqmp_pm_feature(pm_api_id) == PM_FEATURE_INVALID)
202+
return -ENOTSUPP;
203+
163204
smc_arg[0] = PM_SIP_SVC | pm_api_id;
164205
smc_arg[1] = ((u64)arg1 << 32) | arg0;
165206
smc_arg[2] = ((u64)arg3 << 32) | arg2;
@@ -715,6 +756,8 @@ static int zynqmp_firmware_probe(struct platform_device *pdev)
715756
np = of_find_compatible_node(NULL, NULL, "xlnx,versal");
716757
if (!np)
717758
return 0;
759+
760+
feature_check_enabled = true;
718761
}
719762
of_node_put(np);
720763

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

include/linux/firmware/xlnx-zynqmp.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
#define ZYNQMP_PM_CAPABILITY_WAKEUP 0x4U
4949
#define ZYNQMP_PM_CAPABILITY_UNUSABLE 0x8U
5050

51+
/* Feature check status */
52+
#define PM_FEATURE_INVALID -1
53+
#define PM_FEATURE_UNCHECKED 0
54+
5155
/*
5256
* Firmware FPGA Manager flags
5357
* XILINX_ZYNQMP_PM_FPGA_FULL: FPGA full reconfiguration
@@ -78,11 +82,14 @@ enum pm_api_id {
7882
PM_CLOCK_GETRATE,
7983
PM_CLOCK_SETPARENT,
8084
PM_CLOCK_GETPARENT,
85+
PM_FEATURE_CHECK = 63,
86+
PM_API_MAX,
8187
};
8288

8389
/* PMU-FW return status codes */
8490
enum pm_ret_status {
8591
XST_PM_SUCCESS = 0,
92+
XST_PM_NO_FEATURE = 19,
8693
XST_PM_INTERNAL = 2000,
8794
XST_PM_CONFLICT,
8895
XST_PM_NO_ACCESS,

0 commit comments

Comments
 (0)