diff options
author | Tom Rini <trini@konsulko.com> | 2019-10-09 16:22:03 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2019-10-09 16:22:03 -0400 |
commit | 44fb0d6c9f5147a41c710032869e5e01b3c9e310 (patch) | |
tree | f0c7932d0a8a688095e95f7d1de17be4f7d3af0b /drivers | |
parent | 548aefa5b9e5c31681e0a8bd78e96b66eedd1137 (diff) | |
parent | bcaa0e3302e384ad65c352b385678acdf3f20c0a (diff) | |
download | u-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/Kconfig | 9 | ||||
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/clk_versal.c | 746 | ||||
-rw-r--r-- | drivers/firmware/Kconfig | 10 | ||||
-rw-r--r-- | drivers/firmware/Makefile | 1 | ||||
-rw-r--r-- | drivers/firmware/firmware-zynqmp.c | 161 | ||||
-rw-r--r-- | drivers/fpga/Kconfig | 9 | ||||
-rw-r--r-- | drivers/fpga/Makefile | 1 | ||||
-rw-r--r-- | drivers/fpga/versalpl.c | 51 | ||||
-rw-r--r-- | drivers/fpga/xilinx.c | 8 | ||||
-rw-r--r-- | drivers/fpga/zynqmppl.c | 7 | ||||
-rw-r--r-- | drivers/gpio/zynq_gpio.c | 2 | ||||
-rw-r--r-- | drivers/mailbox/Kconfig | 6 | ||||
-rw-r--r-- | drivers/mailbox/Makefile | 1 | ||||
-rw-r--r-- | drivers/mailbox/mailbox-uclass.c | 19 | ||||
-rw-r--r-- | drivers/mailbox/zynqmp-ipi.c | 134 | ||||
-rw-r--r-- | drivers/mmc/Kconfig | 6 | ||||
-rw-r--r-- | drivers/mmc/zynq_sdhci.c | 4 | ||||
-rw-r--r-- | drivers/net/phy/Kconfig | 7 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 49 | ||||
-rw-r--r-- | drivers/net/phy/xilinx_gmii2rgmii.c | 144 | ||||
-rw-r--r-- | drivers/net/xilinx_axi_emac.c | 7 | ||||
-rw-r--r-- | drivers/net/zynq_gem.c | 7 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.c | 4 |
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(®s); + + 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(®s->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; |