summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/core/device.c5
-rw-r--r--drivers/cpu/Kconfig9
-rw-r--r--drivers/cpu/Makefile1
-rw-r--r--drivers/cpu/cpu-uclass.c30
-rw-r--r--drivers/cpu/microblaze_cpu.c180
-rw-r--r--drivers/firmware/firmware-zynqmp.c23
-rw-r--r--drivers/net/xilinx_axi_emac.c38
-rw-r--r--drivers/net/xilinx_emaclite.c43
-rw-r--r--drivers/pinctrl/pinctrl-zynqmp.c9
-rw-r--r--drivers/serial/Kconfig4
-rw-r--r--drivers/soc/soc_xilinx_zynqmp.c286
-rw-r--r--drivers/timer/Kconfig9
-rw-r--r--drivers/timer/Makefile1
-rw-r--r--drivers/timer/xilinx-timer.c82
14 files changed, 683 insertions, 37 deletions
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 3199d6a1b7..3d7fbfe073 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -328,13 +328,8 @@ static void *alloc_priv(int size, uint flags)
* within this range at the start. The driver can then
* use normal flush-after-write, invalidate-before-read
* procedures.
- *
- * TODO(sjg@chromium.org): Drop this microblaze
- * exception.
*/
-#ifndef CONFIG_MICROBLAZE
flush_dcache_range((ulong)priv, (ulong)priv + size);
-#endif
}
} else {
priv = calloc(1, size);
diff --git a/drivers/cpu/Kconfig b/drivers/cpu/Kconfig
index 789728167c..21874335c8 100644
--- a/drivers/cpu/Kconfig
+++ b/drivers/cpu/Kconfig
@@ -19,3 +19,12 @@ config CPU_RISCV
depends on CPU && RISCV
help
Support CPU cores for RISC-V architecture.
+
+config CPU_MICROBLAZE
+ bool "Enable Microblaze CPU driver"
+ depends on CPU && MICROBLAZE
+ select EVENT
+ select DM_EVENT
+ select XILINX_MICROBLAZE0_PVR
+ help
+ Support CPU cores for Microblaze architecture.
diff --git a/drivers/cpu/Makefile b/drivers/cpu/Makefile
index c8532637ca..20884b1795 100644
--- a/drivers/cpu/Makefile
+++ b/drivers/cpu/Makefile
@@ -11,4 +11,5 @@ obj-$(CONFIG_ARCH_IMX8) += imx8_cpu.o
obj-$(CONFIG_ARCH_AT91) += at91_cpu.o
obj-$(CONFIG_CPU_MPC83XX) += mpc83xx_cpu.o
obj-$(CONFIG_CPU_RISCV) += riscv_cpu.o
+obj-$(CONFIG_CPU_MICROBLAZE) += microblaze_cpu.o
obj-$(CONFIG_SANDBOX) += cpu_sandbox.o
diff --git a/drivers/cpu/cpu-uclass.c b/drivers/cpu/cpu-uclass.c
index a5cda6a62c..71e5900d70 100644
--- a/drivers/cpu/cpu-uclass.c
+++ b/drivers/cpu/cpu-uclass.c
@@ -14,6 +14,9 @@
#include <dm/lists.h>
#include <dm/root.h>
#include <linux/err.h>
+#include <relocate.h>
+
+DECLARE_GLOBAL_DATA_PTR;
int cpu_probe_all(void)
{
@@ -136,9 +139,36 @@ static int uclass_cpu_init(struct uclass *uc)
return ret;
}
+static int uclass_cpu_post_bind(struct udevice *dev)
+{
+ if (IS_ENABLED(CONFIG_NEEDS_MANUAL_RELOC) &&
+ (gd->flags & GD_FLG_RELOC)) {
+ struct cpu_ops *ops = cpu_get_ops(dev);
+ static int reloc_done;
+
+ if (!reloc_done) {
+ if (ops->get_desc)
+ MANUAL_RELOC(ops->get_desc);
+ if (ops->get_info)
+ MANUAL_RELOC(ops->get_info);
+ if (ops->get_count)
+ MANUAL_RELOC(ops->get_count);
+ if (ops->get_vendor)
+ MANUAL_RELOC(ops->get_vendor);
+ if (ops->is_current)
+ MANUAL_RELOC(ops->is_current);
+
+ reloc_done++;
+ }
+ }
+
+ return 0;
+}
+
UCLASS_DRIVER(cpu) = {
.id = UCLASS_CPU,
.name = "cpu",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.init = uclass_cpu_init,
+ .post_bind = uclass_cpu_post_bind,
};
diff --git a/drivers/cpu/microblaze_cpu.c b/drivers/cpu/microblaze_cpu.c
new file mode 100644
index 0000000000..969a1047e5
--- /dev/null
+++ b/drivers/cpu/microblaze_cpu.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2022, Ovidiu Panait <ovpanait@gmail.com>
+ */
+#include <common.h>
+#include <cpu.h>
+#include <dm.h>
+#include <asm/cpuinfo.h>
+#include <asm/global_data.h>
+#include <asm/pvr.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define update_cpuinfo_pvr(pvr, ci, name) \
+{ \
+ u32 tmp = PVR_##name(pvr); \
+ if (ci != tmp) \
+ printf("PVR value for " #name " does not match static data!\n");\
+ ci = tmp; \
+}
+
+static int microblaze_cpu_probe_all(void *ctx, struct event *event)
+{
+ int ret;
+
+ ret = cpu_probe_all();
+ if (ret)
+ return log_msg_ret("Microblaze cpus probe failed\n", ret);
+
+ return 0;
+}
+EVENT_SPY(EVT_DM_POST_INIT, microblaze_cpu_probe_all);
+
+static void microblaze_set_cpuinfo_pvr(struct microblaze_cpuinfo *ci)
+{
+ u32 pvr[PVR_FULL_COUNT];
+
+ microblaze_get_all_pvrs(pvr);
+
+ update_cpuinfo_pvr(pvr, ci->icache_size, ICACHE_BYTE_SIZE);
+ update_cpuinfo_pvr(pvr, ci->icache_line_length, ICACHE_LINE_LEN);
+
+ update_cpuinfo_pvr(pvr, ci->dcache_size, DCACHE_BYTE_SIZE);
+ update_cpuinfo_pvr(pvr, ci->dcache_line_length, DCACHE_LINE_LEN);
+
+ update_cpuinfo_pvr(pvr, ci->use_mmu, USE_MMU);
+ update_cpuinfo_pvr(pvr, ci->ver_code, VERSION);
+ update_cpuinfo_pvr(pvr, ci->fpga_code, TARGET_FAMILY);
+}
+
+static void microblaze_set_cpuinfo_static(struct udevice *dev,
+ struct microblaze_cpuinfo *ci)
+{
+ const char *hw_ver = CONFIG_XILINX_MICROBLAZE0_HW_VER;
+ const char *fpga_family = CONFIG_XILINX_MICROBLAZE0_FPGA_FAMILY;
+
+ ci->icache_size = dev_read_u32_default(dev, "i-cache-size", 0);
+ ci->icache_line_length = dev_read_u32_default(dev,
+ "i-cache-line-size", 0);
+
+ ci->dcache_size = dev_read_u32_default(dev, "d-cache-size", 0);
+ ci->dcache_line_length = dev_read_u32_default(dev,
+ "d-cache-line-size", 0);
+
+ ci->cpu_freq = dev_read_u32_default(dev, "clock-frequency", 0);
+ ci->addr_size = dev_read_u32_default(dev, "xlnx,addr-size", 32);
+ ci->use_mmu = dev_read_u32_default(dev, "xlnx,use-mmu", 0);
+
+ ci->ver_code = microblaze_lookup_cpu_version_code(hw_ver);
+ ci->fpga_code = microblaze_lookup_fpga_family_code(fpga_family);
+}
+
+static int microblaze_cpu_probe(struct udevice *dev)
+{
+ microblaze_set_cpuinfo_static(dev, gd_cpuinfo());
+
+ if (microblaze_cpu_has_pvr_full())
+ microblaze_set_cpuinfo_pvr(gd_cpuinfo());
+ else
+ debug("No PVR support. Using only static CPU info.\n");
+
+ return 0;
+}
+
+static int microblaze_cpu_get_desc(const struct udevice *dev, char *buf,
+ int size)
+{
+ struct microblaze_cpuinfo *ci = gd_cpuinfo();
+ const char *cpu_ver, *fpga_family;
+ u32 cpu_freq_mhz;
+ int ret;
+
+ cpu_freq_mhz = ci->cpu_freq / 1000000;
+ cpu_ver = microblaze_lookup_cpu_version_string(ci->ver_code);
+ fpga_family = microblaze_lookup_fpga_family_string(ci->fpga_code);
+
+ ret = snprintf(buf, size,
+ "MicroBlaze @ %uMHz, Rev: %s, FPGA family: %s",
+ cpu_freq_mhz, cpu_ver, fpga_family);
+
+ return 0;
+}
+
+static int microblaze_cpu_get_info(const struct udevice *dev,
+ struct cpu_info *info)
+{
+ struct microblaze_cpuinfo *ci = gd_cpuinfo();
+
+ info->cpu_freq = ci->cpu_freq;
+ info->address_width = ci->addr_size;
+
+ if (ci->icache_size || ci->dcache_size)
+ info->features |= BIT(CPU_FEAT_L1_CACHE);
+
+ if (ci->use_mmu)
+ info->features |= BIT(CPU_FEAT_MMU);
+
+ return 0;
+}
+
+static int microblaze_cpu_get_count(const struct udevice *dev)
+{
+ return 1;
+}
+
+static const struct cpu_ops microblaze_cpu_ops = {
+ .get_desc = microblaze_cpu_get_desc,
+ .get_info = microblaze_cpu_get_info,
+ .get_count = microblaze_cpu_get_count,
+};
+
+static const struct udevice_id microblaze_cpu_ids[] = {
+ { .compatible = "xlnx,microblaze-11.0" },
+ { .compatible = "xlnx,microblaze-10.0" },
+ { .compatible = "xlnx,microblaze-9.6" },
+ { .compatible = "xlnx,microblaze-9.5" },
+ { .compatible = "xlnx,microblaze-9.4" },
+ { .compatible = "xlnx,microblaze-9.3" },
+ { .compatible = "xlnx,microblaze-9.2" },
+ { .compatible = "xlnx,microblaze-9.1" },
+ { .compatible = "xlnx,microblaze-9.0" },
+ { .compatible = "xlnx,microblaze-8.50.c" },
+ { .compatible = "xlnx,microblaze-8.50.b" },
+ { .compatible = "xlnx,microblaze-8.50.a" },
+ { .compatible = "xlnx,microblaze-8.40.b" },
+ { .compatible = "xlnx,microblaze-8.40.a" },
+ { .compatible = "xlnx,microblaze-8.30.a" },
+ { .compatible = "xlnx,microblaze-8.20.b" },
+ { .compatible = "xlnx,microblaze-8.20.a" },
+ { .compatible = "xlnx,microblaze-8.10.a" },
+ { .compatible = "xlnx,microblaze-8.00.b" },
+ { .compatible = "xlnx,microblaze-8.00.a" },
+ { .compatible = "xlnx,microblaze-7.30.b" },
+ { .compatible = "xlnx,microblaze-7.30.a" },
+ { .compatible = "xlnx,microblaze-7.20.d" },
+ { .compatible = "xlnx,microblaze-7.20.c" },
+ { .compatible = "xlnx,microblaze-7.20.b" },
+ { .compatible = "xlnx,microblaze-7.20.a" },
+ { .compatible = "xlnx,microblaze-7.10.d" },
+ { .compatible = "xlnx,microblaze-7.10.c" },
+ { .compatible = "xlnx,microblaze-7.10.b" },
+ { .compatible = "xlnx,microblaze-7.10.a" },
+ { .compatible = "xlnx,microblaze-7.00.b" },
+ { .compatible = "xlnx,microblaze-7.00.a" },
+ { .compatible = "xlnx,microblaze-6.00.b" },
+ { .compatible = "xlnx,microblaze-6.00.a" },
+ { .compatible = "xlnx,microblaze-5.00.c" },
+ { .compatible = "xlnx,microblaze-5.00.b" },
+ { .compatible = "xlnx,microblaze-5.00.a" },
+ { }
+};
+
+U_BOOT_DRIVER(microblaze_cpu) = {
+ .name = "microblaze_cpu",
+ .id = UCLASS_CPU,
+ .of_match = microblaze_cpu_ids,
+ .probe = microblaze_cpu_probe,
+ .ops = &microblaze_cpu_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c
index 0f0d2b07c0..b0cd647aa5 100644
--- a/drivers/firmware/firmware-zynqmp.c
+++ b/drivers/firmware/firmware-zynqmp.c
@@ -26,7 +26,7 @@
struct zynqmp_power {
struct mbox_chan tx_chan;
struct mbox_chan rx_chan;
-} zynqmp_power;
+} zynqmp_power = {};
#define NODE_ID_LOCATION 5
@@ -79,6 +79,20 @@ int zynqmp_pmufw_node(u32 id)
return 0;
}
+static int do_pm_probe(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_driver(UCLASS_FIRMWARE,
+ DM_DRIVER_GET(zynqmp_power),
+ &dev);
+ if (ret)
+ debug("%s: Probing device failed: %d\n", __func__, ret);
+
+ return ret;
+}
+
static int ipi_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen)
{
struct zynqmp_ipi_msg msg;
@@ -92,8 +106,11 @@ static int ipi_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen)
res_maxlen > PMUFW_PAYLOAD_ARG_CNT)
return -EINVAL;
- if (!(zynqmp_power.tx_chan.dev) || !(&zynqmp_power.rx_chan.dev))
- return -EINVAL;
+ if (!(zynqmp_power.tx_chan.dev) || !(zynqmp_power.rx_chan.dev)) {
+ ret = do_pm_probe();
+ if (ret)
+ return ret;
+ }
debug("%s, Sending IPI message with ID: 0x%0x\n", __func__, req[0]);
msg.buf = (u32 *)req;
diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c
index a4715735c3..04277b1269 100644
--- a/drivers/net/xilinx_axi_emac.c
+++ b/drivers/net/xilinx_axi_emac.c
@@ -19,6 +19,7 @@
#include <miiphy.h>
#include <wait_bit.h>
#include <linux/delay.h>
+#include <eth_phy.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -295,6 +296,9 @@ static int axiemac_phy_init(struct udevice *dev)
/* Set default MDIO divisor */
writel(XAE_MDIO_DIV_DFT | XAE_MDIO_MC_MDIOEN_MASK, &regs->mdio_mc);
+ if (IS_ENABLED(CONFIG_DM_ETH_PHY))
+ priv->phyaddr = eth_phy_get_addr(dev);
+
if (priv->phyaddr == -1) {
/* Detect the PHY address */
for (i = 31; i >= 0; i--) {
@@ -778,18 +782,29 @@ static int axi_emac_probe(struct udevice *dev)
priv->phy_of_handle = plat->phy_of_handle;
priv->interface = pdata->phy_interface;
- priv->bus = mdio_alloc();
- priv->bus->read = axiemac_miiphy_read;
- priv->bus->write = axiemac_miiphy_write;
- priv->bus->priv = priv;
+ if (IS_ENABLED(CONFIG_DM_ETH_PHY))
+ priv->bus = eth_phy_get_mdio_bus(dev);
- ret = mdio_register_seq(priv->bus, dev_seq(dev));
- if (ret)
- return ret;
+ if (!priv->bus) {
+ priv->bus = mdio_alloc();
+ priv->bus->read = axiemac_miiphy_read;
+ priv->bus->write = axiemac_miiphy_write;
+ priv->bus->priv = priv;
+
+ ret = mdio_register_seq(priv->bus, dev_seq(dev));
+ if (ret)
+ return ret;
+ }
+
+ if (IS_ENABLED(CONFIG_DM_ETH_PHY))
+ eth_phy_set_mdio_bus(dev, priv->bus);
axiemac_phy_init(dev);
}
+ printf("AXI EMAC: %lx, phyaddr %d, interface %s\n", (ulong)pdata->iobase,
+ priv->phyaddr, phy_string_for_interface(pdata->phy_interface));
+
return 0;
}
@@ -844,8 +859,10 @@ static int axi_emac_of_to_plat(struct udevice *dev)
offset = fdtdec_lookup_phandle(gd->fdt_blob, node,
"phy-handle");
if (offset > 0) {
- plat->phyaddr = fdtdec_get_int(gd->fdt_blob, offset,
- "reg", -1);
+ if (!(IS_ENABLED(CONFIG_DM_ETH_PHY)))
+ plat->phyaddr = fdtdec_get_int(gd->fdt_blob,
+ offset,
+ "reg", -1);
plat->phy_of_handle = offset;
}
@@ -857,9 +874,6 @@ static int axi_emac_of_to_plat(struct udevice *dev)
"xlnx,eth-hasnobuf");
}
- printf("AXI EMAC: %lx, phyaddr %d, interface %s\n", (ulong)pdata->iobase,
- plat->phyaddr, phy_string_for_interface(pdata->phy_interface));
-
return 0;
}
diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c
index 43fc36dc6a..6c9f1f7c27 100644
--- a/drivers/net/xilinx_emaclite.c
+++ b/drivers/net/xilinx_emaclite.c
@@ -22,6 +22,7 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <asm/io.h>
+#include <eth_phy.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -564,14 +565,27 @@ static int emaclite_probe(struct udevice *dev)
struct xemaclite *emaclite = dev_get_priv(dev);
int ret;
- emaclite->bus = mdio_alloc();
- emaclite->bus->read = emaclite_miiphy_read;
- emaclite->bus->write = emaclite_miiphy_write;
- emaclite->bus->priv = emaclite;
+ if (IS_ENABLED(CONFIG_DM_ETH_PHY))
+ emaclite->bus = eth_phy_get_mdio_bus(dev);
- ret = mdio_register_seq(emaclite->bus, dev_seq(dev));
- if (ret)
- return ret;
+ if (!emaclite->bus) {
+ emaclite->bus = mdio_alloc();
+ emaclite->bus->read = emaclite_miiphy_read;
+ emaclite->bus->write = emaclite_miiphy_write;
+ emaclite->bus->priv = emaclite;
+
+ ret = mdio_register_seq(emaclite->bus, dev_seq(dev));
+ if (ret)
+ return ret;
+ }
+
+ if (IS_ENABLED(CONFIG_DM_ETH_PHY)) {
+ eth_phy_set_mdio_bus(dev, emaclite->bus);
+ emaclite->phyaddr = eth_phy_get_addr(dev);
+ }
+
+ printf("EMACLITE: %lx, phyaddr %d, %d/%d\n", (ulong)emaclite->regs,
+ emaclite->phyaddr, emaclite->txpp, emaclite->rxpp);
return 0;
}
@@ -606,20 +620,19 @@ static int emaclite_of_to_plat(struct udevice *dev)
emaclite->phyaddr = -1;
- offset = fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(dev),
- "phy-handle");
- if (offset > 0)
- emaclite->phyaddr = fdtdec_get_int(gd->fdt_blob, offset,
- "reg", -1);
+ if (!(IS_ENABLED(CONFIG_DM_ETH_PHY))) {
+ offset = fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(dev),
+ "phy-handle");
+ if (offset > 0)
+ emaclite->phyaddr = fdtdec_get_int(gd->fdt_blob,
+ offset, "reg", -1);
+ }
emaclite->txpp = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
"xlnx,tx-ping-pong", 0);
emaclite->rxpp = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
"xlnx,rx-ping-pong", 0);
- printf("EMACLITE: %lx, phyaddr %d, %d/%d\n", (ulong)emaclite->regs,
- emaclite->phyaddr, emaclite->txpp, emaclite->rxpp);
-
return 0;
}
diff --git a/drivers/pinctrl/pinctrl-zynqmp.c b/drivers/pinctrl/pinctrl-zynqmp.c
index 7c5a02db1b..52d428f566 100644
--- a/drivers/pinctrl/pinctrl-zynqmp.c
+++ b/drivers/pinctrl/pinctrl-zynqmp.c
@@ -467,6 +467,10 @@ static int zynqmp_pinconf_set(struct udevice *dev, unsigned int pin,
pin);
break;
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ param = PM_PINCTRL_CONFIG_TRI_STATE;
+ arg = PM_PINCTRL_TRI_STATE_ENABLE;
+ ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+ break;
case PIN_CONFIG_LOW_POWER_MODE:
/*
* This cases are mentioned in dts but configurable
@@ -475,6 +479,11 @@ static int zynqmp_pinconf_set(struct udevice *dev, unsigned int pin,
*/
ret = 0;
break;
+ case PIN_CONFIG_OUTPUT_ENABLE:
+ param = PM_PINCTRL_CONFIG_TRI_STATE;
+ arg = PM_PINCTRL_TRI_STATE_DISABLE;
+ ret = zynqmp_pm_pinctrl_set_config(pin, param, arg);
+ break;
default:
dev_warn(dev, "unsupported configuration parameter '%u'\n",
param);
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 45c284a408..f585622fdb 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -476,6 +476,8 @@ config DEBUG_UART_BASE
depends on DEBUG_UART
default 0 if DEBUG_SBI_CONSOLE
default 0 if DEBUG_UART_SANDBOX
+ default 0xff000000 if DEBUG_UART_ZYNQ && ARCH_ZYNQMP
+ default 0xe0000000 if DEBUG_UART_ZYNQ && ARCH_ZYNQ
help
This is the base address of your UART for memory-mapped UARTs.
@@ -502,6 +504,8 @@ config DEBUG_UART_CLOCK
default 0 if DEBUG_SBI_CONSOLE
default 0 if DEBUG_UART_SANDBOX
default 0 if DEBUG_MVEBU_A3700_UART
+ default 100000000 if DEBUG_UART_ZYNQ && ARCH_ZYNQMP
+ default 50000000 if DEBUG_UART_ZYNQ && ARCH_ZYNQ
help
The UART input clock determines the speed of the internal UART
circuitry. The baud rate is derived from this by dividing the input
diff --git a/drivers/soc/soc_xilinx_zynqmp.c b/drivers/soc/soc_xilinx_zynqmp.c
index a71115b17c..c10fc7d444 100644
--- a/drivers/soc/soc_xilinx_zynqmp.c
+++ b/drivers/soc/soc_xilinx_zynqmp.c
@@ -3,10 +3,15 @@
* Xilinx ZynqMP SOC driver
*
* Copyright (C) 2021 Xilinx, Inc.
+ * Michal Simek <michal.simek@xilinx.com>
+ *
+ * Copyright (C) 2022 Weidmüller Interface GmbH & Co. KG
+ * Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com>
*/
#include <common.h>
#include <dm.h>
+#include <dm/device_compat.h>
#include <asm/cache.h>
#include <soc.h>
#include <zynqmp_firmware.h>
@@ -22,11 +27,257 @@
*/
static const char zynqmp_family[] = "ZynqMP";
+#define EFUSE_VCU_DIS_SHIFT 8
+#define EFUSE_VCU_DIS_MASK BIT(EFUSE_VCU_DIS_SHIFT)
+#define EFUSE_GPU_DIS_SHIFT 5
+#define EFUSE_GPU_DIS_MASK BIT(EFUSE_GPU_DIS_SHIFT)
+#define IDCODE_DEV_TYPE_MASK GENMASK(27, 0)
+#define IDCODE2_PL_INIT_SHIFT 9
+#define IDCODE2_PL_INIT_MASK BIT(IDCODE2_PL_INIT_SHIFT)
+
+#define ZYNQMP_VERSION_SIZE 7
+
+enum {
+ ZYNQMP_VARIANT_EG = BIT(0),
+ ZYNQMP_VARIANT_EV = BIT(1),
+ ZYNQMP_VARIANT_CG = BIT(2),
+ ZYNQMP_VARIANT_DR = BIT(3),
+};
+
+struct zynqmp_device {
+ u32 id;
+ u8 device;
+ u8 variants;
+};
+
struct soc_xilinx_zynqmp_priv {
const char *family;
+ char machine[ZYNQMP_VERSION_SIZE];
char revision;
};
+static const struct zynqmp_device zynqmp_devices[] = {
+ {
+ .id = 0x04688093,
+ .device = 1,
+ .variants = ZYNQMP_VARIANT_EG,
+ },
+ {
+ .id = 0x04711093,
+ .device = 2,
+ .variants = ZYNQMP_VARIANT_EG | ZYNQMP_VARIANT_CG,
+ },
+ {
+ .id = 0x04710093,
+ .device = 3,
+ .variants = ZYNQMP_VARIANT_EG | ZYNQMP_VARIANT_CG,
+ },
+ {
+ .id = 0x04721093,
+ .device = 4,
+ .variants = ZYNQMP_VARIANT_EG | ZYNQMP_VARIANT_CG |
+ ZYNQMP_VARIANT_EV,
+ },
+ {
+ .id = 0x04720093,
+ .device = 5,
+ .variants = ZYNQMP_VARIANT_EG | ZYNQMP_VARIANT_CG |
+ ZYNQMP_VARIANT_EV,
+ },
+ {
+ .id = 0x04739093,
+ .device = 6,
+ .variants = ZYNQMP_VARIANT_EG | ZYNQMP_VARIANT_CG,
+ },
+ {
+ .id = 0x04730093,
+ .device = 7,
+ .variants = ZYNQMP_VARIANT_EG | ZYNQMP_VARIANT_CG |
+ ZYNQMP_VARIANT_EV,
+ },
+ {
+ .id = 0x04738093,
+ .device = 9,
+ .variants = ZYNQMP_VARIANT_EG | ZYNQMP_VARIANT_CG,
+ },
+ {
+ .id = 0x04740093,
+ .device = 11,
+ .variants = ZYNQMP_VARIANT_EG,
+ },
+ {
+ .id = 0x04750093,
+ .device = 15,
+ .variants = ZYNQMP_VARIANT_EG,
+ },
+ {
+ .id = 0x04759093,
+ .device = 17,
+ .variants = ZYNQMP_VARIANT_EG,
+ },
+ {
+ .id = 0x04758093,
+ .device = 19,
+ .variants = ZYNQMP_VARIANT_EG,
+ },
+ {
+ .id = 0x047E1093,
+ .device = 21,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x047E3093,
+ .device = 23,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x047E5093,
+ .device = 25,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x047E4093,
+ .device = 27,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x047E0093,
+ .device = 28,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x047E2093,
+ .device = 29,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x047E6093,
+ .device = 39,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x047FD093,
+ .device = 43,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x047F8093,
+ .device = 46,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x047FF093,
+ .device = 47,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x047FB093,
+ .device = 48,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x047FE093,
+ .device = 49,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x046d0093,
+ .device = 67,
+ .variants = ZYNQMP_VARIANT_DR,
+ },
+ {
+ .id = 0x04714093,
+ .device = 24,
+ .variants = 0,
+ },
+ {
+ .id = 0x04724093,
+ .device = 26,
+ .variants = 0,
+ },
+};
+
+static const struct zynqmp_device *zynqmp_get_device(u32 idcode)
+{
+ idcode &= IDCODE_DEV_TYPE_MASK;
+
+ for (int i = 0; i < ARRAY_SIZE(zynqmp_devices); i++) {
+ if (zynqmp_devices[i].id == idcode)
+ return &zynqmp_devices[i];
+ }
+
+ return NULL;
+}
+
+static int soc_xilinx_zynqmp_detect_machine(struct udevice *dev, u32 idcode,
+ u32 idcode2)
+{
+ struct soc_xilinx_zynqmp_priv *priv = dev_get_priv(dev);
+ const struct zynqmp_device *device;
+ int ret;
+
+ device = zynqmp_get_device(idcode);
+ if (!device)
+ return 0;
+
+ /* Add device prefix to the name */
+ ret = snprintf(priv->machine, sizeof(priv->machine), "%s%d",
+ device->variants ? "zu" : "xck", device->device);
+ if (ret < 0)
+ return ret;
+
+ if (device->variants & ZYNQMP_VARIANT_EV) {
+ /* Devices with EV variant might be EG/CG/EV family */
+ if (idcode2 & IDCODE2_PL_INIT_MASK) {
+ u32 family = ((idcode2 & EFUSE_VCU_DIS_MASK) >>
+ EFUSE_VCU_DIS_SHIFT) << 1 |
+ ((idcode2 & EFUSE_GPU_DIS_MASK) >>
+ EFUSE_GPU_DIS_SHIFT);
+
+ /*
+ * Get family name based on extended idcode values as
+ * determined on UG1087, EXTENDED_IDCODE register
+ * description
+ */
+ switch (family) {
+ case 0x00:
+ strlcat(priv->machine, "ev",
+ sizeof(priv->machine));
+ break;
+ case 0x10:
+ strlcat(priv->machine, "eg",
+ sizeof(priv->machine));
+ break;
+ case 0x11:
+ strlcat(priv->machine, "cg",
+ sizeof(priv->machine));
+ break;
+ default:
+ /* Do not append family name*/
+ break;
+ }
+ } else {
+ /*
+ * When PL powered down the VCU Disable efuse cannot be
+ * read. So, ignore the bit and just findout if it is CG
+ * or EG/EV variant.
+ */
+ strlcat(priv->machine, (idcode2 & EFUSE_GPU_DIS_MASK) ?
+ "cg" : "e", sizeof(priv->machine));
+ }
+ } else if (device->variants & ZYNQMP_VARIANT_CG) {
+ /* Devices with CG variant might be EG or CG family */
+ strlcat(priv->machine, (idcode2 & EFUSE_GPU_DIS_MASK) ?
+ "cg" : "eg", sizeof(priv->machine));
+ } else if (device->variants & ZYNQMP_VARIANT_EG) {
+ strlcat(priv->machine, "eg", sizeof(priv->machine));
+ } else if (device->variants & ZYNQMP_VARIANT_DR) {
+ strlcat(priv->machine, "dr", sizeof(priv->machine));
+ }
+
+ return 0;
+}
+
static int soc_xilinx_zynqmp_get_family(struct udevice *dev, char *buf, int size)
{
struct soc_xilinx_zynqmp_priv *priv = dev_get_priv(dev);
@@ -34,6 +285,17 @@ static int soc_xilinx_zynqmp_get_family(struct udevice *dev, char *buf, int size
return snprintf(buf, size, "%s", priv->family);
}
+int soc_xilinx_zynqmp_get_machine(struct udevice *dev, char *buf, int size)
+{
+ struct soc_xilinx_zynqmp_priv *priv = dev_get_priv(dev);
+ const char *machine = priv->machine;
+
+ if (!machine[0])
+ machine = "unknown";
+
+ return snprintf(buf, size, "%s", machine);
+}
+
static int soc_xilinx_zynqmp_get_revision(struct udevice *dev, char *buf, int size)
{
struct soc_xilinx_zynqmp_priv *priv = dev_get_priv(dev);
@@ -44,6 +306,7 @@ static int soc_xilinx_zynqmp_get_revision(struct udevice *dev, char *buf, int si
static const struct soc_ops soc_xilinx_zynqmp_ops = {
.get_family = soc_xilinx_zynqmp_get_family,
.get_revision = soc_xilinx_zynqmp_get_revision,
+ .get_machine = soc_xilinx_zynqmp_get_machine,
};
static int soc_xilinx_zynqmp_probe(struct udevice *dev)
@@ -54,8 +317,7 @@ static int soc_xilinx_zynqmp_probe(struct udevice *dev)
priv->family = zynqmp_family;
- if (IS_ENABLED(CONFIG_SPL_BUILD) || current_el() == 3 ||
- !IS_ENABLED(CONFIG_ZYNQMP_FIRMWARE))
+ if (!IS_ENABLED(CONFIG_ZYNQMP_FIRMWARE))
ret = zynqmp_mmio_read(ZYNQMP_PS_VERSION, &ret_payload[2]);
else
ret = xilinx_pm_request(PM_GET_CHIPID, 0, 0, 0, 0,
@@ -65,6 +327,26 @@ static int soc_xilinx_zynqmp_probe(struct udevice *dev)
priv->revision = ret_payload[2] & ZYNQMP_PS_VER_MASK;
+ if (IS_ENABLED(CONFIG_ZYNQMP_FIRMWARE)) {
+ /*
+ * Firmware returns:
+ * payload[0][31:0] = status of the operation
+ * payload[1] = IDCODE
+ * payload[2][19:0] = Version
+ * payload[2][28:20] = EXTENDED_IDCODE
+ * payload[2][29] = PL_INIT
+ */
+ u32 idcode = ret_payload[1];
+ u32 idcode2 = ret_payload[2] >>
+ ZYNQMP_CSU_VERSION_EMPTY_SHIFT;
+ dev_dbg(dev, "IDCODE: 0x%0x, IDCODE2: 0x%0x\n", idcode,
+ idcode2);
+
+ ret = soc_xilinx_zynqmp_detect_machine(dev, idcode, idcode2);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig
index d592dba285..20b5af7e26 100644
--- a/drivers/timer/Kconfig
+++ b/drivers/timer/Kconfig
@@ -279,4 +279,13 @@ config IMX_GPT_TIMER
Select this to enable support for the timer found on
NXP i.MX devices.
+config XILINX_TIMER
+ bool "Xilinx timer support"
+ depends on TIMER
+ select REGMAP
+ select SPL_REGMAP if SPL
+ help
+ Select this to enable support for the timer found on
+ any Xilinx boards (axi timer).
+
endmenu
diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile
index cc2b8516b5..d9822a5370 100644
--- a/drivers/timer/Makefile
+++ b/drivers/timer/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_X86_TSC_TIMER) += tsc_timer.o
obj-$(CONFIG_MTK_TIMER) += mtk_timer.o
obj-$(CONFIG_MCHP_PIT64B_TIMER) += mchp-pit64b-timer.o
obj-$(CONFIG_IMX_GPT_TIMER) += imx-gpt-timer.o
+obj-$(CONFIG_XILINX_TIMER) += xilinx-timer.o
diff --git a/drivers/timer/xilinx-timer.c b/drivers/timer/xilinx-timer.c
new file mode 100644
index 0000000000..75b4473b63
--- /dev/null
+++ b/drivers/timer/xilinx-timer.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2022 Advanced Micro Devices, Inc
+ * Michal Simek <michal.simek@amd.com>
+ *
+ * (C) Copyright 2007 Michal Simek
+ * Michal SIMEK <monstr@monstr.eu>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <timer.h>
+#include <regmap.h>
+#include <dm/device_compat.h>
+
+#define TIMER_ENABLE_ALL 0x400 /* ENALL */
+#define TIMER_PWM 0x200 /* PWMA0 */
+#define TIMER_INTERRUPT 0x100 /* T0INT */
+#define TIMER_ENABLE 0x080 /* ENT0 */
+#define TIMER_ENABLE_INTR 0x040 /* ENIT0 */
+#define TIMER_RESET 0x020 /* LOAD0 */
+#define TIMER_RELOAD 0x010 /* ARHT0 */
+#define TIMER_EXT_CAPTURE 0x008 /* CAPT0 */
+#define TIMER_EXT_COMPARE 0x004 /* GENT0 */
+#define TIMER_DOWN_COUNT 0x002 /* UDT0 */
+#define TIMER_CAPTURE_MODE 0x001 /* MDT0 */
+
+#define TIMER_CONTROL_OFFSET 0
+#define TIMER_LOADREG_OFFSET 4
+#define TIMER_COUNTER_OFFSET 8
+
+struct xilinx_timer_priv {
+ struct regmap *regs;
+};
+
+static u64 xilinx_timer_get_count(struct udevice *dev)
+{
+ struct xilinx_timer_priv *priv = dev_get_priv(dev);
+ u32 value;
+
+ regmap_read(priv->regs, TIMER_COUNTER_OFFSET, &value);
+
+ return value;
+}
+
+static int xilinx_timer_probe(struct udevice *dev)
+{
+ struct xilinx_timer_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ /* uc_priv->clock_rate has already clock rate */
+ ret = regmap_init_mem(dev_ofnode(dev), &priv->regs);
+ if (ret) {
+ dev_dbg(dev, "failed to get regbase of timer\n");
+ return ret;
+ }
+
+ regmap_write(priv->regs, TIMER_LOADREG_OFFSET, 0);
+ regmap_write(priv->regs, TIMER_CONTROL_OFFSET, TIMER_RESET);
+ regmap_write(priv->regs, TIMER_CONTROL_OFFSET,
+ TIMER_ENABLE | TIMER_RELOAD);
+
+ return 0;
+}
+
+static const struct timer_ops xilinx_timer_ops = {
+ .get_count = xilinx_timer_get_count,
+};
+
+static const struct udevice_id xilinx_timer_ids[] = {
+ { .compatible = "xlnx,xps-timer-1.00.a" },
+ {}
+};
+
+U_BOOT_DRIVER(xilinx_timer) = {
+ .name = "xilinx_timer",
+ .id = UCLASS_TIMER,
+ .of_match = xilinx_timer_ids,
+ .priv_auto = sizeof(struct xilinx_timer_priv),
+ .probe = xilinx_timer_probe,
+ .ops = &xilinx_timer_ops,
+};