diff options
Diffstat (limited to 'freebsd/sys/arm/xilinx/zy7_slcr.c')
-rw-r--r-- | freebsd/sys/arm/xilinx/zy7_slcr.c | 296 |
1 files changed, 292 insertions, 4 deletions
diff --git a/freebsd/sys/arm/xilinx/zy7_slcr.c b/freebsd/sys/arm/xilinx/zy7_slcr.c index 79be30a7..7ce502f9 100644 --- a/freebsd/sys/arm/xilinx/zy7_slcr.c +++ b/freebsd/sys/arm/xilinx/zy7_slcr.c @@ -88,7 +88,6 @@ extern void (*zynq7_cpu_reset); #define ZYNQ_DEFAULT_PS_CLK_FREQUENCY 33333333 /* 33.3 Mhz */ - SYSCTL_NODE(_hw, OID_AUTO, zynq, CTLFLAG_RD, 0, "Xilinx Zynq-7000"); static char zynq_bootmode[64]; @@ -135,7 +134,6 @@ zy7_slcr_lock(struct zy7_slcr_softc *sc) WR4(sc, ZY7_SLCR_LOCK, ZY7_SLCR_LOCK_MAGIC); } - #ifndef __rtems__ static void zy7_slcr_cpu_reset(void) @@ -185,11 +183,12 @@ zy7_slcr_preload_pl(void) ZSLCR_UNLOCK(sc); } +#endif /* __rtems__ */ /* After PL configuration, enable level shifters and deassert top-level * PL resets. Called from zy7_devcfg.c. Optionally, the level shifters * can be left disabled but that's rare of an FPGA application. That option - * is controled by a sysctl in the devcfg driver. + * is controlled by a sysctl in the devcfg driver. */ void zy7_slcr_postload_pl(int en_level_shifters) @@ -216,7 +215,6 @@ zy7_slcr_postload_pl(int en_level_shifters) ZSLCR_UNLOCK(sc); } -#endif /* __rtems__ */ /* Override cgem_set_refclk() in gigabit ethernet driver * (sys/dev/cadence/if_cgem.c). This function is called to @@ -266,6 +264,296 @@ cgem_set_ref_clk(int unit, int frequency) return (0); } +/* + * PL clocks management function + */ +int +zy7_pl_fclk_set_source(int unit, int source) +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + uint32_t reg; + + if (!sc) + return (-1); + + ZSLCR_LOCK(sc); + + /* Unlock SLCR registers. */ + zy7_slcr_unlock(sc); + + /* Modify FPGAx source. */ + reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit)); + reg &= ~(ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_MASK); + reg |= (source << ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_SHIFT); + WR4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit), reg); + + /* Lock SLCR registers. */ + zy7_slcr_lock(sc); + + ZSLCR_UNLOCK(sc); + + return (0); +} + +int +zy7_pl_fclk_get_source(int unit) +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + uint32_t reg; + int source; + + if (!sc) + return (-1); + + ZSLCR_LOCK(sc); + + /* Modify GEM reference clock. */ + reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit)); + source = (reg & ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_MASK) >> + ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_SHIFT; + + /* ZY7_PL_FCLK_SRC_IO is actually b0x */ + if ((source & 2) == 0) + source = ZY7_PL_FCLK_SRC_IO; + + ZSLCR_UNLOCK(sc); + + return (source); +} + +int +zy7_pl_fclk_set_freq(int unit, int frequency) +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + int div0, div1; + int base_frequency; + uint32_t reg; + int source; + + if (!sc) + return (-1); + + source = zy7_pl_fclk_get_source(unit); + switch (source) { + case ZY7_PL_FCLK_SRC_IO: + base_frequency = io_pll_frequency; + break; + + case ZY7_PL_FCLK_SRC_ARM: + base_frequency = arm_pll_frequency; + break; + + case ZY7_PL_FCLK_SRC_DDR: + base_frequency = ddr_pll_frequency; + break; + + default: + return (-1); + } + + /* Find suitable divisor pairs. Round result to nearest khz + * to test for match. + */ + for (div1 = 1; div1 <= ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX; div1++) { + div0 = (base_frequency + div1 * frequency / 2) / + div1 / frequency; + if (div0 > 0 && div0 <= ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX && + ((base_frequency / div0 / div1) + 500) / 1000 == + (frequency + 500) / 1000) + break; + } + + if (div1 > ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX) + return (-1); + + ZSLCR_LOCK(sc); + + /* Unlock SLCR registers. */ + zy7_slcr_unlock(sc); + + /* Modify FPGAx reference clock. */ + reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit)); + reg &= ~(ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_MASK | + ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_MASK); + reg |= (div1 << ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_SHIFT) | + (div0 << ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_SHIFT); + WR4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit), reg); + + /* Lock SLCR registers. */ + zy7_slcr_lock(sc); + + ZSLCR_UNLOCK(sc); + + return (base_frequency / div0 / div1); +} + +int +zy7_pl_fclk_get_freq(int unit) +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + int div0, div1; + int base_frequency; + int frequency; + uint32_t reg; + int source; + + if (!sc) + return (-1); + + source = zy7_pl_fclk_get_source(unit); + switch (source) { + case ZY7_PL_FCLK_SRC_IO: + base_frequency = io_pll_frequency; + break; + + case ZY7_PL_FCLK_SRC_ARM: + base_frequency = arm_pll_frequency; + break; + + case ZY7_PL_FCLK_SRC_DDR: + base_frequency = ddr_pll_frequency; + break; + + default: + return (-1); + } + + ZSLCR_LOCK(sc); + + /* Modify FPGAx reference clock. */ + reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit)); + div1 = (reg & ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_MASK) >> + ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_SHIFT; + div0 = (reg & ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_MASK) >> + ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_SHIFT; + + ZSLCR_UNLOCK(sc); + + if (div0 == 0) + div0 = 1; + + if (div1 == 0) + div1 = 1; + + frequency = (base_frequency / div0 / div1); + /* Round to KHz */ + frequency = (frequency + 500) / 1000; + frequency = frequency * 1000; + + return (frequency); +} + +int +zy7_pl_fclk_enable(int unit) +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + + if (!sc) + return (-1); + + ZSLCR_LOCK(sc); + + /* Unlock SLCR registers. */ + zy7_slcr_unlock(sc); + + WR4(sc, ZY7_SLCR_FPGA_THR_CTRL(unit), 0); + WR4(sc, ZY7_SLCR_FPGA_THR_CNT(unit), 0); + + /* Lock SLCR registers. */ + zy7_slcr_lock(sc); + + ZSLCR_UNLOCK(sc); + + return (0); +} + +int +zy7_pl_fclk_disable(int unit) +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + + if (!sc) + return (-1); + + ZSLCR_LOCK(sc); + + /* Unlock SLCR registers. */ + zy7_slcr_unlock(sc); + + WR4(sc, ZY7_SLCR_FPGA_THR_CTRL(unit), 0); + WR4(sc, ZY7_SLCR_FPGA_THR_CNT(unit), 1); + + /* Lock SLCR registers. */ + zy7_slcr_lock(sc); + + ZSLCR_UNLOCK(sc); + + return (0); +} + +int +zy7_pl_fclk_enabled(int unit) +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + uint32_t reg; + + if (!sc) + return (-1); + + ZSLCR_LOCK(sc); + reg = RD4(sc, ZY7_SLCR_FPGA_THR_CNT(unit)); + ZSLCR_UNLOCK(sc); + + return !(reg & 1); +} + +int +zy7_pl_level_shifters_enabled() +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + + uint32_t reg; + + if (!sc) + return (-1); + + ZSLCR_LOCK(sc); + reg = RD4(sc, ZY7_SLCR_LVL_SHFTR_EN); + ZSLCR_UNLOCK(sc); + + return (reg == ZY7_SLCR_LVL_SHFTR_EN_ALL); +} + +void +zy7_pl_level_shifters_enable() +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + + if (!sc) + return; + + ZSLCR_LOCK(sc); + zy7_slcr_unlock(sc); + WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, ZY7_SLCR_LVL_SHFTR_EN_ALL); + zy7_slcr_lock(sc); + ZSLCR_UNLOCK(sc); +} + +void +zy7_pl_level_shifters_disable() +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + + if (!sc) + return; + + ZSLCR_LOCK(sc); + zy7_slcr_unlock(sc); + WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, 0); + zy7_slcr_lock(sc); + ZSLCR_UNLOCK(sc); +} + static int zy7_slcr_probe(device_t dev) { |