Skip to content

Commit

Permalink
Merge tag 'zynqmp-soc-for-v5.6' of https://github.com/Xilinx/linux-xlnx
Browse files Browse the repository at this point in the history
… into arm/drivers

arm64: soc: ZynqMP SoC changes for v5.6

- Extend firmware interface for feature checking
- Use mailbox for communication with firmware for power management

* tag 'zynqmp-soc-for-v5.6' of https://github.com/Xilinx/linux-xlnx:
  drivers: soc: xilinx: Use mailbox IPI callback
  dt-bindings: power: reset: xilinx: Add bindings for ipi mailbox
  drivers: firmware: xilinx: Add support for feature check

Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Olof Johansson <[email protected]>
  • Loading branch information
olofj committed Jan 21, 2020
2 parents 684415d + ffdbae2 commit 88b4750
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,41 @@ Required properties:
- compatible: Must contain: "xlnx,zynqmp-power"
- interrupts: Interrupt specifier

-------
Example
-------
Optional properties:
- mbox-names : Name given to channels seen in the 'mboxes' property.
"tx" - Mailbox corresponding to transmit path
"rx" - Mailbox corresponding to receive path
- mboxes : Standard property to specify a Mailbox. Each value of
the mboxes property should contain a phandle to the
mailbox controller device node and an args specifier
that will be the phandle to the intended sub-mailbox
child node to be used for communication. See
Documentation/devicetree/bindings/mailbox/mailbox.txt
for more details about the generic mailbox controller
and client driver bindings. Also see
Documentation/devicetree/bindings/mailbox/ \
xlnx,zynqmp-ipi-mailbox.txt for typical controller that
is used to communicate with this System controllers.

--------
Examples
--------

Example with interrupt method:

firmware {
zynqmp_firmware: zynqmp-firmware {
compatible = "xlnx,zynqmp-firmware";
method = "smc";

zynqmp_power: zynqmp-power {
compatible = "xlnx,zynqmp-power";
interrupts = <0 35 4>;
};
};
};

Example with IPI mailbox method:

