aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2019-10-09 16:22:03 -0400
committerTom Rini <trini@konsulko.com>2019-10-09 16:22:03 -0400
commit44fb0d6c9f5147a41c710032869e5e01b3c9e310 (patch)
treef0c7932d0a8a688095e95f7d1de17be4f7d3af0b /drivers
parent548aefa5b9e5c31681e0a8bd78e96b66eedd1137 (diff)
parentbcaa0e3302e384ad65c352b385678acdf3f20c0a (diff)
downloadu-boot-44fb0d6c9f5147a41c710032869e5e01b3c9e310.zip
u-boot-44fb0d6c9f5147a41c710032869e5e01b3c9e310.tar.gz
u-boot-44fb0d6c9f5147a41c710032869e5e01b3c9e310.tar.bz2
Merge tag 'xilinx-for-v2020.01' of https://gitlab.denx.de/u-boot/custodians/u-boot-microblaze
Xilinx/FPGA changes for v2020.01 FPGA: - Enable fpga loading on Versal - Minor fix Microblaze: - Fix LMB configurations to support initrds - Some other cleanups Zynq: - Minor config/dt changes - Add distro boot support for usb1 and mmc1 - Remove Xilinx private boot commands and use only distro boot ZynqMP: - Kconfig cleanups, defconfig updates - Update some dt files - Add firmware driver for talking to PMUFW - Extend distro boot support for jtag - Add new IDs - Add system controller configurations - Convert code to talk firmware via mailbox or SMCs Versal: - Add board_late_init() - Add run time DT memory setup - Add DFU support - Extend distro boot support for jtag and dfu - Add clock driver - Tune mini configurations Xilinx: - Improve documentation (boot scripts, dt binding) - Enable run time initrd_high calculation - Define default SYS_PROMPT - Add zynq/zynqmp virtual defconfig Drivers: - Add Xilinx mailbox driver for talking to firmware - Clean zynq_gem for Versal - Move ZYNQ_HISPD_BROKEN to Kconfig - Wire genphy_init() in phy.c - Add Xilinx gii2rgmii bridge - Cleanup zynq_sdhci - dwc3 fix - zynq_gpio fix - axi_emac fix Others: - apalis-tk1 - clean config file
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Kconfig9
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk_versal.c746
-rw-r--r--drivers/firmware/Kconfig10
-rw-r--r--drivers/firmware/Makefile1
-rw-r--r--drivers/firmware/firmware-zynqmp.c161
-rw-r--r--drivers/fpga/Kconfig9
-rw-r--r--drivers/fpga/Makefile1
-rw-r--r--drivers/fpga/versalpl.c51
-rw-r--r--drivers/fpga/xilinx.c8
-rw-r--r--drivers/fpga/zynqmppl.c7
-rw-r--r--drivers/gpio/zynq_gpio.c2
-rw-r--r--drivers/mailbox/Kconfig6
-rw-r--r--drivers/mailbox/Makefile1
-rw-r--r--drivers/mailbox/mailbox-uclass.c19
-rw-r--r--drivers/mailbox/zynqmp-ipi.c134
-rw-r--r--drivers/mmc/Kconfig6
-rw-r--r--drivers/mmc/zynq_sdhci.c4
-rw-r--r--drivers/net/phy/Kconfig7
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/phy.c49
-rw-r--r--drivers/net/phy/xilinx_gmii2rgmii.c144
-rw-r--r--drivers/net/xilinx_axi_emac.c7
-rw-r--r--drivers/net/zynq_gem.c7
-rw-r--r--drivers/usb/dwc3/core.c4
25 files changed, 1376 insertions, 19 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 95fe0ae..0035f0a 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -95,6 +95,14 @@ config CLK_HSDK
help
Enable this to support the cgu clocks on Synopsys ARC HSDK
+config CLK_VERSAL
+ bool "Enable clock driver support for Versal"
+ depends on ARCH_VERSAL
+ select ZYNQMP_FIRMWARE
+ help
+ This clock driver adds support for clock realted settings for
+ Versal platform.
+
config CLK_VEXPRESS_OSC
bool "Enable driver for Arm Versatile Express OSC clock generators"
depends on CLK && VEXPRESS_CONFIG
@@ -113,6 +121,7 @@ config CLK_ZYNQ
config CLK_ZYNQMP
bool "Enable clock driver support for ZynqMP"
depends on ARCH_ZYNQMP
+ select ZYNQMP_FIRMWARE
help
This clock driver adds support for clock realted settings for
ZynqMP platform.
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 68aabe1..d7cea3b 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -43,3 +43,4 @@ obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o
obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf.o
obj-$(CONFIG_STM32H7) += clk_stm32h7.o
obj-$(CONFIG_CLK_TI_SCI) += clk-ti-sci.o
+obj-$(CONFIG_CLK_VERSAL) += clk_versal.o
diff --git a/drivers/clk/clk_versal.c b/drivers/clk/clk_versal.c
new file mode 100644
index 0000000..df87645
--- /dev/null
+++ b/drivers/clk/clk_versal.c
@@ -0,0 +1,746 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2019 Xilinx, Inc.
+ * Siva Durga Prasad Paladugu <siva.durga.paladugu@xilinx.com>
+ */
+
+#include <common.h>
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <clk.h>
+#include <dm.h>
+#include <asm/arch/sys_proto.h>
+
+#define MAX_PARENT 100
+#define MAX_NODES 6
+#define MAX_NAME_LEN 50
+
+#define CLK_TYPE_SHIFT 2
+
+#define PM_API_PAYLOAD_LEN 3
+
+#define NA_PARENT 0xFFFFFFFF
+#define DUMMY_PARENT 0xFFFFFFFE
+
+#define CLK_TYPE_FIELD_LEN 4
+#define CLK_TOPOLOGY_NODE_OFFSET 16
+#define NODES_PER_RESP 3
+
+#define CLK_TYPE_FIELD_MASK 0xF
+#define CLK_FLAG_FIELD_MASK GENMASK(21, 8)
+#define CLK_TYPE_FLAG_FIELD_MASK GENMASK(31, 24)
+#define CLK_TYPE_FLAG2_FIELD_MASK GENMASK(7, 4)
+#define CLK_TYPE_FLAG_BITS 8
+
+#define CLK_PARENTS_ID_LEN 16
+#define CLK_PARENTS_ID_MASK 0xFFFF
+
+#define END_OF_TOPOLOGY_NODE 1
+#define END_OF_PARENTS 1
+
+#define CLK_VALID_MASK 0x1
+#define NODE_CLASS_SHIFT 26U
+#define NODE_SUBCLASS_SHIFT 20U
+#define NODE_TYPE_SHIFT 14U
+#define NODE_INDEX_SHIFT 0U
+
+#define CLK_GET_NAME_RESP_LEN 16
+#define CLK_GET_TOPOLOGY_RESP_WORDS 3
+#define CLK_GET_PARENTS_RESP_WORDS 3
+#define CLK_GET_ATTR_RESP_WORDS 1
+
+#define NODE_SUBCLASS_CLOCK_PLL 1
+#define NODE_SUBCLASS_CLOCK_OUT 2
+#define NODE_SUBCLASS_CLOCK_REF 3
+
+#define NODE_CLASS_CLOCK 2
+#define NODE_CLASS_MASK 0x3F
+
+#define CLOCK_NODE_TYPE_MUX 1
+#define CLOCK_NODE_TYPE_DIV 4
+#define CLOCK_NODE_TYPE_GATE 6
+
+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_PINCTRL_GET_NUM_PINS,
+ PM_QID_PINCTRL_GET_NUM_FUNCTIONS,
+ PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS,
+ PM_QID_PINCTRL_GET_FUNCTION_NAME,
+ PM_QID_PINCTRL_GET_FUNCTION_GROUPS,
+ PM_QID_PINCTRL_GET_PIN_GROUPS,
+ PM_QID_CLOCK_GET_NUM_CLOCKS,
+ PM_QID_CLOCK_GET_MAX_DIVISOR,
+};
+
+enum clk_type {
+ CLK_TYPE_OUTPUT,
+ CLK_TYPE_EXTERNAL,
+};
+
+struct clock_parent {
+ char name[MAX_NAME_LEN];
+ int id;
+ u32 flag;
+};
+
+struct clock_topology {
+ u32 type;
+ u32 flag;
+ u32 type_flag;
+};
+
+struct versal_clock {
+ char clk_name[MAX_NAME_LEN];
+ u32 valid;
+ enum clk_type type;
+ struct clock_topology node[MAX_NODES];
+ u32 num_nodes;
+ struct clock_parent parent[MAX_PARENT];
+ u32 num_parents;
+ u32 clk_id;
+};
+
+struct versal_clk_priv {
+ struct versal_clock *clk;
+};
+
+static ulong alt_ref_clk;
+static ulong pl_alt_ref_clk;
+static ulong ref_clk;
+
+struct versal_pm_query_data {
+ u32 qid;
+ u32 arg1;
+ u32 arg2;
+ u32 arg3;
+};
+
+static struct versal_clock *clock;
+static unsigned int clock_max_idx;
+
+#define PM_QUERY_DATA 35
+
+static int versal_pm_query(struct versal_pm_query_data qdata, u32 *ret_payload)
+{
+ struct pt_regs regs;
+
+ regs.regs[0] = PM_SIP_SVC | PM_QUERY_DATA;
+ regs.regs[1] = ((u64)qdata.arg1 << 32) | qdata.qid;
+ regs.regs[2] = ((u64)qdata.arg3 << 32) | qdata.arg2;
+
+ smc_call(&regs);
+
+ if (ret_payload) {
+ ret_payload[0] = (u32)regs.regs[0];
+ ret_payload[1] = upper_32_bits(regs.regs[0]);
+ ret_payload[2] = (u32)regs.regs[1];
+ ret_payload[3] = upper_32_bits(regs.regs[1]);
+ ret_payload[4] = (u32)regs.regs[2];
+ }
+
+ return qdata.qid == PM_QID_CLOCK_GET_NAME ? 0 : regs.regs[0];
+}
+
+static inline int versal_is_valid_clock(u32 clk_id)
+{
+ if (clk_id >= clock_max_idx)
+ return -ENODEV;
+
+ return clock[clk_id].valid;
+}
+
+static int versal_get_clock_name(u32 clk_id, char *clk_name)
+{
+ int ret;
+
+ ret = versal_is_valid_clock(clk_id);
+ if (ret == 1) {
+ strncpy(clk_name, clock[clk_id].clk_name, MAX_NAME_LEN);
+ return 0;
+ }
+
+ return ret == 0 ? -EINVAL : ret;
+}
+
+static int versal_get_clock_type(u32 clk_id, u32 *type)
+{
+ int ret;
+
+ ret = versal_is_valid_clock(clk_id);
+ if (ret == 1) {
+ *type = clock[clk_id].type;
+ return 0;
+ }
+
+ return ret == 0 ? -EINVAL : ret;
+}
+
+static int versal_pm_clock_get_num_clocks(u32 *nclocks)
+{
+ struct versal_pm_query_data qdata = {0};
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ qdata.qid = PM_QID_CLOCK_GET_NUM_CLOCKS;
+
+ ret = versal_pm_query(qdata, ret_payload);
+ *nclocks = ret_payload[1];
+
+ return ret;
+}
+
+static int versal_pm_clock_get_name(u32 clock_id, char *name)
+{
+ struct versal_pm_query_data qdata = {0};
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ qdata.qid = PM_QID_CLOCK_GET_NAME;
+ qdata.arg1 = clock_id;
+
+ ret = versal_pm_query(qdata, ret_payload);
+ if (ret)
+ return ret;
+ memcpy(name, ret_payload, CLK_GET_NAME_RESP_LEN);
+
+ return 0;
+}
+
+static int versal_pm_clock_get_topology(u32 clock_id, u32 index, u32 *topology)
+{
+ struct versal_pm_query_data qdata = {0};
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ qdata.qid = PM_QID_CLOCK_GET_TOPOLOGY;
+ qdata.arg1 = clock_id;
+ qdata.arg2 = index;
+
+ ret = versal_pm_query(qdata, ret_payload);
+ memcpy(topology, &ret_payload[1], CLK_GET_TOPOLOGY_RESP_WORDS * 4);
+
+ return ret;
+}
+
+static int versal_pm_clock_get_parents(u32 clock_id, u32 index, u32 *parents)
+{
+ struct versal_pm_query_data qdata = {0};
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ qdata.qid = PM_QID_CLOCK_GET_PARENTS;
+ qdata.arg1 = clock_id;
+ qdata.arg2 = index;
+
+ ret = versal_pm_query(qdata, ret_payload);
+ memcpy(parents, &ret_payload[1], CLK_GET_PARENTS_RESP_WORDS * 4);
+
+ return ret;
+}
+
+static int versal_pm_clock_get_attributes(u32 clock_id, u32 *attr)
+{
+ struct versal_pm_query_data qdata = {0};
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ qdata.qid = PM_QID_CLOCK_GET_ATTRIBUTES;
+ qdata.arg1 = clock_id;
+
+ ret = versal_pm_query(qdata, ret_payload);
+ memcpy(attr, &ret_payload[1], CLK_GET_ATTR_RESP_WORDS * 4);
+
+ return ret;
+}
+
+static int __versal_clock_get_topology(struct clock_topology *topology,
+ u32 *data, u32 *nnodes)
+{
+ int i;
+
+ for (i = 0; i < PM_API_PAYLOAD_LEN; i++) {
+ if (!(data[i] & CLK_TYPE_FIELD_MASK))
+ return END_OF_TOPOLOGY_NODE;
+ topology[*nnodes].type = data[i] & CLK_TYPE_FIELD_MASK;
+ topology[*nnodes].flag = FIELD_GET(CLK_FLAG_FIELD_MASK,
+ data[i]);
+ topology[*nnodes].type_flag =
+ FIELD_GET(CLK_TYPE_FLAG_FIELD_MASK, data[i]);
+ topology[*nnodes].type_flag |=
+ FIELD_GET(CLK_TYPE_FLAG2_FIELD_MASK, data[i]) <<
+ CLK_TYPE_FLAG_BITS;
+ debug("topology type:0x%x, flag:0x%x, type_flag:0x%x\n",
+ topology[*nnodes].type, topology[*nnodes].flag,
+ topology[*nnodes].type_flag);
+ (*nnodes)++;
+ }
+
+ return 0;
+}
+
+static int versal_clock_get_topology(u32 clk_id,
+ struct clock_topology *topology,
+ u32 *num_nodes)
+{
+ int j, ret;
+ u32 pm_resp[PM_API_PAYLOAD_LEN] = {0};
+
+ *num_nodes = 0;
+ for (j = 0; j <= MAX_NODES; j += 3) {
+ ret = versal_pm_clock_get_topology(clock[clk_id].clk_id, j,
+ pm_resp);
+ if (ret)
+ return ret;
+ ret = __versal_clock_get_topology(topology, pm_resp, num_nodes);
+ if (ret == END_OF_TOPOLOGY_NODE)
+ return 0;
+ }
+
+ return 0;
+}
+
+static int __versal_clock_get_parents(struct clock_parent *parents, u32 *data,
+ u32 *nparent)
+{
+ int i;
+ struct clock_parent *parent;
+
+ for (i = 0; i < PM_API_PAYLOAD_LEN; i++) {
+ if (data[i] == NA_PARENT)
+ return END_OF_PARENTS;
+
+ parent = &parents[i];
+ parent->id = data[i] & CLK_PARENTS_ID_MASK;
+ if (data[i] == DUMMY_PARENT) {
+ strcpy(parent->name, "dummy_name");
+ parent->flag = 0;
+ } else {
+ parent->flag = data[i] >> CLK_PARENTS_ID_LEN;
+ if (versal_get_clock_name(parent->id, parent->name))
+ continue;
+ }
+ debug("parent name:%s\n", parent->name);
+ *nparent += 1;
+ }
+
+ return 0;
+}
+
+static int versal_clock_get_parents(u32 clk_id, struct clock_parent *parents,
+ u32 *num_parents)
+{
+ int j = 0, ret;
+ u32 pm_resp[PM_API_PAYLOAD_LEN] = {0};
+
+ *num_parents = 0;
+ do {
+ /* Get parents from firmware */
+ ret = versal_pm_clock_get_parents(clock[clk_id].clk_id, j,
+ pm_resp);
+ if (ret)
+ return ret;
+
+ ret = __versal_clock_get_parents(&parents[j], pm_resp,
+ num_parents);
+ if (ret == END_OF_PARENTS)
+ return 0;
+ j += PM_API_PAYLOAD_LEN;
+ } while (*num_parents <= MAX_PARENT);
+
+ return 0;
+}
+
+static u32 versal_clock_get_div(u32 clk_id)
+{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ u32 div;
+
+ versal_pm_request(PM_CLOCK_GETDIVIDER, clk_id, 0, 0, 0, ret_payload);
+ div = ret_payload[1];
+
+ return div;
+}
+
+static u32 versal_clock_set_div(u32 clk_id, u32 div)
+{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+
+ versal_pm_request(PM_CLOCK_SETDIVIDER, clk_id, div, 0, 0, ret_payload);
+
+ return div;
+}
+
+static u64 versal_clock_ref(u32 clk_id)
+{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ref;
+
+ versal_pm_request(PM_CLOCK_GETPARENT, clk_id, 0, 0, 0, ret_payload);
+ ref = ret_payload[0];
+ if (!(ref & 1))
+ return ref_clk;
+ if (ref & 2)
+ return pl_alt_ref_clk;
+ return 0;
+}
+
+static u64 versal_clock_get_pll_rate(u32 clk_id)
+{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ u32 fbdiv;
+ u32 res;
+ u32 frac;
+ u64 freq;
+ u32 parent_rate, parent_id;
+ u32 id = clk_id & 0xFFF;
+
+ versal_pm_request(PM_CLOCK_GETSTATE, clk_id, 0, 0, 0, ret_payload);
+ res = ret_payload[1];
+ if (!res) {
+ printf("0%x PLL not enabled\n", clk_id);
+ return 0;
+ }
+
+ parent_id = clock[clock[id].parent[0].id].clk_id;
+ parent_rate = versal_clock_ref(parent_id);
+
+ versal_pm_request(PM_CLOCK_GETDIVIDER, clk_id, 0, 0, 0, ret_payload);
+ fbdiv = ret_payload[1];
+ versal_pm_request(PM_CLOCK_PLL_GETPARAM, clk_id, 2, 0, 0, ret_payload);
+ frac = ret_payload[1];
+
+ freq = (fbdiv * parent_rate) >> (1 << frac);
+
+ return freq;
+}
+
+static u32 versal_clock_mux(u32 clk_id)
+{
+ int i;
+ u32 id = clk_id & 0xFFF;
+
+ for (i = 0; i < clock[id].num_nodes; i++)
+ if (clock[id].node[i].type == CLOCK_NODE_TYPE_MUX)
+ return 1;
+
+ return 0;
+}
+
+static u32 versal_clock_get_parentid(u32 clk_id)
+{
+ u32 parent_id = 0;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ u32 id = clk_id & 0xFFF;
+
+ if (versal_clock_mux(clk_id)) {
+ versal_pm_request(PM_CLOCK_GETPARENT, clk_id, 0, 0, 0,
+ ret_payload);
+ parent_id = ret_payload[1];
+ }
+
+ debug("parent_id:0x%x\n", clock[clock[id].parent[parent_id].id].clk_id);
+ return clock[clock[id].parent[parent_id].id].clk_id;
+}
+
+static u32 versal_clock_gate(u32 clk_id)
+{
+ u32 id = clk_id & 0xFFF;
+ int i;
+
+ for (i = 0; i < clock[id].num_nodes; i++)
+ if (clock[id].node[i].type == CLOCK_NODE_TYPE_GATE)
+ return 1;
+
+ return 0;
+}
+
+static u32 versal_clock_div(u32 clk_id)
+{
+ int i;
+ u32 id = clk_id & 0xFFF;
+
+ for (i = 0; i < clock[id].num_nodes; i++)
+ if (clock[id].node[i].type == CLOCK_NODE_TYPE_DIV)
+ return 1;
+
+ return 0;
+}
+
+static u32 versal_clock_pll(u32 clk_id, u64 *clk_rate)
+{
+ if (((clk_id >> NODE_SUBCLASS_SHIFT) & NODE_CLASS_MASK) ==
+ NODE_SUBCLASS_CLOCK_PLL &&
+ ((clk_id >> NODE_CLASS_SHIFT) & NODE_CLASS_MASK) ==
+ NODE_CLASS_CLOCK) {
+ *clk_rate = versal_clock_get_pll_rate(clk_id);
+ return 1;
+ }
+
+ return 0;
+}
+
+static u64 versal_clock_calc(u32 clk_id)
+{
+ u32 parent_id;
+ u64 clk_rate;
+ u32 div;
+
+ if (versal_clock_pll(clk_id, &clk_rate))
+ return clk_rate;
+
+ parent_id = versal_clock_get_parentid(clk_id);
+ if (((parent_id >> NODE_SUBCLASS_SHIFT) &
+ NODE_CLASS_MASK) == NODE_SUBCLASS_CLOCK_REF)
+ return versal_clock_ref(clk_id);
+
+ clk_rate = versal_clock_calc(parent_id);
+
+ if (versal_clock_div(clk_id)) {
+ div = versal_clock_get_div(clk_id);
+ clk_rate = DIV_ROUND_CLOSEST(clk_rate, div);
+ }
+
+ return clk_rate;
+}
+
+static int versal_clock_get_rate(u32 clk_id, u64 *clk_rate)
+{
+ if (((clk_id >> NODE_SUBCLASS_SHIFT) &
+ NODE_CLASS_MASK) == NODE_SUBCLASS_CLOCK_REF)
+ *clk_rate = versal_clock_ref(clk_id);
+
+ if (versal_clock_pll(clk_id, clk_rate))
+ return 0;
+
+ if (((clk_id >> NODE_SUBCLASS_SHIFT) &
+ NODE_CLASS_MASK) == NODE_SUBCLASS_CLOCK_OUT &&
+ ((clk_id >> NODE_CLASS_SHIFT) &
+ NODE_CLASS_MASK) == NODE_CLASS_CLOCK) {
+ if (!versal_clock_gate(clk_id))
+ return -EINVAL;
+ *clk_rate = versal_clock_calc(clk_id);
+ return 0;
+ }
+
+ return 0;
+}
+
+int soc_clk_dump(void)
+{
+ u64 clk_rate = 0;
+ u32 type, ret, i = 0;
+
+ printf("\n ****** VERSAL CLOCKS *****\n");
+
+ printf("alt_ref_clk:%ld pl_alt_ref_clk:%ld ref_clk:%ld\n",
+ alt_ref_clk, pl_alt_ref_clk, ref_clk);
+ for (i = 0; i < clock_max_idx; i++) {
+ debug("%s\n", clock[i].clk_name);
+ ret = versal_get_clock_type(i, &type);
+ if (ret || type != CLK_TYPE_OUTPUT)
+ continue;
+
+ ret = versal_clock_get_rate(clock[i].clk_id, &clk_rate);
+
+ if (ret != -EINVAL)
+ printf("clk: %s freq:%lld\n",
+ clock[i].clk_name, clk_rate);
+ }
+
+ return 0;
+}
+
+static void versal_get_clock_info(void)
+{
+ int i, ret;
+ u32 attr, type = 0, nodetype, subclass, class;
+
+ for (i = 0; i < clock_max_idx; i++) {
+ ret = versal_pm_clock_get_attributes(i, &attr);
+ if (ret)
+ continue;
+
+ clock[i].valid = attr & CLK_VALID_MASK;
+ clock[i].type = ((attr >> CLK_TYPE_SHIFT) & 0x1) ?
+ CLK_TYPE_EXTERNAL : CLK_TYPE_OUTPUT;
+ nodetype = (attr >> NODE_TYPE_SHIFT) & NODE_CLASS_MASK;
+ subclass = (attr >> NODE_SUBCLASS_SHIFT) & NODE_CLASS_MASK;
+ class = (attr >> NODE_CLASS_SHIFT) & NODE_CLASS_MASK;
+
+ clock[i].clk_id = (class << NODE_CLASS_SHIFT) |
+ (subclass << NODE_SUBCLASS_SHIFT) |
+ (nodetype << NODE_TYPE_SHIFT) |
+ (i << NODE_INDEX_SHIFT);
+
+ ret = versal_pm_clock_get_name(clock[i].clk_id,
+ clock[i].clk_name);
+ if (ret)
+ continue;
+ debug("clk name:%s, Valid:%d, type:%d, clk_id:0x%x\n",
+ clock[i].clk_name, clock[i].valid,
+ clock[i].type, clock[i].clk_id);
+ }
+
+ /* Get topology of all clock */
+ for (i = 0; i < clock_max_idx; i++) {
+ ret = versal_get_clock_type(i, &type);
+ if (ret || type != CLK_TYPE_OUTPUT)
+ continue;
+ debug("clk name:%s\n", clock[i].clk_name);
+ ret = versal_clock_get_topology(i, clock[i].node,
+ &clock[i].num_nodes);
+ if (ret)
+ continue;
+
+ ret = versal_clock_get_parents(i, clock[i].parent,
+ &clock[i].num_parents);
+ if (ret)
+ continue;
+ }
+}
+
+int versal_clock_setup(void)
+{
+ int ret;
+
+ ret = versal_pm_clock_get_num_clocks(&clock_max_idx);
+ if (ret)
+ return ret;
+
+ debug("%s, clock_max_idx:0x%x\n", __func__, clock_max_idx);
+ clock = calloc(clock_max_idx, sizeof(*clock));
+ if (!clock)
+ return -ENOMEM;
+
+ versal_get_clock_info();
+
+ return 0;
+}
+
+static int versal_clock_get_freq_by_name(char *name, struct udevice *dev,
+ ulong *freq)
+{
+ struct clk clk;
+ int ret;
+
+ ret = clk_get_by_name(dev, name, &clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to get %s\n", name);
+ return ret;
+ }
+
+ *freq = clk_get_rate(&clk);
+ if (IS_ERR_VALUE(*freq)) {
+ dev_err(dev, "failed to get rate %s\n", name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int versal_clk_probe(struct udevice *dev)
+{
+ int ret;
+ struct versal_clk_priv *priv = dev_get_priv(dev);
+
+ debug("%s\n", __func__);
+
+ ret = versal_clock_get_freq_by_name("alt_ref_clk", dev, &alt_ref_clk);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = versal_clock_get_freq_by_name("pl_alt_ref_clk",
+ dev, &pl_alt_ref_clk);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = versal_clock_get_freq_by_name("ref_clk", dev, &ref_clk);
+ if (ret < 0)
+ return -EINVAL;
+
+ versal_clock_setup();
+
+ priv->clk = clock;
+
+ return ret;
+}
+
+static ulong versal_clk_get_rate(struct clk *clk)
+{
+ struct versal_clk_priv *priv = dev_get_priv(clk->dev);
+ u32 id = clk->id;
+ u32 clk_id;
+ u64 clk_rate = 0;
+
+ debug("%s\n", __func__);
+
+ clk_id = priv->clk[id].clk_id;
+
+ versal_clock_get_rate(clk_id, &clk_rate);
+
+ return clk_rate;
+}
+
+static ulong versal_clk_set_rate(struct clk *clk, ulong rate)
+{
+ struct versal_clk_priv *priv = dev_get_priv(clk->dev);
+ u32 id = clk->id;
+ u32 clk_id;
+ u64 clk_rate = 0;
+ u32 div;
+ int ret;
+
+ debug("%s\n", __func__);
+
+ clk_id = priv->clk[id].clk_id;
+
+ ret = versal_clock_get_rate(clk_id, &clk_rate);
+ if (ret) {
+ printf("Clock is not a Gate:0x%x\n", clk_id);
+ return 0;
+ }
+
+ do {
+ if (versal_clock_div(clk_id)) {
+ div = versal_clock_get_div(clk_id);
+ clk_rate *= div;
+ div = DIV_ROUND_CLOSEST(clk_rate, rate);
+ versal_clock_set_div(clk_id, div);
+ debug("%s, div:%d, newrate:%lld\n", __func__,
+ div, DIV_ROUND_CLOSEST(clk_rate, div));
+ return DIV_ROUND_CLOSEST(clk_rate, div);
+ }
+ clk_id = versal_clock_get_parentid(clk_id);
+ } while (((clk_id >> NODE_SUBCLASS_SHIFT) &
+ NODE_CLASS_MASK) != NODE_SUBCLASS_CLOCK_REF);
+
+ printf("Clock didn't has Divisors:0x%x\n", priv->clk[id].clk_id);
+
+ return clk_rate;
+}
+
+static struct clk_ops versal_clk_ops = {
+ .set_rate = versal_clk_set_rate,
+ .get_rate = versal_clk_get_rate,
+};
+
+static const struct udevice_id versal_clk_ids[] = {
+ { .compatible = "xlnx,versal-clk" },
+ { }
+};
+
+U_BOOT_DRIVER(versal_clk) = {
+ .name = "versal-clk",
+ .id = UCLASS_CLK,
+ .of_match = versal_clk_ids,
+ .probe = versal_clk_probe,
+ .ops = &versal_clk_ops,
+ .priv_auto_alloc_size = sizeof(struct versal_clk_priv),
+};
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 873bc8c..b70a206 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -26,3 +26,13 @@ config TI_SCI_PROTOCOL
This protocol library is used by client drivers to use the features
provided by the system controller.
+
+config ZYNQMP_FIRMWARE
+ bool "ZynqMP Firmware interface"
+ select FIRMWARE
+ help
+ Firmware interface driver is used by different
+ drivers to communicate with the firmware for
+ various platform management services.
+ Say yes to enable ZynqMP firmware interface driver.
+ If in doubt, say N.
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 6c3e129..a0c250a 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_FIRMWARE) += firmware-uclass.o
obj-$(CONFIG_$(SPL_)ARM_PSCI_FW) += psci.o
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
obj-$(CONFIG_SANDBOX) += firmware-sandbox.o
+obj-$(CONFIG_ZYNQMP_FIRMWARE) += firmware-zynqmp.o
diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c
new file mode 100644
index 0000000..15e82ac
--- /dev/null
+++ b/drivers/firmware/firmware-zynqmp.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Zynq MPSoC Firmware driver
+ *
+ * Copyright (C) 2018-2019 Xilinx, Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+
+#if defined(CONFIG_ZYNQMP_IPI)
+#include <mailbox.h>
+#include <zynqmp_firmware.h>
+#include <asm/arch/sys_proto.h>
+
+#define PMUFW_PAYLOAD_ARG_CNT 8
+
+struct zynqmp_power {
+ struct mbox_chan tx_chan;
+ struct mbox_chan rx_chan;
+} zynqmp_power;
+
+static int ipi_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen)
+{
+ struct zynqmp_ipi_msg msg;
+ int ret;
+
+ if (req_len > PMUFW_PAYLOAD_ARG_CNT ||
+ res_maxlen > PMUFW_PAYLOAD_ARG_CNT)
+ return -EINVAL;
+
+ if (!(zynqmp_power.tx_chan.dev) || !(&zynqmp_power.rx_chan.dev))
+ return -EINVAL;
+
+ msg.buf = (u32 *)req;
+ msg.len = req_len;
+ ret = mbox_send(&zynqmp_power.tx_chan, &msg);
+ if (ret) {
+ debug("%s: Sending message failed\n", __func__);
+ return ret;
+ }
+
+ msg.buf = res;
+ msg.len = res_maxlen;
+ ret = mbox_recv(&zynqmp_power.rx_chan, &msg, 100);
+ if (ret)
+ debug("%s: Receiving message failed\n", __func__);
+
+ return ret;
+}
+
+static int send_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen)
+{
+ if (IS_ENABLED(CONFIG_SPL_BUILD))
+ return ipi_req(req, req_len, res, res_maxlen);
+
+ return invoke_smc(req[0] + PM_SIP_SVC, 0, 0, 0, 0, res);
+}
+
+unsigned int zynqmp_firmware_version(void)
+{
+ int ret;
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ static u32 pm_api_version = ZYNQMP_PM_VERSION_INVALID;
+
+ /*
+ * Get PMU version only once and later
+ * just return stored values instead of
+ * asking PMUFW again.
+ **/
+ if (pm_api_version == ZYNQMP_PM_VERSION_INVALID) {
+ const u32 request[] = { PM_GET_API_VERSION };
+
+ ret = send_req(request, ARRAY_SIZE(request), ret_payload, 2);
+ if (ret)
+ panic("PMUFW is not found - Please load it!\n");
+
+ pm_api_version = ret_payload[1];
+ if (pm_api_version < ZYNQMP_PM_VERSION)
+ panic("PMUFW version error. Expected: v%d.%d\n",
+ ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR);
+ }
+
+ return pm_api_version;
+};
+
+/**
+ * Send a configuration object to the PMU firmware.
+ *
+ * @cfg_obj: Pointer to the configuration object
+ * @size: Size of @cfg_obj in bytes
+ */
+void zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size)
+{
+ const u32 request[] = {
+ PM_SET_CONFIGURATION,
+ (u32)((u64)cfg_obj)
+ };
+ u32 response;
+ int err;
+
+ printf("Loading new PMUFW cfg obj (%ld bytes)\n", size);
+
+ err = send_req(request, ARRAY_SIZE(request), &response, 1);
+ if (err)
+ panic("Cannot load PMUFW configuration object (%d)\n", err);
+ if (response != 0)
+ panic("PMUFW returned 0x%08x status!\n", response);
+}
+
+static int zynqmp_power_probe(struct udevice *dev)
+{
+ int ret = 0;
+
+ debug("%s, (dev=%p)\n", __func__, dev);
+
+ ret = mbox_get_by_name(dev, "tx", &zynqmp_power.tx_chan);
+ if (ret) {
+ debug("%s, cannot tx mailbox\n", __func__);
+ return ret;
+ }
+
+ ret = mbox_get_by_name(dev, "rx", &zynqmp_power.rx_chan);
+ if (ret) {
+ debug("%s, cannot rx mailbox\n", __func__);
+ return ret;
+ }
+
+ ret = zynqmp_firmware_version();
+ printf("PMUFW:\tv%d.%d\n",
+ ret >> ZYNQMP_PM_VERSION_MAJOR_SHIFT,
+ ret & ZYNQMP_PM_VERSION_MINOR_MASK);
+
+ return 0;
+};
+
+static const struct udevice_id zynqmp_power_ids[] = {
+ { .compatible = "xlnx,zynqmp-power" },
+ { }
+};
+
+U_BOOT_DRIVER(zynqmp_power) = {
+ .name = "zynqmp_power",
+ .id = UCLASS_FIRMWARE,
+ .of_match = zynqmp_power_ids,
+ .probe = zynqmp_power_probe,
+};
+#endif
+
+static const struct udevice_id zynqmp_firmware_ids[] = {
+ { .compatible = "xlnx,zynqmp-firmware" },
+ { .compatible = "xlnx,versal-firmware"},
+ { }
+};
+
+U_BOOT_DRIVER(zynqmp_firmware) = {
+ .id = UCLASS_FIRMWARE,
+ .name = "zynqmp-firmware",
+ .probe = dm_scan_fdt_dev,
+ .of_match = zynqmp_firmware_ids,
+};
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 105a299..fe398a1 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -56,6 +56,15 @@ config FPGA_ZYNQMPPL
Enable FPGA driver for loading bitstream in BIT and BIN format
on Xilinx Zynq UltraScale+ (ZynqMP) device.
+config FPGA_VERSALPL
+ bool "Enable Xilinx FPGA driver for Versal"
+ depends on FPGA_XILINX
+ help
+ Enable FPGA driver for loading bitstream in PDI format on Xilinx
+ Versal device. PDI is a new programmable device image format for
+ Versal. The bitstream will only be generated as PDI for Versal
+ platform.
+
config FPGA_SPARTAN3
bool "Enable Spartan3 FPGA driver"
depends on FPGA_XILINX
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 5a778c1..04e6480 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -6,6 +6,7 @@
obj-y += fpga.o
obj-$(CONFIG_FPGA_SPARTAN2) += spartan2.o
obj-$(CONFIG_FPGA_SPARTAN3) += spartan3.o
+obj-$(CONFIG_FPGA_VERSALPL) += versalpl.o
obj-$(CONFIG_FPGA_VIRTEX2) += virtex2.o
obj-$(CONFIG_FPGA_ZYNQPL) += zynqpl.o
obj-$(CONFIG_FPGA_ZYNQMPPL) += zynqmppl.o
diff --git a/drivers/fpga/versalpl.c b/drivers/fpga/versalpl.c
new file mode 100644
index 0000000..69617a9
--- /dev/null
+++ b/drivers/fpga/versalpl.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2019, Xilinx, Inc,
+ * Siva Durga Prasad Paladugu <siva.durga.paladugu@xilinx.com>
+ */
+
+#include <common.h>
+#include <asm/arch/sys_proto.h>
+#include <memalign.h>
+#include <versalpl.h>
+
+static ulong versal_align_dma_buffer(ulong *buf, u32 len)
+{
+ ulong *new_buf;
+
+ if ((ulong)buf != ALIGN((ulong)buf, ARCH_DMA_MINALIGN)) {
+ new_buf = (ulong *)ALIGN((ulong)buf, ARCH_DMA_MINALIGN);
+ memcpy(new_buf, buf, len);
+ buf = new_buf;
+ }
+
+ return (ulong)buf;
+}
+
+static int versal_load(xilinx_desc *desc, const void *buf, size_t bsize,
+ bitstream_type bstype)
+{
+ ulong bin_buf;
+ int ret;
+ u32 buf_lo, buf_hi;
+ u32 ret_payload[5];
+
+ bin_buf = versal_align_dma_buffer((ulong *)buf, bsize);
+
+ debug("%s called!\n", __func__);
+ flush_dcache_range(bin_buf, bin_buf + bsize);
+
+ buf_lo = lower_32_bits(bin_buf);
+ buf_hi = upper_32_bits(bin_buf);
+
+ ret = versal_pm_request(VERSAL_PM_LOAD_PDI, VERSAL_PM_PDI_TYPE, buf_lo,
+ buf_hi, 0, ret_payload);
+ if (ret)
+ puts("PL FPGA LOAD fail\n");
+
+ return ret;
+}
+
+struct xilinx_fpga_op versal_op = {
+ .load = versal_load,
+};
diff --git a/drivers/fpga/xilinx.c b/drivers/fpga/xilinx.c
index f513550..4b0334b 100644
--- a/drivers/fpga/xilinx.c
+++ b/drivers/fpga/xilinx.c
@@ -226,7 +226,10 @@ int xilinx_info(xilinx_desc *desc)
case xilinx_zynqmp:
printf("ZynqMP PL\n");
break;
- /* Add new family types here */
+ case xilinx_versal:
+ printf("Versal PL\n");
+ break;
+ /* Add new family types here */
default:
printf ("Unknown family type, %d\n", desc->family);
}
@@ -257,6 +260,9 @@ int xilinx_info(xilinx_desc *desc)
case csu_dma:
printf("csu_dma configuration interface (ZynqMP)\n");
break;
+ case cfi:
+ printf("CFI configuration interface (Versal)\n");
+ break;
/* Add new interface types here */
default:
printf ("Unsupported interface type, %d\n", desc->iface);
diff --git a/drivers/fpga/zynqmppl.c b/drivers/fpga/zynqmppl.c
index 22bfdd8..c267027 100644
--- a/drivers/fpga/zynqmppl.c
+++ b/drivers/fpga/zynqmppl.c
@@ -8,6 +8,7 @@
#include <console.h>
#include <common.h>
#include <zynqmppl.h>
+#include <zynqmp_firmware.h>
#include <linux/sizes.h>
#include <asm/arch/sys_proto.h>
#include <memalign.h>
@@ -151,9 +152,9 @@ static ulong zynqmp_align_dma_buffer(u32 *buf, u32 len, u32 swap)
buf = new_buf;
} else if ((swap != SWAP_DONE) &&
- (zynqmp_pmufw_version() <= PMUFW_V1_0)) {
+ (zynqmp_firmware_version() <= PMUFW_V1_0)) {
/* For bitstream which are aligned */
- u32 *new_buf = (u32 *)buf;
+ new_buf = buf;
printf("%s: Bitstream is not swapped(%d) - swap it\n", __func__,
swap);
@@ -204,7 +205,7 @@ static int zynqmp_load(xilinx_desc *desc, const void *buf, size_t bsize,
u32 ret_payload[PAYLOAD_ARG_CNT];
bool xilfpga_old = false;
- if (zynqmp_pmufw_version() <= PMUFW_V1_0) {
+ if (zynqmp_firmware_version() <= PMUFW_V1_0) {
puts("WARN: PMUFW v1.0 or less is detected\n");
puts("WARN: Not all bitstream formats are supported\n");
puts("WARN: Please upgrade PMUFW\n");
diff --git a/drivers/gpio/zynq_gpio.c b/drivers/gpio/zynq_gpio.c
index 55a5cba..a760c5b 100644
--- a/drivers/gpio/zynq_gpio.c
+++ b/drivers/gpio/zynq_gpio.c
@@ -292,7 +292,7 @@ static int zynq_gpio_direction_output(struct udevice *dev, unsigned gpio,
writel(reg, platdata->base + ZYNQ_GPIO_OUTEN_OFFSET(bank_num));
/* set the state of the pin */
- gpio_set_value(gpio, value);
+ zynq_gpio_set_value(dev, gpio, value);
return 0;
}
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 11bf552..85c2a82 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -41,4 +41,10 @@ config K3_SEC_PROXY
Select this driver if your platform has support for this hardware
block.
+config ZYNQMP_IPI
+ bool "Xilinx ZynqMP IPI controller support"
+ depends on DM_MAILBOX && ARCH_ZYNQMP
+ help
+ This enables support for the Xilinx ZynqMP Inter Processor Interrupt
+ communication controller.
endmenu
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index a753cc4..d2ace8c 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox-test.o
obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o
obj-$(CONFIG_TEGRA_HSP) += tegra-hsp.o
obj-$(CONFIG_K3_SEC_PROXY) += k3-sec-proxy.o
+obj-$(CONFIG_ZYNQMP_IPI) += zynqmp-ipi.o
diff --git a/drivers/mailbox/mailbox-uclass.c b/drivers/mailbox/mailbox-uclass.c
index 1b4a586..9fdb627 100644
--- a/drivers/mailbox/mailbox-uclass.c
+++ b/drivers/mailbox/mailbox-uclass.c
@@ -49,7 +49,16 @@ int mbox_get_by_index(struct udevice *dev, int index, struct mbox_chan *chan)
if (ret) {
debug("%s: uclass_get_device_by_of_offset failed: %d\n",
__func__, ret);
- return ret;
+
+ /* Test with parent node */
+ ret = uclass_get_device_by_ofnode(UCLASS_MAILBOX,
+ ofnode_get_parent(args.node),
+ &dev_mbox);
+ if (ret) {
+ debug("%s: mbox node from parent failed: %d\n",
+ __func__, ret);
+ return ret;
+ };
}
ops = mbox_dev_ops(dev_mbox);
@@ -63,7 +72,8 @@ int mbox_get_by_index(struct udevice *dev, int index, struct mbox_chan *chan)
return ret;
}
- ret = ops->request(chan);
+ if (ops->request)
+ ret = ops->request(chan);
if (ret) {
debug("ops->request() failed: %d\n", ret);
return ret;
@@ -94,7 +104,10 @@ int mbox_free(struct mbox_chan *chan)
debug("%s(chan=%p)\n", __func__, chan);
- return ops->free(chan);
+ if (ops->free)
+ return ops->free(chan);
+
+ return 0;
}
int mbox_send(struct mbox_chan *chan, const void *data)
diff --git a/drivers/mailbox/zynqmp-ipi.c b/drivers/mailbox/zynqmp-ipi.c
new file mode 100644
index 0000000..c181a7b
--- /dev/null
+++ b/drivers/mailbox/zynqmp-ipi.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Xilinx Zynq MPSoC Mailbox driver
+ *
+ * Copyright (C) 2018-2019 Xilinx, Inc.
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <dm.h>
+#include <mailbox-uclass.h>
+#include <mach/sys_proto.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <wait_bit.h>
+
+/* IPI bitmasks, register base */
+/* TODO: move reg base to DT */
+#define IPI_BIT_MASK_PMU0 0x10000
+#define IPI_INT_REG_BASE_APU 0xFF300000
+
+struct ipi_int_regs {
+ u32 trig; /* 0x0 */
+ u32 obs; /* 0x4 */
+ u32 ist; /* 0x8 */
+ u32 imr; /* 0xC */
+ u32 ier; /* 0x10 */
+ u32 idr; /* 0x14 */
+};
+
+#define ipi_int_apu ((struct ipi_int_regs *)IPI_INT_REG_BASE_APU)
+
+struct zynqmp_ipi {
+ void __iomem *local_req_regs;
+ void __iomem *local_res_regs;
+ void __iomem *remote_req_regs;
+ void __iomem *remote_res_regs;
+};
+
+static int zynqmp_ipi_send(struct mbox_chan *chan, const void *data)
+{
+ const struct zynqmp_ipi_msg *msg = (struct zynqmp_ipi_msg *)data;
+ struct zynqmp_ipi *zynqmp = dev_get_priv(chan->dev);
+ u32 ret;
+ u32 *mbx = (u32 *)zynqmp->local_req_regs;
+
+ for (size_t i = 0; i < msg->len; i++)
+ writel(msg->buf[i], &mbx[i]);
+
+ /* Write trigger interrupt */
+ writel(IPI_BIT_MASK_PMU0, &ipi_int_apu->trig);
+
+ /* Wait until observation bit is cleared */
+ ret = wait_for_bit_le32(&ipi_int_apu->obs, IPI_BIT_MASK_PMU0, false,
+ 100, false);
+
+ debug("%s, send %ld bytes\n", __func__, msg->len);
+ return ret;
+};
+
+static int zynqmp_ipi_recv(struct mbox_chan *chan, void *data)
+{
+ struct zynqmp_ipi_msg *msg = (struct zynqmp_ipi_msg *)data;
+ struct zynqmp_ipi *zynqmp = dev_get_priv(chan->dev);
+ u32 *mbx = (u32 *)zynqmp->local_res_regs;
+
+ for (size_t i = 0; i < msg->len; i++)
+ msg->buf[i] = readl(&mbx[i]);
+
+ debug("%s, recv %ld bytes\n", __func__, msg->len);
+ return 0;
+};
+
+static int zynqmp_ipi_probe(struct udevice *dev)
+{
+ struct zynqmp_ipi *zynqmp = dev_get_priv(dev);
+ struct resource res;
+ ofnode node;
+
+ debug("%s(dev=%p)\n", __func__, dev);
+
+ /* Get subnode where the regs are defined */
+ /* Note IPI mailbox node needs to be the first one in DT */
+ node = ofnode_first_subnode(dev_ofnode(dev));
+
+ if (ofnode_read_resource_byname(node, "local_request_region", &res)) {
+ dev_err(dev, "No reg property for local_request_region\n");
+ return -EINVAL;
+ };
+ zynqmp->local_req_regs = devm_ioremap(dev, res.start,
+ (res.start - res.end));
+
+ if (ofnode_read_resource_byname(node, "local_response_region", &res)) {
+ dev_err(dev, "No reg property for local_response_region\n");
+ return -EINVAL;
+ };
+ zynqmp->local_res_regs = devm_ioremap(dev, res.start,
+ (res.start - res.end));
+
+ if (ofnode_read_resource_byname(node, "remote_request_region", &res)) {
+ dev_err(dev, "No reg property for remote_request_region\n");
+ return -EINVAL;
+ };
+ zynqmp->remote_req_regs = devm_ioremap(dev, res.start,
+ (res.start - res.end));
+
+ if (ofnode_read_resource_byname(node, "remote_response_region", &res)) {
+ dev_err(dev, "No reg property for remote_response_region\n");
+ return -EINVAL;
+ };
+ zynqmp->remote_res_regs = devm_ioremap(dev, res.start,
+ (res.start - res.end));
+
+ return 0;
+};
+
+static const struct udevice_id zynqmp_ipi_ids[] = {
+ { .compatible = "xlnx,zynqmp-ipi-mailbox" },
+ { }
+};
+
+struct mbox_ops zynqmp_ipi_mbox_ops = {
+ .send = zynqmp_ipi_send,
+ .recv = zynqmp_ipi_recv,
+};
+
+U_BOOT_DRIVER(zynqmp_ipi) = {
+ .name = "zynqmp-ipi",
+ .id = UCLASS_MAILBOX,
+ .of_match = zynqmp_ipi_ids,
+ .probe = zynqmp_ipi_probe,
+ .priv_auto_alloc_size = sizeof(struct zynqmp_ipi),
+ .ops = &zynqmp_ipi_mbox_ops,
+};
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 7361bca..2bc9d8d 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -634,6 +634,12 @@ config ZYNQ_SDHCI_MIN_FREQ
help
Set the minimum frequency of the controller.
+config ZYNQ_HISPD_BROKEN
+ bool "High speed broken for Zynq SDHCI controller"
+ depends on MMC_SDHCI_ZYNQ
+ help
+ Set if high speed mode is broken.
+
config MMC_SUNXI
bool "Allwinner sunxi SD/MMC Host Controller support"
depends on ARCH_SUNXI && !UART0_PORT_F
diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c
index 3225a7a..529eec9 100644
--- a/drivers/mmc/zynq_sdhci.c
+++ b/drivers/mmc/zynq_sdhci.c
@@ -190,7 +190,7 @@ static void arasan_sdhci_set_control_reg(struct sdhci_host *host)
}
#endif
-#if defined(CONFIG_DM_MMC) && defined(CONFIG_ARCH_ZYNQMP)
+#if defined(CONFIG_ARCH_ZYNQMP)
const struct sdhci_ops arasan_ops = {
.platform_execute_tuning = &arasan_sdhci_execute_tuning,
.set_delay = &arasan_sdhci_set_tapdelay,
@@ -266,7 +266,7 @@ static int arasan_sdhci_ofdata_to_platdata(struct udevice *dev)
priv->host->name = dev->name;
-#if defined(CONFIG_DM_MMC) && defined(CONFIG_ARCH_ZYNQMP)
+#if defined(CONFIG_ARCH_ZYNQMP)
priv->host->ops = &arasan_ops;
#endif
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 2a3da06..30bd8e7 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -228,6 +228,13 @@ config PHY_VITESSE
config PHY_XILINX
bool "Xilinx Ethernet PHYs support"
+config PHY_XILINX_GMII2RGMII
+ bool "Xilinx GMII to RGMII Ethernet PHYs support"
+ help
+ This adds support for Xilinx GMII to RGMII IP core. This IP acts
+ as bridge between MAC connected over GMII and external phy that
+ is connected over RGMII interface.
+
config PHY_FIXED
bool "Fixed-Link PHY"
depends on DM_ETH
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 555da83..76b6197 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_PHY_SMSC) += smsc.o
obj-$(CONFIG_PHY_TERANETICS) += teranetics.o
obj-$(CONFIG_PHY_TI) += ti.o
obj-$(CONFIG_PHY_XILINX) += xilinx_phy.o
+obj-$(CONFIG_PHY_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
obj-$(CONFIG_PHY_VITESSE) += vitesse.o
obj-$(CONFIG_PHY_MSCC) += mscc.o
obj-$(CONFIG_PHY_FIXED) += fixed.o
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index ae37dd6..f2d17aa 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -458,6 +458,11 @@ static struct phy_driver genphy_driver = {
.shutdown = genphy_shutdown,
};
+int genphy_init(void)
+{
+ return phy_register(&genphy_driver);
+}
+
static LIST_HEAD(phy_drivers);
int phy_init(void)
@@ -540,6 +545,11 @@ int phy_init(void)
#ifdef CONFIG_PHY_FIXED
phy_fixed_init();
#endif
+#ifdef CONFIG_PHY_XILINX_GMII2RGMII
+ phy_xilinx_gmii2rgmii_init();
+#endif
+ genphy_init();
+
return 0;
}
@@ -911,6 +921,41 @@ void phy_connect_dev(struct phy_device *phydev, struct eth_device *dev)
debug("%s connected to %s\n", dev->name, phydev->drv->name);
}
+#ifdef CONFIG_PHY_XILINX_GMII2RGMII
+#ifdef CONFIG_DM_ETH
+static struct phy_device *phy_connect_gmii2rgmii(struct mii_dev *bus,
+ struct udevice *dev,
+ phy_interface_t interface)
+#else
+static struct phy_device *phy_connect_gmii2rgmii(struct mii_dev *bus,
+ struct eth_device *dev,
+ phy_interface_t interface)
+#endif
+{
+ struct phy_device *phydev = NULL;
+ int sn = dev_of_offset(dev);
+ int off;
+
+ while (sn > 0) {
+ off = fdt_node_offset_by_compatible(gd->fdt_blob, sn,
+ "xlnx,gmii-to-rgmii-1.0");
+ if (off > 0) {
+ phydev = phy_device_create(bus, off,
+ PHY_GMII2RGMII_ID, false,
+ interface);
+ break;
+ }
+ if (off == -FDT_ERR_NOTFOUND)
+ sn = fdt_first_subnode(gd->fdt_blob, sn);
+ else
+ printf("%s: Error finding compat string:%d\n",
+ __func__, off);
+ }
+
+ return phydev;
+}
+#endif
+
#ifdef CONFIG_PHY_FIXED
#ifdef CONFIG_DM_ETH
static struct phy_device *phy_connect_fixed(struct mii_dev *bus,
@@ -957,6 +1002,10 @@ struct phy_device *phy_connect(struct mii_dev *bus, int addr,
#ifdef CONFIG_PHY_FIXED
phydev = phy_connect_fixed(bus, dev, interface);
#endif
+#ifdef CONFIG_PHY_XILINX_GMII2RGMII
+ if (!phydev)
+ phydev = phy_connect_gmii2rgmii(bus, dev, interface);
+#endif
if (!phydev)
phydev = phy_find_by_mask(bus, mask, interface);
diff --git a/drivers/net/phy/xilinx_gmii2rgmii.c b/drivers/net/phy/xilinx_gmii2rgmii.c
new file mode 100644
index 0000000..8c20da2
--- /dev/null
+++ b/drivers/net/phy/xilinx_gmii2rgmii.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx GMII2RGMII phy driver
+ *
+ * Copyright (C) 2018 Xilinx, Inc.
+ */
+
+#include <dm.h>
+#include <phy.h>
+#include <config.h>
+#include <common.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define ZYNQ_GMII2RGMII_REG 0x10
+#define ZYNQ_GMII2RGMII_SPEED_MASK (BMCR_SPEED1000 | BMCR_SPEED100)
+
+static int xilinxgmiitorgmii_config(struct phy_device *phydev)
+{
+ struct phy_device *ext_phydev = phydev->priv;
+
+ debug("%s\n", __func__);
+ if (ext_phydev->drv->config)
+ ext_phydev->drv->config(ext_phydev);
+
+ return 0;
+}
+
+static int xilinxgmiitorgmii_extread(struct phy_device *phydev, int addr,
+ int devaddr, int regnum)
+{
+ struct phy_device *ext_phydev = phydev->priv;
+
+ debug("%s\n", __func__);
+ if (ext_phydev->drv->readext)
+ ext_phydev->drv->readext(ext_phydev, addr, devaddr, regnum);
+
+ return 0;
+}
+
+static int xilinxgmiitorgmii_extwrite(struct phy_device *phydev, int addr,
+ int devaddr, int regnum, u16 val)
+
+{
+ struct phy_device *ext_phydev = phydev->priv;
+
+ debug("%s\n", __func__);
+ if (ext_phydev->drv->writeext)
+ ext_phydev->drv->writeext(ext_phydev, addr, devaddr, regnum,
+ val);
+
+ return 0;
+}
+
+static int xilinxgmiitorgmii_startup(struct phy_device *phydev)
+{
+ u16 val = 0;
+ struct phy_device *ext_phydev = phydev->priv;
+
+ debug("%s\n", __func__);
+ ext_phydev->dev = phydev->dev;
+ if (ext_phydev->drv->startup)
+ ext_phydev->drv->startup(ext_phydev);
+
+ val = phy_read(phydev, phydev->addr, ZYNQ_GMII2RGMII_REG);
+ val &= ~ZYNQ_GMII2RGMII_SPEED_MASK;
+
+ if (ext_phydev->speed == SPEED_1000)
+ val |= BMCR_SPEED1000;
+ else if (ext_phydev->speed == SPEED_100)
+ val |= BMCR_SPEED100;
+
+ phy_write(phydev, phydev->addr, ZYNQ_GMII2RGMII_REG, val |
+ BMCR_FULLDPLX);
+
+ phydev->duplex = ext_phydev->duplex;
+ phydev->speed = ext_phydev->speed;
+ phydev->link = ext_phydev->link;
+
+ return 0;
+}
+
+static int xilinxgmiitorgmii_probe(struct phy_device *phydev)
+{
+ int ofnode = phydev->addr;
+ u32 phy_of_handle;
+ int ext_phyaddr = -1;
+ struct phy_device *ext_phydev;
+
+ debug("%s\n", __func__);
+
+ if (phydev->interface != PHY_INTERFACE_MODE_GMII) {
+ printf("Incorrect interface type\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Read the phy address again as the one we read in ethernet driver
+ * was overwritten for the purpose of storing the ofnode
+ */
+ phydev->addr = fdtdec_get_int(gd->fdt_blob, ofnode, "reg", -1);
+ phy_of_handle = fdtdec_lookup_phandle(gd->fdt_blob, ofnode,
+ "phy-handle");
+ if (phy_of_handle > 0)
+ ext_phyaddr = fdtdec_get_int(gd->fdt_blob,
+ phy_of_handle,
+ "reg", -1);
+ ext_phydev = phy_find_by_mask(phydev->bus,
+ 1 << ext_phyaddr,
+ PHY_INTERFACE_MODE_RGMII);
+ if (!ext_phydev) {
+ printf("%s, No external phy device found\n", __func__);
+ return -EINVAL;
+ }
+
+ ext_phydev->node = offset_to_ofnode(phy_of_handle);
+ phydev->priv = ext_phydev;
+
+ debug("%s, gmii2rgmmi:0x%x, extphy:0x%x\n", __func__, phydev->addr,
+ ext_phyaddr);
+
+ phydev->flags |= PHY_FLAG_BROKEN_RESET;
+
+ return 0;
+}
+
+static struct phy_driver gmii2rgmii_driver = {
+ .name = "XILINX GMII2RGMII",
+ .uid = PHY_GMII2RGMII_ID,
+ .mask = 0xffffffff,
+ .features = PHY_GBIT_FEATURES,
+ .probe = xilinxgmiitorgmii_probe,
+ .config = xilinxgmiitorgmii_config,
+ .startup = xilinxgmiitorgmii_startup,
+ .writeext = xilinxgmiitorgmii_extwrite,
+ .readext = xilinxgmiitorgmii_extread,
+};
+
+int phy_xilinx_gmii2rgmii_init(void)
+{
+ phy_register(&gmii2rgmii_driver);
+
+ return 0;
+}
diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c
index 26c21c6..36d6511 100644
--- a/drivers/net/xilinx_axi_emac.c
+++ b/drivers/net/xilinx_axi_emac.c
@@ -93,6 +93,7 @@ struct axidma_priv {
struct phy_device *phydev;
struct mii_dev *bus;
u8 eth_hasnobuf;
+ int phy_of_handle;
};
/* BD descriptors */
@@ -276,6 +277,8 @@ static int axiemac_phy_init(struct udevice *dev)
phydev->supported &= supported;
phydev->advertising = phydev->supported;
priv->phydev = phydev;
+ if (priv->phy_of_handle)
+ priv->phydev->node = offset_to_ofnode(priv->phy_of_handle);
phy_config(phydev);
return 0;
@@ -736,8 +739,10 @@ static int axi_emac_ofdata_to_platdata(struct udevice *dev)
priv->phyaddr = -1;
offset = fdtdec_lookup_phandle(gd->fdt_blob, node, "phy-handle");
- if (offset > 0)
+ if (offset > 0) {
priv->phyaddr = fdtdec_get_int(gd->fdt_blob, offset, "reg", -1);
+ priv->phy_of_handle = offset;
+ }
phy_mode = fdt_getprop(gd->fdt_blob, node, "phy-mode", NULL);
if (phy_mode)
diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c
index 033efb8..a7a6ce9 100644
--- a/drivers/net/zynq_gem.c
+++ b/drivers/net/zynq_gem.c
@@ -26,8 +26,6 @@
#include <asm/arch/sys_proto.h>
#include <linux/errno.h>
-DECLARE_GLOBAL_DATA_PTR;
-
/* Bit/mask specification */
#define ZYNQ_GEM_PHYMNTNC_OP_MASK 0x40020000 /* operation mask bits */
#define ZYNQ_GEM_PHYMNTNC_OP_R_MASK 0x20000000 /* read operation */
@@ -465,7 +463,6 @@ static int zynq_gem_init(struct udevice *dev)
break;
}
-#if !defined(CONFIG_ARCH_VERSAL)
ret = clk_set_rate(&priv->clk, clk_rate);
if (IS_ERR_VALUE(ret) && ret != (unsigned long)-ENOSYS) {
dev_err(dev, "failed to set tx clock rate\n");
@@ -477,9 +474,6 @@ static int zynq_gem_init(struct udevice *dev)
dev_err(dev, "failed to enable tx clock\n");
return ret;
}
-#else
- debug("requested clk_rate %ld\n", clk_rate);
-#endif
setbits_le32(&regs->nwctrl, ZYNQ_GEM_NWCTRL_RXEN_MASK |
ZYNQ_GEM_NWCTRL_TXEN_MASK);
@@ -753,6 +747,7 @@ static int zynq_gem_ofdata_to_platdata(struct udevice *dev)
}
static const struct udevice_id zynq_gem_ids[] = {
+ { .compatible = "cdns,versal-gem" },
{ .compatible = "cdns,zynqmp-gem" },
{ .compatible = "cdns,zynq-gem" },
{ .compatible = "cdns,gem" },
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 56e2a04..2498f0e 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -440,6 +440,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
goto err0;
}
+ dwc3_phy_setup(dwc);
+
ret = dwc3_core_soft_reset(dwc);
if (ret)
goto err0;
@@ -514,8 +516,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
- dwc3_phy_setup(dwc);
-
ret = dwc3_alloc_scratch_buffers(dwc);
if (ret)
goto err0;