diff options
author | ian <ian@FreeBSD.org> | 2014-05-17 20:52:10 +0000 |
---|---|---|
committer | ian <ian@FreeBSD.org> | 2014-05-17 20:52:10 +0000 |
commit | 941a79bd10fd8bfd6c52a9caa4896679445ee882 (patch) | |
tree | 5c80a695a527e1bfc6f2c394bf852151aef10bf4 /sys/arm | |
parent | e111203de4ff2e5b910a02a32c680c216158afc6 (diff) | |
download | FreeBSD-src-941a79bd10fd8bfd6c52a9caa4896679445ee882.zip FreeBSD-src-941a79bd10fd8bfd6c52a9caa4896679445ee882.tar.gz |
MFC 264052, 264057, 264065, 264094, 264103, 264120
Actually save the mpcore clock frequency retrieved from fdt data.
imx6..
- Don't call sdhci_init_slot() until after handling the FDT properties
related to detecting card presence.
- Flag several sysctl variables as tunables.
- Rework the cpu frequency management code for imx6 to add "operating
points" and min/max frequency controls.
generic timer...
- Setup both secure and non-secure timer IRQs.
We don't know our ARM security state, so one of them will operate.
- Don't set frequency, since it's unpossible in non-secure state.
Only rely on DTS clock-frequency value or get clock from timer.
Diffstat (limited to 'sys/arm')
-rw-r--r-- | sys/arm/arm/generic_timer.c | 54 | ||||
-rw-r--r-- | sys/arm/arm/mpcore_timer.c | 1 | ||||
-rw-r--r-- | sys/arm/freescale/imx/imx6_anatop.c | 235 | ||||
-rw-r--r-- | sys/arm/freescale/imx/imx_sdhci.c | 6 | ||||
-rw-r--r-- | sys/arm/samsung/exynos/chrome_kb.c | 1 |
5 files changed, 217 insertions, 80 deletions
diff --git a/sys/arm/arm/generic_timer.c b/sys/arm/arm/generic_timer.c index 1621e24..63e1717 100644 --- a/sys/arm/arm/generic_timer.c +++ b/sys/arm/arm/generic_timer.c @@ -73,16 +73,23 @@ __FBSDID("$FreeBSD$"); #define GT_CNTKCTL_PL0VCTEN (1 << 1) /* PL0 CNTVCT and CNTFRQ access */ #define GT_CNTKCTL_PL0PCTEN (1 << 0) /* PL0 CNTPCT and CNTFRQ access */ -#define GT_CNTPSIRQ 29 - struct arm_tmr_softc { - struct resource *irq_res; + struct resource *res[4]; + void *ihl[4]; uint32_t clkfreq; struct eventtimer et; }; static struct arm_tmr_softc *arm_tmr_sc = NULL; +static struct resource_spec timer_spec[] = { + { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Secure */ + { SYS_RES_IRQ, 1, RF_ACTIVE }, /* Non-secure */ + { SYS_RES_IRQ, 2, RF_ACTIVE }, /* Virt */ + { SYS_RES_IRQ, 3, RF_ACTIVE }, /* Hyp */ + { -1, 0 } +}; + static timecounter_get_t arm_tmr_get_timecount; static struct timecounter arm_tmr_timecount = { @@ -261,9 +268,8 @@ arm_tmr_attach(device_t dev) struct arm_tmr_softc *sc; phandle_t node; pcell_t clock; - void *ihl; - int rid; int error; + int i; sc = device_get_softc(dev); if (arm_tmr_sc) @@ -272,29 +278,37 @@ arm_tmr_attach(device_t dev) /* Get the base clock frequency */ node = ofw_bus_get_node(dev); error = OF_getprop(node, "clock-frequency", &clock, sizeof(clock)); - if (error <= 0) { - device_printf(dev, "missing clock-frequency " - "attribute in FDT\n"); + if (error > 0) { + sc->clkfreq = fdt32_to_cpu(clock); + } + + if (sc->clkfreq == 0) { + /* Try to get clock frequency from timer */ + sc->clkfreq = get_freq(); + } + + if (sc->clkfreq == 0) { + device_printf(dev, "No clock frequency specified\n"); return (ENXIO); } - sc->clkfreq = fdt32_to_cpu(clock); - rid = 0; - sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, - GT_CNTPSIRQ, GT_CNTPSIRQ, - 1, RF_SHAREABLE | RF_ACTIVE); + if (bus_alloc_resources(dev, timer_spec, sc->res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + }; arm_tmr_sc = sc; - /* Setup and enable the timer */ - if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_CLK, arm_tmr_intr, - NULL, sc, &ihl) != 0) { - bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res); - device_printf(dev, "Unable to setup the CLK irq handler.\n"); - return (ENXIO); + /* Setup secure and non-secure IRQs handler */ + for (i = 0; i < 2; i++) { + error = bus_setup_intr(dev, sc->res[i], INTR_TYPE_CLK, + arm_tmr_intr, NULL, sc, &sc->ihl[i]); + if (error) { + device_printf(dev, "Unable to alloc int resource.\n"); + return (ENXIO); + } } - set_freq(sc->clkfreq); disable_user_access(); arm_tmr_timecount.tc_frequency = sc->clkfreq; diff --git a/sys/arm/arm/mpcore_timer.c b/sys/arm/arm/mpcore_timer.c index 732c61e..bf30707 100644 --- a/sys/arm/arm/mpcore_timer.c +++ b/sys/arm/arm/mpcore_timer.c @@ -301,6 +301,7 @@ arm_tmr_attach(device_t dev) "attribute in FDT\n"); return (ENXIO); } + sc->clkfreq = clock; } } diff --git a/sys/arm/freescale/imx/imx6_anatop.c b/sys/arm/freescale/imx/imx6_anatop.c index eb784ed..b1757fb 100644 --- a/sys/arm/freescale/imx/imx6_anatop.c +++ b/sys/arm/freescale/imx/imx6_anatop.c @@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/callout.h> #include <sys/kernel.h> +#include <sys/limits.h> #include <sys/sysctl.h> #include <sys/module.h> #include <sys/bus.h> @@ -74,6 +75,8 @@ __FBSDID("$FreeBSD$"); #include <arm/freescale/imx/imx6_anatopreg.h> #include <arm/freescale/imx/imx6_anatopvar.h> +static SYSCTL_NODE(_hw, OID_AUTO, imx6, CTLFLAG_RW, NULL, "i.MX6 container"); + static struct resource_spec imx6_anatop_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, @@ -85,14 +88,15 @@ static struct resource_spec imx6_anatop_spec[] = { struct imx6_anatop_softc { device_t dev; struct resource *res[2]; - uint32_t cpu_curhz; uint32_t cpu_curmhz; uint32_t cpu_curmv; - uint32_t cpu_minhz; + uint32_t cpu_minmhz; uint32_t cpu_minmv; - uint32_t cpu_maxhz; + uint32_t cpu_maxmhz; uint32_t cpu_maxmv; - uint32_t refosc_hz; + uint32_t cpu_maxmhz_hw; + boolean_t cpu_overclock_enable; + uint32_t refosc_mhz; void *temp_intrhand; uint32_t temp_high_val; uint32_t temp_high_cnt; @@ -108,16 +112,27 @@ struct imx6_anatop_softc { static struct imx6_anatop_softc *imx6_anatop_sc; /* - * Tables of CPU max frequencies and corresponding voltages. This is indexed by - * the max frequency value (0-3) from the ocotp CFG3 register. + * Table of "operating points". + * These are combinations of frequency and voltage blessed by Freescale. */ -static uint32_t imx6_cpu_maxhz_tab[] = { - 792000000, 852000000, 996000000, 1200000000 -}; -static uint32_t imx6_cpu_millivolt_tab[] = { - 1150, 1225, 1225, 1275 +static struct oppt { + uint32_t mhz; + uint32_t mv; +} imx6_oppt_table[] = { +/* { 396, 925}, XXX: need functional ccm code for this speed */ + { 792, 1150}, + { 852, 1225}, + { 996, 1225}, + {1200, 1275}, }; +/* + * Table of CPU max frequencies. This is used to translate the max frequency + * value (0-3) from the ocotp CFG3 register into a mhz value that can be looked + * up in the operating points table. + */ +static uint32_t imx6_ocotp_mhz_tab[] = {792, 852, 996, 1200}; + #define TZ_ZEROC 2732 /* deci-Kelvin <-> deci-Celcius offset. */ uint32_t @@ -193,49 +208,58 @@ vdd_set(struct imx6_anatop_softc *sc, int mv) 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) +cpufreq_mhz_from_div(struct imx6_anatop_softc *sc, uint32_t div) { - return (sc->refosc_hz * (div / 2)); + return (sc->refosc_mhz * (div / 2)); } static inline uint32_t -cpufreq_hz_to_div(struct imx6_anatop_softc *sc, uint32_t cpu_hz) +cpufreq_mhz_to_div(struct imx6_anatop_softc *sc, uint32_t cpu_mhz) { - return (cpu_hz / (sc->refosc_hz / 2)); + return (cpu_mhz / (sc->refosc_mhz / 2)); } static inline uint32_t -cpufreq_actual_hz(struct imx6_anatop_softc *sc, uint32_t cpu_hz) +cpufreq_actual_mhz(struct imx6_anatop_softc *sc, uint32_t cpu_mhz) { - return (cpufreq_hz_from_div(sc, cpufreq_hz_to_div(sc, cpu_hz))); + return (cpufreq_mhz_from_div(sc, cpufreq_mhz_to_div(sc, cpu_mhz))); +} + +static struct oppt * +cpufreq_nearest_oppt(struct imx6_anatop_softc *sc, uint32_t cpu_newmhz) +{ + int d, diff, i, nearest; + + if (cpu_newmhz > sc->cpu_maxmhz_hw && !sc->cpu_overclock_enable) + cpu_newmhz = sc->cpu_maxmhz_hw; + + diff = INT_MAX; + nearest = 0; + for (i = 0; i < nitems(imx6_oppt_table); ++i) { + d = abs((int)cpu_newmhz - (int)imx6_oppt_table[i].mhz); + if (diff > d) { + diff = d; + nearest = i; + } + } + return (&imx6_oppt_table[nearest]); } static void -cpufreq_set_clock(struct imx6_anatop_softc * sc, uint32_t cpu_newhz) +cpufreq_set_clock(struct imx6_anatop_softc * sc, struct oppt *op) { - uint32_t div, timeout, wrk32; - const uint32_t mindiv = 54; - const uint32_t maxdiv = 108; + uint32_t timeout, wrk32; - /* - * Clip the requested frequency to the configured max, then clip the - * resulting divisor to the documented min/max values. - */ - cpu_newhz = min(cpu_newhz, sc->cpu_maxhz); - div = cpufreq_hz_to_div(sc, cpu_newhz); - if (div < mindiv) - div = mindiv; - else if (div > maxdiv) - div = maxdiv; - sc->cpu_curhz = cpufreq_hz_from_div(sc, div); - sc->cpu_curmhz = sc->cpu_curhz / 1000000; + /* If increasing the frequency, we must first increase the voltage. */ + if (op->mhz > sc->cpu_curmhz) { + vdd_set(sc, op->mv); + } /* * I can't find a documented procedure for changing the ARM PLL divisor, @@ -244,7 +268,7 @@ cpufreq_set_clock(struct imx6_anatop_softc * sc, uint32_t cpu_newhz) * - Set the PLL into bypass mode; cpu should now be running at 24mhz. * - Change the divisor. * - Wait for the LOCK bit to come on; it takes ~50 loop iterations. - * - Turn off bypass mode; cpu should now be running at cpu_newhz. + * - Turn off bypass mode; cpu should now be running at the new speed. */ imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_CLR, IMX6_ANALOG_CCM_PLL_ARM_CLK_SRC_MASK); @@ -253,7 +277,7 @@ cpufreq_set_clock(struct imx6_anatop_softc * sc, uint32_t cpu_newhz) wrk32 = imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM); wrk32 &= ~IMX6_ANALOG_CCM_PLL_ARM_DIV_MASK; - wrk32 |= div; + wrk32 |= cpufreq_mhz_to_div(sc, op->mhz); imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM, wrk32); timeout = 10000; @@ -265,26 +289,114 @@ cpufreq_set_clock(struct imx6_anatop_softc * sc, uint32_t cpu_newhz) imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_CLR, IMX6_ANALOG_CCM_PLL_ARM_BYPASS); - arm_tmr_change_frequency(sc->cpu_curhz / 2); + /* If lowering the frequency, it is now safe to lower the voltage. */ + if (op->mhz < sc->cpu_curmhz) + vdd_set(sc, op->mv); + sc->cpu_curmhz = op->mhz; + + /* Tell the mpcore timer that its frequency has changed. */ + arm_tmr_change_frequency( + cpufreq_actual_mhz(sc, sc->cpu_curmhz) * 1000000 / 2); +} + +static int +cpufreq_sysctl_minmhz(SYSCTL_HANDLER_ARGS) +{ + struct imx6_anatop_softc *sc; + struct oppt * op; + uint32_t temp; + int err; + + sc = arg1; + + temp = sc->cpu_minmhz; + err = sysctl_handle_int(oidp, &temp, 0, req); + if (err != 0 || req->newptr == NULL) + return (err); + + op = cpufreq_nearest_oppt(sc, temp); + if (op->mhz > sc->cpu_maxmhz) + return (ERANGE); + else if (op->mhz == sc->cpu_minmhz) + return (0); + + /* + * Value changed, update softc. If the new min is higher than the + * current speed, raise the current speed to match. + */ + sc->cpu_minmhz = op->mhz; + if (sc->cpu_minmhz > sc->cpu_curmhz) { + cpufreq_set_clock(sc, op); + } + return (err); +} + +static int +cpufreq_sysctl_maxmhz(SYSCTL_HANDLER_ARGS) +{ + struct imx6_anatop_softc *sc; + struct oppt * op; + uint32_t temp; + int err; + + sc = arg1; + + temp = sc->cpu_maxmhz; + err = sysctl_handle_int(oidp, &temp, 0, req); + if (err != 0 || req->newptr == NULL) + return (err); + + op = cpufreq_nearest_oppt(sc, temp); + if (op->mhz < sc->cpu_minmhz) + return (ERANGE); + else if (op->mhz == sc->cpu_maxmhz) + return (0); + + /* + * Value changed, update softc and hardware. The hardware update is + * unconditional. We always try to run at max speed, so any change of + * the max means we need to change the current speed too, regardless of + * whether it is higher or lower than the old max. + */ + sc->cpu_maxmhz = op->mhz; + cpufreq_set_clock(sc, op); + + return (err); } static void cpufreq_initialize(struct imx6_anatop_softc *sc) { uint32_t cfg3speed; - struct sysctl_ctx_list *ctx; + struct oppt * op; - ctx = device_get_sysctl_ctx(sc->dev); - SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), + SYSCTL_ADD_INT(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx6), OID_AUTO, "cpu_mhz", CTLFLAG_RD, &sc->cpu_curmhz, 0, - "CPU frequency in MHz"); + "CPU frequency"); + + SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx6), + OID_AUTO, "cpu_minmhz", CTLTYPE_INT | CTLFLAG_RWTUN, sc, 0, + cpufreq_sysctl_minmhz, "IU", "Minimum CPU frequency"); + + SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx6), + OID_AUTO, "cpu_maxmhz", CTLTYPE_INT | CTLFLAG_RWTUN, sc, 0, + cpufreq_sysctl_maxmhz, "IU", "Maximum CPU frequency"); + + SYSCTL_ADD_INT(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx6), + OID_AUTO, "cpu_maxmhz_hw", CTLFLAG_RD, &sc->cpu_maxmhz_hw, 0, + "Maximum CPU frequency allowed by hardware"); + + SYSCTL_ADD_INT(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx6), + OID_AUTO, "cpu_overclock_enable", CTLFLAG_RWTUN, + &sc->cpu_overclock_enable, 0, + "Allow setting CPU frequency higher than cpu_maxmhz_hw"); /* * XXX 24mhz shouldn't be hard-coded, should get this from imx6_ccm * (even though in the real world it will always be 24mhz). Oh wait a * sec, I never wrote imx6_ccm. */ - sc->refosc_hz = 24000000; + sc->refosc_mhz = 24; /* * Get the maximum speed this cpu can be set to. The values in the @@ -294,14 +406,25 @@ cpufreq_initialize(struct imx6_anatop_softc *sc) * - 2b'10: 996000000Hz; * - 2b'01: 852000000Hz; -- i.MX6Q Only, exclusive with 996MHz. * - 2b'00: 792000000Hz; + * The default hardware max speed can be overridden by a tunable. */ cfg3speed = (fsl_ocotp_read_4(FSL_OCOTP_CFG3) & FSL_OCOTP_CFG3_SPEED_MASK) >> FSL_OCOTP_CFG3_SPEED_SHIFT; + sc->cpu_maxmhz_hw = imx6_ocotp_mhz_tab[cfg3speed]; + sc->cpu_maxmhz = sc->cpu_maxmhz_hw; - 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]; + TUNABLE_INT_FETCH("hw.imx6.cpu_overclock_enable", + &sc->cpu_overclock_enable); + + TUNABLE_INT_FETCH("hw.imx6.cpu_minmhz", &sc->cpu_minmhz); + op = cpufreq_nearest_oppt(sc, sc->cpu_minmhz); + sc->cpu_minmhz = op->mhz; + sc->cpu_minmv = op->mv; + + TUNABLE_INT_FETCH("hw.imx6.cpu_maxmhz", &sc->cpu_maxmhz); + op = cpufreq_nearest_oppt(sc, sc->cpu_maxmhz); + sc->cpu_maxmhz = op->mhz; + sc->cpu_maxmv = op->mv; /* * Set the CPU to maximum speed. @@ -311,9 +434,7 @@ cpufreq_initialize(struct imx6_anatop_softc *sc) * 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); + cpufreq_set_clock(sc, op); } static inline uint32_t @@ -391,9 +512,8 @@ static void tempmon_gofast(struct imx6_anatop_softc *sc) { - if (sc->cpu_curhz < sc->cpu_maxhz) { - vdd_set(sc, sc->cpu_maxmv); - cpufreq_set_clock(sc, sc->cpu_maxhz); + if (sc->cpu_curmhz < sc->cpu_maxmhz) { + cpufreq_set_clock(sc, cpufreq_nearest_oppt(sc, sc->cpu_maxmhz)); } } @@ -401,9 +521,8 @@ static void tempmon_goslow(struct imx6_anatop_softc *sc) { - if (sc->cpu_curhz > sc->cpu_minhz) { - cpufreq_set_clock(sc, sc->cpu_minhz); - vdd_set(sc, sc->cpu_minmv); + if (sc->cpu_curmhz > sc->cpu_minmhz) { + cpufreq_set_clock(sc, cpufreq_nearest_oppt(sc, sc->cpu_minmhz)); } } @@ -542,6 +661,10 @@ imx6_anatop_attach(device_t dev) cpufreq_initialize(sc); initialize_tempmon(sc); + if (bootverbose) { + device_printf(sc->dev, "CPU %uMHz @ %umV\n", sc->cpu_curmhz, + sc->cpu_curmv); + } err = 0; out: @@ -575,7 +698,7 @@ imx6_get_cpu_clock() div = imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM) & IMX6_ANALOG_CCM_PLL_ARM_DIV_MASK; - return (cpufreq_hz_from_div(imx6_anatop_sc, div)); + return (cpufreq_mhz_from_div(imx6_anatop_sc, div)); } static device_method_t imx6_anatop_methods[] = { diff --git a/sys/arm/freescale/imx/imx_sdhci.c b/sys/arm/freescale/imx/imx_sdhci.c index 2d83fd6..2e3d1a5 100644 --- a/sys/arm/freescale/imx/imx_sdhci.c +++ b/sys/arm/freescale/imx/imx_sdhci.c @@ -730,9 +730,6 @@ imx_sdhci_attach(device_t dev) sc->baseclk_hz = imx51_get_clock(IMX51CLK_PERCLK_ROOT); } - sdhci_init_slot(dev, &sc->slot, 0); - callout_init(&sc->r1bfix_callout, true); - /* * If the slot is flagged with the non-removable property, set our flag * to always force the SDHCI_CARD_PRESENT bit on. @@ -752,6 +749,9 @@ imx_sdhci_attach(device_t dev) sc->force_card_present = true; } + callout_init(&sc->r1bfix_callout, true); + sdhci_init_slot(dev, &sc->slot, 0); + bus_generic_probe(dev); bus_generic_attach(dev); diff --git a/sys/arm/samsung/exynos/chrome_kb.c b/sys/arm/samsung/exynos/chrome_kb.c index 9129131..1ce02811 100644 --- a/sys/arm/samsung/exynos/chrome_kb.c +++ b/sys/arm/samsung/exynos/chrome_kb.c @@ -138,7 +138,6 @@ struct ckb_softc { }; /* prototypes */ -static void ckb_set_leds(struct ckb_softc *, uint8_t); static int ckb_set_typematic(keyboard_t *, int); static uint32_t ckb_read_char(keyboard_t *, int); static void ckb_clear_state(keyboard_t *); |