forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge tag 'zynqmp-soc-for-v5.6' of https://github.com/Xilinx/linux-xlnx…
… 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
Showing
5 changed files
with
201 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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]> | ||
|
@@ -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, | ||
|
@@ -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, | ||
|
@@ -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) | ||
{ | ||
|
@@ -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)) | ||
|
@@ -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); | ||
|
@@ -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; | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters