| /* |
| * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| */ |
| #include "src.h" |
| #include "../../arch/arm/clock.h" |
| #include "../../services.h" |
| #include <assert.h> |
| #include <string.h> |
| #include <utils/util.h> |
| |
| /**************** |
| **** PLLs **** |
| ****************/ |
| |
| /* PLL Control (<pll>_PLL_CTRL) Register */ |
| #define PLL_CTRL_RESET BIT(0) |
| #define PLL_CTRL_PWRDWN BIT(1) |
| #define PLL_CTRL_BYPASS_QUAL BIT(3) |
| #define PLL_CTRL_BYPASS_FORCE BIT(4) |
| |
| #define PLL_CTRL_FDIV_SHIFT 12 |
| #define PLL_CTRL_FDIV(x) ((x) * BIT(PLL_CTRL_FDIV_SHIFT)) |
| #define PLL_CTRL_FDIV_MASK PLL_CTRL_FDIV(0x7F) |
| #define PLL_CTRL_FDIV_MIN 13 |
| #define PLL_CTRL_FDIV_MAX 66 |
| |
| /* PLL Status Register */ |
| #define PLL_STATUS_ARM_PLL_LOCK BIT(0) |
| #define PLL_STATUS_DDR_PLL_LOCK BIT(1) |
| #define PLL_STATUS_IO_PLL_LOCK BIT(2) |
| #define PLL_STATUS_ARM_PLL_STABLE BIT(3) |
| #define PLL_STATUS_DDR_PLL_STABLE BIT(4) |
| #define PLL_STATUS_IO_PLL_STABLE BIT(5) |
| |
| /* PLL Configuration (<pll>_PLL_CFG) Register */ |
| #define PLL_CFG(pll_cp, pll_res, lock_cnt) \ |
| (((lock_cnt) << 12) | (pll_cp << 8) | (pll_res << 4)) |
| |
| /* Required PLL Frequency Configuration Settings */ |
| #define PLL_CFG_FDIV13 PLL_CFG(2, 6, 750) |
| #define PLL_CFG_FDIV14 PLL_CFG(2, 6, 700) |
| #define PLL_CFG_FDIV15 PLL_CFG(2, 6, 650) |
| #define PLL_CFG_FDIV16 PLL_CFG(2, 10, 625) |
| #define PLL_CFG_FDIV17 PLL_CFG(2, 10, 575) |
| #define PLL_CFG_FDIV18 PLL_CFG(2, 10, 550) |
| #define PLL_CFG_FDIV19 PLL_CFG(2, 10, 525) |
| #define PLL_CFG_FDIV20 PLL_CFG(2, 12, 500) |
| #define PLL_CFG_FDIV21 PLL_CFG(2, 12, 475) |
| #define PLL_CFG_FDIV22 PLL_CFG(2, 12, 450) |
| #define PLL_CFG_FDIV23 PLL_CFG(2, 12, 425) |
| #define PLL_CFG_FDIV24 PLL_CFG(2, 12, 400) |
| #define PLL_CFG_FDIV25 PLL_CFG(2, 12, 400) |
| #define PLL_CFG_FDIV26 PLL_CFG(2, 12, 375) |
| #define PLL_CFG_FDIV27 PLL_CFG(2, 12, 350) |
| #define PLL_CFG_FDIV28 PLL_CFG(2, 12, 350) |
| #define PLL_CFG_FDIV29 PLL_CFG(2, 12, 325) |
| #define PLL_CFG_FDIV30 PLL_CFG(2, 12, 325) |
| #define PLL_CFG_FDIV31 PLL_CFG(2, 2, 300) |
| #define PLL_CFG_FDIV32 PLL_CFG(2, 2, 300) |
| #define PLL_CFG_FDIV33 PLL_CFG(2, 2, 300) |
| #define PLL_CFG_FDIV34 PLL_CFG(2, 2, 275) |
| #define PLL_CFG_FDIV35 PLL_CFG(2, 2, 275) |
| #define PLL_CFG_FDIV36 PLL_CFG(2, 2, 275) |
| #define PLL_CFG_FDIV37 PLL_CFG(2, 2, 250) |
| #define PLL_CFG_FDIV38 PLL_CFG(2, 2, 250) |
| #define PLL_CFG_FDIV39 PLL_CFG(2, 2, 250) |
| #define PLL_CFG_FDIV40 PLL_CFG(2, 2, 250) |
| #define PLL_CFG_FDIV41 PLL_CFG(3, 12, 250) |
| #define PLL_CFG_FDIV42 PLL_CFG(3, 12, 250) |
| #define PLL_CFG_FDIV43 PLL_CFG(3, 12, 250) |
| #define PLL_CFG_FDIV44 PLL_CFG(3, 12, 250) |
| #define PLL_CFG_FDIV45 PLL_CFG(3, 12, 250) |
| #define PLL_CFG_FDIV46 PLL_CFG(3, 12, 250) |
| #define PLL_CFG_FDIV47 PLL_CFG(3, 12, 250) |
| #define PLL_CFG_FDIV48 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV49 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV50 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV51 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV52 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV53 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV54 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV55 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV56 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV57 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV58 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV59 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV60 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV61 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV62 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV63 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV64 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV65 PLL_CFG(2, 4, 250) |
| #define PLL_CFG_FDIV66 PLL_CFG(2, 4, 250) |
| |
| struct pll_cfg_t { |
| uint8_t fdiv; |
| uint32_t pll_cfg; |
| }; |
| |
| static struct pll_cfg_t pll_cfg_tbl[] = { |
| /* Need to offset by PLL_CTRL_FDIV_MIN when accessing a row in the table */ |
| {13, PLL_CFG_FDIV13}, |
| {14, PLL_CFG_FDIV14}, |
| {15, PLL_CFG_FDIV15}, |
| {16, PLL_CFG_FDIV16}, |
| {17, PLL_CFG_FDIV17}, |
| {18, PLL_CFG_FDIV18}, |
| {19, PLL_CFG_FDIV19}, |
| {20, PLL_CFG_FDIV20}, |
| {21, PLL_CFG_FDIV21}, |
| {22, PLL_CFG_FDIV22}, |
| {23, PLL_CFG_FDIV23}, |
| {24, PLL_CFG_FDIV24}, |
| {25, PLL_CFG_FDIV25}, |
| {26, PLL_CFG_FDIV26}, |
| {27, PLL_CFG_FDIV27}, |
| {28, PLL_CFG_FDIV28}, |
| {29, PLL_CFG_FDIV29}, |
| {30, PLL_CFG_FDIV30}, |
| {31, PLL_CFG_FDIV31}, |
| {32, PLL_CFG_FDIV32}, |
| {33, PLL_CFG_FDIV33}, |
| {34, PLL_CFG_FDIV34}, |
| {35, PLL_CFG_FDIV35}, |
| {36, PLL_CFG_FDIV36}, |
| {37, PLL_CFG_FDIV37}, |
| {38, PLL_CFG_FDIV38}, |
| {39, PLL_CFG_FDIV39}, |
| {40, PLL_CFG_FDIV40}, |
| {41, PLL_CFG_FDIV41}, |
| {42, PLL_CFG_FDIV42}, |
| {43, PLL_CFG_FDIV43}, |
| {44, PLL_CFG_FDIV44}, |
| {45, PLL_CFG_FDIV45}, |
| {46, PLL_CFG_FDIV46}, |
| {47, PLL_CFG_FDIV47}, |
| {48, PLL_CFG_FDIV48}, |
| {49, PLL_CFG_FDIV49}, |
| {50, PLL_CFG_FDIV50}, |
| {51, PLL_CFG_FDIV51}, |
| {52, PLL_CFG_FDIV52}, |
| {53, PLL_CFG_FDIV53}, |
| {54, PLL_CFG_FDIV54}, |
| {55, PLL_CFG_FDIV55}, |
| {56, PLL_CFG_FDIV56}, |
| {57, PLL_CFG_FDIV57}, |
| {58, PLL_CFG_FDIV58}, |
| {59, PLL_CFG_FDIV59}, |
| {60, PLL_CFG_FDIV60}, |
| {61, PLL_CFG_FDIV61}, |
| {62, PLL_CFG_FDIV62}, |
| {63, PLL_CFG_FDIV63}, |
| {64, PLL_CFG_FDIV64}, |
| {65, PLL_CFG_FDIV65}, |
| {66, PLL_CFG_FDIV66} |
| }; |
| |
| /****************** |
| **** Clocks **** |
| ******************/ |
| |
| /* Clock Source */ |
| #define CLK_SRC_IO_PLL 0x01 |
| #define CLK_SRC_ARM_PLL 0x02 |
| #define CLK_SRC_DDR_PLL 0x03 |
| |
| #define CLK_SRCSEL(x) ((x) << 4) |
| #define CLK_GET_SRCSEL(x) (((x) >> 4) & 0x3) |
| |
| /* Each clock uses a 6-bit divisor */ |
| #define CLK_DIVISOR_MIN 1 |
| #define CLK_DIVISOR_MAX 0x3F |
| |
| /* |
| * The clock divider is located at bits 8:13. Some clock generators have two |
| * cascated dividers, with the second divider located at bits 20:25. |
| */ |
| #define CLK_DIVISOR0_SHIFT 8 |
| #define CLK_DIVISOR1_SHIFT 20 |
| |
| #define CLK_DIVISOR(div, x) ((x) * BIT(CLK_DIVISOR##div##_SHIFT)) |
| #define CLK_DIVISOR_MASK(div) CLK_DIVISOR(div, CLK_DIVISOR_MAX) |
| #define CLK_SET_DIVISOR(div, reg, val) \ |
| do { \ |
| uint32_t v; \ |
| v = reg & ~(CLK_DIVISOR_MASK(div)); \ |
| reg = v | CLK_DIVISOR(div, val); \ |
| } while (0) |
| #define CLK_GET_DIVISOR(div, reg) \ |
| ((reg & CLK_DIVISOR_MASK(div)) >> CLK_DIVISOR##div##_SHIFT) |
| |
| /* Clock Control (enable/disable a clock) */ |
| #define CLK_CLKACT BIT(0) |
| |
| /********************** |
| **** CPU Clocks **** |
| **********************/ |
| |
| /* CPU Clock Control */ |
| #define CPU_CLK_CTRL_CPU_6OR4XCLKACT BIT(24) |
| #define CPU_CLK_CTRL_CPU_3OR2XCLKACT BIT(25) |
| #define CPU_CLK_CTRL_CPU_2XCLKACT BIT(26) |
| #define CPU_CLK_CTRL_CPU_1XCLKACT BIT(27) |
| #define CPU_PERI_CLKACT BIT(28) |
| |
| /* CPU Clock Ratio Mode Select */ |
| #define CPU_CLK_621_TRUE BIT(0) |
| |
| /********************** |
| **** DDR Clocks **** |
| **********************/ |
| |
| /* DDR Clock Control */ |
| #define DDR_CLK_CTRL_DDR_3XCLKACT BIT(0) |
| #define DDR_CLK_CTRL_DDR_2XCLKACT BIT(1) |
| |
| /* DDR Clock Divisors */ |
| #define DDR_3XCLK_DIVISOR_SHIFT 20 |
| #define DDR_2XCLK_DIVISOR_SHIFT 26 |
| #define DDR_CLK_DIVISOR(dom, x) ((x) * BIT(DDR_##dom##XCLK_DIVISOR_SHIFT)) |
| #define DDR_CLK_DIVISOR_MASK(dom) DDR_CLK_DIVISOR(dom, CLK_DIVISOR_MAX) |
| #define DDR_CLK_SET_DIVISOR(dom, val) \ |
| do { \ |
| clk_regs->ddr_clk_ctrl &= ~(DDR_CLK_DIVISOR_MASK(dom)); \ |
| clk_regs->ddr_clk_ctrl |= DDR_CLK_DIVISOR(dom, val); \ |
| } while (0) |
| #define DDR_CLK_GET_DIVISOR(dom) \ |
| ((clk_regs->ddr_clk_ctrl & DDR_CLK_DIVISOR_MASK(dom)) >> DDR_##dom##XCLK_DIVISOR_SHIFT) |
| |
| /************************** |
| **** FPGA PL Clocks **** |
| **************************/ |
| |
| /* Programmable Logic (PL) - i.e. FPGA - clock registers */ |
| typedef volatile struct { |
| uint32_t clk_ctrl; /* PL Clock x Output Control */ |
| uint32_t thr_ctrl; /* PL Clock x Throttle Control */ |
| uint32_t thr_cnt; /* PL Clock x Throttle Count */ |
| uint32_t thr_sta; /* PL Clock x Throttle Status */ |
| } pl_clk_regs_t; |
| |
| /*************************** |
| **** Clock Registers **** |
| ***************************/ |
| |
| struct zynq7000_clk_regs { |
| /* We are only interested in the clock registers in SLCR */ |
| uint32_t arm_pll_ctrl; /* 0x100 ARM PLL Control */ |
| uint32_t ddr_pll_ctrl; /* 0x104 DDR PLL Control */ |
| uint32_t io_pll_ctrl; /* 0x108 IO PLL Control */ |
| uint32_t pll_status; /* 0x10C PLL Status */ |
| |
| uint32_t arm_pll_cfg; /* 0x110 ARM PLL Configuration */ |
| uint32_t ddr_pll_cfg; /* 0x114 DDR PLL Configuration */ |
| uint32_t io_pll_cfg; /* 0x118 IO PLL Configuration */ |
| uint32_t res0[1]; |
| uint32_t arm_clk_ctrl; /* 0x120 CPU Clock Control */ |
| uint32_t ddr_clk_ctrl; /* 0x124 DDR Clock Control */ |
| uint32_t dci_clk_ctrl; /* 0x128 DCI Clock Control */ |
| |
| uint32_t aper_clk_ctrl; /* 0x12C AMBDA Peripheral Clock Control */ |
| uint32_t usb0_clk_ctrl; /* 0x130 USB 0 ULPI Clock Control */ |
| uint32_t usb1_clk_ctrl; /* 0x134 USB 1 ULPI Clock Control */ |
| uint32_t gem0_rclk_ctrl; /* 0x138 GigE 0 Rx Clock and Rx Signals Select */ |
| uint32_t gem1_rclk_ctrl; /* 0x13C GigE 1 Rx Clock and Rx Signals Select */ |
| uint32_t gem0_clk_ctrl; /* 0x140 GigE 0 Ref Clock Control */ |
| uint32_t gem1_clk_ctrl; /* 0x144 GigE 1 Ref Clock Control */ |
| uint32_t smc_clk_ctrl; /* 0x148 SMC Ref Clock Control */ |
| uint32_t lqspi_clk_ctrl; /* 0x14C QUAD SPI REf Clock Control */ |
| uint32_t sdio_clk_ctrl; /* 0x150 SDIO Ref Clock Control */ |
| uint32_t uart_clk_ctrl; /* 0x154 UART Ref Clock Control */ |
| uint32_t spi_clk_ctrl; /* 0x158 SPI Ref Clock Control */ |
| uint32_t can_clk_ctrl; /* 0x15C CAN Ref Clock Control */ |
| uint32_t can_mioclk_ctrl; /* 0x160 CAN MIO Clock Control */ |
| uint32_t dbg_clk_ctrl; /* 0x164 SoC Debug Clock Control */ |
| uint32_t pcap_clk_ctrl; /* 0x168 PCAP Clock Control */ |
| uint32_t topsw_clk_ctrl; /* 0x16C Central Interconnect Clock Control */ |
| pl_clk_regs_t fpga_clk[4]; /* 0x170 PL Clock 0 */ |
| uint32_t pad1[5]; |
| uint32_t clk_621_true; /* 0x1C4 CPU Clock Ratio Mode Select */ |
| uint32_t pad2[79]; |
| uint32_t wdt_clk_sel; /* 0x304 SWDT Clock Source Select */ |
| }; |
| |
| static const enum clk_id cpu_clk_src[] = { |
| CLK_ARM_PLL, |
| CLK_ARM_PLL, |
| CLK_DDR_PLL, |
| CLK_IO_PLL |
| }; |
| |
| static const enum clk_id generic_clk_src[] = { |
| CLK_IO_PLL, |
| CLK_IO_PLL, |
| CLK_ARM_PLL, |
| CLK_DDR_PLL |
| }; |
| |
| #define fpga_clk_src generic_clk_src |
| #define can_clk_src generic_clk_src |
| #define pcap_clk_src generic_clk_src |
| |
| static volatile struct zynq7000_clk_regs* clk_regs = NULL; |
| |
| /* Set divisors, avoiding over clocking peripherals */ |
| static inline void |
| set_divs(volatile uint32_t* ctrl, uint8_t div0, uint8_t div1) |
| { |
| uint8_t old_div0; |
| old_div0 = CLK_GET_DIVISOR(0, *ctrl); |
| if (div0 > old_div0) { |
| CLK_SET_DIVISOR(0, *ctrl, div0); |
| CLK_SET_DIVISOR(1, *ctrl, div1); |
| } else { |
| CLK_SET_DIVISOR(1, *ctrl, div1); |
| CLK_SET_DIVISOR(0, *ctrl, div0); |
| } |
| } |
| |
| /* Set divisors where only one divisor is available */ |
| static inline void |
| set_div(volatile uint32_t* ctrl, uint8_t div0) |
| { |
| CLK_SET_DIVISOR(0, *ctrl, div0); |
| } |
| |
| /* |
| * Calculate the clock rate divisors |
| * @param hz: Desired frequency |
| * @param parent_hz: Parent clock's frequency |
| * @param rdiv0: Calculated DIVISOR0 value (returned) |
| * @param rdiv1: Calculated DIVISOR1 value (returned) |
| * @return : The actual frequency based on the calculated values |
| */ |
| static freq_t |
| zynq7000_clk_calc_divs(freq_t hz, freq_t parent_hz, uint8_t* rdiv0, |
| uint8_t* rdiv1) |
| { |
| /* |
| * Safety check. |
| * |
| * rdiv1 is allowed to be NULL if the particular clock does not have a |
| * second divider. |
| */ |
| assert(rdiv0 != NULL); |
| |
| uint8_t div0, div1, div1_max; |
| freq_t calc_freq, rfreq = 0; |
| uint32_t freq_error, best_freq_error = ~0; |
| |
| /* |
| * The maximum value for div1 is dictated by whether a particular |
| * clock uses one divisor or two. |
| * |
| * If the clock only has one divisor (i.e. rdiv1 is NULL), then this |
| * value will not change. |
| */ |
| if (rdiv1 == NULL) { |
| div1_max = 1; |
| } else { |
| div1_max = CLK_DIVISOR_MAX; |
| } |
| |
| /* |
| * Calculate values for div0 and div1 based on the desired clock |
| * frequency. |
| */ |
| for (div0 = CLK_DIVISOR_MIN; div0 <= CLK_DIVISOR_MAX; div0++) { |
| for (div1 = CLK_DIVISOR_MIN; div1 <= div1_max; div1++) { |
| calc_freq = parent_hz / (div0 * div1); |
| |
| if (hz > calc_freq) { |
| freq_error = hz - calc_freq; |
| } else { |
| freq_error = calc_freq - hz; |
| } |
| |
| if (freq_error < best_freq_error) { |
| best_freq_error = freq_error; |
| *rdiv0 = div0; |
| if (rdiv1 != NULL) { |
| *rdiv1 = div1; |
| } |
| rfreq = calc_freq; |
| |
| /* Short-circuit */ |
| if (freq_error == 0) { |
| goto end; |
| } |
| } |
| } |
| } |
| |
| end: |
| return rfreq; |
| } |
| |
| /* |
| * Ensure the divisor is an even number. If it isn't, make it in even number. |
| * @param divisor: Divisor to check |
| * @return : An even-number divisor |
| */ |
| static inline uint8_t |
| zynq7000_even_divisor(uint8_t divisor) |
| { |
| if ((divisor % 2) != 0) { |
| return INRANGE(CLK_DIVISOR_MIN + 1, divisor - 1, CLK_DIVISOR_MAX - 1); |
| } else { |
| return divisor; |
| } |
| } |
| |
| /* PS_CLK */ |
| static struct clock master_clk = { CLK_OPS_DEFAULT(MASTER) }; |
| |
| static uint32_t |
| _decode_pll(clk_t* clk, volatile uint32_t** ctrl, volatile uint32_t** cfg) |
| { |
| switch (clk->id) { |
| case CLK_ARM_PLL: |
| *ctrl = &clk_regs->arm_pll_ctrl; |
| *cfg = &clk_regs->arm_pll_cfg; |
| return PLL_STATUS_ARM_PLL_LOCK; |
| case CLK_DDR_PLL: |
| *ctrl = &clk_regs->ddr_pll_ctrl; |
| *cfg = &clk_regs->ddr_pll_cfg; |
| return PLL_STATUS_DDR_PLL_LOCK; |
| case CLK_IO_PLL: |
| *ctrl = &clk_regs->io_pll_ctrl; |
| *cfg = &clk_regs->io_pll_cfg; |
| return PLL_STATUS_IO_PLL_LOCK; |
| default: |
| assert(!"Invalid clock"); |
| return 0; |
| } |
| } |
| |
| /* PLLs */ |
| static freq_t |
| _pll_get_freq(clk_t* clk) |
| { |
| volatile uint32_t* ctrl_reg; |
| volatile uint32_t* cfg_reg; |
| uint32_t status_mask; |
| uint8_t fdiv; |
| uint32_t fin, fout; |
| |
| status_mask = _decode_pll(clk, &ctrl_reg, &cfg_reg); |
| if (status_mask == 0) { |
| return 0; |
| } |
| assert(!(*ctrl_reg & (BIT(4) | BIT(1) | BIT(0)))); |
| |
| fin = clk_get_freq(clk->parent); |
| fdiv = (*ctrl_reg & PLL_CTRL_FDIV_MASK) >> PLL_CTRL_FDIV_SHIFT; |
| fout = fin * fdiv; |
| |
| return fout; |
| } |
| |
| static freq_t |
| _pll_set_freq(clk_t* clk, freq_t hz) |
| { |
| volatile uint32_t* ctrl_reg; |
| volatile uint32_t* cfg_reg; |
| uint32_t status_mask; |
| uint32_t fin; |
| uint8_t fdiv; |
| |
| fin = clk_get_freq(clk->parent); |
| fdiv = fin / hz; |
| fdiv = INRANGE(PLL_CTRL_FDIV_MIN, fdiv, PLL_CTRL_FDIV_MAX); |
| |
| status_mask = _decode_pll(clk, &ctrl_reg, &cfg_reg); |
| if (status_mask == 0) { |
| return 0; |
| } |
| |
| /* Program the feedback divider value and the configuration register */ |
| *ctrl_reg &= ~(PLL_CTRL_FDIV_MASK); |
| *ctrl_reg |= PLL_CTRL_FDIV(fdiv); |
| *cfg_reg = pll_cfg_tbl[fdiv - PLL_CTRL_FDIV_MIN].pll_cfg; |
| |
| /* Force the PLL into bypass mode */ |
| *ctrl_reg |= PLL_CTRL_BYPASS_FORCE; |
| |
| /* Assert and de-assert the PLL reset */ |
| *ctrl_reg |= PLL_CTRL_RESET; |
| *ctrl_reg &= ~(PLL_CTRL_RESET); |
| |
| /* Verify that the PLL is locked */ |
| while (!(clk_regs->pll_status & status_mask)); |
| |
| /* Disable the PLL bypass mode */ |
| *ctrl_reg &= ~(PLL_CTRL_BYPASS_FORCE); |
| |
| return clk_get_freq(clk); |
| } |
| |
| static void |
| _pll_recal(clk_t* clk UNUSED) |
| { |
| assert(0); |
| } |
| |
| static clk_t* |
| _pll_init(clk_t* clk) |
| { |
| if (clk->priv == NULL) { |
| clk_t* parent; |
| parent = clk_get_clock(clk_get_clock_sys(clk), CLK_MASTER); |
| clk_register_child(parent, clk); |
| clk->priv = (void*)clk_regs; |
| } |
| |
| return clk; |
| } |
| |
| static struct clock arm_pll_clk = { CLK_OPS(ARM_PLL, pll, NULL) }; |
| static struct clock ddr_pll_clk = { CLK_OPS(DDR_PLL, pll, NULL) }; |
| static struct clock io_pll_clk = { CLK_OPS(IO_PLL, pll, NULL) }; |
| |
| static int |
| _cpu_set_621(clk_t* clk, int v) |
| { |
| switch (clk->id) { |
| case CLK_CPU_6OR4X: |
| case CLK_CPU_3OR2X: |
| case CLK_CPU_2X: |
| case CLK_CPU_1X: |
| if (v) { |
| clk_regs->clk_621_true |= CPU_CLK_621_TRUE; |
| } else { |
| clk_regs->clk_621_true &= ~CPU_CLK_621_TRUE; |
| } |
| return 0; |
| default: |
| return -1; |
| } |
| } |
| |
| int |
| clk_cpu_clk_select_621(clk_t* clk) |
| { |
| return _cpu_set_621(clk, 1); |
| } |
| |
| int |
| clk_cpu_clk_select_421(clk_t* clk) |
| { |
| return _cpu_set_621(clk, 0); |
| } |
| |
| /* CPU Clocks */ |
| static freq_t |
| _cpu_get_freq(clk_t* clk) |
| { |
| uint8_t clk_621_true, divisor0; |
| uint32_t divisor; |
| uint32_t fout, fin; |
| |
| clk_621_true = clk_regs->clk_621_true & CPU_CLK_621_TRUE; |
| divisor0 = CLK_GET_DIVISOR(0, clk_regs->arm_clk_ctrl); |
| |
| switch (clk->id) { |
| case CLK_CPU_6OR4X: |
| divisor = divisor0; |
| break; |
| case CLK_CPU_3OR2X: |
| divisor = divisor0 * 2; |
| break; |
| case CLK_CPU_2X: |
| divisor = divisor0 * ((clk_621_true) ? 3 : 2); |
| break; |
| case CLK_CPU_1X: |
| divisor = divisor0 * ((clk_621_true) ? 6 : 4); |
| break; |
| default: |
| assert(!"Invalid clock"); |
| return -1; |
| } |
| |
| fin = clk_get_freq(clk->parent); |
| fout = fin / divisor; |
| |
| return fout; |
| } |
| |
| static freq_t |
| _cpu_set_freq(clk_t* clk, freq_t hz) |
| { |
| uint32_t fin; |
| uint8_t divisor0; |
| |
| /* |
| * We only set cpu_6x4x's frequency. The other CPU clock frequencies are |
| * derived from cpu_6x4x. |
| */ |
| if (clk->id != CLK_CPU_6OR4X) { |
| return -1; |
| } |
| |
| fin = clk_get_freq(clk->parent); |
| zynq7000_clk_calc_divs(hz, fin, &divisor0, NULL); |
| |
| /* CPU clocks must have an even divisor */ |
| divisor0 = zynq7000_even_divisor(divisor0); |
| set_div(&clk_regs->arm_clk_ctrl, divisor0); |
| |
| return clk_get_freq(clk); |
| } |
| |
| static void |
| _cpu_recal(clk_t* clk UNUSED) |
| { |
| assert(0); |
| } |
| |
| static clk_t* |
| _cpu_init(clk_t* clk) |
| { |
| if (clk->priv == NULL) { |
| clk_t* parent; |
| enum clk_id parent_id; |
| parent_id = cpu_clk_src[CLK_GET_SRCSEL(clk_regs->arm_clk_ctrl)]; |
| parent = clk_get_clock(clk_get_clock_sys(clk), parent_id); |
| clk_register_child(parent, clk); |
| clk->priv = (void*)clk_regs; |
| } |
| |
| return clk; |
| } |
| |
| static struct clock cpu_6or4x_clk = { CLK_OPS(CPU_6OR4X, cpu, NULL) }; |
| static struct clock cpu_3or2x_clk = { CLK_OPS(CPU_3OR2X, cpu, NULL) }; |
| static struct clock cpu_2x_clk = { CLK_OPS(CPU_2X, cpu, NULL) }; |
| static struct clock cpu_1x_clk = { CLK_OPS(CPU_1X, cpu, NULL) }; |
| |
| /* DDR Clocks */ |
| static freq_t |
| _ddr_get_freq(clk_t* clk) |
| { |
| uint8_t divisor0, divisor1; |
| uint32_t fout, fin; |
| |
| switch (clk->id) { |
| case CLK_DDR_2X: |
| divisor0 = DDR_CLK_GET_DIVISOR(2); |
| divisor1 = 1; |
| break; |
| case CLK_DDR_3X: |
| divisor0 = DDR_CLK_GET_DIVISOR(3); |
| divisor1 = 1; |
| break; |
| case CLK_DCI: |
| divisor0 = CLK_GET_DIVISOR(0, clk_regs->dci_clk_ctrl); |
| divisor1 = CLK_GET_DIVISOR(1, clk_regs->dci_clk_ctrl); |
| break; |
| default: |
| assert(!"Invalid clock"); |
| return -1; |
| } |
| |
| fin = clk_get_freq(clk->parent); |
| fout = fin / (divisor0 * divisor1); |
| |
| return fout; |
| } |
| |
| static freq_t |
| _ddr_set_freq(clk_t* clk, freq_t hz) |
| { |
| uint32_t fin; |
| uint8_t divisor0, divisor1; |
| |
| fin = clk_get_freq(clk->parent); |
| |
| switch (clk->id) { |
| case CLK_DDR_2X: |
| zynq7000_clk_calc_divs(hz, fin, &divisor0, NULL); |
| DDR_CLK_SET_DIVISOR(2, divisor0); |
| break; |
| case CLK_DDR_3X: |
| zynq7000_clk_calc_divs(hz, fin, &divisor0, NULL); |
| |
| /* DDR_3XCLK must have an even divisor */ |
| divisor0 = zynq7000_even_divisor(divisor0); |
| |
| DDR_CLK_SET_DIVISOR(3, divisor0); |
| break; |
| case CLK_DCI: |
| zynq7000_clk_calc_divs(hz, fin, &divisor0, &divisor1); |
| set_divs(&clk_regs->dci_clk_ctrl, divisor0, divisor1); |
| break; |
| default: |
| assert(!"Invalid clock"); |
| return -1; |
| } |
| |
| return clk_get_freq(clk); |
| } |
| |
| static void |
| _ddr_recal(clk_t* clk UNUSED) |
| { |
| assert(0); |
| } |
| |
| static clk_t* |
| _ddr_init(clk_t* clk) |
| { |
| if (clk->priv == NULL) { |
| clk_t* parent; |
| parent = clk_get_clock(clk_get_clock_sys(clk), CLK_DDR_PLL); |
| clk_register_child(parent, clk); |
| clk->priv = (void*)clk_regs; |
| } |
| |
| return clk; |
| } |
| |
| static struct clock ddr_2x_clk = { CLK_OPS(DDR_2X, ddr, NULL) }; |
| static struct clock ddr_3x_clk = { CLK_OPS(DDR_3X, ddr, NULL) }; |
| static struct clock dci_clk = { CLK_OPS(DCI, ddr, NULL) }; |
| |
| /* I/O Peripheral Clocks */ |
| static freq_t |
| _aper_get_freq(clk_t* clk) |
| { |
| enum clk_id id; |
| uint8_t divisor0, divisor1; |
| uint32_t fout, fin; |
| |
| id = clk->id; |
| |
| switch (id) { |
| /* One divider clocks */ |
| case CLK_SMC: |
| divisor0 = CLK_GET_DIVISOR(0, clk_regs->smc_clk_ctrl); |
| divisor1 = 1; |
| break; |
| case CLK_LQSPI: |
| divisor0 = CLK_GET_DIVISOR(0, clk_regs->lqspi_clk_ctrl); |
| divisor1 = 1; |
| break; |
| case CLK_SDIO0: |
| case CLK_SDIO1: |
| divisor0 = CLK_GET_DIVISOR(0, clk_regs->sdio_clk_ctrl); |
| divisor1 = 1; |
| break; |
| case CLK_UART0: |
| case CLK_UART1: |
| divisor0 = CLK_GET_DIVISOR(0, clk_regs->uart_clk_ctrl); |
| divisor1 = 1; |
| break; |
| case CLK_SPI0: |
| case CLK_SPI1: |
| divisor0 = CLK_GET_DIVISOR(0, clk_regs->spi_clk_ctrl); |
| divisor1 = 1; |
| break; |
| /* Two divider clocks */ |
| case CLK_GEM0: |
| divisor0 = CLK_GET_DIVISOR(0, clk_regs->gem0_clk_ctrl); |
| divisor1 = CLK_GET_DIVISOR(1, clk_regs->gem0_clk_ctrl); |
| break; |
| case CLK_GEM1: |
| divisor0 = CLK_GET_DIVISOR(0, clk_regs->gem1_clk_ctrl); |
| divisor1 = CLK_GET_DIVISOR(1, clk_regs->gem1_clk_ctrl); |
| break; |
| case CLK_CAN0: |
| case CLK_CAN1: |
| divisor0 = CLK_GET_DIVISOR(0, clk_regs->can_clk_ctrl); |
| divisor1 = CLK_GET_DIVISOR(1, clk_regs->can_clk_ctrl); |
| break; |
| default: |
| assert(!"Invalid clock"); |
| return -1; |
| } |
| |
| fin = clk_get_freq(clk->parent); |
| if (divisor0 == 0 || divisor1 == 0) { |
| fout = 0; |
| } else { |
| fout = fin / (divisor0 * divisor1); |
| } |
| return fout; |
| } |
| |
| static freq_t |
| _aper_set_freq(clk_t* clk, freq_t hz) |
| { |
| uint32_t fin; |
| uint8_t divisor0, divisor1; |
| |
| fin = clk_get_freq(clk->parent); |
| |
| switch (clk->id) { |
| /* One divider clocks */ |
| case CLK_SMC: |
| zynq7000_clk_calc_divs(hz, fin, &divisor0, NULL); |
| set_div(&clk_regs->smc_clk_ctrl, divisor0); |
| break; |
| case CLK_LQSPI: |
| zynq7000_clk_calc_divs(hz, fin, &divisor0, NULL); |
| set_div(&clk_regs->lqspi_clk_ctrl, divisor0); |
| break; |
| case CLK_SDIO0: |
| case CLK_SDIO1: |
| zynq7000_clk_calc_divs(hz, fin, &divisor0, NULL); |
| set_div(&clk_regs->sdio_clk_ctrl, divisor0); |
| break; |
| case CLK_UART0: |
| case CLK_UART1: |
| zynq7000_clk_calc_divs(hz, fin, &divisor0, NULL); |
| set_div(&clk_regs->uart_clk_ctrl, divisor0); |
| break; |
| case CLK_SPI0: |
| case CLK_SPI1: |
| zynq7000_clk_calc_divs(hz, fin, &divisor0, NULL); |
| set_div(&clk_regs->spi_clk_ctrl, divisor0); |
| break; |
| /* Two divider clocks */ |
| case CLK_GEM0: |
| zynq7000_clk_calc_divs(hz, fin, &divisor0, &divisor1); |
| set_divs(&clk_regs->gem0_clk_ctrl, divisor0, divisor1); |
| break; |
| case CLK_GEM1: |
| zynq7000_clk_calc_divs(hz, fin, &divisor0, &divisor1); |
| set_divs(&clk_regs->gem1_clk_ctrl, divisor0, divisor1); |
| break; |
| case CLK_CAN0: |
| case CLK_CAN1: |
| zynq7000_clk_calc_divs(hz, fin, &divisor0, &divisor1); |
| set_divs(&clk_regs->can_clk_ctrl, divisor0, divisor1); |
| break; |
| default: |
| assert(!"Invalid clock"); |
| return -1; |
| } |
| |
| return clk_get_freq(clk); |
| } |
| |
| static void |
| _aper_recal(clk_t* clk UNUSED) |
| { |
| assert(0); |
| } |
| |
| static clk_t* |
| _aper_init(clk_t* clk) |
| { |
| if (clk->priv == NULL) { |
| clk_t* parent; |
| parent = clk_get_clock(clk_get_clock_sys(clk), CLK_IO_PLL); |
| clk_register_child(parent, clk); |
| clk->priv = (void*)clk_regs; |
| } |
| |
| return clk; |
| } |
| |
| static struct clock smc_clk = { CLK_OPS(SMC, aper, NULL) }; |
| static struct clock lqspi_clk = { CLK_OPS(LQSPI, aper, NULL) }; |
| static struct clock gem0_clk = { CLK_OPS(GEM0, aper, NULL) }; |
| static struct clock gem1_clk = { CLK_OPS(GEM1, aper, NULL) }; |
| static struct clock sdio0_clk = { CLK_OPS(SDIO0, aper, NULL) }; |
| static struct clock sdio1_clk = { CLK_OPS(SDIO1, aper, NULL) }; |
| static struct clock uart0_clk = { CLK_OPS(UART0, aper, NULL) }; |
| static struct clock uart1_clk = { CLK_OPS(UART1, aper, NULL) }; |
| static struct clock spi0_clk = { CLK_OPS(SPI0, aper, NULL) }; |
| static struct clock spi1_clk = { CLK_OPS(SPI1, aper, NULL) }; |
| static struct clock can0_clk = { CLK_OPS(CAN0, aper, NULL) }; |
| static struct clock can1_clk = { CLK_OPS(CAN1, aper, NULL) }; |
| |
| static inline pl_clk_regs_t* |
| get_pl_clk_regs(clk_t* clk) |
| { |
| switch (clk->id) { |
| case CLK_FPGA_PL0: |
| return &clk_regs->fpga_clk[0]; |
| case CLK_FPGA_PL1: |
| return &clk_regs->fpga_clk[1]; |
| case CLK_FPGA_PL2: |
| return &clk_regs->fpga_clk[2]; |
| case CLK_FPGA_PL3: |
| return &clk_regs->fpga_clk[3]; |
| default: |
| return NULL; |
| } |
| } |
| |
| /* FPGA PL Clocks */ |
| static freq_t |
| _fpga_get_freq(clk_t* clk) |
| { |
| pl_clk_regs_t* regs = (pl_clk_regs_t*)clk->priv; |
| uint8_t div0, div1; |
| freq_t fin; |
| div0 = CLK_GET_DIVISOR(0, regs->clk_ctrl); |
| div1 = CLK_GET_DIVISOR(1, regs->clk_ctrl); |
| fin = clk_get_freq(clk->parent); |
| return fin / div0 / div1; |
| } |
| |
| static freq_t |
| _fpga_set_freq(clk_t* clk, freq_t hz) |
| { |
| pl_clk_regs_t* regs = (pl_clk_regs_t*)clk->priv; |
| uint8_t div0, div1; |
| freq_t fin; |
| |
| fin = clk_get_freq(clk->parent); |
| zynq7000_clk_calc_divs(hz, fin, &div0, &div1); |
| set_divs(®s->clk_ctrl, div0, div1); |
| |
| return clk_get_freq(clk); |
| } |
| |
| static void |
| _fpga_recal(clk_t* clk UNUSED) |
| { |
| assert(0); |
| } |
| |
| static clk_t* |
| _fpga_init(clk_t* clk) |
| { |
| if (clk->priv == NULL) { |
| pl_clk_regs_t* regs; |
| clk_t* parent; |
| enum clk_id parent_id; |
| regs = get_pl_clk_regs(clk); |
| parent_id = fpga_clk_src[CLK_GET_SRCSEL(regs->clk_ctrl)]; |
| parent = clk_get_clock(clk_get_clock_sys(clk), parent_id); |
| clk_register_child(parent, clk); |
| clk->priv = (void*)regs; |
| /* Continuous clock */ |
| regs->thr_ctrl = 0; |
| } |
| |
| return clk; |
| } |
| |
| static struct clock fpga_pl0_clk = { CLK_OPS(FPGA_PL0, fpga, NULL) }; |
| static struct clock fpga_pl1_clk = { CLK_OPS(FPGA_PL1, fpga, NULL) }; |
| static struct clock fpga_pl2_clk = { CLK_OPS(FPGA_PL2, fpga, NULL) }; |
| static struct clock fpga_pl3_clk = { CLK_OPS(FPGA_PL3, fpga, NULL) }; |
| |
| /*** These clocks yet to be implemented ***/ |
| static struct clock pcap_clk = { CLK_OPS_DEFAULT(PCAP) }; |
| static struct clock dbg_clk = { CLK_OPS_DEFAULT(DBG ) }; |
| |
| static int |
| zynq7000_gate_enable(clock_sys_t* clock_sys, enum clock_gate gate, enum clock_gate_mode mode) |
| { |
| uint32_t aper_clk_ctrl; |
| |
| assert(clk_regs); |
| assert(mode == CLKGATE_ON); |
| assert(gate >= 0); |
| assert(gate < 32); |
| |
| aper_clk_ctrl = clk_regs->aper_clk_ctrl; |
| aper_clk_ctrl |= BIT(gate); |
| clk_regs->aper_clk_ctrl = aper_clk_ctrl; |
| |
| return 0; |
| } |
| |
| int |
| clock_sys_init(ps_io_ops_t* o, clock_sys_t* clock_sys) |
| { |
| src_dev_t slcr; |
| int err; |
| assert(sizeof(struct zynq7000_clk_regs) == 0x208); |
| /* Grab a handle to the clock registers */ |
| err = reset_controller_init(SLCR, o, &slcr); |
| if (err) { |
| return err; |
| } |
| clk_regs = (volatile struct zynq7000_clk_regs*)reset_controller_get_clock_regs(&slcr); |
| assert(clk_regs); |
| |
| /* Initialise the clock subsystem structure */ |
| clock_sys->priv = (void*)clk_regs; |
| clock_sys->get_clock = &ps_get_clock; |
| clock_sys->gate_enable = &zynq7000_gate_enable; |
| return 0; |
| } |
| |
| void |
| clk_print_clock_tree(clock_sys_t* sys) |
| { |
| clk_t *clk = clk_get_clock(sys, CLK_MASTER); |
| clk_print_tree(clk, ""); |
| } |
| |
| clk_t* ps_clocks[] = { |
| [CLK_MASTER] = &master_clk, |
| [CLK_ARM_PLL] = &arm_pll_clk, |
| [CLK_DDR_PLL] = &ddr_pll_clk, |
| [CLK_IO_PLL] = &io_pll_clk, |
| [CLK_CPU_6OR4X] = &cpu_6or4x_clk, |
| [CLK_CPU_3OR2X] = &cpu_3or2x_clk, |
| [CLK_CPU_2X] = &cpu_2x_clk, |
| [CLK_CPU_1X] = &cpu_1x_clk, |
| [CLK_DDR_2X] = &ddr_2x_clk, |
| [CLK_DDR_3X] = &ddr_3x_clk, |
| [CLK_DCI] = &dci_clk, |
| [CLK_SMC] = &smc_clk, |
| [CLK_LQSPI] = &lqspi_clk, |
| [CLK_GEM0] = &gem0_clk, |
| [CLK_GEM1] = &gem1_clk, |
| [CLK_SDIO0] = &sdio0_clk, |
| [CLK_SDIO1] = &sdio1_clk, |
| [CLK_UART0] = &uart0_clk, |
| [CLK_UART1] = &uart1_clk, |
| [CLK_SPI0] = &spi0_clk, |
| [CLK_SPI1] = &spi1_clk, |
| [CLK_CAN0] = &can0_clk, |
| [CLK_CAN1] = &can1_clk, |
| [CLK_DBG] = &dbg_clk, |
| [CLK_PCAP] = &pcap_clk, |
| [CLK_FPGA_PL0] = &fpga_pl0_clk, |
| [CLK_FPGA_PL1] = &fpga_pl1_clk, |
| [CLK_FPGA_PL2] = &fpga_pl2_clk, |
| [CLK_FPGA_PL3] = &fpga_pl3_clk, |
| }; |
| |
| freq_t ps_freq_default[] = { |
| [CLK_MASTER] = 33333 * KHZ, /* PS_CLK frequency = 33.33 MHz */ |
| [CLK_ARM_PLL] = 1333 * MHZ, |
| [CLK_DDR_PLL] = 1067 * MHZ, |
| [CLK_IO_PLL] = 1000 * MHZ, |
| [CLK_CPU_6OR4X] = 667 * MHZ, |
| [CLK_CPU_3OR2X] = 333 * MHZ, |
| [CLK_CPU_2X] = 222 * MHZ, |
| [CLK_CPU_1X] = 111 * MHZ, |
| [CLK_DDR_2X] = 356 * MHZ, |
| [CLK_DDR_3X] = 533 * MHZ, |
| [CLK_DCI] = 10 * MHZ, |
| [CLK_SMC] = 100 * MHZ, |
| [CLK_LQSPI] = 200 * MHZ, |
| [CLK_GEM0] = 125 * MHZ, |
| [CLK_GEM1] = 125 * MHZ, |
| [CLK_SDIO0] = 100 * MHZ, |
| [CLK_SDIO1] = 100 * MHZ, |
| [CLK_UART0] = 25 * MHZ, |
| [CLK_UART1] = 25 * MHZ, |
| [CLK_SPI0] = 200 * MHZ, |
| [CLK_SPI1] = 200 * MHZ, |
| [CLK_CAN0] = 100 * MHZ, |
| [CLK_CAN1] = 100 * MHZ, |
| [CLK_PCAP] = 200 * MHZ, |
| [CLK_DBG] = 100 * MHZ, |
| [CLK_FPGA_PL0] = 50 * MHZ, |
| [CLK_FPGA_PL1] = 50 * MHZ, |
| [CLK_FPGA_PL2] = 50 * MHZ, |
| [CLK_FPGA_PL3] = 50 * MHZ, |
| }; |