firmware {
zynqmp_firmware: zynqmp-firmware {
Expand All @@ -19,7 +51,11 @@ firmware {

zynqmp_power: zynqmp-power {
compatible = "xlnx,zynqmp-power";
interrupt-parent = <&gic>;
interrupts = <0 35 4>;
mboxes = <&ipi_mailbox_pmu0 0>,
<&ipi_mailbox_pmu0 1>;
mbox-names = "tx", "rx";
};
};
};
43 changes: 43 additions & 0 deletions drivers/firmware/xilinx/zynqmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

static const struct zynqmp_eemi_ops *eemi_ops_tbl;

static bool feature_check_enabled;
static u32 zynqmp_pm_features[PM_API_MAX];

static const struct mfd_cell firmware_devs[] = {
{
.name = "zynqmp_power_controller",
Expand All @@ -44,6 +47,8 @@ static int zynqmp_pm_ret_code(u32 ret_status)
case XST_PM_SUCCESS:
case XST_PM_DOUBLE_REQ:
return 0;
case XST_PM_NO_FEATURE:
return -ENOTSUPP;
case XST_PM_NO_ACCESS:
return -EACCES;
case XST_PM_ABORT_SUSPEND:
Expand Down Expand Up @@ -126,6 +131,39 @@ static noinline int do_fw_call_hvc(u64 arg0, u64 arg1, u64 arg2,
return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
}

/**
* zynqmp_pm_feature() - Check weather given feature is supported or not
* @api_id: API ID to check
*
* Return: Returns status, either success or error+reason
*/
static int zynqmp_pm_feature(u32 api_id)
{
int ret;
u32 ret_payload[PAYLOAD_ARG_CNT];
u64 smc_arg[2];

if (!feature_check_enabled)
return 0;

/* Return value if feature is already checked */
if (zynqmp_pm_features[api_id] != PM_FEATURE_UNCHECKED)
return zynqmp_pm_features[api_id];

smc_arg[0] = PM_SIP_SVC | PM_FEATURE_CHECK;
smc_arg[1] = api_id;

ret = do_fw_call(smc_arg[0], smc_arg[1], 0, ret_payload);
if (ret) {
zynqmp_pm_features[api_id] = PM_FEATURE_INVALID;
return PM_FEATURE_INVALID;
}

zynqmp_pm_features[api_id] = ret_payload[1];

return zynqmp_pm_features[api_id];
}

/**
* zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer
* caller function depending on the configuration
Expand Down Expand Up @@ -160,6 +198,9 @@ int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1,
*/
u64 smc_arg[4];

if (zynqmp_pm_feature(pm_api_id) == PM_FEATURE_INVALID)
return -ENOTSUPP;

smc_arg[0] = PM_SIP_SVC | pm_api_id;
smc_arg[1] = ((u64)arg1 << 32) | arg0;
smc_arg[2] = ((u64)arg3 << 32) | arg2;
Expand Down Expand Up @@ -715,6 +756,8 @@ static int zynqmp_firmware_probe(struct platform_device *pdev)
np = of_find_compatible_node(NULL, NULL, "xlnx,versal");
if (!np)
return 0;

feature_check_enabled = true;
}
of_node_put(np);

Expand Down
6 changes: 5 additions & 1 deletion drivers/soc/xilinx/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ config ZYNQMP_POWER
bool "Enable Xilinx Zynq MPSoC Power Management driver"
depends on PM && ARCH_ZYNQMP
default y
select MAILBOX
select ZYNQMP_IPI_MBOX
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.
power management callbacks from firmware. It registers mailbox client
to handle power management callbacks from firmware.

If in doubt, say N.

config ZYNQMP_PM_DOMAINS
Expand Down
120 changes: 107 additions & 13 deletions drivers/soc/xilinx/zynqmp_power.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/*
* Xilinx Zynq MPSoC Power Management
*
* Copyright (C) 2014-2018 Xilinx, Inc.
* Copyright (C) 2014-2019 Xilinx, Inc.
*
* Davorin Mista <[email protected]>
* Jolly Shah <[email protected]>
Expand All @@ -16,6 +16,21 @@
#include <linux/suspend.h>

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

/**
* struct zynqmp_pm_work_struct - Wrapper for struct work_struct
* @callback_work: Work structure
* @args: Callback arguments
*/
struct zynqmp_pm_work_struct {
struct work_struct callback_work;
u32 args[CB_ARG_CNT];
};

static struct zynqmp_pm_work_struct *zynqmp_pm_init_suspend_work;
static struct mbox_chan *rx_chan;
static const struct zynqmp_eemi_ops *eemi_ops;

enum pm_suspend_mode {
PM_SUSPEND_MODE_FIRST = 0,
Expand All @@ -31,7 +46,6 @@ static const char *const suspend_modes[] = {
};

static enum pm_suspend_mode suspend_mode = PM_SUSPEND_MODE_STD;
static const struct zynqmp_eemi_ops *eemi_ops;

enum pm_api_cb_id {
PM_INIT_SUSPEND_CB = 30,
Expand Down Expand Up @@ -68,6 +82,53 @@ static irqreturn_t zynqmp_pm_isr(int irq, void *data)
return IRQ_HANDLED;
}

static void ipi_receive_callback(struct mbox_client *cl, void *data)
{
struct zynqmp_ipi_message *msg = (struct zynqmp_ipi_message *)data;
u32 payload[CB_PAYLOAD_SIZE];
int ret;

memcpy(payload, msg->data, sizeof(msg->len));
/* First element is callback API ID, others are callback arguments */
if (payload[0] == PM_INIT_SUSPEND_CB) {
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);

/* Send NULL message to mbox controller to ack the message */
ret = mbox_send_message(rx_chan, NULL);
if (ret)
pr_err("IPI ack failed. Error %d\n", ret);
}
}

/**
* zynqmp_pm_init_suspend_work_fn - Initialize suspend
* @work: Pointer to work_struct
*
* Bottom-half of PM callback IRQ handler.
*/
static void zynqmp_pm_init_suspend_work_fn(struct work_struct *work)
{
struct zynqmp_pm_work_struct *pm_work =
container_of(work, struct zynqmp_pm_work_struct, callback_work);

if (pm_work->args[0] == SUSPEND_SYSTEM_SHUTDOWN) {
orderly_poweroff(true);
} else if (pm_work->args[0] == SUSPEND_POWER_REQUEST) {
pm_suspend(PM_SUSPEND_MEM);
} else {
pr_err("%s Unsupported InitSuspendCb reason code %d.\n",
__func__, pm_work->args[0]);
}
}

static ssize_t suspend_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
Expand Down Expand Up @@ -119,6 +180,7 @@ static int zynqmp_pm_probe(struct platform_device *pdev)
{
int ret, irq;
u32 pm_api_version;
struct mbox_client *client;

eemi_ops = zynqmp_pm_get_eemi_ops();
if (IS_ERR(eemi_ops))
Expand All @@ -134,17 +196,46 @@ static int zynqmp_pm_probe(struct platform_device *pdev)
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;
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),
GFP_KERNEL);
if (!zynqmp_pm_init_suspend_work)
return -ENOMEM;

INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work,
zynqmp_pm_init_suspend_work_fn);
client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL);
if (!client)
return -ENOMEM;

client->dev = &pdev->dev;
client->rx_callback = ipi_receive_callback;

rx_chan = mbox_request_channel_byname(client, "rx");
if (IS_ERR(rx_chan)) {
dev_err(&pdev->dev, "Failed to request rx channel\n");
return IS_ERR(rx_chan);
}
} else if (of_find_property(pdev->dev.of_node, "interrupts", NULL)) {
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;
}
} else {
dev_err(&pdev->dev, "Required property not found in DT node\n");
return -ENOENT;
}

ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr);
Expand All @@ -160,6 +251,9 @@ static int zynqmp_pm_remove(struct platform_device *pdev)
{
sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr);

if (!rx_chan)
mbox_free_channel(rx_chan);

return 0;
}

Expand Down
7 changes: 7 additions & 0 deletions include/linux/firmware/xlnx-zynqmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@
#define ZYNQMP_PM_CAPABILITY_WAKEUP 0x4U
#define ZYNQMP_PM_CAPABILITY_UNUSABLE 0x8U

/* Feature check status */
#define PM_FEATURE_INVALID -1
#define PM_FEATURE_UNCHECKED 0

/*
* Firmware FPGA Manager flags
* XILINX_ZYNQMP_PM_FPGA_FULL: FPGA full reconfiguration
Expand Down Expand Up @@ -78,11 +82,14 @@ enum pm_api_id {
PM_CLOCK_GETRATE,
PM_CLOCK_SETPARENT,
PM_CLOCK_GETPARENT,
PM_FEATURE_CHECK = 63,
PM_API_MAX,
};

/* PMU-FW return status codes */
enum pm_ret_status {
XST_PM_SUCCESS = 0,
XST_PM_NO_FEATURE = 19,
XST_PM_INTERNAL = 2000,
XST_PM_CONFLICT,
XST_PM_NO_ACCESS,
Expand Down

0 comments on commit 88b4750

Please sign in to comment.