summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-zynqmp
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-zynqmp')
-rw-r--r--arch/arm/mach-zynqmp/Kconfig13
-rw-r--r--arch/arm/mach-zynqmp/firmware-zynqmp.c203
-rw-r--r--arch/arm/mach-zynqmp/include/mach/debug_ll.h31
-rw-r--r--arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h80
-rw-r--r--arch/arm/mach-zynqmp/include/mach/zynqmp-bbu.h21
-rw-r--r--arch/arm/mach-zynqmp/zynqmp-bbu.c2
-rw-r--r--arch/arm/mach-zynqmp/zynqmp.c6
7 files changed, 214 insertions, 142 deletions
diff --git a/arch/arm/mach-zynqmp/Kconfig b/arch/arm/mach-zynqmp/Kconfig
index 78cb901653..23d04926e6 100644
--- a/arch/arm/mach-zynqmp/Kconfig
+++ b/arch/arm/mach-zynqmp/Kconfig
@@ -1,5 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-or-later
-if ARCH_ZYNQMP
+
+menu "ZynqMP Features"
+ depends on ARCH_ZYNQMP
+
+config MACH_XILINX_ZCU102
+ bool "Xilinx Zynq UltraScale+ MPSoC ZCU102"
+ select ARM_USE_COMPRESSED_DTB
+ help
+ Say Y here if you are using the Xilinx Zynq UltraScale+ MPSoC ZCU102
+ evaluation board.
config MACH_XILINX_ZCU104
bool "Xilinx Zynq UltraScale+ MPSoC ZCU104"
@@ -13,4 +22,4 @@ config MACH_XILINX_ZCU106
Say Y here if you are using the Xilinx Zynq UltraScale+ MPSoC ZCU106
evaluation board.
-endif
+endmenu
diff --git a/arch/arm/mach-zynqmp/firmware-zynqmp.c b/arch/arm/mach-zynqmp/firmware-zynqmp.c
index c23b434031..039a46e767 100644
--- a/arch/arm/mach-zynqmp/firmware-zynqmp.c
+++ b/arch/arm/mach-zynqmp/firmware-zynqmp.c
@@ -14,9 +14,17 @@
#include <common.h>
#include <init.h>
+#include <driver.h>
+#include <param.h>
#include <linux/arm-smccc.h>
-#include <mach/firmware-zynqmp.h>
+#include <mach/zynqmp/firmware-zynqmp.h>
+
+struct zynqmp_fw {
+ struct device *dev;
+ u32 ggs[4];
+ u32 pggs[4];
+};
#define ZYNQMP_TZ_VERSION(MAJOR, MINOR) ((MAJOR << 16) | MINOR)
@@ -40,6 +48,7 @@ enum pm_ret_status {
enum pm_api_id {
PM_GET_API_VERSION = 1,
+ PM_MMIO_WRITE = 19,
PM_FPGA_LOAD = 22,
PM_FPGA_GET_STATUS,
PM_IOCTL = 34,
@@ -504,6 +513,130 @@ static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2,
}
/**
+ * zynqmp_pm_set_sd_tapdelay() - Set tap delay for the SD device
+ *
+ * @node_id: Node ID of the device
+ * @type: Type of tap delay to set (input/output)
+ * @value: Value to set fot the tap delay
+ *
+ * This function sets input/output tap delay for the SD device.
+ *
+ * Return: Returns status, either success or error+reason
+ */
+int zynqmp_pm_set_sd_tapdelay(u32 node_id, u32 type, u32 value)
+{
+ u32 reg = (type == PM_TAPDELAY_INPUT) ? SD_ITAPDLY : SD_OTAPDLYSEL;
+ u32 mask = (node_id == NODE_SD_0) ? GENMASK(15, 0) : GENMASK(31, 16);
+
+ if (value) {
+ return zynqmp_pm_invoke_fn(PM_IOCTL, node_id,
+ IOCTL_SET_SD_TAPDELAY,
+ type, value, NULL);
+ }
+
+ /*
+ * Work around completely misdesigned firmware API on Xilinx ZynqMP.
+ * The IOCTL_SET_SD_TAPDELAY firmware call allows the caller to only
+ * ever set IOU_SLCR SD_ITAPDLY Register SD0_ITAPDLYENA/SD1_ITAPDLYENA
+ * bits, but there is no matching call to clear those bits. If those
+ * bits are not cleared, SDMMC tuning may fail.
+ *
+ * Luckily, there are PM_MMIO_READ/PM_MMIO_WRITE calls which seem to
+ * allow complete unrestricted access to all address space, including
+ * IOU_SLCR SD_ITAPDLY Register and all the other registers, access
+ * to which was supposed to be protected by the current firmware API.
+ *
+ * Use PM_MMIO_READ/PM_MMIO_WRITE to re-implement the missing counter
+ * part of IOCTL_SET_SD_TAPDELAY which clears SDx_ITAPDLYENA bits.
+ */
+ return zynqmp_pm_invoke_fn(PM_MMIO_WRITE, reg, mask, 0, 0, NULL);
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_set_sd_tapdelay);
+
+/**
+ * zynqmp_pm_sd_dll_reset() - Reset DLL logic
+ *
+ * @node_id: Node ID of the device
+ * @type: Reset type
+ *
+ * This function resets DLL logic for the SD device.
+ *
+ * Return: Returns status, either success or error+reason
+ */
+int zynqmp_pm_sd_dll_reset(u32 node_id, u32 type)
+{
+ return zynqmp_pm_invoke_fn(PM_IOCTL, node_id, IOCTL_SD_DLL_RESET,
+ type, 0, NULL);
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_sd_dll_reset);
+
+/*
+ * zynqmp_pm_write_ggs() - PM API for writing global general storage (ggs)
+ * @index: GGS register index
+ * @value: Register value to be written
+ *
+ * This function writes value to GGS register.
+ *
+ * Return: Returns status, either success or error+reason
+ */
+int zynqmp_pm_write_ggs(u32 index, u32 value)
+{
+ return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_WRITE_GGS,
+ index, value, NULL);
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_write_ggs);
+
+/**
+ * zynqmp_pm_read_ggs() - PM API for reading global general storage (ggs)
+ * @index: GGS register index
+ * @value: Register value to be written
+ *
+ * This function returns GGS register value.
+ *
+ * Return: Returns status, either success or error+reason
+ */
+int zynqmp_pm_read_ggs(u32 index, u32 *value)
+{
+ return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_READ_GGS,
+ index, 0, value);
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_read_ggs);
+
+/**
+ * zynqmp_pm_write_pggs() - PM API for writing persistent global general
+ * storage (pggs)
+ * @index: PGGS register index
+ * @value: Register value to be written
+ *
+ * This function writes value to PGGS register.
+ *
+ * Return: Returns status, either success or error+reason
+ */
+int zynqmp_pm_write_pggs(u32 index, u32 value)
+{
+ return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_WRITE_PGGS, index, value,
+ NULL);
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_write_pggs);
+
+/**
+ * zynqmp_pm_read_pggs() - PM API for reading persistent global general
+ * storage (pggs)
+ * @index: PGGS register index
+ * @value: Register value to be written
+ *
+ * This function returns PGGS register value.
+ *
+ * Return: Returns status, either success or error+reason
+ */
+int zynqmp_pm_read_pggs(u32 index, u32 *value)
+{
+ return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_READ_PGGS, index, 0,
+ value);
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_read_pggs);
+
+/**
* zynqmp_pm_fpga_load - Perform the fpga load
* @address: Address to write to
* @size: pl bitstream size
@@ -576,12 +709,59 @@ const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void)
}
EXPORT_SYMBOL_GPL(zynqmp_pm_get_eemi_ops);
+static bool parse_reg(const char *reg, unsigned *idx)
+{
+ bool pggs = reg[0] == 'p';
+ kstrtouint(reg + pggs + sizeof("ggs") - 1, 10, idx);
+ return pggs;
+}
-static int zynqmp_firmware_probe(struct device_d *dev)
+static int ggs_set(struct param_d *p, void *_val)
+{
+ u32 *val = _val;
+ unsigned idx;
+
+ if (parse_reg(p->name, &idx))
+ return zynqmp_pm_write_pggs(idx, *val);
+ else
+ return zynqmp_pm_write_ggs(idx, *val);
+}
+static int ggs_get(struct param_d *p, void *_val)
{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ u32 *val = _val;
+ unsigned idx;
+ int ret;
+
+ if (parse_reg(p->name, &idx))
+ ret = zynqmp_pm_read_pggs(idx, ret_payload);
+ else
+ ret = zynqmp_pm_read_ggs(idx, ret_payload);
+
+ if (ret)
+ return ret;
+
+ *val = ret_payload[1];
+
+ return 0;
+}
+
+static inline void dev_add_param_ggs(struct zynqmp_fw *fw, const char *str, u32 *value)
+{
+ dev_add_param_uint32(fw->dev, str, ggs_set, ggs_get, value, "0x%x", value);
+}
+
+static int zynqmp_firmware_probe(struct device *dev)
+{
+
+ struct zynqmp_fw *fw;
int ret;
- ret = get_set_conduit_method(dev->device_node);
+ fw = xzalloc(sizeof(*fw));
+
+ dev_add_alias(dev, "zynqmp_fw");
+
+ ret = get_set_conduit_method(dev->of_node);
if (ret)
goto out;
@@ -619,7 +799,19 @@ static int zynqmp_firmware_probe(struct device_d *dev)
dev_dbg(dev, "Trustzone version v%d.%d\n",
pm_tz_version >> 16, pm_tz_version & 0xFFFF);
- of_platform_populate(dev->device_node, NULL, dev);
+ of_platform_populate(dev->of_node, NULL, dev);
+
+ fw->dev = dev;
+
+ dev_add_param_ggs(fw, "ggs0", &fw->ggs[0]);
+ dev_add_param_ggs(fw, "ggs1", &fw->ggs[1]);
+ dev_add_param_ggs(fw, "ggs2", &fw->ggs[2]);
+ dev_add_param_ggs(fw, "ggs3", &fw->ggs[3]);
+
+ dev_add_param_ggs(fw, "pggs0", &fw->pggs[0]);
+ dev_add_param_ggs(fw, "pggs1", &fw->pggs[1]);
+ dev_add_param_ggs(fw, "pggs2", &fw->pggs[2]);
+ dev_add_param_ggs(fw, "pggs3", &fw->pggs[3]);
out:
if (ret)
do_fw_call = do_fw_call_fail;
@@ -630,8 +822,9 @@ static struct of_device_id zynqmp_firmware_id_table[] = {
{ .compatible = "xlnx,zynqmp-firmware", },
{}
};
+MODULE_DEVICE_TABLE(of, zynqmp_firmware_id_table);
-static struct driver_d zynqmp_firmware_driver = {
+static struct driver zynqmp_firmware_driver = {
.name = "zynqmp_firmware",
.probe = zynqmp_firmware_probe,
.of_compatible = DRV_OF_COMPAT(zynqmp_firmware_id_table),
diff --git a/arch/arm/mach-zynqmp/include/mach/debug_ll.h b/arch/arm/mach-zynqmp/include/mach/debug_ll.h
deleted file mode 100644
index 67571fe2e1..0000000000
--- a/arch/arm/mach-zynqmp/include/mach/debug_ll.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-#ifndef __MACH_DEBUG_LL_H__
-#define __MACH_DEBUG_LL_H__
-
-#include <io.h>
-
-#define ZYNQMP_UART0_BASE 0xFF000000
-#define ZYNQMP_UART1_BASE 0xFF010000
-#define ZYNQMP_UART_BASE ZYNQMP_UART0_BASE
-#define ZYNQMP_DEBUG_LL_UART_BASE ZYNQMP_UART_BASE
-
-#define ZYNQMP_UART_RXTXFIFO 0x30
-#define ZYNQMP_UART_CHANNEL_STS 0x2C
-
-#define ZYNQMP_UART_STS_TFUL (1 << 4)
-#define ZYNQMP_UART_TXDIS (1 << 5)
-
-static inline void PUTC_LL(int c)
-{
- void __iomem *base = (void __iomem *)ZYNQMP_DEBUG_LL_UART_BASE;
-
- if (readl(base) & ZYNQMP_UART_TXDIS)
- return;
-
- while ((readl(base + ZYNQMP_UART_CHANNEL_STS) & ZYNQMP_UART_STS_TFUL) != 0)
- ;
-
- writel(c, base + 0x30);
-}
-
-#endif
diff --git a/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h b/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h
deleted file mode 100644
index a04482237d..0000000000
--- a/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Xilinx Zynq MPSoC Firmware layer
- *
- * Copyright (c) 2018 Thomas Haemmerle <thomas.haemmerle@wolfvision.net>
- *
- * based on Linux xlnx-zynqmp
- *
- * Michal Simek <michal.simek@xilinx.com>
- * Davorin Mista <davorin.mista@aggios.com>
- * Jolly Shah <jollys@xilinx.com>
- * Rajan Vaja <rajanv@xilinx.com>
- */
-
-#ifndef FIRMWARE_ZYNQMP_H_
-#define FIRMWARE_ZYNQMP_H_
-
-#define PAYLOAD_ARG_CNT 4
-
-#define ZYNQMP_PM_VERSION(MAJOR, MINOR) ((MAJOR << 16) | MINOR)
-
-#define ZYNQMP_FPGA_BIT_AUTH_DDR BIT(1)
-#define ZYNQMP_FPGA_BIT_AUTH_OCM BIT(2)
-#define ZYNQMP_FPGA_BIT_ENC_USR_KEY BIT(3)
-#define ZYNQMP_FPGA_BIT_ENC_DEV_KEY BIT(4)
-#define ZYNQMP_FPGA_BIT_ONLY_BIN BIT(5)
-
-#define ZYNQMP_PCAP_STATUS_FPGA_DONE BIT(3)
-
-enum pm_ioctl_id {
- IOCTL_SET_PLL_FRAC_MODE = 8,
- IOCTL_GET_PLL_FRAC_MODE,
- IOCTL_SET_PLL_FRAC_DATA,
- IOCTL_GET_PLL_FRAC_DATA,
-};
-
-enum pm_query_id {
- PM_QID_INVALID,
- PM_QID_CLOCK_GET_NAME,
- PM_QID_CLOCK_GET_TOPOLOGY,
- PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS,
- PM_QID_CLOCK_GET_PARENTS,
- PM_QID_CLOCK_GET_ATTRIBUTES,
- PM_QID_CLOCK_GET_NUM_CLOCKS = 12,
-};
-
-/**
- * struct zynqmp_pm_query_data - PM query data
- * @qid: query ID
- * @arg1: Argument 1 of query data
- * @arg2: Argument 2 of query data
- * @arg3: Argument 3 of query data
- */
-struct zynqmp_pm_query_data {
- u32 qid;
- u32 arg1;
- u32 arg2;
- u32 arg3;
-};
-
-struct zynqmp_eemi_ops {
- int (*get_api_version)(u32 *version);
- int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out);
- int (*clock_enable)(u32 clock_id);
- int (*clock_disable)(u32 clock_id);
- int (*clock_getstate)(u32 clock_id, u32 *state);
- int (*clock_setdivider)(u32 clock_id, u32 divider);
- int (*clock_getdivider)(u32 clock_id, u32 *divider);
- int (*clock_setrate)(u32 clock_id, u64 rate);
- int (*clock_getrate)(u32 clock_id, u64 *rate);
- int (*clock_setparent)(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 (*fpga_getstatus)(u32 *status);
- int (*fpga_load)(u64 address, u32 size, u32 flags);
-};
-
-const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void);
-
-#endif /* FIRMWARE_ZYNQMP_H_ */
diff --git a/arch/arm/mach-zynqmp/include/mach/zynqmp-bbu.h b/arch/arm/mach-zynqmp/include/mach/zynqmp-bbu.h
deleted file mode 100644
index 8502791ee0..0000000000
--- a/arch/arm/mach-zynqmp/include/mach/zynqmp-bbu.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) 2020 Michael Tretter <m.tretter@pengutronix.de>
- */
-#ifndef __MACH_ZYNQMP_BBU_H
-#define __MACH_ZYNQMP_BBU_H
-
-#include <bbu.h>
-
-#ifdef CONFIG_BAREBOX_UPDATE
-int zynqmp_bbu_register_handler(const char *name, char *devicefile,
- unsigned long flags);
-#else
-static int zynqmp_bbu_register_handler(const char *name, char *devicefile,
- unsigned long flags)
-{
- return 0;
-};
-#endif
-
-#endif /* __MACH_ZYNQMP_BBU_H */
diff --git a/arch/arm/mach-zynqmp/zynqmp-bbu.c b/arch/arm/mach-zynqmp/zynqmp-bbu.c
index 7ac8c2b8a9..3c5e2fe885 100644
--- a/arch/arm/mach-zynqmp/zynqmp-bbu.c
+++ b/arch/arm/mach-zynqmp/zynqmp-bbu.c
@@ -4,7 +4,7 @@
*/
#include <common.h>
-#include <mach/zynqmp-bbu.h>
+#include <mach/zynqmp/zynqmp-bbu.h>
int zynqmp_bbu_register_handler(const char *name, char *devicefile,
unsigned long flags)
diff --git a/arch/arm/mach-zynqmp/zynqmp.c b/arch/arm/mach-zynqmp/zynqmp.c
index 610d4bba6e..f86bda1693 100644
--- a/arch/arm/mach-zynqmp/zynqmp.c
+++ b/arch/arm/mach-zynqmp/zynqmp.c
@@ -147,9 +147,11 @@ static int zynqmp_init(void)
enum bootsource boot_src;
int boot_instance;
+ if (!of_machine_is_compatible("xlnx,zynqmp"))
+ return 0;
+
zynqmp_get_bootsource(&boot_src, &boot_instance);
- bootsource_set(boot_src);
- bootsource_set_instance(boot_instance);
+ bootsource_set_raw(boot_src, boot_instance);
reset_source_set(zynqmp_get_reset_src());