summaryrefslogtreecommitdiffstats
path: root/sys/arm
diff options
context:
space:
mode:
authorian <ian@FreeBSD.org>2015-05-05 23:27:49 +0000
committerian <ian@FreeBSD.org>2015-05-05 23:27:49 +0000
commitc56bc9560bd89b4b64a49be4648c2734cb73e2dd (patch)
tree391d67038dca0be13e705af72bb81f6fde998ee4 /sys/arm
parent7ac181d7c02defef331b1fdb6e1e6af31bb9cc0b (diff)
downloadFreeBSD-src-c56bc9560bd89b4b64a49be4648c2734cb73e2dd.zip
FreeBSD-src-c56bc9560bd89b4b64a49be4648c2734cb73e2dd.tar.gz
Add the code necessary to run the imx6 chip at its lowest clock/power
operating point (396MHz/950mV).
Diffstat (limited to 'sys/arm')
-rw-r--r--sys/arm/freescale/imx/imx6_anatop.c64
-rw-r--r--sys/arm/freescale/imx/imx6_ccm.c14
-rw-r--r--sys/arm/freescale/imx/imx6_ccmreg.h2
-rw-r--r--sys/arm/freescale/imx/imx_ccmvar.h4
4 files changed, 63 insertions, 21 deletions
diff --git a/sys/arm/freescale/imx/imx6_anatop.c b/sys/arm/freescale/imx/imx6_anatop.c
index a384501..16a09d3 100644
--- a/sys/arm/freescale/imx/imx6_anatop.c
+++ b/sys/arm/freescale/imx/imx6_anatop.c
@@ -71,6 +71,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>
@@ -116,12 +117,16 @@ static struct imx6_anatop_softc *imx6_anatop_sc;
/*
* 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},
@@ -158,14 +163,15 @@ imx6_anatop_write_4(bus_size_t offset, uint32_t value)
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);
@@ -180,19 +186,29 @@ vdd_set(struct imx6_anatop_softc *sc, int mv)
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;
}
@@ -205,7 +221,7 @@ vdd_set(struct imx6_anatop_softc *sc, int mv)
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);
@@ -213,24 +229,29 @@ vdd_set(struct imx6_anatop_softc *sc, int mv)
}
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 *
@@ -256,7 +277,7 @@ cpufreq_nearest_oppt(struct imx6_anatop_softc *sc, uint32_t cpu_newmhz)
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) {
@@ -272,6 +293,7 @@ cpufreq_set_clock(struct imx6_anatop_softc * sc, struct oppt *op)
* - 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,
@@ -279,7 +301,7 @@ cpufreq_set_clock(struct imx6_anatop_softc * sc, struct oppt *op)
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;
@@ -290,6 +312,7 @@ cpufreq_set_clock(struct imx6_anatop_softc * sc, struct oppt *op)
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)
@@ -297,7 +320,7 @@ cpufreq_set_clock(struct imx6_anatop_softc * sc, struct oppt *op)
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);
}
@@ -748,11 +771,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[] = {
diff --git a/sys/arm/freescale/imx/imx6_ccm.c b/sys/arm/freescale/imx/imx6_ccm.c
index 8d7f14f..488c55a 100644
--- a/sys/arm/freescale/imx/imx6_ccm.c
+++ b/sys/arm/freescale/imx/imx6_ccm.c
@@ -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),
diff --git a/sys/arm/freescale/imx/imx6_ccmreg.h b/sys/arm/freescale/imx/imx6_ccmreg.h
index 31d92ca..85dd372 100644
--- a/sys/arm/freescale/imx/imx6_ccmreg.h
+++ b/sys/arm/freescale/imx/imx6_ccmreg.h
@@ -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
diff --git a/sys/arm/freescale/imx/imx_ccmvar.h b/sys/arm/freescale/imx/imx_ccmvar.h
index f159437..91a3e45 100644
--- a/sys/arm/freescale/imx/imx_ccmvar.h
+++ b/sys/arm/freescale/imx/imx_ccmvar.h
@@ -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
OpenPOWER on IntegriCloud