summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/arm/xilinx/zy7_slcr.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/arm/xilinx/zy7_slcr.c')
-rw-r--r--freebsd/sys/arm/xilinx/zy7_slcr.c296
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)
{