Skip to content

Commit fcf544a

Browse files
jbuddhabmichalsimek
authored andcommitted
soc: xilinx: Add cb event for subsystem restart
Add support to register subsystem restart events from firmware for Versal and Versal NET platforms. This event is received when firmware requests for subsystem restart. After receiving this event, the kernel needs to be restarted. Signed-off-by: Jay Buddhabhatti <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Michal Simek <[email protected]>
1 parent 4a95449 commit fcf544a

File tree

2 files changed

+141
-20
lines changed

2 files changed

+141
-20
lines changed

drivers/soc/xilinx/zynqmp_power.c

Lines changed: 131 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,27 @@ struct zynqmp_pm_work_struct {
3030
u32 args[CB_ARG_CNT];
3131
};
3232

33-
static struct zynqmp_pm_work_struct *zynqmp_pm_init_suspend_work;
33+
/**
34+
* struct zynqmp_pm_event_info - event related information
35+
* @cb_fun: Function pointer to store the callback function.
36+
* @cb_type: Type of callback from pm_api_cb_id,
37+
* PM_NOTIFY_CB - for Error Events,
38+
* PM_INIT_SUSPEND_CB - for suspend callback.
39+
* @node_id: Node-Id related to event.
40+
* @event: Event Mask for the Error Event.
41+
* @wake: Flag specifying whether the subsystem should be woken upon
42+
* event notification.
43+
*/
44+
struct zynqmp_pm_event_info {
45+
event_cb_func_t cb_fun;
46+
enum pm_api_cb_id cb_type;
47+
u32 node_id;
48+
u32 event;
49+
bool wake;
50+
};
51+
52+
static struct zynqmp_pm_work_struct *zynqmp_pm_init_suspend_work, *zynqmp_pm_init_restart_work;
3453
static struct mbox_chan *rx_chan;
35-
static bool event_registered;
3654

3755
enum pm_suspend_mode {
3856
PM_SUSPEND_MODE_FIRST = 0,
@@ -54,6 +72,19 @@ static void zynqmp_pm_get_callback_data(u32 *buf)
5472
zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, buf, 0);
5573
}
5674

75+
static void subsystem_restart_event_callback(const u32 *payload, void *data)
76+
{
77+
/* First element is callback API ID, others are callback arguments */
78+
if (work_pending(&zynqmp_pm_init_restart_work->callback_work))
79+
return;
80+
81+
/* Copy callback arguments into work's structure */
82+
memcpy(zynqmp_pm_init_restart_work->args, &payload[0],
83+
sizeof(zynqmp_pm_init_restart_work->args));
84+
85+
queue_work(system_unbound_wq, &zynqmp_pm_init_restart_work->callback_work);
86+
}
87+
5788
static void suspend_event_callback(const u32 *payload, void *data)
5889
{
5990
/* First element is callback API ID, others are callback arguments */
@@ -119,6 +150,37 @@ static void ipi_receive_callback(struct mbox_client *cl, void *data)
119150
}
120151
}
121152

