diff --git a/sys/arm/freescale/imx/imx6_anatop.c b/sys/arm/freescale/imx/imx6_anatop.c index 893fb1807930..c39365619566 100644 --- a/sys/arm/freescale/imx/imx6_anatop.c +++ b/sys/arm/freescale/imx/imx6_anatop.c @@ -1,158 +1,521 @@ /*- * Copyright (c) 2013 Ian Lepore + * Copyright (c) 2014 Steven Lawrance * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Analog PLL and power regulator driver for Freescale i.MX6 family of SoCs. + * Also, temperature montoring and cpu frequency control. It was Freescale who + * kitchen-sinked this device, not us. :) * * We don't really do anything with analog PLLs, but the registers for * controlling them belong to the same block as the power regulator registers. * Since the newbus hierarchy makes it hard for anyone other than us to get at * them, we just export a couple public functions to allow the imx6 CCM clock * driver to read and write those registers. * * We also don't do anything about power regulation yet, but when the need * arises, this would be the place for that code to live. * * I have no idea where the "anatop" name comes from. It's in the standard DTS * source describing i.MX6 SoCs, and in the linux and u-boot code which comes * from Freescale, but it's not in the SoC manual. + * + * Note that temperature values throughout this code are handled in two types of + * units. Items with '_cnt' in the name use the hardware temperature count + * units (higher counts are lower temperatures). Items with '_val' in the name + * are deci-Celcius, which are converted to/from deci-Kelvins in the sysctl + * handlers (dK is the standard unit for temperature in sysctl). */ #include #include +#include #include +#include #include #include #include #include #include #include +#include +#include #include #include +static struct resource_spec imx6_anatop_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; +#define MEMRES 0 +#define IRQRES 1 + struct imx6_anatop_softc { device_t dev; - struct resource *mem_res; + struct resource *res[2]; + uint32_t cpu_curhz; + uint32_t cpu_curmhz; + uint32_t cpu_minhz; + uint32_t cpu_maxhz; + uint32_t refosc_hz; + void *temp_intrhand; + uint32_t temp_high_val; + uint32_t temp_high_cnt; + uint32_t temp_last_cnt; + uint32_t temp_room_cnt; + struct callout temp_throttle_callout; + sbintime_t temp_throttle_delay; + uint32_t temp_throttle_reset_cnt; + uint32_t temp_throttle_trigger_cnt; + uint32_t temp_throttle_val; }; 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. + */ +static uint32_t imx6_cpu_maxhz_tab[] = { + 792000000, 852000000, 996000000, 1200000000 +}; + +#define TZ_ZEROC 2732 /* deci-Kelvin <-> deci-Celcius offset. */ + uint32_t imx6_anatop_read_4(bus_size_t offset) { - return (bus_read_4(imx6_anatop_sc->mem_res, offset)); + KASSERT(imx6_anatop_sc != NULL, ("imx6_anatop_read_4 sc NULL")); + + return (bus_read_4(imx6_anatop_sc->res[MEMRES], offset)); } void imx6_anatop_write_4(bus_size_t offset, uint32_t value) { - bus_write_4(imx6_anatop_sc->mem_res, offset, value); + KASSERT(imx6_anatop_sc != NULL, ("imx6_anatop_write_4 sc NULL")); + + bus_write_4(imx6_anatop_sc->res[MEMRES], offset, value); +} + +static inline uint32_t +cpufreq_hz_from_div(struct imx6_anatop_softc *sc, uint32_t div) +{ + + return (sc->refosc_hz * (div / 2)); +} + +static inline uint32_t +cpufreq_hz_to_div(struct imx6_anatop_softc *sc, uint32_t cpu_hz) +{ + + return (cpu_hz / (sc->refosc_hz / 2)); +} + +static inline uint32_t +cpufreq_actual_hz(struct imx6_anatop_softc *sc, uint32_t cpu_hz) +{ + + return (cpufreq_hz_from_div(sc, cpufreq_hz_to_div(sc, cpu_hz))); +} + +static void +cpufreq_set_clock(struct imx6_anatop_softc * sc, uint32_t cpu_newhz) +{ + uint32_t div, timeout, wrk32; + const uint32_t mindiv = 54; + const uint32_t maxdiv = 108; + + /* + * 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; + + /* + * I can't find a documented procedure for changing the ARM PLL divisor, + * but some trial and error came up with this: + * - Set the bypass clock source to REF_CLK_24M (source #0). + * - 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. + */ + 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, + IMX6_ANALOG_CCM_PLL_ARM_BYPASS); + + wrk32 = imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM); + wrk32 &= ~IMX6_ANALOG_CCM_PLL_ARM_DIV_MASK; + wrk32 |= div; + imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM, wrk32); + + timeout = 10000; + while ((imx6_anatop_read_4(IMX6_ANALOG_CCM_PLL_ARM) & + IMX6_ANALOG_CCM_PLL_ARM_LOCK) == 0) + if (--timeout == 0) + panic("imx6_set_cpu_clock(): PLL never locked"); + + imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_ARM_CLR, + IMX6_ANALOG_CCM_PLL_ARM_BYPASS); +} + +static void +cpufreq_initialize(struct imx6_anatop_softc *sc) +{ + uint32_t cfg3speed; + struct sysctl_ctx_list *ctx; + + ctx = device_get_sysctl_ctx(sc->dev); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), + OID_AUTO, "cpu_mhz", CTLFLAG_RD, &sc->cpu_curmhz, 0, + "CPU frequency in MHz"); + + /* + * 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; + + /* + * Get the maximum speed this cpu can be set to. The values in the + * OCOTP CFG3 register are not documented in the reference manual. + * The following info was in an archived email found via web search: + * - 2b'11: 1200000000Hz; + * - 2b'10: 996000000Hz; + * - 2b'01: 852000000Hz; -- i.MX6Q Only, exclusive with 996MHz. + * - 2b'00: 792000000Hz; + */ + cfg3speed = (fsl_ocotp_read_4(FSL_OCOTP_CFG3) & + FSL_OCOTP_CFG3_SPEED_MASK) >> FSL_OCOTP_CFG3_SPEED_SHIFT; + + sc->cpu_minhz = cpufreq_actual_hz(sc, imx6_cpu_maxhz_tab[0]); + sc->cpu_maxhz = cpufreq_actual_hz(sc, imx6_cpu_maxhz_tab[cfg3speed]); + + /* + * Set the CPU to maximum speed. + * + * We won't have thermal throttling until interrupts are enabled, but we + * want to run at full speed through all the device init stuff. This + * basically assumes that a single core can't overheat before interrupts + * are enabled; empirical testing shows that to be a safe assumption. + */ + cpufreq_set_clock(sc, sc->cpu_maxhz); + device_printf(sc->dev, "CPU frequency %uMHz\n", sc->cpu_curmhz); +} + +static inline uint32_t +temp_from_count(struct imx6_anatop_softc *sc, uint32_t count) +{ + + return (((sc->temp_high_val - (count - sc->temp_high_cnt) * + (sc->temp_high_val - 250) / + (sc->temp_room_cnt - sc->temp_high_cnt)))); +} + +static inline uint32_t +temp_to_count(struct imx6_anatop_softc *sc, uint32_t temp) +{ + + return ((sc->temp_room_cnt - sc->temp_high_cnt) * + (sc->temp_high_val - temp) / (sc->temp_high_val - 250) + + sc->temp_high_cnt); +} + +static void +temp_update_count(struct imx6_anatop_softc *sc) +{ + uint32_t val; + + val = imx6_anatop_read_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0); + if (!(val & IMX6_ANALOG_TEMPMON_TEMPSENSE0_VALID)) + return; + sc->temp_last_cnt = + (val & IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_MASK) >> + IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_SHIFT; } static int -imx6_anatop_detach(device_t dev) +temp_sysctl_handler(SYSCTL_HANDLER_ARGS) { - struct imx6_anatop_softc *sc; + struct imx6_anatop_softc *sc = arg1; + uint32_t t; - sc = device_get_softc(dev); + temp_update_count(sc); + + t = temp_from_count(sc, sc->temp_last_cnt) + TZ_ZEROC; + + return (sysctl_handle_int(oidp, &t, 0, req)); +} + +static int +temp_throttle_sysctl_handler(SYSCTL_HANDLER_ARGS) +{ + struct imx6_anatop_softc *sc = arg1; + int err; + uint32_t temp; + + temp = sc->temp_throttle_val + TZ_ZEROC; + err = sysctl_handle_int(oidp, &temp, 0, req); + if (temp < TZ_ZEROC) + return (ERANGE); + temp -= TZ_ZEROC; + if (err != 0 || req->newptr == NULL || temp == sc->temp_throttle_val) + return (err); + + /* Value changed, update counts in softc and hardware. */ + sc->temp_throttle_val = temp; + sc->temp_throttle_trigger_cnt = temp_to_count(sc, sc->temp_throttle_val); + sc->temp_throttle_reset_cnt = temp_to_count(sc, sc->temp_throttle_val - 100); + imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0_CLR, + IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_MASK); + imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0_SET, + (sc->temp_throttle_trigger_cnt << + IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_SHIFT)); + return (err); +} + +static void +tempmon_gofast(struct imx6_anatop_softc *sc) +{ + + if (sc->cpu_curhz < sc->cpu_maxhz) { + cpufreq_set_clock(sc, sc->cpu_maxhz); + } +} + +static void +tempmon_goslow(struct imx6_anatop_softc *sc) +{ + + if (sc->cpu_curhz > sc->cpu_minhz) { + cpufreq_set_clock(sc, sc->cpu_minhz); + } +} + +static int +tempmon_intr(void *arg) +{ + struct imx6_anatop_softc *sc = arg; + + /* + * XXX Note that this code doesn't currently run (for some mysterious + * reason we just never get an interrupt), so the real monitoring is + * done by tempmon_throttle_check(). + */ + tempmon_goslow(sc); + /* XXX Schedule callout to speed back up eventually. */ + return (FILTER_HANDLED); +} + +static void +tempmon_throttle_check(void *arg) +{ + struct imx6_anatop_softc *sc = arg; - if (sc->mem_res != NULL) - bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + /* Lower counts are higher temperatures. */ + if (sc->temp_last_cnt < sc->temp_throttle_trigger_cnt) + tempmon_goslow(sc); + else if (sc->temp_last_cnt > (sc->temp_throttle_reset_cnt)) + tempmon_gofast(sc); - return (0); + callout_reset_sbt(&sc->temp_throttle_callout, sc->temp_throttle_delay, + 0, tempmon_throttle_check, sc, 0); + +} + +static void +initialize_tempmon(struct imx6_anatop_softc *sc) +{ + uint32_t cal; + struct sysctl_ctx_list *ctx; + + /* + * Fetch calibration data: a sensor count at room temperature (25C), + * a sensor count at a high temperature, and that temperature + */ + cal = fsl_ocotp_read_4(FSL_OCOTP_ANA1); + sc->temp_room_cnt = (cal & 0xFFF00000) >> 20; + sc->temp_high_cnt = (cal & 0x000FFF00) >> 8; + sc->temp_high_val = (cal & 0x000000FF) * 10; + + /* + * Throttle to a lower cpu freq at 10C below the "hot" temperature, and + * reset back to max cpu freq at 5C below the trigger. + */ + sc->temp_throttle_val = sc->temp_high_val - 100; + sc->temp_throttle_trigger_cnt = + temp_to_count(sc, sc->temp_throttle_val); + sc->temp_throttle_reset_cnt = + temp_to_count(sc, sc->temp_throttle_val - 50); + + /* + * Set the sensor to sample automatically at 16Hz (32.768KHz/0x800), set + * the throttle count, and begin making measurements. + */ + imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE1, 0x0800); + imx6_anatop_write_4(IMX6_ANALOG_TEMPMON_TEMPSENSE0, + (sc->temp_throttle_trigger_cnt << + IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_SHIFT) | + IMX6_ANALOG_TEMPMON_TEMPSENSE0_MEASURE); + + /* + * XXX Note that the alarm-interrupt feature isn't working yet, so + * we'll use a callout handler to check at 10Hz. Make sure we have an + * initial temperature reading before starting up the callouts so we + * don't get a bogus reading of zero. + */ + while (sc->temp_last_cnt == 0) + temp_update_count(sc); + sc->temp_throttle_delay = 100 * SBT_1MS; + callout_init(&sc->temp_throttle_callout, 0); + callout_reset_sbt(&sc->temp_throttle_callout, sc->temp_throttle_delay, + 0, tempmon_throttle_check, sc, 0); + + ctx = device_get_sysctl_ctx(sc->dev); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), + OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0, + temp_sysctl_handler, "IK", "Current die temperature"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), + OID_AUTO, "throttle_temperature", CTLTYPE_INT | CTLFLAG_RW, sc, + 0, temp_throttle_sysctl_handler, "IK", + "Throttle CPU when exceeding this temperature"); +} + +static int +imx6_anatop_detach(device_t dev) +{ + + return (EBUSY); } static int imx6_anatop_attach(device_t dev) { struct imx6_anatop_softc *sc; - int err, rid; + int err; sc = device_get_softc(dev); + sc->dev = dev; /* Allocate bus_space resources. */ - rid = 0; - sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, - RF_ACTIVE); - if (sc->mem_res == NULL) { - device_printf(dev, "Cannot allocate memory resources\n"); + if (bus_alloc_resources(dev, imx6_anatop_spec, sc->res)) { + device_printf(dev, "Cannot allocate resources\n"); err = ENXIO; goto out; } + err = bus_setup_intr(dev, sc->res[IRQRES], INTR_TYPE_MISC | INTR_MPSAFE, + tempmon_intr, NULL, sc, &sc->temp_intrhand); + if (err != 0) + goto out; + imx6_anatop_sc = sc; + + /* + * Other code seen on the net sets this SELFBIASOFF flag around the same + * time the temperature sensor is set up, although it's unclear how the + * two are related (if at all). + */ + imx6_anatop_write_4(IMX6_ANALOG_PMU_MISC0_SET, + IMX6_ANALOG_PMU_MISC0_SELFBIASOFF); + + cpufreq_initialize(sc); + initialize_tempmon(sc); + err = 0; out: - if (err != 0) - imx6_anatop_detach(dev); + if (err != 0) { + bus_release_resources(dev, imx6_anatop_spec, sc->res); + } return (err); } static int imx6_anatop_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (ofw_bus_is_compatible(dev, "fsl,imx6q-anatop") == 0) + if (ofw_bus_is_compatible(dev, "fsl,imx6q-anatop") == 0) return (ENXIO); device_set_desc(dev, "Freescale i.MX6 Analog PLLs and Power"); return (BUS_PROBE_DEFAULT); } +uint32_t +imx6_get_cpu_clock() +{ + uint32_t div; + + 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)); +} + static device_method_t imx6_anatop_methods[] = { /* Device interface */ DEVMETHOD(device_probe, imx6_anatop_probe), DEVMETHOD(device_attach, imx6_anatop_attach), DEVMETHOD(device_detach, imx6_anatop_detach), DEVMETHOD_END }; static driver_t imx6_anatop_driver = { "imx6_anatop", imx6_anatop_methods, sizeof(struct imx6_anatop_softc) }; static devclass_t imx6_anatop_devclass; DRIVER_MODULE(imx6_anatop, simplebus, imx6_anatop_driver, imx6_anatop_devclass, 0, 0); diff --git a/sys/arm/freescale/imx/imx6_anatopreg.h b/sys/arm/freescale/imx/imx6_anatopreg.h index bcff8080e48a..bb75735c521b 100644 --- a/sys/arm/freescale/imx/imx6_anatopreg.h +++ b/sys/arm/freescale/imx/imx6_anatopreg.h @@ -1,141 +1,188 @@ /*- * Copyright (c) 2013 Ian Lepore * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef IMX6_ANATOPREG_H #define IMX6_ANATOPREG_H #define IMX6_ANALOG_CCM_PLL_ARM 0x000 #define IMX6_ANALOG_CCM_PLL_ARM_SET 0x004 #define IMX6_ANALOG_CCM_PLL_ARM_CLR 0x008 #define IMX6_ANALOG_CCM_PLL_ARM_TOG 0x00C +#define IMX6_ANALOG_CCM_PLL_ARM_DIV_MASK 0x7F +#define IMX6_ANALOG_CCM_PLL_ARM_LOCK (1U << 31) +#define IMX6_ANALOG_CCM_PLL_ARM_BYPASS (1 << 16) +#define IMX6_ANALOG_CCM_PLL_ARM_CLK_SRC_MASK (0x03 << 16) #define IMX6_ANALOG_CCM_PLL_USB1 0x010 #define IMX6_ANALOG_CCM_PLL_USB1_SET 0x014 #define IMX6_ANALOG_CCM_PLL_USB1_CLR 0x018 #define IMX6_ANALOG_CCM_PLL_USB1_TOG 0x01C #define IMX6_ANALOG_CCM_PLL_USB_LOCK (1U << 31) #define IMX6_ANALOG_CCM_PLL_USB_BYPASS (1 << 16) #define IMX6_ANALOG_CCM_PLL_USB_ENABLE (1 << 13) #define IMX6_ANALOG_CCM_PLL_USB_POWER (1 << 12) #define IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS (1 << 6) #define IMX6_ANALOG_CCM_PLL_USB2 0x020 #define IMX6_ANALOG_CCM_PLL_USB2_SET 0x024 #define IMX6_ANALOG_CCM_PLL_USB2_CLR 0x028 #define IMX6_ANALOG_CCM_PLL_USB2_TOG 0x02C #define IMX6_ANALOG_CCM_PLL_SYS 0x030 #define IMX6_ANALOG_CCM_PLL_SYS_SET 0x034 #define IMX6_ANALOG_CCM_PLL_SYS_CLR 0x038 #define IMX6_ANALOG_CCM_PLL_SYS_TOG 0x03C #define IMX6_ANALOG_CCM_PLL_SYS_SS 0x040 #define IMX6_ANALOG_CCM_PLL_SYS_NUM 0x050 #define IMX6_ANALOG_CCM_PLL_SYS_DENOM 0x060 #define IMX6_ANALOG_CCM_PLL_AUDIO 0x070 #define IMX6_ANALOG_CCM_PLL_AUDIO_SET 0x074 #define IMX6_ANALOG_CCM_PLL_AUDIO_CLR 0x078 #define IMX6_ANALOG_CCM_PLL_AUDIO_TOG 0x07C #define IMX6_ANALOG_CCM_PLL_AUDIO_NUM 0x080 #define IMX6_ANALOG_CCM_PLL_AUDIO_DENOM 0x090 #define IMX6_ANALOG_CCM_PLL_VIDEO 0x0A0 #define IMX6_ANALOG_CCM_PLL_VIDEO_SET 0x0A4 #define IMX6_ANALOG_CCM_PLL_VIDEO_CLR 0x0A8 #define IMX6_ANALOG_CCM_PLL_VIDEO_TOG 0x0AC #define IMX6_ANALOG_CCM_PLL_VIDEO_NUM 0x0B0 #define IMX6_ANALOG_CCM_PLL_VIDEO_DENOM 0x0C0 #define IMX6_ANALOG_CCM_PLL_MLB 0x0D0 #define IMX6_ANALOG_CCM_PLL_MLB_SET 0x0D4 #define IMX6_ANALOG_CCM_PLL_MLB_CLR 0x0D8 #define IMX6_ANALOG_CCM_PLL_MLB_TOG 0x0DC #define IMX6_ANALOG_CCM_PLL_ENET 0x0E0 #define IMX6_ANALOG_CCM_PLL_ENET_SET 0x0E4 #define IMX6_ANALOG_CCM_PLL_ENET_CLR 0x0E8 #define IMX6_ANALOG_CCM_PLL_ENET_TOG 0x0EC #define IMX6_ANALOG_CCM_PFD_480 0x0F0 #define IMX6_ANALOG_CCM_PFD_480_SET 0x0F4 #define IMX6_ANALOG_CCM_PFD_480_CLR 0x0F8 #define IMX6_ANALOG_CCM_PFD_480_TOG 0x0FC #define IMX6_ANALOG_CCM_PFD_528 0x100 #define IMX6_ANALOG_CCM_PFD_528_SET 0x104 #define IMX6_ANALOG_CCM_PFD_528_CLR 0x108 #define IMX6_ANALOG_CCM_PFD_528_TOG 0x10C + #define IMX6_ANALOG_PMU_REG_CORE 0x140 #define IMX6_ANALOG_PMU_REG2_TARG_SHIFT 18 #define IMX6_ANALOG_PMU_REG2_TARG_MASK \ (0x1f << IMX6_ANALOG_PMU_REG2_TARG_SHIFT) #define IMX6_ANALOG_PMU_REG1_TARG_SHIFT 9 #define IMX6_ANALOG_PMU_REG1_TARG_MASK \ (0x1f << IMX6_ANALOG_PMU_REG1_TARG_SHIFT) #define IMX6_ANALOG_PMU_REG0_TARG_SHIFT 0 #define IMX6_ANALOG_PMU_REG0_TARG_MASK \ (0x1f << IMX6_ANALOG_PMU_REG0_TARG_SHIFT) -#define IMX6_ANALOG_CCM_MISC0 0x150 -#define IMX6_ANALOG_CCM_MISC0_SET 0x154 -#define IMX6_ANALOG_CCM_MISC0_CLR 0x158 -#define IMX6_ANALOG_CCM_MISC0_TOG 0x15C -#define IMX6_ANALOG_CCM_MISC2 0x170 -#define IMX6_ANALOG_CCM_MISC2_SET 0x174 -#define IMX6_ANALOG_CCM_MISC2_CLR 0x178 -#define IMX6_ANALOG_CCM_MISC2_TOG 0x17C +#define IMX6_ANALOG_PMU_MISC0 0x150 +#define IMX6_ANALOG_PMU_MISC0_SET 0x154 +#define IMX6_ANALOG_PMU_MISC0_CLR 0x158 +#define IMX6_ANALOG_PMU_MISC0_TOG 0x15C +#define IMX6_ANALOG_PMU_MISC0_SELFBIASOFF (1 << 3) + +#define IMX6_ANALOG_PMU_MISC1 0x160 +#define IMX6_ANALOG_PMU_MISC1_SET 0x164 +#define IMX6_ANALOG_PMU_MISC1_CLR 0x168 +#define IMX6_ANALOG_PMU_MISC1_TOG 0x16C +#define IMX6_ANALOG_PMU_MISC1_IRQ_TEMPSENSE (1 << 29) + +#define IMX6_ANALOG_PMU_MISC2 0x170 +#define IMX6_ANALOG_PMU_MISC2_SET 0x174 +#define IMX6_ANALOG_PMU_MISC2_CLR 0x178 +#define IMX6_ANALOG_PMU_MISC2_TOG 0x17C + +/* + * Note that the ANALOG_CCM_MISCn registers are the same as the PMU_MISCn + * registers; some bits conceptually belong to the PMU and some to the CCM. + */ +#define IMX6_ANALOG_CCM_MISC0 IMX6_ANALOG_PMU_MISC0 +#define IMX6_ANALOG_CCM_MISC0_SET IMX6_ANALOG_PMU_MISC0_SET +#define IMX6_ANALOG_CCM_MISC0_CLR IMX6_ANALOG_PMU_MISC0_CLR +#define IMX6_ANALOG_CCM_MISC0_TOG IMX6_ANALOG_PMU_MISC0_TOG + +#define IMX6_ANALOG_CCM_MISC2 IMX6_ANALOG_PMU_MISC2 +#define IMX6_ANALOG_CCM_MISC2_SET IMX6_ANALOG_PMU_MISC2_SET +#define IMX6_ANALOG_CCM_MISC2_CLR IMX6_ANALOG_PMU_MISC2_CLR +#define IMX6_ANALOG_CCM_MISC2_TOG IMX6_ANALOG_PMU_MISC2_TOG + +#define IMX6_ANALOG_TEMPMON_TEMPSENSE0 0x180 +#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_SET 0x184 +#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_CLR 0x188 +#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_TOG 0x18C +#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_TOG 0x18C +#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_SHIFT 20 +#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_MASK \ + (0xfff << IMX6_ANALOG_TEMPMON_TEMPSENSE0_ALARM_SHIFT) +#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_SHIFT 8 +#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_MASK \ + (0xfff << IMX6_ANALOG_TEMPMON_TEMPSENSE0_TEMP_CNT_SHIFT) +#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_VALID 0x4 +#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_MEASURE 0x2 +#define IMX6_ANALOG_TEMPMON_TEMPSENSE0_POWER_DOWN 0x1 + +#define IMX6_ANALOG_TEMPMON_TEMPSENSE1 0x190 +#define IMX6_ANALOG_TEMPMON_TEMPSENSE1_SET 0x194 +#define IMX6_ANALOG_TEMPMON_TEMPSENSE1_CLR 0x198 +#define IMX6_ANALOG_TEMPMON_TEMPSENSE1_TOG 0x19C #define IMX6_ANALOG_USB1_VBUS_DETECT 0x1A0 #define IMX6_ANALOG_USB1_VBUS_DETECT_SET 0x1A4 #define IMX6_ANALOG_USB1_VBUS_DETECT_CLR 0x1A8 #define IMX6_ANALOG_USB1_VBUS_DETECT_TOG 0x1AC #define IMX6_ANALOG_USB1_CHRG_DETECT 0x1B0 #define IMX6_ANALOG_USB1_CHRG_DETECT_SET 0x1B4 #define IMX6_ANALOG_USB1_CHRG_DETECT_CLR 0x1B8 #define IMX6_ANALOG_USB1_CHRG_DETECT_TOG 0x1BC #define IMX6_ANALOG_USB_CHRG_DETECT_N_ENABLE (1 << 20) /* EN_B */ #define IMX6_ANALOG_USB_CHRG_DETECT_N_CHK_CHRG (1 << 19) /* CHK_CHRG_B */ #define IMX6_ANALOG_USB_CHRG_DETECT_CHK_CONTACT (1 << 18) #define IMX6_ANALOG_USB1_VBUS_DETECT_STAT 0x1C0 #define IMX6_ANALOG_USB1_CHRG_DETECT_STAT 0x1D0 #define IMX6_ANALOG_USB1_MISC 0x1F0 #define IMX6_ANALOG_USB1_MISC_SET 0x1F4 #define IMX6_ANALOG_USB1_MISC_CLR 0x1F8 #define IMX6_ANALOG_USB1_MISC_TOG 0x1FC #define IMX6_ANALOG_USB2_VBUS_DETECT 0x200 #define IMX6_ANALOG_USB2_VBUS_DETECT_SET 0x204 #define IMX6_ANALOG_USB2_VBUS_DETECT_CLR 0x208 #define IMX6_ANALOG_USB2_VBUS_DETECT_TOG 0x20C #define IMX6_ANALOG_USB2_CHRG_DETECT 0x210 #define IMX6_ANALOG_USB2_CHRG_DETECT_SET 0x214 #define IMX6_ANALOG_USB2_CHRG_DETECT_CLR 0x218 #define IMX6_ANALOG_USB2_CHRG_DETECT_TOG 0x21C #define IMX6_ANALOG_USB2_VBUS_DETECT_STAT 0x220 #define IMX6_ANALOG_USB2_CHRG_DETECT_STAT 0x230 #define IMX6_ANALOG_USB2_MISC 0x250 #define IMX6_ANALOG_USB2_MISC_SET 0x254 #define IMX6_ANALOG_USB2_MISC_CLR 0x258 #define IMX6_ANALOG_USB2_MISC_TOG 0x25C #define IMX6_ANALOG_DIGPROG 0x260 #define IMX6_ANALOG_DIGPROG_SL 0x280 #define IMX6_ANALOG_DIGPROG_SOCTYPE_SHIFT 16 #define IMX6_ANALOG_DIGPROG_SOCTYPE_MASK \ (0xff << IMX6_ANALOG_DIGPROG_SOCTYPE_SHIFT) #endif diff --git a/sys/arm/freescale/imx/imx6_anatopvar.h b/sys/arm/freescale/imx/imx6_anatopvar.h index 1974089dee81..5ede71187e6d 100644 --- a/sys/arm/freescale/imx/imx6_anatopvar.h +++ b/sys/arm/freescale/imx/imx6_anatopvar.h @@ -1,43 +1,45 @@ /*- * Copyright (c) 2013 Ian Lepore * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef IMX6_ANATOPVAR_H #define IMX6_ANATOPVAR_H /* * All registers controlling various analog aspects of the SoC (such as PLLs or * voltage regulators or USB VBUS detection) are gathered together under the * anatop device (because of newbus hierarchical resource management), but other * drivers such as CMM or USBPHY need access to these registers. These * functions let them have at the hardware directly. No effort is made by these * functions to mediate concurrent access. */ uint32_t imx6_anatop_read_4(bus_size_t _offset); void imx6_anatop_write_4(bus_size_t _offset, uint32_t _value); +uint32_t imx6_get_cpu_clock(void); + #endif diff --git a/sys/boot/fdt/dts/imx6.dtsi b/sys/boot/fdt/dts/imx6.dtsi index 377a4afd2e9d..da81d79f9abb 100644 --- a/sys/boot/fdt/dts/imx6.dtsi +++ b/sys/boot/fdt/dts/imx6.dtsi @@ -1,347 +1,349 @@ /* * Copyright (c) 2013 Ian Lepore * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Freescale i.MX6 Common Device Tree Source. * There are enough differences between the Solo, Dual, Quad, and *-lite * flavors of this SoC that eventually we will need a finer-grained breakdown * of some of this stuff. For now this file works for all of them. I think. * * $FreeBSD$ */ / { cpus { #address-cells = <1>; #size-cells = <0>; cpu@0 { device_type = "cpu"; compatible = "ARM,MCIMX6"; reg = <0x0>; d-cache-line-size = <32>; i-cache-line-size = <32>; d-cache-size = <0x8000>; i-cache-size = <0x8000>; /* TODO: describe L2 cache also */ timebase-frequency = <0>; bus-frequency = <0>; clock-frequency = <0>; }; }; aliases { soc = &SOC; }; SOC: soc@00000000 { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; interrupt-parent = <&gic>; ranges = <0x00000000 0x00000000 0x10000000>; gic: generic-interrupt-controller@00a00100 { compatible = "arm,gic"; interrupt-controller; #interrupt-cells = <1>; reg = <0x00a01000 0x00001000 0x00a00100 0x00000100>; }; l2-cache@00a02000 { compatible = "arm,pl310-cache", "arm,pl310"; reg = <0xa02000 0x1000>; interrupts = <124>; cache-level = <0x2>; interrupt-parent = < &gic >; }; aips@02000000 { /* AIPS1 */ compatible = "fsl,aips-bus", "simple-bus"; #address-cells = <1>; #size-cells = <1>; interrupt-parent = <&gic>; reg = <0x02000000 0x00100000>; ranges; /* Required by many devices, so better to stay first */ clks: ccm@020c4000 { compatible = "fsl,imx6q-ccm"; reg = <0x020c4000 0x4000>; interrupts = <119 120>; }; anatop: anatop@020c8000 { compatible = "fsl,imx6q-anatop"; reg = <0x020c8000 0x1000>; + interrupt-parent = <&gic>; + interrupts = <49>; } gpt: timer@02098000 { compatible = "fsl,imx6q-gpt", "fsl,imx51-gpt"; reg = <0x02098000 0x4000>; interrupt-parent = <&gic>; interrupts = <87>; }; // iomux@73fa8000 { // compatible = "fsl,imx51-iomux"; // reg = <0x73fa8000 0x4000>; // interrupt-parent = <&gic>; interrupts = <7>; // status = "disabled"; // }; gpio1: gpio@0209c000 { compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; reg = <0x0209c000 0x4000>; interrupts = <0 66 0x04 0 67 0x04>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; }; gpio2: gpio@020a0000 { compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; reg = <0x020a0000 0x4000>; interrupts = <0 68 0x04 0 69 0x04>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; }; gpio3: gpio@020a4000 { compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; reg = <0x020a4000 0x4000>; interrupts = <0 70 0x04 0 71 0x04>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; }; gpio4: gpio@020a8000 { compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; reg = <0x020a8000 0x4000>; interrupts = <0 72 0x04 0 73 0x04>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; }; gpio5: gpio@020ac000 { compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; reg = <0x020ac000 0x4000>; interrupts = <0 74 0x04 0 75 0x04>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; }; gpio6: gpio@020b0000 { compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; reg = <0x020b0000 0x4000>; interrupts = <0 76 0x04 0 77 0x04>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; }; gpio7: gpio@020b4000 { compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio"; reg = <0x020b4000 0x4000>; interrupts = <0 78 0x04 0 79 0x04>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; }; uart1: serial@02020000 { compatible = "fsl,imx6q-uart"; reg = <0x02020000 0x4000>; interrupt-parent = <&gic>; interrupts = <58>; clock-frequency = <80000000>; status = "disabled"; }; uart2: serial@021e8000 { compatible = "fsl,imx6q-uart"; reg = <0x021e8000 0x4000>; interrupt-parent = <&gic>; interrupts = <59>; clock-frequency = <80000000>; status = "disabled"; }; uart3: serial@021ec000 { compatible = "fsl,imx6q-uart"; reg = <0x021ec000 0x4000>; interrupt-parent = <&gic>; interrupts = <60>; clock-frequency = <80000000>; status = "disabled"; }; uart4: serial@021f0000 { compatible = "fsl,imx6q-uart"; reg = <0x021f0000 0x4000>; interrupt-parent = <&gic>; interrupts = <61>; clock-frequency = <80000000>; status = "disabled"; }; uart5: serial@021f4000 { compatible = "fsl,imx6q-uart"; reg = <0x021f4000 0x4000>; interrupt-parent = <&gic>; interrupts = <62>; clock-frequency = <80000000>; status = "disabled"; }; usbphy1: usbphy@020c9000 { compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy"; reg = <0x020c9000 0x1000>; interrupts = <44>; status = "disabled"; }; usbphy2: usbphy@020ca000 { compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy"; reg = <0x020ca000 0x1000>; interrupts = <45>; status = "disabled"; }; }; aips@02100000 { /* AIPS2 */ compatible = "fsl,aips-bus", "simple-bus"; #address-cells = <1>; #size-cells = <1>; interrupt-parent = <&gic>; reg = <0x02100000 0x00100000>; ranges; fec1: ethernet@02188000 { compatible = "fsl,imx6q-fec"; reg = <0x02188000 0x4000>; interrupts = <150 151>; status = "disabled"; }; usbotg1: usb@02184000 { compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; reg = <0x02184000 0x200>; interrupts = <75>; fsl,usbphy = <&usbphy1>; fsl,usbmisc = <&usbmisc 0>; status = "disabled"; }; usbh1: usb@02184200 { compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; reg = <0x02184200 0x200>; interrupts = <72>; fsl,usbphy = <&usbphy2>; fsl,usbmisc = <&usbmisc 1>; status = "disabled"; }; usbh2: usb@02184400 { compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; reg = <0x02184400 0x200>; interrupts = <73>; fsl,usbmisc = <&usbmisc 2>; status = "disabled"; }; usbh3: usb@02184600 { compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; reg = <0x02184600 0x200>; interrupts = <74>; fsl,usbmisc = <&usbmisc 3>; status = "disabled"; }; usbmisc: usbmisc@02184800 { #index-cells = <1>; compatible = "fsl,imx6q-usbmisc"; reg = <0x02184800 0x200>; // Not disabled on purpose. }; usdhc1: usdhc@02190000 { compatible = "fsl,imx6q-usdhc"; reg = <0x02190000 0x4000>; interrupt-parent = <&gic>; interrupts = <54>; cd-gpios = <&gpio1 2 0>; bus-width = <0x4>; status ="disabled"; }; usdhc2: usdhc@02194000 { compatible = "fsl,imx6q-usdhc"; reg = <0x02194000 0x4000>; interrupt-parent = <&gic>; interrupts = <55>; non-removable; bus-width = <0x4>; status ="disabled"; }; usdhc3: usdhc@02198000 { compatible = "fsl,imx6q-usdhc"; reg = <0x02198000 0x4000>; interrupt-parent = <&gic>; interrupts = <56>; cd-gpios = <&gpio3 9 0>; bus-width = <0x4>; status ="disabled"; }; usdhc4: usdhc@0219c000 { compatible = "fsl,imx6q-usdhc"; reg = <0x0219c000 0x4000>; interrupt-parent = <&gic>; interrupts = <57>; bus-width = <0x4>; status ="disabled"; }; ocotp0: ocotp@021bc000 { compatible = "fsl,imx6q-ocotp"; reg = <0x021bc000 0x4000>; } }; }; };