Date: Fri, 28 Feb 2014 00:26:57 +0000 (UTC) From: Ian Lepore <ian@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r262585 - head/sys/arm/freescale/imx Message-ID: <201402280026.s1S0QvBr069053@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: ian Date: Fri Feb 28 00:26:57 2014 New Revision: 262585 URL: http://svnweb.freebsd.org/changeset/base/262585 Log: Add some rudimentary voltage control to go with the rudimentary frequency control. If we have to scale back the frequency due to temperature, it will help to lower the voltage as well. Modified: head/sys/arm/freescale/imx/imx6_anatop.c Modified: head/sys/arm/freescale/imx/imx6_anatop.c ============================================================================== --- head/sys/arm/freescale/imx/imx6_anatop.c Fri Feb 28 00:23:04 2014 (r262584) +++ head/sys/arm/freescale/imx/imx6_anatop.c Fri Feb 28 00:26:57 2014 (r262585) @@ -66,6 +66,7 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_bus_subr.h> #include <machine/bus.h> +#include <machine/fdt.h> #include <arm/freescale/fsl_ocotpreg.h> #include <arm/freescale/fsl_ocotpvar.h> @@ -85,8 +86,11 @@ struct imx6_anatop_softc { struct resource *res[2]; uint32_t cpu_curhz; uint32_t cpu_curmhz; + uint32_t cpu_curmv; uint32_t cpu_minhz; + uint32_t cpu_minmv; uint32_t cpu_maxhz; + uint32_t cpu_maxmv; uint32_t refosc_hz; void *temp_intrhand; uint32_t temp_high_val; @@ -103,12 +107,15 @@ struct imx6_anatop_softc { static struct imx6_anatop_softc *imx6_anatop_sc; /* - * Table of CPU max frequencies. This is indexed by the max frequency value - * (0-3) from the ocotp CFG3 register. + * Tables of CPU max frequencies and corresponding voltages. This is indexed by + * the max frequency value (0-3) from the ocotp CFG3 register. */ static uint32_t imx6_cpu_maxhz_tab[] = { 792000000, 852000000, 996000000, 1200000000 }; +static uint32_t imx6_cpu_millivolt_tab[] = { + 1150, 1225, 1225, 1275 +}; #define TZ_ZEROC 2732 /* deci-Kelvin <-> deci-Celcius offset. */ @@ -130,6 +137,64 @@ imx6_anatop_write_4(bus_size_t offset, u bus_write_4(imx6_anatop_sc->res[MEMRES], offset, value); } +static void +vdd_set(struct imx6_anatop_softc *sc, int mv) +{ + int newtarg, 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. + */ + + pmureg = imx6_anatop_read_4(IMX6_ANALOG_PMU_REG_CORE); + oldtarg = pmureg & IMX6_ANALOG_PMU_REG0_TARG_MASK; + + /* Convert mV to target value. Clamp target to valid range. */ + if (mv < 725) + newtarg = 0x00; + else if (mv > 1450) + newtarg = 0x1F; + else + newtarg = (mv - 700) / 25; + + /* + * 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. + */ + if (init_done) { + if (newtarg == oldtarg) + return; + else if (newtarg > oldtarg) + delay = (newtarg - oldtarg) * 3; + else + delay = 0; + } else { + delay = 700 / 25 * 3; + init_done = true; + } + + /* + * Make the change and wait for it to take effect. + */ + pmureg &= ~(IMX6_ANALOG_PMU_REG0_TARG_MASK | + IMX6_ANALOG_PMU_REG1_TARG_MASK | + IMX6_ANALOG_PMU_REG2_TARG_MASK); + + pmureg |= newtarg << IMX6_ANALOG_PMU_REG0_TARG_SHIFT; + pmureg |= newtarg << IMX6_ANALOG_PMU_REG1_TARG_SHIFT; + pmureg |= newtarg << IMX6_ANALOG_PMU_REG2_TARG_SHIFT; + + imx6_anatop_write_4(IMX6_ANALOG_PMU_REG_CORE, pmureg); + DELAY(delay); + sc->cpu_curmv = newtarg * 25 + 700; + device_printf(sc->dev, "voltage set to %u\n", sc->cpu_curmv); +} + static inline uint32_t cpufreq_hz_from_div(struct imx6_anatop_softc *sc, uint32_t div) { @@ -231,7 +296,9 @@ cpufreq_initialize(struct imx6_anatop_so FSL_OCOTP_CFG3_SPEED_MASK) >> FSL_OCOTP_CFG3_SPEED_SHIFT; sc->cpu_minhz = cpufreq_actual_hz(sc, imx6_cpu_maxhz_tab[0]); + sc->cpu_minmv = imx6_cpu_millivolt_tab[0]; sc->cpu_maxhz = cpufreq_actual_hz(sc, imx6_cpu_maxhz_tab[cfg3speed]); + sc->cpu_maxmv = imx6_cpu_millivolt_tab[cfg3speed]; /* * Set the CPU to maximum speed. @@ -241,6 +308,7 @@ cpufreq_initialize(struct imx6_anatop_so * basically assumes that a single core can't overheat before interrupts * are enabled; empirical testing shows that to be a safe assumption. */ + vdd_set(sc, sc->cpu_maxmv); cpufreq_set_clock(sc, sc->cpu_maxhz); device_printf(sc->dev, "CPU frequency %uMHz\n", sc->cpu_curmhz); } @@ -321,6 +389,7 @@ tempmon_gofast(struct imx6_anatop_softc { if (sc->cpu_curhz < sc->cpu_maxhz) { + vdd_set(sc, sc->cpu_maxmv); cpufreq_set_clock(sc, sc->cpu_maxhz); } } @@ -331,6 +400,7 @@ tempmon_goslow(struct imx6_anatop_softc if (sc->cpu_curhz > sc->cpu_minhz) { cpufreq_set_clock(sc, sc->cpu_minhz); + vdd_set(sc, sc->cpu_minmv); } } @@ -451,6 +521,11 @@ imx6_anatop_attach(device_t dev) if (err != 0) goto out; + SYSCTL_ADD_UINT(device_get_sysctl_ctx(sc->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), + OID_AUTO, "cpu_voltage", CTLFLAG_RD, + &sc->cpu_curmv, 0, "Current CPU voltage in millivolts"); + imx6_anatop_sc = sc; /*
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201402280026.s1S0QvBr069053>