summaryrefslogtreecommitdiff
path: root/drivers/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/Kconfig2
-rw-r--r--drivers/firmware/firmware-zynqmp.c247
-rw-r--r--drivers/firmware/psci.c4
-rw-r--r--drivers/firmware/scmi/Kconfig3
-rw-r--r--drivers/firmware/scmi/scmi_agent-uclass.c10
5 files changed, 247 insertions, 19 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 220de731950..f524f741e54 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -14,7 +14,7 @@ config ARM_PSCI_FW
select FIRMWARE
config TI_SCI_PROTOCOL
- tristate "TI System Control Interface (TISCI) Message Protocol"
+ bool "TI System Control Interface (TISCI) Message Protocol"
depends on K3_SEC_PROXY
select DEVRES
select FIRMWARE
diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c
index fb583580ebe..fae66ccb3d8 100644
--- a/drivers/firmware/firmware-zynqmp.c
+++ b/drivers/firmware/firmware-zynqmp.c
@@ -3,10 +3,11 @@
* Xilinx Zynq MPSoC Firmware driver
*
* Copyright (C) 2018-2019 Xilinx, Inc.
- * Copyright (C) 2022 - 2025, Advanced Micro Devices, Inc.
+ * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
*/
#include <asm/arch/hardware.h>
+#include <asm/arch/sys_proto.h>
#include <asm/io.h>
#include <cpu_func.h>
#include <dm.h>
@@ -16,11 +17,12 @@
#include <zynqmp_firmware.h>
#include <asm/cache.h>
#include <asm/ptrace.h>
+#include <asm/system.h>
#include <linux/bitfield.h>
+#include <linux/delay.h>
#if defined(CONFIG_ZYNQMP_IPI)
#include <mailbox.h>
-#include <asm/arch/sys_proto.h>
#define PMUFW_PAYLOAD_ARG_CNT 8
@@ -174,28 +176,158 @@ unsigned int zynqmp_firmware_version(void)
};
#if defined(CONFIG_ARCH_VERSAL2)
-int zynqmp_pm_ufs_get_txrx_cfgrdy(u32 *value)
+/*
+ * Poll the M-PHY TX/RX config-ready status until it settles or @timeout_us
+ * elapses. Legacy EEMI firmware only offers the per-read status primitive, so
+ * the poll loop lives here rather than in the UFS driver; the timeout budget is
+ * owned by the caller.
+ */
+int zynqmp_pm_wait_mphy_tx_rx_config_ready(u32 timeout_us)
{
- *value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_TX_RX_CFG_RDY);
- return 0;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ while (timeout_us--) {
+ ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR,
+ IOCTL_READ_REG, TXRX_CFGRDY_OFFSET, 0, 0,
+ 0, ret_payload);
+ if (ret)
+ return ret;
+
+ if (!(ret_payload[1] & TX_RX_CFG_RDY_MASK))
+ return 0;
+
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
}
-int zynqmp_pm_ufs_sram_csr_read(u32 *value)
+int zynqmp_pm_wait_sram_init_done(u32 timeout_us)
{
- *value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR);
- return 0;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ while (timeout_us--) {
+ ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR,
+ IOCTL_READ_REG, SRAM_CSR_OFFSET, 0, 0,
+ 0, ret_payload);
+ if (ret)
+ return ret;
+
+ if (ret_payload[1] & SRAM_CSR_INIT_DONE_MASK)
+ return 0;
+
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
}
-int zynqmp_pm_ufs_sram_csr_write(u32 *value)
+int zynqmp_pm_set_sram_bypass(void)
{
- writel(*value, PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR);
- return 0;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ u32 sram_csr;
+ int ret;
+
+ ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_MASK_WRITE_REG);
+ if (ret) {
+ printf("%s: IOCTL_MASK_WRITE_REG is not supported : %d\n"
+ , __func__, ret);
+ return 0;
+ }
+
+ ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR,
+ IOCTL_READ_REG, SRAM_CSR_OFFSET, 0, 0,
+ 0, ret_payload);
+ if (ret)
+ return ret;
+
+ sram_csr = ret_payload[1];
+ sram_csr &= ~SRAM_CSR_EXT_LD_DONE_MASK;
+ sram_csr |= SRAM_CSR_BYPASS_MASK;
+
+ return xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR,
+ IOCTL_MASK_WRITE_REG, SRAM_CSR_OFFSET,
+ GENMASK(2, 1), sram_csr, 0, NULL);
}
-int zynqmp_pm_ufs_cal_reg(u32 *value)
+int zynqmp_pm_get_ufs_calibration_values(u32 *value)
{
- *value = readl(PMXC_EFUSE_CACHE_BASE_ADDRESS + PMXC_UFS_CAL_1_OFFSET);
- return 0;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ if (!value)
+ return -EINVAL;
+
+ ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_READ_REG);
+ if (ret) {
+ printf("%s: IOCTL_READ_REG is not supported : %d\n"
+ , __func__, ret);
+ return 0;
+ }
+
+ ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_EFUSE_CACHE,
+ IOCTL_READ_REG, UFS_CAL_1_OFFSET, 0, 0,
+ 0, ret_payload);
+ if (ret)
+ return ret;
+
+ *value = ret_payload[1];
+
+ return ret;
+}
+#endif /* CONFIG_ARCH_VERSAL2 */
+
+#if defined(CONFIG_ARCH_VERSAL) || defined(CONFIG_ARCH_VERSAL2)
+u32 zynqmp_pm_get_pmc_global_pggs_reg(u32 reg_addr)
+{
+ int ret;
+ u32 value = 0;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+
+ if (reg_addr == PMC_GLOBAL_PGGS3_REG) {
+ value = 0;
+ } else if (reg_addr == PMC_GLOBAL_PGGS4_REG) {
+ value = 1;
+ } else {
+ printf("%s: not supported pggs register 0x%x\n",
+ __func__, reg_addr);
+ return 0;
+ }
+
+ ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_READ_PGGS);
+ if (ret) {
+ ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_READ_REG);
+ if (ret) {
+ printf("%s: IOCTL_READ_REG is not supported : %d\n"
+ , __func__, ret);
+ return 0;
+ }
+
+ /* find node ID from the pggs3 offset */
+ value = PM_REG_PGGS3 + value;
+
+ ret = xilinx_pm_request(PM_IOCTL, value,
+ IOCTL_READ_REG, 0, 0, 0, 0,
+ ret_payload);
+ if (ret) {
+ printf("%s: node 0x%x get pggs register failed\n",
+ __func__, value);
+ return 0;
+ }
+ } else {
+ ret = xilinx_pm_request(PM_IOCTL, PMC_GLOBAL_PGGS3_REG_NODE,
+ IOCTL_READ_PGGS, value, 0, 0, 0,
+ ret_payload);
+ if (ret) {
+ printf("%s: node 0x%x get pggs register failed\n",
+ __func__, PMC_GLOBAL_PGGS3_REG_NODE);
+ return 0;
+ }
+ }
+
+ return ret_payload[1];
}
#endif
@@ -274,6 +406,93 @@ u32 zynqmp_pm_get_pmc_multi_boot_reg(void)
}
#endif
+#if defined(CONFIG_ARCH_VERSAL)
+u32 versal_pmc_multi_boot(void)
+{
+ /* At EL3 the SMC path to firmware is unavailable, read directly */
+ if (current_el() == 3)
+ return versal_multi_boot_reg();
+
+ return zynqmp_pm_get_pmc_multi_boot_reg() & PMC_MULTI_BOOT_MASK;
+}
+
+u8 versal_get_bootmode(void)
+{
+ u32 reg;
+
+ /* At EL3 the SMC path to firmware is unavailable, read directly */
+ if (current_el() == 3)
+ reg = versal_bootmode_reg();
+ else
+ reg = zynqmp_pm_get_bootmode_reg();
+
+ if (reg >> BOOT_MODE_ALT_SHIFT)
+ reg >>= BOOT_MODE_ALT_SHIFT;
+
+ return reg & BOOT_MODES_MASK;
+}
+#endif
+
+#if defined(CONFIG_ARCH_VERSAL_NET)
+u8 versal_net_get_bootmode(void)
+{
+ u32 reg;
+
+ /* At EL3 the SMC path to firmware is unavailable, read directly */
+ if (current_el() == 3)
+ reg = versal_net_bootmode_reg();
+ else
+ reg = zynqmp_pm_get_bootmode_reg();
+
+ if (reg >> BOOT_MODE_ALT_SHIFT)
+ reg >>= BOOT_MODE_ALT_SHIFT;
+
+ return reg & BOOT_MODES_MASK;
+}
+#endif
+
+#if defined(CONFIG_ARCH_ZYNQMP)
+int zynqmp_mmio_write(const u32 address, const u32 mask, const u32 value)
+{
+ /* At EL3 or in SPL the firmware (SMC) path is unavailable */
+ if (IS_ENABLED(CONFIG_XPL_BUILD) || current_el() == 3)
+ return zynqmp_mmio_rawwrite(address, mask, value);
+
+ return xilinx_pm_request(PM_MMIO_WRITE, address, mask, value,
+ 0, 0, 0, NULL);
+}
+
+int zynqmp_mmio_read(const u32 address, u32 *value)
+{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ if (!value)
+ return -EINVAL;
+
+ /* At EL3 or in SPL the firmware (SMC) path is unavailable */
+ if (IS_ENABLED(CONFIG_XPL_BUILD) || current_el() == 3)
+ return zynqmp_mmio_rawread(address, value);
+
+ ret = xilinx_pm_request(PM_MMIO_READ, address, 0, 0, 0, 0, 0,
+ ret_payload);
+ *value = ret_payload[1];
+
+ return ret;
+}
+#endif
+
+#if defined(CONFIG_ARCH_VERSAL2)
+u32 versal2_pmc_multi_boot(void)
+{
+ /* At EL3 the SMC path to firmware is unavailable, read directly */
+ if (current_el() == 3)
+ return versal2_multi_boot_reg();
+
+ return zynqmp_pm_get_pmc_multi_boot_reg() & PMC_MULTI_BOOT_MASK;
+}
+#endif
+
int zynqmp_pm_feature(const u32 api_id)
{
int ret;
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index b6838a244d2..2e3223e1c32 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -186,10 +186,6 @@ static int psci_bind(struct udevice *dev)
NULL);
if (ret)
pr_debug("PSCI System Reset was not bound.\n");
- if (IS_ENABLED(CONFIG_SYSRESET_QCOM_PSCI) &&
- device_bind_driver(dev, "qcom_psci-sysreset",
- "qcom_psci-sysreset", NULL))
- pr_debug("QCOM PSCI System Reset was not bound.\n");
}
/* From PSCI v1.0 onward we can discover services through ARM_SMCCC_FEATURE */
diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig
index cd912ebe409..64bc9aef5af 100644
--- a/drivers/firmware/scmi/Kconfig
+++ b/drivers/firmware/scmi/Kconfig
@@ -46,6 +46,9 @@ config SCMI_AGENT_OPTEE
config SCMI_ID_VENDOR_80
bool
+config SCMI_ID_VENDOR_81
+ bool
+
config SCMI_ID_VENDOR_82
bool
diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c
index cd458a7f458..9159e59385b 100644
--- a/drivers/firmware/scmi/scmi_agent-uclass.c
+++ b/drivers/firmware/scmi/scmi_agent-uclass.c
@@ -116,6 +116,11 @@ struct udevice *scmi_get_protocol(struct udevice *dev,
proto = priv->vendor_dev_80;
break;
#endif
+#if IS_ENABLED(CONFIG_SCMI_ID_VENDOR_81)
+ case SCMI_PROTOCOL_ID_VENDOR_81:
+ proto = priv->vendor_dev_81;
+ break;
+#endif
#if IS_ENABLED(CONFIG_SCMI_ID_VENDOR_82)
case SCMI_PROTOCOL_ID_VENDOR_82:
proto = priv->vendor_dev_82;
@@ -189,6 +194,11 @@ static int scmi_add_protocol(struct udevice *dev,
priv->vendor_dev_80 = proto;
break;
#endif
+#if IS_ENABLED(CONFIG_SCMI_ID_VENDOR_81)
+ case SCMI_PROTOCOL_ID_VENDOR_81:
+ priv->vendor_dev_81 = proto;
+ break;
+#endif
#if IS_ENABLED(CONFIG_SCMI_ID_VENDOR_82)
case SCMI_PROTOCOL_ID_VENDOR_82:
priv->vendor_dev_82 = proto;