Hi Sudeep, > -----Original Message----- > From: Sudeep Holla <sudeep.holla@xxxxxxx> > Sent: Wednesday, January 30, 2019 2:13 AM > To: Jolly Shah <JOLLYS@xxxxxxxxxx> > Cc: matthias.bgg@xxxxxxxxx; andy.gross@xxxxxxxxxx; shawnguo@xxxxxxxxxx; > geert+renesas@xxxxxxxxx; bjorn.andersson@xxxxxxxxxx; > sean.wang@xxxxxxxxxxxx; m.szyprowski@xxxxxxxxxxx; Michal Simek > <michals@xxxxxxxxxx>; robh+dt@xxxxxxxxxx; mark.rutland@xxxxxxx; Rajan > Vaja <RAJANV@xxxxxxxxxx>; devicetree@xxxxxxxxxxxxxxx; linux-arm- > kernel@xxxxxxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx; Sudeep Holla > <sudeep.holla@xxxxxxx>; Rajan Vaja <RAJANV@xxxxxxxxxx>; Jolly Shah > <JOLLYS@xxxxxxxxxx> > Subject: Re: [PATCH v6 3/3] drivers: soc: xilinx: Add ZynqMP PM driver > > On Tue, Jan 29, 2019 at 12:38:21PM -0800, Jolly Shah wrote: > > From: Rajan Vaja <rajan.vaja@xxxxxxxxxx> > > > > Add ZynqMP PM driver. PM driver provides power management > > support for ZynqMP. > > > > Signed-off-by: Rajan Vaja <rajan.vaja@xxxxxxxxxx> > > Signed-off-by: Jolly Shah <jollys@xxxxxxxxxx> > > --- > > drivers/soc/xilinx/Kconfig | 11 +++ > > drivers/soc/xilinx/Makefile | 1 + > > drivers/soc/xilinx/zynqmp_power.c | 178 > ++++++++++++++++++++++++++++++++++++++ > > 3 files changed, 190 insertions(+) > > create mode 100644 drivers/soc/xilinx/zynqmp_power.c > > > > diff --git a/drivers/soc/xilinx/Kconfig b/drivers/soc/xilinx/Kconfig > > index 687c8f3..5025e0e 100644 > > --- a/drivers/soc/xilinx/Kconfig > > +++ b/drivers/soc/xilinx/Kconfig > > @@ -17,4 +17,15 @@ config XILINX_VCU > > To compile this driver as a module, choose M here: the > > module will be called xlnx_vcu. > > > > +config ZYNQMP_POWER > > + bool "Enable Xilinx Zynq MPSoC Power Management driver" > > + depends on PM && ARCH_ZYNQMP > > + default y > > + help > > + Say yes to enable power management support for ZyqnMP SoC. > > + This driver uses firmware driver as an interface for power > > + management request to firmware. It registers isr to handle > > + power management callbacks from firmware. > > + If in doubt, say N. > > + > > endmenu > > diff --git a/drivers/soc/xilinx/Makefile b/drivers/soc/xilinx/Makefile > > index dee8fd5..428b9db 100644 > > --- a/drivers/soc/xilinx/Makefile > > +++ b/drivers/soc/xilinx/Makefile > > @@ -1,2 +1,3 @@ > > # SPDX-License-Identifier: GPL-2.0 > > obj-$(CONFIG_XILINX_VCU) += xlnx_vcu.o > > +obj-$(CONFIG_ZYNQMP_POWER) += zynqmp_power.o > > diff --git a/drivers/soc/xilinx/zynqmp_power.c > b/drivers/soc/xilinx/zynqmp_power.c > > new file mode 100644 > > index 0000000..771cb59 > > --- /dev/null > > +++ b/drivers/soc/xilinx/zynqmp_power.c > > @@ -0,0 +1,178 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Xilinx Zynq MPSoC Power Management > > + * > > + * Copyright (C) 2014-2018 Xilinx, Inc. > > + * > > + * Davorin Mista <davorin.mista@xxxxxxxxxx> > > + * Jolly Shah <jollys@xxxxxxxxxx> > > + * Rajan Vaja <rajan.vaja@xxxxxxxxxx> > > + */ > > + > > +#include <linux/mailbox_client.h> > > +#include <linux/module.h> > > +#include <linux/platform_device.h> > > +#include <linux/reboot.h> > > +#include <linux/suspend.h> > > + > > +#include <linux/firmware/xlnx-zynqmp.h> > > + > > +enum pm_suspend_mode { > > + PM_SUSPEND_MODE_FIRST = 0, > > + PM_SUSPEND_MODE_STD = PM_SUSPEND_MODE_FIRST, > > + PM_SUSPEND_MODE_POWER_OFF, > > +}; > > + > > +#define PM_SUSPEND_MODE_FIRST PM_SUSPEND_MODE_STD > > + > > +static const char *const suspend_modes[] = { > > + [PM_SUSPEND_MODE_STD] = "standard", > > + [PM_SUSPEND_MODE_POWER_OFF] = "power-off", > > +}; > > + > > +static enum pm_suspend_mode suspend_mode = PM_SUSPEND_MODE_STD; > > + > > +enum pm_api_cb_id { > > + PM_INIT_SUSPEND_CB = 30, > > + PM_ACKNOWLEDGE_CB, > > + PM_NOTIFY_CB, > > +}; > > + > > +static void zynqmp_pm_get_callback_data(u32 *buf) > > +{ > > + zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, 0, 0, 0, 0, buf); > > +} > > + > > +static irqreturn_t zynqmp_pm_isr(int irq, void *data) > > +{ > > + u32 payload[CB_PAYLOAD_SIZE]; > > + > > + zynqmp_pm_get_callback_data(payload); > > + > > + /* First element is callback API ID, others are callback arguments */ > > + if (payload[0] == PM_INIT_SUSPEND_CB) { > > + switch (payload[1]) { > > + case SUSPEND_SYSTEM_SHUTDOWN: > > + orderly_poweroff(true); > > + break; > > + case SUSPEND_POWER_REQUEST: > > + pm_suspend(PM_SUSPEND_MEM); > > + break; > > + default: > > + pr_err("%s Unsupported InitSuspendCb reason " > > + "code %d\n", __func__, payload[1]); > > + } > > + } > > + > > + return IRQ_HANDLED; > > +} > > + > > +static ssize_t suspend_mode_show(struct device *dev, > > + struct device_attribute *attr, char *buf) > > +{ > > + char *s = buf; > > + int md; > > + > > + for (md = PM_SUSPEND_MODE_FIRST; md < > ARRAY_SIZE(suspend_modes); md++) > > + if (suspend_modes[md]) { > > + if (md == suspend_mode) > > + s += sprintf(s, "[%s] ", suspend_modes[md]); > > + else > > + s += sprintf(s, "%s ", suspend_modes[md]); > > + } > > + > > + /* Convert last space to newline */ > > + if (s != buf) > > + *(s - 1) = '\n'; > > + return (s - buf); > > +} > > + > > +static ssize_t suspend_mode_store(struct device *dev, > > + struct device_attribute *attr, > > + const char *buf, size_t count) > > +{ > > + int md, ret = -EINVAL; > > + const struct zynqmp_eemi_ops *eemi_ops = > zynqmp_pm_get_eemi_ops(); > > + > > + if (!eemi_ops || !eemi_ops->set_suspend_mode) > > + return ret; > > + > > + for (md = PM_SUSPEND_MODE_FIRST; md < > ARRAY_SIZE(suspend_modes); md++) > > + if (suspend_modes[md] && > > + sysfs_streq(suspend_modes[md], buf)) { > > + ret = 0; > > + break; > > + } > > + > > + if (!ret && md != suspend_mode) { > > + ret = eemi_ops->set_suspend_mode(md); > > + if (likely(!ret)) > > + suspend_mode = md; > > + } > > + > > + return ret ? ret : count; > > +} > > + > > +static DEVICE_ATTR_RW(suspend_mode); > > + > > +static int zynqmp_pm_probe(struct platform_device *pdev) > > +{ > > + int ret, irq; > > + u32 pm_api_version; > > + > > + const struct zynqmp_eemi_ops *eemi_ops = > zynqmp_pm_get_eemi_ops(); > > + > > + if (!eemi_ops || !eemi_ops->get_api_version || !eemi_ops- > >init_finalize) > > + return -ENXIO; > > + > > + eemi_ops->init_finalize(); > > + eemi_ops->get_api_version(&pm_api_version); > > + > > + /* Check PM API version number */ > > + if (pm_api_version < ZYNQMP_PM_VERSION) > > + return -ENODEV; > > + > > + irq = platform_get_irq(pdev, 0); > > + if (irq <= 0) > > + return -ENXIO; > > + > > + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, > zynqmp_pm_isr, > > + IRQF_NO_SUSPEND | IRQF_ONESHOT, > > + dev_name(&pdev->dev), &pdev->dev); > > + if (ret) { > > + dev_err(&pdev->dev, "devm_request_threaded_irq '%d' failed " > > + "with %d\n", irq, ret); > > + return ret; > > + } > > + > > + ret = sysfs_create_file(&pdev->dev.kobj, > &dev_attr_suspend_mode.attr); > > NACK, if this is for system suspend/reset ? You can just use exiting > sysfs, no need to create Xilinx specific new ones. Moreover you need to > use PSCI to make sure higher ELs can do orderly suspend/shutdown. > We have power off suspend mode which is not supported by existing sysfs and hence new one is needed. Suspend is handled through PSCI interface only. Thanks, Jolly Shah > -- > Regards, > Sudeep