Skip to content

Commit

Permalink
driver: soc: xilinx: register for power events in zynqmp power driver
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Abhyuday Godhasara authored and Greg Kroah-Hartman committed Dec 3, 2021
1 parent a515814 commit 70602b3
Showing 1 changed file with 47 additions and 1 deletion.
48 changes: 47 additions & 1 deletion drivers/soc/xilinx/zynqmp_power.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <linux/suspend.h>

#include <linux/firmware/xlnx-zynqmp.h>
#include <linux/firmware/xlnx-event-manager.h>
#include <linux/mailbox/zynqmp-ipi-message.h>

/**
Expand All @@ -30,6 +31,7 @@ struct zynqmp_pm_work_struct {

static struct zynqmp_pm_work_struct *zynqmp_pm_init_suspend_work;
static struct mbox_chan *rx_chan;
static bool event_registered;

enum pm_suspend_mode {
PM_SUSPEND_MODE_FIRST = 0,
Expand All @@ -51,6 +53,19 @@ static void zynqmp_pm_get_callback_data(u32 *buf)
zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, 0, 0, 0, 0, buf);
}

static void suspend_event_callback(const u32 *payload, void *data)
{
/* First element is callback API ID, others are callback arguments */
if (work_pending(&zynqmp_pm_init_suspend_work->callback_work))
return;

/* Copy callback arguments into work's structure */
memcpy(zynqmp_pm_init_suspend_work->args, &payload[1],
sizeof(zynqmp_pm_init_suspend_work->args));

queue_work(system_unbound_wq, &zynqmp_pm_init_suspend_work->callback_work);
}

static irqreturn_t zynqmp_pm_isr(int irq, void *data)
{
u32 payload[CB_PAYLOAD_SIZE];
Expand Down Expand Up @@ -179,7 +194,32 @@ static int zynqmp_pm_probe(struct platform_device *pdev)
if (pm_api_version < ZYNQMP_PM_VERSION)
return -ENODEV;

if (of_find_property(pdev->dev.of_node, "mboxes", NULL)) {
/*
* First try to use Xilinx Event Manager by registering suspend_event_callback
* for suspend/shutdown event.
* If xlnx_register_event() returns -EACCES (Xilinx Event Manager
* is not available to use) or -ENODEV(Xilinx Event Manager not compiled),
* then use ipi-mailbox or interrupt method.
*/
ret = xlnx_register_event(PM_INIT_SUSPEND_CB, 0, 0, false,
suspend_event_callback, NULL);
if (!ret) {
zynqmp_pm_init_suspend_work = devm_kzalloc(&pdev->dev,
sizeof(struct zynqmp_pm_work_struct),
GFP_KERNEL);
if (!zynqmp_pm_init_suspend_work) {
xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0,
suspend_event_callback);
return -ENOMEM;
}
event_registered = true;

INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work,
zynqmp_pm_init_suspend_work_fn);
} else if (ret != -EACCES && ret != -ENODEV) {
dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n", ret);
return ret;
} else if (of_find_property(pdev->dev.of_node, "mboxes", NULL)) {
zynqmp_pm_init_suspend_work =
devm_kzalloc(&pdev->dev,
sizeof(struct zynqmp_pm_work_struct),
Expand Down Expand Up @@ -223,6 +263,10 @@ static int zynqmp_pm_probe(struct platform_device *pdev)

ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr);
if (ret) {
if (event_registered) {
xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback);
event_registered = false;
}
dev_err(&pdev->dev, "unable to create sysfs interface\n");
return ret;
}
Expand All @@ -233,6 +277,8 @@ static int zynqmp_pm_probe(struct platform_device *pdev)
static int zynqmp_pm_remove(struct platform_device *pdev)
{
sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr);
if (event_registered)
xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback);

if (!rx_chan)
mbox_free_channel(rx_chan);
Expand Down

0 comments on commit 70602b3

Please sign in to comment.