Add the code necessary to run the imx6 chip at its lowest clock/power
operating point (396MHz/950mV).
This commit is contained in:
parent
ace79aa796
commit
955691fecd
@ -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);
|
||||
@ -179,20 +185,30 @@ vdd_set(struct imx6_anatop_softc *sc, int mv)
|
||||
else
|
||||
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[] = {
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user