Author: ian
Date: Sun May 24 19:00:46 2015
New Revision: 283501
URL: https://svnweb.freebsd.org/changeset/base/283501

Log:
  MFC r282516:
  
    Add the code necessary to run the imx6 chip at its lowest clock/power
    operating point (396MHz/950mV).

Modified:
  stable/10/sys/arm/freescale/imx/imx6_anatop.c
  stable/10/sys/arm/freescale/imx/imx6_ccm.c
  stable/10/sys/arm/freescale/imx/imx6_ccmreg.h
  stable/10/sys/arm/freescale/imx/imx_ccmvar.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/arm/freescale/imx/imx6_anatop.c
==============================================================================
--- stable/10/sys/arm/freescale/imx/imx6_anatop.c       Sun May 24 18:59:45 
2015        (r283500)
+++ stable/10/sys/arm/freescale/imx/imx6_anatop.c       Sun May 24 19:00:46 
2015        (r283501)
@@ -72,6 +72,7 @@ __FBSDID("$FreeBSD$");
 #include <arm/arm/mpcore_timervar.h>
 #include <arm/freescale/fsl_ocotpreg.h>
 #include <arm/freescale/fsl_ocotpvar.h>
+#include <arm/freescale/imx/imx_ccmvar.h>
 #include <arm/freescale/imx/imx6_anatopreg.h>
 #include <arm/freescale/imx/imx6_anatopvar.h>
 
