Author: mmel
Date: Sun Dec  4 16:04:22 2016
New Revision: 309538
URL: https://svnweb.freebsd.org/changeset/base/309538

Log:
  Fixes for NVIDIA Tegra124 clocks:
   - EMC clock have standard peripheral clock block. Use it.
   - Implement full frequency set method for PLLD2. This PLL
     is used as HDMI pixel clock so we must be able to set it
     to wide range of frequencies, within 5% tolerance allowed
     by HDMI specification. Due to this, full state space search
     (over m, n, p fields) is necessary.
  
  MFC after: 3 weeks

Modified:
  head/sys/arm/nvidia/tegra124/tegra124_car.c
  head/sys/arm/nvidia/tegra124/tegra124_clk_per.c
  head/sys/arm/nvidia/tegra124/tegra124_clk_pll.c

Modified: head/sys/arm/nvidia/tegra124/tegra124_car.c
==============================================================================
--- head/sys/arm/nvidia/tegra124/tegra124_car.c Sun Dec  4 16:02:59 2016        
(r309537)
+++ head/sys/arm/nvidia/tegra124/tegra124_car.c Sun Dec  4 16:04:22 2016        
(r309538)
@@ -189,8 +189,6 @@ PLIST(mux_pll_srcs) = {"osc_div_clk", NU
 PLIST(mux_plle_src1) = {"osc_div_clk", "pllP_out0"};
 PLIST(mux_plle_src) = {"pllE_src1", "pllREFE_out"};
 PLIST(mux_plld_out0_plld2_out0) = {"pllD_out0", "pllD2_out0"};
-PLIST(mux_pllmcp_clkm) = {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m",
-    "pllM_UD", "pllC2_out0", "pllC3_out0", "pllC_UD"};
 PLIST(mux_xusb_hs) = {"xusb_ss_div2", "pllU_60"};
 PLIST(mux_xusb_ss) = {"pc_xusb_ss", "osc_div_clk"};
 
@@ -240,7 +238,6 @@ static struct clk_mux_def tegra124_mux_c
        /* Base peripheral clocks. */
        MUX(0, "dsia_mux", mux_plld_out0_plld2_out0, PLLD_BASE, 25, 1),
        MUX(0, "dsib_mux", mux_plld_out0_plld2_out0, PLLD2_BASE, 25, 1),
-       MUX(0, "emc_mux", mux_pllmcp_clkm, CLK_SOURCE_EMC, 29, 3),
 
        /* USB. */
        MUX(TEGRA124_CLK_XUSB_HS_SRC, "xusb_hs", mux_xusb_hs, 
CLK_SOURCE_XUSB_SS, 25, 1),

Modified: head/sys/arm/nvidia/tegra124/tegra124_clk_per.c
==============================================================================
--- head/sys/arm/nvidia/tegra124/tegra124_clk_per.c     Sun Dec  4 16:02:59 
2016        (r309537)
+++ head/sys/arm/nvidia/tegra124/tegra124_clk_per.c     Sun Dec  4 16:04:22 
2016        (r309538)
@@ -126,6 +126,10 @@ PLIST(mux_m_c_p_a_c2_c3_clkm_c4) =
 PLIST(mux_m_c_p_clkm_mud_c2_c3) =
     {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m",
      "pllM_UD", "pllC2_out0", "pllC3_out0"};
+PLIST(mux_m_c_p_clkm_mud_c2_c3_cud) =
+    {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m",
+     "pllM_UD", "pllC2_out0", "pllC3_out0", "pllC_UD"};
+
 PLIST(mux_m_c2_c_c3_p_N_a) =
     {"pllM_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
      "pllP_out0", NULL, "pllA_out0"};
@@ -260,7 +264,7 @@ static struct pgate_def pgate_def[] = {
        GATE(I2C2, "i2c2", "pc_i2c2", H(22)),
        GATE(UARTC, "uartc", "pc_uartc", H(23)),
        GATE(MIPI_CAL, "mipi_cal", "clk_m", H(24)),
-       GATE(EMC, "emc", "emc_mux", H(25)),
+       GATE(EMC, "emc", "pc_emc_2x", H(25)),
        GATE(USB2, "usb2", "clk_m", H(26)),
        GATE(USB3, "usb3", "clk_m", H(27)),
        GATE(VDE, "vde", "pc_vde", H(29)),
@@ -356,7 +360,7 @@ static struct pgate_def pgate_def[] = {
        /* GATE(CAM_MCLK, "CAM_MCLK", "clk_m", X(4)), */
        /* GATE(CAM_MCLK2, "CAM_MCLK2", "clk_m", X(5)), */
        GATE(I2C6, "i2c6", "pc_i2c6", X(6)),
-       /* GATE(VIM2_CLK, "vim2_clk", clk_m, X(11)), */
+       GATE(VIM2_CLK, "vim2_clk", "clk_m", X(11)),
        /* GATE(EMC_DLL, "emc_dll", "pc_emc_dll", X(14)), */
        GATE(HDMI_AUDIO, "hdmi_audio", "pc_hdmi_audio", X(16)),
        GATE(CLK72MHZ, "clk72mhz", "pc_clk72mhz", X(17)),
@@ -373,17 +377,18 @@ static struct pgate_def pgate_def[] = {
 #define        DCF_HAVE_ENA            0x0200 /* Block with enable bit */
 #define        DCF_HAVE_DIV            0x0400 /* Block with divider */
 
-/* Mark block with additional bis / functionality. */
+/* Mark block with additional bits / functionality. */
 #define        DCF_IS_MASK             0x00FF
 #define        DCF_IS_UART             0x0001
 #define        DCF_IS_VI               0x0002
 #define        DCF_IS_HOST1X           0x0003
 #define        DCF_IS_XUSB_SS          0x0004
 #define        DCF_IS_EMC_DLL          0x0005
-#define        FDS_IS_SATA             0x0006
+#define        DCF_IS_SATA             0x0006
 #define        DCF_IS_VIC              0x0007
 #define        DCF_IS_AUDIO            0x0008
 #define        DCF_IS_SOR0             0x0009
+#define        DCF_IS_EMC              0x000A
 
 /* Basic pheripheral clock */
 #define        PER_CLK(_id, cn, pl, r, diw, fiw, f)                            
\
@@ -438,7 +443,7 @@ static struct periph_def periph_def[] = 
        CLK_8_1(0, "pc_host1x", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HOST1X, 
DCF_IS_HOST1X),
        CLK_8_1(0, "pc_hdmi", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_HDMI, 0),
        CLK16_0(0, "pc_i2c2", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C2, 0),
-/* EMC  8 */
+       CLK_8_1(0, "pc_emc_2x", mux_m_c_p_clkm_mud_c2_c3_cud, CLK_SOURCE_EMC, 
DCF_IS_EMC),
        CLK16_1(0, "pc_uartc", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTC, 
DCF_IS_UART),
        CLK_8_1(0, "pc_vi_sensor", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR, 
0),
        CLK_8_1(0, "pc_spi4", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI4, 0),
@@ -476,7 +481,7 @@ static struct periph_def periph_def[] = 
 /* SYS */
        CLK_8_1(0, "pc_sor0", mux_p_m_d_a_c_d2_clkm,  CLK_SOURCE_SOR0, 
DCF_IS_SOR0),
        CLK_8_1(0, "pc_sata_oob", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA_OOB, 0),
-       CLK_8_1(0, "pc_sata", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA, 
FDS_IS_SATA),
+       CLK_8_1(0, "pc_sata", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA, 
DCF_IS_SATA),
        CLK_8_1(0, "pc_hda", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HDA, 0),
        CLK_8_1(TEGRA124_CLK_XUSB_HOST_SRC,
                   "pc_xusb_core_host", mux_clkm_p_c2_c_c3_refre, 
CLK_SOURCE_XUSB_CORE_HOST, 0),

Modified: head/sys/arm/nvidia/tegra124/tegra124_clk_pll.c
==============================================================================
--- head/sys/arm/nvidia/tegra124/tegra124_clk_pll.c     Sun Dec  4 16:02:59 
2016        (r309537)
+++ head/sys/arm/nvidia/tegra124/tegra124_clk_pll.c     Sun Dec  4 16:04:22 
2016        (r309538)
@@ -713,6 +713,7 @@ pll_set_std(struct pll_sc *sc, uint64_t 
                        return (ERANGE);
 
                *fout = ((fin / m) * n) /p;
+
                return (0);
        }
 
@@ -771,7 +772,6 @@ pllc_set_freq(struct pll_sc *sc, uint64_
 {
        uint32_t m, n, p;
 
-
        p = 2;
        m = 1;
        n = (*fout * p * m + fin / 2)/ fin;
@@ -779,19 +779,88 @@ pllc_set_freq(struct pll_sc *sc, uint64_
        return (pll_set_std( sc, fin, fout, flags, m, n, p));
 }
 
+/*
+ * PLLD2 is used as source for pixel clock for HDMI.
+ * We must be able to set it frequency very flexibly and
+ * precisely (within 5% tolerance limit allowed by HDMI specs).
+ *
+ * For this reason, it is necessary to search the full state space.
+ * Fortunately, thanks to early cycle terminations, performance
+ * is within acceptable limits.
+ */
+#define        PLLD2_PFD_MIN             12000000      /*  12 MHz */
+#define        PLLD2_PFD_MAX             38000000      /*  38 MHz */
+#define        PLLD2_VCO_MIN            600000000      /* 600 MHz */
+#define        PLLD2_VCO_MAX           1200000000      /* 1.2 GHz */
+
 static int
 plld2_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
 {
        uint32_t m, n, p;
+       uint32_t best_m, best_n, best_p;
+       uint64_t vco, pfd;
+       int64_t err, best_err;
+       struct mnp_bits *mnp_bits;
+       struct pdiv_table *tbl;
+       int p_idx, rv;
 
-       p = 2;
-       m = 1;
-       n = (*fout * p * m + fin / 2)/ fin;
-       dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p);
-       return (pll_set_std(sc, fin, fout, flags, m, n, p));
-}
+       mnp_bits = &sc->mnp_bits;
+       tbl = sc->pdiv_table;
+       best_err = INT64_MAX;
 
+       for (p_idx = 0; tbl[p_idx].divider != 0; p_idx++) {
+               p = tbl[p_idx].divider;
 
+               /* Check constraints */
+               vco = *fout * p;
+               if (vco < PLLD2_VCO_MIN)
+                       continue;
+               if (vco > PLLD2_VCO_MAX)
+                       break;
+
+               for (m = 1; m < (1 << mnp_bits->m_width); m++) {
+                       n = (*fout * p * m + fin / 2) / fin;
+
+                       /* Check constraints */
+                       if (n == 0)
+                               continue;
+                       if (n >= (1 << mnp_bits->n_width))
+                               break;
+                       vco = (fin * n) / m;
+                       if (vco > PLLD2_VCO_MAX || vco < PLLD2_VCO_MIN)
+                               continue;
+                       pfd = fin / m;
+                       if (pfd > PLLD2_PFD_MAX || vco < PLLD2_PFD_MIN)
+                               continue;
+
+                       /* Constraints passed, save best result */
+                       err = *fout - vco / p;
+                       if (err < 0)
+                               err = -err;
+                       if (err < best_err) {
+                               best_err = err;
+                               best_p = p;
+                               best_m = m;
+                               best_n = n;
+                       }
+                       if (err == 0)
+                               goto done;
+               }
+       }
+done:
+       /*
+        * HDMI specification allows 5% pixel clock tolerance,
+        * we will by a slightly stricter
+        */
+       if (best_err > ((*fout * 100) / 4))
+               return (ERANGE);
+
+       if (flags & CLK_SET_DRYRUN)
+               return (0);
+       rv = pll_set_std(sc, fin, fout, flags, best_m, best_n, best_p);
+       /* XXXX Panic for rv == ERANGE ? */
+       return (rv);
+}
 
 static int
 pllrefe_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
@@ -883,8 +952,8 @@ tegra124_pll_set_freq(struct clknode *cl
        struct pll_sc *sc;
 
        sc = clknode_get_softc(clknode);
-       dprintf("%s: Requested freq: %llu, input freq: %llu\n", __func__,
-           *fout, fin);
+       dprintf("%s: %s requested freq: %llu, input freq: %llu\n", __func__,
+          clknode_get_name(clknode), *fout, fin);
        switch (sc->type) {
        case PLL_A:
                rv = plla_set_freq(sc, fin, fout, flags);
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to