From c9c2866262c9a8e6b3ef5e0cef4bf2c8c86bd670 Mon Sep 17 00:00:00 2001 From: Justin Hammond Date: Sun, 16 Aug 2026 01:43:03 +0800 Subject: [PATCH] arch/risc-v/eic7700x: Describe the clock tree the boot loader leaves. Nothing in this port knew what any clock ran at, so a driver needing a rate had to carry a hard coded one, which is wrong the moment the boot loader changes. Register the Clock and Reset Generator with the NuttX clock framework: the PLLs, muxes, dividers and gates covering the low speed peripherals, the U84 cluster, the RTC and timers, the NOC, boot SPI, SCPU, LPCPU, DDR and TCU, the high speed peripherals, the always on DMA and secure blocks, the GPU, DSP, die to die link and NPU, and the video input, output and codec paths. The tree is visible through /proc/clk. Registration writes nothing: the tree comes up describing what the boot loader left behind. A clock moves only when a driver asks, by enabling a gate, setting a divider or reparenting a mux. A mux carrying a clock the system is running on will speed up on request and refuses to slow down, because that changes the timing every driver downstream was configured for while they are using it. The PLL post divider fields do not sit where the TRM's register diagram puts them; they are ordered here to match the rates the tree reports. Assisted-by: Claude:claude-opus-5 Signed-off-by: Justin Hammond --- arch/risc-v/src/eic7700x/Kconfig | 23 + arch/risc-v/src/eic7700x/Make.defs | 4 + arch/risc-v/src/eic7700x/eic7700x_clk.c | 1336 +++++++++++++++++ arch/risc-v/src/eic7700x/eic7700x_clk.h | 149 ++ arch/risc-v/src/eic7700x/eic7700x_clk_ops.c | 438 ++++++ arch/risc-v/src/eic7700x/eic7700x_start.c | 25 + .../src/eic7700x/hardware/eic7700x_clk.h | 424 ++++++ 7 files changed, 2399 insertions(+) create mode 100644 arch/risc-v/src/eic7700x/eic7700x_clk.c create mode 100644 arch/risc-v/src/eic7700x/eic7700x_clk.h create mode 100644 arch/risc-v/src/eic7700x/eic7700x_clk_ops.c create mode 100644 arch/risc-v/src/eic7700x/hardware/eic7700x_clk.h diff --git a/arch/risc-v/src/eic7700x/Kconfig b/arch/risc-v/src/eic7700x/Kconfig index e69de29bb2d1d..729cc3b633edf 100644 --- a/arch/risc-v/src/eic7700x/Kconfig +++ b/arch/risc-v/src/eic7700x/Kconfig @@ -0,0 +1,23 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +comment "EIC7700X Configuration Options" + +config EIC7700X_CLK + bool "CRG clock provider" + default y + select CLK + ---help--- + Register the Clock and Reset Generator (CRG) clock tree with the + NuttX clock framework. Registration itself writes nothing: the + tree comes up describing the PLL, mux, divider and gate + configuration the boot loader left behind. The tree is visible + through /proc/clk when procfs is enabled. + + A clock moves only when a driver asks it to, by enabling a gate, + setting a divider, or reparenting a mux. A mux that carries a + clock the system is running on will speed up on request but + refuses to slow down, since that would change the timing every + driver downstream was configured for while they are using it. diff --git a/arch/risc-v/src/eic7700x/Make.defs b/arch/risc-v/src/eic7700x/Make.defs index 0180847c90e16..b70ce76c27f86 100644 --- a/arch/risc-v/src/eic7700x/Make.defs +++ b/arch/risc-v/src/eic7700x/Make.defs @@ -30,3 +30,7 @@ HEAD_ASRC = eic7700x_head.S CHIP_CSRCS = eic7700x_start.c eic7700x_irq_dispatch.c eic7700x_irq.c CHIP_CSRCS += eic7700x_timerisr.c eic7700x_allocateheap.c CHIP_CSRCS += eic7700x_mm_init.c eic7700x_pgalloc.c + +ifeq ($(CONFIG_EIC7700X_CLK),y) +CHIP_CSRCS += eic7700x_clk.c eic7700x_clk_ops.c +endif diff --git a/arch/risc-v/src/eic7700x/eic7700x_clk.c b/arch/risc-v/src/eic7700x/eic7700x_clk.c new file mode 100644 index 0000000000000..f84d6f5f60428 --- /dev/null +++ b/arch/risc-v/src/eic7700x/eic7700x_clk.c @@ -0,0 +1,1336 @@ +/**************************************************************************** + * arch/risc-v/src/eic7700x/eic7700x_clk.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "riscv_internal.h" +#include "eic7700x_clk.h" +#include "hardware/eic7700x_clk.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Clock names live in read only memory for the lifetime of the system, so + * the framework is told to reference them rather than copy them. + */ + +#define CLK_STATIC (CLK_NAME_IS_STATIC | CLK_PARENT_NAME_IS_STATIC) + +/* Table entry shorthands. Keeping one clock per source line makes the + * table reviewable against the register descriptions in the TRM. + */ + +#define CLK_FIXED(_name, _rate) \ + { \ + .name = _name, .type = EIC7700X_CLKTYPE_FIXED, \ + .cflags = CLK_STATIC, .arg = _rate \ + } + +#define CLK_PLL(_name, _parent, _base, _out, _lock) \ + { \ + .name = _name, .parent = _parent, .type = EIC7700X_CLKTYPE_PLL, \ + .cflags = CLK_STATIC, .reg = _base, .bit = _out, .arg = _lock \ + } + +#define CLK_FACTOR(_name, _parent, _mult, _div) \ + { \ + .name = _name, .parent = _parent, .type = EIC7700X_CLKTYPE_FACTOR, \ + .cflags = CLK_STATIC, .arg = ((_mult) << 8) | (_div) \ + } + +#define CLK_DIV(_name, _parent, _off, _shift, _width, _lowdiv) \ + { \ + .name = _name, .parent = _parent, .type = EIC7700X_CLKTYPE_DIV, \ + .cflags = CLK_STATIC, .reg = EIC7700X_CLK_BASE + (_off), \ + .shift = _shift, .width = _width, .arg = _lowdiv \ + } + +#define CLK_MUX(_name, _srcs, _tbl, _off, _shift, _width) \ + { \ + .name = _name, .parents = _srcs, .table = _tbl, \ + .nparents = nitems(_srcs), .type = EIC7700X_CLKTYPE_MUX, \ + .cflags = CLK_STATIC, .reg = EIC7700X_CLK_BASE + (_off), \ + .shift = _shift, .width = _width \ + } + +#define CLK_GATE(_name, _parent, _off, _bit, _extra) \ + { \ + .name = _name, .parent = _parent, .type = EIC7700X_CLKTYPE_GATE, \ + .cflags = CLK_STATIC | (_extra), \ + .reg = EIC7700X_CLK_BASE + (_off), .bit = _bit \ + } + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum eic7700x_clktype_e +{ + EIC7700X_CLKTYPE_FIXED = 0, /* Root oscillator or board reference */ + EIC7700X_CLKTYPE_PLL, /* One output of one PLL */ + EIC7700X_CLKTYPE_FACTOR, /* Fixed ratio tap */ + EIC7700X_CLKTYPE_MUX, /* Selector field */ + EIC7700X_CLKTYPE_DIV, /* Divisor field */ + EIC7700X_CLKTYPE_GATE, /* Single enable bit, set means running */ +}; + +/* One row per clock. Rows must appear after every clock they name as a + * parent. Registering in topological order keeps the framework orphan + * list empty, which matters because the orphan rescan in clk_register() + * subscripts parent_names[] with a value read straight out of a mux + * register. + */ + +struct eic7700x_clkdesc_s +{ + FAR const char *name; /* Unique, under 40 characters, no / */ + FAR const char *parent; /* Single parent, NULL when a root */ + FAR const char * const *parents; /* Parent list, mux only */ + FAR const uint8_t *table; /* Selector values, mux only */ + uintptr_t reg; /* Absolute register address */ + uint32_t arg; /* Rate, ratio, lowdiv or lock bit */ + uint8_t type; + uint8_t nparents; + uint8_t cflags; + uint8_t bit; /* Gate bit or PLL output number */ + uint8_t shift; + uint8_t width; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* System configuration root selector (TRM p136). Value 0 routes the + * divided spll0_fout3, value 1 routes the crystal. The reset value is 1; + * the boot loader is what moves it onto the PLL. + */ + +static const char * const g_syscfg_parents[] = +{ + "div_sys_cfg", + "xtal_24m", +}; + +static const uint8_t g_syscfg_table[] = +{ + 0, 1 +}; + +/* U84 core selector (TRM p135). Resets to the crystal, like the rest of + * the root selectors on this SoC. + */ + +static const char * const g_u84_core_parents[] = +{ + "cpupll_fout2", + "clk_u84_core_lp", + "xtal_24m", +}; + +static const uint8_t g_u84_core_table[] = +{ + 0, 1, 2 +}; + +/* U84 bus ratio selector (TRM p135). Unlike the scpu and lpcpu clusters, + * whose ratio bits are documented as always two to one, the U84 bus can + * run at either half the core clock or at the core clock. + */ + +static const char * const g_u84_bus_parents[] = +{ + "clk_u84_core_div2", + "clk_u84_core", +}; + +static const uint8_t g_u84_bus_table[] = +{ + 0, 1 +}; + +/* The boot SPI, SCPU and LPCPU core selectors all choose between their + * own divided PLL output and the crystal, and all three reset to the + * crystal (TRM p100 to p102). + */ + +static const char * const g_bootspi_parents[] = +{ + "div_bootspi", + "xtal_24m", +}; + +static const char * const g_scpu_core_parents[] = +{ + "div_scpu_core", + "xtal_24m", +}; + +static const char * const g_lpcpu_core_parents[] = +{ + "div_lpcpu_core", + "xtal_24m", +}; + +/* SCPU bus ratio: value 0 is two to one, value 1 is one to one */ + +static const char * const g_scpu_bus_parents[] = +{ + "clk_scpu_core_div2", + "clk_scpu_core", +}; + +/* DDR AXI source (TRM p103). Note the parent order is spll2 first here, + * which is the opposite of the video domain selectors. + */ + +static const char * const g_ddr_aclk_parents[] = +{ + "spll2_fout1", + "spll0_fout1", +}; + +static const uint8_t g_sel2_table[] = +{ + 0, 1 +}; + +/* High speed peripheral backup selectors (TRM p110). Value 0 keeps the + * derived clock, value 1 switches to the board supplied LPDDR reference. + */ + +static const char * const g_sata_phy_parents[] = +{ + "div_sata_phy_ref", + "lpddr_ref_bak", +}; + +static const char * const g_eth_core_parents[] = +{ + "div_eth0_core", + "lpddr_ref_bak", +}; + +static const char * const g_rmii_ref_parents[] = +{ + "div_rmii_ref", + "lpddr_ref_bak", +}; + +/* SD and eMMC core selectors (TRM p112) */ + +static const char * const g_mshc_parents[] = +{ + "spll0_fout3", + "spll2_fout3", +}; + +/* Always on DMA selector (TRM p130), which also resets to the crystal */ + +static const char * const g_aondma_parents[] = +{ + "div_aondma_axi", + "xtal_24m", +}; + +/* DSP and die to die AXI sources (TRM p106, p108). spll2 first, as with + * the DDR selector. + */ + +static const char * const g_spll2_spll0_parents[] = +{ + "spll2_fout1", + "spll0_fout1", +}; + +/* Neural processor selectors (TRM p115, p116). Both fields are two bits + * wide but only three encodings are defined, so an unexpected value has + * somewhere safe to land. + */ + +static const char * const g_npu_llc_parents[] = +{ + "div_npu_llc_src0", + "div_npu_llc_src1", + "vpll_fout1", +}; + +static const char * const g_npu_core_parents[] = +{ + "spll1_fout1", + "vpll_fout1", + "spll2_fout2", +}; + +static const uint8_t g_sel3_table[] = +{ + 0, 1, 2 +}; + +/* Video AXI sources (TRM p118, p122). Note the order is the reverse of + * the DDR, DSP and die to die selectors: spll0 is encoding zero here. + */ + +static const char * const g_spll0_spll2_parents[] = +{ + "spll0_fout1", + "spll2_fout1", +}; + +/* Video input pipeline sources (TRM p117 to p119) */ + +static const char * const g_vpll_spll0_parents[] = +{ + "vpll_fout1", + "spll0_fout1", +}; + +/* Video output pixel and audio master sources (TRM p124, p125) */ + +static const char * const g_vo_pixel_parents[] = +{ + "vpll_fout1", + "spll2_fout2", +}; + +static const char * const g_vo_mclk_parents[] = +{ + "clk_vo_mclk_st1", + "ext_mclk", +}; + +/* The clock tree, in topological order. + * + * Rates are never hard coded except for the crystals and the board + * supplied references: everything else is computed from the registers. + * That is not a stylistic choice. Six root selectors on this SoC reset + * to the 24MHz crystal and are switched onto their PLLs by the boot + * loader, so the frequencies quoted in the TRM clock tree describe the + * post boot state rather than what a given board is actually running. + */ + +static const struct eic7700x_clkdesc_s g_eic7700x_clks[] = +{ + /* Roots (TRM p72) */ + + CLK_FIXED("xtal_24m", 24000000), + CLK_FIXED("xtal_32k", 32768), + + /* Backup reference arriving on the LPDDR reference pad. The clock + * tree table leaves its default blank and gives 125MHz as the maximum, + * while the registers that select it call it the 50MHz backup clock. + * It is a board level input, so 50MHz is the sensible assumption and + * boards that drive it differently will need to say so. + */ + + CLK_FIXED("lpddr_ref_bak", 50000000), + + /* External audio master clock arriving on a pad. The manual gives it + * no frequency at all, so it is registered at zero and a board that + * drives it has to say what it drives it with. + */ + + CLK_FIXED("ext_mclk", 0), + + /* PLL outputs (TRM p72, p80 to p99). Documented rates are noted for + * review; the driver computes them rather than trusting them. + */ + + CLK_PLL("spll0_fout1", "xtal_24m", EIC7700X_SPLL0_BASE, 1, + PLL_STATUS_SPLL0_LOCK), /* 1600 MHz */ + CLK_PLL("spll0_fout2", "xtal_24m", EIC7700X_SPLL0_BASE, 2, + PLL_STATUS_SPLL0_LOCK), /* 800 MHz */ + CLK_PLL("spll0_fout3", "xtal_24m", EIC7700X_SPLL0_BASE, 3, + PLL_STATUS_SPLL0_LOCK), /* 400 MHz */ + + CLK_PLL("spll1_fout1", "xtal_24m", EIC7700X_SPLL1_BASE, 1, + PLL_STATUS_SPLL1_LOCK), /* 1500 MHz */ + CLK_PLL("spll1_fout2", "xtal_24m", EIC7700X_SPLL1_BASE, 2, + PLL_STATUS_SPLL1_LOCK), /* 300 MHz */ + CLK_PLL("spll1_fout3", "xtal_24m", EIC7700X_SPLL1_BASE, 3, + PLL_STATUS_SPLL1_LOCK), /* 250 MHz */ + + CLK_PLL("spll2_fout1", "xtal_24m", EIC7700X_SPLL2_BASE, 1, + PLL_STATUS_SPLL2_LOCK), /* 2080 MHz */ + CLK_PLL("spll2_fout2", "xtal_24m", EIC7700X_SPLL2_BASE, 2, + PLL_STATUS_SPLL2_LOCK), /* 1040 MHz */ + CLK_PLL("spll2_fout3", "xtal_24m", EIC7700X_SPLL2_BASE, 3, + PLL_STATUS_SPLL2_LOCK), /* 416 MHz */ + + CLK_PLL("vpll_fout1", "xtal_24m", EIC7700X_VPLL_BASE, 1, + PLL_STATUS_VPLL_LOCK), /* 1188 MHz */ + CLK_PLL("vpll_fout2", "xtal_24m", EIC7700X_VPLL_BASE, 2, + PLL_STATUS_VPLL_LOCK), /* 594 MHz */ + CLK_PLL("vpll_fout3", "xtal_24m", EIC7700X_VPLL_BASE, 3, + PLL_STATUS_VPLL_LOCK), /* 49.5 MHz */ + + CLK_PLL("apll_fout1", "xtal_24m", EIC7700X_APLL_BASE, 1, + PLL_STATUS_APLL_LOCK), /* 983.04 MHz */ + + /* All three CPU PLL outputs are registered even though the output + * enable field only defaults to outputs 1 and 3, because the U84 core + * selector documents its first choice as fout2 (TRM p135). The manual + * does not reconcile the two, so the tree carries all of them and + * leaves it to the enable state to say which are actually running. + */ + + CLK_PLL("cpupll_fout1", "xtal_24m", EIC7700X_CPUPLL_BASE, 1, + PLL_STATUS_CPUPLL_LOCK), + CLK_PLL("cpupll_fout2", "xtal_24m", EIC7700X_CPUPLL_BASE, 2, + PLL_STATUS_CPUPLL_LOCK), + CLK_PLL("cpupll_fout3", "xtal_24m", EIC7700X_CPUPLL_BASE, 3, + PLL_STATUS_CPUPLL_LOCK), + + CLK_PLL("ddrpll_fout1", "xtal_24m", EIC7700X_DDRPLL_BASE, 1, + PLL_STATUS_DDRPLL_LOCK), + CLK_PLL("d2dpll_fout1", "xtal_24m", EIC7700X_D2DPLL_BASE, 1, + PLL_STATUS_D2DPLL_LOCK), + + /* Configuration domain root (TRM p136). clk_sys_cfg is the parent of + * every peripheral pclk on the SoC, including the console UART, so it + * is marked critical. + */ + + CLK_DIV("div_sys_cfg", "spll0_fout3", EIC7700X_SYSCFG_CLK_CTRL, + SYSCFG_DIVSOR_SHIFT, SYSCFG_DIVSOR_WIDTH, 2), + + { + .name = "clk_sys_cfg", + .parents = g_syscfg_parents, + .table = g_syscfg_table, + .nparents = nitems(g_syscfg_parents), + .type = EIC7700X_CLKTYPE_MUX, + .cflags = CLK_STATIC | CLK_IS_CRITICAL, + .reg = EIC7700X_CLK_BASE + EIC7700X_SYSCFG_CLK_CTRL, + .shift = SYSCFG_CLK_SEL_SHIFT, + .width = SYSCFG_CLK_SEL_WIDTH, + }, + + /* Low speed peripheral pclk gates, bank 0 (TRM p134). Every one of + * these hangs off the configuration domain. uart0 is the NuttX console + * on the boards supported so far, so it is marked critical to keep a + * stray disable from taking the console down. + */ + + CLK_GATE("lsp_fan_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_FAN_PCLK, 0), + CLK_GATE("lsp_pvt_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_PVT_PCLK, 0), + + CLK_GATE("lsp_i2c0_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_I2C_PCLK(0), 0), + CLK_GATE("lsp_i2c1_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_I2C_PCLK(1), 0), + CLK_GATE("lsp_i2c2_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_I2C_PCLK(2), 0), + CLK_GATE("lsp_i2c3_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_I2C_PCLK(3), 0), + CLK_GATE("lsp_i2c4_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_I2C_PCLK(4), 0), + CLK_GATE("lsp_i2c5_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_I2C_PCLK(5), 0), + CLK_GATE("lsp_i2c6_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_I2C_PCLK(6), 0), + CLK_GATE("lsp_i2c7_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_I2C_PCLK(7), 0), + CLK_GATE("lsp_i2c8_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_I2C_PCLK(8), 0), + CLK_GATE("lsp_i2c9_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_I2C_PCLK(9), 0), + + /* The other two I2C controllers are in the always on domain and take a + * register each rather than a bit in the LSP bank. They are named for + * the controllers they gate rather than for the registers, which count + * from zero again: i2c10 and i2c11 to the rest of the world. + */ + + CLK_GATE("aon_i2c0_pclk", "clk_sys_cfg", EIC7700X_I2C0_CLK_CTRL, + I2C_CLK_CTRL_PCLKEN, 0), + CLK_GATE("aon_i2c1_pclk", "clk_sys_cfg", EIC7700X_I2C1_CLK_CTRL, + I2C_CLK_CTRL_PCLKEN, 0), + + CLK_GATE("lsp_uart0_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_UART_PCLK(0), CLK_IS_CRITICAL), + CLK_GATE("lsp_uart1_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_UART_PCLK(1), 0), + CLK_GATE("lsp_uart2_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_UART_PCLK(2), 0), + CLK_GATE("lsp_uart3_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_UART_PCLK(3), 0), + CLK_GATE("lsp_uart4_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_UART_PCLK(4), 0), + + CLK_GATE("lsp_timer_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_TIMER_PCLK, 0), + + CLK_GATE("lsp_ssi0_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_SSI_PCLK(0), 0), + CLK_GATE("lsp_ssi1_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_SSI_PCLK(1), 0), + + CLK_GATE("lsp_wdt0_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_WDT_PCLK(0), 0), + CLK_GATE("lsp_wdt1_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_WDT_PCLK(1), 0), + CLK_GATE("lsp_wdt2_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_WDT_PCLK(2), 0), + CLK_GATE("lsp_wdt3_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN0, + LSP_CLK_EN0_WDT_PCLK(3), 0), + + /* Low speed peripheral gates, bank 1 (TRM p135) */ + + CLK_GATE("lsp_mbox0_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(0), 0), + CLK_GATE("lsp_mbox1_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(1), 0), + CLK_GATE("lsp_mbox2_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(2), 0), + CLK_GATE("lsp_mbox3_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(3), 0), + CLK_GATE("lsp_mbox4_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(4), 0), + CLK_GATE("lsp_mbox5_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(5), 0), + CLK_GATE("lsp_mbox6_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(6), 0), + CLK_GATE("lsp_mbox7_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(7), 0), + CLK_GATE("lsp_mbox8_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(8), 0), + CLK_GATE("lsp_mbox9_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(9), 0), + CLK_GATE("lsp_mbox10_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(10), 0), + CLK_GATE("lsp_mbox11_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(11), 0), + CLK_GATE("lsp_mbox12_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(12), 0), + CLK_GATE("lsp_mbox13_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(13), 0), + CLK_GATE("lsp_mbox14_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(14), 0), + CLK_GATE("lsp_mbox15_pclk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_MBOX_PCLK(15), 0), + + CLK_GATE("lsp_pvt_clk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_PVT_CLK(0), 0), + CLK_GATE("ddr_pvt_clk", "clk_sys_cfg", EIC7700X_LSP_CLK_EN1, + LSP_CLK_EN1_PVT_CLK(1), 0), + + /* U84 application cluster (TRM p73, p135). These are the harts NuttX + * runs on, so the cluster clocks are critical. + */ + + CLK_FACTOR("clk_u84_core_lp", "spll0_fout2", 1, 2), /* 400 MHz */ + + { + .name = "clk_u84_core", + .parents = g_u84_core_parents, + .table = g_u84_core_table, + .nparents = nitems(g_u84_core_parents), + .type = EIC7700X_CLKTYPE_MUX, + .cflags = CLK_STATIC | CLK_IS_CRITICAL, + .reg = EIC7700X_CLK_BASE + EIC7700X_U84_CLK_CTRL, + .shift = U84_CORE_CLK_SEL_SHIFT, + .width = U84_CORE_CLK_SEL_WIDTH, + }, + + CLK_FACTOR("clk_u84_core_div2", "clk_u84_core", 1, 2), + + { + .name = "clk_u84_bus", + .parents = g_u84_bus_parents, + .table = g_u84_bus_table, + .nparents = nitems(g_u84_bus_parents), + .type = EIC7700X_CLKTYPE_MUX, + .cflags = CLK_STATIC | CLK_IS_CRITICAL, + .reg = EIC7700X_CLK_BASE + EIC7700X_U84_CLK_CTRL, + .shift = U84_BUSCLK_RATIO_SEL_SHIFT, + .width = U84_BUSCLK_RATIO_SEL_WIDTH, + }, + + CLK_GATE("u84_core0_clk", "clk_u84_core", EIC7700X_U84_CLK_CTRL, + U84_CORE_CLKEN(0), CLK_IS_CRITICAL), + CLK_GATE("u84_core1_clk", "clk_u84_core", EIC7700X_U84_CLK_CTRL, + U84_CORE_CLKEN(1), CLK_IS_CRITICAL), + CLK_GATE("u84_core2_clk", "clk_u84_core", EIC7700X_U84_CLK_CTRL, + U84_CORE_CLKEN(2), CLK_IS_CRITICAL), + CLK_GATE("u84_core3_clk", "clk_u84_core", EIC7700X_U84_CLK_CTRL, + U84_CORE_CLKEN(3), CLK_IS_CRITICAL), + + CLK_GATE("u84_trace0_clk", "clk_u84_bus", EIC7700X_U84_CLK_CTRL, + U84_TRACE_CLKEN(0), 0), + CLK_GATE("u84_trace1_clk", "clk_u84_bus", EIC7700X_U84_CLK_CTRL, + U84_TRACE_CLKEN(1), 0), + CLK_GATE("u84_trace2_clk", "clk_u84_bus", EIC7700X_U84_CLK_CTRL, + U84_TRACE_CLKEN(2), 0), + CLK_GATE("u84_trace3_clk", "clk_u84_bus", EIC7700X_U84_CLK_CTRL, + U84_TRACE_CLKEN(3), 0), + CLK_GATE("u84_trace_com_clk", "clk_u84_bus", EIC7700X_U84_CLK_CTRL, + U84_TRACE_COM_CLKEN, 0), + + /* Real time clock chain (TRM p132). clk_1m is the cpu rtc toggle + * signal the CLINT mtime counter increments from (TRM p251), which + * makes it the source of the NuttX system tick. It is critical for + * that reason. Note the two divisors in this register do not share an + * encoding: rtc_toggle_divsor treats 0 and 1 as bypass while + * aon_rtc_divsor uses the usual divide by two aliases. + */ + + { + .name = "clk_1m", + .parent = "xtal_24m", + .type = EIC7700X_CLKTYPE_DIV, + .cflags = CLK_STATIC | CLK_IS_CRITICAL, + .reg = EIC7700X_CLK_BASE + EIC7700X_RTC_CLK_CTRL, + .shift = RTC_TOGGLE_DIVSOR_SHIFT, + .width = RTC_TOGGLE_DIVSOR_WIDTH, + .arg = 1, + }, + + CLK_DIV("div_clk_rtc", "clk_1m", EIC7700X_RTC_CLK_CTRL, + RTC_AON_DIVSOR_SHIFT, RTC_AON_DIVSOR_WIDTH, 2), + CLK_GATE("clk_rtc", "div_clk_rtc", EIC7700X_RTC_CLK_CTRL, + RTC_CLKEN, 0), + CLK_GATE("clk_rtc_cfg", "clk_sys_cfg", EIC7700X_RTC_CLK_CTRL, + RTC_CFG_CLKEN, 0), + + /* Low speed timers (TRM p131). The counters run straight off the + * crystal; only the register interface uses the configuration domain. + */ + + CLK_GATE("timer0_clk", "xtal_24m", EIC7700X_TIMER_CLK_CTRL, + TIMER_CLK_CLKEN(0), 0), + CLK_GATE("timer1_clk", "xtal_24m", EIC7700X_TIMER_CLK_CTRL, + TIMER_CLK_CLKEN(1), 0), + CLK_GATE("timer2_clk", "xtal_24m", EIC7700X_TIMER_CLK_CTRL, + TIMER_CLK_CLKEN(2), 0), + CLK_GATE("timer3_clk", "xtal_24m", EIC7700X_TIMER_CLK_CTRL, + TIMER_CLK_CLKEN(3), 0), + + CLK_GATE("timer0_pclk", "clk_sys_cfg", EIC7700X_TIMER_CLK_CTRL, + TIMER_PCLK_CLKEN(0), 0), + CLK_GATE("timer1_pclk", "clk_sys_cfg", EIC7700X_TIMER_CLK_CTRL, + TIMER_PCLK_CLKEN(1), 0), + CLK_GATE("timer2_pclk", "clk_sys_cfg", EIC7700X_TIMER_CLK_CTRL, + TIMER_PCLK_CLKEN(2), 0), + CLK_GATE("timer3_pclk", "clk_sys_cfg", EIC7700X_TIMER_CLK_CTRL, + TIMER_PCLK_CLKEN(3), 0), + + CLK_GATE("timer3_clk8", "vpll_fout3", EIC7700X_TIMER_CLK_CTRL, + TIMER3_CLK8_CLKEN, 0), + + /* Network on chip (TRM p99). The watchdog reference comes off the + * crystal: the clock tree table names the configuration domain as its + * source, but that cannot be right because the divisor resets to one + * and the tree itself gives the result as 24MHz. + */ + + CLK_DIV("div_noc_nsp", "spll2_fout1", EIC7700X_NOC_CLK_CTRL, + NOC_NSP_DIVSOR_SHIFT, NOC_NSP_DIVSOR_WIDTH, 2), + CLK_GATE("noc_nsp_clk", "div_noc_nsp", EIC7700X_NOC_CLK_CTRL, + NOC_NSP_CLKEN, CLK_IS_CRITICAL), + CLK_GATE("rnoc_nsp_clk", "div_noc_nsp", EIC7700X_NOC_CLK_CTRL, + NOC_RNOC_NSP_CLKEN, CLK_IS_CRITICAL), + + CLK_DIV("div_noc_wdref", "xtal_24m", EIC7700X_NOC_CLK_CTRL, + NOC_WDREF_DIVSOR_SHIFT, NOC_WDREF_DIVSOR_WIDTH, 1), + CLK_GATE("noc_wd_refclk", "div_noc_wdref", EIC7700X_NOC_CLK_CTRL, + NOC_WDREF_CLKEN, 0), + + /* Boot SPI (TRM p100). The divisor resets to twenty, which against + * the 800MHz parent gives 40MHz rather than the 50MHz the selector + * description quotes. The two statements cannot both hold; the rate + * reported here is whatever the registers actually say. + */ + + CLK_DIV("div_bootspi", "spll0_fout2", EIC7700X_BOOTSPI_CLK_CTRL, + BOOTSPI_DIVSOR_SHIFT, BOOTSPI_DIVSOR_WIDTH, 2), + CLK_MUX("clk_bootspi_st3", g_bootspi_parents, g_sel2_table, + EIC7700X_BOOTSPI_CLK_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_GATE("clk_bootspi", "clk_bootspi_st3", EIC7700X_BOOTSPI_CLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("clk_bootspi_cfg", "clk_sys_cfg", EIC7700X_BOOTSPI_CFG_CTRL, + CLKCTRL_CLKEN, 0), + + /* Secure and low power clusters (TRM p101, p102). Neither runs NuttX, + * so unlike the U84 cluster these are not marked critical. + */ + + CLK_DIV("div_scpu_core", "spll0_fout1", EIC7700X_SCPU_CORE_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_MUX("clk_scpu_core_src", g_scpu_core_parents, g_sel2_table, + EIC7700X_SCPU_CORE_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_GATE("clk_scpu_core", "clk_scpu_core_src", EIC7700X_SCPU_CORE_CTRL, + CLKCTRL_CLKEN, 0), + CLK_FACTOR("clk_scpu_core_div2", "clk_scpu_core", 1, 2), + CLK_MUX("clk_scpu_bus_src", g_scpu_bus_parents, g_sel2_table, + EIC7700X_SCPU_BUS_CTRL, CPU_BUSCLK_SEL_SHIFT, + CPU_BUSCLK_SEL_WIDTH), + CLK_GATE("clk_scpu_bus", "clk_scpu_bus_src", EIC7700X_SCPU_BUS_CTRL, + CLKCTRL_CLKEN, 0), + + CLK_DIV("div_lpcpu_core", "spll0_fout1", EIC7700X_LPCPU_CORE_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_MUX("clk_lpcpu_core_src", g_lpcpu_core_parents, g_sel2_table, + EIC7700X_LPCPU_CORE_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_GATE("clk_lpcpu_core", "clk_lpcpu_core_src", + EIC7700X_LPCPU_CORE_CTRL, CLKCTRL_CLKEN, 0), + + /* The LPCPU bus ratio selector is documented as reserved, so that bus + * is always half the core clock (TRM p102). + */ + + CLK_FACTOR("clk_lpcpu_bus_div", "clk_lpcpu_core", 1, 2), + CLK_GATE("clk_lpcpu_bus", "clk_lpcpu_bus_div", EIC7700X_LPCPU_BUS_CTRL, + CLKCTRL_CLKEN, 0), + + /* DDR controllers (TRM p103). Everything downstream of the AXI clock + * is critical: gating any of it stops main memory. + */ + + { + .name = "clk_ddr_aclk_st0", + .parents = g_ddr_aclk_parents, + .table = g_sel2_table, + .nparents = nitems(g_ddr_aclk_parents), + .type = EIC7700X_CLKTYPE_MUX, + .cflags = CLK_STATIC | CLK_IS_CRITICAL, + .reg = EIC7700X_CLK_BASE + EIC7700X_DDR0_CLK_CTRL, + .shift = DDR_ACLKSRC_SEL_SHIFT, + .width = DDR_ACLKSRC_SEL_WIDTH, + }, + + { + .name = "clk_ddr_aclk", + .parent = "clk_ddr_aclk_st0", + .type = EIC7700X_CLKTYPE_DIV, + .cflags = CLK_STATIC | CLK_IS_CRITICAL, + .reg = EIC7700X_CLK_BASE + EIC7700X_DDR0_CLK_CTRL, + .shift = DDR_ACLK_DIVSOR_SHIFT, + .width = DDR_ACLK_DIVSOR_WIDTH, + .arg = 2, + }, + + CLK_GATE("ddrt_cfg_clk", "clk_sys_cfg", EIC7700X_DDR0_CLK_CTRL, + DDR_CFG_CLKEN, CLK_IS_CRITICAL), + + CLK_GATE("ddrt0_p0_aclk", "clk_ddr_aclk", EIC7700X_DDR0_CLK_CTRL, + DDR_PORT_ACLK_CLKEN(0), CLK_IS_CRITICAL), + CLK_GATE("ddrt0_p1_aclk", "clk_ddr_aclk", EIC7700X_DDR0_CLK_CTRL, + DDR_PORT_ACLK_CLKEN(1), CLK_IS_CRITICAL), + CLK_GATE("ddrt0_p2_aclk", "clk_ddr_aclk", EIC7700X_DDR0_CLK_CTRL, + DDR_PORT_ACLK_CLKEN(2), CLK_IS_CRITICAL), + CLK_GATE("ddrt0_p3_aclk", "clk_ddr_aclk", EIC7700X_DDR0_CLK_CTRL, + DDR_PORT_ACLK_CLKEN(3), CLK_IS_CRITICAL), + CLK_GATE("ddrt0_p4_aclk", "clk_ddr_aclk", EIC7700X_DDR0_CLK_CTRL, + DDR_PORT_ACLK_CLKEN(4), CLK_IS_CRITICAL), + CLK_GATE("ddrt0_trace_aclk", "clk_ddr_aclk", EIC7700X_DDR0_CLK_CTRL, + DDR_TRACE_CLKEN, 0), + + CLK_GATE("ddrt1_p0_aclk", "clk_ddr_aclk", EIC7700X_DDR1_CLK_CTRL, + DDR_PORT_ACLK_CLKEN(0), CLK_IS_CRITICAL), + CLK_GATE("ddrt1_p1_aclk", "clk_ddr_aclk", EIC7700X_DDR1_CLK_CTRL, + DDR_PORT_ACLK_CLKEN(1), CLK_IS_CRITICAL), + CLK_GATE("ddrt1_p2_aclk", "clk_ddr_aclk", EIC7700X_DDR1_CLK_CTRL, + DDR_PORT_ACLK_CLKEN(2), CLK_IS_CRITICAL), + CLK_GATE("ddrt1_p3_aclk", "clk_ddr_aclk", EIC7700X_DDR1_CLK_CTRL, + DDR_PORT_ACLK_CLKEN(3), CLK_IS_CRITICAL), + CLK_GATE("ddrt1_p4_aclk", "clk_ddr_aclk", EIC7700X_DDR1_CLK_CTRL, + DDR_PORT_ACLK_CLKEN(4), CLK_IS_CRITICAL), + CLK_GATE("ddrt1_trace_aclk", "clk_ddr_aclk", EIC7700X_DDR1_CLK_CTRL, + DDR_TRACE_CLKEN, 0), + + /* Translation control unit (TRM p103). Its AXI clock is a tap off the + * DDR AXI clock rather than a domain of its own. + */ + + CLK_GATE("tcu_aclk", "clk_ddr_aclk", EIC7700X_TCU_ACLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("tcu_cfg_clk", "clk_sys_cfg", EIC7700X_TCU_CFG_CTRL, + CLKCTRL_CLKEN, 0), + + /* High speed peripheral fabric (TRM p109). The clock tree names the + * parent of hsp_aclk as hsp_aclk_free, which has no row of its own; + * the divisor here is annotated with that name and its reset value + * against spll0_fout1 gives the documented 800MHz. + */ + + CLK_DIV("div_hsp_aclk", "spll0_fout1", EIC7700X_HSP_ACLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("hsp_aclk", "div_hsp_aclk", EIC7700X_HSP_ACLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("hsp_dma0_clk", "hsp_aclk", EIC7700X_HSP_ACLK_CTRL, + HSP_DMA0_CLKEN, 0), + CLK_GATE("hsp_cfg_clk", "clk_sys_cfg", EIC7700X_HSP_CFG_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("hsp_pclk", "clk_sys_cfg", EIC7700X_HSP_CFG_CTRL, + HSP_PCLK_EN, 0), + + /* SATA reference (TRM p110) */ + + CLK_DIV("div_sata_phy_ref", "spll1_fout2", EIC7700X_SATA_OOB_CTRL, + SATA_PHY_REF_DIVSOR_SHIFT, SATA_PHY_REF_DIVSOR_WIDTH, 2), + CLK_MUX("sata_phy_ref_gated", g_sata_phy_parents, g_sel2_table, + EIC7700X_SATA_OOB_CTRL, SATA_PHY_BAK_SEL_SHIFT, 1), + CLK_GATE("hsp_sata_phy_ref_clk", "sata_phy_ref_gated", + EIC7700X_SATA_OOB_CTRL, SATA_OOB_CLKEN, 0), + + /* Ethernet (TRM p110). The reduced media independent interface + * reference is a fixed sixth of spll1_fout2 rather than a register + * divisor; the clock tree gives its minimum, default and maximum + * divisor all as six. + * + * Only the first controller carries backup selectors. Their field + * names are not indexed, so whether they also steer the second + * controller is unclear; the second is modelled without them. + */ + + CLK_DIV("div_eth0_core", "spll1_fout3", EIC7700X_ETH0_CTRL, + ETH_DIVSOR_SHIFT, ETH_DIVSOR_WIDTH, 2), + CLK_MUX("eth0_core_src", g_eth_core_parents, g_sel2_table, + EIC7700X_ETH0_CTRL, ETH_CORE_BAK_SEL_SHIFT, 1), + CLK_GATE("hsp_eth0_core_clk", "eth0_core_src", EIC7700X_ETH0_CTRL, + ETH_CLK_EN, 0), + + CLK_FACTOR("div_rmii_ref", "spll1_fout2", 1, 6), + CLK_MUX("rmii_ref_clk_mux", g_rmii_ref_parents, g_sel2_table, + EIC7700X_ETH0_CTRL, ETH_RMII_BAK_SEL_SHIFT, 1), + CLK_GATE("rmii0_ref_clk", "rmii_ref_clk_mux", EIC7700X_ETH0_CTRL, + ETH_RMII_REF_CLKEN, 0), + + CLK_DIV("div_eth1_core", "spll1_fout3", EIC7700X_ETH1_CTRL, + ETH_DIVSOR_SHIFT, ETH_DIVSOR_WIDTH, 2), + CLK_GATE("hsp_eth1_core_clk", "div_eth1_core", EIC7700X_ETH1_CTRL, + ETH_CLK_EN, 0), + CLK_GATE("rmii1_ref_clk", "rmii_ref_clk_mux", EIC7700X_ETH1_CTRL, + ETH_RMII_REF_CLKEN, 0), + + /* SD and eMMC controllers (TRM p112) */ + + CLK_MUX("mshc0_core_src", g_mshc_parents, g_sel2_table, + EIC7700X_MSHC0_CORE_CTRL, MSHC_CORE_SEL_SHIFT, 1), + CLK_DIV("div_mshc0_core", "mshc0_core_src", EIC7700X_MSHC0_CORE_CTRL, + MSHC_DIVSOR_SHIFT, MSHC_DIVSOR_WIDTH, 2), + CLK_GATE("hsp_mshc0_core_clk", "div_mshc0_core", + EIC7700X_MSHC0_CORE_CTRL, MSHC_CORE_CLKEN, 0), + + CLK_MUX("mshc1_core_src", g_mshc_parents, g_sel2_table, + EIC7700X_MSHC1_CORE_CTRL, MSHC_CORE_SEL_SHIFT, 1), + CLK_DIV("div_mshc1_core", "mshc1_core_src", EIC7700X_MSHC1_CORE_CTRL, + MSHC_DIVSOR_SHIFT, MSHC_DIVSOR_WIDTH, 2), + CLK_GATE("hsp_mshc1_core_clk", "div_mshc1_core", + EIC7700X_MSHC1_CORE_CTRL, MSHC_CORE_CLKEN, 0), + + CLK_MUX("mshc2_core_src", g_mshc_parents, g_sel2_table, + EIC7700X_MSHC2_CORE_CTRL, MSHC_CORE_SEL_SHIFT, 1), + CLK_DIV("div_mshc2_core", "mshc2_core_src", EIC7700X_MSHC2_CORE_CTRL, + MSHC_DIVSOR_SHIFT, MSHC_DIVSOR_WIDTH, 2), + CLK_GATE("hsp_mshc2_core_clk", "div_mshc2_core", + EIC7700X_MSHC2_CORE_CTRL, MSHC_CORE_CLKEN, 0), + + /* PCI Express (TRM p114) */ + + CLK_DIV("div_pcie_aclk", "spll2_fout2", EIC7700X_PCIE_ACLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("pciet_aclk", "div_pcie_aclk", EIC7700X_PCIE_ACLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("pciet_cfg_clk", "clk_sys_cfg", EIC7700X_PCIE_CFG_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("pciet_aux_clk", "clk_sys_cfg", EIC7700X_PCIE_CFG_CTRL, + PCIE_AUX_CLKEN, 0), + CLK_FACTOR("div_pciet_cr", "clk_sys_cfg", 1, 2), + CLK_GATE("pciet_cr_clk", "div_pciet_cr", EIC7700X_PCIE_CFG_CTRL, + PCIE_CR_CLKEN, 0), + + /* Always on DMA (TRM p130) */ + + CLK_DIV("div_aondma_axi", "spll0_fout1", EIC7700X_AON_DMA_CLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_MUX("clk_aondma_axi_st3", g_aondma_parents, g_sel2_table, + EIC7700X_AON_DMA_CLK_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_GATE("aondma_axi_clk", "clk_aondma_axi_st3", + EIC7700X_AON_DMA_CLK_CTRL, AONDMA_AXI_CLKEN, 0), + CLK_GATE("aondma_cfg_clk", "clk_sys_cfg", EIC7700X_AON_DMA_CLK_CTRL, + AONDMA_CFG_CLKEN, 0), + CLK_GATE("aon_aclk", "clk_aondma_axi_st3", EIC7700X_AON_DMA_CLK_CTRL, + AON_ACLK_CLKEN, 0), + + /* Secure blocks (TRM p133) */ + + CLK_GATE("clk_pka_cfg", "clk_sys_cfg", EIC7700X_PKA_CLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("clk_spacc_cfg", "clk_sys_cfg", EIC7700X_SPACC_CLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_DIV("div_crypto", "spll0_fout1", EIC7700X_SPACC_CLK_CTRL, + CRYPTO_DIVSOR_SHIFT, CRYPTO_DIVSOR_WIDTH, 2), + CLK_GATE("clk_crypto", "div_crypto", EIC7700X_SPACC_CLK_CTRL, + SPACC_CRYPTO_CLKEN, 0), + CLK_GATE("clk_trng_cfg", "clk_sys_cfg", EIC7700X_TRNG_CLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("clk_otp_cfg", "clk_sys_cfg", EIC7700X_OTP_CLK_CTRL, + CLKCTRL_CLKEN, 0), + + /* 3D graphics (TRM p105). The gray clock comes off the crystal rather + * than off the graphics AXI clock. + */ + + CLK_DIV("div_gpu_aclk", "spll0_fout1", EIC7700X_GPU_ACLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("gpu_aclk", "div_gpu_aclk", EIC7700X_GPU_ACLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("gpu_cfg_clk", "clk_sys_cfg", EIC7700X_GPU_CFG_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("gpu_gray_clk", "xtal_24m", EIC7700X_GPU_GRAY_CTRL, + CLKCTRL_CLKEN, 0), + + /* Digital signal processor (TRM p106). clk_dsp_root is the selector + * output ahead of the divisor; the 2D graphics block taps it there + * rather than after the divisor, which is why it is a clock in its own + * right. + */ + + CLK_MUX("clk_dsp_root", g_spll2_spll0_parents, g_sel2_table, + EIC7700X_DSP_ACLK_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_DIV("clk_dsp_aclk_st1", "clk_dsp_root", EIC7700X_DSP_ACLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("dspt_aclk", "clk_dsp_aclk_st1", EIC7700X_DSP_ACLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("dspt_cfg_clk", "clk_sys_cfg", EIC7700X_DSP_CFG_CTRL, + CLKCTRL_CLKEN, 0), + + /* Die to die interface (TRM p108). Its configuration gate is at bit 0 + * rather than the usual bit 31. + */ + + CLK_MUX("clk_d2d_root", g_spll2_spll0_parents, g_sel2_table, + EIC7700X_D2D_ACLK_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_DIV("clk_d2ddr_aclk", "clk_d2d_root", EIC7700X_D2D_ACLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("d2d_aclk", "clk_d2ddr_aclk", EIC7700X_D2D_ACLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("d2d_cfg_clk", "clk_sys_cfg", EIC7700X_D2D_CFG_CTRL, + D2D_CFG_CLKEN, 0), + + /* Neural processor (TRM p115) */ + + CLK_DIV("div_npu_aclk", "spll0_fout1", EIC7700X_NPU_ACLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("npu_aclk", "div_npu_aclk", EIC7700X_NPU_ACLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("npu_cfg_clk", "clk_sys_cfg", EIC7700X_NPU_ACLK_CTRL, + NPU_CFG_CLKEN, 0), + + CLK_DIV("div_npu_llc_src0", "spll0_fout1", EIC7700X_NPU_LLC_CTRL, + NPU_LLC_SRC0_DIVSOR_SHIFT, NPU_LLC_DIVSOR_WIDTH, 2), + CLK_DIV("div_npu_llc_src1", "spll2_fout1", EIC7700X_NPU_LLC_CTRL, + NPU_LLC_SRC1_DIVSOR_SHIFT, NPU_LLC_DIVSOR_WIDTH, 2), + CLK_MUX("npu_llclk_src", g_npu_llc_parents, g_sel3_table, + EIC7700X_NPU_LLC_CTRL, NPU_LLCLK_SEL_SHIFT, NPU_SEL_WIDTH), + CLK_GATE("npu_llc_aclk", "npu_llclk_src", EIC7700X_NPU_LLC_CTRL, + CLKCTRL_CLKEN, 0), + + CLK_MUX("npu_core_src", g_npu_core_parents, g_sel3_table, + EIC7700X_NPU_CORE_CTRL, NPU_CORECLK_SEL_SHIFT, NPU_SEL_WIDTH), + CLK_DIV("div_npu_core", "npu_core_src", EIC7700X_NPU_CORE_CTRL, + NPU_CORECLK_DIVSOR_SHIFT, NPU_DIVSOR_WIDTH, 2), + CLK_GATE("npu_clk", "div_npu_core", EIC7700X_NPU_CORE_CTRL, + CLKCTRL_CLKEN, 0), + + CLK_MUX("npu_e31_src", g_npu_core_parents, g_sel3_table, + EIC7700X_NPU_CORE_CTRL, NPU_E31CLK_SEL_SHIFT, NPU_SEL_WIDTH), + CLK_DIV("div_npu_e31", "npu_e31_src", EIC7700X_NPU_CORE_CTRL, + NPU_E31CLK_DIVSOR_SHIFT, NPU_DIVSOR_WIDTH, 2), + CLK_GATE("npu_e31_clk", "div_npu_e31", EIC7700X_NPU_CORE_CTRL, + NPU_E31_CORE_CLKEN, 0), + + /* MIPI escape clock (TRM p73). A fixed tenth of the configuration + * domain with no register of its own, feeding both display phys. + */ + + CLK_FACTOR("clk_mipi_txesc", "clk_sys_cfg", 1, 10), + + /* Video input (TRM p117). The dewarp, image signal and digital video + * pipelines share a selector layout between the video PLL and spll0. + */ + + CLK_MUX("clk_vi_aclk_root", g_spll0_spll2_parents, g_sel2_table, + EIC7700X_VI_ACLK_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_DIV("clk_vi_aclk_st1", "clk_vi_aclk_root", EIC7700X_VI_ACLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("vi_aclk", "clk_vi_aclk_st1", EIC7700X_VI_ACLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("vi_cfg_clk", "clk_sys_cfg", EIC7700X_VI_ACLK_CTRL, + VI_CFG_CLKEN, 0), + + CLK_MUX("vi_dw_root", g_vpll_spll0_parents, g_sel2_table, + EIC7700X_VI_DW_CLK_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_DIV("div_vi_dw", "vi_dw_root", EIC7700X_VI_DW_CLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("vi_dig_dw_clk", "div_vi_dw", EIC7700X_VI_DW_CLK_CTRL, + CLKCTRL_CLKEN, 0), + + CLK_MUX("vi_dig_isp_root", g_vpll_spll0_parents, g_sel2_table, + EIC7700X_VI_DIG_ISP_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_DIV("div_vi_dig_isp", "vi_dig_isp_root", EIC7700X_VI_DIG_ISP_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("vi_dig_isp_clk", "div_vi_dig_isp", EIC7700X_VI_DIG_ISP_CTRL, + CLKCTRL_CLKEN, 0), + + CLK_MUX("vi_dvp_root", g_vpll_spll0_parents, g_sel2_table, + EIC7700X_VI_DVP_CLK_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_DIV("div_vi_dvp", "vi_dvp_root", EIC7700X_VI_DVP_CLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("vi_dvp_clk", "div_vi_dvp", EIC7700X_VI_DVP_CLK_CTRL, + CLKCTRL_CLKEN, 0), + + /* Camera shutter references, six identical blocks on a four byte + * stride (TRM p120). + */ + + CLK_DIV("div_vi_ref0", "vpll_fout2", EIC7700X_VI_SHUTTER_CTRL(0), + VI_SHUTTER_DIVSOR_SHIFT, VI_SHUTTER_DIVSOR_WIDTH, 2), + CLK_GATE("vi_ref_clk0", "div_vi_ref0", EIC7700X_VI_SHUTTER_CTRL(0), + CLKCTRL_CLKEN, 0), + CLK_DIV("div_vi_ref1", "vpll_fout2", EIC7700X_VI_SHUTTER_CTRL(1), + VI_SHUTTER_DIVSOR_SHIFT, VI_SHUTTER_DIVSOR_WIDTH, 2), + CLK_GATE("vi_ref_clk1", "div_vi_ref1", EIC7700X_VI_SHUTTER_CTRL(1), + CLKCTRL_CLKEN, 0), + CLK_DIV("div_vi_ref2", "vpll_fout2", EIC7700X_VI_SHUTTER_CTRL(2), + VI_SHUTTER_DIVSOR_SHIFT, VI_SHUTTER_DIVSOR_WIDTH, 2), + CLK_GATE("vi_ref_clk2", "div_vi_ref2", EIC7700X_VI_SHUTTER_CTRL(2), + CLKCTRL_CLKEN, 0), + CLK_DIV("div_vi_ref3", "vpll_fout2", EIC7700X_VI_SHUTTER_CTRL(3), + VI_SHUTTER_DIVSOR_SHIFT, VI_SHUTTER_DIVSOR_WIDTH, 2), + CLK_GATE("vi_ref_clk3", "div_vi_ref3", EIC7700X_VI_SHUTTER_CTRL(3), + CLKCTRL_CLKEN, 0), + CLK_DIV("div_vi_ref4", "vpll_fout2", EIC7700X_VI_SHUTTER_CTRL(4), + VI_SHUTTER_DIVSOR_SHIFT, VI_SHUTTER_DIVSOR_WIDTH, 2), + CLK_GATE("vi_ref_clk4", "div_vi_ref4", EIC7700X_VI_SHUTTER_CTRL(4), + CLKCTRL_CLKEN, 0), + CLK_DIV("div_vi_ref5", "vpll_fout2", EIC7700X_VI_SHUTTER_CTRL(5), + VI_SHUTTER_DIVSOR_SHIFT, VI_SHUTTER_DIVSOR_WIDTH, 2), + CLK_GATE("vi_ref_clk5", "div_vi_ref5", EIC7700X_VI_SHUTTER_CTRL(5), + CLKCTRL_CLKEN, 0), + + CLK_GATE("vi_phy_cfg_clk", "clk_sys_cfg", EIC7700X_VI_PHY_CLK_CTRL, + VI_PHYCFG_CLKEN, 0), + CLK_GATE("vi_phy_txclkesc", "clk_mipi_txesc", EIC7700X_VI_PHY_CLK_CTRL, + VI_TXESC_CLKEN, 0), + + /* Video output (TRM p123). The clock tree gives the video input AXI + * clock as the parent of vo_aclk, but this register carries its own + * selector and divisor annotated with vo_aclk, so the tree row looks + * like a copy of the video input one. The registers are used here. + */ + + CLK_MUX("vo_aclk_root", g_spll0_spll2_parents, g_sel2_table, + EIC7700X_VO_ACLK_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_DIV("div_vo_aclk", "vo_aclk_root", EIC7700X_VO_ACLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("vo_aclk", "div_vo_aclk", EIC7700X_VO_ACLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_GATE("vo_cfg_clk", "clk_sys_cfg", EIC7700X_VO_ACLK_CTRL, + VO_CFG_CLKEN, 0), + + CLK_DIV("div_vo_iesm", "spll0_fout3", EIC7700X_VO_IESM_CLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("vo_hdmi_iesmclk", "div_vo_iesm", EIC7700X_VO_IESM_CLK_CTRL, + CLKCTRL_CLKEN, 0), + + CLK_MUX("vo_pixel_root", g_vo_pixel_parents, g_sel2_table, + EIC7700X_VO_PIXEL_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_DIV("div_vo_pixel", "vo_pixel_root", EIC7700X_VO_PIXEL_CTRL, + VO_PIXEL_DIVSOR_SHIFT, VO_PIXEL_DIVSOR_WIDTH, 2), + CLK_GATE("vo_pixel_clk", "div_vo_pixel", EIC7700X_VO_PIXEL_CTRL, + CLKCTRL_CLKEN, 0), + + CLK_DIV("clk_vo_mclk_st1", "apll_fout1", EIC7700X_VO_MCLK_CTRL, + VO_MCLK_DIVSOR_SHIFT, VO_MCLK_DIVSOR_WIDTH, 2), + CLK_MUX("vo_mclk_src", g_vo_mclk_parents, g_sel2_table, + EIC7700X_VO_MCLK_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_GATE("vo_i2s_mclk", "vo_mclk_src", EIC7700X_VO_MCLK_CTRL, + CLKCTRL_CLKEN, 0), + + CLK_DIV("vo_cec_clk", "vpll_fout2", EIC7700X_VO_PHY_CLK_CTRL, + VO_CEC_DIVSOR_SHIFT, VO_CEC_DIVSOR_WIDTH, 2), + CLK_GATE("vo_cr_clk", "clk_mipi_txesc", EIC7700X_VO_PHY_CLK_CTRL, + VO_CR_CLKEN, 0), + CLK_GATE("vo_phy_txclkesc", "clk_mipi_txesc", + EIC7700X_VO_PHY_CLK_CTRL, VO_TXESC_CLKEN, 0), + + /* Video codec (TRM p126). The codec root selector names spll2_fout1 + * in the register description while the clock tree names spll2_fout2; + * the register is followed here, which is also what makes the maximum + * frequency the tree quotes come out right. + */ + + CLK_MUX("vc_aclk_root", g_spll0_spll2_parents, g_sel2_table, + EIC7700X_VC_ACLK_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + CLK_DIV("div_vc_aclk", "vc_aclk_root", EIC7700X_VC_ACLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("vc_aclk", "div_vc_aclk", EIC7700X_VC_ACLK_CTRL, + CLKCTRL_CLKEN, 0), + + CLK_MUX("clk_vc_cdec_root", g_spll0_spll2_parents, g_sel2_table, + EIC7700X_VCDEC_ROOT_CTRL, CLKCTRL_SRC_SEL_SHIFT, + CLKCTRL_SRC_SEL_WIDTH), + + CLK_DIV("div_vc_je", "clk_vc_cdec_root", EIC7700X_JE_CLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("vc_je_clk", "div_vc_je", EIC7700X_JE_CLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_DIV("div_vc_jd", "clk_vc_cdec_root", EIC7700X_JD_CLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("vc_jd_clk", "div_vc_jd", EIC7700X_JD_CLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_DIV("div_vc_vd", "clk_vc_cdec_root", EIC7700X_VD_CLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("vc_vd_clk", "div_vc_vd", EIC7700X_VD_CLK_CTRL, + CLKCTRL_CLKEN, 0), + CLK_DIV("div_vc_ve", "clk_vc_cdec_root", EIC7700X_VE_CLK_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("vc_ve_clk", "div_vc_ve", EIC7700X_VE_CLK_CTRL, + CLKCTRL_CLKEN, 0), + + CLK_GATE("vc_cfg_clk", "clk_sys_cfg", EIC7700X_VC_CLKEN_CTRL, + VC_CFG_CLKEN, 0), + CLK_GATE("vc_jd_pclk", "clk_sys_cfg", EIC7700X_VC_CLKEN_CTRL, + VC_JD_PCLK_EN, 0), + CLK_GATE("vc_je_pclk", "clk_sys_cfg", EIC7700X_VC_CLKEN_CTRL, + VC_JE_PCLK_EN, 0), + CLK_GATE("vc_mon_pclk", "clk_sys_cfg", EIC7700X_VC_CLKEN_CTRL, + VC_MON_PCLK_EN, 0), + CLK_GATE("vc_vd_pclk", "clk_sys_cfg", EIC7700X_VC_CLKEN_CTRL, + VC_VD_PCLK_EN, 0), + CLK_GATE("vc_ve_pclk", "clk_sys_cfg", EIC7700X_VC_CLKEN_CTRL, + VC_VE_PCLK_EN, 0), + + /* 2D graphics (TRM p127), which taps the DSP selector output */ + + CLK_DIV("clk_g2d_st1", "clk_dsp_root", EIC7700X_G2D_CTRL, + CLKCTRL_DIVSOR_SHIFT, CLKCTRL_DIVSOR_WIDTH, 2), + CLK_GATE("g2d_aclk", "clk_g2d_st1", EIC7700X_G2D_CTRL, G2D_ACLKEN, 0), + CLK_GATE("g2d_clk", "clk_g2d_st1", EIC7700X_G2D_CTRL, G2D_CLKEN, 0), + CLK_GATE("g2d_pclk", "clk_sys_cfg", EIC7700X_G2D_CTRL, G2D_PCLK_EN, 0), +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: eic7700x_clk_register_one + * + * Description: + * Register a single clock described by one table row. + * + * Input Parameters: + * desc - The table row to register + * + * Returned Value: + * OK on success, or a negated errno on failure. + * + ****************************************************************************/ + +static int eic7700x_clk_register_one( + FAR const struct eic7700x_clkdesc_s *desc) +{ + struct eic7700x_clk_pll_s pll; + struct eic7700x_clk_div_s divider; + struct eic7700x_clk_mux_s mux; + FAR struct clk_s *clk = NULL; + + switch (desc->type) + { + case EIC7700X_CLKTYPE_FIXED: + clk = clk_register_fixed_rate(desc->name, NULL, desc->cflags, + desc->arg); + break; + + case EIC7700X_CLKTYPE_PLL: + pll.reg = desc->reg; + pll.out = desc->bit; + pll.lockbit = desc->arg; + clk = clk_register(desc->name, &desc->parent, 1, desc->cflags, + &g_eic7700x_clk_pll_ops, &pll, sizeof(pll)); + break; + + case EIC7700X_CLKTYPE_FACTOR: + clk = clk_register_fixed_factor(desc->name, desc->parent, + desc->cflags, + (uint8_t)(desc->arg >> 8), + (uint8_t)(desc->arg & 0xff)); + break; + + case EIC7700X_CLKTYPE_MUX: + mux.reg = desc->reg; + mux.table = desc->table; + mux.shift = desc->shift; + mux.width = desc->width; + mux.nparents = desc->nparents; + clk = clk_register(desc->name, desc->parents, desc->nparents, + desc->cflags, &g_eic7700x_clk_mux_ops, + &mux, sizeof(mux)); + break; + + case EIC7700X_CLKTYPE_DIV: + divider.reg = desc->reg; + divider.shift = desc->shift; + divider.width = desc->width; + divider.lowdiv = desc->arg; + clk = clk_register(desc->name, &desc->parent, 1, desc->cflags, + &g_eic7700x_clk_div_ops, ÷r, + sizeof(divider)); + break; + + case EIC7700X_CLKTYPE_GATE: + clk = clk_register_gate(desc->name, desc->parent, desc->cflags, + desc->reg, desc->bit, 0); + break; + + default: + return -EINVAL; + } + + /* A NULL return also covers a duplicate name, which the framework + * rejects without any other diagnostic. + */ + + if (clk == NULL) + { + syslog(LOG_ERR, "clk: %s: registration failed\n", desc->name); + return -ENOMEM; + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: eic7700x_clk_initialize + * + * Description: + * Register the EIC7700X Clock and Reset Generator clock tree with the + * clock framework. See eic7700x_clk.h. + * + ****************************************************************************/ + +/* What eic7700x_clk_initialize() registered, for the board to report */ + +static unsigned int g_eic7700x_clk_count; +static unsigned int g_eic7700x_clk_failed; + +int eic7700x_clk_initialize(void) +{ + size_t i; + int nfail = 0; + + for (i = 0; i < nitems(g_eic7700x_clks); i++) + { + if (eic7700x_clk_register_one(&g_eic7700x_clks[i]) < 0) + { + nfail++; + } + } + +#ifdef CONFIG_DEBUG_ASSERTIONS + /* A row naming a parent that never registered leaves a permanent + * orphan whose rate silently reads back as zero, so a mistyped parent + * name is worth catching loudly rather than debugging on hardware. + */ + + for (i = 0; i < nitems(g_eic7700x_clks); i++) + { + FAR const struct eic7700x_clkdesc_s *desc = &g_eic7700x_clks[i]; + FAR struct clk_s *clk; + + if (desc->parent == NULL && desc->parents == NULL) + { + continue; + } + + clk = clk_get(desc->name); + if (clk == NULL || clk_get_parent(clk) == NULL) + { + syslog(LOG_ERR, "clk: %s: orphaned, check the parent name\n", + desc->name); + DEBUGPANIC(); + } + } +#endif + + g_eic7700x_clk_count = nitems(g_eic7700x_clks) - nfail; + g_eic7700x_clk_failed = nfail; + + return nfail == 0 ? OK : -EIO; +} + +/**************************************************************************** + * Name: eic7700x_clk_count + * + * Description: + * What eic7700x_clk_initialize() registered. See eic7700x_clk.h. + * + ****************************************************************************/ + +unsigned int eic7700x_clk_count(FAR unsigned int *failed) +{ + if (failed != NULL) + { + *failed = g_eic7700x_clk_failed; + } + + return g_eic7700x_clk_count; +} diff --git a/arch/risc-v/src/eic7700x/eic7700x_clk.h b/arch/risc-v/src/eic7700x/eic7700x_clk.h new file mode 100644 index 0000000000000..6302b39510fcd --- /dev/null +++ b/arch/risc-v/src/eic7700x/eic7700x_clk.h @@ -0,0 +1,149 @@ +/**************************************************************************** + * arch/risc-v/src/eic7700x/eic7700x_clk.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_EIC7700X_EIC7700X_CLK_H +#define __ARCH_RISCV_SRC_EIC7700X_EIC7700X_CLK_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* One output of one PLL. + * + * The voltage controlled oscillator is deliberately not modelled as a + * clock of its own. Clock rates in the framework are uint32_t, and the + * pre divider term FREF * FBDIV can reach roughly 24GHz for the largest + * feedback divider the hardware accepts. Each output therefore reads the + * shared configuration words itself and keeps the intermediate result in + * 64 bit arithmetic. + */ + +struct eic7700x_clk_pll_s +{ + uintptr_t reg; /* Address of the PLL cfg0 register */ + uint8_t out; /* Which output this clock is, 0 to 3 */ + uint8_t lockbit; /* Bit reporting lock in EIC7700X_PLL_STATUS */ +}; + +/* A divisor field in one of the CRG control registers. + * + * Most CRG divisors read "0, 1, 2: 2 divisor ... n: n divisor", so field + * values below two are aliases for divide by two. A few fields instead + * document 0 and 1 as bypass. Both are covered by lowdiv, the effective + * divisor to apply when the field reads 0 or 1. + */ + +struct eic7700x_clk_div_s +{ + uintptr_t reg; /* Address of the control register */ + uint8_t shift; /* Least significant bit of the divisor */ + uint8_t width; /* Width of the divisor in bits */ + uint8_t lowdiv; /* Divisor to use for field values 0 and 1 */ +}; + +/* A selector field in one of the CRG control registers. + * + * The generic clk_mux returns the raw register field as a parent index + * without bounding it against the parent count. Several selectors here + * are two bits wide but only have three valid values, so the table below + * maps register values onto parent indices and anything unexpected is + * reported and folded onto parent zero. + */ + +struct eic7700x_clk_mux_s +{ + uintptr_t reg; /* Address of the control register */ + FAR const uint8_t *table; /* Selector value routing each parent */ + uint8_t shift; /* Least significant bit of the selector */ + uint8_t width; /* Width of the selector in bits */ + uint8_t nparents; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +EXTERN const struct clk_ops_s g_eic7700x_clk_pll_ops; +EXTERN const struct clk_ops_s g_eic7700x_clk_div_ops; +EXTERN const struct clk_ops_s g_eic7700x_clk_mux_ops; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: eic7700x_clk_initialize + * + * Description: + * Register the EIC7700X Clock and Reset Generator clock tree with the + * clock framework. The provider is read only: it reports the PLL, mux, + * divider and gate configuration left behind by the boot loader and + * never reprograms it. + * + * Returned Value: + * OK on success, or a negated errno if any clock failed to register. + * + ****************************************************************************/ + +int eic7700x_clk_initialize(void); + +/**************************************************************************** + * Name: eic7700x_clk_count + * + * Description: + * How many clocks eic7700x_clk_initialize() registered, and how many it + * failed to. Zero before it has run. + * + * Input Parameters: + * failed - Where to report the number that failed, or NULL + * + * Returned Value: + * The number registered. + * + ****************************************************************************/ + +unsigned int eic7700x_clk_count(FAR unsigned int *failed); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __ARCH_RISCV_SRC_EIC7700X_EIC7700X_CLK_H */ diff --git a/arch/risc-v/src/eic7700x/eic7700x_clk_ops.c b/arch/risc-v/src/eic7700x/eic7700x_clk_ops.c new file mode 100644 index 0000000000000..20b425e73cf2c --- /dev/null +++ b/arch/risc-v/src/eic7700x/eic7700x_clk_ops.c @@ -0,0 +1,438 @@ +/**************************************************************************** + * arch/risc-v/src/eic7700x/eic7700x_clk_ops.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include + +#include "riscv_internal.h" +#include "eic7700x_clk.h" +#include "hardware/eic7700x_clk.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define to_clk_pll(_clk) (FAR struct eic7700x_clk_pll_s *)(_clk->private_data) +#define to_clk_div(_clk) (FAR struct eic7700x_clk_div_s *)(_clk->private_data) +#define to_clk_mux(_clk) (FAR struct eic7700x_clk_mux_s *)(_clk->private_data) + +#define FIELD(v, s, w) (((v) >> (s)) & ((1ul << (w)) - 1)) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: eic7700x_clk_pll_recalc_rate + * + * Description: + * Compute one PLL output frequency from the CRG configuration words. + * The TRM (Part 1 section 3.2.3.3.2, p78) gives two formulas: + * + * integer FOUT = FREF * FBDIV + * / (4 * REFDIV * (POSTDIV_A + 1) * (POSTDIV_B + 1)) + * fractional FOUT = FREF * (FBDIV + FRAC / 2^24) + * / (4 * REFDIV * (POSTDIV_A + 1) * (POSTDIV_B + 1)) + * + * Only one expression is needed because FRAC is forced to zero when the + * PLL is in integer mode, which collapses the second formula onto the + * first. All arithmetic is done in 64 bits: the numerator reaches + * roughly 1.7e18 for the largest dividers the fields can hold, far + * beyond the uint32_t that carries rates through the framework. + * + * Input Parameters: + * clk - The clock being queried + * parent_rate - Frequency of the PLL reference clock + * + * Returned Value: + * The output frequency in Hz, or zero if the configuration is not + * representable. + * + ****************************************************************************/ + +static uint32_t eic7700x_clk_pll_recalc_rate(FAR struct clk_s *clk, + uint32_t parent_rate) +{ + FAR struct eic7700x_clk_pll_s *pll = to_clk_pll(clk); + uint32_t cfg0; + uint32_t cfg1; + uint32_t cfg2; + uint32_t fbdiv; + uint32_t refdiv; + uint32_t frac; + uint32_t posta; + uint32_t postb; + uint64_t num; + uint64_t den; + + cfg0 = getreg32(pll->reg + EIC7700X_PLL_CFG0); + cfg1 = getreg32(pll->reg + EIC7700X_PLL_CFG1); + cfg2 = getreg32(pll->reg + EIC7700X_PLL_CFG2); + + /* A bypassed output passes the reference clock through untouched */ + + if ((cfg0 & (1ul << (PLL_CFG0_BYP_SHIFT + pll->out))) != 0) + { + return parent_rate; + } + + fbdiv = FIELD(cfg0, PLL_CFG0_FBDIV_SHIFT, 12); + refdiv = FIELD(cfg0, PLL_CFG0_REFDIV_SHIFT, 6); + posta = FIELD(cfg2, PLL_CFG2_POSTDIV_A_SHIFT(pll->out), + PLL_CFG2_POSTDIV_WIDTH); + postb = FIELD(cfg2, PLL_CFG2_POSTDIV_B_SHIFT(pll->out), + PLL_CFG2_POSTDIV_WIDTH); + + frac = 0; + if ((cfg0 & PLL_CFG0_DSMEN) != 0) + { + frac = FIELD(cfg1, PLL_CFG1_FRAC_SHIFT, PLL_CFG1_FRAC_BITS); + } + + if (refdiv == 0 || parent_rate == 0) + { + return 0; + } + + num = (uint64_t)parent_rate * + (((uint64_t)fbdiv << PLL_CFG1_FRAC_BITS) + frac); + den = ((uint64_t)4 * refdiv * (posta + 1) * (postb + 1)) << + PLL_CFG1_FRAC_BITS; + + num = (num + den / 2) / den; + + if (num > UINT32_MAX) + { + clkerr("%s: rate %" PRIu64 " out of range\n", clk->name, num); + return 0; + } + + return (uint32_t)num; +} + +/**************************************************************************** + * Name: eic7700x_clk_pll_is_enabled + * + * Description: + * Report whether a PLL output is running. All three conditions must + * hold: the PLL is enabled, this particular output is enabled, and the + * PLL reports lock. + * + ****************************************************************************/ + +static int eic7700x_clk_pll_is_enabled(FAR struct clk_s *clk) +{ + FAR struct eic7700x_clk_pll_s *pll = to_clk_pll(clk); + uint32_t cfg0 = getreg32(pll->reg + EIC7700X_PLL_CFG0); + uint32_t cfg1 = getreg32(pll->reg + EIC7700X_PLL_CFG1); + uint32_t status = getreg32(EIC7700X_PLL_STATUS); + + return (cfg0 & PLL_CFG0_EN) != 0 && + (cfg1 & (1ul << (PLL_CFG1_FOUTEN_SHIFT + pll->out))) != 0 && + (status & (1ul << pll->lockbit)) != 0; +} + +/**************************************************************************** + * Name: eic7700x_clk_div_recalc_rate + * + * Description: + * Read a CRG divisor field and divide the parent rate by it. Field + * values below two are aliases handled by the lowdiv member; see the + * comment on struct eic7700x_clk_div_s. + * + ****************************************************************************/ + +static uint32_t eic7700x_clk_div_recalc_rate(FAR struct clk_s *clk, + uint32_t parent_rate) +{ + FAR struct eic7700x_clk_div_s *divider = to_clk_div(clk); + uint32_t val; + uint32_t div; + + val = FIELD(getreg32(divider->reg), divider->shift, divider->width); + div = val < 2 ? divider->lowdiv : val; + + if (div == 0) + { + return parent_rate; + } + + return (uint32_t)(((uint64_t)parent_rate + div / 2) / div); +} + +/**************************************************************************** + * Name: eic7700x_clk_div_divisor + * + * Description: + * Which divisor this field would have to hold to bring the parent rate + * down to the one asked for, clamped to what the field can express. + * + * Rounded up rather than to nearest, so that the result is never faster + * than what was asked for. A consumer asking for a rate is usually + * asking for a limit: the ethernet controllers ask for the transmit + * clock a link speed needs, and being under it is recoverable in a way + * that being over it is not. + * + ****************************************************************************/ + +static uint32_t eic7700x_clk_div_divisor(FAR struct eic7700x_clk_div_s *div, + uint32_t parent_rate, uint32_t rate) +{ + uint32_t maxdiv = (1ul << div->width) - 1; + uint32_t divisor; + + if (rate == 0) + { + return maxdiv; + } + + divisor = (parent_rate + rate - 1) / rate; + + /* Field values below two are aliases for one divisor, so nothing between + * that alias and two can be asked for. + */ + + if (divisor < div->lowdiv) + { + divisor = div->lowdiv; + } + + return divisor > maxdiv ? maxdiv : divisor; +} + +/**************************************************************************** + * Name: eic7700x_clk_div_round_rate + * + * Description: + * The rate this divider would actually produce if asked for this one. + * + ****************************************************************************/ + +static uint32_t eic7700x_clk_div_round_rate(FAR struct clk_s *clk, + uint32_t rate, + FAR uint32_t *parent_rate) +{ + FAR struct eic7700x_clk_div_s *divider = to_clk_div(clk); + uint32_t divisor; + + divisor = eic7700x_clk_div_divisor(divider, *parent_rate, rate); + return (uint32_t)(((uint64_t)*parent_rate + divisor / 2) / divisor); +} + +/**************************************************************************** + * Name: eic7700x_clk_div_set_rate + * + * Description: + * Write a CRG divisor field. This is the one place this provider + * changes the hardware rather than reporting it, and it is deliberately + * the only one: a divider belongs to a single peripheral, so writing it + * affects only the driver that asked, whereas the muxes and the PLLs are + * shared and reprogramming one underneath its other users would be a + * different and much less local kind of change. + * + * Read modify write, because most of these registers carry a gate, a + * selector or a second peripheral's divisor in the same word. + * + ****************************************************************************/ + +static int eic7700x_clk_div_set_rate(FAR struct clk_s *clk, uint32_t rate, + uint32_t parent_rate) +{ + FAR struct eic7700x_clk_div_s *divider = to_clk_div(clk); + uint32_t mask = ((1ul << divider->width) - 1) << divider->shift; + uint32_t divisor; + uint32_t regval; + + divisor = eic7700x_clk_div_divisor(divider, parent_rate, rate); + + regval = getreg32(divider->reg); + regval &= ~mask; + regval |= (divisor << divider->shift) & mask; + putreg32(regval, divider->reg); + + clkinfo("%s: %" PRIu32 " / %" PRIu32 " for %" PRIu32 "\n", + clk->name, parent_rate, divisor, rate); + return 0; +} + +/**************************************************************************** + * Name: eic7700x_clk_mux_get_parent + * + * Description: + * Map a CRG selector field onto a parent index. Unlike the generic + * clk_mux this never returns an index beyond the parent count: the core + * uses the result to subscript parent_names[] without checking it, and + * several selectors are two bits wide with only three valid values. + * + ****************************************************************************/ + +static uint8_t eic7700x_clk_mux_get_parent(FAR struct clk_s *clk) +{ + FAR struct eic7700x_clk_mux_s *mux = to_clk_mux(clk); + uint32_t val; + uint8_t i; + + val = FIELD(getreg32(mux->reg), mux->shift, mux->width); + + for (i = 0; i < mux->nparents; i++) + { + if (mux->table[i] == val) + { + return i; + } + } + + clkwarn("%s: unexpected selector %" PRIu32 ", using parent 0\n", + clk->name, val); + return 0; +} + +/**************************************************************************** + * Name: eic7700x_clk_mux_set_parent + * + * Description: + * Point a mux at one of its parents, the write side of the read above. + * + * This exists because the boot loader's configuration cannot be taken on + * trust. Several muxes on this chip reset to a slow or idle parent and + * reach their working one only because firmware moved them, and until + * now the tree could observe that and not correct it, which left any + * peripheral the boot loader had not set up stuck that way. + * + * A clock the system itself runs on is only allowed to speed up. Going + * the other way pulls the ground out from under whatever is executing: + * the timing every downstream driver was configured for changes while + * they are using it. Recovering a mux that came up on the wrong parent + * is always the raising direction, so refusing the other one costs + * nothing and is the same bargain the reset driver strikes, where a line + * that would take the system down may be released and read but not + * asserted. + * + * Input Parameters: + * clk - The mux + * index - Which of its parents to select + * + * Returned Value: + * Zero on success, or a negated errno: + * + * -EINVAL the index names no parent of this mux + * -EPERM the switch would slow a clock the system is running on + * -EIO the selector did not read back as written + * + ****************************************************************************/ + +static int eic7700x_clk_mux_set_parent(FAR struct clk_s *clk, uint8_t index) +{ + FAR struct eic7700x_clk_mux_s *mux = to_clk_mux(clk); + uint32_t mask; + uint32_t regval; + + if (index >= mux->nparents) + { + return -EINVAL; + } + + if ((clk->flags & CLK_IS_CRITICAL) != 0) + { + FAR struct clk_s *target = clk_get_parent_by_index(clk, index); + uint32_t now = clk_get_rate(clk); + + if (target == NULL) + { + return -EINVAL; + } + + if (clk_get_rate(target) < now) + { + clkerr("%s: refusing %" PRIu32 " to %" PRIu32 + ", the system is running on it\n", + clk->name, now, clk_get_rate(target)); + return -EPERM; + } + } + + mask = ((1ul << mux->width) - 1) << mux->shift; + regval = getreg32(mux->reg); + regval &= ~mask; + regval |= ((uint32_t)mux->table[index] << mux->shift) & mask; + putreg32(regval, mux->reg); + + /* Read it back. Some of these selectors are held by hardware that is + * not ready to switch, and a mux that quietly ignored the write would + * otherwise leave the tree describing a parent the clock does not have. + */ + + if (FIELD(getreg32(mux->reg), mux->shift, mux->width) != mux->table[index]) + { + clkerr("%s: selector will not move to %u\n", clk->name, + mux->table[index]); + return -EIO; + } + + clkinfo("%s: now on parent %u\n", clk->name, index); + return 0; +} + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* The PLL and divider clocks report a rate; the mux has no recalc_rate so + * the core passes the selected parent rate through unchanged, which is + * exactly what a selector does. + * + * Only the divider can be written, and only its divisor field. A divider + * in this block feeds one peripheral, so a driver that sets one is + * changing its own clock and nobody else's; the PLLs and the selectors are + * shared, and moving one under its other users would be a different kind + * of change entirely. So those two stay read only, and the gates are left + * to the generic gate implementation the tree already provides. + */ + +const struct clk_ops_s g_eic7700x_clk_pll_ops = +{ + .recalc_rate = eic7700x_clk_pll_recalc_rate, + .is_enabled = eic7700x_clk_pll_is_enabled, +}; + +const struct clk_ops_s g_eic7700x_clk_div_ops = +{ + .recalc_rate = eic7700x_clk_div_recalc_rate, + .round_rate = eic7700x_clk_div_round_rate, + .set_rate = eic7700x_clk_div_set_rate, +}; + +const struct clk_ops_s g_eic7700x_clk_mux_ops = +{ + .get_parent = eic7700x_clk_mux_get_parent, + .set_parent = eic7700x_clk_mux_set_parent, +}; diff --git a/arch/risc-v/src/eic7700x/eic7700x_start.c b/arch/risc-v/src/eic7700x/eic7700x_start.c index 8c55cf3538270..dbad4c4949fb1 100644 --- a/arch/risc-v/src/eic7700x/eic7700x_start.c +++ b/arch/risc-v/src/eic7700x/eic7700x_start.c @@ -39,6 +39,9 @@ #include "chip.h" #include "eic7700x_mm_init.h" #include "eic7700x_memorymap.h" +#ifdef CONFIG_EIC7700X_CLK +# include "eic7700x_clk.h" +#endif /**************************************************************************** * Pre-processor Definitions @@ -429,3 +432,25 @@ void riscv_serialinit(void) { u16550_serialinit(); } + +/**************************************************************************** + * Name: riscv_soc_initialize + * + * Description: + * SoC specific initialization, called from up_initialize() once the heap + * and the serial driver are available. + * + * This has to live in a file the linker is already pulling out of the + * arch library. up_initialize() reaches it through a weak undefined + * reference, and the link uses neither --whole-archive nor a strong + * reference elsewhere, so a member whose only symbol is this function + * would never be extracted and the body would silently never run. + * + ****************************************************************************/ + +void weak_function riscv_soc_initialize(void) +{ +#ifdef CONFIG_EIC7700X_CLK + eic7700x_clk_initialize(); +#endif +} diff --git a/arch/risc-v/src/eic7700x/hardware/eic7700x_clk.h b/arch/risc-v/src/eic7700x/hardware/eic7700x_clk.h new file mode 100644 index 0000000000000..ab602c897c976 --- /dev/null +++ b/arch/risc-v/src/eic7700x/hardware/eic7700x_clk.h @@ -0,0 +1,424 @@ +/**************************************************************************** + * arch/risc-v/src/eic7700x/hardware/eic7700x_clk.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_EIC7700X_HARDWARE_EIC7700X_CLK_H +#define __ARCH_RISCV_SRC_EIC7700X_HARDWARE_EIC7700X_CLK_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register definitions transcribed from the EIC7700X SoC Technical + * Reference Manual v1.0.0, Part 1 section 3.2 "clock". Page numbers in + * the comments below are PDF page numbers of Part 1; the printed page + * number is the PDF page number minus 16. + * + * The Clock and Reset Generator (CRG) occupies 0x51828000..0x5182ffff + * (TRM p70, p80). Only the clock half is described here; the reset + * controller at offsets 0x300..0x4f0 is deliberately omitted. + */ + +/* Clock and Reset Generator base address ***********************************/ + +#define EIC7700X_CLK_BASE 0x51828000ul + +/* PLL registers ************************************************************/ + +/* Each PLL owns five consecutive words. The offsets below locate the + * first word of each block; add the EIC7700X_PLL_CFG* offsets to reach + * the individual registers. Note that the d2d PLL sits outside the + * regular 0x14 stride (TRM p97). + */ + +#define EIC7700X_SPLL0_BASE (EIC7700X_CLK_BASE + 0x0000) /* p80 */ +#define EIC7700X_SPLL1_BASE (EIC7700X_CLK_BASE + 0x0014) /* p82 */ +#define EIC7700X_SPLL2_BASE (EIC7700X_CLK_BASE + 0x0028) /* p84 */ +#define EIC7700X_VPLL_BASE (EIC7700X_CLK_BASE + 0x003c) /* p86 */ +#define EIC7700X_APLL_BASE (EIC7700X_CLK_BASE + 0x0050) /* p89 */ +#define EIC7700X_CPUPLL_BASE (EIC7700X_CLK_BASE + 0x0064) /* p91 */ +#define EIC7700X_DDRPLL_BASE (EIC7700X_CLK_BASE + 0x0078) /* p93 */ +#define EIC7700X_D2DPLL_BASE (EIC7700X_CLK_BASE + 0x00b0) /* p97 */ + +#define EIC7700X_PLL_CFG0 0x00 /* Feedback and reference div */ +#define EIC7700X_PLL_CFG1 0x04 /* Fractional part, fout en */ +#define EIC7700X_PLL_CFG2 0x08 /* Per output post dividers */ +#define EIC7700X_PLL_DSKEWCAL 0x0c /* Deskew calibration */ +#define EIC7700X_PLL_SSC 0x10 /* Spread spectrum modulator */ + +/* PLL configuration word 0 (TRM p80) */ + +#define PLL_CFG0_EN (1 << 0) /* PLL enable */ +#define PLL_CFG0_FREFCMLEN (1 << 1) /* Ref select, do not change */ +#define PLL_CFG0_BYP_SHIFT (4) /* One bypass bit per output */ +#define PLL_CFG0_BYP_MASK (0xf << PLL_CFG0_BYP_SHIFT) +#define PLL_CFG0_DACEN (1 << 8) /* Fractional noise cancel */ +#define PLL_CFG0_DSMEN (1 << 9) /* 0: integer, 1: fractional */ +#define PLL_CFG0_REFDIV_SHIFT (12) /* Reference divider */ +#define PLL_CFG0_REFDIV_MASK (0x3f << PLL_CFG0_REFDIV_SHIFT) +#define PLL_CFG0_FBDIV_SHIFT (20) /* Feedback divider */ +#define PLL_CFG0_FBDIV_MASK (0xfff << PLL_CFG0_FBDIV_SHIFT) + +/* PLL configuration word 1 (TRM p80) */ + +#define PLL_CFG1_FOUTEN_SHIFT (0) /* One enable bit per output */ +#define PLL_CFG1_FOUTEN_MASK (0xf << PLL_CFG1_FOUTEN_SHIFT) +#define PLL_CFG1_FRAC_SHIFT (4) /* 24 bit fractional part */ +#define PLL_CFG1_FRAC_MASK (0xfffffful << PLL_CFG1_FRAC_SHIFT) +#define PLL_CFG1_FRAC_BITS (24) + +/* PLL configuration word 2 (TRM p81). Every output n has its own pair of + * three bit post dividers, postdiv_a and postdiv_b. The effective + * divisor is (postdiv_a + 1) * (postdiv_b + 1); the TRM notes that + * POSTDIV_A should be bigger than POSTDIV_B. + * + * The groups are not evenly spaced, and the exception is easy to miss. + * Only group zero carries a pre bit, at bit 0, and only group zero is + * therefore pushed up by one: + * + * postdiv0_pre 0 postdiv0_a 3:1 postdiv0_b 6:4 + * postdiv1_a 10:8 postdiv1_b 14:12 + * postdiv2_a 18:16 postdiv2_b 22:20 + * postdiv3_a 26:24 postdiv3_b 30:28 + * + * Reading groups one and above as though they were also offset by the pre + * bit takes each field one bit too high, which quietly halves or doubles + * an output rather than failing. Linux agrees with the layout above: + * drivers/clk/eswin/clk.c places postdiv1 at GENMASK(10, 8) and postdiv2 + * at GENMASK(18, 16). + */ + +#define PLL_CFG2_POSTDIV_PRE0 (1 << 0) +#define PLL_CFG2_POSTDIV_WIDTH (3) +#define PLL_CFG2_POSTDIV_A_SHIFT(n) ((n) == 0 ? 1 : 8 * (n)) +#define PLL_CFG2_POSTDIV_B_SHIFT(n) ((n) == 0 ? 4 : 8 * (n) + 4) + +/* PLL lock status, one bit per PLL, active high (TRM p96) */ + +#define EIC7700X_PLL_STATUS (EIC7700X_CLK_BASE + 0x00a4) + +#define PLL_STATUS_SPLL0_LOCK (0) +#define PLL_STATUS_SPLL1_LOCK (1) +#define PLL_STATUS_SPLL2_LOCK (2) +#define PLL_STATUS_VPLL_LOCK (3) +#define PLL_STATUS_APLL_LOCK (4) +#define PLL_STATUS_CPUPLL_LOCK (5) +#define PLL_STATUS_DDRPLL_LOCK (6) +#define PLL_STATUS_D2DPLL_LOCK (7) + +/* Peripheral clock control registers ***************************************/ + +/* Offsets from EIC7700X_CLK_BASE. The house layout puts gate enables at + * the top of the word counting down from bit 31, the divisor in a field + * starting at bit 4, and the mux selector at bit 0. + */ + +#define EIC7700X_NOC_CLK_CTRL 0x0100 /* NOC clocks TRM p99 */ +#define EIC7700X_BOOTSPI_CLK_CTRL 0x0104 /* Boot SPI TRM p100 */ +#define EIC7700X_BOOTSPI_CFG_CTRL 0x0108 /* Boot SPI config TRM p101 */ +#define EIC7700X_SCPU_CORE_CTRL 0x010c /* SCPU core TRM p101 */ +#define EIC7700X_SCPU_BUS_CTRL 0x0110 /* SCPU bus TRM p101 */ +#define EIC7700X_LPCPU_CORE_CTRL 0x0114 /* LPCPU core TRM p102 */ +#define EIC7700X_LPCPU_BUS_CTRL 0x0118 /* LPCPU bus TRM p102 */ +#define EIC7700X_TCU_ACLK_CTRL 0x011c /* TCU AXI TRM p103 */ +#define EIC7700X_TCU_CFG_CTRL 0x0120 /* TCU config TRM p103 */ +#define EIC7700X_DDR0_CLK_CTRL 0x0124 /* DDR controller 0 TRM p103 */ +#define EIC7700X_DDR1_CLK_CTRL 0x0128 /* DDR controller 1 TRM p104 */ +#define EIC7700X_GPU_ACLK_CTRL 0x012c /* GPU AXI TRM p105 */ +#define EIC7700X_GPU_CFG_CTRL 0x0130 /* GPU config TRM p105 */ +#define EIC7700X_GPU_GRAY_CTRL 0x0134 /* GPU gray TRM p106 */ +#define EIC7700X_DSP_ACLK_CTRL 0x0138 /* DSP AXI TRM p106 */ +#define EIC7700X_DSP_CFG_CTRL 0x013c /* DSP config TRM p106 */ +#define EIC7700X_D2D_ACLK_CTRL 0x0140 /* Die to die AXI TRM p108 */ +#define EIC7700X_D2D_CFG_CTRL 0x0144 /* Die to die config TRM p108 */ +#define EIC7700X_NPU_ACLK_CTRL 0x0178 /* NPU AXI TRM p115 */ +#define EIC7700X_NPU_LLC_CTRL 0x017c /* NPU last level $ TRM p115 */ +#define EIC7700X_NPU_CORE_CTRL 0x0180 /* NPU core and E31 TRM p116 */ +#define EIC7700X_VI_DW_CLK_CTRL 0x0184 /* VI dewarp TRM p117 */ +#define EIC7700X_VI_ACLK_CTRL 0x0188 /* VI AXI TRM p118 */ +#define EIC7700X_VI_DIG_ISP_CTRL 0x018c /* VI image signal TRM p118 */ +#define EIC7700X_VI_DVP_CLK_CTRL 0x0190 /* VI digital video TRM p119 */ + +/* Six camera shutter blocks on a four byte stride (TRM p120) */ + +#define EIC7700X_VI_SHUTTER_CTRL(n) (0x0194 + 4 * (n)) + +#define EIC7700X_VI_PHY_CLK_CTRL 0x01ac /* VI phy TRM p123 */ +#define EIC7700X_VO_ACLK_CTRL 0x01b0 /* VO AXI TRM p123 */ +#define EIC7700X_VO_IESM_CLK_CTRL 0x01b4 /* VO HDMI iesm TRM p124 */ +#define EIC7700X_VO_PIXEL_CTRL 0x01b8 /* VO pixel TRM p124 */ +#define EIC7700X_VO_MCLK_CTRL 0x01bc /* VO audio master TRM p125 */ +#define EIC7700X_VO_PHY_CLK_CTRL 0x01c0 /* VO phy and CEC TRM p125 */ +#define EIC7700X_VC_ACLK_CTRL 0x01c4 /* Video codec AXI TRM p126 */ +#define EIC7700X_VCDEC_ROOT_CTRL 0x01c8 /* Codec root select TRM p127 */ +#define EIC7700X_G2D_CTRL 0x01cc /* 2D graphics TRM p127 */ +#define EIC7700X_VC_CLKEN_CTRL 0x01d0 /* Codec pclk gates TRM p128 */ +#define EIC7700X_JE_CLK_CTRL 0x01d4 /* JPEG encoder TRM p128 */ +#define EIC7700X_JD_CLK_CTRL 0x01d8 /* JPEG decoder TRM p129 */ +#define EIC7700X_VD_CLK_CTRL 0x01dc /* Video decoder TRM p129 */ +#define EIC7700X_VE_CLK_CTRL 0x01e0 /* Video encoder TRM p130 */ +#define EIC7700X_HSP_ACLK_CTRL 0x0148 /* HSP AXI TRM p109 */ +#define EIC7700X_HSP_CFG_CTRL 0x014c /* HSP config TRM p109 */ +#define EIC7700X_SATA_RBC_CTRL 0x0150 /* SATA rbc TRM p110 */ +#define EIC7700X_SATA_OOB_CTRL 0x0154 /* SATA oob and phy TRM p110 */ +#define EIC7700X_ETH0_CTRL 0x0158 /* Ethernet 0 TRM p110 */ +#define EIC7700X_ETH1_CTRL 0x015c /* Ethernet 1 TRM p111 */ +#define EIC7700X_MSHC0_CORE_CTRL 0x0160 /* SD/eMMC 0 TRM p112 */ +#define EIC7700X_MSHC1_CORE_CTRL 0x0164 /* SD/eMMC 1 TRM p112 */ +#define EIC7700X_MSHC2_CORE_CTRL 0x0168 /* SD/eMMC 2 TRM p113 */ +#define EIC7700X_PCIE_ACLK_CTRL 0x0170 /* PCIe AXI TRM p114 */ +#define EIC7700X_PCIE_CFG_CTRL 0x0174 /* PCIe config TRM p114 */ +#define EIC7700X_PKA_CLK_CTRL 0x01f0 /* Public key accel TRM p133 */ +#define EIC7700X_SPACC_CLK_CTRL 0x01f4 /* Crypto TRM p133 */ +#define EIC7700X_TRNG_CLK_CTRL 0x01f8 /* Random generator TRM p133 */ +#define EIC7700X_OTP_CLK_CTRL 0x01fc /* One time program TRM p134 */ +#define EIC7700X_AON_DMA_CLK_CTRL 0x01e4 /* Always on DMA TRM p130 */ +#define EIC7700X_TIMER_CLK_CTRL 0x01e8 /* LSP timers TRM p131 */ +#define EIC7700X_RTC_CLK_CTRL 0x01ec /* RTC and mtime TRM p132 */ +#define EIC7700X_LSP_CLK_EN0 0x0200 /* LSP gates bank 0 TRM p134 */ +#define EIC7700X_LSP_CLK_EN1 0x0204 /* LSP gates bank 1 TRM p135 */ +#define EIC7700X_U84_CLK_CTRL 0x0208 /* U84 cluster TRM p135 */ +#define EIC7700X_SYSCFG_CLK_CTRL 0x020c /* APB config root TRM p136 */ + +/* The two always on I2C controllers get a register each rather than a bit + * in a bank, and the numbering inverts: the register called i2c0 gates the + * controller the manual and the device tree call i2c10, and i2c1 gates + * i2c11. The inversion runs through the resets and the low power core's + * interrupt list as well, so it is the naming to expect rather than a + * transcription error here. + */ + +#define EIC7700X_I2C0_CLK_CTRL 0x0210 /* i2c10 pclk TRM p136 */ +#define EIC7700X_I2C1_CLK_CTRL 0x0214 /* i2c11 pclk TRM p136 */ + +/* NOC clock control (TRM p99). Unlike most CRG divisors, the watchdog + * reference divisor documents 0 and 1 as no divisor rather than as + * divide by two. + */ + +#define NOC_NSP_DIVSOR_SHIFT (0) +#define NOC_NSP_DIVSOR_WIDTH (3) +#define NOC_WDREF_DIVSOR_SHIFT (4) +#define NOC_WDREF_DIVSOR_WIDTH (16) +#define NOC_RNOC_NSP_CLKEN (29) +#define NOC_WDREF_CLKEN (30) +#define NOC_NSP_CLKEN (31) + +/* Boot SPI (TRM p100), SCPU (TRM p101) and LPCPU (TRM p102). These three + * share the CRG house layout: a divisor at [7:4], a selector at bit 0 + * choosing between the divided PLL output and the crystal, and a gate at + * bit 31. Every one of those selectors resets to the crystal. + */ + +#define CLKCTRL_DIVSOR_SHIFT (4) +#define CLKCTRL_DIVSOR_WIDTH (4) +#define CLKCTRL_SRC_SEL_SHIFT (0) +#define CLKCTRL_SRC_SEL_WIDTH (1) +#define CLKCTRL_CLKEN (31) + +#define BOOTSPI_DIVSOR_SHIFT (4) +#define BOOTSPI_DIVSOR_WIDTH (6) + +/* CPU bus ratio selectors sit at bit 16. The SCPU one is a real choice + * between half rate and full rate; the LPCPU one is documented as + * reserved, so that bus is always half the core clock. + */ + +#define CPU_BUSCLK_SEL_SHIFT (16) +#define CPU_BUSCLK_SEL_WIDTH (1) + +/* DDR controller clock control (TRM p103). Both controllers expose five + * port clocks and a trace clock; only controller 0 carries the shared + * source selector, divisor and configuration gate. + */ + +#define DDR_ACLK_DIVSOR_SHIFT (20) +#define DDR_ACLK_DIVSOR_WIDTH (4) +#define DDR_ACLKSRC_SEL_SHIFT (16) +#define DDR_ACLKSRC_SEL_WIDTH (1) +#define DDR_CFG_CLKEN (9) +#define DDR_PORT_ACLK_CLKEN(n) (4 + (n)) /* port0..port4 */ +#define DDR_TRACE_CLKEN (0) + +/* Video input, output and codec (TRM p117 to p130). The shutter, pixel, + * audio master and CEC dividers are all wider than the usual four bits. + */ + +#define VI_CFG_CLKEN (30) +#define VI_SHUTTER_DIVSOR_SHIFT (4) +#define VI_SHUTTER_DIVSOR_WIDTH (7) +#define VI_PHYCFG_CLKEN (1) +#define VI_TXESC_CLKEN (0) +#define VO_CFG_CLKEN (30) +#define VO_PIXEL_DIVSOR_SHIFT (4) +#define VO_PIXEL_DIVSOR_WIDTH (6) +#define VO_MCLK_DIVSOR_SHIFT (4) +#define VO_MCLK_DIVSOR_WIDTH (8) +#define VO_CEC_DIVSOR_SHIFT (16) +#define VO_CEC_DIVSOR_WIDTH (16) +#define VO_CR_CLKEN (1) +#define VO_TXESC_CLKEN (0) +#define G2D_ACLKEN (31) +#define G2D_CLKEN (30) +#define G2D_PCLK_EN (28) +#define VC_VE_PCLK_EN (5) +#define VC_VD_PCLK_EN (4) +#define VC_MON_PCLK_EN (3) +#define VC_JE_PCLK_EN (2) +#define VC_JD_PCLK_EN (1) +#define VC_CFG_CLKEN (0) + +/* Die to die config gate sits at bit 0 rather than the usual bit 31 + * (TRM p108). + */ + +#define D2D_CFG_CLKEN (0) + +/* Neural processor (TRM p115). Both the cache and the core selectors + * are two bits wide with only three valid encodings, which is why they + * need the table driven selector rather than the generic one. + */ + +#define NPU_CFG_CLKEN (30) +#define NPU_LLC_SRC0_DIVSOR_SHIFT (4) +#define NPU_LLC_SRC1_DIVSOR_SHIFT (8) +#define NPU_LLC_DIVSOR_WIDTH (4) +#define NPU_LLCLK_SEL_SHIFT (0) +#define NPU_SEL_WIDTH (2) +#define NPU_CORECLK_SEL_SHIFT (0) +#define NPU_CORECLK_DIVSOR_SHIFT (4) +#define NPU_E31CLK_SEL_SHIFT (8) +#define NPU_E31CLK_DIVSOR_SHIFT (12) +#define NPU_DIVSOR_WIDTH (4) +#define NPU_E31_CORE_CLKEN (30) + +/* High speed peripherals (TRM p109 to p114). The SATA, ethernet and + * SD/eMMC blocks each carry a backup selector choosing between their + * derived clock and the board supplied LPDDR reference. + */ + +#define HSP_DMA0_CLKEN (0) +#define HSP_PCLK_EN (30) +#define SATA_RBC_CLKEN (0) +#define SATA_OOB_CLKEN (31) +#define SATA_PHY_BAK_SEL_SHIFT (9) +#define SATA_PHY_REF_DIVSOR_SHIFT (0) +#define SATA_PHY_REF_DIVSOR_WIDTH (4) +#define ETH_RMII_REF_CLKEN (31) +#define ETH_DIVSOR_SHIFT (4) +#define ETH_DIVSOR_WIDTH (7) +#define ETH_RMII_BAK_SEL_SHIFT (2) +#define ETH_CORE_BAK_SEL_SHIFT (1) +#define ETH_CLK_EN (0) +#define MSHC_CORE_CLKEN (16) +#define MSHC_DIVSOR_SHIFT (4) +#define MSHC_DIVSOR_WIDTH (12) +#define MSHC_CORE_SEL_SHIFT (0) +#define PCIE_AUX_CLKEN (1) +#define PCIE_CR_CLKEN (0) + +/* Always on DMA (TRM p130). Like the other root selectors this one + * resets to the crystal rather than to the divided PLL output. + */ + +#define AONDMA_AXI_CLKEN (31) +#define AONDMA_CFG_CLKEN (30) +#define AON_ACLK_CLKEN (29) + +/* Secure block clocks (TRM p133) */ + +#define SPACC_CRYPTO_CLKEN (30) +#define CRYPTO_DIVSOR_SHIFT (4) +#define CRYPTO_DIVSOR_WIDTH (4) + +/* Timer clock control (TRM p131). The four low speed timers are fed + * directly from the crystal with no divider. Counter 7 of timer 3 has a + * separate 49.5MHz feed used to derive a 90kHz AVC time stamp. + */ + +#define TIMER_CLK_CLKEN(n) (0 + (n)) /* timer_clk[0..3] */ +#define TIMER_PCLK_CLKEN(n) (4 + (n)) /* timer_pclk[0..3] */ +#define TIMER3_CLK8_CLKEN (8) + +/* RTC clock control (TRM p132). rtc_toggle is the signal the CLINT mtime + * counter increments from (TRM p251), so rtc_toggle_divsor sets the NuttX + * tick source frequency. Note the two divisors do not share an encoding: + * rtc_toggle_divsor documents 0 and 1 as bypass, aon_rtc_divsor uses the + * usual "0, 1, 2 all mean divide by two". + */ + +#define RTC_TOGGLE_DIVSOR_SHIFT (16) +#define RTC_TOGGLE_DIVSOR_WIDTH (5) +#define RTC_AON_DIVSOR_SHIFT (21) +#define RTC_AON_DIVSOR_WIDTH (11) +#define RTC_CLKEN (1) +#define RTC_CFG_CLKEN (2) + +/* U84 cluster clock control (TRM p135). The core selector resets to the + * crystal; the boot loader is what moves the cluster onto the CPU PLL. + */ + +#define U84_CORE_CLK_SEL_SHIFT (0) +#define U84_CORE_CLK_SEL_WIDTH (2) +#define U84_BUSCLK_RATIO_SEL_SHIFT (20) +#define U84_BUSCLK_RATIO_SEL_WIDTH (1) +#define U84_TRACE_COM_CLKEN (23) +#define U84_TRACE_CLKEN(n) (24 + (n)) /* hart0..hart3 */ +#define U84_CORE_CLKEN(n) (28 + (n)) /* hart0..hart3 */ + +/* LSP gate bank 0 (TRM p134). Bit set enables the clock. The uart and + * i2c fields are contiguous runs, one bit per instance, lowest instance + * in the least significant bit. + */ + +#define LSP_CLK_EN0_FAN_PCLK (0) +#define LSP_CLK_EN0_PVT_PCLK (1) +#define LSP_CLK_EN0_I2C_PCLK(n) (7 + (n)) /* i2c0..i2c9 */ +#define I2C_CLK_CTRL_PCLKEN (31) /* In I2Cn_CLK_CTRL */ +#define LSP_CLK_EN0_UART_PCLK(n) (17 + (n)) /* uart0..uart4 */ +#define LSP_CLK_EN0_TIMER_PCLK (25) +#define LSP_CLK_EN0_SSI_PCLK(n) (26 + (n)) /* ssi0..ssi1 */ +#define LSP_CLK_EN0_WDT_PCLK(n) (28 + (n)) /* wdt0..wdt3 */ + +/* LSP gate bank 1 (TRM p135) */ + +#define LSP_CLK_EN1_MBOX_PCLK(n) (0 + (n)) /* mailbox0..mailbox15 */ +#define LSP_CLK_EN1_PVT_CLK(n) (16 + (n)) /* 0: lsp pvt, 1: ddr pvt */ + +/* System configuration clock control (TRM p136). This is the root of the + * 200MHz APB domain that feeds every peripheral pclk. Note that the + * selector resets to 1, that is to the 24MHz crystal: the 200MHz figure + * quoted in the TRM clock tree is the post bootloader configuration, so + * the value must always be read rather than assumed. + */ + +#define SYSCFG_CLK_SEL_SHIFT (0) +#define SYSCFG_CLK_SEL_WIDTH (1) +#define SYSCFG_DIVSOR_SHIFT (4) +#define SYSCFG_DIVSOR_WIDTH (3) + +#endif /* __ARCH_RISCV_SRC_EIC7700X_HARDWARE_EIC7700X_CLK_H */