arm64: zynqmp: SoC changes for v5.1
- Extend firmware interface with reset, nvmem, power management and power domain support - Add reset, nvmem driver, power management and power domain drivers - -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iEYEABECAAYFAlxixLEACgkQykllyylKDCEduwCeLkIFr48uJ/5Fv1X16gitcrOk F38An2wbsk21xkWQpfzCFdUrpPbT0u4t =GOP8 -----END PGP SIGNATURE----- Merge tag 'zynqmp-soc-for-v5.1' of https://github.com/Xilinx/linux-xlnx into arm/drivers arm64: zynqmp: SoC changes for v5.1 - Extend firmware interface with reset, nvmem, power management and power domain support - Add reset, nvmem driver, power management and power domain drivers - * tag 'zynqmp-soc-for-v5.1' of https://github.com/Xilinx/linux-xlnx: drivers: soc: xilinx: Add ZynqMP power domain driver firmware: xilinx: Add APIs to control node status/power dt-bindings: power: Add ZynqMP power domain bindings drivers: soc: xilinx: Add ZynqMP PM driver firmware: xilinx: Implement ZynqMP power management APIs dt-bindings: soc: Add ZynqMP PM bindings nvmem: zynqmp: Added zynqmp nvmem firmware driver dt-bindings: nvmem: Add bindings for ZynqMP nvmem driver firmware: xilinx: Add zynqmp_pm_get_chipid() API reset: reset-zynqmp: Adding support for Xilinx zynqmp reset controller. dt-bindings: reset: Add bindings for ZynqMP reset driver firmware: xilinx: Add reset API's Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
59f527dd7a
|
@ -0,0 +1,46 @@
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
= Zynq UltraScale+ MPSoC nvmem firmware driver binding =
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
The nvmem_firmware node provides access to the hardware related data
|
||||||
|
like soc revision, IDCODE... etc, By using the firmware interface.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be "xlnx,zynqmp-nvmem-fw"
|
||||||
|
|
||||||
|
= Data cells =
|
||||||
|
Are child nodes of silicon id, bindings of which as described in
|
||||||
|
bindings/nvmem/nvmem.txt
|
||||||
|
|
||||||
|
-------
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
firmware {
|
||||||
|
zynqmp_firmware: zynqmp-firmware {
|
||||||
|
compatible = "xlnx,zynqmp-firmware";
|
||||||
|
method = "smc";
|
||||||
|
|
||||||
|
nvmem_firmware {
|
||||||
|
compatible = "xlnx,zynqmp-nvmem-fw";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
|
||||||
|
/* Data cells */
|
||||||
|
soc_revision: soc_revision {
|
||||||
|
reg = <0x0 0x4>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
= Data consumers =
|
||||||
|
Are device nodes which consume nvmem data cells.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
pcap {
|
||||||
|
...
|
||||||
|
|
||||||
|
nvmem-cells = <&soc_revision>;
|
||||||
|
nvmem-cell-names = "soc_revision";
|
||||||
|
|
||||||
|
...
|
||||||
|
};
|
|
@ -0,0 +1,25 @@
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
Device Tree Bindings for the Xilinx Zynq MPSoC Power Management
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
The zynqmp-power node describes the power management configurations.
|
||||||
|
It will control remote suspend/shutdown interfaces.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: Must contain: "xlnx,zynqmp-power"
|
||||||
|
- interrupts: Interrupt specifier
|
||||||
|
|
||||||
|
-------
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
|
||||||
|
firmware {
|
||||||
|
zynqmp_firmware: zynqmp-firmware {
|
||||||
|
compatible = "xlnx,zynqmp-firmware";
|
||||||
|
method = "smc";
|
||||||
|
|
||||||
|
zynqmp_power: zynqmp-power {
|
||||||
|
compatible = "xlnx,zynqmp-power";
|
||||||
|
interrupts = <0 35 4>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,34 @@
|
||||||
|
-----------------------------------------------------------
|
||||||
|
Device Tree Bindings for the Xilinx Zynq MPSoC PM domains
|
||||||
|
-----------------------------------------------------------
|
||||||
|
The binding for zynqmp-power-controller follow the common
|
||||||
|
generic PM domain binding[1].
|
||||||
|
|
||||||
|
[1] Documentation/devicetree/bindings/power/power_domain.txt
|
||||||
|
|
||||||
|
== Zynq MPSoC Generic PM Domain Node ==
|
||||||
|
|
||||||
|
Required property:
|
||||||
|
- Below property should be in zynqmp-firmware node.
|
||||||
|
- #power-domain-cells: Number of cells in a PM domain specifier. Must be 1.
|
||||||
|
|
||||||
|
Power domain ID indexes are mentioned in
|
||||||
|
include/dt-bindings/power/xlnx-zynqmp-power.h.
|
||||||
|
|
||||||
|
-------
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
|
||||||
|
firmware {
|
||||||
|
zynqmp_firmware: zynqmp-firmware {
|
||||||
|
...
|
||||||
|
#power-domain-cells = <1>;
|
||||||
|
...
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
sata {
|
||||||
|
...
|
||||||
|
power-domains = <&zynqmp_firmware 28>;
|
||||||
|
...
|
||||||
|
};
|
|
@ -0,0 +1,52 @@
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
= Zynq UltraScale+ MPSoC reset driver binding =
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
The Zynq UltraScale+ MPSoC has several different resets.
|
||||||
|
|
||||||
|
See Chapter 36 of the Zynq UltraScale+ MPSoC TRM (UG) for more information
|
||||||
|
about zynqmp resets.
|
||||||
|
|
||||||
|
Please also refer to reset.txt in this directory for common reset
|
||||||
|
controller binding usage.
|
||||||
|
|
||||||
|
Required Properties:
|
||||||
|
- compatible: "xlnx,zynqmp-reset"
|
||||||
|
- #reset-cells: Specifies the number of cells needed to encode reset
|
||||||
|
line, should be 1
|
||||||
|
|
||||||
|
-------
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
|
||||||
|
firmware {
|
||||||
|
zynqmp_firmware: zynqmp-firmware {
|
||||||
|
compatible = "xlnx,zynqmp-firmware";
|
||||||
|
method = "smc";
|
||||||
|
|
||||||
|
zynqmp_reset: reset-controller {
|
||||||
|
compatible = "xlnx,zynqmp-reset";
|
||||||
|
#reset-cells = <1>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Specifying reset lines connected to IP modules
|
||||||
|
==============================================
|
||||||
|
|
||||||
|
Device nodes that need access to reset lines should
|
||||||
|
specify them as a reset phandle in their corresponding node as
|
||||||
|
specified in reset.txt.
|
||||||
|
|
||||||
|
For list of all valid reset indicies see
|
||||||
|
<dt-bindings/reset/xlnx-zynqmp-resets.h>
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
serdes: zynqmp_phy@fd400000 {
|
||||||
|
...
|
||||||
|
|
||||||
|
resets = <&zynqmp_reset ZYNQMP_RESET_SATA>;
|
||||||
|
reset-names = "sata_rst";
|
||||||
|
|
||||||
|
...
|
||||||
|
};
|
|
@ -6,6 +6,7 @@ menu "Zynq MPSoC Firmware Drivers"
|
||||||
|
|
||||||
config ZYNQMP_FIRMWARE
|
config ZYNQMP_FIRMWARE
|
||||||
bool "Enable Xilinx Zynq MPSoC firmware interface"
|
bool "Enable Xilinx Zynq MPSoC firmware interface"
|
||||||
|
select MFD_CORE
|
||||||
help
|
help
|
||||||
Firmware interface driver is used by different
|
Firmware interface driver is used by different
|
||||||
drivers to communicate with the firmware for
|
drivers to communicate with the firmware for
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/mfd/core.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
|
@ -23,6 +24,12 @@
|
||||||
#include <linux/firmware/xlnx-zynqmp.h>
|
#include <linux/firmware/xlnx-zynqmp.h>
|
||||||
#include "zynqmp-debug.h"
|
#include "zynqmp-debug.h"
|
||||||
|
|
||||||
|
static const struct mfd_cell firmware_devs[] = {
|
||||||
|
{
|
||||||
|
.name = "zynqmp_power_controller",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes
|
* zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes
|
||||||
* @ret_status: PMUFW return code
|
* @ret_status: PMUFW return code
|
||||||
|
@ -186,6 +193,29 @@ static int zynqmp_pm_get_api_version(u32 *version)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynqmp_pm_get_chipid - Get silicon ID registers
|
||||||
|
* @idcode: IDCODE register
|
||||||
|
* @version: version register
|
||||||
|
*
|
||||||
|
* Return: Returns the status of the operation and the idcode and version
|
||||||
|
* registers in @idcode and @version.
|
||||||
|
*/
|
||||||
|
static int zynqmp_pm_get_chipid(u32 *idcode, u32 *version)
|
||||||
|
{
|
||||||
|
u32 ret_payload[PAYLOAD_ARG_CNT];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!idcode || !version)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, 0, 0, 0, 0, ret_payload);
|
||||||
|
*idcode = ret_payload[1];
|
||||||
|
*version = ret_payload[2];
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version
|
* zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version
|
||||||
* @version: Returned version value
|
* @version: Returned version value
|
||||||
|
@ -469,8 +499,129 @@ static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2,
|
||||||
arg1, arg2, out);
|
arg1, arg2, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynqmp_pm_reset_assert - Request setting of reset (1 - assert, 0 - release)
|
||||||
|
* @reset: Reset to be configured
|
||||||
|
* @assert_flag: Flag stating should reset be asserted (1) or
|
||||||
|
* released (0)
|
||||||
|
*
|
||||||
|
* Return: Returns status, either success or error+reason
|
||||||
|
*/
|
||||||
|
static int zynqmp_pm_reset_assert(const enum zynqmp_pm_reset reset,
|
||||||
|
const enum zynqmp_pm_reset_action assert_flag)
|
||||||
|
{
|
||||||
|
return zynqmp_pm_invoke_fn(PM_RESET_ASSERT, reset, assert_flag,
|
||||||
|
0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynqmp_pm_reset_get_status - Get status of the reset
|
||||||
|
* @reset: Reset whose status should be returned
|
||||||
|
* @status: Returned status
|
||||||
|
*
|
||||||
|
* Return: Returns status, either success or error+reason
|
||||||
|
*/
|
||||||
|
static int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset,
|
||||||
|
u32 *status)
|
||||||
|
{
|
||||||
|
u32 ret_payload[PAYLOAD_ARG_CNT];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = zynqmp_pm_invoke_fn(PM_RESET_GET_STATUS, reset, 0,
|
||||||
|
0, 0, ret_payload);
|
||||||
|
*status = ret_payload[1];
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynqmp_pm_init_finalize() - PM call to inform firmware that the caller
|
||||||
|
* master has initialized its own power management
|
||||||
|
*
|
||||||
|
* This API function is to be used for notify the power management controller
|
||||||
|
* about the completed power management initialization.
|
||||||
|
*
|
||||||
|
* Return: Returns status, either success or error+reason
|
||||||
|
*/
|
||||||
|
static int zynqmp_pm_init_finalize(void)
|
||||||
|
{
|
||||||
|
return zynqmp_pm_invoke_fn(PM_PM_INIT_FINALIZE, 0, 0, 0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynqmp_pm_set_suspend_mode() - Set system suspend mode
|
||||||
|
* @mode: Mode to set for system suspend
|
||||||
|
*
|
||||||
|
* This API function is used to set mode of system suspend.
|
||||||
|
*
|
||||||
|
* Return: Returns status, either success or error+reason
|
||||||
|
*/
|
||||||
|
static int zynqmp_pm_set_suspend_mode(u32 mode)
|
||||||
|
{
|
||||||
|
return zynqmp_pm_invoke_fn(PM_SET_SUSPEND_MODE, mode, 0, 0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynqmp_pm_request_node() - Request a node with specific capabilities
|
||||||
|
* @node: Node ID of the slave
|
||||||
|
* @capabilities: Requested capabilities of the slave
|
||||||
|
* @qos: Quality of service (not supported)
|
||||||
|
* @ack: Flag to specify whether acknowledge is requested
|
||||||
|
*
|
||||||
|
* This function is used by master to request particular node from firmware.
|
||||||
|
* Every master must request node before using it.
|
||||||
|
*
|
||||||
|
* Return: Returns status, either success or error+reason
|
||||||
|
*/
|
||||||
|
static int zynqmp_pm_request_node(const u32 node, const u32 capabilities,
|
||||||
|
const u32 qos,
|
||||||
|
const enum zynqmp_pm_request_ack ack)
|
||||||
|
{
|
||||||
|
return zynqmp_pm_invoke_fn(PM_REQUEST_NODE, node, capabilities,
|
||||||
|
qos, ack, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynqmp_pm_release_node() - Release a node
|
||||||
|
* @node: Node ID of the slave
|
||||||
|
*
|
||||||
|
* This function is used by master to inform firmware that master
|
||||||
|
* has released node. Once released, master must not use that node
|
||||||
|
* without re-request.
|
||||||
|
*
|
||||||
|
* Return: Returns status, either success or error+reason
|
||||||
|
*/
|
||||||
|
static int zynqmp_pm_release_node(const u32 node)
|
||||||
|
{
|
||||||
|
return zynqmp_pm_invoke_fn(PM_RELEASE_NODE, node, 0, 0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynqmp_pm_set_requirement() - PM call to set requirement for PM slaves
|
||||||
|
* @node: Node ID of the slave
|
||||||
|
* @capabilities: Requested capabilities of the slave
|
||||||
|
* @qos: Quality of service (not supported)
|
||||||
|
* @ack: Flag to specify whether acknowledge is requested
|
||||||
|
*
|
||||||
|
* This API function is to be used for slaves a PU already has requested
|
||||||
|
* to change its capabilities.
|
||||||
|
*
|
||||||
|
* Return: Returns status, either success or error+reason
|
||||||
|
*/
|
||||||
|
static int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities,
|
||||||
|
const u32 qos,
|
||||||
|
const enum zynqmp_pm_request_ack ack)
|
||||||
|
{
|
||||||
|
return zynqmp_pm_invoke_fn(PM_SET_REQUIREMENT, node, capabilities,
|
||||||
|
qos, ack, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct zynqmp_eemi_ops eemi_ops = {
|
static const struct zynqmp_eemi_ops eemi_ops = {
|
||||||
.get_api_version = zynqmp_pm_get_api_version,
|
.get_api_version = zynqmp_pm_get_api_version,
|
||||||
|
.get_chipid = zynqmp_pm_get_chipid,
|
||||||
.query_data = zynqmp_pm_query_data,
|
.query_data = zynqmp_pm_query_data,
|
||||||
.clock_enable = zynqmp_pm_clock_enable,
|
.clock_enable = zynqmp_pm_clock_enable,
|
||||||
.clock_disable = zynqmp_pm_clock_disable,
|
.clock_disable = zynqmp_pm_clock_disable,
|
||||||
|
@ -482,6 +633,13 @@ static const struct zynqmp_eemi_ops eemi_ops = {
|
||||||
.clock_setparent = zynqmp_pm_clock_setparent,
|
.clock_setparent = zynqmp_pm_clock_setparent,
|
||||||
.clock_getparent = zynqmp_pm_clock_getparent,
|
.clock_getparent = zynqmp_pm_clock_getparent,
|
||||||
.ioctl = zynqmp_pm_ioctl,
|
.ioctl = zynqmp_pm_ioctl,
|
||||||
|
.reset_assert = zynqmp_pm_reset_assert,
|
||||||
|
.reset_get_status = zynqmp_pm_reset_get_status,
|
||||||
|
.init_finalize = zynqmp_pm_init_finalize,
|
||||||
|
.set_suspend_mode = zynqmp_pm_set_suspend_mode,
|
||||||
|
.request_node = zynqmp_pm_request_node,
|
||||||
|
.release_node = zynqmp_pm_release_node,
|
||||||
|
.set_requirement = zynqmp_pm_set_requirement,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -538,11 +696,19 @@ static int zynqmp_firmware_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
zynqmp_pm_api_debugfs_init();
|
zynqmp_pm_api_debugfs_init();
|
||||||
|
|
||||||
|
ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, firmware_devs,
|
||||||
|
ARRAY_SIZE(firmware_devs), NULL, 0, NULL);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "failed to add MFD devices %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return of_platform_populate(dev->of_node, NULL, NULL, dev);
|
return of_platform_populate(dev->of_node, NULL, NULL, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int zynqmp_firmware_remove(struct platform_device *pdev)
|
static int zynqmp_firmware_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
mfd_remove_devices(&pdev->dev);
|
||||||
zynqmp_pm_api_debugfs_exit();
|
zynqmp_pm_api_debugfs_exit();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -192,4 +192,14 @@ config SC27XX_EFUSE
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called nvmem-sc27xx-efuse.
|
will be called nvmem-sc27xx-efuse.
|
||||||
|
|
||||||
|
config NVMEM_ZYNQMP
|
||||||
|
bool "Xilinx ZYNQMP SoC nvmem firmware support"
|
||||||
|
depends on ARCH_ZYNQMP
|
||||||
|
help
|
||||||
|
This is a driver to access hardware related data like
|
||||||
|
soc revision, IDCODE... etc by using the firmware
|
||||||
|
interface.
|
||||||
|
|
||||||
|
If sure, say yes. If unsure, say no.
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -41,3 +41,5 @@ obj-$(CONFIG_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o
|
||||||
nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o
|
nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o
|
||||||
obj-$(CONFIG_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o
|
obj-$(CONFIG_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o
|
||||||
nvmem-sc27xx-efuse-y := sc27xx-efuse.o
|
nvmem-sc27xx-efuse-y := sc27xx-efuse.o
|
||||||
|
obj-$(CONFIG_NVMEM_ZYNQMP) += nvmem_zynqmp_nvmem.o
|
||||||
|
nvmem_zynqmp_nvmem-y := zynqmp_nvmem.o
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Xilinx, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/nvmem-provider.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/firmware/xlnx-zynqmp.h>
|
||||||
|
|
||||||
|
#define SILICON_REVISION_MASK 0xF
|
||||||
|
|
||||||
|
struct zynqmp_nvmem_data {
|
||||||
|
struct device *dev;
|
||||||
|
struct nvmem_device *nvmem;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int zynqmp_nvmem_read(void *context, unsigned int offset,
|
||||||
|
void *val, size_t bytes)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int idcode, version;
|
||||||
|
struct zynqmp_nvmem_data *priv = context;
|
||||||
|
|
||||||
|
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
|
||||||
|
|
||||||
|
if (!eemi_ops || !eemi_ops->get_chipid)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
ret = eemi_ops->get_chipid(&idcode, &version);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
dev_dbg(priv->dev, "Read chipid val %x %x\n", idcode, version);
|
||||||
|
*(int *)val = version & SILICON_REVISION_MASK;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nvmem_config econfig = {
|
||||||
|
.name = "zynqmp-nvmem",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.word_size = 1,
|
||||||
|
.size = 1,
|
||||||
|
.read_only = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id zynqmp_nvmem_match[] = {
|
||||||
|
{ .compatible = "xlnx,zynqmp-nvmem-fw", },
|
||||||
|
{ /* sentinel */ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, zynqmp_nvmem_match);
|
||||||
|
|
||||||
|
static int zynqmp_nvmem_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct zynqmp_nvmem_data *priv;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(dev, sizeof(struct zynqmp_nvmem_data), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv->dev = dev;
|
||||||
|
econfig.dev = dev;
|
||||||
|
econfig.reg_read = zynqmp_nvmem_read;
|
||||||
|
econfig.priv = priv;
|
||||||
|
|
||||||
|
priv->nvmem = devm_nvmem_register(dev, &econfig);
|
||||||
|
|
||||||
|
return PTR_ERR_OR_ZERO(priv->nvmem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver zynqmp_nvmem_driver = {
|
||||||
|
.probe = zynqmp_nvmem_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "zynqmp-nvmem",
|
||||||
|
.of_match_table = zynqmp_nvmem_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(zynqmp_nvmem_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>, Nava kishore Manne <navam@xilinx.com>");
|
||||||
|
MODULE_DESCRIPTION("ZynqMP NVMEM driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -26,4 +26,5 @@ obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o
|
||||||
obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
|
obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
|
||||||
obj-$(CONFIG_RESET_UNIPHIER_GLUE) += reset-uniphier-glue.o
|
obj-$(CONFIG_RESET_UNIPHIER_GLUE) += reset-uniphier-glue.o
|
||||||
obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o
|
obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o
|
||||||
|
obj-$(CONFIG_ARCH_ZYNQMP) += reset-zynqmp.o
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Xilinx, Inc.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/reset-controller.h>
|
||||||
|
#include <linux/firmware/xlnx-zynqmp.h>
|
||||||
|
|
||||||
|
#define ZYNQMP_NR_RESETS (ZYNQMP_PM_RESET_END - ZYNQMP_PM_RESET_START)
|
||||||
|
#define ZYNQMP_RESET_ID ZYNQMP_PM_RESET_START
|
||||||
|
|
||||||
|
struct zynqmp_reset_data {
|
||||||
|
struct reset_controller_dev rcdev;
|
||||||
|
const struct zynqmp_eemi_ops *eemi_ops;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct zynqmp_reset_data *
|
||||||
|
to_zynqmp_reset_data(struct reset_controller_dev *rcdev)
|
||||||
|
{
|
||||||
|
return container_of(rcdev, struct zynqmp_reset_data, rcdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zynqmp_reset_assert(struct reset_controller_dev *rcdev,
|
||||||
|
unsigned long id)
|
||||||
|
{
|
||||||
|
struct zynqmp_reset_data *priv = to_zynqmp_reset_data(rcdev);
|
||||||
|
|
||||||
|
return priv->eemi_ops->reset_assert(ZYNQMP_RESET_ID + id,
|
||||||
|
PM_RESET_ACTION_ASSERT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zynqmp_reset_deassert(struct reset_controller_dev *rcdev,
|
||||||
|
unsigned long id)
|
||||||
|
{
|
||||||
|
struct zynqmp_reset_data *priv = to_zynqmp_reset_data(rcdev);
|
||||||
|
|
||||||
|
return priv->eemi_ops->reset_assert(ZYNQMP_RESET_ID + id,
|
||||||
|
PM_RESET_ACTION_RELEASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zynqmp_reset_status(struct reset_controller_dev *rcdev,
|
||||||
|
unsigned long id)
|
||||||
|
{
|
||||||
|
struct zynqmp_reset_data *priv = to_zynqmp_reset_data(rcdev);
|
||||||
|
int val, err;
|
||||||
|
|
||||||
|
err = priv->eemi_ops->reset_get_status(ZYNQMP_RESET_ID + id, &val);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zynqmp_reset_reset(struct reset_controller_dev *rcdev,
|
||||||
|
unsigned long id)
|
||||||
|
{
|
||||||
|
struct zynqmp_reset_data *priv = to_zynqmp_reset_data(rcdev);
|
||||||
|
|
||||||
|
return priv->eemi_ops->reset_assert(ZYNQMP_RESET_ID + id,
|
||||||
|
PM_RESET_ACTION_PULSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct reset_control_ops zynqmp_reset_ops = {
|
||||||
|
.reset = zynqmp_reset_reset,
|
||||||
|
.assert = zynqmp_reset_assert,
|
||||||
|
.deassert = zynqmp_reset_deassert,
|
||||||
|
.status = zynqmp_reset_status,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int zynqmp_reset_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct zynqmp_reset_data *priv;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, priv);
|
||||||
|
|
||||||
|
priv->eemi_ops = zynqmp_pm_get_eemi_ops();
|
||||||
|
if (!priv->eemi_ops)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
priv->rcdev.ops = &zynqmp_reset_ops;
|
||||||
|
priv->rcdev.owner = THIS_MODULE;
|
||||||
|
priv->rcdev.of_node = pdev->dev.of_node;
|
||||||
|
priv->rcdev.nr_resets = ZYNQMP_NR_RESETS;
|
||||||
|
|
||||||
|
return devm_reset_controller_register(&pdev->dev, &priv->rcdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id zynqmp_reset_dt_ids[] = {
|
||||||
|
{ .compatible = "xlnx,zynqmp-reset", },
|
||||||
|
{ /* sentinel */ },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver zynqmp_reset_driver = {
|
||||||
|
.probe = zynqmp_reset_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = KBUILD_MODNAME,
|
||||||
|
.of_match_table = zynqmp_reset_dt_ids,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init zynqmp_reset_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&zynqmp_reset_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
arch_initcall(zynqmp_reset_init);
|
|
@ -17,4 +17,24 @@ config XILINX_VCU
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called xlnx_vcu.
|
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.
|
||||||
|
|
||||||
|
config ZYNQMP_PM_DOMAINS
|
||||||
|
bool "Enable Zynq MPSoC generic PM domains"
|
||||||
|
default y
|
||||||
|
depends on PM && ARCH_ZYNQMP && ZYNQMP_FIRMWARE
|
||||||
|
select PM_GENERIC_DOMAINS
|
||||||
|
help
|
||||||
|
Say yes to enable device power management through PM domains
|
||||||
|
If in doubt, say N.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
obj-$(CONFIG_XILINX_VCU) += xlnx_vcu.o
|
obj-$(CONFIG_XILINX_VCU) += xlnx_vcu.o
|
||||||
|
obj-$(CONFIG_ZYNQMP_POWER) += zynqmp_power.o
|
||||||
|
obj-$(CONFIG_ZYNQMP_PM_DOMAINS) += zynqmp_pm_domains.o
|
||||||
|
|
|
@ -0,0 +1,321 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* ZynqMP Generic PM domain support
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015-2018 Xilinx, Inc.
|
||||||
|
*
|
||||||
|
* Davorin Mista <davorin.mista@aggios.com>
|
||||||
|
* Jolly Shah <jollys@xilinx.com>
|
||||||
|
* Rajan Vaja <rajan.vaja@xilinx.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_domain.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include <linux/firmware/xlnx-zynqmp.h>
|
||||||
|
|
||||||
|
#define ZYNQMP_NUM_DOMAINS (100)
|
||||||
|
/* Flag stating if PM nodes mapped to the PM domain has been requested */
|
||||||
|
#define ZYNQMP_PM_DOMAIN_REQUESTED BIT(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain
|
||||||
|
* @gpd: Generic power domain
|
||||||
|
* @node_id: PM node ID corresponding to device inside PM domain
|
||||||
|
* @flags: ZynqMP PM domain flags
|
||||||
|
*/
|
||||||
|
struct zynqmp_pm_domain {
|
||||||
|
struct generic_pm_domain gpd;
|
||||||
|
u32 node_id;
|
||||||
|
u8 flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynqmp_gpd_is_active_wakeup_path() - Check if device is in wakeup source
|
||||||
|
* path
|
||||||
|
* @dev: Device to check for wakeup source path
|
||||||
|
* @not_used: Data member (not required)
|
||||||
|
*
|
||||||
|
* This function is checks device's child hierarchy and checks if any device is
|
||||||
|
* set as wakeup source.
|
||||||
|
*
|
||||||
|
* Return: 1 if device is in wakeup source path else 0
|
||||||
|
*/
|
||||||
|
static int zynqmp_gpd_is_active_wakeup_path(struct device *dev, void *not_used)
|
||||||
|
{
|
||||||
|
int may_wakeup;
|
||||||
|
|
||||||
|
may_wakeup = device_may_wakeup(dev);
|
||||||
|
if (may_wakeup)
|
||||||
|
return may_wakeup;
|
||||||
|
|
||||||
|
return device_for_each_child(dev, NULL,
|
||||||
|
zynqmp_gpd_is_active_wakeup_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynqmp_gpd_power_on() - Power on PM domain
|
||||||
|
* @domain: Generic PM domain
|
||||||
|
*
|
||||||
|
* This function is called before devices inside a PM domain are resumed, to
|
||||||
|
* power on PM domain.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, error code otherwise
|
||||||
|
*/
|
||||||
|
static int zynqmp_gpd_power_on(struct generic_pm_domain *domain)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct zynqmp_pm_domain *pd;
|
||||||
|
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
|
||||||
|
|
||||||
|
if (!eemi_ops || !eemi_ops->set_requirement)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
pd = container_of(domain, struct zynqmp_pm_domain, gpd);
|
||||||
|
ret = eemi_ops->set_requirement(pd->node_id,
|
||||||
|
ZYNQMP_PM_CAPABILITY_ACCESS,
|
||||||
|
ZYNQMP_PM_MAX_QOS,
|
||||||
|
ZYNQMP_PM_REQUEST_ACK_BLOCKING);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s() %s set requirement for node %d failed: %d\n",
|
||||||
|
__func__, domain->name, pd->node_id, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_debug("%s() Powered on %s domain\n", __func__, domain->name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynqmp_gpd_power_off() - Power off PM domain
|
||||||
|
* @domain: Generic PM domain
|
||||||
|
*
|
||||||
|
* This function is called after devices inside a PM domain are suspended, to
|
||||||
|
* power off PM domain.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, error code otherwise
|
||||||
|
*/
|
||||||
|
static int zynqmp_gpd_power_off(struct generic_pm_domain *domain)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct pm_domain_data *pdd, *tmp;
|
||||||
|
struct zynqmp_pm_domain *pd;
|
||||||
|
u32 capabilities = 0;
|
||||||
|
bool may_wakeup;
|
||||||
|
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
|
||||||
|
|
||||||
|
if (!eemi_ops || !eemi_ops->set_requirement)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
pd = container_of(domain, struct zynqmp_pm_domain, gpd);
|
||||||
|
|
||||||
|
/* If domain is already released there is nothing to be done */
|
||||||
|
if (!(pd->flags & ZYNQMP_PM_DOMAIN_REQUESTED)) {
|
||||||
|
pr_debug("%s() %s domain is already released\n",
|
||||||
|
__func__, domain->name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry_safe(pdd, tmp, &domain->dev_list, list_node) {
|
||||||
|
/* If device is in wakeup path, set capability to WAKEUP */
|
||||||
|
may_wakeup = zynqmp_gpd_is_active_wakeup_path(pdd->dev, NULL);
|
||||||
|
if (may_wakeup) {
|
||||||
|
dev_dbg(pdd->dev, "device is in wakeup path in %s\n",
|
||||||
|
domain->name);
|
||||||
|
capabilities = ZYNQMP_PM_CAPABILITY_WAKEUP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = eemi_ops->set_requirement(pd->node_id, capabilities, 0,
|
||||||
|
ZYNQMP_PM_REQUEST_ACK_NO);
|
||||||
|
/**
|
||||||
|
* If powering down of any node inside this domain fails,
|
||||||
|
* report and return the error
|
||||||
|
*/
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s() %s set requirement for node %d failed: %d\n",
|
||||||
|
__func__, domain->name, pd->node_id, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_debug("%s() Powered off %s domain\n", __func__, domain->name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynqmp_gpd_attach_dev() - Attach device to the PM domain
|
||||||
|
* @domain: Generic PM domain
|
||||||
|
* @dev: Device to attach
|
||||||
|
*
|
||||||
|
* Return: 0 on success, error code otherwise
|
||||||
|
*/
|
||||||
|
static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain,
|
||||||
|
struct device *dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct zynqmp_pm_domain *pd;
|
||||||
|
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
|
||||||
|
|
||||||
|
if (!eemi_ops || !eemi_ops->request_node)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
pd = container_of(domain, struct zynqmp_pm_domain, gpd);
|
||||||
|
|
||||||
|
/* If this is not the first device to attach there is nothing to do */
|
||||||
|
if (domain->device_count)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = eemi_ops->request_node(pd->node_id, 0, 0,
|
||||||
|
ZYNQMP_PM_REQUEST_ACK_BLOCKING);
|
||||||
|
/* If requesting a node fails print and return the error */
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s() %s request failed for node %d: %d\n",
|
||||||
|
__func__, domain->name, pd->node_id, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pd->flags |= ZYNQMP_PM_DOMAIN_REQUESTED;
|
||||||
|
|
||||||
|
pr_debug("%s() %s attached to %s domain\n", __func__,
|
||||||
|
dev_name(dev), domain->name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynqmp_gpd_detach_dev() - Detach device from the PM domain
|
||||||
|
* @domain: Generic PM domain
|
||||||
|
* @dev: Device to detach
|
||||||
|
*/
|
||||||
|
static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain,
|
||||||
|
struct device *dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct zynqmp_pm_domain *pd;
|
||||||
|
const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
|
||||||
|
|
||||||
|
if (!eemi_ops || !eemi_ops->release_node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pd = container_of(domain, struct zynqmp_pm_domain, gpd);
|
||||||
|
|
||||||
|
/* If this is not the last device to detach there is nothing to do */
|
||||||
|
if (domain->device_count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ret = eemi_ops->release_node(pd->node_id);
|
||||||
|
/* If releasing a node fails print the error and return */
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s() %s release failed for node %d: %d\n",
|
||||||
|
__func__, domain->name, pd->node_id, ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pd->flags &= ~ZYNQMP_PM_DOMAIN_REQUESTED;
|
||||||
|
|
||||||
|
pr_debug("%s() %s detached from %s domain\n", __func__,
|
||||||
|
dev_name(dev), domain->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct generic_pm_domain *zynqmp_gpd_xlate
|
||||||
|
(struct of_phandle_args *genpdspec, void *data)
|
||||||
|
{
|
||||||
|
struct genpd_onecell_data *genpd_data = data;
|
||||||
|
unsigned int i, idx = genpdspec->args[0];
|
||||||
|
struct zynqmp_pm_domain *pd;
|
||||||
|
|
||||||
|
pd = container_of(genpd_data->domains[0], struct zynqmp_pm_domain, gpd);
|
||||||
|
|
||||||
|
if (genpdspec->args_count != 1)
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
/* Check for existing pm domains */
|
||||||
|
for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++) {
|
||||||
|
if (pd[i].node_id == idx)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add index in empty node_id of power domain list as no existing
|
||||||
|
* power domain found for current index.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++) {
|
||||||
|
if (pd[i].node_id == 0) {
|
||||||
|
pd[i].node_id = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (!genpd_data->domains[i] || i == ZYNQMP_NUM_DOMAINS)
|
||||||
|
return ERR_PTR(-ENOENT);
|
||||||
|
|
||||||
|
return genpd_data->domains[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zynqmp_gpd_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct genpd_onecell_data *zynqmp_pd_data;
|
||||||
|
struct generic_pm_domain **domains;
|
||||||
|
struct zynqmp_pm_domain *pd;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
|
||||||
|
pd = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*pd), GFP_KERNEL);
|
||||||
|
if (!pd)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
zynqmp_pd_data = devm_kzalloc(dev, sizeof(*zynqmp_pd_data), GFP_KERNEL);
|
||||||
|
if (!zynqmp_pd_data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
zynqmp_pd_data->xlate = zynqmp_gpd_xlate;
|
||||||
|
|
||||||
|
domains = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*domains),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!domains)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++, pd++) {
|
||||||
|
pd->node_id = 0;
|
||||||
|
pd->gpd.name = kasprintf(GFP_KERNEL, "domain%d", i);
|
||||||
|
pd->gpd.power_off = zynqmp_gpd_power_off;
|
||||||
|
pd->gpd.power_on = zynqmp_gpd_power_on;
|
||||||
|
pd->gpd.attach_dev = zynqmp_gpd_attach_dev;
|
||||||
|
pd->gpd.detach_dev = zynqmp_gpd_detach_dev;
|
||||||
|
|
||||||
|
domains[i] = &pd->gpd;
|
||||||
|
|
||||||
|
/* Mark all PM domains as initially powered off */
|
||||||
|
pm_genpd_init(&pd->gpd, NULL, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
zynqmp_pd_data->domains = domains;
|
||||||
|
zynqmp_pd_data->num_domains = ZYNQMP_NUM_DOMAINS;
|
||||||
|
of_genpd_add_provider_onecell(dev->parent->of_node, zynqmp_pd_data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zynqmp_gpd_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
of_genpd_del_provider(pdev->dev.parent->of_node);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver zynqmp_power_domain_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "zynqmp_power_controller",
|
||||||
|
},
|
||||||
|
.probe = zynqmp_gpd_probe,
|
||||||
|
.remove = zynqmp_gpd_remove,
|
||||||
|
};
|
||||||
|
module_platform_driver(zynqmp_power_domain_driver);
|
||||||
|
|
||||||
|
MODULE_ALIAS("platform:zynqmp_power_controller");
|
|
@ -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@aggios.com>
|
||||||
|
* Jolly Shah <jollys@xilinx.com>
|
||||||
|
* Rajan Vaja <rajan.vaja@xilinx.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "unable to create sysfs interface\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zynqmp_pm_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id pm_of_match[] = {
|
||||||
|
{ .compatible = "xlnx,zynqmp-power", },
|
||||||
|
{ /* end of table */ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, pm_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver zynqmp_pm_platform_driver = {
|
||||||
|
.probe = zynqmp_pm_probe,
|
||||||
|
.remove = zynqmp_pm_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "zynqmp_power",
|
||||||
|
.of_match_table = pm_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(zynqmp_pm_platform_driver);
|
|
@ -0,0 +1,39 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Xilinx, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DT_BINDINGS_ZYNQMP_POWER_H
|
||||||
|
#define _DT_BINDINGS_ZYNQMP_POWER_H
|
||||||
|
|
||||||
|
#define PD_USB_0 22
|
||||||
|
#define PD_USB_1 23
|
||||||
|
#define PD_TTC_0 24
|
||||||
|
#define PD_TTC_1 25
|
||||||
|
#define PD_TTC_2 26
|
||||||
|
#define PD_TTC_3 27
|
||||||
|
#define PD_SATA 28
|
||||||
|
#define PD_ETH_0 29
|
||||||
|
#define PD_ETH_1 30
|
||||||
|
#define PD_ETH_2 31
|
||||||
|
#define PD_ETH_3 32
|
||||||
|
#define PD_UART_0 33
|
||||||
|
#define PD_UART_1 34
|
||||||
|
#define PD_SPI_0 35
|
||||||
|
#define PD_SPI_1 36
|
||||||
|
#define PD_I2C_0 37
|
||||||
|
#define PD_I2C_1 38
|
||||||
|
#define PD_SD_0 39
|
||||||
|
#define PD_SD_1 40
|
||||||
|
#define PD_DP 41
|
||||||
|
#define PD_GDMA 42
|
||||||
|
#define PD_ADMA 43
|
||||||
|
#define PD_NAND 44
|
||||||
|
#define PD_QSPI 45
|
||||||
|
#define PD_GPIO 46
|
||||||
|
#define PD_CAN_0 47
|
||||||
|
#define PD_CAN_1 48
|
||||||
|
#define PD_GPU 58
|
||||||
|
#define PD_PCIE 59
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,130 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Xilinx, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DT_BINDINGS_ZYNQMP_RESETS_H
|
||||||
|
#define _DT_BINDINGS_ZYNQMP_RESETS_H
|
||||||
|
|
||||||
|
#define ZYNQMP_RESET_PCIE_CFG 0
|
||||||
|
#define ZYNQMP_RESET_PCIE_BRIDGE 1
|
||||||
|
#define ZYNQMP_RESET_PCIE_CTRL 2
|
||||||
|
#define ZYNQMP_RESET_DP 3
|
||||||
|
#define ZYNQMP_RESET_SWDT_CRF 4
|
||||||
|
#define ZYNQMP_RESET_AFI_FM5 5
|
||||||
|
#define ZYNQMP_RESET_AFI_FM4 6
|
||||||
|
#define ZYNQMP_RESET_AFI_FM3 7
|
||||||
|
#define ZYNQMP_RESET_AFI_FM2 8
|
||||||
|
#define ZYNQMP_RESET_AFI_FM1 9
|
||||||
|
#define ZYNQMP_RESET_AFI_FM0 10
|
||||||
|
#define ZYNQMP_RESET_GDMA 11
|
||||||
|
#define ZYNQMP_RESET_GPU_PP1 12
|
||||||
|
#define ZYNQMP_RESET_GPU_PP0 13
|
||||||
|
#define ZYNQMP_RESET_GPU 14
|
||||||
|
#define ZYNQMP_RESET_GT 15
|
||||||
|
#define ZYNQMP_RESET_SATA 16
|
||||||
|
#define ZYNQMP_RESET_ACPU3_PWRON 17
|
||||||
|
#define ZYNQMP_RESET_ACPU2_PWRON 18
|
||||||
|
#define ZYNQMP_RESET_ACPU1_PWRON 19
|
||||||
|
#define ZYNQMP_RESET_ACPU0_PWRON 20
|
||||||
|
#define ZYNQMP_RESET_APU_L2 21
|
||||||
|
#define ZYNQMP_RESET_ACPU3 22
|
||||||
|
#define ZYNQMP_RESET_ACPU2 23
|
||||||
|
#define ZYNQMP_RESET_ACPU1 24
|
||||||
|
#define ZYNQMP_RESET_ACPU0 25
|
||||||
|
#define ZYNQMP_RESET_DDR 26
|
||||||
|
#define ZYNQMP_RESET_APM_FPD 27
|
||||||
|
#define ZYNQMP_RESET_SOFT 28
|
||||||
|
#define ZYNQMP_RESET_GEM0 29
|
||||||
|
#define ZYNQMP_RESET_GEM1 30
|
||||||
|
#define ZYNQMP_RESET_GEM2 31
|
||||||
|
#define ZYNQMP_RESET_GEM3 32
|
||||||
|
#define ZYNQMP_RESET_QSPI 33
|
||||||
|
#define ZYNQMP_RESET_UART0 34
|
||||||
|
#define ZYNQMP_RESET_UART1 35
|
||||||
|
#define ZYNQMP_RESET_SPI0 36
|
||||||
|
#define ZYNQMP_RESET_SPI1 37
|
||||||
|
#define ZYNQMP_RESET_SDIO0 38
|
||||||
|
#define ZYNQMP_RESET_SDIO1 39
|
||||||
|
#define ZYNQMP_RESET_CAN0 40
|
||||||
|
#define ZYNQMP_RESET_CAN1 41
|
||||||
|
#define ZYNQMP_RESET_I2C0 42
|
||||||
|
#define ZYNQMP_RESET_I2C1 43
|
||||||
|
#define ZYNQMP_RESET_TTC0 44
|
||||||
|
#define ZYNQMP_RESET_TTC1 45
|
||||||
|
#define ZYNQMP_RESET_TTC2 46
|
||||||
|
#define ZYNQMP_RESET_TTC3 47
|
||||||
|
#define ZYNQMP_RESET_SWDT_CRL 48
|
||||||
|
#define ZYNQMP_RESET_NAND 49
|
||||||
|
#define ZYNQMP_RESET_ADMA 50
|
||||||
|
#define ZYNQMP_RESET_GPIO 51
|
||||||
|
#define ZYNQMP_RESET_IOU_CC 52
|
||||||
|
#define ZYNQMP_RESET_TIMESTAMP 53
|
||||||
|
#define ZYNQMP_RESET_RPU_R50 54
|
||||||
|
#define ZYNQMP_RESET_RPU_R51 55
|
||||||
|
#define ZYNQMP_RESET_RPU_AMBA 56
|
||||||
|
#define ZYNQMP_RESET_OCM 57
|
||||||
|
#define ZYNQMP_RESET_RPU_PGE 58
|
||||||
|
#define ZYNQMP_RESET_USB0_CORERESET 59
|
||||||
|
#define ZYNQMP_RESET_USB1_CORERESET 60
|
||||||
|
#define ZYNQMP_RESET_USB0_HIBERRESET 61
|
||||||
|
#define ZYNQMP_RESET_USB1_HIBERRESET 62
|
||||||
|
#define ZYNQMP_RESET_USB0_APB 63
|
||||||
|
#define ZYNQMP_RESET_USB1_APB 64
|
||||||
|
#define ZYNQMP_RESET_IPI 65
|
||||||
|
#define ZYNQMP_RESET_APM_LPD 66
|
||||||
|
#define ZYNQMP_RESET_RTC 67
|
||||||
|
#define ZYNQMP_RESET_SYSMON 68
|
||||||
|
#define ZYNQMP_RESET_AFI_FM6 69
|
||||||
|
#define ZYNQMP_RESET_LPD_SWDT 70
|
||||||
|
#define ZYNQMP_RESET_FPD 71
|
||||||
|
#define ZYNQMP_RESET_RPU_DBG1 72
|
||||||
|
#define ZYNQMP_RESET_RPU_DBG0 73
|
||||||
|
#define ZYNQMP_RESET_DBG_LPD 74
|
||||||
|
#define ZYNQMP_RESET_DBG_FPD 75
|
||||||
|
#define ZYNQMP_RESET_APLL 76
|
||||||
|
#define ZYNQMP_RESET_DPLL 77
|
||||||
|
#define ZYNQMP_RESET_VPLL 78
|
||||||
|
#define ZYNQMP_RESET_IOPLL 79
|
||||||
|
#define ZYNQMP_RESET_RPLL 80
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_0 81
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_1 82
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_2 83
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_3 84
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_4 85
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_5 86
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_6 87
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_7 88
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_8 89
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_9 90
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_10 91
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_11 92
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_12 93
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_13 94
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_14 95
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_15 96
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_16 97
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_17 98
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_18 99
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_19 100
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_20 101
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_21 102
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_22 103
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_23 104
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_24 105
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_25 106
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_26 107
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_27 108
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_28 109
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_29 110
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_30 111
|
||||||
|
#define ZYNQMP_RESET_GPO3_PL_31 112
|
||||||
|
#define ZYNQMP_RESET_RPU_LS 113
|
||||||
|
#define ZYNQMP_RESET_PS_ONLY 114
|
||||||
|
#define ZYNQMP_RESET_PL 115
|
||||||
|
#define ZYNQMP_RESET_PS_PL0 116
|
||||||
|
#define ZYNQMP_RESET_PS_PL1 117
|
||||||
|
#define ZYNQMP_RESET_PS_PL2 118
|
||||||
|
#define ZYNQMP_RESET_PS_PL3 119
|
||||||
|
|
||||||
|
#endif
|
|
@ -28,12 +28,35 @@
|
||||||
/* SMC SIP service Call Function Identifier Prefix */
|
/* SMC SIP service Call Function Identifier Prefix */
|
||||||
#define PM_SIP_SVC 0xC2000000
|
#define PM_SIP_SVC 0xC2000000
|
||||||
#define PM_GET_TRUSTZONE_VERSION 0xa03
|
#define PM_GET_TRUSTZONE_VERSION 0xa03
|
||||||
|
#define PM_SET_SUSPEND_MODE 0xa02
|
||||||
|
#define GET_CALLBACK_DATA 0xa01
|
||||||
|
|
||||||
/* Number of 32bits values in payload */
|
/* Number of 32bits values in payload */
|
||||||
#define PAYLOAD_ARG_CNT 4U
|
#define PAYLOAD_ARG_CNT 4U
|
||||||
|
|
||||||
|
/* Number of arguments for a callback */
|
||||||
|
#define CB_ARG_CNT 4
|
||||||
|
|
||||||
|
/* Payload size (consists of callback API ID + arguments) */
|
||||||
|
#define CB_PAYLOAD_SIZE (CB_ARG_CNT + 1)
|
||||||
|
|
||||||
|
#define ZYNQMP_PM_MAX_QOS 100U
|
||||||
|
|
||||||
|
/* Node capabilities */
|
||||||
|
#define ZYNQMP_PM_CAPABILITY_ACCESS 0x1U
|
||||||
|
#define ZYNQMP_PM_CAPABILITY_CONTEXT 0x2U
|
||||||
|
#define ZYNQMP_PM_CAPABILITY_WAKEUP 0x4U
|
||||||
|
#define ZYNQMP_PM_CAPABILITY_POWER 0x8U
|
||||||
|
|
||||||
enum pm_api_id {
|
enum pm_api_id {
|
||||||
PM_GET_API_VERSION = 1,
|
PM_GET_API_VERSION = 1,
|
||||||
|
PM_REQUEST_NODE = 13,
|
||||||
|
PM_RELEASE_NODE,
|
||||||
|
PM_SET_REQUIREMENT,
|
||||||
|
PM_RESET_ASSERT = 17,
|
||||||
|
PM_RESET_GET_STATUS,
|
||||||
|
PM_PM_INIT_FINALIZE = 21,
|
||||||
|
PM_GET_CHIPID = 24,
|
||||||
PM_IOCTL = 34,
|
PM_IOCTL = 34,
|
||||||
PM_QUERY_DATA,
|
PM_QUERY_DATA,
|
||||||
PM_CLOCK_ENABLE,
|
PM_CLOCK_ENABLE,
|
||||||
|
@ -75,6 +98,149 @@ enum pm_query_id {
|
||||||
PM_QID_CLOCK_GET_NUM_CLOCKS = 12,
|
PM_QID_CLOCK_GET_NUM_CLOCKS = 12,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum zynqmp_pm_reset_action {
|
||||||
|
PM_RESET_ACTION_RELEASE,
|
||||||
|
PM_RESET_ACTION_ASSERT,
|
||||||
|
PM_RESET_ACTION_PULSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum zynqmp_pm_reset {
|
||||||
|
ZYNQMP_PM_RESET_START = 1000,
|
||||||
|
ZYNQMP_PM_RESET_PCIE_CFG = ZYNQMP_PM_RESET_START,
|
||||||
|
ZYNQMP_PM_RESET_PCIE_BRIDGE,
|
||||||
|
ZYNQMP_PM_RESET_PCIE_CTRL,
|
||||||
|
ZYNQMP_PM_RESET_DP,
|
||||||
|
ZYNQMP_PM_RESET_SWDT_CRF,
|
||||||
|
ZYNQMP_PM_RESET_AFI_FM5,
|
||||||
|
ZYNQMP_PM_RESET_AFI_FM4,
|
||||||
|
ZYNQMP_PM_RESET_AFI_FM3,
|
||||||
|
ZYNQMP_PM_RESET_AFI_FM2,
|
||||||
|
ZYNQMP_PM_RESET_AFI_FM1,
|
||||||
|
ZYNQMP_PM_RESET_AFI_FM0,
|
||||||
|
ZYNQMP_PM_RESET_GDMA,
|
||||||
|
ZYNQMP_PM_RESET_GPU_PP1,
|
||||||
|
ZYNQMP_PM_RESET_GPU_PP0,
|
||||||
|
ZYNQMP_PM_RESET_GPU,
|
||||||
|
ZYNQMP_PM_RESET_GT,
|
||||||
|
ZYNQMP_PM_RESET_SATA,
|
||||||
|
ZYNQMP_PM_RESET_ACPU3_PWRON,
|
||||||
|
ZYNQMP_PM_RESET_ACPU2_PWRON,
|
||||||
|
ZYNQMP_PM_RESET_ACPU1_PWRON,
|
||||||
|
ZYNQMP_PM_RESET_ACPU0_PWRON,
|
||||||
|
ZYNQMP_PM_RESET_APU_L2,
|
||||||
|
ZYNQMP_PM_RESET_ACPU3,
|
||||||
|
ZYNQMP_PM_RESET_ACPU2,
|
||||||
|
ZYNQMP_PM_RESET_ACPU1,
|
||||||
|
ZYNQMP_PM_RESET_ACPU0,
|
||||||
|
ZYNQMP_PM_RESET_DDR,
|
||||||
|
ZYNQMP_PM_RESET_APM_FPD,
|
||||||
|
ZYNQMP_PM_RESET_SOFT,
|
||||||
|
ZYNQMP_PM_RESET_GEM0,
|
||||||
|
ZYNQMP_PM_RESET_GEM1,
|
||||||
|
ZYNQMP_PM_RESET_GEM2,
|
||||||
|
ZYNQMP_PM_RESET_GEM3,
|
||||||
|
ZYNQMP_PM_RESET_QSPI,
|
||||||
|
ZYNQMP_PM_RESET_UART0,
|
||||||
|
ZYNQMP_PM_RESET_UART1,
|
||||||
|
ZYNQMP_PM_RESET_SPI0,
|
||||||
|
ZYNQMP_PM_RESET_SPI1,
|
||||||
|
ZYNQMP_PM_RESET_SDIO0,
|
||||||
|
ZYNQMP_PM_RESET_SDIO1,
|
||||||
|
ZYNQMP_PM_RESET_CAN0,
|
||||||
|
ZYNQMP_PM_RESET_CAN1,
|
||||||
|
ZYNQMP_PM_RESET_I2C0,
|
||||||
|
ZYNQMP_PM_RESET_I2C1,
|
||||||
|
ZYNQMP_PM_RESET_TTC0,
|
||||||
|
ZYNQMP_PM_RESET_TTC1,
|
||||||
|
ZYNQMP_PM_RESET_TTC2,
|
||||||
|
ZYNQMP_PM_RESET_TTC3,
|
||||||
|
ZYNQMP_PM_RESET_SWDT_CRL,
|
||||||
|
ZYNQMP_PM_RESET_NAND,
|
||||||
|
ZYNQMP_PM_RESET_ADMA,
|
||||||
|
ZYNQMP_PM_RESET_GPIO,
|
||||||
|
ZYNQMP_PM_RESET_IOU_CC,
|
||||||
|
ZYNQMP_PM_RESET_TIMESTAMP,
|
||||||
|
ZYNQMP_PM_RESET_RPU_R50,
|
||||||
|
ZYNQMP_PM_RESET_RPU_R51,
|
||||||
|
ZYNQMP_PM_RESET_RPU_AMBA,
|
||||||
|
ZYNQMP_PM_RESET_OCM,
|
||||||
|
ZYNQMP_PM_RESET_RPU_PGE,
|
||||||
|
ZYNQMP_PM_RESET_USB0_CORERESET,
|
||||||
|
ZYNQMP_PM_RESET_USB1_CORERESET,
|
||||||
|
ZYNQMP_PM_RESET_USB0_HIBERRESET,
|
||||||
|
ZYNQMP_PM_RESET_USB1_HIBERRESET,
|
||||||
|
ZYNQMP_PM_RESET_USB0_APB,
|
||||||
|
ZYNQMP_PM_RESET_USB1_APB,
|
||||||
|
ZYNQMP_PM_RESET_IPI,
|
||||||
|
ZYNQMP_PM_RESET_APM_LPD,
|
||||||
|
ZYNQMP_PM_RESET_RTC,
|
||||||
|
ZYNQMP_PM_RESET_SYSMON,
|
||||||
|
ZYNQMP_PM_RESET_AFI_FM6,
|
||||||
|
ZYNQMP_PM_RESET_LPD_SWDT,
|
||||||
|
ZYNQMP_PM_RESET_FPD,
|
||||||
|
ZYNQMP_PM_RESET_RPU_DBG1,
|
||||||
|
ZYNQMP_PM_RESET_RPU_DBG0,
|
||||||
|
ZYNQMP_PM_RESET_DBG_LPD,
|
||||||
|
ZYNQMP_PM_RESET_DBG_FPD,
|
||||||
|
ZYNQMP_PM_RESET_APLL,
|
||||||
|
ZYNQMP_PM_RESET_DPLL,
|
||||||
|
ZYNQMP_PM_RESET_VPLL,
|
||||||
|
ZYNQMP_PM_RESET_IOPLL,
|
||||||
|
ZYNQMP_PM_RESET_RPLL,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_0,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_1,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_2,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_3,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_4,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_5,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_6,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_7,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_8,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_9,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_10,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_11,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_12,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_13,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_14,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_15,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_16,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_17,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_18,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_19,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_20,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_21,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_22,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_23,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_24,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_25,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_26,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_27,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_28,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_29,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_30,
|
||||||
|
ZYNQMP_PM_RESET_GPO3_PL_31,
|
||||||
|
ZYNQMP_PM_RESET_RPU_LS,
|
||||||
|
ZYNQMP_PM_RESET_PS_ONLY,
|
||||||
|
ZYNQMP_PM_RESET_PL,
|
||||||
|
ZYNQMP_PM_RESET_PS_PL0,
|
||||||
|
ZYNQMP_PM_RESET_PS_PL1,
|
||||||
|
ZYNQMP_PM_RESET_PS_PL2,
|
||||||
|
ZYNQMP_PM_RESET_PS_PL3,
|
||||||
|
ZYNQMP_PM_RESET_END = ZYNQMP_PM_RESET_PS_PL3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum zynqmp_pm_suspend_reason {
|
||||||
|
SUSPEND_POWER_REQUEST = 201,
|
||||||
|
SUSPEND_ALERT,
|
||||||
|
SUSPEND_SYSTEM_SHUTDOWN,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum zynqmp_pm_request_ack {
|
||||||
|
ZYNQMP_PM_REQUEST_ACK_NO = 1,
|
||||||
|
ZYNQMP_PM_REQUEST_ACK_BLOCKING,
|
||||||
|
ZYNQMP_PM_REQUEST_ACK_NON_BLOCKING,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct zynqmp_pm_query_data - PM query data
|
* struct zynqmp_pm_query_data - PM query data
|
||||||
* @qid: query ID
|
* @qid: query ID
|
||||||
|
@ -91,6 +257,7 @@ struct zynqmp_pm_query_data {
|
||||||
|
|
||||||
struct zynqmp_eemi_ops {
|
struct zynqmp_eemi_ops {
|
||||||
int (*get_api_version)(u32 *version);
|
int (*get_api_version)(u32 *version);
|
||||||
|
int (*get_chipid)(u32 *idcode, u32 *version);
|
||||||
int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out);
|
int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out);
|
||||||
int (*clock_enable)(u32 clock_id);
|
int (*clock_enable)(u32 clock_id);
|
||||||
int (*clock_disable)(u32 clock_id);
|
int (*clock_disable)(u32 clock_id);
|
||||||
|
@ -102,8 +269,25 @@ struct zynqmp_eemi_ops {
|
||||||
int (*clock_setparent)(u32 clock_id, u32 parent_id);
|
int (*clock_setparent)(u32 clock_id, u32 parent_id);
|
||||||
int (*clock_getparent)(u32 clock_id, u32 *parent_id);
|
int (*clock_getparent)(u32 clock_id, u32 *parent_id);
|
||||||
int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out);
|
int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out);
|
||||||
|
int (*reset_assert)(const enum zynqmp_pm_reset reset,
|
||||||
|
const enum zynqmp_pm_reset_action assert_flag);
|
||||||
|
int (*reset_get_status)(const enum zynqmp_pm_reset reset, u32 *status);
|
||||||
|
int (*init_finalize)(void);
|
||||||
|
int (*set_suspend_mode)(u32 mode);
|
||||||
|
int (*request_node)(const u32 node,
|
||||||
|
const u32 capabilities,
|
||||||
|
const u32 qos,
|
||||||
|
const enum zynqmp_pm_request_ack ack);
|
||||||
|
int (*release_node)(const u32 node);
|
||||||
|
int (*set_requirement)(const u32 node,
|
||||||
|
const u32 capabilities,
|
||||||
|
const u32 qos,
|
||||||
|
const enum zynqmp_pm_request_ack ack);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1,
|
||||||
|
u32 arg2, u32 arg3, u32 *ret_payload);
|
||||||
|
|
||||||
#if IS_REACHABLE(CONFIG_ARCH_ZYNQMP)
|
#if IS_REACHABLE(CONFIG_ARCH_ZYNQMP)
|
||||||
const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void);
|
const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void);
|
||||||
#else
|
#else
|
||||||
|
|
Loading…
Reference in New Issue