@@ -117,12 +118,16 @@ static struct imx6_anatop_softc *imx6_an
 /*
  * Table of "operating points".
  * These are combinations of frequency and voltage blessed by Freescale.
+ * While the datasheet says the ARM voltage can be as low as 925mV at
+ * 396MHz, it also says that the ARM and SOC voltages can't differ by
+ * more than 200mV, and the minimum SOC voltage is 1150mV, so that
+ * dictates the 950mV entry in this table.
  */
 static struct oppt {
        uint32_t        mhz;
        uint32_t        mv;
 } imx6_oppt_table[] = {
-/*      { 396,  925},  XXX: need functional ccm code for this speed */
+       { 396,   950},
        { 792,  1150},
        { 852,  1225},
        { 996,  1225},
@@ -159,14 +164,15 @@ imx6_anatop_write_4(bus_size_t offset, u
 static void
 vdd_set(struct imx6_anatop_softc *sc, int mv)
 {
-       int newtarg, oldtarg;
+       int newtarg, newtargSoc, oldtarg;
        uint32_t delay, pmureg;
        static boolean_t init_done = false;
 
        /*
         * The datasheet says VDD_PU and VDD_SOC must be equal, and VDD_ARM
-        * can't be more than 50mV above or 200mV below them.  For now to keep
-        * things simple we set all three to the same value.
+        * can't be more than 50mV above or 200mV below them.  We keep them the
+        * same except in the case of the lowest operating point, which is
+        * handled as a special case below.
         */
 
        pmureg = imx6_anatop_read_4(IMX6_ANALOG_PMU_REG_CORE);
@@ -181,19 +187,29 @@ vdd_set(struct imx6_anatop_softc *sc, in
                newtarg = (mv - 700) / 25;
 
        /*
+        * The SOC voltage can't go below 1150mV, and thus because of the 200mV
+        * rule, the ARM voltage can't go below 950mV.  The 950 is encoded in
+        * our oppt table, here we handle the SOC 1150 rule as a special case.
+        * (1150-700/25=18).
+        */
+       newtargSoc = (newtarg < 18) ? 18 : newtarg;
+
+       /*
         * The first time through the 3 voltages might not be equal so use a
         * long conservative delay.  After that we need to delay 3uS for every
-        * 25mV step upward.  No need to delay at all when lowering.
+        * 25mV step upward; we actually delay 6uS because empirically, it works
+        * and the 3uS per step recommended by the docs doesn't (3uS fails when
+        * going from 400->1200, but works for smaller changes).
         */
        if (init_done) {
                if (newtarg == oldtarg)
                        return;
                else if (newtarg > oldtarg)
-                       delay = (newtarg - oldtarg) * 3;
+                       delay = (newtarg - oldtarg) * 6;
                else
                        delay = 0;
        } else {
-               delay = 700 / 25 * 3;
+               delay = (700 / 25) * 6;
                init_done = true;
        }
 
@@ -206,7 +222,7 @@ vdd_set(struct imx6_anatop_softc *sc, in
 
        pmureg |= newtarg << IMX6_ANALOG_PMU_REG0_TARG_SHIFT;
        pmureg |= newtarg << IMX6_ANALOG_PMU_REG1_TARG_SHIFT;
-       pmureg |= newtarg << IMX6_ANALOG_PMU_REG2_TARG_SHIFT;
+       pmureg |= newtargSoc << IMX6_ANALOG_PMU_REG2_TARG_SHIFT;
 
        imx6_anatop_write_4(IMX6_ANALOG_PMU_REG_CORE, pmureg);
        DELAY(delay);
@@ -214,24 +230,29 @@ vdd_set(struct imx6_anatop_softc *sc, in
 }
 
 static inline uint32_t
-cpufreq_mhz_from_div(struct imx6_anatop_softc *sc, uint32_t div)
+cpufreq_mhz_from_div(struct imx6_anatop_softc *sc, uint32_t corediv, 
+    uint32_t plldiv)
 {
 
-       return (sc->refosc_mhz * (div / 2));
+       return ((sc->refosc_mhz * (plldiv / 2)) / (corediv + 1));
 }
 
-static inline uint32_t
-cpufreq_mhz_to_div(struct imx6_anatop_softc *sc, uint32_t cpu_mhz)
+static inline void
+cpufreq_mhz_to_div(struct imx6_anatop_softc *sc, uint32_t cpu_mhz,
+    uint32_t *corediv, uint32_t *plldiv)
 {
 
-       return (cpu_mhz / (sc->refosc_mhz / 2));
+       *corediv = (cpu_mhz < 650) ? 1 : 0;
+       *plldiv = ((*corediv + 1) * cpu_mhz) / (sc->refosc_mhz / 2);
 }
 
 static inline uint32_t
 cpufreq_actual_mhz(struct imx6_anatop_softc *sc, uint32_t cpu_mhz)
 {
+       uint32_t corediv, plldiv;
 
-       return (cpufreq_mhz_from_div(sc, cpufreq_mhz_to_div(sc, cpu_mhz)));
+       cpufreq_mhz_to_div(sc, cpu_mhz, &corediv, &plldiv);
+       return (cpufreq_mhz_from_div(sc, corediv, plldiv));
 }
 
 static struct oppt *
@@ -257,7 +278,7 @@ cpufreq_nearest_oppt(struct imx6_anatop_
 static void 
 cpufreq_set_clock(struct imx6_anatop_softc * sc, struct oppt *op)
 {
-       uint32_t timeout, wrk32;
+       uint32_t corediv, plldiv, timeout, wrk32;
 
        /* If increasing the frequency, we must first increase the voltage. */
        if (op->mhz > sc->cpu_curmhz) {
@@ -273,6 +294,7 @@ cpufreq_set_clock(struct imx6_anatop_sof
         *  - Wait for the LOCK bit to come on; it takes ~50 loop iterations.
         *  - Turn off bypass mode; cpu should now be running at the new speed.
         */
+       cpufreq_mhz_to_div(sc, op->mhz, &corediv, &plldiv);
        imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_CLR, 
            IMX6_ANALOG_CCM_PLL_ARM_CLK_SRC_MASK);
        imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_SET, 
@@ -280,7 +302,7 @@ cpufreq_set_clock(struct imx6_anatop_sof
 
        wrk32 = imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM);
        wrk32 &= ~IMX6_ANALOG_CCM_PLL_ARM_DIV_MASK;
-       wrk32 |= cpufreq_mhz_to_div(sc, op->mhz);
+       wrk32 |= plldiv;
        imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM, wrk32);
 
        timeout = 10000;
@@ -291,6 +313,7 @@ cpufreq_set_clock(struct imx6_anatop_sof
 
        imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_CLR, 
            IMX6_ANALOG_CCM_PLL_ARM_BYPASS);
+       imx_ccm_set_cacrr(corediv);
 
        /* If lowering the frequency, it is now safe to lower the voltage. */
        if (op->mhz < sc->cpu_curmhz)
@@ -298,7 +321,7 @@ cpufreq_set_clock(struct imx6_anatop_sof
        sc->cpu_curmhz = op->mhz;
 
        /* Tell the mpcore timer that its frequency has changed. */
-        arm_tmr_change_frequency(
+       arm_tmr_change_frequency(
            cpufreq_actual_mhz(sc, sc->cpu_curmhz) * 1000000 / 2);
 }
 
@@ -752,11 +775,12 @@ imx6_anatop_probe(device_t dev)
 uint32_t 
 imx6_get_cpu_clock()
 {
-       uint32_t div;
+       uint32_t corediv, plldiv;
 
-       div = imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM) &
+       corediv = imx_ccm_get_cacrr();
+       plldiv = imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM) &
            IMX6_ANALOG_CCM_PLL_ARM_DIV_MASK;
-       return (cpufreq_mhz_from_div(imx6_anatop_sc, div));
+       return (cpufreq_mhz_from_div(imx6_anatop_sc, corediv, plldiv));
 }
 
 static device_method_t imx6_anatop_methods[] = {

Modified: stable/10/sys/arm/freescale/imx/imx6_ccm.c
==============================================================================
--- stable/10/sys/arm/freescale/imx/imx6_ccm.c  Sun May 24 18:59:45 2015        
(r283500)
+++ stable/10/sys/arm/freescale/imx/imx6_ccm.c  Sun May 24 19:00:46 2015        
(r283501)
@@ -320,6 +320,20 @@ imx_ccm_ahb_hz(void)
        return (132000000);
 }
 
+uint32_t
+imx_ccm_get_cacrr(void)
+{
+
+       return (RD4(ccm_sc, CCM_CACCR));
+}
+
+void
+imx_ccm_set_cacrr(uint32_t divisor)
+{
+
+       WR4(ccm_sc, CCM_CACCR, divisor);
+}
+
 static device_method_t ccm_methods[] = {
        /* Device interface */
        DEVMETHOD(device_probe,  ccm_probe),

Modified: stable/10/sys/arm/freescale/imx/imx6_ccmreg.h
==============================================================================
--- stable/10/sys/arm/freescale/imx/imx6_ccmreg.h       Sun May 24 18:59:45 
2015        (r283500)
+++ stable/10/sys/arm/freescale/imx/imx6_ccmreg.h       Sun May 24 19:00:46 
2015        (r283501)
@@ -29,6 +29,7 @@
 #ifndef        IMX6_CCMREG_H
 #define        IMX6_CCMREG_H
 
+#define        CCM_CACCR                       0x010
 #define        CCM_CSCMR1                      0x01C
 #define          SSI1_CLK_SEL_S                  10
 #define          SSI2_CLK_SEL_S                  12
@@ -64,6 +65,5 @@
 #define        CCM_CCGR5                       0x07C
 #define        CCM_CCGR6                       0x080
 #define        CCM_CMEOR                       0x088
-                  
 
 #endif

Modified: stable/10/sys/arm/freescale/imx/imx_ccmvar.h
==============================================================================
--- stable/10/sys/arm/freescale/imx/imx_ccmvar.h        Sun May 24 18:59:45 
2015        (r283500)
+++ stable/10/sys/arm/freescale/imx/imx_ccmvar.h        Sun May 24 19:00:46 
2015        (r283501)
@@ -53,4 +53,8 @@ void imx_ccm_usb_enable(device_t _usbdev
 void imx_ccm_usbphy_enable(device_t _phydev);
 void imx_ccm_ssi_configure(device_t _ssidev);
 
+/* Routines to get and set the arm clock root divisor register. */
+uint32_t imx_ccm_get_cacrr(void);
+void     imx_ccm_set_cacrr(uint32_t _divisor);
+
 #endif
_______________________________________________
svn-src-stable-10@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-stable-10
To unsubscribe, send any mail to "svn-src-stable-10-unsubscr...@freebsd.org"

Reply via email to