From 0b180d02a30191478ec8088661049e19930253f7 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Tue, 14 Feb 2017 17:40:21 +0100 Subject: [PATCH 01/22] arm: zynq: Label whole PL part as fpga_full region This will simplify dt overlay structure for the whole PL. Signed-off-by: Michal Simek Reviewed-by: Moritz Fischer --- arch/arm/dts/zynq-7000.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/dts/zynq-7000.dtsi b/arch/arm/dts/zynq-7000.dtsi index 34fc6e5f89..f993e19ef2 100644 --- a/arch/arm/dts/zynq-7000.dtsi +++ b/arch/arm/dts/zynq-7000.dtsi @@ -38,6 +38,14 @@ }; }; + fpga_full: fpga-full { + compatible = "fpga-region"; + fpga-mgr = <&devcfg>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + }; + pmu@f8891000 { compatible = "arm,cortex-a9-pmu"; interrupts = <0 5 4>, <0 6 4>; From f5e46b49195fdaa4309d6d21ee2f0b43d53a302d Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 3 Jul 2017 13:41:34 +0200 Subject: [PATCH 02/22] zynq: Add EFI runtime sections to linker script When using EFI_LOADER, we add a few special sections for runtime code and data which get relocated on demand when executing a target OS. These runtime structures need to get annotated properly in the linker script. While we do that properly in the generic one, we missed out on the zynq specific linker script. This patch adds the EFI runtime section annotations into the zynq linker script so that the efi loader code actually works on that platform. Signed-off-by: Alexander Graf Signed-off-by: Michal Simek --- arch/arm/mach-zynq/u-boot.lds | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/arch/arm/mach-zynq/u-boot.lds b/arch/arm/mach-zynq/u-boot.lds index 4dc9bb0102..86559cb6aa 100644 --- a/arch/arm/mach-zynq/u-boot.lds +++ b/arch/arm/mach-zynq/u-boot.lds @@ -42,6 +42,35 @@ SECTIONS . = ALIGN(4); + .__efi_runtime_start : { + *(.__efi_runtime_start) + } + + .efi_runtime : { + *(efi_runtime_text) + *(efi_runtime_data) + } + + .__efi_runtime_stop : { + *(.__efi_runtime_stop) + } + + .efi_runtime_rel_start : + { + *(.__efi_runtime_rel_start) + } + + .efi_runtime_rel : { + *(.relefi_runtime_text) + *(.relefi_runtime_data) + } + + .efi_runtime_rel_stop : + { + *(.__efi_runtime_rel_stop) + } + + . = ALIGN(4); .image_copy_end : { *(.__image_copy_end) From 61d8eeb0bceee686895d7703b3702298c7891b0b Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 3 Jul 2017 13:41:35 +0200 Subject: [PATCH 03/22] zynq: Enable distro boot Distro boot allows devices to boot using standardized boot methods by default. This can be very handy for distributions that want to run on different platforms. This patch moves the zynq platform to use its old, zynq specific boot method first and then fall back to distro boot. That way supporting Linux distributions like openSUSE is much easier. Signed-off-by: Alexander Graf Signed-off-by: Michal Simek --- include/configs/zynq-common.h | 55 ++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/include/configs/zynq-common.h b/include/configs/zynq-common.h index 4975d76c36..056eef7ce8 100644 --- a/include/configs/zynq-common.h +++ b/include/configs/zynq-common.h @@ -171,6 +171,50 @@ /* enable preboot to be loaded before CONFIG_BOOTDELAY */ #define CONFIG_PREBOOT +/* Boot configuration */ +#define CONFIG_BOOTCOMMAND "run $modeboot || run distro_bootcmd" +#define CONFIG_SYS_LOAD_ADDR 0 /* default? */ + +/* Distro boot enablement */ + +#ifdef CONFIG_SPL_BUILD +#define BOOTENV +#else +#include + +#ifdef CONFIG_CMD_MMC +#define BOOT_TARGET_DEVICES_MMC(func) func(MMC, mmc, 0) +#else +#define BOOT_TARGET_DEVICES_MMC(func) +#endif + +#ifdef CONFIG_CMD_USB +#define BOOT_TARGET_DEVICES_USB(func) func(USB, usb, 0) +#else +#define BOOT_TARGET_DEVICES_USB(func) +#endif + +#if defined(CONFIG_CMD_PXE) +#define BOOT_TARGET_DEVICES_PXE(func) func(PXE, pxe, na) +#else +#define BOOT_TARGET_DEVICES_PXE(func) +#endif + +#if defined(CONFIG_CMD_DHCP) +#define BOOT_TARGET_DEVICES_DHCP(func) func(DHCP, dhcp, na) +#else +#define BOOT_TARGET_DEVICES_DHCP(func) +#endif + +#define BOOT_TARGET_DEVICES(func) \ + BOOT_TARGET_DEVICES_MMC(func) \ + BOOT_TARGET_DEVICES_USB(func) \ + BOOT_TARGET_DEVICES_PXE(func) \ + BOOT_TARGET_DEVICES_DHCP(func) + +#include +#endif /* CONFIG_SPL_BUILD */ + /* Default environment */ #ifndef CONFIG_EXTRA_ENV_SETTINGS #define CONFIG_EXTRA_ENV_SETTINGS \ @@ -182,6 +226,11 @@ "fdt_high=0x20000000\0" \ "initrd_high=0x20000000\0" \ "loadbootenv_addr=0x2000000\0" \ + "fdt_addr_r=0x1f00000\0" \ + "pxefile_addr_r=0x2000000\0" \ + "kernel_addr_r=0x2000000\0" \ + "scriptaddr=0x3000000\0" \ + "ramdisk_addr_r=0x3100000\0" \ "bootenv=uEnv.txt\0" \ "bootenv_dev=mmc\0" \ "loadbootenv=load ${bootenv_dev} 0 ${loadbootenv_addr} ${bootenv}\0" \ @@ -217,12 +266,10 @@ "echo Copying FIT from USB to RAM... && " \ "load usb 0 ${load_addr} ${fit_image} && " \ "bootm ${load_addr}; fi\0" \ - DFU_ALT_INFO + DFU_ALT_INFO \ + BOOTENV #endif -#define CONFIG_BOOTCOMMAND "run $modeboot" -#define CONFIG_SYS_LOAD_ADDR 0 /* default? */ - /* Miscellaneous configurable options */ #define CONFIG_CMDLINE_EDITING From 584dc4095ed2e5d70146c5cb220fac18645798d1 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 3 Jul 2017 13:41:36 +0200 Subject: [PATCH 04/22] zynq: Add Z-Turn board The Z-Turn board is a low cost development board based on the Xilinx Zynq SoC. While it's powerful and quite versatile, it so far lacked upstream support. This patch adds basic support for the Z-Turn. It does however for now miss enablement for MIO51 reset which means that USB and ethernet don't work. For that either FSBL or SPL need to be adjusted. The SPL part will follow later. Signed-off-by: Alexander Graf Signed-off-by: Michal Simek --- arch/arm/dts/Makefile | 1 + arch/arm/dts/zynq-zturn-myir.dts | 161 +++++++++++++++++++++++++++++++ configs/zynq_z_turn_defconfig | 59 +++++++++++ 3 files changed, 221 insertions(+) create mode 100644 arch/arm/dts/zynq-zturn-myir.dts create mode 100644 configs/zynq_z_turn_defconfig diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 132fa69fe5..e4d118d51c 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -132,6 +132,7 @@ dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb \ zynq-topic-miami.dtb \ zynq-topic-miamilite.dtb \ zynq-topic-miamiplus.dtb \ + zynq-zturn-myir.dtb \ zynq-zc770-xm010.dtb \ zynq-zc770-xm011.dtb \ zynq-zc770-xm012.dtb \ diff --git a/arch/arm/dts/zynq-zturn-myir.dts b/arch/arm/dts/zynq-zturn-myir.dts new file mode 100644 index 0000000000..a5ecfcc1d7 --- /dev/null +++ b/arch/arm/dts/zynq-zturn-myir.dts @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2015 Andrea Merello + * Copyright (C) 2017 Alexander Graf + * + * Based on zynq-zed.dts which is: + * Copyright (C) 2011 - 2014 Xilinx + * Copyright (C) 2012 National Instruments Corp. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +/dts-v1/; +/include/ "zynq-7000.dtsi" + +/ { + model = "Zynq Z-Turn MYIR Board"; + compatible = "xlnx,zynq-7000"; + + aliases { + ethernet0 = &gem0; + serial0 = &uart1; + serial1 = &uart0; + spi0 = &qspi; + mmc0 = &sdhci0; + }; + + memory { + device_type = "memory"; + reg = <0x0 0x40000000>; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + gpio-leds { + compatible = "gpio-leds"; + led_r { + label = "led_r"; + gpios = <&gpio0 0x72 0x1>; + default-state = "on"; + linux,default-trigger = "heartbeat"; + }; + + led_g { + label = "led_g"; + gpios = <&gpio0 0x73 0x1>; + default-state = "on"; + linux,default-trigger = "heartbeat"; + }; + + led_b { + label = "led_b"; + gpios = <&gpio0 0x74 0x1>; + default-state = "on"; + linux,default-trigger = "heartbeat"; + }; + + usr_led1 { + label = "usr_led1"; + gpios = <&gpio0 0x0 0x1>; + default-state = "off"; + linux,default-trigger = "none"; + }; + + usr_led2 { + label = "usr_led2"; + gpios = <&gpio0 0x9 0x1>; + default-state = "off"; + linux,default-trigger = "none"; + }; + }; + + gpio-beep { + compatible = "gpio-beeper"; + label = "pl-beep"; + gpios = <&gpio0 0x75 0x0>; + }; + + gpio-keys { + compatible = "gpio-keys"; + #address-cells = <0x1>; + #size-cells = <0x0>; + autorepeat; + K1 { + label = "K1"; + gpios = <&gpio0 0x32 0x1>; + linux,code = <0x66>; + gpio-key,wakeup; + autorepeat; + }; + }; +}; + +&clkc { + ps-clk-frequency = <33333333>; + fclk-enable = <0xf>; +}; + +&qspi { + u-boot,dm-pre-reloc; + status = "okay"; +}; + +&gem0 { + status = "okay"; + phy-mode = "rgmii-id"; + phy-handle = <ðernet_phy>; + + ethernet_phy: ethernet-phy@0 { + reg = <0x0>; + }; +}; + +&sdhci0 { + u-boot,dm-pre-reloc; + status = "okay"; +}; + +&uart0 { + u-boot,dm-pre-reloc; + status = "okay"; +}; + +&uart1 { + u-boot,dm-pre-reloc; + status = "okay"; +}; + +&usb0 { + status = "okay"; + dr_mode = "host"; +}; + +&can0 { + status = "okay"; +}; + +&i2c0 { + status = "okay"; + clock-frequency = <400000>; + + stlm75@49 { + status = "okay"; + compatible = "lm75"; + reg = <0x49>; + }; + + adxl345@53 { + compatible = "adi,adxl34x", "adxl34x"; + reg = <0x53>; + interrupt-parent = <&intc>; + interrupts = <0x0 0x1e 0x4>; + }; +}; diff --git a/configs/zynq_z_turn_defconfig b/configs/zynq_z_turn_defconfig new file mode 100644 index 0000000000..b550912222 --- /dev/null +++ b/configs/zynq_z_turn_defconfig @@ -0,0 +1,59 @@ +CONFIG_ARM=y +CONFIG_ARCH_ZYNQ=y +CONFIG_SYS_TEXT_BASE=0x4000000 +CONFIG_DEFAULT_DEVICE_TREE="zynq-zturn-myir" +CONFIG_DEBUG_UART=y +CONFIG_FIT=y +CONFIG_FIT_SIGNATURE=y +CONFIG_FIT_VERBOSE=y +CONFIG_ENV_IS_NOWHERE=y +# CONFIG_DISPLAY_CPUINFO is not set +CONFIG_SPL=y +CONFIG_SPL_OS_BOOT=y +CONFIG_HUSH_PARSER=y +CONFIG_SYS_PROMPT="Zynq> " +# CONFIG_CMD_IMLS is not set +# CONFIG_CMD_FLASH is not set +CONFIG_CMD_MMC=y +CONFIG_CMD_SF=y +CONFIG_CMD_USB=y +CONFIG_CMD_DFU=y +CONFIG_CMD_GPIO=y +# CONFIG_CMD_SETEXPR is not set +CONFIG_CMD_TFTPPUT=y +CONFIG_CMD_DHCP=y +CONFIG_CMD_MII=y +CONFIG_CMD_PING=y +CONFIG_CMD_CACHE=y +CONFIG_CMD_EXT2=y +CONFIG_CMD_EXT4=y +CONFIG_CMD_EXT4_WRITE=y +CONFIG_CMD_FAT=y +CONFIG_CMD_FS_GENERIC=y +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_SPL_DM_SEQ_ALIAS=y +CONFIG_DFU_MMC=y +CONFIG_DFU_RAM=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_ZYNQ=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_BAR=y +CONFIG_SPI_FLASH_SPANSION=y +CONFIG_SPI_FLASH_STMICRO=y +CONFIG_SPI_FLASH_WINBOND=y +CONFIG_ZYNQ_GEM=y +CONFIG_DEBUG_UART_ZYNQ=y +CONFIG_DEBUG_UART_BASE=0xe0001000 +CONFIG_DEBUG_UART_CLOCK=50000000 +CONFIG_ZYNQ_QSPI=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_ULPI_VIEWPORT=y +CONFIG_USB_ULPI=y +CONFIG_USB_STORAGE=y +CONFIG_USB_GADGET=y +CONFIG_CI_UDC=y +CONFIG_USB_GADGET_DOWNLOAD=y +CONFIG_G_DNL_MANUFACTURER="Xilinx" +CONFIG_G_DNL_VENDOR_NUM=0x03FD +CONFIG_G_DNL_PRODUCT_NUM=0x0300 From 60873f736e46c0b2fe80c8c0d322bcdbdb8acc63 Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Thu, 13 Jul 2017 19:01:08 +0530 Subject: [PATCH 05/22] common: board_f: Make reserve_mmu a weak function Make reserve_mmu a weak so that it provides an option to customize this routine as per platform need Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek Reviewed-by: Simon Glass --- common/board_f.c | 2 +- include/common.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/board_f.c b/common/board_f.c index 19b80556be..5915e500ae 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -340,7 +340,7 @@ static int reserve_round_4k(void) } #ifdef CONFIG_ARM -static int reserve_mmu(void) +__weak int reserve_mmu(void) { #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) /* reserve TLB table */ diff --git a/include/common.h b/include/common.h index 751665f8a4..c8fb277cde 100644 --- a/include/common.h +++ b/include/common.h @@ -286,6 +286,7 @@ void board_show_dram(phys_size_t size); */ int arch_fixup_fdt(void *blob); +int reserve_mmu(void); /* common/flash.c */ void flash_perror (int); From ad76f8cedf61fa1e307a4b4243e3ff58ade8ec9e Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Fri, 3 Feb 2017 23:56:49 +0530 Subject: [PATCH 06/22] clk: zynqmp: Add support for CCF driver Add support for CCF, this CCF reads the ref clocks from dt and checks all the required clock control registers for its source , divisors and calculates the clock from them. This supports clock and set functions. Panic when read/write fails. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek Reviewed-by: Simon Glass --- drivers/clk/clk_zynqmp.c | 703 ++++++++++++++++++++++++++++++--------- 1 file changed, 541 insertions(+), 162 deletions(-) diff --git a/drivers/clk/clk_zynqmp.c b/drivers/clk/clk_zynqmp.c index 50eaf31613..247b55e850 100644 --- a/drivers/clk/clk_zynqmp.c +++ b/drivers/clk/clk_zynqmp.c @@ -10,217 +10,595 @@ #include #include #include +#include #include -#define ZYNQMP_GEM0_REF_CTRL 0xFF5E0050 -#define ZYNQMP_IOPLL_CTRL 0xFF5E0020 -#define ZYNQMP_RPLL_CTRL 0xFF5E0030 -#define ZYNQMP_DPLL_CTRL 0xFD1A002C -#define ZYNQMP_SIP_SVC_MMIO_WRITE 0xC2000013 -#define ZYNQMP_SIP_SVC_MMIO_WRITE 0xC2000013 -#define ZYNQMP_SIP_SVC_MMIO_WRITE 0xC2000013 -#define ZYNQMP_SIP_SVC_MMIO_READ 0xC2000014 -#define ZYNQMP_DIV_MAX_VAL 0x3F -#define ZYNQMP_DIV1_SHFT 8 -#define ZYNQMP_DIV1_SHFT 8 -#define ZYNQMP_DIV2_SHFT 16 -#define ZYNQMP_DIV_MASK 0x3F -#define ZYNQMP_PLL_CTRL_FBDIV_MASK 0x7F -#define ZYNQMP_PLL_CTRL_FBDIV_SHFT 8 -#define ZYNQMP_GEM_REF_CTRL_SRC_MASK 0x7 -#define ZYNQMP_GEM0_CLK_ID 45 -#define ZYNQMP_GEM1_CLK_ID 46 -#define ZYNQMP_GEM2_CLK_ID 47 -#define ZYNQMP_GEM3_CLK_ID 48 +DECLARE_GLOBAL_DATA_PTR; -static unsigned long pss_ref_clk; +static const resource_size_t zynqmp_crf_apb_clkc_base = 0xfd1a0020; +static const resource_size_t zynqmp_crl_apb_clkc_base = 0xff5e0020; +static const resource_size_t zynqmp_iou_clkc_base = 0xff180000; -static int zynqmp_calculate_divisors(unsigned long req_rate, - unsigned long parent_rate, - u32 *div1, u32 *div2) +/* Full power domain clocks */ +#define CRF_APB_APLL_CTRL (zynqmp_crf_apb_clkc_base + 0x00) +#define CRF_APB_DPLL_CTRL (zynqmp_crf_apb_clkc_base + 0x0c) +#define CRF_APB_VPLL_CTRL (zynqmp_crf_apb_clkc_base + 0x18) +#define CRF_APB_PLL_STATUS (zynqmp_crf_apb_clkc_base + 0x24) +#define CRF_APB_APLL_TO_LPD_CTRL (zynqmp_crf_apb_clkc_base + 0x28) +#define CRF_APB_DPLL_TO_LPD_CTRL (zynqmp_crf_apb_clkc_base + 0x2c) +#define CRF_APB_VPLL_TO_LPD_CTRL (zynqmp_crf_apb_clkc_base + 0x30) +/* Peripheral clocks */ +#define CRF_APB_ACPU_CTRL (zynqmp_crf_apb_clkc_base + 0x40) +#define CRF_APB_DBG_TRACE_CTRL (zynqmp_crf_apb_clkc_base + 0x44) +#define CRF_APB_DBG_FPD_CTRL (zynqmp_crf_apb_clkc_base + 0x48) +#define CRF_APB_DP_VIDEO_REF_CTRL (zynqmp_crf_apb_clkc_base + 0x50) +#define CRF_APB_DP_AUDIO_REF_CTRL (zynqmp_crf_apb_clkc_base + 0x54) +#define CRF_APB_DP_STC_REF_CTRL (zynqmp_crf_apb_clkc_base + 0x5c) +#define CRF_APB_DDR_CTRL (zynqmp_crf_apb_clkc_base + 0x60) +#define CRF_APB_GPU_REF_CTRL (zynqmp_crf_apb_clkc_base + 0x64) +#define CRF_APB_SATA_REF_CTRL (zynqmp_crf_apb_clkc_base + 0x80) +#define CRF_APB_PCIE_REF_CTRL (zynqmp_crf_apb_clkc_base + 0x94) +#define CRF_APB_GDMA_REF_CTRL (zynqmp_crf_apb_clkc_base + 0x98) +#define CRF_APB_DPDMA_REF_CTRL (zynqmp_crf_apb_clkc_base + 0x9c) +#define CRF_APB_TOPSW_MAIN_CTRL (zynqmp_crf_apb_clkc_base + 0xa0) +#define CRF_APB_TOPSW_LSBUS_CTRL (zynqmp_crf_apb_clkc_base + 0xa4) +#define CRF_APB_GTGREF0_REF_CTRL (zynqmp_crf_apb_clkc_base + 0xa8) +#define CRF_APB_DBG_TSTMP_CTRL (zynqmp_crf_apb_clkc_base + 0xd8) + +/* Low power domain clocks */ +#define CRL_APB_IOPLL_CTRL (zynqmp_crl_apb_clkc_base + 0x00) +#define CRL_APB_RPLL_CTRL (zynqmp_crl_apb_clkc_base + 0x10) +#define CRL_APB_PLL_STATUS (zynqmp_crl_apb_clkc_base + 0x20) +#define CRL_APB_IOPLL_TO_FPD_CTRL (zynqmp_crl_apb_clkc_base + 0x24) +#define CRL_APB_RPLL_TO_FPD_CTRL (zynqmp_crl_apb_clkc_base + 0x28) +/* Peripheral clocks */ +#define CRL_APB_USB3_DUAL_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x2c) +#define CRL_APB_GEM0_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x30) +#define CRL_APB_GEM1_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x34) +#define CRL_APB_GEM2_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x38) +#define CRL_APB_GEM3_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x3c) +#define CRL_APB_USB0_BUS_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x40) +#define CRL_APB_USB1_BUS_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x44) +#define CRL_APB_QSPI_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x48) +#define CRL_APB_SDIO0_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x4c) +#define CRL_APB_SDIO1_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x50) +#define CRL_APB_UART0_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x54) +#define CRL_APB_UART1_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x58) +#define CRL_APB_SPI0_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x5c) +#define CRL_APB_SPI1_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x60) +#define CRL_APB_CAN0_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x64) +#define CRL_APB_CAN1_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x68) +#define CRL_APB_CPU_R5_CTRL (zynqmp_crl_apb_clkc_base + 0x70) +#define CRL_APB_IOU_SWITCH_CTRL (zynqmp_crl_apb_clkc_base + 0x7c) +#define CRL_APB_CSU_PLL_CTRL (zynqmp_crl_apb_clkc_base + 0x80) +#define CRL_APB_PCAP_CTRL (zynqmp_crl_apb_clkc_base + 0x84) +#define CRL_APB_LPD_SWITCH_CTRL (zynqmp_crl_apb_clkc_base + 0x88) +#define CRL_APB_LPD_LSBUS_CTRL (zynqmp_crl_apb_clkc_base + 0x8c) +#define CRL_APB_DBG_LPD_CTRL (zynqmp_crl_apb_clkc_base + 0x90) +#define CRL_APB_NAND_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x94) +#define CRL_APB_ADMA_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x98) +#define CRL_APB_PL0_REF_CTRL (zynqmp_crl_apb_clkc_base + 0xa0) +#define CRL_APB_PL1_REF_CTRL (zynqmp_crl_apb_clkc_base + 0xa4) +#define CRL_APB_PL2_REF_CTRL (zynqmp_crl_apb_clkc_base + 0xa8) +#define CRL_APB_PL3_REF_CTRL (zynqmp_crl_apb_clkc_base + 0xac) +#define CRL_APB_PL0_THR_CNT (zynqmp_crl_apb_clkc_base + 0xb4) +#define CRL_APB_PL1_THR_CNT (zynqmp_crl_apb_clkc_base + 0xbc) +#define CRL_APB_PL2_THR_CNT (zynqmp_crl_apb_clkc_base + 0xc4) +#define CRL_APB_PL3_THR_CNT (zynqmp_crl_apb_clkc_base + 0xdc) +#define CRL_APB_GEM_TSU_REF_CTRL (zynqmp_crl_apb_clkc_base + 0xe0) +#define CRL_APB_DLL_REF_CTRL (zynqmp_crl_apb_clkc_base + 0xe4) +#define CRL_APB_AMS_REF_CTRL (zynqmp_crl_apb_clkc_base + 0xe8) +#define CRL_APB_I2C0_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x100) +#define CRL_APB_I2C1_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x104) +#define CRL_APB_TIMESTAMP_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x108) +#define IOU_SLCR_GEM_CLK_CTRL (zynqmp_iou_clkc_base + 0x308) +#define IOU_SLCR_CAN_MIO_CTRL (zynqmp_iou_clkc_base + 0x304) +#define IOU_SLCR_WDT_CLK_SEL (zynqmp_iou_clkc_base + 0x300) + +#define ZYNQ_CLK_MAXDIV 0x3f +#define CLK_CTRL_DIV1_SHIFT 16 +#define CLK_CTRL_DIV1_MASK (ZYNQ_CLK_MAXDIV << CLK_CTRL_DIV1_SHIFT) +#define CLK_CTRL_DIV0_SHIFT 8 +#define CLK_CTRL_DIV0_MASK (ZYNQ_CLK_MAXDIV << CLK_CTRL_DIV0_SHIFT) +#define CLK_CTRL_SRCSEL_SHIFT 0 +#define CLK_CTRL_SRCSEL_MASK (0x3 << CLK_CTRL_SRCSEL_SHIFT) +#define PLLCTRL_FBDIV_MASK 0x7f00 +#define PLLCTRL_FBDIV_SHIFT 8 +#define PLLCTRL_RESET_MASK 1 +#define PLLCTRL_RESET_SHIFT 0 +#define PLLCTRL_BYPASS_MASK 0x8 +#define PLLCTRL_BYPASS_SHFT 3 +#define PLLCTRL_POST_SRC_SHFT 24 +#define PLLCTRL_POST_SRC_MASK (0x7 << PLLCTRL_POST_SRC_SHFT) + + +#define NUM_MIO_PINS 77 + +enum zynqmp_clk { + iopll, rpll, + apll, dpll, vpll, + iopll_to_fpd, rpll_to_fpd, apll_to_lpd, dpll_to_lpd, vpll_to_lpd, + acpu, acpu_half, + dbg_fpd, dbg_lpd, dbg_trace, dbg_tstmp, + dp_video_ref, dp_audio_ref, + dp_stc_ref, gdma_ref, dpdma_ref, + ddr_ref, sata_ref, pcie_ref, + gpu_ref, gpu_pp0_ref, gpu_pp1_ref, + topsw_main, topsw_lsbus, + gtgref0_ref, + lpd_switch, lpd_lsbus, + usb0_bus_ref, usb1_bus_ref, usb3_dual_ref, usb0, usb1, + cpu_r5, cpu_r5_core, + csu_spb, csu_pll, pcap, + iou_switch, + gem_tsu_ref, gem_tsu, + gem0_ref, gem1_ref, gem2_ref, gem3_ref, + gem0_rx, gem1_rx, gem2_rx, gem3_rx, + qspi_ref, + sdio0_ref, sdio1_ref, + uart0_ref, uart1_ref, + spi0_ref, spi1_ref, + nand_ref, + i2c0_ref, i2c1_ref, can0_ref, can1_ref, can0, can1, + dll_ref, + adma_ref, + timestamp_ref, + ams_ref, + pl0, pl1, pl2, pl3, + wdt, + clk_max, +}; + +static const char * const clk_names[clk_max] = { + "iopll", "rpll", "apll", "dpll", + "vpll", "iopll_to_fpd", "rpll_to_fpd", + "apll_to_lpd", "dpll_to_lpd", "vpll_to_lpd", + "acpu", "acpu_half", "dbf_fpd", "dbf_lpd", + "dbg_trace", "dbg_tstmp", "dp_video_ref", + "dp_audio_ref", "dp_stc_ref", "gdma_ref", + "dpdma_ref", "ddr_ref", "sata_ref", "pcie_ref", + "gpu_ref", "gpu_pp0_ref", "gpu_pp1_ref", + "topsw_main", "topsw_lsbus", "gtgref0_ref", + "lpd_switch", "lpd_lsbus", "usb0_bus_ref", + "usb1_bus_ref", "usb3_dual_ref", "usb0", + "usb1", "cpu_r5", "cpu_r5_core", "csu_spb", + "csu_pll", "pcap", "iou_switch", "gem_tsu_ref", + "gem_tsu", "gem0_ref", "gem1_ref", "gem2_ref", + "gem3_ref", "gem0_tx", "gem1_tx", "gem2_tx", + "gem3_tx", "qspi_ref", "sdio0_ref", "sdio1_ref", + "uart0_ref", "uart1_ref", "spi0_ref", + "spi1_ref", "nand_ref", "i2c0_ref", "i2c1_ref", + "can0_ref", "can1_ref", "can0", "can1", + "dll_ref", "adma_ref", "timestamp_ref", + "ams_ref", "pl0", "pl1", "pl2", "pl3", "wdt" +}; + +struct zynqmp_clk_priv { + unsigned long ps_clk_freq; + unsigned long video_clk; + unsigned long pss_alt_ref_clk; + unsigned long gt_crx_ref_clk; + unsigned long aux_ref_clk; +}; + +static u32 zynqmp_clk_get_register(enum zynqmp_clk id) { - u32 req_div = 1; - u32 i; + switch (id) { + case iopll: + return CRL_APB_IOPLL_CTRL; + case rpll: + return CRL_APB_RPLL_CTRL; + case apll: + return CRF_APB_APLL_CTRL; + case dpll: + return CRF_APB_DPLL_CTRL; + case vpll: + return CRF_APB_VPLL_CTRL; + case acpu: + return CRF_APB_ACPU_CTRL; + case ddr_ref: + return CRF_APB_DDR_CTRL; + case qspi_ref: + return CRL_APB_QSPI_REF_CTRL; + case gem0_ref: + return CRL_APB_GEM0_REF_CTRL; + case gem1_ref: + return CRL_APB_GEM1_REF_CTRL; + case gem2_ref: + return CRL_APB_GEM2_REF_CTRL; + case gem3_ref: + return CRL_APB_GEM3_REF_CTRL; + case uart0_ref: + return CRL_APB_UART0_REF_CTRL; + case uart1_ref: + return CRL_APB_UART1_REF_CTRL; + case sdio0_ref: + return CRL_APB_SDIO0_REF_CTRL; + case sdio1_ref: + return CRL_APB_SDIO1_REF_CTRL; + case spi0_ref: + return CRL_APB_SPI0_REF_CTRL; + case spi1_ref: + return CRL_APB_SPI1_REF_CTRL; + case nand_ref: + return CRL_APB_NAND_REF_CTRL; + case i2c0_ref: + return CRL_APB_I2C0_REF_CTRL; + case i2c1_ref: + return CRL_APB_I2C1_REF_CTRL; + case can0_ref: + return CRL_APB_CAN0_REF_CTRL; + case can1_ref: + return CRL_APB_CAN1_REF_CTRL; + default: + debug("Invalid clk id%d\n", id); + } + return 0; +} - /* - * calculate two divisors to get - * required rate and each divisor - * should be less than 63 - */ - req_div = DIV_ROUND_UP(parent_rate, req_rate); +static enum zynqmp_clk zynqmp_clk_get_cpu_pll(u32 clk_ctrl) +{ + u32 srcsel = (clk_ctrl & CLK_CTRL_SRCSEL_MASK) >> + CLK_CTRL_SRCSEL_SHIFT; - for (i = 1; i <= req_div; i++) { - if ((req_div % i) == 0) { - *div1 = req_div / i; - *div2 = i; - if ((*div1 < ZYNQMP_DIV_MAX_VAL) && - (*div2 < ZYNQMP_DIV_MAX_VAL)) - return 0; + switch (srcsel) { + case 2: + return dpll; + case 3: + return vpll; + case 0 ... 1: + default: + return apll; + } +} + +static enum zynqmp_clk zynqmp_clk_get_ddr_pll(u32 clk_ctrl) +{ + u32 srcsel = (clk_ctrl & CLK_CTRL_SRCSEL_MASK) >> + CLK_CTRL_SRCSEL_SHIFT; + + switch (srcsel) { + case 1: + return vpll; + case 0: + default: + return dpll; + } +} + +static enum zynqmp_clk zynqmp_clk_get_peripheral_pll(u32 clk_ctrl) +{ + u32 srcsel = (clk_ctrl & CLK_CTRL_SRCSEL_MASK) >> + CLK_CTRL_SRCSEL_SHIFT; + + switch (srcsel) { + case 2: + return rpll; + case 3: + return dpll; + case 0 ... 1: + default: + return iopll; + } +} + +static ulong zynqmp_clk_get_pll_src(ulong clk_ctrl, + struct zynqmp_clk_priv *priv, + bool is_pre_src) +{ + u32 src_sel; + + if (is_pre_src) + src_sel = (clk_ctrl & PLLCTRL_POST_SRC_MASK) >> + PLLCTRL_POST_SRC_SHFT; + else + src_sel = (clk_ctrl & PLLCTRL_POST_SRC_MASK) >> + PLLCTRL_POST_SRC_SHFT; + + switch (src_sel) { + case 4: + return priv->video_clk; + case 5: + return priv->pss_alt_ref_clk; + case 6: + return priv->aux_ref_clk; + case 7: + return priv->gt_crx_ref_clk; + case 0 ... 3: + default: + return priv->ps_clk_freq; + } +} + +static ulong zynqmp_clk_get_pll_rate(struct zynqmp_clk_priv *priv, + enum zynqmp_clk id) +{ + u32 clk_ctrl, reset, mul; + ulong freq; + int ret; + + ret = zynqmp_mmio_read(zynqmp_clk_get_register(id), &clk_ctrl); + if (ret) + panic("%s mio read fail\n", __func__); + + if (clk_ctrl & PLLCTRL_BYPASS_MASK) + freq = zynqmp_clk_get_pll_src(clk_ctrl, priv, 0); + else + freq = zynqmp_clk_get_pll_src(clk_ctrl, priv, 1); + + reset = (clk_ctrl & PLLCTRL_RESET_MASK) >> PLLCTRL_RESET_SHIFT; + if (reset && !(clk_ctrl & PLLCTRL_BYPASS_MASK)) + return 0; + + mul = (clk_ctrl & PLLCTRL_FBDIV_MASK) >> PLLCTRL_FBDIV_SHIFT; + + freq *= mul; + + if (clk_ctrl & (1 << 16)) + freq /= 2; + + return freq; +} + +static ulong zynqmp_clk_get_cpu_rate(struct zynqmp_clk_priv *priv, + enum zynqmp_clk id) +{ + u32 clk_ctrl, div; + enum zynqmp_clk pll; + int ret; + + ret = zynqmp_mmio_read(CRF_APB_ACPU_CTRL, &clk_ctrl); + if (ret) + panic("%s mio read fail\n", __func__); + + + div = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; + + pll = zynqmp_clk_get_cpu_pll(clk_ctrl); + + return DIV_ROUND_CLOSEST(zynqmp_clk_get_pll_rate(priv, pll), div); +} + +static ulong zynqmp_clk_get_ddr_rate(struct zynqmp_clk_priv *priv) +{ + u32 clk_ctrl, div; + enum zynqmp_clk pll; + int ret; + + ret = zynqmp_mmio_read(CRF_APB_DDR_CTRL, &clk_ctrl); + if (ret) + panic("%s mio read fail\n", __func__); + + div = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; + + pll = zynqmp_clk_get_ddr_pll(clk_ctrl); + + return DIV_ROUND_CLOSEST(zynqmp_clk_get_pll_rate(priv, pll), div); +} + +static ulong zynqmp_clk_get_peripheral_rate(struct zynqmp_clk_priv *priv, + enum zynqmp_clk id, bool two_divs) +{ + enum zynqmp_clk pll; + u32 clk_ctrl, div0; + u32 div1 = 1; + int ret; + + ret = zynqmp_mmio_read(zynqmp_clk_get_register(id), &clk_ctrl); + if (ret) + panic("%s mio read fail\n", __func__); + + div0 = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; + if (!div0) + div0 = 1; + + if (two_divs) { + div1 = (clk_ctrl & CLK_CTRL_DIV1_MASK) >> CLK_CTRL_DIV1_SHIFT; + if (!div1) + div1 = 1; + } + + pll = zynqmp_clk_get_peripheral_pll(clk_ctrl); + + return + DIV_ROUND_CLOSEST( + DIV_ROUND_CLOSEST( + zynqmp_clk_get_pll_rate(priv, pll), div0), + div1); +} + +static unsigned long zynqmp_clk_calc_peripheral_two_divs(ulong rate, + ulong pll_rate, + u32 *div0, u32 *div1) +{ + long new_err, best_err = (long)(~0UL >> 1); + ulong new_rate, best_rate = 0; + u32 d0, d1; + + for (d0 = 1; d0 <= ZYNQ_CLK_MAXDIV; d0++) { + for (d1 = 1; d1 <= ZYNQ_CLK_MAXDIV >> 1; d1++) { + new_rate = DIV_ROUND_CLOSEST( + DIV_ROUND_CLOSEST(pll_rate, d0), d1); + new_err = abs(new_rate - rate); + + if (new_err < best_err) { + *div0 = d0; + *div1 = d1; + best_err = new_err; + best_rate = new_rate; + } } } - return -1; + return best_rate; } -static int zynqmp_get_periph_id(unsigned long id) +static ulong zynqmp_clk_set_peripheral_rate(struct zynqmp_clk_priv *priv, + enum zynqmp_clk id, ulong rate, + bool two_divs) { - int periph_id; + enum zynqmp_clk pll; + u32 clk_ctrl, div0 = 0, div1 = 0; + ulong pll_rate, new_rate; + u32 reg; + int ret; + u32 mask; + + reg = zynqmp_clk_get_register(id); + ret = zynqmp_mmio_read(reg, &clk_ctrl); + if (ret) + panic("%s mio read fail\n", __func__); + + pll = zynqmp_clk_get_peripheral_pll(clk_ctrl); + pll_rate = zynqmp_clk_get_pll_rate(priv, pll); + clk_ctrl &= ~CLK_CTRL_DIV0_MASK; + if (two_divs) { + clk_ctrl &= ~CLK_CTRL_DIV1_MASK; + new_rate = zynqmp_clk_calc_peripheral_two_divs(rate, pll_rate, + &div0, &div1); + clk_ctrl |= div1 << CLK_CTRL_DIV1_SHIFT; + } else { + div0 = DIV_ROUND_CLOSEST(pll_rate, rate); + if (div0 > ZYNQ_CLK_MAXDIV) + div0 = ZYNQ_CLK_MAXDIV; + new_rate = DIV_ROUND_CLOSEST(rate, div0); + } + clk_ctrl |= div0 << CLK_CTRL_DIV0_SHIFT; + + mask = (ZYNQ_CLK_MAXDIV << CLK_CTRL_DIV0_SHIFT) | + (ZYNQ_CLK_MAXDIV << CLK_CTRL_DIV1_SHIFT); + + ret = zynqmp_mmio_write(reg, mask, clk_ctrl); + if (ret) + panic("%s mio write fail\n", __func__); + + return new_rate; +} + +static ulong zynqmp_clk_get_rate(struct clk *clk) +{ + struct zynqmp_clk_priv *priv = dev_get_priv(clk->dev); + enum zynqmp_clk id = clk->id; + bool two_divs = false; switch (id) { - case ZYNQMP_GEM0_CLK_ID: - periph_id = 0; - break; - case ZYNQMP_GEM1_CLK_ID: - periph_id = 1; - break; - case ZYNQMP_GEM2_CLK_ID: - periph_id = 2; - break; - case ZYNQMP_GEM3_CLK_ID: - periph_id = 3; - break; + case iopll ... vpll: + return zynqmp_clk_get_pll_rate(priv, id); + case acpu: + return zynqmp_clk_get_cpu_rate(priv, id); + case ddr_ref: + return zynqmp_clk_get_ddr_rate(priv); + case gem0_ref ... gem3_ref: + case qspi_ref ... can1_ref: + two_divs = true; + return zynqmp_clk_get_peripheral_rate(priv, id, two_divs); default: - printf("%s, Invalid clock id:%ld\n", __func__, id); - return -EINVAL; + return -ENXIO; } - - return periph_id; } -static int zynqmp_set_clk(unsigned long id, u32 div1, u32 div2) +static ulong zynqmp_clk_set_rate(struct clk *clk, ulong rate) { - struct pt_regs regs; - ulong reg; - u32 mask, value; + struct zynqmp_clk_priv *priv = dev_get_priv(clk->dev); + enum zynqmp_clk id = clk->id; + bool two_divs = true; - id = zynqmp_get_periph_id(id); - if (id < 0) - return -EINVAL; - - reg = (ulong)((u32 *)ZYNQMP_GEM0_REF_CTRL + id); - mask = (ZYNQMP_DIV_MASK << ZYNQMP_DIV1_SHFT) | - (ZYNQMP_DIV_MASK << ZYNQMP_DIV2_SHFT); - value = (div1 << ZYNQMP_DIV1_SHFT) | (div2 << ZYNQMP_DIV2_SHFT); - - debug("%s: reg:0x%lx, mask:0x%x, value:0x%x\n", __func__, reg, mask, - value); - - regs.regs[0] = ZYNQMP_SIP_SVC_MMIO_WRITE; - regs.regs[1] = ((u64)mask << 32) | reg; - regs.regs[2] = value; - regs.regs[3] = 0; - - smc_call(®s); - - return regs.regs[0]; -} - -static unsigned long zynqmp_clk_get_rate(struct clk *clk) -{ - struct pt_regs regs; - ulong reg; - unsigned long value; - int id; - - id = zynqmp_get_periph_id(clk->id); - if (id < 0) - return -EINVAL; - - reg = (ulong)((u32 *)ZYNQMP_GEM0_REF_CTRL + id); - - regs.regs[0] = ZYNQMP_SIP_SVC_MMIO_READ; - regs.regs[1] = reg; - regs.regs[2] = 0; - regs.regs[3] = 0; - - smc_call(®s); - - value = upper_32_bits(regs.regs[0]); - - value &= ZYNQMP_GEM_REF_CTRL_SRC_MASK; - - switch (value) { - case 0: - regs.regs[1] = ZYNQMP_IOPLL_CTRL; - break; - case 2: - regs.regs[1] = ZYNQMP_RPLL_CTRL; - break; - case 3: - regs.regs[1] = ZYNQMP_DPLL_CTRL; - break; + switch (id) { + case gem0_ref ... gem3_ref: + case qspi_ref ... can1_ref: + return zynqmp_clk_set_peripheral_rate(priv, id, + rate, two_divs); default: - return -EINVAL; + return -ENXIO; } - - regs.regs[0] = ZYNQMP_SIP_SVC_MMIO_READ; - regs.regs[2] = 0; - regs.regs[3] = 0; - - smc_call(®s); - - value = upper_32_bits(regs.regs[0]) & - (ZYNQMP_PLL_CTRL_FBDIV_MASK << - ZYNQMP_PLL_CTRL_FBDIV_SHFT); - value >>= ZYNQMP_PLL_CTRL_FBDIV_SHFT; - value *= pss_ref_clk; - - return value; } -static ulong zynqmp_clk_set_rate(struct clk *clk, unsigned long clk_rate) +int soc_clk_dump(void) { - int ret; - u32 div1 = 0; - u32 div2 = 0; - unsigned long input_clk; + struct udevice *dev; + int i, ret; - input_clk = zynqmp_clk_get_rate(clk); - if (IS_ERR_VALUE(input_clk)) { - dev_err(dev, "failed to get input_clk\n"); - return -EINVAL; - } + ret = uclass_get_device_by_driver(UCLASS_CLK, + DM_GET_DRIVER(zynqmp_clk), &dev); + if (ret) + return ret; - debug("%s: i/p CLK %ld, clk_rate:0x%ld\n", __func__, input_clk, - clk_rate); + printf("clk\t\tfrequency\n"); + for (i = 0; i < clk_max; i++) { + const char *name = clk_names[i]; + if (name) { + struct clk clk; + unsigned long rate; - ret = zynqmp_calculate_divisors(clk_rate, input_clk, &div1, &div2); - if (ret) { - dev_err(dev, "failed to proper divisors\n"); - return -EINVAL; - } + clk.id = i; + ret = clk_request(dev, &clk); + if (ret < 0) + return ret; - debug("%s: Div1:%d, Div2:%d\n", __func__, div1, div2); + rate = clk_get_rate(&clk); - ret = zynqmp_set_clk(clk->id, div1, div2); - if (ret) { - dev_err(dev, "failed to set gem clk\n"); - return -EINVAL; + clk_free(&clk); + + if ((rate == (unsigned long)-ENOSYS) || + (rate == (unsigned long)-ENXIO)) + printf("%10s%20s\n", name, "unknown"); + else + printf("%10s%20lu\n", name, rate); + } } return 0; } -static int zynqmp_clk_probe(struct udevice *dev) +static int zynqmp_get_freq_by_name(char *name, struct udevice *dev, ulong *freq) { struct clk clk; int ret; - debug("%s\n", __func__); - ret = clk_get_by_name(dev, "pss_ref_clk", &clk); + ret = clk_get_by_name(dev, name, &clk); if (ret < 0) { - dev_err(dev, "failed to get pss_ref_clk\n"); + dev_err(dev, "failed to get %s\n", name); return ret; } - pss_ref_clk = clk_get_rate(&clk); - if (IS_ERR_VALUE(pss_ref_clk)) { - dev_err(dev, "failed to get rate pss_ref_clk\n"); + *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 zynqmp_clk_probe(struct udevice *dev) +{ + int ret; + struct zynqmp_clk_priv *priv = dev_get_priv(dev); + + debug("%s\n", __func__); + ret = zynqmp_get_freq_by_name("pss_ref_clk", dev, &priv->ps_clk_freq); + if (ret < 0) + return -EINVAL; + + ret = zynqmp_get_freq_by_name("video_clk", dev, &priv->video_clk); + if (ret < 0) + return -EINVAL; + + ret = zynqmp_get_freq_by_name("pss_alt_ref_clk", dev, + &priv->pss_alt_ref_clk); + if (ret < 0) + return -EINVAL; + + ret = zynqmp_get_freq_by_name("aux_ref_clk", dev, &priv->aux_ref_clk); + if (ret < 0) + return -EINVAL; + + ret = zynqmp_get_freq_by_name("gt_crx_ref_clk", dev, + &priv->gt_crx_ref_clk); + if (ret < 0) + return -EINVAL; + + return 0; +} static struct clk_ops zynqmp_clk_ops = { .set_rate = zynqmp_clk_set_rate, @@ -238,4 +616,5 @@ U_BOOT_DRIVER(zynqmp_clk) = { .of_match = zynqmp_clk_ids, .probe = zynqmp_clk_probe, .ops = &zynqmp_clk_ops, + .priv_auto_alloc_size = sizeof(struct zynqmp_clk_priv), }; From 154799ac956ac991249366801b70c9714a21e533 Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Thu, 13 Apr 2017 16:59:38 +0530 Subject: [PATCH 07/22] clk: zynqmp: Dont panic incase of mmio write/read failures Dont panic incase of mmio write/read failures instead return error and let the peripheral driver take care of clock get and set failures. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek Reviewed-by: Simon Glass --- drivers/clk/clk_zynqmp.c | 63 ++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/drivers/clk/clk_zynqmp.c b/drivers/clk/clk_zynqmp.c index 247b55e850..b5469f73ee 100644 --- a/drivers/clk/clk_zynqmp.c +++ b/drivers/clk/clk_zynqmp.c @@ -318,8 +318,10 @@ static ulong zynqmp_clk_get_pll_rate(struct zynqmp_clk_priv *priv, int ret; ret = zynqmp_mmio_read(zynqmp_clk_get_register(id), &clk_ctrl); - if (ret) - panic("%s mio read fail\n", __func__); + if (ret) { + printf("%s mio read fail\n", __func__); + return -EIO; + } if (clk_ctrl & PLLCTRL_BYPASS_MASK) freq = zynqmp_clk_get_pll_src(clk_ctrl, priv, 0); @@ -346,17 +348,22 @@ static ulong zynqmp_clk_get_cpu_rate(struct zynqmp_clk_priv *priv, u32 clk_ctrl, div; enum zynqmp_clk pll; int ret; + unsigned long pllrate; ret = zynqmp_mmio_read(CRF_APB_ACPU_CTRL, &clk_ctrl); - if (ret) - panic("%s mio read fail\n", __func__); - + if (ret) { + printf("%s mio read fail\n", __func__); + return -EIO; + } div = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; pll = zynqmp_clk_get_cpu_pll(clk_ctrl); + pllrate = zynqmp_clk_get_pll_rate(priv, pll); + if (IS_ERR_VALUE(pllrate)) + return pllrate; - return DIV_ROUND_CLOSEST(zynqmp_clk_get_pll_rate(priv, pll), div); + return DIV_ROUND_CLOSEST(pllrate, div); } static ulong zynqmp_clk_get_ddr_rate(struct zynqmp_clk_priv *priv) @@ -364,16 +371,22 @@ static ulong zynqmp_clk_get_ddr_rate(struct zynqmp_clk_priv *priv) u32 clk_ctrl, div; enum zynqmp_clk pll; int ret; + ulong pllrate; ret = zynqmp_mmio_read(CRF_APB_DDR_CTRL, &clk_ctrl); - if (ret) - panic("%s mio read fail\n", __func__); + if (ret) { + printf("%s mio read fail\n", __func__); + return -EIO; + } div = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; pll = zynqmp_clk_get_ddr_pll(clk_ctrl); + pllrate = zynqmp_clk_get_pll_rate(priv, pll); + if (IS_ERR_VALUE(pllrate)) + return pllrate; - return DIV_ROUND_CLOSEST(zynqmp_clk_get_pll_rate(priv, pll), div); + return DIV_ROUND_CLOSEST(pllrate, div); } static ulong zynqmp_clk_get_peripheral_rate(struct zynqmp_clk_priv *priv, @@ -383,10 +396,13 @@ static ulong zynqmp_clk_get_peripheral_rate(struct zynqmp_clk_priv *priv, u32 clk_ctrl, div0; u32 div1 = 1; int ret; + ulong pllrate; ret = zynqmp_mmio_read(zynqmp_clk_get_register(id), &clk_ctrl); - if (ret) - panic("%s mio read fail\n", __func__); + if (ret) { + printf("%s mio read fail\n", __func__); + return -EIO; + } div0 = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; if (!div0) @@ -399,12 +415,13 @@ static ulong zynqmp_clk_get_peripheral_rate(struct zynqmp_clk_priv *priv, } pll = zynqmp_clk_get_peripheral_pll(clk_ctrl); + pllrate = zynqmp_clk_get_pll_rate(priv, pll); + if (IS_ERR_VALUE(pllrate)) + return pllrate; return DIV_ROUND_CLOSEST( - DIV_ROUND_CLOSEST( - zynqmp_clk_get_pll_rate(priv, pll), div0), - div1); + DIV_ROUND_CLOSEST(pllrate, div0), div1); } static unsigned long zynqmp_clk_calc_peripheral_two_divs(ulong rate, @@ -446,11 +463,16 @@ static ulong zynqmp_clk_set_peripheral_rate(struct zynqmp_clk_priv *priv, reg = zynqmp_clk_get_register(id); ret = zynqmp_mmio_read(reg, &clk_ctrl); - if (ret) - panic("%s mio read fail\n", __func__); + if (ret) { + printf("%s mio read fail\n", __func__); + return -EIO; + } pll = zynqmp_clk_get_peripheral_pll(clk_ctrl); pll_rate = zynqmp_clk_get_pll_rate(priv, pll); + if (IS_ERR_VALUE(pll_rate)) + return pll_rate; + clk_ctrl &= ~CLK_CTRL_DIV0_MASK; if (two_divs) { clk_ctrl &= ~CLK_CTRL_DIV1_MASK; @@ -469,8 +491,10 @@ static ulong zynqmp_clk_set_peripheral_rate(struct zynqmp_clk_priv *priv, (ZYNQ_CLK_MAXDIV << CLK_CTRL_DIV1_SHIFT); ret = zynqmp_mmio_write(reg, mask, clk_ctrl); - if (ret) - panic("%s mio write fail\n", __func__); + if (ret) { + printf("%s mio write fail\n", __func__); + return -EIO; + } return new_rate; } @@ -540,7 +564,8 @@ int soc_clk_dump(void) clk_free(&clk); if ((rate == (unsigned long)-ENOSYS) || - (rate == (unsigned long)-ENXIO)) + (rate == (unsigned long)-ENXIO) || + (rate == (unsigned long)-EIO)) printf("%10s%20s\n", name, "unknown"); else printf("%10s%20lu\n", name, rate); From cf772e96905b26d4bf057c09ed267729738634a0 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 24 Apr 2017 14:06:27 +0200 Subject: [PATCH 08/22] clk: zynqmp: Remove unused macros/variables These macros and one variable is not used anywhere that's why they should be removed. Signed-off-by: Michal Simek Reviewed-by: Simon Glass --- drivers/clk/clk_zynqmp.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/clk/clk_zynqmp.c b/drivers/clk/clk_zynqmp.c index b5469f73ee..bcc62904f1 100644 --- a/drivers/clk/clk_zynqmp.c +++ b/drivers/clk/clk_zynqmp.c @@ -17,7 +17,6 @@ DECLARE_GLOBAL_DATA_PTR; static const resource_size_t zynqmp_crf_apb_clkc_base = 0xfd1a0020; static const resource_size_t zynqmp_crl_apb_clkc_base = 0xff5e0020; -static const resource_size_t zynqmp_iou_clkc_base = 0xff180000; /* Full power domain clocks */ #define CRF_APB_APLL_CTRL (zynqmp_crf_apb_clkc_base + 0x00) @@ -91,9 +90,6 @@ static const resource_size_t zynqmp_iou_clkc_base = 0xff180000; #define CRL_APB_I2C0_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x100) #define CRL_APB_I2C1_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x104) #define CRL_APB_TIMESTAMP_REF_CTRL (zynqmp_crl_apb_clkc_base + 0x108) -#define IOU_SLCR_GEM_CLK_CTRL (zynqmp_iou_clkc_base + 0x308) -#define IOU_SLCR_CAN_MIO_CTRL (zynqmp_iou_clkc_base + 0x304) -#define IOU_SLCR_WDT_CLK_SEL (zynqmp_iou_clkc_base + 0x300) #define ZYNQ_CLK_MAXDIV 0x3f #define CLK_CTRL_DIV1_SHIFT 16 From d863909f36cbe001510e47520886dbb4d1a6ba6c Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Thu, 2 Mar 2017 18:50:11 +0530 Subject: [PATCH 09/22] fpga: xilinx: Avoid using local intermediate buffer Dont use local temporary buffer for printing out the info instead use directly from memroy. This fixes the issue of stack corruprion due to local buffer overflow. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- drivers/fpga/xilinx.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/fpga/xilinx.c b/drivers/fpga/xilinx.c index 2cd0104d8b..941f30010a 100644 --- a/drivers/fpga/xilinx.c +++ b/drivers/fpga/xilinx.c @@ -29,7 +29,6 @@ int fpga_loadbitstream(int devnum, char *fpgadata, size_t size, { unsigned int length; unsigned int swapsize; - char buffer[80]; unsigned char *dataptr; unsigned int i; const fpga_desc *desc; @@ -57,10 +56,8 @@ int fpga_loadbitstream(int devnum, char *fpgadata, size_t size, length = (*dataptr << 8) + *(dataptr + 1); dataptr += 2; - for (i = 0; i < length; i++) - buffer[i] = *dataptr++; - - printf(" design filename = \"%s\"\n", buffer); + printf(" design filename = \"%s\"\n", dataptr); + dataptr += length; /* get part number (identifier, length, string) */ if (*dataptr++ != 0x62) { @@ -71,23 +68,22 @@ int fpga_loadbitstream(int devnum, char *fpgadata, size_t size, length = (*dataptr << 8) + *(dataptr + 1); dataptr += 2; - for (i = 0; i < length; i++) - buffer[i] = *dataptr++; if (xdesc->name) { - i = (ulong)strstr(buffer, xdesc->name); + i = (ulong)strstr((char *)dataptr, xdesc->name); if (!i) { printf("%s: Wrong bitstream ID for this device\n", __func__); printf("%s: Bitstream ID %s, current device ID %d/%s\n", - __func__, buffer, devnum, xdesc->name); + __func__, dataptr, devnum, xdesc->name); return FPGA_FAIL; } } else { printf("%s: Please fill correct device ID to xilinx_desc\n", __func__); } - printf(" part number = \"%s\"\n", buffer); + printf(" part number = \"%s\"\n", dataptr); + dataptr += length; /* get date (identifier, length, string) */ if (*dataptr++ != 0x63) { @@ -98,9 +94,8 @@ int fpga_loadbitstream(int devnum, char *fpgadata, size_t size, length = (*dataptr << 8) + *(dataptr+1); dataptr += 2; - for (i = 0; i < length; i++) - buffer[i] = *dataptr++; - printf(" date = \"%s\"\n", buffer); + printf(" date = \"%s\"\n", dataptr); + dataptr += length; /* get time (identifier, length, string) */ if (*dataptr++ != 0x64) { @@ -111,9 +106,8 @@ int fpga_loadbitstream(int devnum, char *fpgadata, size_t size, length = (*dataptr << 8) + *(dataptr+1); dataptr += 2; - for (i = 0; i < length; i++) - buffer[i] = *dataptr++; - printf(" time = \"%s\"\n", buffer); + printf(" time = \"%s\"\n", dataptr); + dataptr += length; /* get fpga data length (identifier, length) */ if (*dataptr++ != 0x65) { From 189bec47ab1ff1b4bec6060f199f11dab9cd7b2d Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Thu, 13 Jul 2017 19:01:10 +0530 Subject: [PATCH 10/22] arm64: zynqmp: Provide a Kconfig option to define OCM and TCM in MMU This patch provides an option to include OCM and TCM memory into MMU table with corresponding memory attributes. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- arch/arm/cpu/armv8/zynqmp/Kconfig | 6 ++++++ arch/arm/cpu/armv8/zynqmp/cpu.c | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/arch/arm/cpu/armv8/zynqmp/Kconfig b/arch/arm/cpu/armv8/zynqmp/Kconfig index 5ac48ebc4d..2a0e8f2cbf 100644 --- a/arch/arm/cpu/armv8/zynqmp/Kconfig +++ b/arch/arm/cpu/armv8/zynqmp/Kconfig @@ -56,6 +56,12 @@ config ZYNQMP_USB config SYS_MALLOC_F_LEN default 0x600 +config DEFINE_TCM_OCM_MMAP + bool "Define TCM and OCM memory in MMU Table" + help + This option if enabled defines the TCM and OCM memory and its + memory attributes in MMU table entry. + config SPL_ZYNQMP_ALT_BOOTMODE_ENABLED bool "Overwrite SPL bootmode" depends on SPL diff --git a/arch/arm/cpu/armv8/zynqmp/cpu.c b/arch/arm/cpu/armv8/zynqmp/cpu.c index 94ecf90660..5fba0716ca 100644 --- a/arch/arm/cpu/armv8/zynqmp/cpu.c +++ b/arch/arm/cpu/armv8/zynqmp/cpu.c @@ -38,6 +38,14 @@ static struct mm_region zynqmp_mem_map[] = { PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { +#if defined(CONFIG_DEFINE_TCM_OCM_MMAP) + .virt = 0xffe00000UL, + .phys = 0xffe00000UL, + .size = 0x00200000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_INNER_SHARE + }, { +#endif .virt = 0x400000000UL, .phys = 0x400000000UL, .size = 0x200000000UL, From a076789efe60ba9a9e22e47c278e03040812cde7 Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Thu, 13 Jul 2017 19:01:09 +0530 Subject: [PATCH 11/22] arm64: zynqmp: Define a way to intialize TCM TCM on ZynqMP needs to be intialized in a sequence and this patch provides a global routine to perform this as per requirement. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- arch/arm/cpu/armv8/zynqmp/mp.c | 15 +++++++++++++++ arch/arm/include/asm/arch-zynqmp/sys_proto.h | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/arch/arm/cpu/armv8/zynqmp/mp.c b/arch/arm/cpu/armv8/zynqmp/mp.c index e10fc3136c..76f889ba7d 100644 --- a/arch/arm/cpu/armv8/zynqmp/mp.c +++ b/arch/arm/cpu/armv8/zynqmp/mp.c @@ -206,6 +206,21 @@ static void write_tcm_boot_trampoline(u32 boot_addr) } } +void initialize_tcm(bool mode) +{ + if (!mode) { + set_r5_tcm_mode(LOCK); + set_r5_halt_mode(HALT, LOCK); + enable_clock_r5(); + release_r5_reset(LOCK); + } else { + set_r5_tcm_mode(SPLIT); + set_r5_halt_mode(HALT, SPLIT); + enable_clock_r5(); + release_r5_reset(SPLIT); + } +} + int cpu_release(int nr, int argc, char * const argv[]) { if (nr >= ZYNQMP_CORE_APU0 && nr <= ZYNQMP_CORE_APU3) { diff --git a/arch/arm/include/asm/arch-zynqmp/sys_proto.h b/arch/arm/include/asm/arch-zynqmp/sys_proto.h index d91d98a119..3d7fad7731 100644 --- a/arch/arm/include/asm/arch-zynqmp/sys_proto.h +++ b/arch/arm/include/asm/arch-zynqmp/sys_proto.h @@ -10,6 +10,11 @@ #define PAYLOAD_ARG_CNT 5 +enum { + TCM_LOCK, + TCM_SPLIT, +}; + int zynq_slcr_get_mio_pin_status(const char *periph); unsigned int zynqmp_get_silicon_version(void); @@ -24,4 +29,6 @@ int zynqmp_mmio_read(const u32 address, u32 *value); int invoke_smc(u32 pm_api_id, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 *ret_payload); +void initialize_tcm(bool mode); + #endif /* _ASM_ARCH_SYS_PROTO_H */ From cb186e74fb795bf38037ae9734647a581d29c15a Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Thu, 13 Jul 2017 19:01:12 +0530 Subject: [PATCH 12/22] arm64: zynqmp: Remove ifdef around zynqmp mmio read and write rotuines This patch removes ifdef around mmio read and write rotuines and make them a single routine by checking the current el. This patch helps to remove ifdef around invoke_smc as well. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- arch/arm/cpu/armv8/zynqmp/cpu.c | 63 +++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/arch/arm/cpu/armv8/zynqmp/cpu.c b/arch/arm/cpu/armv8/zynqmp/cpu.c index 5fba0716ca..1b5066a826 100644 --- a/arch/arm/cpu/armv8/zynqmp/cpu.c +++ b/arch/arm/cpu/armv8/zynqmp/cpu.c @@ -110,9 +110,8 @@ unsigned int zynqmp_get_silicon_version(void) #define ZYNQMP_MMIO_READ 0xC2000014 #define ZYNQMP_MMIO_WRITE 0xC2000013 -#ifndef CONFIG_SPL_BUILD -int invoke_smc(u32 pm_api_id, u32 arg0, u32 arg1, u32 arg2, u32 arg3, - u32 *ret_payload) +int __maybe_unused invoke_smc(u32 pm_api_id, u32 arg0, u32 arg1, u32 arg2, + u32 arg3, u32 *ret_payload) { /* * Added SIP service call Function Identifier @@ -172,28 +171,7 @@ void zynqmp_pmufw_version(void) } #endif -int zynqmp_mmio_write(const u32 address, - const u32 mask, - const u32 value) -{ - return invoke_smc(ZYNQMP_MMIO_WRITE, address, mask, value, 0, NULL); -} - -int zynqmp_mmio_read(const u32 address, u32 *value) -{ - u32 ret_payload[PAYLOAD_ARG_CNT]; - u32 ret; - - if (!value) - return -EINVAL; - - ret = invoke_smc(ZYNQMP_MMIO_READ, address, 0, 0, 0, ret_payload); - *value = ret_payload[1]; - - return ret; -} -#else -int zynqmp_mmio_write(const u32 address, +static int zynqmp_mmio_rawwrite(const u32 address, const u32 mask, const u32 value) { @@ -208,9 +186,40 @@ int zynqmp_mmio_write(const u32 address, return 0; } -int zynqmp_mmio_read(const u32 address, u32 *value) +static int zynqmp_mmio_rawread(const u32 address, u32 *value) { *value = readl((ulong)address); return 0; } -#endif + +int zynqmp_mmio_write(const u32 address, + const u32 mask, + const u32 value) +{ + if (IS_ENABLED(CONFIG_SPL_BUILD) || current_el() == 3) + return zynqmp_mmio_rawwrite(address, mask, value); + else if (!IS_ENABLED(CONFIG_SPL_BUILD)) + return invoke_smc(ZYNQMP_MMIO_WRITE, address, mask, + value, 0, NULL); + + return -EINVAL; +} + +int zynqmp_mmio_read(const u32 address, u32 *value) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + u32 ret; + + if (!value) + return -EINVAL; + + if (IS_ENABLED(CONFIG_SPL_BUILD) || current_el() == 3) { + ret = zynqmp_mmio_rawread(address, value); + } else if (!IS_ENABLED(CONFIG_SPL_BUILD)) { + ret = invoke_smc(ZYNQMP_MMIO_READ, address, 0, 0, + 0, ret_payload); + *value = ret_payload[1]; + } + + return ret; +} From 55de09292f140c82c6bc84beb7500696353ab11d Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 12 Jul 2017 13:08:41 +0200 Subject: [PATCH 13/22] arm64: zynqmp: Call psu_init from board_early_init_f For some mini platforms there could be a need to include psu_init. That's why move it to board file instead of spl only file. Signed-off-by: Michal Simek --- arch/arm/cpu/armv8/zynqmp/spl.c | 2 +- board/xilinx/zynqmp/zynqmp.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm/cpu/armv8/zynqmp/spl.c b/arch/arm/cpu/armv8/zynqmp/spl.c index 26bf80ec52..468dc1dc4d 100644 --- a/arch/arm/cpu/armv8/zynqmp/spl.c +++ b/arch/arm/cpu/armv8/zynqmp/spl.c @@ -17,7 +17,7 @@ void board_init_f(ulong dummy) { - psu_init(); + board_early_init_f(); board_early_init_r(); #ifdef CONFIG_DEBUG_UART diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c index 51a3d9f276..442637bcae 100644 --- a/board/xilinx/zynqmp/zynqmp.c +++ b/board/xilinx/zynqmp/zynqmp.c @@ -118,6 +118,11 @@ int board_early_init_f(void) #if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_CLK_ZYNQMP) zynqmp_pmufw_version(); #endif + +#if defined(CONFIG_SPL_BUILD) + psu_init(); +#endif + return 0; } From fd1b635c0636a62e109ad1c5bfd009cde078cd98 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 12 Jul 2017 13:21:27 +0200 Subject: [PATCH 14/22] arm64: zynqmp: Add Kconfig option for adding psu_init to binary There is a need to include psu_init also in mini u-boot configuration that's why handle psu_init via Kconfig property. Signed-off-by: Michal Simek --- arch/arm/cpu/armv8/zynqmp/Kconfig | 5 +++++ board/xilinx/zynqmp/Makefile | 6 +++++- board/xilinx/zynqmp/zynqmp.c | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/arm/cpu/armv8/zynqmp/Kconfig b/arch/arm/cpu/armv8/zynqmp/Kconfig index 2a0e8f2cbf..5ffc9f6c86 100644 --- a/arch/arm/cpu/armv8/zynqmp/Kconfig +++ b/arch/arm/cpu/armv8/zynqmp/Kconfig @@ -62,6 +62,11 @@ config DEFINE_TCM_OCM_MMAP This option if enabled defines the TCM and OCM memory and its memory attributes in MMU table entry. +config ZYNQMP_PSU_INIT_ENABLED + bool "Include psu_init" + help + Include psu_init to full u-boot. SPL include psu_init by default. + config SPL_ZYNQMP_ALT_BOOTMODE_ENABLED bool "Overwrite SPL bootmode" depends on SPL diff --git a/board/xilinx/zynqmp/Makefile b/board/xilinx/zynqmp/Makefile index 9d69d6546e..75aab92f04 100644 --- a/board/xilinx/zynqmp/Makefile +++ b/board/xilinx/zynqmp/Makefile @@ -20,7 +20,11 @@ $(warning Put custom psu_init_gpl.c/h to board/xilinx/zynqmp/custom_hw_platform/ endif endif -obj-$(CONFIG_SPL_BUILD) += $(init-objs) +ifdef_any_of = $(filter-out undefined,$(foreach v,$(1),$(origin $(v)))) + +ifneq ($(call ifdef_any_of, CONFIG_ZYNQMP_PSU_INIT_ENABLED CONFIG_SPL_BUILD),) +obj-y += $(init-objs) +endif # Suppress "warning: function declaration isn't a prototype" CFLAGS_REMOVE_psu_init_gpl.o := -Wstrict-prototypes diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c index 442637bcae..ecdae5e261 100644 --- a/board/xilinx/zynqmp/zynqmp.c +++ b/board/xilinx/zynqmp/zynqmp.c @@ -119,7 +119,7 @@ int board_early_init_f(void) zynqmp_pmufw_version(); #endif -#if defined(CONFIG_SPL_BUILD) +#if defined(CONFIG_SPL_BUILD) || defined(CONFIG_ZYNQMP_PSU_INIT_ENABLED) psu_init(); #endif From 926870478d1fd5e8cf6a38716c9cf1ae845435e1 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 28 Jun 2017 15:40:32 +0200 Subject: [PATCH 15/22] arm64: zynqmp: Fix SVD mask for getting chip ID Mask should start from the first bit - using 0xe is just wrong. 3bits are used that's why 0x7 mask is correct. This patch is fixing silicon ID code detection. Previous behavior was that bit0 was completely ignored. Issue was found on 2eg chip detection. Signed-off-by: Michal Simek --- include/zynqmppl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/zynqmppl.h b/include/zynqmppl.h index fb5200ec84..4c8c2f88f0 100644 --- a/include/zynqmppl.h +++ b/include/zynqmppl.h @@ -20,7 +20,7 @@ #define ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK (0xf << \ ZYNQMP_CSU_IDCODE_DEVICE_CODE_SHIFT) #define ZYNQMP_CSU_IDCODE_SVD_SHIFT 12 -#define ZYNQMP_CSU_IDCODE_SVD_MASK (0xe << ZYNQMP_CSU_IDCODE_SVD_SHIFT) +#define ZYNQMP_CSU_IDCODE_SVD_MASK (0x7 << ZYNQMP_CSU_IDCODE_SVD_SHIFT) extern struct xilinx_fpga_op zynqmp_op; From 90a35db410ae5b6123ebdb2f6233a1f9ce8d0412 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 12 Jul 2017 10:32:18 +0200 Subject: [PATCH 16/22] arm64: zynqmp: Do not setup time if already setup Newer psu_init_gpl.c/h contain clock setup. Detect if reference clock is active. If yes, skip timer setup. Signed-off-by: Michal Simek --- board/xilinx/zynqmp/zynqmp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c index ecdae5e261..a67473f344 100644 --- a/board/xilinx/zynqmp/zynqmp.c +++ b/board/xilinx/zynqmp/zynqmp.c @@ -155,7 +155,10 @@ int board_early_init_r(void) { u32 val; - if (current_el() == 3) { + val = readl(&crlapb_base->timestamp_ref_ctrl); + val &= ZYNQMP_CRL_APB_TIMESTAMP_REF_CTRL_CLKACT; + + if (current_el() == 3 && !val) { val = readl(&crlapb_base->timestamp_ref_ctrl); val |= ZYNQMP_CRL_APB_TIMESTAMP_REF_CTRL_CLKACT; writel(val, &crlapb_base->timestamp_ref_ctrl); From 4b5b0fcd212bb4f2cb4c8f9d4f3699c999dfbd2d Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Tue, 21 Feb 2017 17:58:29 +0530 Subject: [PATCH 17/22] arm64: zynqmp: Dont write to system timestamp generator Remove incorrect code of writing to system timestamp counter registers. This register writes does nothing and can be removed. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- arch/arm/include/asm/arch-zynqmp/hardware.h | 9 --------- board/xilinx/zynqmp/zynqmp.c | 6 ------ 2 files changed, 15 deletions(-) diff --git a/arch/arm/include/asm/arch-zynqmp/hardware.h b/arch/arm/include/asm/arch-zynqmp/hardware.h index cf187f3111..1228c7acb8 100644 --- a/arch/arm/include/asm/arch-zynqmp/hardware.h +++ b/arch/arm/include/asm/arch-zynqmp/hardware.h @@ -48,18 +48,9 @@ struct crlapb_regs { #define crlapb_base ((struct crlapb_regs *)ZYNQMP_CRL_APB_BASEADDR) #define ZYNQMP_IOU_SCNTR_SECURE 0xFF260000 -#define ZYNQMP_IOU_SCNTR 0xFF250000 #define ZYNQMP_IOU_SCNTR_COUNTER_CONTROL_REGISTER_EN 0x1 #define ZYNQMP_IOU_SCNTR_COUNTER_CONTROL_REGISTER_HDBG 0x2 -struct iou_scntr { - u32 counter_control_register; - u32 reserved0[7]; - u32 base_frequency_id_register; -}; - -#define iou_scntr ((struct iou_scntr *)ZYNQMP_IOU_SCNTR) - struct iou_scntr_secure { u32 counter_control_register; u32 reserved0[7]; diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c index a67473f344..5b1852a8ce 100644 --- a/board/xilinx/zynqmp/zynqmp.c +++ b/board/xilinx/zynqmp/zynqmp.c @@ -170,12 +170,6 @@ int board_early_init_r(void) writel(ZYNQMP_IOU_SCNTR_COUNTER_CONTROL_REGISTER_EN, &iou_scntr_secure->counter_control_register); } - /* Program freq register in System counter and enable system counter */ - writel(gd->cpu_clk, &iou_scntr->base_frequency_id_register); - writel(ZYNQMP_IOU_SCNTR_COUNTER_CONTROL_REGISTER_HDBG | - ZYNQMP_IOU_SCNTR_COUNTER_CONTROL_REGISTER_EN, - &iou_scntr->counter_control_register); - return 0; } From be4634511a55c52a68131cf7114c97f5c2b608d6 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 20 Jul 2017 12:38:27 +0200 Subject: [PATCH 18/22] arm64: zynqmp: Move dts zcu102 to zcu102-revA Not using board revision is causing confusion about which board is supported and tested. Mark dts files exactly with board revision which was tested. When new board revision arives it can be symlink if SW view is the same. Also add -revX suffix to compatible string because user space tools are parsing this string and can change behavior depends of board revision. Signed-off-by: Michal Simek Reviewed-by: Simon Glass --- arch/arm/dts/Makefile | 2 +- arch/arm/dts/{zynqmp-zcu102.dts => zynqmp-zcu102-revA.dts} | 2 +- arch/arm/dts/zynqmp-zcu102-revB.dts | 2 +- ...qmp_zcu102_defconfig => xilinx_zynqmp_zcu102_revA_defconfig} | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename arch/arm/dts/{zynqmp-zcu102.dts => zynqmp-zcu102-revA.dts} (99%) rename configs/{xilinx_zynqmp_zcu102_defconfig => xilinx_zynqmp_zcu102_revA_defconfig} (97%) diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index e4d118d51c..175c706e7a 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -139,7 +139,7 @@ dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb \ zynq-zc770-xm013.dtb dtb-$(CONFIG_ARCH_ZYNQMP) += \ zynqmp-ep108.dtb \ - zynqmp-zcu102.dtb \ + zynqmp-zcu102-revA.dtb \ zynqmp-zcu102-revB.dtb \ zynqmp-zc1751-xm015-dc1.dtb \ zynqmp-zc1751-xm016-dc2.dtb \ diff --git a/arch/arm/dts/zynqmp-zcu102.dts b/arch/arm/dts/zynqmp-zcu102-revA.dts similarity index 99% rename from arch/arm/dts/zynqmp-zcu102.dts rename to arch/arm/dts/zynqmp-zcu102-revA.dts index 0e9150e6b1..d8ac008f2b 100644 --- a/arch/arm/dts/zynqmp-zcu102.dts +++ b/arch/arm/dts/zynqmp-zcu102-revA.dts @@ -16,7 +16,7 @@ / { model = "ZynqMP ZCU102 RevA"; - compatible = "xlnx,zynqmp-zcu102", "xlnx,zynqmp"; + compatible = "xlnx,zynqmp-zcu102-revA", "xlnx,zynqmp-zcu102", "xlnx,zynqmp"; aliases { ethernet0 = &gem3; diff --git a/arch/arm/dts/zynqmp-zcu102-revB.dts b/arch/arm/dts/zynqmp-zcu102-revB.dts index 765108e437..82337332f9 100644 --- a/arch/arm/dts/zynqmp-zcu102-revB.dts +++ b/arch/arm/dts/zynqmp-zcu102-revB.dts @@ -8,7 +8,7 @@ * SPDX-License-Identifier: GPL-2.0+ */ -#include "zynqmp-zcu102.dts" +#include "zynqmp-zcu102-revA.dts" / { model = "ZynqMP ZCU102 RevB"; diff --git a/configs/xilinx_zynqmp_zcu102_defconfig b/configs/xilinx_zynqmp_zcu102_revA_defconfig similarity index 97% rename from configs/xilinx_zynqmp_zcu102_defconfig rename to configs/xilinx_zynqmp_zcu102_revA_defconfig index 4d0f73f782..fc14459d3c 100644 --- a/configs/xilinx_zynqmp_zcu102_defconfig +++ b/configs/xilinx_zynqmp_zcu102_revA_defconfig @@ -5,7 +5,7 @@ CONFIG_SYS_TEXT_BASE=0x8000000 CONFIG_SYS_MALLOC_F_LEN=0x8000 CONFIG_IDENT_STRING=" Xilinx ZynqMP ZCU102" CONFIG_ZYNQMP_USB=y -CONFIG_DEFAULT_DEVICE_TREE="zynqmp-zcu102" +CONFIG_DEFAULT_DEVICE_TREE="zynqmp-zcu102-revA" CONFIG_DEBUG_UART=y CONFIG_AHCI=y CONFIG_DISTRO_DEFAULTS=y From db3123b40d4e65e4badc71130b77af24189e607d Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Tue, 25 Jul 2017 11:51:36 +0530 Subject: [PATCH 19/22] arm64: zynqmp: Modify chip_id routine to get either idcode or version This patch modifies the chip_id routine to get either idcode or silicon version based on the argument received. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- arch/arm/include/asm/arch-zynqmp/sys_proto.h | 14 ++++++++++ board/xilinx/zynqmp/zynqmp.c | 27 +++++++++++++++----- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/arch/arm/include/asm/arch-zynqmp/sys_proto.h b/arch/arm/include/asm/arch-zynqmp/sys_proto.h index 3d7fad7731..cef4bd6bd4 100644 --- a/arch/arm/include/asm/arch-zynqmp/sys_proto.h +++ b/arch/arm/include/asm/arch-zynqmp/sys_proto.h @@ -10,6 +10,20 @@ #define PAYLOAD_ARG_CNT 5 +#define ZYNQMP_CSU_SILICON_VER_MASK 0xF + +enum { + IDCODE, + VERSION, +}; + +enum { + ZYNQMP_SILICON_V1, + ZYNQMP_SILICON_V2, + ZYNQMP_SILICON_V3, + ZYNQMP_SILICON_V4, +}; + enum { TCM_LOCK, TCM_SPLIT, diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c index 5b1852a8ce..5958350a00 100644 --- a/board/xilinx/zynqmp/zynqmp.c +++ b/board/xilinx/zynqmp/zynqmp.c @@ -76,13 +76,14 @@ static const struct { }, }; -static int chip_id(void) +static int chip_id(unsigned char id) { struct pt_regs regs; regs.regs[0] = ZYNQMP_SIP_SVC_CSU_DMA_CHIPID; regs.regs[1] = 0; regs.regs[2] = 0; regs.regs[3] = 0; + int val = -EINVAL; smc_call(®s); @@ -92,19 +93,31 @@ static int chip_id(void) * regs[0][63:32] = CSU.IDCODE register * regs[1][31:0] = CSU.version register */ - regs.regs[0] = upper_32_bits(regs.regs[0]); - regs.regs[0] &= ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK | - ZYNQMP_CSU_IDCODE_SVD_MASK; - regs.regs[0] >>= ZYNQMP_CSU_IDCODE_SVD_SHIFT; + switch (id) { + case IDCODE: + regs.regs[0] = upper_32_bits(regs.regs[0]); + regs.regs[0] &= ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK | + ZYNQMP_CSU_IDCODE_SVD_MASK; + regs.regs[0] >>= ZYNQMP_CSU_IDCODE_SVD_SHIFT; + val = regs.regs[0]; + break; + case VERSION: + regs.regs[1] = lower_32_bits(regs.regs[1]); + regs.regs[1] &= ZYNQMP_CSU_SILICON_VER_MASK; + val = regs.regs[1]; + break; + default: + printf("%s, Invalid Req:0x%x\n", __func__, id); + } - return regs.regs[0]; + return val; } static char *zynqmp_get_silicon_idcode_name(void) { uint32_t i, id; - id = chip_id(); + id = chip_id(IDCODE); for (i = 0; i < ARRAY_SIZE(zynqmp_devices); i++) { if (zynqmp_devices[i].id == id) return zynqmp_devices[i].name; From f52bf5a3dd6e474032a18185e0f855e5a3b78dfc Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Tue, 25 Jul 2017 11:51:38 +0530 Subject: [PATCH 20/22] arm64: zynqmp: Make chip_id a global routine() This patch makes chip_id() as a global routine so that it can be used in other places as required. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- arch/arm/include/asm/arch-zynqmp/sys_proto.h | 2 ++ board/xilinx/zynqmp/zynqmp.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/arch-zynqmp/sys_proto.h b/arch/arm/include/asm/arch-zynqmp/sys_proto.h index cef4bd6bd4..e52abd71a5 100644 --- a/arch/arm/include/asm/arch-zynqmp/sys_proto.h +++ b/arch/arm/include/asm/arch-zynqmp/sys_proto.h @@ -45,4 +45,6 @@ int invoke_smc(u32 pm_api_id, u32 arg0, u32 arg1, u32 arg2, u32 arg3, void initialize_tcm(bool mode); +int chip_id(unsigned char id); + #endif /* _ASM_ARCH_SYS_PROTO_H */ diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c index 5958350a00..ae69615bfd 100644 --- a/board/xilinx/zynqmp/zynqmp.c +++ b/board/xilinx/zynqmp/zynqmp.c @@ -76,7 +76,7 @@ static const struct { }, }; -static int chip_id(unsigned char id) +int chip_id(unsigned char id) { struct pt_regs regs; regs.regs[0] = ZYNQMP_SIP_SVC_CSU_DMA_CHIPID; From 74ba69db35954e473a27711de89dae9572913478 Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Tue, 25 Jul 2017 11:51:37 +0530 Subject: [PATCH 21/22] arm64: zynqmp: Make chip_id routine to handle based on el. Modify chip_id() routine such that to handle based on the current el. Also make it available even if FPGA is not enabled in system such it can be used always. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- arch/arm/include/asm/arch-zynqmp/hardware.h | 3 + board/xilinx/zynqmp/zynqmp.c | 73 +++++++++++++-------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/arch/arm/include/asm/arch-zynqmp/hardware.h b/arch/arm/include/asm/arch-zynqmp/hardware.h index 1228c7acb8..cab29ba036 100644 --- a/arch/arm/include/asm/arch-zynqmp/hardware.h +++ b/arch/arm/include/asm/arch-zynqmp/hardware.h @@ -144,4 +144,7 @@ struct pmu_regs { #define pmu_base ((struct pmu_regs *)ZYNQMP_PMU_BASEADDR) +#define ZYNQMP_CSU_IDCODE_ADDR 0xFFCA0040 +#define ZYNQMP_CSU_VER_ADDR 0xFFCA0044 + #endif /* _ASM_ARCH_HARDWARE_H */ diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c index ae69615bfd..07e0486817 100644 --- a/board/xilinx/zynqmp/zynqmp.c +++ b/board/xilinx/zynqmp/zynqmp.c @@ -75,44 +75,65 @@ static const struct { .name = "17eg", }, }; +#endif int chip_id(unsigned char id) { struct pt_regs regs; - regs.regs[0] = ZYNQMP_SIP_SVC_CSU_DMA_CHIPID; - regs.regs[1] = 0; - regs.regs[2] = 0; - regs.regs[3] = 0; int val = -EINVAL; - smc_call(®s); + if (current_el() != 3) { + regs.regs[0] = ZYNQMP_SIP_SVC_CSU_DMA_CHIPID; + regs.regs[1] = 0; + regs.regs[2] = 0; + regs.regs[3] = 0; - /* - * SMC returns: - * regs[0][31:0] = status of the operation - * regs[0][63:32] = CSU.IDCODE register - * regs[1][31:0] = CSU.version register - */ - switch (id) { - case IDCODE: - regs.regs[0] = upper_32_bits(regs.regs[0]); - regs.regs[0] &= ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK | - ZYNQMP_CSU_IDCODE_SVD_MASK; - regs.regs[0] >>= ZYNQMP_CSU_IDCODE_SVD_SHIFT; - val = regs.regs[0]; - break; - case VERSION: - regs.regs[1] = lower_32_bits(regs.regs[1]); - regs.regs[1] &= ZYNQMP_CSU_SILICON_VER_MASK; - val = regs.regs[1]; - break; - default: - printf("%s, Invalid Req:0x%x\n", __func__, id); + smc_call(®s); + + /* + * SMC returns: + * regs[0][31:0] = status of the operation + * regs[0][63:32] = CSU.IDCODE register + * regs[1][31:0] = CSU.version register + */ + switch (id) { + case IDCODE: + regs.regs[0] = upper_32_bits(regs.regs[0]); + regs.regs[0] &= ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK | + ZYNQMP_CSU_IDCODE_SVD_MASK; + regs.regs[0] >>= ZYNQMP_CSU_IDCODE_SVD_SHIFT; + val = regs.regs[0]; + break; + case VERSION: + regs.regs[1] = lower_32_bits(regs.regs[1]); + regs.regs[1] &= ZYNQMP_CSU_SILICON_VER_MASK; + val = regs.regs[1]; + break; + default: + printf("%s, Invalid Req:0x%x\n", __func__, id); + } + } else { + switch (id) { + case IDCODE: + val = readl(ZYNQMP_CSU_IDCODE_ADDR); + val &= ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK | + ZYNQMP_CSU_IDCODE_SVD_MASK; + val >>= ZYNQMP_CSU_IDCODE_SVD_SHIFT; + break; + case VERSION: + val = readl(ZYNQMP_CSU_VER_ADDR); + val &= ZYNQMP_CSU_SILICON_VER_MASK; + break; + default: + printf("%s, Invalid Req:0x%x\n", __func__, id); + } } return val; } +#if defined(CONFIG_FPGA) && defined(CONFIG_FPGA_ZYNQMPPL) && \ + !defined(CONFIG_SPL_BUILD) static char *zynqmp_get_silicon_idcode_name(void) { uint32_t i, id; From df1cd46fb84922735e1c12f54b7202b0268dcddd Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 30 Jul 2017 22:18:18 +0200 Subject: [PATCH 22/22] arm64: zynqmp: avoid out of buffer access strncat(a, b, c) appends a maximum of c characters plus the 0 byte to a. In board_init we first write 4 characters plus 0 byte to version. So only ZYNQMP_VERSION_SIZE - 5 additional characters fit into version. The problem was indicated by cppcheck. Signed-off-by: Heinrich Schuchardt Signed-off-by: Michal Simek --- board/xilinx/zynqmp/zynqmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c index 07e0486817..aebd3df8a9 100644 --- a/board/xilinx/zynqmp/zynqmp.c +++ b/board/xilinx/zynqmp/zynqmp.c @@ -172,10 +172,10 @@ int board_init(void) if (current_el() != 3) { static char version[ZYNQMP_VERSION_SIZE]; - strncat(version, "xczu", ZYNQMP_VERSION_SIZE); + strncat(version, "xczu", 4); zynqmppl.name = strncat(version, zynqmp_get_silicon_idcode_name(), - ZYNQMP_VERSION_SIZE); + ZYNQMP_VERSION_SIZE - 5); printf("Chip ID:\t%s\n", zynqmppl.name); fpga_init(); fpga_add(fpga_xilinx, &zynqmppl);