diff options
author | ian <ian@FreeBSD.org> | 2015-05-05 23:27:49 +0000 |
---|---|---|
committer | ian <ian@FreeBSD.org> | 2015-05-05 23:27:49 +0000 |
commit | c56bc9560bd89b4b64a49be4648c2734cb73e2dd (patch) | |
tree | 391d67038dca0be13e705af72bb81f6fde998ee4 /sys/arm | |
parent | 7ac181d7c02defef331b1fdb6e1e6af31bb9cc0b (diff) | |
download | FreeBSD-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.c | 64 | ||||
-rw-r--r-- | sys/arm/freescale/imx/imx6_ccm.c | 14 | ||||
-rw-r--r-- | sys/arm/freescale/imx/imx6_ccmreg.h | 2 | ||||
-rw-r--r-- | sys/arm/freescale/imx/imx_ccmvar.h | 4 |
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 |