Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 24 May 2015 19:00:46 +0000 (UTC)
From:      Ian Lepore <ian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r283501 - stable/10/sys/arm/freescale/imx
Message-ID:  <201505241900.t4OJ0kJw073292@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201505241900.t4OJ0kJw073292>