153+
/**
154+
* zynqmp_pm_subsystem_restart_work_fn - Initiate Subsystem restart
155+
* @work: Pointer to work_struct
156+
*
157+
* Bottom-half of PM callback IRQ handler.
158+
*/
159+
static void zynqmp_pm_subsystem_restart_work_fn(struct work_struct *work)
160+
{
161+
int ret;
162+
struct zynqmp_pm_work_struct *pm_work = container_of(work, struct zynqmp_pm_work_struct,
163+
callback_work);
164+
165+
/* First element is callback API ID, others are callback arguments */
166+
if (pm_work->args[0] == PM_NOTIFY_CB) {
167+
if (pm_work->args[2] == EVENT_SUBSYSTEM_RESTART) {
168+
ret = zynqmp_pm_system_shutdown(ZYNQMP_PM_SHUTDOWN_TYPE_SETSCOPE_ONLY,
169+
ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM);
170+
if (ret) {
171+
pr_err("unable to set shutdown scope\n");
172+
return;
173+
}
174+
175+
kernel_restart(NULL);
176+
} else {
177+
pr_err("%s Unsupported Event - %d\n", __func__, pm_work->args[2]);
178+
}
179+
} else {
180+
pr_err("%s() Unsupported Callback %d\n", __func__, pm_work->args[0]);
181+
}
182+
}
183+
122184
/**
123185
* zynqmp_pm_init_suspend_work_fn - Initialize suspend
124186
* @work: Pointer to work_struct
@@ -184,10 +246,46 @@ static ssize_t suspend_mode_store(struct device *dev,
184246

185247
static DEVICE_ATTR_RW(suspend_mode);
186248

249+
static void unregister_event(struct device *dev, void *res)
250+
{
251+
struct zynqmp_pm_event_info *event_info = res;
252+
253+
xlnx_unregister_event(event_info->cb_type, event_info->node_id,
254+
event_info->event, event_info->cb_fun, NULL);
255+
}
256+
257+
static int register_event(struct device *dev, const enum pm_api_cb_id cb_type, const u32 node_id,
258+
const u32 event, const bool wake, event_cb_func_t cb_fun)
259+
{
260+
int ret;
261+
struct zynqmp_pm_event_info *event_info;
262+
263+
event_info = devres_alloc(unregister_event, sizeof(struct zynqmp_pm_event_info),
264+
GFP_KERNEL);
265+
if (!event_info)
266+
return -ENOMEM;
267+
268+
event_info->cb_type = cb_type;
269+
event_info->node_id = node_id;
270+
event_info->event = event;
271+
event_info->wake = wake;
272+
event_info->cb_fun = cb_fun;
273+
274+
ret = xlnx_register_event(event_info->cb_type, event_info->node_id,
275+
event_info->event, event_info->wake, event_info->cb_fun, NULL);
276+
if (ret) {
277+
devres_free(event_info);
278+
return ret;
279+
}
280+
281+
devres_add(dev, event_info);
282+
return 0;
283+
}
284+
187285
static int zynqmp_pm_probe(struct platform_device *pdev)
188286
{
189287
int ret, irq;
190-
u32 pm_api_version;
288+
u32 pm_api_version, pm_family_code, pm_sub_family_code, node_id;
191289
struct mbox_client *client;
192290

193291
zynqmp_pm_get_api_version(&pm_api_version);
@@ -203,21 +301,43 @@ static int zynqmp_pm_probe(struct platform_device *pdev)
203301
* is not available to use) or -ENODEV(Xilinx Event Manager not compiled),
204302
* then use ipi-mailbox or interrupt method.
205303
*/
206-
ret = xlnx_register_event(PM_INIT_SUSPEND_CB, 0, 0, false,
207-
suspend_event_callback, NULL);
304+
ret = register_event(&pdev->dev, PM_INIT_SUSPEND_CB, 0, 0, false,
305+
suspend_event_callback);
208306
if (!ret) {
209307
zynqmp_pm_init_suspend_work = devm_kzalloc(&pdev->dev,
210308
sizeof(struct zynqmp_pm_work_struct),
211309
GFP_KERNEL);
212-
if (!zynqmp_pm_init_suspend_work) {
213-
xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0,
214-
suspend_event_callback, NULL);
310+
if (!zynqmp_pm_init_suspend_work)
215311
return -ENOMEM;
216-
}
217-
event_registered = true;
218312

219313
INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work,
220314
zynqmp_pm_init_suspend_work_fn);
315+
316+
ret = zynqmp_pm_get_family_info(&pm_family_code, &pm_sub_family_code);
317+
if (ret < 0)
318+
return ret;
319+
320+
if (pm_sub_family_code == VERSALNET_SUB_FAMILY_CODE)
321+
node_id = PM_DEV_ACPU_0_0;
322+
else
323+
node_id = PM_DEV_ACPU_0;
324+
325+
ret = register_event(&pdev->dev, PM_NOTIFY_CB, node_id, EVENT_SUBSYSTEM_RESTART,
326+
false, subsystem_restart_event_callback);
327+
if (ret) {
328+
dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n",
329+
ret);
330+
return ret;
331+
}
332+
333+
zynqmp_pm_init_restart_work = devm_kzalloc(&pdev->dev,
334+
sizeof(struct zynqmp_pm_work_struct),
335+
GFP_KERNEL);
336+
if (!zynqmp_pm_init_restart_work)
337+
return -ENOMEM;
338+
339+
INIT_WORK(&zynqmp_pm_init_restart_work->callback_work,
340+
zynqmp_pm_subsystem_restart_work_fn);
221341
} else if (ret != -EACCES && ret != -ENODEV) {
222342
dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n", ret);
223343
return ret;
@@ -264,24 +384,15 @@ static int zynqmp_pm_probe(struct platform_device *pdev)
264384
}
265385

266386
ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr);
267-
if (ret) {
268-
if (event_registered) {
269-
xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback,
270-
NULL);
271-
event_registered = false;
272-
}
273-
dev_err(&pdev->dev, "unable to create sysfs interface\n");
387+
if (ret)
274388
return ret;
275-
}
276389

277390
return 0;
278391
}
279392

280393
static void zynqmp_pm_remove(struct platform_device *pdev)
281394
{
282395
sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr);
283-
if (event_registered)
284-
xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback, NULL);
285396

286397
if (!rx_chan)
287398
mbox_free_channel(rx_chan);

include/linux/firmware/xlnx-event-manager.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Xilinx Event Management Driver
4+
*
5+
* Copyright (C) 2024, Advanced Micro Devices, Inc.
6+
*/
27

38
#ifndef _FIRMWARE_XLNX_EVENT_MANAGER_H_
49
#define _FIRMWARE_XLNX_EVENT_MANAGER_H_
@@ -7,6 +12,11 @@
712

813
#define CB_MAX_PAYLOAD_SIZE (4U) /*In payload maximum 32bytes */
914

15+
#define EVENT_SUBSYSTEM_RESTART (4U)
16+
17+
#define PM_DEV_ACPU_0_0 (0x1810c0afU)
18+
#define PM_DEV_ACPU_0 (0x1810c003U)
19+
1020
/************************** Exported Function *****************************/
1121

1222
typedef void (*event_cb_func_t)(const u32 *payload, void *data);

0 commit comments

Comments
 (0)