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

driver: soc: xilinx: register for power events in zynqmp power driver

With Xilinx Event Management driver, all types of events like power and
error gets handled from single place as part of event management driver.

So power events(SUSPEND_POWER_REQUEST and SUSPEND_SYSTEM_SHUTDOWN)
also gets handled by event management driver instead of zynqmp_power
driver.

zynqmp-power driver use event management driver and provide callback
function for Suspend and shutdown handler, which will be called by event
management driver when respective event is arrived.

If event management driver is not available than use ipi-mailbox rx channel
or IPI interrupt IRQ handler for power events (suspend/shutdown) same as
current zynqmp-power driver.

Acked-by: Michal Simek <michal.simek@xilinx.com>
Signed-off-by: Rajan Vaja <rajan.vaja@xilinx.com>
Signed-off-by: Abhyuday Godhasara <abhyuday.godhasara@xilinx.com>
Link: https://lore.kernel.org/r/20211129070216.30253-4-abhyuday.godhasara@xilinx.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Abhyuday Godhasara and committed by
Greg Kroah-Hartman
70602b37 a515814e

+47 -1
+47 -1
drivers/soc/xilinx/zynqmp_power.c
··· 16 16 #include <linux/suspend.h> 17 17 18 18 #include <linux/firmware/xlnx-zynqmp.h> 19 + #include <linux/firmware/xlnx-event-manager.h> 19 20 #include <linux/mailbox/zynqmp-ipi-message.h> 20 21 21 22 /** ··· 31 30 32 31 static struct zynqmp_pm_work_struct *zynqmp_pm_init_suspend_work; 33 32 static struct mbox_chan *rx_chan; 33 + static bool event_registered; 34 34 35 35 enum pm_suspend_mode { 36 36 PM_SUSPEND_MODE_FIRST = 0, ··· 51 49 static void zynqmp_pm_get_callback_data(u32 *buf) 52 50 { 53 51 zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, 0, 0, 0, 0, buf); 52 + } 53 + 54 + static void suspend_event_callback(const u32 *payload, void *data) 55 + { 56 + /* First element is callback API ID, others are callback arguments */ 57 + if (work_pending(&zynqmp_pm_init_suspend_work->callback_work)) 58 + return; 59 + 60 + /* Copy callback arguments into work's structure */ 61 + memcpy(zynqmp_pm_init_suspend_work->args, &payload[1], 62 + sizeof(zynqmp_pm_init_suspend_work->args)); 63 + 64 + queue_work(system_unbound_wq, &zynqmp_pm_init_suspend_work->callback_work); 54 65 } 55 66 56 67 static irqreturn_t zynqmp_pm_isr(int irq, void *data) ··· 194 179 if (pm_api_version < ZYNQMP_PM_VERSION) 195 180 return -ENODEV; 196 181 197 - if (of_find_property(pdev->dev.of_node, "mboxes", NULL)) { 182 + /* 183 + * First try to use Xilinx Event Manager by registering suspend_event_callback 184 + * for suspend/shutdown event. 185 + * If xlnx_register_event() returns -EACCES (Xilinx Event Manager 186 + * is not available to use) or -ENODEV(Xilinx Event Manager not compiled), 187 + * then use ipi-mailbox or interrupt method. 188 + */ 189 + ret = xlnx_register_event(PM_INIT_SUSPEND_CB, 0, 0, false, 190 + suspend_event_callback, NULL); 191 + if (!ret) { 192 + zynqmp_pm_init_suspend_work = devm_kzalloc(&pdev->dev, 193 + sizeof(struct zynqmp_pm_work_struct), 194 + GFP_KERNEL); 195 + if (!zynqmp_pm_init_suspend_work) { 196 + xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, 197 + suspend_event_callback); 198 + return -ENOMEM; 199 + } 200 + event_registered = true; 201 + 202 + INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work, 203 + zynqmp_pm_init_suspend_work_fn); 204 + } else if (ret != -EACCES && ret != -ENODEV) { 205 + dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n", ret); 206 + return ret; 207 + } else if (of_find_property(pdev->dev.of_node, "mboxes", NULL)) { 198 208 zynqmp_pm_init_suspend_work = 199 209 devm_kzalloc(&pdev->dev, 200 210 sizeof(struct zynqmp_pm_work_struct), ··· 263 223 264 224 ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); 265 225 if (ret) { 226 + if (event_registered) { 227 + xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback); 228 + event_registered = false; 229 + } 266 230 dev_err(&pdev->dev, "unable to create sysfs interface\n"); 267 231 return ret; 268 232 } ··· 277 233 static int zynqmp_pm_remove(struct platform_device *pdev) 278 234 { 279 235 sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); 236 + if (event_registered) 237 + xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback); 280 238 281 239 if (!rx_chan) 282 240 mbox_free_channel(rx_chan);