Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <jay.buddhabhatti@amd.com>
Link: https://lore.kernel.org/r/20240424124900.29287-1-jay.buddhabhatti@amd.com
Signed-off-by: Michal Simek <michal.simek@amd.com>

authored by

Jay Buddhabhatti and committed by
Michal Simek
fcf544ac 4a95449d

+141 -20
+131 -20
drivers/soc/xilinx/zynqmp_power.c
··· 30 30 u32 args[CB_ARG_CNT]; 31 31 }; 32 32 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; 34 53 static struct mbox_chan *rx_chan; 35 - static bool event_registered; 36 54 37 55 enum pm_suspend_mode { 38 56 PM_SUSPEND_MODE_FIRST = 0, ··· 70 52 static void zynqmp_pm_get_callback_data(u32 *buf) 71 53 { 72 54 zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, buf, 0); 55 + } 56 + 57 + static void subsystem_restart_event_callback(const u32 *payload, void *data) 58 + { 59 + /* First element is callback API ID, others are callback arguments */ 60 + if (work_pending(&zynqmp_pm_init_restart_work->callback_work)) 61 + return; 62 + 63 + /* Copy callback arguments into work's structure */ 64 + memcpy(zynqmp_pm_init_restart_work->args, &payload[0], 65 + sizeof(zynqmp_pm_init_restart_work->args)); 66 + 67 + queue_work(system_unbound_wq, &zynqmp_pm_init_restart_work->callback_work); 73 68 } 74 69 75 70 static void suspend_event_callback(const u32 *payload, void *data) ··· 151 120 } 152 121 153 122 /** 123 + * zynqmp_pm_subsystem_restart_work_fn - Initiate Subsystem restart 124 + * @work: Pointer to work_struct 125 + * 126 + * Bottom-half of PM callback IRQ handler. 127 + */ 128 + static void zynqmp_pm_subsystem_restart_work_fn(struct work_struct *work) 129 + { 130 + int ret; 131 + struct zynqmp_pm_work_struct *pm_work = container_of(work, struct zynqmp_pm_work_struct, 132 + callback_work); 133 + 134 + /* First element is callback API ID, others are callback arguments */ 135 + if (pm_work->args[0] == PM_NOTIFY_CB) { 136 + if (pm_work->args[2] == EVENT_SUBSYSTEM_RESTART) { 137 + ret = zynqmp_pm_system_shutdown(ZYNQMP_PM_SHUTDOWN_TYPE_SETSCOPE_ONLY, 138 + ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM); 139 + if (ret) { 140 + pr_err("unable to set shutdown scope\n"); 141 + return; 142 + } 143 + 144 + kernel_restart(NULL); 145 + } else { 146 + pr_err("%s Unsupported Event - %d\n", __func__, pm_work->args[2]); 147 + } 148 + } else { 149 + pr_err("%s() Unsupported Callback %d\n", __func__, pm_work->args[0]); 150 + } 151 + } 152 + 153 + /** 154 154 * zynqmp_pm_init_suspend_work_fn - Initialize suspend 155 155 * @work: Pointer to work_struct 156 156 * ··· 246 184 247 185 static DEVICE_ATTR_RW(suspend_mode); 248 186 187 + static void unregister_event(struct device *dev, void *res) 188 + { 189 + struct zynqmp_pm_event_info *event_info = res; 190 + 191 + xlnx_unregister_event(event_info->cb_type, event_info->node_id, 192 + event_info->event, event_info->cb_fun, NULL); 193 + } 194 + 195 + static int register_event(struct device *dev, const enum pm_api_cb_id cb_type, const u32 node_id, 196 + const u32 event, const bool wake, event_cb_func_t cb_fun) 197 + { 198 + int ret; 199 + struct zynqmp_pm_event_info *event_info; 200 + 201 + event_info = devres_alloc(unregister_event, sizeof(struct zynqmp_pm_event_info), 202 + GFP_KERNEL); 203 + if (!event_info) 204 + return -ENOMEM; 205 + 206 + event_info->cb_type = cb_type; 207 + event_info->node_id = node_id; 208 + event_info->event = event; 209 + event_info->wake = wake; 210 + event_info->cb_fun = cb_fun; 211 + 212 + ret = xlnx_register_event(event_info->cb_type, event_info->node_id, 213 + event_info->event, event_info->wake, event_info->cb_fun, NULL); 214 + if (ret) { 215 + devres_free(event_info); 216 + return ret; 217 + } 218 + 219 + devres_add(dev, event_info); 220 + return 0; 221 + } 222 + 249 223 static int zynqmp_pm_probe(struct platform_device *pdev) 250 224 { 251 225 int ret, irq; 252 - u32 pm_api_version; 226 + u32 pm_api_version, pm_family_code, pm_sub_family_code, node_id; 253 227 struct mbox_client *client; 254 228 255 229 zynqmp_pm_get_api_version(&pm_api_version); ··· 301 203 * is not available to use) or -ENODEV(Xilinx Event Manager not compiled), 302 204 * then use ipi-mailbox or interrupt method. 303 205 */ 304 - ret = xlnx_register_event(PM_INIT_SUSPEND_CB, 0, 0, false, 305 - suspend_event_callback, NULL); 206 + ret = register_event(&pdev->dev, PM_INIT_SUSPEND_CB, 0, 0, false, 207 + suspend_event_callback); 306 208 if (!ret) { 307 209 zynqmp_pm_init_suspend_work = devm_kzalloc(&pdev->dev, 308 210 sizeof(struct zynqmp_pm_work_struct), 309 211 GFP_KERNEL); 310 - if (!zynqmp_pm_init_suspend_work) { 311 - xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, 312 - suspend_event_callback, NULL); 212 + if (!zynqmp_pm_init_suspend_work) 313 213 return -ENOMEM; 314 - } 315 - event_registered = true; 316 214 317 215 INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work, 318 216 zynqmp_pm_init_suspend_work_fn); 217 + 218 + ret = zynqmp_pm_get_family_info(&pm_family_code, &pm_sub_family_code); 219 + if (ret < 0) 220 + return ret; 221 + 222 + if (pm_sub_family_code == VERSALNET_SUB_FAMILY_CODE) 223 + node_id = PM_DEV_ACPU_0_0; 224 + else 225 + node_id = PM_DEV_ACPU_0; 226 + 227 + ret = register_event(&pdev->dev, PM_NOTIFY_CB, node_id, EVENT_SUBSYSTEM_RESTART, 228 + false, subsystem_restart_event_callback); 229 + if (ret) { 230 + dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n", 231 + ret); 232 + return ret; 233 + } 234 + 235 + zynqmp_pm_init_restart_work = devm_kzalloc(&pdev->dev, 236 + sizeof(struct zynqmp_pm_work_struct), 237 + GFP_KERNEL); 238 + if (!zynqmp_pm_init_restart_work) 239 + return -ENOMEM; 240 + 241 + INIT_WORK(&zynqmp_pm_init_restart_work->callback_work, 242 + zynqmp_pm_subsystem_restart_work_fn); 319 243 } else if (ret != -EACCES && ret != -ENODEV) { 320 244 dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n", ret); 321 245 return ret; ··· 384 264 } 385 265 386 266 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); 387 - if (ret) { 388 - if (event_registered) { 389 - xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback, 390 - NULL); 391 - event_registered = false; 392 - } 393 - dev_err(&pdev->dev, "unable to create sysfs interface\n"); 267 + if (ret) 394 268 return ret; 395 - } 396 269 397 270 return 0; 398 271 } ··· 393 280 static void zynqmp_pm_remove(struct platform_device *pdev) 394 281 { 395 282 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); 396 - if (event_registered) 397 - xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback, NULL); 398 283 399 284 if (!rx_chan) 400 285 mbox_free_channel(rx_chan);
+10
include/linux/firmware/xlnx-event-manager.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Xilinx Event Management Driver 4 + * 5 + * Copyright (C) 2024, Advanced Micro Devices, Inc. 6 + */ 2 7 3 8 #ifndef _FIRMWARE_XLNX_EVENT_MANAGER_H_ 4 9 #define _FIRMWARE_XLNX_EVENT_MANAGER_H_ ··· 11 6 #include <linux/firmware/xlnx-zynqmp.h> 12 7 13 8 #define CB_MAX_PAYLOAD_SIZE (4U) /*In payload maximum 32bytes */ 9 + 10 + #define EVENT_SUBSYSTEM_RESTART (4U) 11 + 12 + #define PM_DEV_ACPU_0_0 (0x1810c0afU) 13 + #define PM_DEV_ACPU_0 (0x1810c003U) 14 14 15 15 /************************** Exported Function *****************************/ 16 16