From caacb33fd1f9c270b0f0c878cfca08b56c0b257c Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 25 Mar 2015 13:35:04 +0100 Subject: zynq: Enable GPIO driver and GPIO commands Enable GPIO driver and GPIO commands. Signed-off-by: Michal Simek --- include/configs/zynq-common.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/configs/zynq-common.h b/include/configs/zynq-common.h index 485babd8242..f613b920470 100644 --- a/include/configs/zynq-common.h +++ b/include/configs/zynq-common.h @@ -39,6 +39,9 @@ # define CONFIG_ZYNQ_SERIAL #endif +#define CONFIG_ZYNQ_GPIO +#define CONFIG_CMD_GPIO + /* Ethernet driver */ #if defined(CONFIG_ZYNQ_GEM0) || defined(CONFIG_ZYNQ_GEM1) # define CONFIG_NET_MULTI -- cgit v1.3.1 From 7a1aec8de8a0acaa669226266617705d274bb22d Mon Sep 17 00:00:00 2001 From: Nathan Rossi Date: Tue, 14 Apr 2015 14:58:10 +1000 Subject: zynq: Add Zynq PicoZed board support The PicoZed is a System-on-Module board which is marketed as part of the ZedBoard/MicroZed/etc. collection. It includes a Zynq-7000 processor. This patch adds support that covers all the variants of the PicoZed including the SKUs with Z7010/Z7020 and Z7015/Z7030 Zynq chips. This patch set however only covers support for the System-on-Module and does not cover any extra components that are available on carrier boards (except those that are fanned out of the module itself). More information on this board, its variants and available carrier boards is available at: http://zedboard.org/product/picozed Signed-off-by: Nathan Rossi Signed-off-by: Michal Simek --- arch/arm/cpu/armv7/zynq/Kconfig | 4 ++++ arch/arm/dts/Makefile | 1 + arch/arm/dts/zynq-picozed.dts | 23 +++++++++++++++++++++++ configs/zynq_picozed_defconfig | 6 ++++++ include/configs/zynq_picozed.h | 27 +++++++++++++++++++++++++++ 5 files changed, 61 insertions(+) create mode 100644 arch/arm/dts/zynq-picozed.dts create mode 100644 configs/zynq_picozed_defconfig create mode 100644 include/configs/zynq_picozed.h (limited to 'include') diff --git a/arch/arm/cpu/armv7/zynq/Kconfig b/arch/arm/cpu/armv7/zynq/Kconfig index 3a52535ce00..26e570eb662 100644 --- a/arch/arm/cpu/armv7/zynq/Kconfig +++ b/arch/arm/cpu/armv7/zynq/Kconfig @@ -9,6 +9,9 @@ config TARGET_ZYNQ_ZED config TARGET_ZYNQ_MICROZED bool "Zynq MicroZed" +config TARGET_ZYNQ_PICOZED + bool "Zynq PicoZed" + config TARGET_ZYNQ_ZC70X bool "Zynq ZC702/ZC706 Board" @@ -32,6 +35,7 @@ config SYS_SOC config SYS_CONFIG_NAME default "zynq_zed" if TARGET_ZYNQ_ZED default "zynq_microzed" if TARGET_ZYNQ_MICROZED + default "zynq_picozed" if TARGET_ZYNQ_PICOZED default "zynq_zc70x" if TARGET_ZYNQ_ZC70X default "zynq_zc770" if TARGET_ZYNQ_ZC770 default "zynq_zybo" if TARGET_ZYNQ_ZYBO diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 09708d9a414..0883fe4ecfb 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -44,6 +44,7 @@ dtb-$(CONFIG_ZYNQ) += zynq-zc702.dtb \ zynq-zed.dtb \ zynq-zybo.dtb \ zynq-microzed.dtb \ + zynq-picozed.dtb \ zynq-zc770-xm010.dtb \ zynq-zc770-xm012.dtb \ zynq-zc770-xm013.dtb diff --git a/arch/arm/dts/zynq-picozed.dts b/arch/arm/dts/zynq-picozed.dts new file mode 100644 index 00000000000..686b98fe7ef --- /dev/null +++ b/arch/arm/dts/zynq-picozed.dts @@ -0,0 +1,23 @@ +/* + * Avnet PicoZed board DTS + * + * Copyright (C) 2015 Xilinx, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +/dts-v1/; +#include "zynq-7000.dtsi" + +/ { + model = "Zynq PicoZed Board"; + compatible = "xlnx,zynq-picozed", "xlnx,zynq-7000"; + + aliases { + serial0 = &uart1; + }; + + memory { + device_type = "memory"; + reg = <0 0x40000000>; + }; +}; diff --git a/configs/zynq_picozed_defconfig b/configs/zynq_picozed_defconfig new file mode 100644 index 00000000000..b7828736c0f --- /dev/null +++ b/configs/zynq_picozed_defconfig @@ -0,0 +1,6 @@ +CONFIG_SPL=y +CONFIG_ARM=y +CONFIG_ZYNQ=y +CONFIG_TARGET_ZYNQ_PICOZED=y +CONFIG_OF_CONTROL=n +CONFIG_DEFAULT_DEVICE_TREE="zynq-picozed" diff --git a/include/configs/zynq_picozed.h b/include/configs/zynq_picozed.h new file mode 100644 index 00000000000..d116e05ac7d --- /dev/null +++ b/include/configs/zynq_picozed.h @@ -0,0 +1,27 @@ +/* + * (C) Copyright 2015 Xilinx, Inc. + * + * Configuration for PicoZed + * See zynq-common.h for Zynq common configs + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __CONFIG_ZYNQ_PICOZED_H +#define __CONFIG_ZYNQ_PICOZED_H + +#define CONFIG_SYS_SDRAM_SIZE (1024 * 1024 * 1024) + +#define CONFIG_ZYNQ_SERIAL_UART1 +#define CONFIG_ZYNQ_GEM0 +#define CONFIG_ZYNQ_GEM_PHY_ADDR0 0 + +#define CONFIG_SYS_NO_FLASH + +#define CONFIG_ZYNQ_SDHCI1 +#define CONFIG_ZYNQ_USB +#define CONFIG_ZYNQ_BOOT_FREEBSD + +#include + +#endif /* __CONFIG_ZYNQ_PICOZED_H */ -- cgit v1.3.1 From 222b2129371b40b4fcdda037b8ff304602af1f21 Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Sat, 6 Dec 2014 12:57:51 +0530 Subject: zynqmp: caches: Enable dcache for zynqmp Define the mmu table till 2MB granularity enable dcaches for zynqmp. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- arch/arm/cpu/armv8/zynqmp/cpu.c | 164 ++++++++++++++++++++++++++++++++++++++++ include/configs/xilinx_zynqmp.h | 2 +- 2 files changed, 165 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/arm/cpu/armv8/zynqmp/cpu.c b/arch/arm/cpu/armv8/zynqmp/cpu.c index 6fae03c1af5..11958fea6f1 100644 --- a/arch/arm/cpu/armv8/zynqmp/cpu.c +++ b/arch/arm/cpu/armv8/zynqmp/cpu.c @@ -26,3 +26,167 @@ unsigned int zynqmp_get_silicon_version(void) return ZYNQMP_CSU_VERSION_EP108; } + +#ifndef CONFIG_SYS_DCACHE_OFF +#include + +#define SECTION_SHIFT_L1 30UL +#define SECTION_SHIFT_L2 21UL +#define BLOCK_SIZE_L0 0x8000000000UL +#define BLOCK_SIZE_L1 (1 << SECTION_SHIFT_L1) +#define BLOCK_SIZE_L2 (1 << SECTION_SHIFT_L2) + +#define TCR_TG1_4K (1 << 31) +#define TCR_EPD1_DISABLE (1 << 23) +#define ZYNQMO_VA_BITS 40 +#define ZYNQMP_TCR TCR_TG1_4K | \ + TCR_EPD1_DISABLE | \ + TCR_SHARED_OUTER | \ + TCR_SHARED_INNER | \ + TCR_IRGN_WBWA | \ + TCR_ORGN_WBWA | \ + TCR_T0SZ(ZYNQMO_VA_BITS) + +#define MEMORY_ATTR PMD_SECT_AF | PMD_SECT_INNER_SHARE | \ + PMD_ATTRINDX(MT_NORMAL) | \ + PMD_TYPE_SECT +#define DEVICE_ATTR PMD_SECT_AF | PMD_SECT_PXN | \ + PMD_SECT_UXN | PMD_ATTRINDX(MT_DEVICE_NGNRNE) | \ + PMD_TYPE_SECT + +/* 4K size is required to place 512 entries in each level */ +#define TLB_TABLE_SIZE 0x1000 + +struct attr_tbl { + u32 num; + u64 attr; +}; + +static struct attr_tbl attr_tbll1t0[4] = { {16, 0x0}, + {8, DEVICE_ATTR}, + {32, MEMORY_ATTR}, + {456, DEVICE_ATTR} + }; +static struct attr_tbl attr_tbll2t3[4] = { {0x180, DEVICE_ATTR}, + {0x40, 0x0}, + {0x3F, DEVICE_ATTR}, + {0x1, MEMORY_ATTR} + }; + +/* + * This mmu table looks as below + * Level 0 table contains two entries to 512GB sizes. One is Level1 Table 0 + * and other Level1 Table1. + * Level1 Table0 contains entries for each 1GB from 0 to 511GB. + * Level1 Table1 contains entries for each 1GB from 512GB to 1TB. + * Level2 Table0, Level2 Table1, Level2 Table2 and Level2 Table3 contains + * entries for each 2MB starting from 0GB, 1GB, 2GB and 3GB respectively. + */ +static void zynqmp_mmu_setup(void) +{ + int el; + u32 index_attr; + u64 i, section_l1t0, section_l1t1; + u64 section_l2t0, section_l2t1, section_l2t2, section_l2t3; + u64 *level0_table = (u64 *)gd->arch.tlb_addr; + u64 *level1_table_0 = (u64 *)(gd->arch.tlb_addr + TLB_TABLE_SIZE); + u64 *level1_table_1 = (u64 *)(gd->arch.tlb_addr + (2 * TLB_TABLE_SIZE)); + u64 *level2_table_0 = (u64 *)(gd->arch.tlb_addr + (3 * TLB_TABLE_SIZE)); + u64 *level2_table_1 = (u64 *)(gd->arch.tlb_addr + (4 * TLB_TABLE_SIZE)); + u64 *level2_table_2 = (u64 *)(gd->arch.tlb_addr + (5 * TLB_TABLE_SIZE)); + u64 *level2_table_3 = (u64 *)(gd->arch.tlb_addr + (6 * TLB_TABLE_SIZE)); + + level0_table[0] = + (u64)level1_table_0 | PMD_TYPE_TABLE; + level0_table[1] = + (u64)level1_table_1 | PMD_TYPE_TABLE; + + /* + * set level 1 table 0, covering 0 to 512GB + * set level 1 table 1, covering 512GB to 1TB + */ + section_l1t0 = 0; + section_l1t1 = BLOCK_SIZE_L0; + + index_attr = 0; + for (i = 0; i < 512; i++) { + level1_table_0[i] = section_l1t0; + level1_table_0[i] |= attr_tbll1t0[index_attr].attr; + attr_tbll1t0[index_attr].num--; + if (attr_tbll1t0[index_attr].num == 0) + index_attr++; + level1_table_1[i] = section_l1t1; + level1_table_1[i] |= DEVICE_ATTR; + section_l1t0 += BLOCK_SIZE_L1; + section_l1t1 += BLOCK_SIZE_L1; + } + + level1_table_0[0] = + (u64)level2_table_0 | PMD_TYPE_TABLE; + level1_table_0[1] = + (u64)level2_table_1 | PMD_TYPE_TABLE; + level1_table_0[2] = + (u64)level2_table_2 | PMD_TYPE_TABLE; + level1_table_0[3] = + (u64)level2_table_3 | PMD_TYPE_TABLE; + + section_l2t0 = 0; + section_l2t1 = section_l2t0 + BLOCK_SIZE_L1; /* 1GB */ + section_l2t2 = section_l2t1 + BLOCK_SIZE_L1; /* 2GB */ + section_l2t3 = section_l2t2 + BLOCK_SIZE_L1; /* 3GB */ + + index_attr = 0; + + for (i = 0; i < 512; i++) { + level2_table_0[i] = section_l2t0 | MEMORY_ATTR; + level2_table_1[i] = section_l2t1 | MEMORY_ATTR; + level2_table_2[i] = section_l2t2 | DEVICE_ATTR; + level2_table_3[i] = section_l2t3 | + attr_tbll2t3[index_attr].attr; + attr_tbll2t3[index_attr].num--; + if (attr_tbll2t3[index_attr].num == 0) + index_attr++; + section_l2t0 += BLOCK_SIZE_L2; + section_l2t1 += BLOCK_SIZE_L2; + section_l2t2 += BLOCK_SIZE_L2; + section_l2t3 += BLOCK_SIZE_L2; + } + + /* flush new MMU table */ + flush_dcache_range(gd->arch.tlb_addr, + gd->arch.tlb_addr + gd->arch.tlb_size); + + /* point TTBR to the new table */ + el = current_el(); + set_ttbr_tcr_mair(el, gd->arch.tlb_addr, + ZYNQMP_TCR, MEMORY_ATTRIBUTES); + + set_sctlr(get_sctlr() | CR_M); +} + +int arch_cpu_init(void) +{ + icache_enable(); + __asm_invalidate_dcache_all(); + __asm_invalidate_tlb_all(); + return 0; +} + +/* + * This function is called from lib/board.c. + * It recreates MMU table in main memory. MMU and d-cache are enabled earlier. + * There is no need to disable d-cache for this operation. + */ +void enable_caches(void) +{ + /* The data cache is not active unless the mmu is enabled */ + if (!(get_sctlr() & CR_M)) { + invalidate_dcache_all(); + __asm_invalidate_tlb_all(); + zynqmp_mmu_setup(); + } + puts("Enabling Caches...\n"); + + set_sctlr(get_sctlr() | CR_C); +} +#endif diff --git a/include/configs/xilinx_zynqmp.h b/include/configs/xilinx_zynqmp.h index 511ecca0b82..74942f18706 100644 --- a/include/configs/xilinx_zynqmp.h +++ b/include/configs/xilinx_zynqmp.h @@ -36,7 +36,7 @@ #define CPU_RELEASE_ADDR 0xFFFFFF0 /* Cache Definitions */ -#define CONFIG_SYS_DCACHE_OFF +#define CONFIG_SYS_CACHELINE_SIZE 64 #define CONFIG_IDENT_STRING " Xilinx ZynqMP" -- cgit v1.3.1 From 5cb24200373fa693d3de25185d0ea742b5835b9a Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 15 Apr 2015 13:36:40 +0200 Subject: zynqmp: Add support for R5 sw loading Add support for loading sw for R5 with enabling for zynqmp. Signed-off-by: Michal Simek Signed-off-by: Siva Durga Prasad Paladugu --- arch/arm/cpu/armv8/zynqmp/Makefile | 1 + arch/arm/cpu/armv8/zynqmp/mp.c | 242 ++++++++++++++++++++++++++++ arch/arm/include/asm/arch-zynqmp/hardware.h | 43 ++++- include/configs/xilinx_zynqmp.h | 2 + 4 files changed, 285 insertions(+), 3 deletions(-) create mode 100644 arch/arm/cpu/armv8/zynqmp/mp.c (limited to 'include') diff --git a/arch/arm/cpu/armv8/zynqmp/Makefile b/arch/arm/cpu/armv8/zynqmp/Makefile index a997e045bd0..efab5eabc97 100644 --- a/arch/arm/cpu/armv8/zynqmp/Makefile +++ b/arch/arm/cpu/armv8/zynqmp/Makefile @@ -7,3 +7,4 @@ obj-y += clk.o obj-y += cpu.o +obj-$(CONFIG_MP) += mp.o diff --git a/arch/arm/cpu/armv8/zynqmp/mp.c b/arch/arm/cpu/armv8/zynqmp/mp.c new file mode 100644 index 00000000000..17e32a7b7ce --- /dev/null +++ b/arch/arm/cpu/armv8/zynqmp/mp.c @@ -0,0 +1,242 @@ +/* + * (C) Copyright 2014 - 2015 Xilinx, Inc. + * Michal Simek + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#define LOCK 0 +#define SPLIT 1 + +#define HALT 0 +#define RELEASE 1 + +#define ZYNQMP_BOOTADDR_HIGH_MASK 0xFFFFFFFF +#define ZYNQMP_R5_HIVEC_ADDR 0xFFFF0000 +#define ZYNQMP_R5_LOVEC_ADDR 0x0 +#define ZYNQMP_RPU_CFG_CPU_HALT_MASK 0x01 +#define ZYNQMP_RPU_CFG_HIVEC_MASK 0x04 +#define ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK 0x08 +#define ZYNQMP_RPU_GLBL_CTRL_TCM_COMB_MASK 0x40 +#define ZYNQMP_RPU_GLBL_CTRL_SLCLAMP_MASK 0x10 + +#define ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK 0x04 +#define ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK 0x01 +#define ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK 0x02 +#define ZYNQMP_CRLAPB_CPU_R5_CTRL_CLKACT_MASK 0x1000000 + +#define ZYNQMP_TCM_START_ADDRESS 0xFFE00000 +#define ZYNQMP_TCM_BOTH_SIZE 0x40000 + +#define ZYNQMP_CORE_APU0 0 +#define ZYNQMP_CORE_APU3 3 + +#define ZYNQMP_MAX_CORES 6 + +int is_core_valid(unsigned int core) +{ + if (core < ZYNQMP_MAX_CORES) + return 1; + + return 0; +} + +int cpu_reset(int nr) +{ + puts("Feature is not implemented.\n"); + return 0; +} + +static void set_r5_halt_mode(u8 halt, u8 mode) +{ + u32 tmp; + + tmp = readl(&rpu_base->rpu0_cfg); + if (halt == HALT) + tmp &= ~ZYNQMP_RPU_CFG_CPU_HALT_MASK; + else + tmp |= ZYNQMP_RPU_CFG_CPU_HALT_MASK; + writel(tmp, &rpu_base->rpu0_cfg); + + if (mode == LOCK) { + tmp = readl(&rpu_base->rpu1_cfg); + if (halt == HALT) + tmp &= ~ZYNQMP_RPU_CFG_CPU_HALT_MASK; + else + tmp |= ZYNQMP_RPU_CFG_CPU_HALT_MASK; + writel(tmp, &rpu_base->rpu1_cfg); + } +} + +static void set_r5_tcm_mode(u8 mode) +{ + u32 tmp; + + tmp = readl(&rpu_base->rpu_glbl_ctrl); + if (mode == LOCK) { + tmp &= ~ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK; + tmp |= ZYNQMP_RPU_GLBL_CTRL_TCM_COMB_MASK | + ZYNQMP_RPU_GLBL_CTRL_SLCLAMP_MASK; + } else { + tmp |= ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK; + tmp &= ~(ZYNQMP_RPU_GLBL_CTRL_TCM_COMB_MASK | + ZYNQMP_RPU_GLBL_CTRL_SLCLAMP_MASK); + } + + writel(tmp, &rpu_base->rpu_glbl_ctrl); +} + +static void set_r5_reset(u8 mode) +{ + u32 tmp; + + tmp = readl(&crlapb_base->rst_lpd_top); + tmp |= (ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK | + ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK); + + if (mode == LOCK) + tmp |= ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK; + + writel(tmp, &crlapb_base->rst_lpd_top); +} + +static void release_r5_reset(u8 mode) +{ + u32 tmp; + + tmp = readl(&crlapb_base->rst_lpd_top); + tmp &= ~(ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK | + ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK); + + if (mode == LOCK) + tmp &= ~ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK; + + writel(tmp, &crlapb_base->rst_lpd_top); +} + +static void enable_clock_r5(void) +{ + u32 tmp; + + tmp = readl(&crlapb_base->cpu_r5_ctrl); + tmp |= ZYNQMP_CRLAPB_CPU_R5_CTRL_CLKACT_MASK; + writel(tmp, &crlapb_base->cpu_r5_ctrl); + + /* Give some delay for clock + * to propogate */ + udelay(0x500); +} + +int cpu_disable(int nr) +{ + if (nr >= ZYNQMP_CORE_APU0 && nr <= ZYNQMP_CORE_APU3) { + u32 val = readl(&crfapb_base->rst_fpd_apu); + val |= 1 << nr; + writel(val, &crfapb_base->rst_fpd_apu); + } else { + set_r5_reset(LOCK); + } + + return 0; +} + +int cpu_status(int nr) +{ + if (nr >= ZYNQMP_CORE_APU0 && nr <= ZYNQMP_CORE_APU3) { + u32 addr_low = readl(((u8 *)&apu_base->rvbar_addr0_l) + nr * 8); + u32 addr_high = readl(((u8 *)&apu_base->rvbar_addr0_h) + + nr * 8); + u32 val = readl(&crfapb_base->rst_fpd_apu); + val &= 1 << nr; + printf("APU CPU%d %s - starting address HI: %x, LOW: %x\n", + nr, val ? "OFF" : "ON" , addr_high, addr_low); + } else { + u32 val = readl(&crlapb_base->rst_lpd_top); + val &= 1 << (nr - 4); + printf("RPU CPU%d %s\n", nr - 4, val ? "OFF" : "ON"); + } + + return 0; +} + +static void set_r5_start(u8 high) +{ + u32 tmp; + + tmp = readl(&rpu_base->rpu0_cfg); + if (high) + tmp |= ZYNQMP_RPU_CFG_HIVEC_MASK; + else + tmp &= ~ZYNQMP_RPU_CFG_HIVEC_MASK; + writel(tmp, &rpu_base->rpu0_cfg); + + tmp = readl(&rpu_base->rpu1_cfg); + if (high) + tmp |= ZYNQMP_RPU_CFG_HIVEC_MASK; + else + tmp &= ~ZYNQMP_RPU_CFG_HIVEC_MASK; + writel(tmp, &rpu_base->rpu1_cfg); +} + +int cpu_release(int nr, int argc, char * const argv[]) +{ + if (nr >= ZYNQMP_CORE_APU0 && nr <= ZYNQMP_CORE_APU3) { + u64 boot_addr = simple_strtoull(argv[0], NULL, 16); + /* HIGH */ + writel((u32)(boot_addr >> 32), + ((u8 *)&apu_base->rvbar_addr0_h) + nr * 8); + /* LOW */ + writel((u32)(boot_addr & ZYNQMP_BOOTADDR_HIGH_MASK), + ((u8 *)&apu_base->rvbar_addr0_l) + nr * 8); + + u32 val = readl(&crfapb_base->rst_fpd_apu); + val &= ~(1 << nr); + writel(val, &crfapb_base->rst_fpd_apu); + } else { + if (argc != 2) { + printf("Invalid number of arguments to release.\n"); + printf(" -Start addr lockstep or split\n"); + return 1; + } + + u32 boot_addr = simple_strtoul(argv[0], NULL, 16); + if (!(boot_addr == ZYNQMP_R5_LOVEC_ADDR || + boot_addr == ZYNQMP_R5_HIVEC_ADDR)) { + printf("Invalid starting address 0x%x\n", boot_addr); + printf("0 or 0xffff0000 are permitted\n"); + return 1; + } + + if (!strncmp(argv[1], "lockstep", 8)) { + printf("R5 lockstep mode\n"); + set_r5_tcm_mode(LOCK); + set_r5_halt_mode(HALT, LOCK); + + if (boot_addr == 0) + set_r5_start(0); + else + set_r5_start(1); + + enable_clock_r5(); + release_r5_reset(LOCK); + set_r5_halt_mode(RELEASE, LOCK); + } else if (!strncmp(argv[1], "split", 5)) { + printf("R5 split mode\n"); + set_r5_tcm_mode(SPLIT); + set_r5_halt_mode(HALT, SPLIT); + enable_clock_r5(); + release_r5_reset(SPLIT); + set_r5_halt_mode(RELEASE, SPLIT); + } else { + printf("Unsupported mode\n"); + return 1; + } + } + + return 0; +} diff --git a/arch/arm/include/asm/arch-zynqmp/hardware.h b/arch/arm/include/asm/arch-zynqmp/hardware.h index 97fb49a2a7b..3df3147fc4c 100644 --- a/arch/arm/include/asm/arch-zynqmp/hardware.h +++ b/arch/arm/include/asm/arch-zynqmp/hardware.h @@ -18,11 +18,15 @@ #define ZYNQMP_CRL_APB_TIMESTAMP_REF_CTRL_CLKACT 0x1000000 struct crlapb_regs { - u32 reserved0[74]; + u32 reserved0[36]; + u32 cpu_r5_ctrl; /* 0x90 */ + u32 reserved1[37]; u32 timestamp_ref_ctrl; /* 0x128 */ - u32 reserved0_1[53]; + u32 reserved2[53]; u32 boot_mode; /* 0x200 */ - u32 reserved1[26]; + u32 reserved3[14]; + u32 rst_lpd_top; /* 0x23C */ + u32 reserved4[26]; }; #define crlapb_base ((struct crlapb_regs *)ZYNQMP_CRL_APB_BASEADDR) @@ -44,6 +48,39 @@ struct iou_scntr { #define SD_MODE 0x00000005 #define JTAG_MODE 0x00000000 +#define ZYNQMP_RPU_BASEADDR 0xFF9A0000 + +struct rpu_regs { + u32 rpu_glbl_ctrl; + u32 reserved0[63]; + u32 rpu0_cfg; /* 0x100 */ + u32 reserved1[63]; + u32 rpu1_cfg; /* 0x200 */ +}; + +#define rpu_base ((struct rpu_regs *)ZYNQMP_RPU_BASEADDR) + +#define ZYNQMP_CRF_APB_BASEADDR 0xFD1A0000 + +struct crfapb_regs { + u32 reserved0[65]; + u32 rst_fpd_apu; /* 0x104 */ + u32 reserved1; +}; + +#define crfapb_base ((struct crfapb_regs *)ZYNQMP_CRF_APB_BASEADDR) + +#define ZYNQMP_APU_BASEADDR 0xFD5C0000 + +struct apu_regs { + u32 reserved0[16]; + u32 rvbar_addr0_l; /* 0x40 */ + u32 rvbar_addr0_h; /* 0x44 */ + u32 reserved1[20]; +}; + +#define apu_base ((struct apu_regs *)ZYNQMP_APU_BASEADDR) + /* Board version value */ #define ZYNQMP_CSU_VERSION_SILICON 0x0 #define ZYNQMP_CSU_VERSION_EP108 0x1 diff --git a/include/configs/xilinx_zynqmp.h b/include/configs/xilinx_zynqmp.h index 74942f18706..d0ec3c066f3 100644 --- a/include/configs/xilinx_zynqmp.h +++ b/include/configs/xilinx_zynqmp.h @@ -68,6 +68,8 @@ #define CONFIG_CMD_FAT #define CONFIG_CMD_MEMORY #define CONFIG_DOS_PARTITION +#define CONFIG_CMD_ELF +#define CONFIG_MP #if defined(CONFIG_ZYNQ_SDHCI0) || defined(CONFIG_ZYNQ_SDHCI1) # define CONFIG_MMC -- cgit v1.3.1 From 2594e03c64782e858e2e2d11beebd7e4685620dc Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Tue, 3 Mar 2015 15:01:44 +0530 Subject: zynqmp: i2c: Enable i2c driver for zynqMP Enable the i2c driver for ZynqMP Also enable the eeprom for read and writes to eeprom on ZynqMP ZynqMP uses the same i2c controller as in Zynq Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- arch/arm/include/asm/arch-zynqmp/hardware.h | 3 +++ include/configs/xilinx_zynqmp.h | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'include') diff --git a/arch/arm/include/asm/arch-zynqmp/hardware.h b/arch/arm/include/asm/arch-zynqmp/hardware.h index 87792c21a5d..1fedb1bb4b9 100644 --- a/arch/arm/include/asm/arch-zynqmp/hardware.h +++ b/arch/arm/include/asm/arch-zynqmp/hardware.h @@ -11,6 +11,9 @@ #define ZYNQ_SERIAL_BASEADDR0 0xFF000000 #define ZYNQ_SERIAL_BASEADDR1 0xFF001000 +#define ZYNQ_I2C_BASEADDR0 0xFF020000 +#define ZYNQ_I2C_BASEADDR1 0xFF030000 + #define ZYNQ_SDHCI_BASEADDR0 0xFF160000 #define ZYNQ_SDHCI_BASEADDR1 0xFF170000 diff --git a/include/configs/xilinx_zynqmp.h b/include/configs/xilinx_zynqmp.h index d0ec3c066f3..54bca6d47b7 100644 --- a/include/configs/xilinx_zynqmp.h +++ b/include/configs/xilinx_zynqmp.h @@ -119,6 +119,29 @@ #define CONFIG_CMDLINE_EDITING #define CONFIG_SYS_MAXARGS 64 +#define CONFIG_ZYNQ_I2C0 +#define CONFIG_SYS_I2C_ZYNQ + +/* I2C */ +#if defined(CONFIG_SYS_I2C_ZYNQ) +# define CONFIG_CMD_I2C +# define CONFIG_SYS_I2C +# define CONFIG_SYS_I2C_ZYNQ_SPEED 100000 +# define CONFIG_SYS_I2C_ZYNQ_SLAVE 0 +#endif + +#define CONFIG_ZYNQMP_EEPROM + +/* EEPROM */ +#ifdef CONFIG_ZYNQMP_EEPROM +# define CONFIG_CMD_EEPROM +# define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 2 +# define CONFIG_SYS_I2C_EEPROM_ADDR 0x54 +# define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 4 +# define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 5 +# define CONFIG_SYS_EEPROM_SIZE (64 * 1024) +#endif + #define CONFIG_FIT #define CONFIG_FIT_VERBOSE /* enable fit_format_{error,warning}() */ -- cgit v1.3.1 From 48d7260d19d7b256f6404fe56e71754a18f88ceb Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Wed, 15 Apr 2015 11:48:48 +0530 Subject: zynqmp: Add SPI driver support for ZynqMP Added the SPI driver support for ZynqMP The controller is same as zynq SPI controller Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek Reviewed-by: Jagannadha Sutradharudu Teki --- arch/arm/include/asm/arch-zynqmp/hardware.h | 3 +++ include/configs/xilinx_zynqmp.h | 7 +++++++ 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/arch/arm/include/asm/arch-zynqmp/hardware.h b/arch/arm/include/asm/arch-zynqmp/hardware.h index 1fedb1bb4b9..c9dc49d7831 100644 --- a/arch/arm/include/asm/arch-zynqmp/hardware.h +++ b/arch/arm/include/asm/arch-zynqmp/hardware.h @@ -11,6 +11,9 @@ #define ZYNQ_SERIAL_BASEADDR0 0xFF000000 #define ZYNQ_SERIAL_BASEADDR1 0xFF001000 +#define ZYNQ_SPI_BASEADDR0 0xFF040000 +#define ZYNQ_SPI_BASEADDR1 0xFF050000 + #define ZYNQ_I2C_BASEADDR0 0xFF020000 #define ZYNQ_I2C_BASEADDR1 0xFF030000 diff --git a/include/configs/xilinx_zynqmp.h b/include/configs/xilinx_zynqmp.h index 54bca6d47b7..1d8eaa76dfe 100644 --- a/include/configs/xilinx_zynqmp.h +++ b/include/configs/xilinx_zynqmp.h @@ -71,6 +71,13 @@ #define CONFIG_CMD_ELF #define CONFIG_MP +/* SPI */ +#ifdef CONFIG_ZYNQ_SPI +# define CONFIG_SPI_FLASH +# define CONFIG_SPI_FLASH_SST +# define CONFIG_CMD_SF +#endif + #if defined(CONFIG_ZYNQ_SDHCI0) || defined(CONFIG_ZYNQ_SDHCI1) # define CONFIG_MMC # define CONFIG_GENERIC_MMC -- cgit v1.3.1 From 5af37bd070ac5088cd6deef6691fbed4be432a6f Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Fri, 13 Mar 2015 17:43:49 +0530 Subject: zynqmp: Enable FS_GENERIC option Provide an option to write filesystem independend commands. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- include/configs/xilinx_zynqmp.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/configs/xilinx_zynqmp.h b/include/configs/xilinx_zynqmp.h index 1d8eaa76dfe..45282ed445e 100644 --- a/include/configs/xilinx_zynqmp.h +++ b/include/configs/xilinx_zynqmp.h @@ -66,6 +66,7 @@ #define CONFIG_CMD_EXT2 #define CONFIG_CMD_EXT4 #define CONFIG_CMD_FAT +#define CONFIG_CMD_FS_GENERIC #define CONFIG_CMD_MEMORY #define CONFIG_DOS_PARTITION #define CONFIG_CMD_ELF @@ -99,8 +100,8 @@ "kernel_addr=0x80000\0" \ "fdt_addr=0x7000000\0" \ "fdt_high=0x10000000\0" \ - "sdboot=mmcinfo && fatload mmc 0:0 $fdt_addr system.dtb && " \ - "fatload mmc 0:0 $kernel_addr Image && booti $kernel_addr - $fdt_addr\0" + "sdboot=mmcinfo && load mmc 0:0 $fdt_addr system.dtb && " \ + "load mmc 0:0 $kernel_addr Image && booti $kernel_addr - $fdt_addr\0" #define CONFIG_BOOTARGS "setenv bootargs console=ttyPS0,${baudrate} " \ "earlycon=cdns,mmio,0xff000000,${baudrate}n8" -- cgit v1.3.1 From 31137acb27edc4e7d37bcae8f489740f5ea53a41 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 15 Apr 2015 15:21:37 +0200 Subject: zynqmp: Enable SDHCI0 options Enable SDHCI0 for zynqmp. Add empty gpio.h because of sdhci requirement. Signed-off-by: Michal Simek --- arch/arm/include/asm/arch-zynqmp/gpio.h | 12 ++++++++++++ include/configs/xilinx_zynqmp.h | 2 ++ 2 files changed, 14 insertions(+) create mode 100644 arch/arm/include/asm/arch-zynqmp/gpio.h (limited to 'include') diff --git a/arch/arm/include/asm/arch-zynqmp/gpio.h b/arch/arm/include/asm/arch-zynqmp/gpio.h new file mode 100644 index 00000000000..098bbde06f3 --- /dev/null +++ b/arch/arm/include/asm/arch-zynqmp/gpio.h @@ -0,0 +1,12 @@ +/* + * Copyright 2015 Xilinx, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ARCH_ZYNQMP_GPIO_H +#define __ARCH_ZYNQMP_GPIO_H + +/* Empty file - sdhci requires this. */ + +#endif diff --git a/include/configs/xilinx_zynqmp.h b/include/configs/xilinx_zynqmp.h index 45282ed445e..880d29ca715 100644 --- a/include/configs/xilinx_zynqmp.h +++ b/include/configs/xilinx_zynqmp.h @@ -61,6 +61,8 @@ #define CONFIG_SYS_BAUDRATE_TABLE \ { 4800, 9600, 19200, 38400, 57600, 115200 } +#define CONFIG_ZYNQ_SDHCI0 + /* Command line configuration */ #define CONFIG_CMD_ENV #define CONFIG_CMD_EXT2 -- cgit v1.3.1 From 0107f2403669f764ab726d0d404e35bb9447bbcc Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 16 Mar 2015 16:43:22 +0900 Subject: ARM: zynq: move SoC sources to mach-zynq Move arch/arm/cpu/armv7/zynq/* -> arch/arm/mach-zynq/* Signed-off-by: Masahiro Yamada Signed-off-by: Michal Simek --- arch/arm/Kconfig | 2 +- arch/arm/Makefile | 1 + arch/arm/cpu/armv7/Makefile | 1 - arch/arm/cpu/armv7/zynq/Kconfig | 43 --- arch/arm/cpu/armv7/zynq/Makefile | 18 - arch/arm/cpu/armv7/zynq/clk.c | 664 -------------------------------- arch/arm/cpu/armv7/zynq/cpu.c | 67 ---- arch/arm/cpu/armv7/zynq/ddrc.c | 50 --- arch/arm/cpu/armv7/zynq/lowlevel_init.S | 26 -- arch/arm/cpu/armv7/zynq/slcr.c | 203 ---------- arch/arm/cpu/armv7/zynq/spl.c | 90 ----- arch/arm/cpu/armv7/zynq/timer.c | 168 -------- arch/arm/cpu/armv7/zynq/u-boot-spl.lds | 62 --- arch/arm/cpu/armv7/zynq/u-boot.lds | 105 ----- arch/arm/mach-zynq/Kconfig | 43 +++ arch/arm/mach-zynq/Makefile | 18 + arch/arm/mach-zynq/clk.c | 664 ++++++++++++++++++++++++++++++++ arch/arm/mach-zynq/cpu.c | 67 ++++ arch/arm/mach-zynq/ddrc.c | 50 +++ arch/arm/mach-zynq/lowlevel_init.S | 26 ++ arch/arm/mach-zynq/slcr.c | 203 ++++++++++ arch/arm/mach-zynq/spl.c | 90 +++++ arch/arm/mach-zynq/timer.c | 168 ++++++++ arch/arm/mach-zynq/u-boot-spl.lds | 62 +++ arch/arm/mach-zynq/u-boot.lds | 105 +++++ include/configs/zynq-common.h | 4 +- 26 files changed, 1500 insertions(+), 1500 deletions(-) delete mode 100644 arch/arm/cpu/armv7/zynq/Kconfig delete mode 100644 arch/arm/cpu/armv7/zynq/Makefile delete mode 100644 arch/arm/cpu/armv7/zynq/clk.c delete mode 100644 arch/arm/cpu/armv7/zynq/cpu.c delete mode 100644 arch/arm/cpu/armv7/zynq/ddrc.c delete mode 100644 arch/arm/cpu/armv7/zynq/lowlevel_init.S delete mode 100644 arch/arm/cpu/armv7/zynq/slcr.c delete mode 100644 arch/arm/cpu/armv7/zynq/spl.c delete mode 100644 arch/arm/cpu/armv7/zynq/timer.c delete mode 100644 arch/arm/cpu/armv7/zynq/u-boot-spl.lds delete mode 100644 arch/arm/cpu/armv7/zynq/u-boot.lds create mode 100644 arch/arm/mach-zynq/Kconfig create mode 100644 arch/arm/mach-zynq/Makefile create mode 100644 arch/arm/mach-zynq/clk.c create mode 100644 arch/arm/mach-zynq/cpu.c create mode 100644 arch/arm/mach-zynq/ddrc.c create mode 100644 arch/arm/mach-zynq/lowlevel_init.S create mode 100644 arch/arm/mach-zynq/slcr.c create mode 100644 arch/arm/mach-zynq/spl.c create mode 100644 arch/arm/mach-zynq/timer.c create mode 100644 arch/arm/mach-zynq/u-boot-spl.lds create mode 100644 arch/arm/mach-zynq/u-boot.lds (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 5f21b59ba81..21cd1c8cdf4 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -832,7 +832,7 @@ source "arch/arm/mach-uniphier/Kconfig" source "arch/arm/mach-versatile/Kconfig" -source "arch/arm/cpu/armv7/zynq/Kconfig" +source "arch/arm/mach-zynq/Kconfig" source "arch/arm/cpu/armv7/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index bd4749c55e9..5705d6402b9 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -55,6 +55,7 @@ machine-$(CONFIG_ORION5X) += orion5x machine-$(CONFIG_TEGRA) += tegra machine-$(CONFIG_ARCH_UNIPHIER) += uniphier machine-$(CONFIG_ARCH_VERSATILE) += versatile +machine-$(CONFIG_ZYNQ) += zynq machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y)) diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile index 21fc03b97ee..3c991e152d3 100644 --- a/arch/arm/cpu/armv7/Makefile +++ b/arch/arm/cpu/armv7/Makefile @@ -56,4 +56,3 @@ obj-$(if $(filter stv0991,$(SOC)),y) += stv0991/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_U8500) += u8500/ obj-$(CONFIG_VF610) += vf610/ -obj-$(CONFIG_ZYNQ) += zynq/ diff --git a/arch/arm/cpu/armv7/zynq/Kconfig b/arch/arm/cpu/armv7/zynq/Kconfig deleted file mode 100644 index 26e570eb662..00000000000 --- a/arch/arm/cpu/armv7/zynq/Kconfig +++ /dev/null @@ -1,43 +0,0 @@ -if ZYNQ - -choice - prompt "Xilinx Zynq board select" - -config TARGET_ZYNQ_ZED - bool "Zynq ZedBoard" - -config TARGET_ZYNQ_MICROZED - bool "Zynq MicroZed" - -config TARGET_ZYNQ_PICOZED - bool "Zynq PicoZed" - -config TARGET_ZYNQ_ZC70X - bool "Zynq ZC702/ZC706 Board" - -config TARGET_ZYNQ_ZC770 - bool "Zynq ZC770 Board" - -config TARGET_ZYNQ_ZYBO - bool "Zynq Zybo Board" - -endchoice - -config SYS_BOARD - default "zynq" - -config SYS_VENDOR - default "xilinx" - -config SYS_SOC - default "zynq" - -config SYS_CONFIG_NAME - default "zynq_zed" if TARGET_ZYNQ_ZED - default "zynq_microzed" if TARGET_ZYNQ_MICROZED - default "zynq_picozed" if TARGET_ZYNQ_PICOZED - default "zynq_zc70x" if TARGET_ZYNQ_ZC70X - default "zynq_zc770" if TARGET_ZYNQ_ZC770 - default "zynq_zybo" if TARGET_ZYNQ_ZYBO - -endif diff --git a/arch/arm/cpu/armv7/zynq/Makefile b/arch/arm/cpu/armv7/zynq/Makefile deleted file mode 100644 index bf29b4d396d..00000000000 --- a/arch/arm/cpu/armv7/zynq/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# -# (C) Copyright 2000-2003 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# (C) Copyright 2008 -# Guennadi Liakhovetki, DENX Software Engineering, -# -# SPDX-License-Identifier: GPL-2.0+ -# - -obj-y := timer.o -obj-y += cpu.o -obj-y += ddrc.o -obj-y += slcr.o -obj-y += clk.o -obj-y += lowlevel_init.o -AFLAGS_lowlevel_init.o := -mfpu=neon -obj-$(CONFIG_SPL_BUILD) += spl.o diff --git a/arch/arm/cpu/armv7/zynq/clk.c b/arch/arm/cpu/armv7/zynq/clk.c deleted file mode 100644 index d2885dc2b9e..00000000000 --- a/arch/arm/cpu/armv7/zynq/clk.c +++ /dev/null @@ -1,664 +0,0 @@ -/* - * Copyright (C) 2013 Soren Brinkmann - * Copyright (C) 2013 Xilinx, Inc. All rights reserved. - * - * SPDX-License-Identifier: GPL-2.0+ - */ -#include -#include -#include -#include -#include -#include - -/* Board oscillator frequency */ -#ifndef CONFIG_ZYNQ_PS_CLK_FREQ -# define CONFIG_ZYNQ_PS_CLK_FREQ 33333333UL -#endif - -/* Register bitfield defines */ -#define PLLCTRL_FBDIV_MASK 0x7f000 -#define PLLCTRL_FBDIV_SHIFT 12 -#define PLLCTRL_BPFORCE_MASK (1 << 4) -#define PLLCTRL_PWRDWN_MASK 2 -#define PLLCTRL_PWRDWN_SHIFT 1 -#define PLLCTRL_RESET_MASK 1 -#define PLLCTRL_RESET_SHIFT 0 - -#define ZYNQ_CLK_MAXDIV 0x3f -#define CLK_CTRL_DIV1_SHIFT 20 -#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 4 -#define CLK_CTRL_SRCSEL_MASK (0x3 << CLK_CTRL_SRCSEL_SHIFT) - -#define CLK_CTRL_DIV2X_SHIFT 26 -#define CLK_CTRL_DIV2X_MASK (ZYNQ_CLK_MAXDIV << CLK_CTRL_DIV2X_SHIFT) -#define CLK_CTRL_DIV3X_SHIFT 20 -#define CLK_CTRL_DIV3X_MASK (ZYNQ_CLK_MAXDIV << CLK_CTRL_DIV3X_SHIFT) - -#define ZYNQ_CLKMUX_SEL_0 0 -#define ZYNQ_CLKMUX_SEL_1 1 -#define ZYNQ_CLKMUX_SEL_2 2 -#define ZYNQ_CLKMUX_SEL_3 3 - -DECLARE_GLOBAL_DATA_PTR; - -struct clk; - -/** - * struct clk_ops: - * @set_rate: Function pointer to set_rate() implementation - * @get_rate: Function pointer to get_rate() implementation - */ -struct clk_ops { - int (*set_rate)(struct clk *clk, unsigned long rate); - unsigned long (*get_rate)(struct clk *clk); -}; - -/** - * struct clk: - * @name: Clock name - * @frequency: Currenct frequency - * @parent: Parent clock - * @flags: Clock flags - * @reg: Clock control register - * @ops: Clock operations - */ -struct clk { - char *name; - unsigned long frequency; - enum zynq_clk parent; - unsigned int flags; - u32 *reg; - struct clk_ops ops; -}; -#define ZYNQ_CLK_FLAGS_HAS_2_DIVS 1 - -static struct clk clks[clk_max]; - -/** - * __zynq_clk_cpu_get_parent() - Decode clock multiplexer - * @srcsel: Mux select value - * Returns the clock identifier associated with the selected mux input. - */ -static int __zynq_clk_cpu_get_parent(unsigned int srcsel) -{ - unsigned int ret; - - switch (srcsel) { - case ZYNQ_CLKMUX_SEL_0: - case ZYNQ_CLKMUX_SEL_1: - ret = armpll_clk; - break; - case ZYNQ_CLKMUX_SEL_2: - ret = ddrpll_clk; - break; - case ZYNQ_CLKMUX_SEL_3: - ret = iopll_clk; - break; - default: - ret = armpll_clk; - break; - } - - return ret; -} - -/** - * ddr2x_get_rate() - Get clock rate of DDR2x clock - * @clk: Clock handle - * Returns the current clock rate of @clk. - */ -static unsigned long ddr2x_get_rate(struct clk *clk) -{ - u32 clk_ctrl = readl(clk->reg); - u32 div = (clk_ctrl & CLK_CTRL_DIV2X_MASK) >> CLK_CTRL_DIV2X_SHIFT; - - return DIV_ROUND_CLOSEST(zynq_clk_get_rate(clk->parent), div); -} - -/** - * ddr3x_get_rate() - Get clock rate of DDR3x clock - * @clk: Clock handle - * Returns the current clock rate of @clk. - */ -static unsigned long ddr3x_get_rate(struct clk *clk) -{ - u32 clk_ctrl = readl(clk->reg); - u32 div = (clk_ctrl & CLK_CTRL_DIV3X_MASK) >> CLK_CTRL_DIV3X_SHIFT; - - return DIV_ROUND_CLOSEST(zynq_clk_get_rate(clk->parent), div); -} - -static void init_ddr_clocks(void) -{ - u32 div0, div1; - unsigned long prate = zynq_clk_get_rate(ddrpll_clk); - u32 clk_ctrl = readl(&slcr_base->ddr_clk_ctrl); - - /* DDR2x */ - clks[ddr2x_clk].reg = &slcr_base->ddr_clk_ctrl; - clks[ddr2x_clk].parent = ddrpll_clk; - clks[ddr2x_clk].name = "ddr_2x"; - clks[ddr2x_clk].frequency = ddr2x_get_rate(&clks[ddr2x_clk]); - clks[ddr2x_clk].ops.get_rate = ddr2x_get_rate; - - /* DDR3x */ - clks[ddr3x_clk].reg = &slcr_base->ddr_clk_ctrl; - clks[ddr3x_clk].parent = ddrpll_clk; - clks[ddr3x_clk].name = "ddr_3x"; - clks[ddr3x_clk].frequency = ddr3x_get_rate(&clks[ddr3x_clk]); - clks[ddr3x_clk].ops.get_rate = ddr3x_get_rate; - - /* DCI */ - clk_ctrl = readl(&slcr_base->dci_clk_ctrl); - div0 = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; - div1 = (clk_ctrl & CLK_CTRL_DIV1_MASK) >> CLK_CTRL_DIV1_SHIFT; - clks[dci_clk].reg = &slcr_base->dci_clk_ctrl; - clks[dci_clk].parent = ddrpll_clk; - clks[dci_clk].frequency = DIV_ROUND_CLOSEST( - DIV_ROUND_CLOSEST(prate, div0), div1); - clks[dci_clk].name = "dci"; - - gd->bd->bi_ddr_freq = clks[ddr3x_clk].frequency / 1000000; -} - -static void init_cpu_clocks(void) -{ - int clk_621; - u32 reg, div, srcsel; - enum zynq_clk parent; - - reg = readl(&slcr_base->arm_clk_ctrl); - clk_621 = readl(&slcr_base->clk_621_true) & 1; - div = (reg & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; - srcsel = (reg & CLK_CTRL_SRCSEL_MASK) >> CLK_CTRL_SRCSEL_SHIFT; - parent = __zynq_clk_cpu_get_parent(srcsel); - - /* cpu clocks */ - clks[cpu_6or4x_clk].reg = &slcr_base->arm_clk_ctrl; - clks[cpu_6or4x_clk].parent = parent; - clks[cpu_6or4x_clk].frequency = DIV_ROUND_CLOSEST( - zynq_clk_get_rate(parent), div); - clks[cpu_6or4x_clk].name = "cpu_6or4x"; - - clks[cpu_3or2x_clk].reg = &slcr_base->arm_clk_ctrl; - clks[cpu_3or2x_clk].parent = cpu_6or4x_clk; - clks[cpu_3or2x_clk].frequency = zynq_clk_get_rate(cpu_6or4x_clk) / 2; - clks[cpu_3or2x_clk].name = "cpu_3or2x"; - - clks[cpu_2x_clk].reg = &slcr_base->arm_clk_ctrl; - clks[cpu_2x_clk].parent = cpu_6or4x_clk; - clks[cpu_2x_clk].frequency = zynq_clk_get_rate(cpu_6or4x_clk) / - (2 + clk_621); - clks[cpu_2x_clk].name = "cpu_2x"; - - clks[cpu_1x_clk].reg = &slcr_base->arm_clk_ctrl; - clks[cpu_1x_clk].parent = cpu_6or4x_clk; - clks[cpu_1x_clk].frequency = zynq_clk_get_rate(cpu_6or4x_clk) / - (4 + 2 * clk_621); - clks[cpu_1x_clk].name = "cpu_1x"; -} - -/** - * periph_calc_two_divs() - Calculate clock dividers - * @cur_rate: Current clock rate - * @tgt_rate: Target clock rate - * @prate: Parent clock rate - * @div0: First divider (output) - * @div1: Second divider (output) - * Returns the actual clock rate possible. - * - * Calculates clock dividers for clocks with two 6-bit dividers. - */ -static unsigned long periph_calc_two_divs(unsigned long cur_rate, - unsigned long tgt_rate, unsigned long prate, u32 *div0, - u32 *div1) -{ - long err, best_err = (long)(~0UL >> 1); - unsigned long rate, best_rate = 0; - u32 d0, d1; - - for (d0 = 1; d0 <= ZYNQ_CLK_MAXDIV; d0++) { - for (d1 = 1; d1 <= ZYNQ_CLK_MAXDIV >> 1; d1++) { - rate = DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(prate, d0), - d1); - err = abs(rate - tgt_rate); - - if (err < best_err) { - *div0 = d0; - *div1 = d1; - best_err = err; - best_rate = rate; - } - } - } - - return best_rate; -} - -/** - * zynq_clk_periph_set_rate() - Set clock rate - * @clk: Handle of the peripheral clock - * @rate: New clock rate - * Sets the clock frequency of @clk to @rate. Returns zero on success. - */ -static int zynq_clk_periph_set_rate(struct clk *clk, - unsigned long rate) -{ - u32 ctrl, div0 = 0, div1 = 0; - unsigned long prate, new_rate, cur_rate = clk->frequency; - - ctrl = readl(clk->reg); - prate = zynq_clk_get_rate(clk->parent); - ctrl &= ~CLK_CTRL_DIV0_MASK; - - if (clk->flags & ZYNQ_CLK_FLAGS_HAS_2_DIVS) { - ctrl &= ~CLK_CTRL_DIV1_MASK; - new_rate = periph_calc_two_divs(cur_rate, rate, prate, &div0, - &div1); - ctrl |= div1 << CLK_CTRL_DIV1_SHIFT; - } else { - div0 = DIV_ROUND_CLOSEST(prate, rate); - div0 &= ZYNQ_CLK_MAXDIV; - new_rate = DIV_ROUND_CLOSEST(rate, div0); - } - - /* write new divs to hardware */ - ctrl |= div0 << CLK_CTRL_DIV0_SHIFT; - writel(ctrl, clk->reg); - - /* update frequency in clk framework */ - clk->frequency = new_rate; - - return 0; -} - -/** - * zynq_clk_periph_get_rate() - Get clock rate - * @clk: Handle of the peripheral clock - * Returns the current clock rate of @clk. - */ -static unsigned long zynq_clk_periph_get_rate(struct clk *clk) -{ - u32 clk_ctrl = readl(clk->reg); - u32 div0 = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; - u32 div1 = 1; - - if (clk->flags & ZYNQ_CLK_FLAGS_HAS_2_DIVS) - div1 = (clk_ctrl & CLK_CTRL_DIV1_MASK) >> CLK_CTRL_DIV1_SHIFT; - - /* a register value of zero == division by 1 */ - if (!div0) - div0 = 1; - if (!div1) - div1 = 1; - - return - DIV_ROUND_CLOSEST( - DIV_ROUND_CLOSEST(zynq_clk_get_rate(clk->parent), div0), - div1); -} - -/** - * __zynq_clk_periph_get_parent() - Decode clock multiplexer - * @srcsel: Mux select value - * Returns the clock identifier associated with the selected mux input. - */ -static enum zynq_clk __zynq_clk_periph_get_parent(u32 srcsel) -{ - switch (srcsel) { - case ZYNQ_CLKMUX_SEL_0: - case ZYNQ_CLKMUX_SEL_1: - return iopll_clk; - case ZYNQ_CLKMUX_SEL_2: - return armpll_clk; - case ZYNQ_CLKMUX_SEL_3: - return ddrpll_clk; - default: - return 0; - } -} - -/** - * zynq_clk_periph_get_parent() - Decode clock multiplexer - * @clk: Clock handle - * Returns the clock identifier associated with the selected mux input. - */ -static enum zynq_clk zynq_clk_periph_get_parent(struct clk *clk) -{ - u32 clk_ctrl = readl(clk->reg); - u32 srcsel = (clk_ctrl & CLK_CTRL_SRCSEL_MASK) >> CLK_CTRL_SRCSEL_SHIFT; - - return __zynq_clk_periph_get_parent(srcsel); -} - -/** - * zynq_clk_register_periph_clk() - Set up a peripheral clock with the framework - * @clk: Pointer to struct clk for the clock - * @ctrl: Clock control register - * @name: PLL name - * @two_divs: Indicates whether the clock features one or two dividers - */ -static int zynq_clk_register_periph_clk(struct clk *clk, u32 *ctrl, char *name, - bool two_divs) -{ - clk->name = name; - clk->reg = ctrl; - if (two_divs) - clk->flags = ZYNQ_CLK_FLAGS_HAS_2_DIVS; - clk->parent = zynq_clk_periph_get_parent(clk); - clk->frequency = zynq_clk_periph_get_rate(clk); - clk->ops.get_rate = zynq_clk_periph_get_rate; - clk->ops.set_rate = zynq_clk_periph_set_rate; - - return 0; -} - -static void init_periph_clocks(void) -{ - zynq_clk_register_periph_clk(&clks[gem0_clk], &slcr_base->gem0_clk_ctrl, - "gem0", 1); - zynq_clk_register_periph_clk(&clks[gem1_clk], &slcr_base->gem1_clk_ctrl, - "gem1", 1); - - zynq_clk_register_periph_clk(&clks[smc_clk], &slcr_base->smc_clk_ctrl, - "smc", 0); - - zynq_clk_register_periph_clk(&clks[lqspi_clk], - &slcr_base->lqspi_clk_ctrl, "lqspi", 0); - - zynq_clk_register_periph_clk(&clks[sdio0_clk], - &slcr_base->sdio_clk_ctrl, "sdio0", 0); - zynq_clk_register_periph_clk(&clks[sdio1_clk], - &slcr_base->sdio_clk_ctrl, "sdio1", 0); - - zynq_clk_register_periph_clk(&clks[spi0_clk], &slcr_base->spi_clk_ctrl, - "spi0", 0); - zynq_clk_register_periph_clk(&clks[spi1_clk], &slcr_base->spi_clk_ctrl, - "spi1", 0); - - zynq_clk_register_periph_clk(&clks[uart0_clk], - &slcr_base->uart_clk_ctrl, "uart0", 0); - zynq_clk_register_periph_clk(&clks[uart1_clk], - &slcr_base->uart_clk_ctrl, "uart1", 0); - - zynq_clk_register_periph_clk(&clks[dbg_trc_clk], - &slcr_base->dbg_clk_ctrl, "dbg_trc", 0); - zynq_clk_register_periph_clk(&clks[dbg_apb_clk], - &slcr_base->dbg_clk_ctrl, "dbg_apb", 0); - - zynq_clk_register_periph_clk(&clks[pcap_clk], - &slcr_base->pcap_clk_ctrl, "pcap", 0); - - zynq_clk_register_periph_clk(&clks[fclk0_clk], - &slcr_base->fpga0_clk_ctrl, "fclk0", 1); - zynq_clk_register_periph_clk(&clks[fclk1_clk], - &slcr_base->fpga1_clk_ctrl, "fclk1", 1); - zynq_clk_register_periph_clk(&clks[fclk2_clk], - &slcr_base->fpga2_clk_ctrl, "fclk2", 1); - zynq_clk_register_periph_clk(&clks[fclk3_clk], - &slcr_base->fpga3_clk_ctrl, "fclk3", 1); -} - -/** - * zynq_clk_register_aper_clk() - Set up a APER clock with the framework - * @clk: Pointer to struct clk for the clock - * @ctrl: Clock control register - * @name: PLL name - */ -static void zynq_clk_register_aper_clk(struct clk *clk, u32 *ctrl, char *name) -{ - clk->name = name; - clk->reg = ctrl; - clk->parent = cpu_1x_clk; - clk->frequency = zynq_clk_get_rate(clk->parent); -} - -static void init_aper_clocks(void) -{ - zynq_clk_register_aper_clk(&clks[usb0_aper_clk], - &slcr_base->aper_clk_ctrl, "usb0_aper"); - zynq_clk_register_aper_clk(&clks[usb1_aper_clk], - &slcr_base->aper_clk_ctrl, "usb1_aper"); - - zynq_clk_register_aper_clk(&clks[gem0_aper_clk], - &slcr_base->aper_clk_ctrl, "gem0_aper"); - zynq_clk_register_aper_clk(&clks[gem1_aper_clk], - &slcr_base->aper_clk_ctrl, "gem1_aper"); - - zynq_clk_register_aper_clk(&clks[sdio0_aper_clk], - &slcr_base->aper_clk_ctrl, "sdio0_aper"); - zynq_clk_register_aper_clk(&clks[sdio1_aper_clk], - &slcr_base->aper_clk_ctrl, "sdio1_aper"); - - zynq_clk_register_aper_clk(&clks[spi0_aper_clk], - &slcr_base->aper_clk_ctrl, "spi0_aper"); - zynq_clk_register_aper_clk(&clks[spi1_aper_clk], - &slcr_base->aper_clk_ctrl, "spi1_aper"); - - zynq_clk_register_aper_clk(&clks[can0_aper_clk], - &slcr_base->aper_clk_ctrl, "can0_aper"); - zynq_clk_register_aper_clk(&clks[can1_aper_clk], - &slcr_base->aper_clk_ctrl, "can1_aper"); - - zynq_clk_register_aper_clk(&clks[i2c0_aper_clk], - &slcr_base->aper_clk_ctrl, "i2c0_aper"); - zynq_clk_register_aper_clk(&clks[i2c1_aper_clk], - &slcr_base->aper_clk_ctrl, "i2c1_aper"); - - zynq_clk_register_aper_clk(&clks[uart0_aper_clk], - &slcr_base->aper_clk_ctrl, "uart0_aper"); - zynq_clk_register_aper_clk(&clks[uart1_aper_clk], - &slcr_base->aper_clk_ctrl, "uart1_aper"); - - zynq_clk_register_aper_clk(&clks[gpio_aper_clk], - &slcr_base->aper_clk_ctrl, "gpio_aper"); - - zynq_clk_register_aper_clk(&clks[lqspi_aper_clk], - &slcr_base->aper_clk_ctrl, "lqspi_aper"); - - zynq_clk_register_aper_clk(&clks[smc_aper_clk], - &slcr_base->aper_clk_ctrl, "smc_aper"); -} - -/** - * __zynq_clk_pll_get_rate() - Get PLL rate - * @addr: Address of the PLL's control register - * Returns the current PLL output rate. - */ -static unsigned long __zynq_clk_pll_get_rate(u32 *addr) -{ - u32 reg, mul, bypass; - - reg = readl(addr); - bypass = reg & PLLCTRL_BPFORCE_MASK; - if (bypass) - mul = 1; - else - mul = (reg & PLLCTRL_FBDIV_MASK) >> PLLCTRL_FBDIV_SHIFT; - - return CONFIG_ZYNQ_PS_CLK_FREQ * mul; -} - -/** - * zynq_clk_pll_get_rate() - Get PLL rate - * @pll: Handle of the PLL - * Returns the current clock rate of @pll. - */ -static unsigned long zynq_clk_pll_get_rate(struct clk *pll) -{ - return __zynq_clk_pll_get_rate(pll->reg); -} - -/** - * zynq_clk_register_pll() - Set up a PLL with the framework - * @clk: Pointer to struct clk for the PLL - * @ctrl: PLL control register - * @name: PLL name - * @prate: PLL input clock rate - */ -static void zynq_clk_register_pll(struct clk *clk, u32 *ctrl, char *name, - unsigned long prate) -{ - clk->name = name; - clk->reg = ctrl; - clk->frequency = zynq_clk_pll_get_rate(clk); - clk->ops.get_rate = zynq_clk_pll_get_rate; -} - -/** - * clkid_2_register() - Get clock control register - * @id: Clock identifier of one of the PLLs - * Returns the address of the requested PLL's control register. - */ -static u32 *clkid_2_register(enum zynq_clk id) -{ - switch (id) { - case armpll_clk: - return &slcr_base->arm_pll_ctrl; - case ddrpll_clk: - return &slcr_base->ddr_pll_ctrl; - case iopll_clk: - return &slcr_base->io_pll_ctrl; - default: - return &slcr_base->io_pll_ctrl; - } -} - -/* API */ -/** - * zynq_clk_early_init() - Early init for the clock framework - * - * This function is called from before relocation and sets up the CPU clock - * frequency in the global data struct. - */ -void zynq_clk_early_init(void) -{ - u32 reg = readl(&slcr_base->arm_clk_ctrl); - u32 div = (reg & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; - u32 srcsel = (reg & CLK_CTRL_SRCSEL_MASK) >> CLK_CTRL_SRCSEL_SHIFT; - enum zynq_clk parent = __zynq_clk_cpu_get_parent(srcsel); - u32 *pllreg = clkid_2_register(parent); - unsigned long prate = __zynq_clk_pll_get_rate(pllreg); - - if (!div) - div = 1; - - gd->cpu_clk = DIV_ROUND_CLOSEST(prate, div); -} - -/** - * get_uart_clk() - Get UART input frequency - * @dev_index: UART ID - * Returns UART input clock frequency in Hz. - * - * Compared to zynq_clk_get_rate() this function is designed to work before - * relocation and can be called when the serial UART is set up. - */ -unsigned long get_uart_clk(int dev_index) -{ - u32 reg = readl(&slcr_base->uart_clk_ctrl); - u32 div = (reg & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; - u32 srcsel = (reg & CLK_CTRL_SRCSEL_MASK) >> CLK_CTRL_SRCSEL_SHIFT; - enum zynq_clk parent = __zynq_clk_periph_get_parent(srcsel); - u32 *pllreg = clkid_2_register(parent); - unsigned long prate = __zynq_clk_pll_get_rate(pllreg); - - if (!div) - div = 1; - - return DIV_ROUND_CLOSEST(prate, div); -} - -/** - * set_cpu_clk_info() - Initialize clock framework - * Always returns zero. - * - * This function is called from common code after relocation and sets up the - * clock framework. The framework must not be used before this function had been - * called. - */ -int set_cpu_clk_info(void) -{ - zynq_clk_register_pll(&clks[armpll_clk], &slcr_base->arm_pll_ctrl, - "armpll", CONFIG_ZYNQ_PS_CLK_FREQ); - zynq_clk_register_pll(&clks[ddrpll_clk], &slcr_base->ddr_pll_ctrl, - "ddrpll", CONFIG_ZYNQ_PS_CLK_FREQ); - zynq_clk_register_pll(&clks[iopll_clk], &slcr_base->io_pll_ctrl, - "iopll", CONFIG_ZYNQ_PS_CLK_FREQ); - - init_ddr_clocks(); - init_cpu_clocks(); - init_periph_clocks(); - init_aper_clocks(); - - gd->bd->bi_arm_freq = gd->cpu_clk / 1000000; - gd->bd->bi_dsp_freq = 0; - - return 0; -} - -/** - * zynq_clk_get_rate() - Get clock rate - * @clk: Clock identifier - * Returns the current clock rate of @clk on success or zero for an invalid - * clock id. - */ -unsigned long zynq_clk_get_rate(enum zynq_clk clk) -{ - if (clk < 0 || clk >= clk_max) - return 0; - - return clks[clk].frequency; -} - -/** - * zynq_clk_set_rate() - Set clock rate - * @clk: Clock identifier - * @rate: Requested clock rate - * Passes on the return value from the clock's set_rate() function or negative - * errno. - */ -int zynq_clk_set_rate(enum zynq_clk clk, unsigned long rate) -{ - if (clk < 0 || clk >= clk_max) - return -ENODEV; - - if (clks[clk].ops.set_rate) - return clks[clk].ops.set_rate(&clks[clk], rate); - - return -ENXIO; -} - -/** - * zynq_clk_get_name() - Get clock name - * @clk: Clock identifier - * Returns the name of @clk. - */ -const char *zynq_clk_get_name(enum zynq_clk clk) -{ - return clks[clk].name; -} - -/** - * soc_clk_dump() - Print clock frequencies - * Returns zero on success - * - * Implementation for the clk dump command. - */ -int soc_clk_dump(void) -{ - int i; - - printf("clk\t\tfrequency\n"); - for (i = 0; i < clk_max; i++) { - const char *name = zynq_clk_get_name(i); - if (name) - printf("%10s%20lu\n", name, zynq_clk_get_rate(i)); - } - - return 0; -} diff --git a/arch/arm/cpu/armv7/zynq/cpu.c b/arch/arm/cpu/armv7/zynq/cpu.c deleted file mode 100644 index 914b1feb683..00000000000 --- a/arch/arm/cpu/armv7/zynq/cpu.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2012 Michal Simek - * Copyright (C) 2012 Xilinx, Inc. All rights reserved. - * - * SPDX-License-Identifier: GPL-2.0+ - */ -#include -#include -#include -#include -#include - -#define ZYNQ_SILICON_VER_MASK 0xF0000000 -#define ZYNQ_SILICON_VER_SHIFT 28 - -int arch_cpu_init(void) -{ - zynq_slcr_unlock(); -#ifndef CONFIG_SPL_BUILD - /* Device config APB, unlock the PCAP */ - writel(0x757BDF0D, &devcfg_base->unlock); - writel(0xFFFFFFFF, &devcfg_base->rom_shadow); - -#if (CONFIG_SYS_SDRAM_BASE == 0) - /* remap DDR to zero, FILTERSTART */ - writel(0, &scu_base->filter_start); - - /* OCM_CFG, Mask out the ROM, map ram into upper addresses */ - writel(0x1F, &slcr_base->ocm_cfg); - /* FPGA_RST_CTRL, clear resets on AXI fabric ports */ - writel(0x0, &slcr_base->fpga_rst_ctrl); - /* Set urgent bits with register */ - writel(0x0, &slcr_base->ddr_urgent_sel); - /* Urgent write, ports S2/S3 */ - writel(0xC, &slcr_base->ddr_urgent); -#endif -#endif - zynq_clk_early_init(); - zynq_slcr_lock(); - - return 0; -} - -unsigned int zynq_get_silicon_version(void) -{ - unsigned int ver; - - ver = (readl(&devcfg_base->mctrl) & - ZYNQ_SILICON_VER_MASK) >> ZYNQ_SILICON_VER_SHIFT; - - return ver; -} - -void reset_cpu(ulong addr) -{ - zynq_slcr_cpu_reset(); - while (1) - ; -} - -#ifndef CONFIG_SYS_DCACHE_OFF -void enable_caches(void) -{ - /* Enable D-cache. I-cache is already enabled in start.S */ - dcache_enable(); -} -#endif diff --git a/arch/arm/cpu/armv7/zynq/ddrc.c b/arch/arm/cpu/armv7/zynq/ddrc.c deleted file mode 100644 index 5b20accbcb1..00000000000 --- a/arch/arm/cpu/armv7/zynq/ddrc.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2012 - 2013 Michal Simek - * Copyright (C) 2012 - 2013 Xilinx, Inc. All rights reserved. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -/* Control regsiter bitfield definitions */ -#define ZYNQ_DDRC_CTRLREG_BUSWIDTH_MASK 0xC -#define ZYNQ_DDRC_CTRLREG_BUSWIDTH_SHIFT 2 -#define ZYNQ_DDRC_CTRLREG_BUSWIDTH_16BIT 1 - -/* ECC scrub regsiter definitions */ -#define ZYNQ_DDRC_ECC_SCRUBREG_ECC_MODE_MASK 0x7 -#define ZYNQ_DDRC_ECC_SCRUBREG_ECCMODE_SECDED 0x4 - -void zynq_ddrc_init(void) -{ - u32 width, ecctype; - - width = readl(&ddrc_base->ddrc_ctrl); - width = (width & ZYNQ_DDRC_CTRLREG_BUSWIDTH_MASK) >> - ZYNQ_DDRC_CTRLREG_BUSWIDTH_SHIFT; - ecctype = (readl(&ddrc_base->ecc_scrub) & - ZYNQ_DDRC_ECC_SCRUBREG_ECC_MODE_MASK); - - /* ECC is enabled when memory is in 16bit mode and it is enabled */ - if ((ecctype == ZYNQ_DDRC_ECC_SCRUBREG_ECCMODE_SECDED) && - (width == ZYNQ_DDRC_CTRLREG_BUSWIDTH_16BIT)) { - puts("ECC enabled "); - /* - * Clear the first 1MB because it is not initialized from - * first stage bootloader. To get ECC to work all memory has - * been initialized by writing any value. - */ - /* cppcheck-suppress nullPointer */ - memset((void *)0, 0, 1 * 1024 * 1024); - - gd->ram_size /= 2; - } else { - puts("ECC disabled "); - } -} diff --git a/arch/arm/cpu/armv7/zynq/lowlevel_init.S b/arch/arm/cpu/armv7/zynq/lowlevel_init.S deleted file mode 100644 index 6d714b711cb..00000000000 --- a/arch/arm/cpu/armv7/zynq/lowlevel_init.S +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2013 Xilinx, Inc. All rights reserved. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include - -ENTRY(lowlevel_init) - - /* Enable the the VFP */ - mrc p15, 0, r1, c1, c0, 2 - orr r1, r1, #(0x3 << 20) - orr r1, r1, #(0x3 << 20) - mcr p15, 0, r1, c1, c0, 2 - isb - fmrx r1, FPEXC - orr r1,r1, #(1<<30) - fmxr FPEXC, r1 - - /* Move back to caller */ - mov pc, lr - -ENDPROC(lowlevel_init) diff --git a/arch/arm/cpu/armv7/zynq/slcr.c b/arch/arm/cpu/armv7/zynq/slcr.c deleted file mode 100644 index 05f4099aaee..00000000000 --- a/arch/arm/cpu/armv7/zynq/slcr.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2013 Xilinx Inc. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include - -#define SLCR_LOCK_MAGIC 0x767B -#define SLCR_UNLOCK_MAGIC 0xDF0D - -#define SLCR_USB_L1_SEL 0x04 - -#define SLCR_IDCODE_MASK 0x1F000 -#define SLCR_IDCODE_SHIFT 12 - -/* - * zynq_slcr_mio_get_status - Get the status of MIO peripheral. - * - * @peri_name: Name of the peripheral for checking MIO status - * @get_pins: Pointer to array of get pin for this peripheral - * @num_pins: Number of pins for this peripheral - * @mask: Mask value - * @check_val: Required check value to get the status of periph - */ -struct zynq_slcr_mio_get_status { - const char *peri_name; - const int *get_pins; - int num_pins; - u32 mask; - u32 check_val; -}; - -static const int usb0_pins[] = { - 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39 -}; - -static const int usb1_pins[] = { - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 -}; - -static const struct zynq_slcr_mio_get_status mio_periphs[] = { - { - "usb0", - usb0_pins, - ARRAY_SIZE(usb0_pins), - SLCR_USB_L1_SEL, - SLCR_USB_L1_SEL, - }, - { - "usb1", - usb1_pins, - ARRAY_SIZE(usb1_pins), - SLCR_USB_L1_SEL, - SLCR_USB_L1_SEL, - }, -}; - -static int slcr_lock = 1; /* 1 means locked, 0 means unlocked */ - -void zynq_slcr_lock(void) -{ - if (!slcr_lock) { - writel(SLCR_LOCK_MAGIC, &slcr_base->slcr_lock); - slcr_lock = 1; - } -} - -void zynq_slcr_unlock(void) -{ - if (slcr_lock) { - writel(SLCR_UNLOCK_MAGIC, &slcr_base->slcr_unlock); - slcr_lock = 0; - } -} - -/* Reset the entire system */ -void zynq_slcr_cpu_reset(void) -{ - /* - * Unlock the SLCR then reset the system. - * Note that this seems to require raw i/o - * functions or there's a lockup? - */ - zynq_slcr_unlock(); - - /* - * Clear 0x0F000000 bits of reboot status register to workaround - * the FSBL not loading the bitstream after soft-reboot - * This is a temporary solution until we know more. - */ - clrbits_le32(&slcr_base->reboot_status, 0xF000000); - - writel(1, &slcr_base->pss_rst_ctrl); -} - -/* Setup clk for network */ -void zynq_slcr_gem_clk_setup(u32 gem_id, unsigned long clk_rate) -{ - int ret; - - zynq_slcr_unlock(); - - if (gem_id > 1) { - printf("Non existing GEM id %d\n", gem_id); - goto out; - } - - ret = zynq_clk_set_rate(gem0_clk + gem_id, clk_rate); - if (ret) - goto out; - - if (gem_id) { - /* Configure GEM_RCLK_CTRL */ - writel(1, &slcr_base->gem1_rclk_ctrl); - } else { - /* Configure GEM_RCLK_CTRL */ - writel(1, &slcr_base->gem0_rclk_ctrl); - } - udelay(100000); -out: - zynq_slcr_lock(); -} - -void zynq_slcr_devcfg_disable(void) -{ - u32 reg_val; - - zynq_slcr_unlock(); - - /* Disable AXI interface by asserting FPGA resets */ - writel(0xF, &slcr_base->fpga_rst_ctrl); - - /* Disable Level shifters before setting PS-PL */ - reg_val = readl(&slcr_base->lvl_shftr_en); - reg_val &= ~0xF; - writel(reg_val, &slcr_base->lvl_shftr_en); - - /* Set Level Shifters DT618760 */ - writel(0xA, &slcr_base->lvl_shftr_en); - - zynq_slcr_lock(); -} - -void zynq_slcr_devcfg_enable(void) -{ - zynq_slcr_unlock(); - - /* Set Level Shifters DT618760 */ - writel(0xF, &slcr_base->lvl_shftr_en); - - /* Enable AXI interface by de-asserting FPGA resets */ - writel(0x0, &slcr_base->fpga_rst_ctrl); - - zynq_slcr_lock(); -} - -u32 zynq_slcr_get_boot_mode(void) -{ - /* Get the bootmode register value */ - return readl(&slcr_base->boot_mode); -} - -u32 zynq_slcr_get_idcode(void) -{ - return (readl(&slcr_base->pss_idcode) & SLCR_IDCODE_MASK) >> - SLCR_IDCODE_SHIFT; -} - -/* - * zynq_slcr_get_mio_pin_status - Get the MIO pin status of peripheral. - * - * @periph: Name of the peripheral - * - * Returns count to indicate the number of pins configured for the - * given @periph. - */ -int zynq_slcr_get_mio_pin_status(const char *periph) -{ - const struct zynq_slcr_mio_get_status *mio_ptr; - int val, i, j; - int mio = 0; - - for (i = 0; i < ARRAY_SIZE(mio_periphs); i++) { - if (strcmp(periph, mio_periphs[i].peri_name) == 0) { - mio_ptr = &mio_periphs[i]; - for (j = 0; j < mio_ptr->num_pins; j++) { - val = readl(&slcr_base->mio_pin - [mio_ptr->get_pins[j]]); - if ((val & mio_ptr->mask) == mio_ptr->check_val) - mio++; - } - break; - } - } - - return mio; -} diff --git a/arch/arm/cpu/armv7/zynq/spl.c b/arch/arm/cpu/armv7/zynq/spl.c deleted file mode 100644 index 13025f034bf..00000000000 --- a/arch/arm/cpu/armv7/zynq/spl.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * (C) Copyright 2014 Xilinx, Inc. Michal Simek - * - * SPDX-License-Identifier: GPL-2.0+ - */ -#include -#include - -#include -#include -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -void board_init_f(ulong dummy) -{ - ps7_init(); - - /* Clear the BSS. */ - memset(__bss_start, 0, __bss_end - __bss_start); - - preloader_console_init(); - arch_cpu_init(); - board_init_r(NULL, 0); -} - -#ifdef CONFIG_SPL_BOARD_INIT -void spl_board_init(void) -{ - board_init(); -} -#endif - -u32 spl_boot_device(void) -{ - u32 mode; - - switch ((zynq_slcr_get_boot_mode()) & ZYNQ_BM_MASK) { -#ifdef CONFIG_SPL_SPI_SUPPORT - case ZYNQ_BM_QSPI: - puts("qspi boot\n"); - mode = BOOT_DEVICE_SPI; - break; -#endif - case ZYNQ_BM_NAND: - mode = BOOT_DEVICE_NAND; - break; - case ZYNQ_BM_NOR: - mode = BOOT_DEVICE_NOR; - break; -#ifdef CONFIG_SPL_MMC_SUPPORT - case ZYNQ_BM_SD: - puts("mmc boot\n"); - mode = BOOT_DEVICE_MMC1; - break; -#endif - case ZYNQ_BM_JTAG: - mode = BOOT_DEVICE_RAM; - break; - default: - puts("Unsupported boot mode selected\n"); - hang(); - } - - return mode; -} - -#ifdef CONFIG_SPL_MMC_SUPPORT -u32 spl_boot_mode(void) -{ - return MMCSD_MODE_FS; -} -#endif - -#ifdef CONFIG_SPL_OS_BOOT -int spl_start_uboot(void) -{ - /* boot linux */ - return 0; -} -#endif - -__weak void ps7_init(void) -{ - /* - * This function is overridden by the one in - * board/xilinx/zynq/ps7_init_gpl.c, if it exists. - */ -} diff --git a/arch/arm/cpu/armv7/zynq/timer.c b/arch/arm/cpu/armv7/zynq/timer.c deleted file mode 100644 index 5ed9642df9b..00000000000 --- a/arch/arm/cpu/armv7/zynq/timer.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2012 Michal Simek - * Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved. - * - * (C) Copyright 2008 - * Guennadi Liakhovetki, DENX Software Engineering, - * - * (C) Copyright 2004 - * Philippe Robin, ARM Ltd. - * - * (C) Copyright 2002-2004 - * Gary Jennejohn, DENX Software Engineering, - * - * (C) Copyright 2003 - * Texas Instruments - * - * (C) Copyright 2002 - * Sysgo Real-Time Solutions, GmbH - * Marius Groeger - * - * (C) Copyright 2002 - * Sysgo Real-Time Solutions, GmbH - * Alex Zuepke - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -struct scu_timer { - u32 load; /* Timer Load Register */ - u32 counter; /* Timer Counter Register */ - u32 control; /* Timer Control Register */ -}; - -static struct scu_timer *timer_base = - (struct scu_timer *)ZYNQ_SCUTIMER_BASEADDR; - -#define SCUTIMER_CONTROL_PRESCALER_MASK 0x0000FF00 /* Prescaler */ -#define SCUTIMER_CONTROL_PRESCALER_SHIFT 8 -#define SCUTIMER_CONTROL_AUTO_RELOAD_MASK 0x00000002 /* Auto-reload */ -#define SCUTIMER_CONTROL_ENABLE_MASK 0x00000001 /* Timer enable */ - -#define TIMER_LOAD_VAL 0xFFFFFFFF -#define TIMER_PRESCALE 255 - -int timer_init(void) -{ - const u32 emask = SCUTIMER_CONTROL_AUTO_RELOAD_MASK | - (TIMER_PRESCALE << SCUTIMER_CONTROL_PRESCALER_SHIFT) | - SCUTIMER_CONTROL_ENABLE_MASK; - - gd->arch.timer_rate_hz = (gd->cpu_clk / 2) / (TIMER_PRESCALE + 1); - - /* Load the timer counter register */ - writel(0xFFFFFFFF, &timer_base->load); - - /* - * Start the A9Timer device - * Enable Auto reload mode, Clear prescaler control bits - * Set prescaler value, Enable the decrementer - */ - clrsetbits_le32(&timer_base->control, SCUTIMER_CONTROL_PRESCALER_MASK, - emask); - - /* Reset time */ - gd->arch.lastinc = readl(&timer_base->counter) / - (gd->arch.timer_rate_hz / CONFIG_SYS_HZ); - gd->arch.tbl = 0; - - return 0; -} - -/* - * This function is derived from PowerPC code (read timebase as long long). - * On ARM it just returns the timer value. - */ -ulong get_timer_masked(void) -{ - ulong now; - - now = readl(&timer_base->counter) / - (gd->arch.timer_rate_hz / CONFIG_SYS_HZ); - - if (gd->arch.lastinc >= now) { - /* Normal mode */ - gd->arch.tbl += gd->arch.lastinc - now; - } else { - /* We have an overflow ... */ - gd->arch.tbl += gd->arch.lastinc + (TIMER_LOAD_VAL / - (gd->arch.timer_rate_hz / CONFIG_SYS_HZ)) - - now + 1; - } - gd->arch.lastinc = now; - - return gd->arch.tbl; -} - -void __udelay(unsigned long usec) -{ - u32 countticks; - u32 timeend; - u32 timediff; - u32 timenow; - - if (usec == 0) - return; - - countticks = lldiv(((unsigned long long)gd->arch.timer_rate_hz * usec), - 1000000); - - /* decrementing timer */ - timeend = readl(&timer_base->counter) - countticks; - -#if TIMER_LOAD_VAL != 0xFFFFFFFF - /* do not manage multiple overflow */ - if (countticks >= TIMER_LOAD_VAL) - countticks = TIMER_LOAD_VAL - 1; -#endif - - do { - timenow = readl(&timer_base->counter); - - if (timenow >= timeend) { - /* normal case */ - timediff = timenow - timeend; - } else { - if ((TIMER_LOAD_VAL - timeend + timenow) <= - countticks) { - /* overflow */ - timediff = TIMER_LOAD_VAL - timeend + timenow; - } else { - /* missed the exact match */ - break; - } - } - } while (timediff > 0); -} - -/* Timer without interrupts */ -ulong get_timer(ulong base) -{ - return get_timer_masked() - base; -} - -/* - * This function is derived from PowerPC code (read timebase as long long). - * On ARM it just returns the timer value. - */ -unsigned long long get_ticks(void) -{ - return get_timer(0); -} - -/* - * This function is derived from PowerPC code (timebase clock frequency). - * On ARM it returns the number of timer ticks per second. - */ -ulong get_tbclk(void) -{ - return CONFIG_SYS_HZ; -} diff --git a/arch/arm/cpu/armv7/zynq/u-boot-spl.lds b/arch/arm/cpu/armv7/zynq/u-boot-spl.lds deleted file mode 100644 index 0f2f756f835..00000000000 --- a/arch/arm/cpu/armv7/zynq/u-boot-spl.lds +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2014 Xilinx, Inc. Michal Simek - * Copyright (c) 2004-2008 Texas Instruments - * - * (C) Copyright 2002 - * Gary Jennejohn, DENX Software Engineering, - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -MEMORY { .sram : ORIGIN = CONFIG_SPL_TEXT_BASE,\ - LENGTH = CONFIG_SPL_MAX_SIZE } -MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR, \ - LENGTH = CONFIG_SPL_BSS_MAX_SIZE } - -OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") -OUTPUT_ARCH(arm) -ENTRY(_start) -SECTIONS -{ - . = ALIGN(4); - .text : - { - __image_copy_start = .; - *(.vectors) - CPUDIR/start.o (.text*) - *(.text*) - } > .sram - - . = ALIGN(4); - .rodata : { - *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) - } > .sram - - . = ALIGN(4); - .data : { - *(.data*) - } > .sram - - . = ALIGN(4); - - . = .; - - __image_copy_end = .; - - _end = .; - - /* Move BSS section to RAM because of FAT */ - .bss (NOLOAD) : { - __bss_start = .; - *(.bss*) - . = ALIGN(4); - __bss_end = .; - } > .sdram - - /DISCARD/ : { *(.dynsym) } - /DISCARD/ : { *(.dynstr*) } - /DISCARD/ : { *(.dynamic*) } - /DISCARD/ : { *(.plt*) } - /DISCARD/ : { *(.interp*) } - /DISCARD/ : { *(.gnu*) } -} diff --git a/arch/arm/cpu/armv7/zynq/u-boot.lds b/arch/arm/cpu/armv7/zynq/u-boot.lds deleted file mode 100644 index 4dc9bb0102c..00000000000 --- a/arch/arm/cpu/armv7/zynq/u-boot.lds +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2004-2008 Texas Instruments - * - * (C) Copyright 2002 - * Gary Jennejohn, DENX Software Engineering, - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") -OUTPUT_ARCH(arm) -ENTRY(_start) -SECTIONS -{ - . = 0x00000000; - - . = ALIGN(4); - .text : - { - *(.__image_copy_start) - *(.vectors) - CPUDIR/start.o (.text*) - *(.text*) - } - - . = ALIGN(4); - .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } - - . = ALIGN(4); - .data : { - *(.data*) - } - - . = ALIGN(4); - - . = .; - - . = ALIGN(4); - .u_boot_list : { - KEEP(*(SORT(.u_boot_list*))); - } - - . = ALIGN(4); - - .image_copy_end : - { - *(.__image_copy_end) - } - - .rel_dyn_start : - { - *(.__rel_dyn_start) - } - - .rel.dyn : { - *(.rel*) - } - - .rel_dyn_end : - { - *(.__rel_dyn_end) - } - - .end : - { - *(.__end) - } - - _image_binary_end = .; - -/* - * Compiler-generated __bss_start and __bss_end, see arch/arm/lib/bss.c - * __bss_base and __bss_limit are for linker only (overlay ordering) - */ - - .bss_start __rel_dyn_start (OVERLAY) : { - KEEP(*(.__bss_start)); - __bss_base = .; - } - - .bss __bss_base (OVERLAY) : { - *(.bss*) - . = ALIGN(4); - __bss_limit = .; - } - - .bss_end __bss_limit (OVERLAY) : { - KEEP(*(.__bss_end)); - } - - /* - * Zynq needs to discard these sections because the user - * is expected to pass this image on to tools for boot.bin - * generation that require them to be dropped. - */ - /DISCARD/ : { *(.dynsym) } - /DISCARD/ : { *(.dynbss*) } - /DISCARD/ : { *(.dynstr*) } - /DISCARD/ : { *(.dynamic*) } - /DISCARD/ : { *(.plt*) } - /DISCARD/ : { *(.interp*) } - /DISCARD/ : { *(.gnu*) } - /DISCARD/ : { *(.ARM.exidx*) } - /DISCARD/ : { *(.gnu.linkonce.armexidx.*) } -} diff --git a/arch/arm/mach-zynq/Kconfig b/arch/arm/mach-zynq/Kconfig new file mode 100644 index 00000000000..26e570eb662 --- /dev/null +++ b/arch/arm/mach-zynq/Kconfig @@ -0,0 +1,43 @@ +if ZYNQ + +choice + prompt "Xilinx Zynq board select" + +config TARGET_ZYNQ_ZED + bool "Zynq ZedBoard" + +config TARGET_ZYNQ_MICROZED + bool "Zynq MicroZed" + +config TARGET_ZYNQ_PICOZED + bool "Zynq PicoZed" + +config TARGET_ZYNQ_ZC70X + bool "Zynq ZC702/ZC706 Board" + +config TARGET_ZYNQ_ZC770 + bool "Zynq ZC770 Board" + +config TARGET_ZYNQ_ZYBO + bool "Zynq Zybo Board" + +endchoice + +config SYS_BOARD + default "zynq" + +config SYS_VENDOR + default "xilinx" + +config SYS_SOC + default "zynq" + +config SYS_CONFIG_NAME + default "zynq_zed" if TARGET_ZYNQ_ZED + default "zynq_microzed" if TARGET_ZYNQ_MICROZED + default "zynq_picozed" if TARGET_ZYNQ_PICOZED + default "zynq_zc70x" if TARGET_ZYNQ_ZC70X + default "zynq_zc770" if TARGET_ZYNQ_ZC770 + default "zynq_zybo" if TARGET_ZYNQ_ZYBO + +endif diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile new file mode 100644 index 00000000000..bf29b4d396d --- /dev/null +++ b/arch/arm/mach-zynq/Makefile @@ -0,0 +1,18 @@ +# +# (C) Copyright 2000-2003 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2008 +# Guennadi Liakhovetki, DENX Software Engineering, +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y := timer.o +obj-y += cpu.o +obj-y += ddrc.o +obj-y += slcr.o +obj-y += clk.o +obj-y += lowlevel_init.o +AFLAGS_lowlevel_init.o := -mfpu=neon +obj-$(CONFIG_SPL_BUILD) += spl.o diff --git a/arch/arm/mach-zynq/clk.c b/arch/arm/mach-zynq/clk.c new file mode 100644 index 00000000000..d2885dc2b9e --- /dev/null +++ b/arch/arm/mach-zynq/clk.c @@ -0,0 +1,664 @@ +/* + * Copyright (C) 2013 Soren Brinkmann + * Copyright (C) 2013 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include +#include + +/* Board oscillator frequency */ +#ifndef CONFIG_ZYNQ_PS_CLK_FREQ +# define CONFIG_ZYNQ_PS_CLK_FREQ 33333333UL +#endif + +/* Register bitfield defines */ +#define PLLCTRL_FBDIV_MASK 0x7f000 +#define PLLCTRL_FBDIV_SHIFT 12 +#define PLLCTRL_BPFORCE_MASK (1 << 4) +#define PLLCTRL_PWRDWN_MASK 2 +#define PLLCTRL_PWRDWN_SHIFT 1 +#define PLLCTRL_RESET_MASK 1 +#define PLLCTRL_RESET_SHIFT 0 + +#define ZYNQ_CLK_MAXDIV 0x3f +#define CLK_CTRL_DIV1_SHIFT 20 +#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 4 +#define CLK_CTRL_SRCSEL_MASK (0x3 << CLK_CTRL_SRCSEL_SHIFT) + +#define CLK_CTRL_DIV2X_SHIFT 26 +#define CLK_CTRL_DIV2X_MASK (ZYNQ_CLK_MAXDIV << CLK_CTRL_DIV2X_SHIFT) +#define CLK_CTRL_DIV3X_SHIFT 20 +#define CLK_CTRL_DIV3X_MASK (ZYNQ_CLK_MAXDIV << CLK_CTRL_DIV3X_SHIFT) + +#define ZYNQ_CLKMUX_SEL_0 0 +#define ZYNQ_CLKMUX_SEL_1 1 +#define ZYNQ_CLKMUX_SEL_2 2 +#define ZYNQ_CLKMUX_SEL_3 3 + +DECLARE_GLOBAL_DATA_PTR; + +struct clk; + +/** + * struct clk_ops: + * @set_rate: Function pointer to set_rate() implementation + * @get_rate: Function pointer to get_rate() implementation + */ +struct clk_ops { + int (*set_rate)(struct clk *clk, unsigned long rate); + unsigned long (*get_rate)(struct clk *clk); +}; + +/** + * struct clk: + * @name: Clock name + * @frequency: Currenct frequency + * @parent: Parent clock + * @flags: Clock flags + * @reg: Clock control register + * @ops: Clock operations + */ +struct clk { + char *name; + unsigned long frequency; + enum zynq_clk parent; + unsigned int flags; + u32 *reg; + struct clk_ops ops; +}; +#define ZYNQ_CLK_FLAGS_HAS_2_DIVS 1 + +static struct clk clks[clk_max]; + +/** + * __zynq_clk_cpu_get_parent() - Decode clock multiplexer + * @srcsel: Mux select value + * Returns the clock identifier associated with the selected mux input. + */ +static int __zynq_clk_cpu_get_parent(unsigned int srcsel) +{ + unsigned int ret; + + switch (srcsel) { + case ZYNQ_CLKMUX_SEL_0: + case ZYNQ_CLKMUX_SEL_1: + ret = armpll_clk; + break; + case ZYNQ_CLKMUX_SEL_2: + ret = ddrpll_clk; + break; + case ZYNQ_CLKMUX_SEL_3: + ret = iopll_clk; + break; + default: + ret = armpll_clk; + break; + } + + return ret; +} + +/** + * ddr2x_get_rate() - Get clock rate of DDR2x clock + * @clk: Clock handle + * Returns the current clock rate of @clk. + */ +static unsigned long ddr2x_get_rate(struct clk *clk) +{ + u32 clk_ctrl = readl(clk->reg); + u32 div = (clk_ctrl & CLK_CTRL_DIV2X_MASK) >> CLK_CTRL_DIV2X_SHIFT; + + return DIV_ROUND_CLOSEST(zynq_clk_get_rate(clk->parent), div); +} + +/** + * ddr3x_get_rate() - Get clock rate of DDR3x clock + * @clk: Clock handle + * Returns the current clock rate of @clk. + */ +static unsigned long ddr3x_get_rate(struct clk *clk) +{ + u32 clk_ctrl = readl(clk->reg); + u32 div = (clk_ctrl & CLK_CTRL_DIV3X_MASK) >> CLK_CTRL_DIV3X_SHIFT; + + return DIV_ROUND_CLOSEST(zynq_clk_get_rate(clk->parent), div); +} + +static void init_ddr_clocks(void) +{ + u32 div0, div1; + unsigned long prate = zynq_clk_get_rate(ddrpll_clk); + u32 clk_ctrl = readl(&slcr_base->ddr_clk_ctrl); + + /* DDR2x */ + clks[ddr2x_clk].reg = &slcr_base->ddr_clk_ctrl; + clks[ddr2x_clk].parent = ddrpll_clk; + clks[ddr2x_clk].name = "ddr_2x"; + clks[ddr2x_clk].frequency = ddr2x_get_rate(&clks[ddr2x_clk]); + clks[ddr2x_clk].ops.get_rate = ddr2x_get_rate; + + /* DDR3x */ + clks[ddr3x_clk].reg = &slcr_base->ddr_clk_ctrl; + clks[ddr3x_clk].parent = ddrpll_clk; + clks[ddr3x_clk].name = "ddr_3x"; + clks[ddr3x_clk].frequency = ddr3x_get_rate(&clks[ddr3x_clk]); + clks[ddr3x_clk].ops.get_rate = ddr3x_get_rate; + + /* DCI */ + clk_ctrl = readl(&slcr_base->dci_clk_ctrl); + div0 = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; + div1 = (clk_ctrl & CLK_CTRL_DIV1_MASK) >> CLK_CTRL_DIV1_SHIFT; + clks[dci_clk].reg = &slcr_base->dci_clk_ctrl; + clks[dci_clk].parent = ddrpll_clk; + clks[dci_clk].frequency = DIV_ROUND_CLOSEST( + DIV_ROUND_CLOSEST(prate, div0), div1); + clks[dci_clk].name = "dci"; + + gd->bd->bi_ddr_freq = clks[ddr3x_clk].frequency / 1000000; +} + +static void init_cpu_clocks(void) +{ + int clk_621; + u32 reg, div, srcsel; + enum zynq_clk parent; + + reg = readl(&slcr_base->arm_clk_ctrl); + clk_621 = readl(&slcr_base->clk_621_true) & 1; + div = (reg & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; + srcsel = (reg & CLK_CTRL_SRCSEL_MASK) >> CLK_CTRL_SRCSEL_SHIFT; + parent = __zynq_clk_cpu_get_parent(srcsel); + + /* cpu clocks */ + clks[cpu_6or4x_clk].reg = &slcr_base->arm_clk_ctrl; + clks[cpu_6or4x_clk].parent = parent; + clks[cpu_6or4x_clk].frequency = DIV_ROUND_CLOSEST( + zynq_clk_get_rate(parent), div); + clks[cpu_6or4x_clk].name = "cpu_6or4x"; + + clks[cpu_3or2x_clk].reg = &slcr_base->arm_clk_ctrl; + clks[cpu_3or2x_clk].parent = cpu_6or4x_clk; + clks[cpu_3or2x_clk].frequency = zynq_clk_get_rate(cpu_6or4x_clk) / 2; + clks[cpu_3or2x_clk].name = "cpu_3or2x"; + + clks[cpu_2x_clk].reg = &slcr_base->arm_clk_ctrl; + clks[cpu_2x_clk].parent = cpu_6or4x_clk; + clks[cpu_2x_clk].frequency = zynq_clk_get_rate(cpu_6or4x_clk) / + (2 + clk_621); + clks[cpu_2x_clk].name = "cpu_2x"; + + clks[cpu_1x_clk].reg = &slcr_base->arm_clk_ctrl; + clks[cpu_1x_clk].parent = cpu_6or4x_clk; + clks[cpu_1x_clk].frequency = zynq_clk_get_rate(cpu_6or4x_clk) / + (4 + 2 * clk_621); + clks[cpu_1x_clk].name = "cpu_1x"; +} + +/** + * periph_calc_two_divs() - Calculate clock dividers + * @cur_rate: Current clock rate + * @tgt_rate: Target clock rate + * @prate: Parent clock rate + * @div0: First divider (output) + * @div1: Second divider (output) + * Returns the actual clock rate possible. + * + * Calculates clock dividers for clocks with two 6-bit dividers. + */ +static unsigned long periph_calc_two_divs(unsigned long cur_rate, + unsigned long tgt_rate, unsigned long prate, u32 *div0, + u32 *div1) +{ + long err, best_err = (long)(~0UL >> 1); + unsigned long rate, best_rate = 0; + u32 d0, d1; + + for (d0 = 1; d0 <= ZYNQ_CLK_MAXDIV; d0++) { + for (d1 = 1; d1 <= ZYNQ_CLK_MAXDIV >> 1; d1++) { + rate = DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(prate, d0), + d1); + err = abs(rate - tgt_rate); + + if (err < best_err) { + *div0 = d0; + *div1 = d1; + best_err = err; + best_rate = rate; + } + } + } + + return best_rate; +} + +/** + * zynq_clk_periph_set_rate() - Set clock rate + * @clk: Handle of the peripheral clock + * @rate: New clock rate + * Sets the clock frequency of @clk to @rate. Returns zero on success. + */ +static int zynq_clk_periph_set_rate(struct clk *clk, + unsigned long rate) +{ + u32 ctrl, div0 = 0, div1 = 0; + unsigned long prate, new_rate, cur_rate = clk->frequency; + + ctrl = readl(clk->reg); + prate = zynq_clk_get_rate(clk->parent); + ctrl &= ~CLK_CTRL_DIV0_MASK; + + if (clk->flags & ZYNQ_CLK_FLAGS_HAS_2_DIVS) { + ctrl &= ~CLK_CTRL_DIV1_MASK; + new_rate = periph_calc_two_divs(cur_rate, rate, prate, &div0, + &div1); + ctrl |= div1 << CLK_CTRL_DIV1_SHIFT; + } else { + div0 = DIV_ROUND_CLOSEST(prate, rate); + div0 &= ZYNQ_CLK_MAXDIV; + new_rate = DIV_ROUND_CLOSEST(rate, div0); + } + + /* write new divs to hardware */ + ctrl |= div0 << CLK_CTRL_DIV0_SHIFT; + writel(ctrl, clk->reg); + + /* update frequency in clk framework */ + clk->frequency = new_rate; + + return 0; +} + +/** + * zynq_clk_periph_get_rate() - Get clock rate + * @clk: Handle of the peripheral clock + * Returns the current clock rate of @clk. + */ +static unsigned long zynq_clk_periph_get_rate(struct clk *clk) +{ + u32 clk_ctrl = readl(clk->reg); + u32 div0 = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; + u32 div1 = 1; + + if (clk->flags & ZYNQ_CLK_FLAGS_HAS_2_DIVS) + div1 = (clk_ctrl & CLK_CTRL_DIV1_MASK) >> CLK_CTRL_DIV1_SHIFT; + + /* a register value of zero == division by 1 */ + if (!div0) + div0 = 1; + if (!div1) + div1 = 1; + + return + DIV_ROUND_CLOSEST( + DIV_ROUND_CLOSEST(zynq_clk_get_rate(clk->parent), div0), + div1); +} + +/** + * __zynq_clk_periph_get_parent() - Decode clock multiplexer + * @srcsel: Mux select value + * Returns the clock identifier associated with the selected mux input. + */ +static enum zynq_clk __zynq_clk_periph_get_parent(u32 srcsel) +{ + switch (srcsel) { + case ZYNQ_CLKMUX_SEL_0: + case ZYNQ_CLKMUX_SEL_1: + return iopll_clk; + case ZYNQ_CLKMUX_SEL_2: + return armpll_clk; + case ZYNQ_CLKMUX_SEL_3: + return ddrpll_clk; + default: + return 0; + } +} + +/** + * zynq_clk_periph_get_parent() - Decode clock multiplexer + * @clk: Clock handle + * Returns the clock identifier associated with the selected mux input. + */ +static enum zynq_clk zynq_clk_periph_get_parent(struct clk *clk) +{ + u32 clk_ctrl = readl(clk->reg); + u32 srcsel = (clk_ctrl & CLK_CTRL_SRCSEL_MASK) >> CLK_CTRL_SRCSEL_SHIFT; + + return __zynq_clk_periph_get_parent(srcsel); +} + +/** + * zynq_clk_register_periph_clk() - Set up a peripheral clock with the framework + * @clk: Pointer to struct clk for the clock + * @ctrl: Clock control register + * @name: PLL name + * @two_divs: Indicates whether the clock features one or two dividers + */ +static int zynq_clk_register_periph_clk(struct clk *clk, u32 *ctrl, char *name, + bool two_divs) +{ + clk->name = name; + clk->reg = ctrl; + if (two_divs) + clk->flags = ZYNQ_CLK_FLAGS_HAS_2_DIVS; + clk->parent = zynq_clk_periph_get_parent(clk); + clk->frequency = zynq_clk_periph_get_rate(clk); + clk->ops.get_rate = zynq_clk_periph_get_rate; + clk->ops.set_rate = zynq_clk_periph_set_rate; + + return 0; +} + +static void init_periph_clocks(void) +{ + zynq_clk_register_periph_clk(&clks[gem0_clk], &slcr_base->gem0_clk_ctrl, + "gem0", 1); + zynq_clk_register_periph_clk(&clks[gem1_clk], &slcr_base->gem1_clk_ctrl, + "gem1", 1); + + zynq_clk_register_periph_clk(&clks[smc_clk], &slcr_base->smc_clk_ctrl, + "smc", 0); + + zynq_clk_register_periph_clk(&clks[lqspi_clk], + &slcr_base->lqspi_clk_ctrl, "lqspi", 0); + + zynq_clk_register_periph_clk(&clks[sdio0_clk], + &slcr_base->sdio_clk_ctrl, "sdio0", 0); + zynq_clk_register_periph_clk(&clks[sdio1_clk], + &slcr_base->sdio_clk_ctrl, "sdio1", 0); + + zynq_clk_register_periph_clk(&clks[spi0_clk], &slcr_base->spi_clk_ctrl, + "spi0", 0); + zynq_clk_register_periph_clk(&clks[spi1_clk], &slcr_base->spi_clk_ctrl, + "spi1", 0); + + zynq_clk_register_periph_clk(&clks[uart0_clk], + &slcr_base->uart_clk_ctrl, "uart0", 0); + zynq_clk_register_periph_clk(&clks[uart1_clk], + &slcr_base->uart_clk_ctrl, "uart1", 0); + + zynq_clk_register_periph_clk(&clks[dbg_trc_clk], + &slcr_base->dbg_clk_ctrl, "dbg_trc", 0); + zynq_clk_register_periph_clk(&clks[dbg_apb_clk], + &slcr_base->dbg_clk_ctrl, "dbg_apb", 0); + + zynq_clk_register_periph_clk(&clks[pcap_clk], + &slcr_base->pcap_clk_ctrl, "pcap", 0); + + zynq_clk_register_periph_clk(&clks[fclk0_clk], + &slcr_base->fpga0_clk_ctrl, "fclk0", 1); + zynq_clk_register_periph_clk(&clks[fclk1_clk], + &slcr_base->fpga1_clk_ctrl, "fclk1", 1); + zynq_clk_register_periph_clk(&clks[fclk2_clk], + &slcr_base->fpga2_clk_ctrl, "fclk2", 1); + zynq_clk_register_periph_clk(&clks[fclk3_clk], + &slcr_base->fpga3_clk_ctrl, "fclk3", 1); +} + +/** + * zynq_clk_register_aper_clk() - Set up a APER clock with the framework + * @clk: Pointer to struct clk for the clock + * @ctrl: Clock control register + * @name: PLL name + */ +static void zynq_clk_register_aper_clk(struct clk *clk, u32 *ctrl, char *name) +{ + clk->name = name; + clk->reg = ctrl; + clk->parent = cpu_1x_clk; + clk->frequency = zynq_clk_get_rate(clk->parent); +} + +static void init_aper_clocks(void) +{ + zynq_clk_register_aper_clk(&clks[usb0_aper_clk], + &slcr_base->aper_clk_ctrl, "usb0_aper"); + zynq_clk_register_aper_clk(&clks[usb1_aper_clk], + &slcr_base->aper_clk_ctrl, "usb1_aper"); + + zynq_clk_register_aper_clk(&clks[gem0_aper_clk], + &slcr_base->aper_clk_ctrl, "gem0_aper"); + zynq_clk_register_aper_clk(&clks[gem1_aper_clk], + &slcr_base->aper_clk_ctrl, "gem1_aper"); + + zynq_clk_register_aper_clk(&clks[sdio0_aper_clk], + &slcr_base->aper_clk_ctrl, "sdio0_aper"); + zynq_clk_register_aper_clk(&clks[sdio1_aper_clk], + &slcr_base->aper_clk_ctrl, "sdio1_aper"); + + zynq_clk_register_aper_clk(&clks[spi0_aper_clk], + &slcr_base->aper_clk_ctrl, "spi0_aper"); + zynq_clk_register_aper_clk(&clks[spi1_aper_clk], + &slcr_base->aper_clk_ctrl, "spi1_aper"); + + zynq_clk_register_aper_clk(&clks[can0_aper_clk], + &slcr_base->aper_clk_ctrl, "can0_aper"); + zynq_clk_register_aper_clk(&clks[can1_aper_clk], + &slcr_base->aper_clk_ctrl, "can1_aper"); + + zynq_clk_register_aper_clk(&clks[i2c0_aper_clk], + &slcr_base->aper_clk_ctrl, "i2c0_aper"); + zynq_clk_register_aper_clk(&clks[i2c1_aper_clk], + &slcr_base->aper_clk_ctrl, "i2c1_aper"); + + zynq_clk_register_aper_clk(&clks[uart0_aper_clk], + &slcr_base->aper_clk_ctrl, "uart0_aper"); + zynq_clk_register_aper_clk(&clks[uart1_aper_clk], + &slcr_base->aper_clk_ctrl, "uart1_aper"); + + zynq_clk_register_aper_clk(&clks[gpio_aper_clk], + &slcr_base->aper_clk_ctrl, "gpio_aper"); + + zynq_clk_register_aper_clk(&clks[lqspi_aper_clk], + &slcr_base->aper_clk_ctrl, "lqspi_aper"); + + zynq_clk_register_aper_clk(&clks[smc_aper_clk], + &slcr_base->aper_clk_ctrl, "smc_aper"); +} + +/** + * __zynq_clk_pll_get_rate() - Get PLL rate + * @addr: Address of the PLL's control register + * Returns the current PLL output rate. + */ +static unsigned long __zynq_clk_pll_get_rate(u32 *addr) +{ + u32 reg, mul, bypass; + + reg = readl(addr); + bypass = reg & PLLCTRL_BPFORCE_MASK; + if (bypass) + mul = 1; + else + mul = (reg & PLLCTRL_FBDIV_MASK) >> PLLCTRL_FBDIV_SHIFT; + + return CONFIG_ZYNQ_PS_CLK_FREQ * mul; +} + +/** + * zynq_clk_pll_get_rate() - Get PLL rate + * @pll: Handle of the PLL + * Returns the current clock rate of @pll. + */ +static unsigned long zynq_clk_pll_get_rate(struct clk *pll) +{ + return __zynq_clk_pll_get_rate(pll->reg); +} + +/** + * zynq_clk_register_pll() - Set up a PLL with the framework + * @clk: Pointer to struct clk for the PLL + * @ctrl: PLL control register + * @name: PLL name + * @prate: PLL input clock rate + */ +static void zynq_clk_register_pll(struct clk *clk, u32 *ctrl, char *name, + unsigned long prate) +{ + clk->name = name; + clk->reg = ctrl; + clk->frequency = zynq_clk_pll_get_rate(clk); + clk->ops.get_rate = zynq_clk_pll_get_rate; +} + +/** + * clkid_2_register() - Get clock control register + * @id: Clock identifier of one of the PLLs + * Returns the address of the requested PLL's control register. + */ +static u32 *clkid_2_register(enum zynq_clk id) +{ + switch (id) { + case armpll_clk: + return &slcr_base->arm_pll_ctrl; + case ddrpll_clk: + return &slcr_base->ddr_pll_ctrl; + case iopll_clk: + return &slcr_base->io_pll_ctrl; + default: + return &slcr_base->io_pll_ctrl; + } +} + +/* API */ +/** + * zynq_clk_early_init() - Early init for the clock framework + * + * This function is called from before relocation and sets up the CPU clock + * frequency in the global data struct. + */ +void zynq_clk_early_init(void) +{ + u32 reg = readl(&slcr_base->arm_clk_ctrl); + u32 div = (reg & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; + u32 srcsel = (reg & CLK_CTRL_SRCSEL_MASK) >> CLK_CTRL_SRCSEL_SHIFT; + enum zynq_clk parent = __zynq_clk_cpu_get_parent(srcsel); + u32 *pllreg = clkid_2_register(parent); + unsigned long prate = __zynq_clk_pll_get_rate(pllreg); + + if (!div) + div = 1; + + gd->cpu_clk = DIV_ROUND_CLOSEST(prate, div); +} + +/** + * get_uart_clk() - Get UART input frequency + * @dev_index: UART ID + * Returns UART input clock frequency in Hz. + * + * Compared to zynq_clk_get_rate() this function is designed to work before + * relocation and can be called when the serial UART is set up. + */ +unsigned long get_uart_clk(int dev_index) +{ + u32 reg = readl(&slcr_base->uart_clk_ctrl); + u32 div = (reg & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; + u32 srcsel = (reg & CLK_CTRL_SRCSEL_MASK) >> CLK_CTRL_SRCSEL_SHIFT; + enum zynq_clk parent = __zynq_clk_periph_get_parent(srcsel); + u32 *pllreg = clkid_2_register(parent); + unsigned long prate = __zynq_clk_pll_get_rate(pllreg); + + if (!div) + div = 1; + + return DIV_ROUND_CLOSEST(prate, div); +} + +/** + * set_cpu_clk_info() - Initialize clock framework + * Always returns zero. + * + * This function is called from common code after relocation and sets up the + * clock framework. The framework must not be used before this function had been + * called. + */ +int set_cpu_clk_info(void) +{ + zynq_clk_register_pll(&clks[armpll_clk], &slcr_base->arm_pll_ctrl, + "armpll", CONFIG_ZYNQ_PS_CLK_FREQ); + zynq_clk_register_pll(&clks[ddrpll_clk], &slcr_base->ddr_pll_ctrl, + "ddrpll", CONFIG_ZYNQ_PS_CLK_FREQ); + zynq_clk_register_pll(&clks[iopll_clk], &slcr_base->io_pll_ctrl, + "iopll", CONFIG_ZYNQ_PS_CLK_FREQ); + + init_ddr_clocks(); + init_cpu_clocks(); + init_periph_clocks(); + init_aper_clocks(); + + gd->bd->bi_arm_freq = gd->cpu_clk / 1000000; + gd->bd->bi_dsp_freq = 0; + + return 0; +} + +/** + * zynq_clk_get_rate() - Get clock rate + * @clk: Clock identifier + * Returns the current clock rate of @clk on success or zero for an invalid + * clock id. + */ +unsigned long zynq_clk_get_rate(enum zynq_clk clk) +{ + if (clk < 0 || clk >= clk_max) + return 0; + + return clks[clk].frequency; +} + +/** + * zynq_clk_set_rate() - Set clock rate + * @clk: Clock identifier + * @rate: Requested clock rate + * Passes on the return value from the clock's set_rate() function or negative + * errno. + */ +int zynq_clk_set_rate(enum zynq_clk clk, unsigned long rate) +{ + if (clk < 0 || clk >= clk_max) + return -ENODEV; + + if (clks[clk].ops.set_rate) + return clks[clk].ops.set_rate(&clks[clk], rate); + + return -ENXIO; +} + +/** + * zynq_clk_get_name() - Get clock name + * @clk: Clock identifier + * Returns the name of @clk. + */ +const char *zynq_clk_get_name(enum zynq_clk clk) +{ + return clks[clk].name; +} + +/** + * soc_clk_dump() - Print clock frequencies + * Returns zero on success + * + * Implementation for the clk dump command. + */ +int soc_clk_dump(void) +{ + int i; + + printf("clk\t\tfrequency\n"); + for (i = 0; i < clk_max; i++) { + const char *name = zynq_clk_get_name(i); + if (name) + printf("%10s%20lu\n", name, zynq_clk_get_rate(i)); + } + + return 0; +} diff --git a/arch/arm/mach-zynq/cpu.c b/arch/arm/mach-zynq/cpu.c new file mode 100644 index 00000000000..914b1feb683 --- /dev/null +++ b/arch/arm/mach-zynq/cpu.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2012 Michal Simek + * Copyright (C) 2012 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include +#include +#include +#include + +#define ZYNQ_SILICON_VER_MASK 0xF0000000 +#define ZYNQ_SILICON_VER_SHIFT 28 + +int arch_cpu_init(void) +{ + zynq_slcr_unlock(); +#ifndef CONFIG_SPL_BUILD + /* Device config APB, unlock the PCAP */ + writel(0x757BDF0D, &devcfg_base->unlock); + writel(0xFFFFFFFF, &devcfg_base->rom_shadow); + +#if (CONFIG_SYS_SDRAM_BASE == 0) + /* remap DDR to zero, FILTERSTART */ + writel(0, &scu_base->filter_start); + + /* OCM_CFG, Mask out the ROM, map ram into upper addresses */ + writel(0x1F, &slcr_base->ocm_cfg); + /* FPGA_RST_CTRL, clear resets on AXI fabric ports */ + writel(0x0, &slcr_base->fpga_rst_ctrl); + /* Set urgent bits with register */ + writel(0x0, &slcr_base->ddr_urgent_sel); + /* Urgent write, ports S2/S3 */ + writel(0xC, &slcr_base->ddr_urgent); +#endif +#endif + zynq_clk_early_init(); + zynq_slcr_lock(); + + return 0; +} + +unsigned int zynq_get_silicon_version(void) +{ + unsigned int ver; + + ver = (readl(&devcfg_base->mctrl) & + ZYNQ_SILICON_VER_MASK) >> ZYNQ_SILICON_VER_SHIFT; + + return ver; +} + +void reset_cpu(ulong addr) +{ + zynq_slcr_cpu_reset(); + while (1) + ; +} + +#ifndef CONFIG_SYS_DCACHE_OFF +void enable_caches(void) +{ + /* Enable D-cache. I-cache is already enabled in start.S */ + dcache_enable(); +} +#endif diff --git a/arch/arm/mach-zynq/ddrc.c b/arch/arm/mach-zynq/ddrc.c new file mode 100644 index 00000000000..5b20accbcb1 --- /dev/null +++ b/arch/arm/mach-zynq/ddrc.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 - 2013 Michal Simek + * Copyright (C) 2012 - 2013 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* Control regsiter bitfield definitions */ +#define ZYNQ_DDRC_CTRLREG_BUSWIDTH_MASK 0xC +#define ZYNQ_DDRC_CTRLREG_BUSWIDTH_SHIFT 2 +#define ZYNQ_DDRC_CTRLREG_BUSWIDTH_16BIT 1 + +/* ECC scrub regsiter definitions */ +#define ZYNQ_DDRC_ECC_SCRUBREG_ECC_MODE_MASK 0x7 +#define ZYNQ_DDRC_ECC_SCRUBREG_ECCMODE_SECDED 0x4 + +void zynq_ddrc_init(void) +{ + u32 width, ecctype; + + width = readl(&ddrc_base->ddrc_ctrl); + width = (width & ZYNQ_DDRC_CTRLREG_BUSWIDTH_MASK) >> + ZYNQ_DDRC_CTRLREG_BUSWIDTH_SHIFT; + ecctype = (readl(&ddrc_base->ecc_scrub) & + ZYNQ_DDRC_ECC_SCRUBREG_ECC_MODE_MASK); + + /* ECC is enabled when memory is in 16bit mode and it is enabled */ + if ((ecctype == ZYNQ_DDRC_ECC_SCRUBREG_ECCMODE_SECDED) && + (width == ZYNQ_DDRC_CTRLREG_BUSWIDTH_16BIT)) { + puts("ECC enabled "); + /* + * Clear the first 1MB because it is not initialized from + * first stage bootloader. To get ECC to work all memory has + * been initialized by writing any value. + */ + /* cppcheck-suppress nullPointer */ + memset((void *)0, 0, 1 * 1024 * 1024); + + gd->ram_size /= 2; + } else { + puts("ECC disabled "); + } +} diff --git a/arch/arm/mach-zynq/lowlevel_init.S b/arch/arm/mach-zynq/lowlevel_init.S new file mode 100644 index 00000000000..6d714b711cb --- /dev/null +++ b/arch/arm/mach-zynq/lowlevel_init.S @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2013 Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +ENTRY(lowlevel_init) + + /* Enable the the VFP */ + mrc p15, 0, r1, c1, c0, 2 + orr r1, r1, #(0x3 << 20) + orr r1, r1, #(0x3 << 20) + mcr p15, 0, r1, c1, c0, 2 + isb + fmrx r1, FPEXC + orr r1,r1, #(1<<30) + fmxr FPEXC, r1 + + /* Move back to caller */ + mov pc, lr + +ENDPROC(lowlevel_init) diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c new file mode 100644 index 00000000000..05f4099aaee --- /dev/null +++ b/arch/arm/mach-zynq/slcr.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2013 Xilinx Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#define SLCR_LOCK_MAGIC 0x767B +#define SLCR_UNLOCK_MAGIC 0xDF0D + +#define SLCR_USB_L1_SEL 0x04 + +#define SLCR_IDCODE_MASK 0x1F000 +#define SLCR_IDCODE_SHIFT 12 + +/* + * zynq_slcr_mio_get_status - Get the status of MIO peripheral. + * + * @peri_name: Name of the peripheral for checking MIO status + * @get_pins: Pointer to array of get pin for this peripheral + * @num_pins: Number of pins for this peripheral + * @mask: Mask value + * @check_val: Required check value to get the status of periph + */ +struct zynq_slcr_mio_get_status { + const char *peri_name; + const int *get_pins; + int num_pins; + u32 mask; + u32 check_val; +}; + +static const int usb0_pins[] = { + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39 +}; + +static const int usb1_pins[] = { + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 +}; + +static const struct zynq_slcr_mio_get_status mio_periphs[] = { + { + "usb0", + usb0_pins, + ARRAY_SIZE(usb0_pins), + SLCR_USB_L1_SEL, + SLCR_USB_L1_SEL, + }, + { + "usb1", + usb1_pins, + ARRAY_SIZE(usb1_pins), + SLCR_USB_L1_SEL, + SLCR_USB_L1_SEL, + }, +}; + +static int slcr_lock = 1; /* 1 means locked, 0 means unlocked */ + +void zynq_slcr_lock(void) +{ + if (!slcr_lock) { + writel(SLCR_LOCK_MAGIC, &slcr_base->slcr_lock); + slcr_lock = 1; + } +} + +void zynq_slcr_unlock(void) +{ + if (slcr_lock) { + writel(SLCR_UNLOCK_MAGIC, &slcr_base->slcr_unlock); + slcr_lock = 0; + } +} + +/* Reset the entire system */ +void zynq_slcr_cpu_reset(void) +{ + /* + * Unlock the SLCR then reset the system. + * Note that this seems to require raw i/o + * functions or there's a lockup? + */ + zynq_slcr_unlock(); + + /* + * Clear 0x0F000000 bits of reboot status register to workaround + * the FSBL not loading the bitstream after soft-reboot + * This is a temporary solution until we know more. + */ + clrbits_le32(&slcr_base->reboot_status, 0xF000000); + + writel(1, &slcr_base->pss_rst_ctrl); +} + +/* Setup clk for network */ +void zynq_slcr_gem_clk_setup(u32 gem_id, unsigned long clk_rate) +{ + int ret; + + zynq_slcr_unlock(); + + if (gem_id > 1) { + printf("Non existing GEM id %d\n", gem_id); + goto out; + } + + ret = zynq_clk_set_rate(gem0_clk + gem_id, clk_rate); + if (ret) + goto out; + + if (gem_id) { + /* Configure GEM_RCLK_CTRL */ + writel(1, &slcr_base->gem1_rclk_ctrl); + } else { + /* Configure GEM_RCLK_CTRL */ + writel(1, &slcr_base->gem0_rclk_ctrl); + } + udelay(100000); +out: + zynq_slcr_lock(); +} + +void zynq_slcr_devcfg_disable(void) +{ + u32 reg_val; + + zynq_slcr_unlock(); + + /* Disable AXI interface by asserting FPGA resets */ + writel(0xF, &slcr_base->fpga_rst_ctrl); + + /* Disable Level shifters before setting PS-PL */ + reg_val = readl(&slcr_base->lvl_shftr_en); + reg_val &= ~0xF; + writel(reg_val, &slcr_base->lvl_shftr_en); + + /* Set Level Shifters DT618760 */ + writel(0xA, &slcr_base->lvl_shftr_en); + + zynq_slcr_lock(); +} + +void zynq_slcr_devcfg_enable(void) +{ + zynq_slcr_unlock(); + + /* Set Level Shifters DT618760 */ + writel(0xF, &slcr_base->lvl_shftr_en); + + /* Enable AXI interface by de-asserting FPGA resets */ + writel(0x0, &slcr_base->fpga_rst_ctrl); + + zynq_slcr_lock(); +} + +u32 zynq_slcr_get_boot_mode(void) +{ + /* Get the bootmode register value */ + return readl(&slcr_base->boot_mode); +} + +u32 zynq_slcr_get_idcode(void) +{ + return (readl(&slcr_base->pss_idcode) & SLCR_IDCODE_MASK) >> + SLCR_IDCODE_SHIFT; +} + +/* + * zynq_slcr_get_mio_pin_status - Get the MIO pin status of peripheral. + * + * @periph: Name of the peripheral + * + * Returns count to indicate the number of pins configured for the + * given @periph. + */ +int zynq_slcr_get_mio_pin_status(const char *periph) +{ + const struct zynq_slcr_mio_get_status *mio_ptr; + int val, i, j; + int mio = 0; + + for (i = 0; i < ARRAY_SIZE(mio_periphs); i++) { + if (strcmp(periph, mio_periphs[i].peri_name) == 0) { + mio_ptr = &mio_periphs[i]; + for (j = 0; j < mio_ptr->num_pins; j++) { + val = readl(&slcr_base->mio_pin + [mio_ptr->get_pins[j]]); + if ((val & mio_ptr->mask) == mio_ptr->check_val) + mio++; + } + break; + } + } + + return mio; +} diff --git a/arch/arm/mach-zynq/spl.c b/arch/arm/mach-zynq/spl.c new file mode 100644 index 00000000000..13025f034bf --- /dev/null +++ b/arch/arm/mach-zynq/spl.c @@ -0,0 +1,90 @@ +/* + * (C) Copyright 2014 Xilinx, Inc. Michal Simek + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include +#include + +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +void board_init_f(ulong dummy) +{ + ps7_init(); + + /* Clear the BSS. */ + memset(__bss_start, 0, __bss_end - __bss_start); + + preloader_console_init(); + arch_cpu_init(); + board_init_r(NULL, 0); +} + +#ifdef CONFIG_SPL_BOARD_INIT +void spl_board_init(void) +{ + board_init(); +} +#endif + +u32 spl_boot_device(void) +{ + u32 mode; + + switch ((zynq_slcr_get_boot_mode()) & ZYNQ_BM_MASK) { +#ifdef CONFIG_SPL_SPI_SUPPORT + case ZYNQ_BM_QSPI: + puts("qspi boot\n"); + mode = BOOT_DEVICE_SPI; + break; +#endif + case ZYNQ_BM_NAND: + mode = BOOT_DEVICE_NAND; + break; + case ZYNQ_BM_NOR: + mode = BOOT_DEVICE_NOR; + break; +#ifdef CONFIG_SPL_MMC_SUPPORT + case ZYNQ_BM_SD: + puts("mmc boot\n"); + mode = BOOT_DEVICE_MMC1; + break; +#endif + case ZYNQ_BM_JTAG: + mode = BOOT_DEVICE_RAM; + break; + default: + puts("Unsupported boot mode selected\n"); + hang(); + } + + return mode; +} + +#ifdef CONFIG_SPL_MMC_SUPPORT +u32 spl_boot_mode(void) +{ + return MMCSD_MODE_FS; +} +#endif + +#ifdef CONFIG_SPL_OS_BOOT +int spl_start_uboot(void) +{ + /* boot linux */ + return 0; +} +#endif + +__weak void ps7_init(void) +{ + /* + * This function is overridden by the one in + * board/xilinx/zynq/ps7_init_gpl.c, if it exists. + */ +} diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c new file mode 100644 index 00000000000..5ed9642df9b --- /dev/null +++ b/arch/arm/mach-zynq/timer.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2012 Michal Simek + * Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved. + * + * (C) Copyright 2008 + * Guennadi Liakhovetki, DENX Software Engineering, + * + * (C) Copyright 2004 + * Philippe Robin, ARM Ltd. + * + * (C) Copyright 2002-2004 + * Gary Jennejohn, DENX Software Engineering, + * + * (C) Copyright 2003 + * Texas Instruments + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Marius Groeger + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Alex Zuepke + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct scu_timer { + u32 load; /* Timer Load Register */ + u32 counter; /* Timer Counter Register */ + u32 control; /* Timer Control Register */ +}; + +static struct scu_timer *timer_base = + (struct scu_timer *)ZYNQ_SCUTIMER_BASEADDR; + +#define SCUTIMER_CONTROL_PRESCALER_MASK 0x0000FF00 /* Prescaler */ +#define SCUTIMER_CONTROL_PRESCALER_SHIFT 8 +#define SCUTIMER_CONTROL_AUTO_RELOAD_MASK 0x00000002 /* Auto-reload */ +#define SCUTIMER_CONTROL_ENABLE_MASK 0x00000001 /* Timer enable */ + +#define TIMER_LOAD_VAL 0xFFFFFFFF +#define TIMER_PRESCALE 255 + +int timer_init(void) +{ + const u32 emask = SCUTIMER_CONTROL_AUTO_RELOAD_MASK | + (TIMER_PRESCALE << SCUTIMER_CONTROL_PRESCALER_SHIFT) | + SCUTIMER_CONTROL_ENABLE_MASK; + + gd->arch.timer_rate_hz = (gd->cpu_clk / 2) / (TIMER_PRESCALE + 1); + + /* Load the timer counter register */ + writel(0xFFFFFFFF, &timer_base->load); + + /* + * Start the A9Timer device + * Enable Auto reload mode, Clear prescaler control bits + * Set prescaler value, Enable the decrementer + */ + clrsetbits_le32(&timer_base->control, SCUTIMER_CONTROL_PRESCALER_MASK, + emask); + + /* Reset time */ + gd->arch.lastinc = readl(&timer_base->counter) / + (gd->arch.timer_rate_hz / CONFIG_SYS_HZ); + gd->arch.tbl = 0; + + return 0; +} + +/* + * This function is derived from PowerPC code (read timebase as long long). + * On ARM it just returns the timer value. + */ +ulong get_timer_masked(void) +{ + ulong now; + + now = readl(&timer_base->counter) / + (gd->arch.timer_rate_hz / CONFIG_SYS_HZ); + + if (gd->arch.lastinc >= now) { + /* Normal mode */ + gd->arch.tbl += gd->arch.lastinc - now; + } else { + /* We have an overflow ... */ + gd->arch.tbl += gd->arch.lastinc + (TIMER_LOAD_VAL / + (gd->arch.timer_rate_hz / CONFIG_SYS_HZ)) - + now + 1; + } + gd->arch.lastinc = now; + + return gd->arch.tbl; +} + +void __udelay(unsigned long usec) +{ + u32 countticks; + u32 timeend; + u32 timediff; + u32 timenow; + + if (usec == 0) + return; + + countticks = lldiv(((unsigned long long)gd->arch.timer_rate_hz * usec), + 1000000); + + /* decrementing timer */ + timeend = readl(&timer_base->counter) - countticks; + +#if TIMER_LOAD_VAL != 0xFFFFFFFF + /* do not manage multiple overflow */ + if (countticks >= TIMER_LOAD_VAL) + countticks = TIMER_LOAD_VAL - 1; +#endif + + do { + timenow = readl(&timer_base->counter); + + if (timenow >= timeend) { + /* normal case */ + timediff = timenow - timeend; + } else { + if ((TIMER_LOAD_VAL - timeend + timenow) <= + countticks) { + /* overflow */ + timediff = TIMER_LOAD_VAL - timeend + timenow; + } else { + /* missed the exact match */ + break; + } + } + } while (timediff > 0); +} + +/* Timer without interrupts */ +ulong get_timer(ulong base) +{ + return get_timer_masked() - base; +} + +/* + * This function is derived from PowerPC code (read timebase as long long). + * On ARM it just returns the timer value. + */ +unsigned long long get_ticks(void) +{ + return get_timer(0); +} + +/* + * This function is derived from PowerPC code (timebase clock frequency). + * On ARM it returns the number of timer ticks per second. + */ +ulong get_tbclk(void) +{ + return CONFIG_SYS_HZ; +} diff --git a/arch/arm/mach-zynq/u-boot-spl.lds b/arch/arm/mach-zynq/u-boot-spl.lds new file mode 100644 index 00000000000..0f2f756f835 --- /dev/null +++ b/arch/arm/mach-zynq/u-boot-spl.lds @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014 Xilinx, Inc. Michal Simek + * Copyright (c) 2004-2008 Texas Instruments + * + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +MEMORY { .sram : ORIGIN = CONFIG_SPL_TEXT_BASE,\ + LENGTH = CONFIG_SPL_MAX_SIZE } +MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR, \ + LENGTH = CONFIG_SPL_BSS_MAX_SIZE } + +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +SECTIONS +{ + . = ALIGN(4); + .text : + { + __image_copy_start = .; + *(.vectors) + CPUDIR/start.o (.text*) + *(.text*) + } > .sram + + . = ALIGN(4); + .rodata : { + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } > .sram + + . = ALIGN(4); + .data : { + *(.data*) + } > .sram + + . = ALIGN(4); + + . = .; + + __image_copy_end = .; + + _end = .; + + /* Move BSS section to RAM because of FAT */ + .bss (NOLOAD) : { + __bss_start = .; + *(.bss*) + . = ALIGN(4); + __bss_end = .; + } > .sdram + + /DISCARD/ : { *(.dynsym) } + /DISCARD/ : { *(.dynstr*) } + /DISCARD/ : { *(.dynamic*) } + /DISCARD/ : { *(.plt*) } + /DISCARD/ : { *(.interp*) } + /DISCARD/ : { *(.gnu*) } +} diff --git a/arch/arm/mach-zynq/u-boot.lds b/arch/arm/mach-zynq/u-boot.lds new file mode 100644 index 00000000000..4dc9bb0102c --- /dev/null +++ b/arch/arm/mach-zynq/u-boot.lds @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2004-2008 Texas Instruments + * + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +SECTIONS +{ + . = 0x00000000; + + . = ALIGN(4); + .text : + { + *(.__image_copy_start) + *(.vectors) + CPUDIR/start.o (.text*) + *(.text*) + } + + . = ALIGN(4); + .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } + + . = ALIGN(4); + .data : { + *(.data*) + } + + . = ALIGN(4); + + . = .; + + . = ALIGN(4); + .u_boot_list : { + KEEP(*(SORT(.u_boot_list*))); + } + + . = ALIGN(4); + + .image_copy_end : + { + *(.__image_copy_end) + } + + .rel_dyn_start : + { + *(.__rel_dyn_start) + } + + .rel.dyn : { + *(.rel*) + } + + .rel_dyn_end : + { + *(.__rel_dyn_end) + } + + .end : + { + *(.__end) + } + + _image_binary_end = .; + +/* + * Compiler-generated __bss_start and __bss_end, see arch/arm/lib/bss.c + * __bss_base and __bss_limit are for linker only (overlay ordering) + */ + + .bss_start __rel_dyn_start (OVERLAY) : { + KEEP(*(.__bss_start)); + __bss_base = .; + } + + .bss __bss_base (OVERLAY) : { + *(.bss*) + . = ALIGN(4); + __bss_limit = .; + } + + .bss_end __bss_limit (OVERLAY) : { + KEEP(*(.__bss_end)); + } + + /* + * Zynq needs to discard these sections because the user + * is expected to pass this image on to tools for boot.bin + * generation that require them to be dropped. + */ + /DISCARD/ : { *(.dynsym) } + /DISCARD/ : { *(.dynbss*) } + /DISCARD/ : { *(.dynstr*) } + /DISCARD/ : { *(.dynamic*) } + /DISCARD/ : { *(.plt*) } + /DISCARD/ : { *(.interp*) } + /DISCARD/ : { *(.gnu*) } + /DISCARD/ : { *(.ARM.exidx*) } + /DISCARD/ : { *(.gnu.linkonce.armexidx.*) } +} diff --git a/include/configs/zynq-common.h b/include/configs/zynq-common.h index f613b920470..b83e037f6f5 100644 --- a/include/configs/zynq-common.h +++ b/include/configs/zynq-common.h @@ -294,7 +294,7 @@ # define CONFIG_SYS_MMC_MAX_DEVICE 1 #endif -#define CONFIG_SYS_LDSCRIPT "arch/arm/cpu/armv7/zynq/u-boot.lds" +#define CONFIG_SYS_LDSCRIPT "arch/arm/mach-zynq/u-boot.lds" /* Commands */ #include @@ -312,7 +312,7 @@ #define CONFIG_SPL_SERIAL_SUPPORT #define CONFIG_SPL_BOARD_INIT -#define CONFIG_SPL_LDSCRIPT "arch/arm/cpu/armv7/zynq/u-boot-spl.lds" +#define CONFIG_SPL_LDSCRIPT "arch/arm/mach-zynq/u-boot-spl.lds" /* MMC support */ #ifdef CONFIG_ZYNQ_SDHCI0 -- cgit v1.3.1