Index: head/sys/arm/allwinner/aw_wdog.c =================================================================== --- head/sys/arm/allwinner/aw_wdog.c (revision 310020) +++ head/sys/arm/allwinner/aw_wdog.c (revision 310021) @@ -1,275 +1,275 @@ /*- * Copyright (c) 2013 Oleksandr Tymoshenko * Copyright (c) 2016 Emmanuel Vadot * 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define READ(_sc, _r) bus_read_4((_sc)->res, (_r)) #define WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v)) #define A10_WDOG_CTRL 0x00 #define A31_WDOG_CTRL 0x10 #define WDOG_CTRL_RESTART (1 << 0) #define A31_WDOG_CTRL_KEY (0xa57 << 1) #define A10_WDOG_MODE 0x04 #define A31_WDOG_MODE 0x18 #define A10_WDOG_MODE_INTVL_SHIFT 3 #define A31_WDOG_MODE_INTVL_SHIFT 4 #define A10_WDOG_MODE_RST_EN (1 << 1) #define WDOG_MODE_EN (1 << 0) #define A31_WDOG_CONFIG 0x14 #define A31_WDOG_CONFIG_RST_EN_SYSTEM (1 << 0) #define A31_WDOG_CONFIG_RST_EN_INT (2 << 0) struct aw_wdog_interval { uint64_t milliseconds; unsigned int value; }; struct aw_wdog_interval wd_intervals[] = { { 500, 0 }, { 1000, 1 }, { 2000, 2 }, { 3000, 3 }, { 4000, 4 }, { 5000, 5 }, { 6000, 6 }, { 8000, 7 }, { 10000, 8 }, { 12000, 9 }, { 14000, 10 }, { 16000, 11 }, { 0, 0 } /* sentinel */ }; static struct aw_wdog_softc *aw_wdog_sc = NULL; struct aw_wdog_softc { device_t dev; struct resource * res; struct mtx mtx; uint8_t wdog_ctrl; uint32_t wdog_ctrl_key; uint8_t wdog_mode; uint8_t wdog_mode_intvl_shift; uint8_t wdog_mode_en; uint8_t wdog_config; uint8_t wdog_config_value; }; #define A10_WATCHDOG 1 #define A31_WATCHDOG 2 static struct ofw_compat_data compat_data[] = { {"allwinner,sun4i-a10-wdt", A10_WATCHDOG}, {"allwinner,sun6i-a31-wdt", A31_WATCHDOG}, {NULL, 0} }; static void aw_wdog_watchdog_fn(void *, u_int, int *); static void aw_wdog_shutdown_fn(void *, int); static int aw_wdog_probe(device_t dev) { struct aw_wdog_softc *sc; sc = device_get_softc(dev); if (!ofw_bus_status_okay(dev)) return (ENXIO); switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { case A10_WATCHDOG: device_set_desc(dev, "Allwinner A10 Watchdog"); return (BUS_PROBE_DEFAULT); case A31_WATCHDOG: device_set_desc(dev, "Allwinner A31 Watchdog"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int aw_wdog_attach(device_t dev) { struct aw_wdog_softc *sc; int rid; if (aw_wdog_sc != NULL) return (ENXIO); sc = device_get_softc(dev); sc->dev = dev; rid = 0; sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } aw_wdog_sc = sc; switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { case A10_WATCHDOG: sc->wdog_ctrl = A10_WDOG_CTRL; sc->wdog_mode = A10_WDOG_MODE; sc->wdog_mode_intvl_shift = A10_WDOG_MODE_INTVL_SHIFT; sc->wdog_mode_en = A10_WDOG_MODE_RST_EN | WDOG_MODE_EN; break; case A31_WATCHDOG: sc->wdog_ctrl = A31_WDOG_CTRL; sc->wdog_ctrl_key = A31_WDOG_CTRL_KEY; sc->wdog_mode = A31_WDOG_MODE; sc->wdog_mode_intvl_shift = A31_WDOG_MODE_INTVL_SHIFT; sc->wdog_mode_en = WDOG_MODE_EN; sc->wdog_config = A31_WDOG_CONFIG; sc->wdog_config_value = A31_WDOG_CONFIG_RST_EN_SYSTEM; break; default: bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res); return (ENXIO); } mtx_init(&sc->mtx, "AW Watchdog", "aw_wdog", MTX_DEF); EVENTHANDLER_REGISTER(watchdog_list, aw_wdog_watchdog_fn, sc, 0); EVENTHANDLER_REGISTER(shutdown_final, aw_wdog_shutdown_fn, sc, SHUTDOWN_PRI_LAST - 1); return (0); } static void aw_wdog_watchdog_fn(void *private, u_int cmd, int *error) { struct aw_wdog_softc *sc; uint64_t ms; int i; sc = private; mtx_lock(&sc->mtx); cmd &= WD_INTERVAL; if (cmd > 0) { ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000; i = 0; while (wd_intervals[i].milliseconds && (ms > wd_intervals[i].milliseconds)) i++; if (wd_intervals[i].milliseconds) { WRITE(sc, sc->wdog_mode, (wd_intervals[i].value << sc->wdog_mode_intvl_shift) | sc->wdog_mode_en); WRITE(sc, sc->wdog_ctrl, WDOG_CTRL_RESTART | sc->wdog_ctrl_key); if (sc->wdog_config) WRITE(sc, sc->wdog_config, sc->wdog_config_value); *error = 0; } else { /* * Can't arm * disable watchdog as watchdog(9) requires */ device_printf(sc->dev, "Can't arm, timeout is more than 16 sec\n"); mtx_unlock(&sc->mtx); WRITE(sc, sc->wdog_mode, 0); return; } } else WRITE(sc, sc->wdog_mode, 0); mtx_unlock(&sc->mtx); } static void aw_wdog_shutdown_fn(void *private, int howto) { if ((howto & (RB_POWEROFF|RB_HALT)) == 0) aw_wdog_watchdog_reset(); } void -aw_wdog_watchdog_reset() +aw_wdog_watchdog_reset(void) { if (aw_wdog_sc == NULL) { printf("Reset: watchdog device has not been initialized\n"); return; } WRITE(aw_wdog_sc, aw_wdog_sc->wdog_mode, (wd_intervals[0].value << aw_wdog_sc->wdog_mode_intvl_shift) | aw_wdog_sc->wdog_mode_en); if (aw_wdog_sc->wdog_config) WRITE(aw_wdog_sc, aw_wdog_sc->wdog_config, aw_wdog_sc->wdog_config_value); WRITE(aw_wdog_sc, aw_wdog_sc->wdog_ctrl, WDOG_CTRL_RESTART | aw_wdog_sc->wdog_ctrl_key); while(1) ; } static device_method_t aw_wdog_methods[] = { DEVMETHOD(device_probe, aw_wdog_probe), DEVMETHOD(device_attach, aw_wdog_attach), DEVMETHOD_END }; static driver_t aw_wdog_driver = { "aw_wdog", aw_wdog_methods, sizeof(struct aw_wdog_softc), }; static devclass_t aw_wdog_devclass; DRIVER_MODULE(aw_wdog, simplebus, aw_wdog_driver, aw_wdog_devclass, 0, 0); Index: head/sys/arm/amlogic/aml8726/aml8726_identsoc.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_identsoc.c (revision 310020) +++ head/sys/arm/amlogic/aml8726/aml8726_identsoc.c (revision 310021) @@ -1,142 +1,142 @@ /*- * Copyright 2015 John Wehle * 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. * */ /* * Amlogic aml8726 SoC identification. * * The SoC identification is used by some of the drivers in order to * handle hardware differences so the identification needs to happen * early in the boot process (e.g. before SMP startup). * * It's expected that the register addresses for identifying the SoC * are set in stone. * * Currently missing an entry for the aml8726-m and doesn't distinguish * between the m801, m802, m805, s802, s805, and s812 which are all * variations of the aml8726-m8. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include uint32_t aml8726_soc_hw_rev = AML_SOC_HW_REV_UNKNOWN; uint32_t aml8726_soc_metal_rev = AML_SOC_METAL_REV_UNKNOWN; static const struct { uint32_t hw_rev; char *desc; } aml8726_soc_desc[] = { { AML_SOC_HW_REV_M3, "aml8726-m3" }, { AML_SOC_HW_REV_M6, "aml8726-m6" }, { AML_SOC_HW_REV_M6TV, "aml8726-m6tv" }, { AML_SOC_HW_REV_M6TVL, "aml8726-m6tvl" }, { AML_SOC_HW_REV_M8, "aml8726-m8" }, { AML_SOC_HW_REV_M8B, "aml8726-m8b" }, { 0xff, NULL } }; static const struct { uint32_t metal_rev; char *desc; } aml8726_m8_soc_rev[] = { { AML_SOC_M8_METAL_REV_A, "A" }, { AML_SOC_M8_METAL_REV_M2_A, "MarkII A" }, { AML_SOC_M8_METAL_REV_B, "B" }, { AML_SOC_M8_METAL_REV_C, "C" }, { 0xff, NULL } }; void -aml8726_identify_soc() +aml8726_identify_soc(void) { int err; struct resource res; memset(&res, 0, sizeof(res)); res.r_bustag = fdtbus_bs_tag; err = bus_space_map(res.r_bustag, AML_SOC_CBUS_BASE_ADDR, 0x100000, 0, &res.r_bushandle); if (err) panic("Could not allocate resource for SoC identification\n"); aml8726_soc_hw_rev = bus_read_4(&res, AML_SOC_HW_REV_REG); aml8726_soc_metal_rev = bus_read_4(&res, AML_SOC_METAL_REV_REG); bus_space_unmap(res.r_bustag, res.r_bushandle, 0x100000); } static void aml8726_identify_announce_soc(void *dummy) { int i; for (i = 0; aml8726_soc_desc[i].desc; i++) if (aml8726_soc_desc[i].hw_rev == aml8726_soc_hw_rev) break; if (aml8726_soc_desc[i].desc == NULL) panic("Amlogic unknown aml8726 SoC %#x\n", aml8726_soc_hw_rev); printf("Amlogic %s SoC", aml8726_soc_desc[i].desc); if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8) { for (i = 0; aml8726_m8_soc_rev[i].desc; i++) if (aml8726_m8_soc_rev[i].metal_rev == aml8726_soc_metal_rev) break; if (aml8726_m8_soc_rev[i].desc == NULL) printf(", unknown rev %#x", aml8726_soc_metal_rev); else printf(", rev %s", aml8726_m8_soc_rev[i].desc); } printf("\n"); } SYSINIT(aml8726_identify_announce_soc, SI_SUB_CPU, SI_ORDER_SECOND, aml8726_identify_announce_soc, NULL); Index: head/sys/arm/amlogic/aml8726/aml8726_machdep.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_machdep.c (revision 310020) +++ head/sys/arm/amlogic/aml8726/aml8726_machdep.c (revision 310021) @@ -1,196 +1,196 @@ /*- * Copyright 2013-2015 John Wehle * 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 "opt_global.h" #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(SOCDEV_PA) && defined(SOCDEV_VA) vm_offset_t aml8726_aobus_kva_base = SOCDEV_VA; #else vm_offset_t aml8726_aobus_kva_base; #endif static void -aml8726_fixup_busfreq() +aml8726_fixup_busfreq(void) { phandle_t node; pcell_t freq, prop; ssize_t len; /* * Set the bus-frequency for the SoC simple-bus if it * needs updating (meaning the current frequency is zero). */ if ((freq = aml8726_clkmsr_bus_frequency()) == 0 || (node = OF_finddevice("/soc")) == 0 || fdt_is_compatible_strict(node, "simple-bus") == 0) while (1); freq = cpu_to_fdt32(freq); len = OF_getencprop(node, "bus-frequency", &prop, sizeof(prop)); if ((len / sizeof(prop)) == 1 && prop == 0) OF_setprop(node, "bus-frequency", (void *)&freq, sizeof(freq)); } vm_offset_t platform_lastaddr(void) { return (devmap_lastaddr()); } void platform_probe_and_attach(void) { } void platform_gpio_init(void) { /* * The UART console driver used for debugging early boot code * needs to know the virtual base address of the aobus. It's * expected to equal SOCDEV_VA prior to initarm calling setttb * ... afterwards it needs to be updated due to the new page * tables. * * This means there's a deadzone in initarm between setttb * and platform_gpio_init during which printf can't be used. */ aml8726_aobus_kva_base = (vm_offset_t)devmap_ptov(0xc8100000, 0x100000); /* * The hardware mux used by clkmsr is unique to the SoC (though * currently clk81 is at a fixed location, however that might * change in the future). */ aml8726_identify_soc(); /* * My aml8726-m3 development box which identifies the CPU as * a Cortex A9-r2 rev 4 randomly locks up during boot when WFI * is used. */ switch (aml8726_soc_hw_rev) { case AML_SOC_HW_REV_M3: cpufuncs.cf_sleep = (void *)cpufunc_nullop; break; default: break; } /* * This FDT fixup should arguably be called through fdt_fixup_table, * however currently there's no mechanism to specify a fixup which * should always be invoked. * * It needs to be called prior to the console being initialized which * is why it's called here, rather than from platform_late_init. */ aml8726_fixup_busfreq(); } void platform_late_init(void) { } /* * Construct static devmap entries to map out the core * peripherals using 1mb section mappings. */ int platform_devmap_init(void) { devmap_add_entry(0xc1100000, 0x200000); /* cbus */ devmap_add_entry(0xc4200000, 0x100000); /* pl310 */ devmap_add_entry(0xc4300000, 0x100000); /* periph */ devmap_add_entry(0xc8000000, 0x100000); /* apbbus */ devmap_add_entry(0xc8100000, 0x100000); /* aobus */ devmap_add_entry(0xc9000000, 0x800000); /* ahbbus */ devmap_add_entry(0xd9000000, 0x100000); /* ahb */ devmap_add_entry(0xda000000, 0x100000); /* secbus */ return (0); } #ifndef INTRNG #ifndef DEV_GIC static int fdt_pic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, int *pol) { /* * The single core chips have just an Amlogic PIC. */ if (!fdt_is_compatible_strict(node, "amlogic,aml8726-pic")) return (ENXIO); *interrupt = fdt32_to_cpu(intr[1]); *trig = INTR_TRIGGER_EDGE; *pol = INTR_POLARITY_HIGH; return (0); } #endif fdt_pic_decode_t fdt_pic_table[] = { #ifdef DEV_GIC &gic_decode_fdt, #else &fdt_pic_decode_ic, #endif NULL }; #endif /* INTRNG */ Index: head/sys/arm/amlogic/aml8726/aml8726_wdt.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_wdt.c (revision 310020) +++ head/sys/arm/amlogic/aml8726/aml8726_wdt.c (revision 310021) @@ -1,306 +1,306 @@ /*- * Copyright 2013-2015 John Wehle * 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. * */ /* * Amlogic aml8726 watchdog driver. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include struct aml8726_wdt_softc { device_t dev; struct resource * res[2]; struct mtx mtx; void * ih_cookie; }; static struct resource_spec aml8726_wdt_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static struct { uint32_t ctrl_cpu_mask; uint32_t ctrl_en; uint32_t term_cnt_mask; uint32_t reset_cnt_mask; } aml8726_wdt_soc_params; /* * devclass_get_device / device_get_softc could be used * to dynamically locate this, however the wdt is a * required device which can't be unloaded so there's * no need for the overhead. */ static struct aml8726_wdt_softc *aml8726_wdt_sc = NULL; #define AML_WDT_LOCK(sc) mtx_lock_spin(&(sc)->mtx) #define AML_WDT_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx) #define AML_WDT_LOCK_INIT(sc) \ mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ "wdt", MTX_SPIN) #define AML_WDT_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); #define AML_WDT_CTRL_REG 0 #define AML_WDT_CTRL_CPU_WDRESET_MASK aml8726_wdt_soc_params.ctrl_cpu_mask #define AML_WDT_CTRL_CPU_WDRESET_SHIFT 24 #define AML_WDT_CTRL_IRQ_EN (1 << 23) #define AML_WDT_CTRL_EN aml8726_wdt_soc_params.ctrl_en #define AML_WDT_CTRL_TERMINAL_CNT_MASK aml8726_wdt_soc_params.term_cnt_mask #define AML_WDT_CTRL_TERMINAL_CNT_SHIFT 0 #define AML_WDT_RESET_REG 4 #define AML_WDT_RESET_CNT_MASK aml8726_wdt_soc_params.reset_cnt_mask #define AML_WDT_RESET_CNT_SHIFT 0 #define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) #define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) #define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) static void aml8726_wdt_watchdog(void *private, u_int cmd, int *error) { struct aml8726_wdt_softc *sc = (struct aml8726_wdt_softc *)private; uint32_t wcr; uint64_t tens_of_usec; AML_WDT_LOCK(sc); tens_of_usec = (((uint64_t)1 << (cmd & WD_INTERVAL)) + 9999) / 10000; if (cmd != 0 && tens_of_usec <= (AML_WDT_CTRL_TERMINAL_CNT_MASK >> AML_WDT_CTRL_TERMINAL_CNT_SHIFT)) { wcr = AML_WDT_CTRL_CPU_WDRESET_MASK | AML_WDT_CTRL_EN | ((uint32_t)tens_of_usec << AML_WDT_CTRL_TERMINAL_CNT_SHIFT); CSR_WRITE_4(sc, AML_WDT_RESET_REG, 0); CSR_WRITE_4(sc, AML_WDT_CTRL_REG, wcr); *error = 0; } else CSR_WRITE_4(sc, AML_WDT_CTRL_REG, (CSR_READ_4(sc, AML_WDT_CTRL_REG) & ~(AML_WDT_CTRL_IRQ_EN | AML_WDT_CTRL_EN))); AML_WDT_UNLOCK(sc); } static int aml8726_wdt_intr(void *arg) { struct aml8726_wdt_softc *sc = (struct aml8726_wdt_softc *)arg; /* * Normally a timeout causes a hardware reset, however * the watchdog timer can be configured to cause an * interrupt instead by setting AML_WDT_CTRL_IRQ_EN * and clearing AML_WDT_CTRL_CPU_WDRESET_MASK. */ AML_WDT_LOCK(sc); CSR_WRITE_4(sc, AML_WDT_CTRL_REG, (CSR_READ_4(sc, AML_WDT_CTRL_REG) & ~(AML_WDT_CTRL_IRQ_EN | AML_WDT_CTRL_EN))); CSR_BARRIER(sc, AML_WDT_CTRL_REG); AML_WDT_UNLOCK(sc); device_printf(sc->dev, "timeout expired\n"); return (FILTER_HANDLED); } static int aml8726_wdt_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "amlogic,meson6-wdt")) return (ENXIO); device_set_desc(dev, "Amlogic aml8726 WDT"); return (BUS_PROBE_DEFAULT); } static int aml8726_wdt_attach(device_t dev) { struct aml8726_wdt_softc *sc = device_get_softc(dev); /* There should be exactly one instance. */ if (aml8726_wdt_sc != NULL) return (ENXIO); sc->dev = dev; if (bus_alloc_resources(dev, aml8726_wdt_spec, sc->res)) { device_printf(dev, "can not allocate resources for device\n"); return (ENXIO); } /* * Certain bitfields are dependent on the hardware revision. */ switch (aml8726_soc_hw_rev) { case AML_SOC_HW_REV_M8: aml8726_wdt_soc_params.ctrl_cpu_mask = 0xf << AML_WDT_CTRL_CPU_WDRESET_SHIFT; switch (aml8726_soc_metal_rev) { case AML_SOC_M8_METAL_REV_M2_A: aml8726_wdt_soc_params.ctrl_en = 1 << 19; aml8726_wdt_soc_params.term_cnt_mask = 0x07ffff << AML_WDT_CTRL_TERMINAL_CNT_SHIFT; aml8726_wdt_soc_params.reset_cnt_mask = 0x07ffff << AML_WDT_RESET_CNT_SHIFT; break; default: aml8726_wdt_soc_params.ctrl_en = 1 << 22; aml8726_wdt_soc_params.term_cnt_mask = 0x3fffff << AML_WDT_CTRL_TERMINAL_CNT_SHIFT; aml8726_wdt_soc_params.reset_cnt_mask = 0x3fffff << AML_WDT_RESET_CNT_SHIFT; break; } break; case AML_SOC_HW_REV_M8B: aml8726_wdt_soc_params.ctrl_cpu_mask = 0xf << AML_WDT_CTRL_CPU_WDRESET_SHIFT; aml8726_wdt_soc_params.ctrl_en = 1 << 19; aml8726_wdt_soc_params.term_cnt_mask = 0x07ffff << AML_WDT_CTRL_TERMINAL_CNT_SHIFT; aml8726_wdt_soc_params.reset_cnt_mask = 0x07ffff << AML_WDT_RESET_CNT_SHIFT; break; default: aml8726_wdt_soc_params.ctrl_cpu_mask = 3 << AML_WDT_CTRL_CPU_WDRESET_SHIFT; aml8726_wdt_soc_params.ctrl_en = 1 << 22; aml8726_wdt_soc_params.term_cnt_mask = 0x3fffff << AML_WDT_CTRL_TERMINAL_CNT_SHIFT; aml8726_wdt_soc_params.reset_cnt_mask = 0x3fffff << AML_WDT_RESET_CNT_SHIFT; break; } /* * Disable the watchdog. */ CSR_WRITE_4(sc, AML_WDT_CTRL_REG, (CSR_READ_4(sc, AML_WDT_CTRL_REG) & ~(AML_WDT_CTRL_IRQ_EN | AML_WDT_CTRL_EN))); /* * Initialize the mutex prior to installing the interrupt handler * in case of a spurious interrupt. */ AML_WDT_LOCK_INIT(sc); if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, aml8726_wdt_intr, NULL, sc, &sc->ih_cookie)) { device_printf(dev, "could not setup interrupt handler\n"); bus_release_resources(dev, aml8726_wdt_spec, sc->res); AML_WDT_LOCK_DESTROY(sc); return (ENXIO); } aml8726_wdt_sc = sc; EVENTHANDLER_REGISTER(watchdog_list, aml8726_wdt_watchdog, sc, 0); return (0); } static int aml8726_wdt_detach(device_t dev) { return (EBUSY); } static device_method_t aml8726_wdt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aml8726_wdt_probe), DEVMETHOD(device_attach, aml8726_wdt_attach), DEVMETHOD(device_detach, aml8726_wdt_detach), DEVMETHOD_END }; static driver_t aml8726_wdt_driver = { "wdt", aml8726_wdt_methods, sizeof(struct aml8726_wdt_softc), }; static devclass_t aml8726_wdt_devclass; EARLY_DRIVER_MODULE(wdt, simplebus, aml8726_wdt_driver, aml8726_wdt_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); void -cpu_reset() +cpu_reset(void) { /* Watchdog has not yet been initialized */ if (aml8726_wdt_sc == NULL) printf("Reset hardware has not yet been initialized.\n"); else { CSR_WRITE_4(aml8726_wdt_sc, AML_WDT_RESET_REG, 0); CSR_WRITE_4(aml8726_wdt_sc, AML_WDT_CTRL_REG, (AML_WDT_CTRL_CPU_WDRESET_MASK | AML_WDT_CTRL_EN | (10 << AML_WDT_CTRL_TERMINAL_CNT_SHIFT))); } while (1); } Index: head/sys/arm/arm/cpufunc.c =================================================================== --- head/sys/arm/arm/cpufunc.c (revision 310020) +++ head/sys/arm/arm/cpufunc.c (revision 310021) @@ -1,1019 +1,1019 @@ /* $NetBSD: cpufunc.c,v 1.65 2003/11/05 12:53:15 scw Exp $ */ /*- * arm9 support code Copyright (C) 2001 ARM Ltd * Copyright (c) 1997 Mark Brinicombe. * Copyright (c) 1997 Causality Limited * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Causality Limited. * 4. The name of Causality Limited may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY CAUSALITY LIMITED ``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 CAUSALITY LIMITED 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. * * RiscBSD kernel project * * cpufuncs.c * * C functions for supporting CPU / MMU / TLB specific operations. * * Created : 30/01/97 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CPU_XSCALE_81342) #include #endif #ifdef CPU_XSCALE_IXP425 #include #include #endif /* PRIMARY CACHE VARIABLES */ int arm_picache_size; int arm_picache_line_size; int arm_picache_ways; int arm_pdcache_size; /* and unified */ int arm_pdcache_line_size; int arm_pdcache_ways; int arm_pcache_type; int arm_pcache_unified; int arm_dcache_align; int arm_dcache_align_mask; u_int arm_cache_level; u_int arm_cache_type[14]; u_int arm_cache_loc; #ifdef CPU_ARM9 struct cpu_functions arm9_cpufuncs = { /* CPU functions */ cpufunc_nullop, /* cpwait */ /* MMU functions */ cpufunc_control, /* control */ arm9_setttb, /* Setttb */ /* TLB functions */ armv4_tlb_flushID, /* tlb_flushID */ arm9_tlb_flushID_SE, /* tlb_flushID_SE */ armv4_tlb_flushD, /* tlb_flushD */ armv4_tlb_flushD_SE, /* tlb_flushD_SE */ /* Cache operations */ arm9_icache_sync_range, /* icache_sync_range */ arm9_dcache_wbinv_all, /* dcache_wbinv_all */ arm9_dcache_wbinv_range, /* dcache_wbinv_range */ arm9_dcache_inv_range, /* dcache_inv_range */ arm9_dcache_wb_range, /* dcache_wb_range */ armv4_idcache_inv_all, /* idcache_inv_all */ arm9_idcache_wbinv_all, /* idcache_wbinv_all */ arm9_idcache_wbinv_range, /* idcache_wbinv_range */ cpufunc_nullop, /* l2cache_wbinv_all */ (void *)cpufunc_nullop, /* l2cache_wbinv_range */ (void *)cpufunc_nullop, /* l2cache_inv_range */ (void *)cpufunc_nullop, /* l2cache_wb_range */ (void *)cpufunc_nullop, /* l2cache_drain_writebuf */ /* Other functions */ armv4_drain_writebuf, /* drain_writebuf */ (void *)cpufunc_nullop, /* sleep */ /* Soft functions */ arm9_context_switch, /* context_switch */ arm9_setup /* cpu setup */ }; #endif /* CPU_ARM9 */ #if defined(CPU_ARM9E) struct cpu_functions armv5_ec_cpufuncs = { /* CPU functions */ cpufunc_nullop, /* cpwait */ /* MMU functions */ cpufunc_control, /* control */ armv5_ec_setttb, /* Setttb */ /* TLB functions */ armv4_tlb_flushID, /* tlb_flushID */ arm9_tlb_flushID_SE, /* tlb_flushID_SE */ armv4_tlb_flushD, /* tlb_flushD */ armv4_tlb_flushD_SE, /* tlb_flushD_SE */ /* Cache operations */ armv5_ec_icache_sync_range, /* icache_sync_range */ armv5_ec_dcache_wbinv_all, /* dcache_wbinv_all */ armv5_ec_dcache_wbinv_range, /* dcache_wbinv_range */ armv5_ec_dcache_inv_range, /* dcache_inv_range */ armv5_ec_dcache_wb_range, /* dcache_wb_range */ armv4_idcache_inv_all, /* idcache_inv_all */ armv5_ec_idcache_wbinv_all, /* idcache_wbinv_all */ armv5_ec_idcache_wbinv_range, /* idcache_wbinv_range */ cpufunc_nullop, /* l2cache_wbinv_all */ (void *)cpufunc_nullop, /* l2cache_wbinv_range */ (void *)cpufunc_nullop, /* l2cache_inv_range */ (void *)cpufunc_nullop, /* l2cache_wb_range */ (void *)cpufunc_nullop, /* l2cache_drain_writebuf */ /* Other functions */ armv4_drain_writebuf, /* drain_writebuf */ (void *)cpufunc_nullop, /* sleep */ /* Soft functions */ arm9_context_switch, /* context_switch */ arm10_setup /* cpu setup */ }; struct cpu_functions sheeva_cpufuncs = { /* CPU functions */ cpufunc_nullop, /* cpwait */ /* MMU functions */ cpufunc_control, /* control */ sheeva_setttb, /* Setttb */ /* TLB functions */ armv4_tlb_flushID, /* tlb_flushID */ arm9_tlb_flushID_SE, /* tlb_flushID_SE */ armv4_tlb_flushD, /* tlb_flushD */ armv4_tlb_flushD_SE, /* tlb_flushD_SE */ /* Cache operations */ armv5_ec_icache_sync_range, /* icache_sync_range */ armv5_ec_dcache_wbinv_all, /* dcache_wbinv_all */ sheeva_dcache_wbinv_range, /* dcache_wbinv_range */ sheeva_dcache_inv_range, /* dcache_inv_range */ sheeva_dcache_wb_range, /* dcache_wb_range */ armv4_idcache_inv_all, /* idcache_inv_all */ armv5_ec_idcache_wbinv_all, /* idcache_wbinv_all */ sheeva_idcache_wbinv_range, /* idcache_wbinv_all */ sheeva_l2cache_wbinv_all, /* l2cache_wbinv_all */ sheeva_l2cache_wbinv_range, /* l2cache_wbinv_range */ sheeva_l2cache_inv_range, /* l2cache_inv_range */ sheeva_l2cache_wb_range, /* l2cache_wb_range */ (void *)cpufunc_nullop, /* l2cache_drain_writebuf */ /* Other functions */ armv4_drain_writebuf, /* drain_writebuf */ sheeva_cpu_sleep, /* sleep */ /* Soft functions */ arm9_context_switch, /* context_switch */ arm10_setup /* cpu setup */ }; #endif /* CPU_ARM9E */ #ifdef CPU_MV_PJ4B struct cpu_functions pj4bv7_cpufuncs = { /* Cache operations */ .cf_l2cache_wbinv_all = (void *)cpufunc_nullop, .cf_l2cache_wbinv_range = (void *)cpufunc_nullop, .cf_l2cache_inv_range = (void *)cpufunc_nullop, .cf_l2cache_wb_range = (void *)cpufunc_nullop, .cf_l2cache_drain_writebuf = (void *)cpufunc_nullop, /* Other functions */ .cf_sleep = (void *)cpufunc_nullop, /* Soft functions */ .cf_setup = pj4bv7_setup }; #endif /* CPU_MV_PJ4B */ #if defined(CPU_XSCALE_PXA2X0) || defined(CPU_XSCALE_IXP425) struct cpu_functions xscale_cpufuncs = { /* CPU functions */ xscale_cpwait, /* cpwait */ /* MMU functions */ xscale_control, /* control */ xscale_setttb, /* setttb */ /* TLB functions */ armv4_tlb_flushID, /* tlb_flushID */ xscale_tlb_flushID_SE, /* tlb_flushID_SE */ armv4_tlb_flushD, /* tlb_flushD */ armv4_tlb_flushD_SE, /* tlb_flushD_SE */ /* Cache operations */ xscale_cache_syncI_rng, /* icache_sync_range */ xscale_cache_purgeD, /* dcache_wbinv_all */ xscale_cache_purgeD_rng, /* dcache_wbinv_range */ xscale_cache_flushD_rng, /* dcache_inv_range */ xscale_cache_cleanD_rng, /* dcache_wb_range */ xscale_cache_flushID, /* idcache_inv_all */ xscale_cache_purgeID, /* idcache_wbinv_all */ xscale_cache_purgeID_rng, /* idcache_wbinv_range */ cpufunc_nullop, /* l2cache_wbinv_all */ (void *)cpufunc_nullop, /* l2cache_wbinv_range */ (void *)cpufunc_nullop, /* l2cache_inv_range */ (void *)cpufunc_nullop, /* l2cache_wb_range */ (void *)cpufunc_nullop, /* l2cache_drain_writebuf */ /* Other functions */ armv4_drain_writebuf, /* drain_writebuf */ xscale_cpu_sleep, /* sleep */ /* Soft functions */ xscale_context_switch, /* context_switch */ xscale_setup /* cpu setup */ }; #endif /* CPU_XSCALE_PXA2X0 || CPU_XSCALE_IXP425 */ #ifdef CPU_XSCALE_81342 struct cpu_functions xscalec3_cpufuncs = { /* CPU functions */ xscale_cpwait, /* cpwait */ /* MMU functions */ xscale_control, /* control */ xscalec3_setttb, /* setttb */ /* TLB functions */ armv4_tlb_flushID, /* tlb_flushID */ xscale_tlb_flushID_SE, /* tlb_flushID_SE */ armv4_tlb_flushD, /* tlb_flushD */ armv4_tlb_flushD_SE, /* tlb_flushD_SE */ /* Cache operations */ xscalec3_cache_syncI_rng, /* icache_sync_range */ xscalec3_cache_purgeD, /* dcache_wbinv_all */ xscalec3_cache_purgeD_rng, /* dcache_wbinv_range */ xscale_cache_flushD_rng, /* dcache_inv_range */ xscalec3_cache_cleanD_rng, /* dcache_wb_range */ xscale_cache_flushID, /* idcache_inv_all */ xscalec3_cache_purgeID, /* idcache_wbinv_all */ xscalec3_cache_purgeID_rng, /* idcache_wbinv_range */ xscalec3_l2cache_purge, /* l2cache_wbinv_all */ xscalec3_l2cache_purge_rng, /* l2cache_wbinv_range */ xscalec3_l2cache_flush_rng, /* l2cache_inv_range */ xscalec3_l2cache_clean_rng, /* l2cache_wb_range */ (void *)cpufunc_nullop, /* l2cache_drain_writebuf */ /* Other functions */ armv4_drain_writebuf, /* drain_writebuf */ xscale_cpu_sleep, /* sleep */ /* Soft functions */ xscalec3_context_switch, /* context_switch */ xscale_setup /* cpu setup */ }; #endif /* CPU_XSCALE_81342 */ #if defined(CPU_FA526) struct cpu_functions fa526_cpufuncs = { /* CPU functions */ cpufunc_nullop, /* cpwait */ /* MMU functions */ cpufunc_control, /* control */ fa526_setttb, /* setttb */ /* TLB functions */ armv4_tlb_flushID, /* tlb_flushID */ fa526_tlb_flushID_SE, /* tlb_flushID_SE */ armv4_tlb_flushD, /* tlb_flushD */ armv4_tlb_flushD_SE, /* tlb_flushD_SE */ /* Cache operations */ fa526_icache_sync_range, /* icache_sync_range */ fa526_dcache_wbinv_all, /* dcache_wbinv_all */ fa526_dcache_wbinv_range, /* dcache_wbinv_range */ fa526_dcache_inv_range, /* dcache_inv_range */ fa526_dcache_wb_range, /* dcache_wb_range */ armv4_idcache_inv_all, /* idcache_inv_all */ fa526_idcache_wbinv_all, /* idcache_wbinv_all */ fa526_idcache_wbinv_range, /* idcache_wbinv_range */ cpufunc_nullop, /* l2cache_wbinv_all */ (void *)cpufunc_nullop, /* l2cache_wbinv_range */ (void *)cpufunc_nullop, /* l2cache_inv_range */ (void *)cpufunc_nullop, /* l2cache_wb_range */ (void *)cpufunc_nullop, /* l2cache_drain_writebuf */ /* Other functions */ armv4_drain_writebuf, /* drain_writebuf */ fa526_cpu_sleep, /* sleep */ /* Soft functions */ fa526_context_switch, /* context_switch */ fa526_setup /* cpu setup */ }; #endif /* CPU_FA526 */ #if defined(CPU_ARM1176) struct cpu_functions arm1176_cpufuncs = { /* Cache operations */ .cf_l2cache_wbinv_all = (void *)cpufunc_nullop, .cf_l2cache_wbinv_range = (void *)cpufunc_nullop, .cf_l2cache_inv_range = (void *)cpufunc_nullop, .cf_l2cache_wb_range = (void *)cpufunc_nullop, .cf_l2cache_drain_writebuf = (void *)cpufunc_nullop, /* Other functions */ .cf_sleep = arm11x6_sleep, /* Soft functions */ .cf_setup = arm11x6_setup }; #endif /*CPU_ARM1176 */ #if defined(CPU_CORTEXA8) || defined(CPU_CORTEXA_MP) || defined(CPU_KRAIT) struct cpu_functions cortexa_cpufuncs = { /* Cache operations */ /* * Note: For CPUs using the PL310 the L2 ops are filled in when the * L2 cache controller is actually enabled. */ .cf_l2cache_wbinv_all = cpufunc_nullop, .cf_l2cache_wbinv_range = (void *)cpufunc_nullop, .cf_l2cache_inv_range = (void *)cpufunc_nullop, .cf_l2cache_wb_range = (void *)cpufunc_nullop, .cf_l2cache_drain_writebuf = (void *)cpufunc_nullop, /* Other functions */ .cf_sleep = armv7_cpu_sleep, /* Soft functions */ .cf_setup = cortexa_setup }; #endif /* CPU_CORTEXA8 || CPU_CORTEXA_MP || CPU_KRAIT */ /* * Global constants also used by locore.s */ struct cpu_functions cpufuncs; u_int cputype; #if __ARM_ARCH <= 5 u_int cpu_reset_needs_v4_MMU_disable; /* flag used in locore-v4.s */ #endif #if defined(CPU_ARM9) || \ defined (CPU_ARM9E) || \ defined(CPU_ARM1176) || \ defined(CPU_XSCALE_PXA2X0) || defined(CPU_XSCALE_IXP425) || \ defined(CPU_FA526) || defined(CPU_MV_PJ4B) || \ defined(CPU_XSCALE_81342) || \ defined(CPU_CORTEXA8) || defined(CPU_CORTEXA_MP) || defined(CPU_KRAIT) /* Global cache line sizes, use 32 as default */ int arm_dcache_min_line_size = 32; int arm_icache_min_line_size = 32; int arm_idcache_min_line_size = 32; static void get_cachetype_cp15(void); /* Additional cache information local to this file. Log2 of some of the above numbers. */ static int arm_dcache_l2_nsets; static int arm_dcache_l2_assoc; static int arm_dcache_l2_linesize; static void -get_cachetype_cp15() +get_cachetype_cp15(void) { u_int ctype, isize, dsize, cpuid; u_int clevel, csize, i, sel; u_int multiplier; u_char type; __asm __volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctype)); cpuid = cpu_ident(); /* * ...and thus spake the ARM ARM: * * If an value corresponding to an unimplemented or * reserved ID register is encountered, the System Control * processor returns the value of the main ID register. */ if (ctype == cpuid) goto out; if (CPU_CT_FORMAT(ctype) == CPU_CT_ARMV7) { /* Resolve minimal cache line sizes */ arm_dcache_min_line_size = 1 << (CPU_CT_DMINLINE(ctype) + 2); arm_icache_min_line_size = 1 << (CPU_CT_IMINLINE(ctype) + 2); arm_idcache_min_line_size = min(arm_icache_min_line_size, arm_dcache_min_line_size); __asm __volatile("mrc p15, 1, %0, c0, c0, 1" : "=r" (clevel)); arm_cache_level = clevel; arm_cache_loc = CPU_CLIDR_LOC(arm_cache_level); i = 0; while ((type = (clevel & 0x7)) && i < 7) { if (type == CACHE_DCACHE || type == CACHE_UNI_CACHE || type == CACHE_SEP_CACHE) { sel = i << 1; __asm __volatile("mcr p15, 2, %0, c0, c0, 0" : : "r" (sel)); __asm __volatile("mrc p15, 1, %0, c0, c0, 0" : "=r" (csize)); arm_cache_type[sel] = csize; arm_dcache_align = 1 << (CPUV7_CT_xSIZE_LEN(csize) + 4); arm_dcache_align_mask = arm_dcache_align - 1; } if (type == CACHE_ICACHE || type == CACHE_SEP_CACHE) { sel = (i << 1) | 1; __asm __volatile("mcr p15, 2, %0, c0, c0, 0" : : "r" (sel)); __asm __volatile("mrc p15, 1, %0, c0, c0, 0" : "=r" (csize)); arm_cache_type[sel] = csize; } i++; clevel >>= 3; } } else { if ((ctype & CPU_CT_S) == 0) arm_pcache_unified = 1; /* * If you want to know how this code works, go read the ARM ARM. */ arm_pcache_type = CPU_CT_CTYPE(ctype); if (arm_pcache_unified == 0) { isize = CPU_CT_ISIZE(ctype); multiplier = (isize & CPU_CT_xSIZE_M) ? 3 : 2; arm_picache_line_size = 1U << (CPU_CT_xSIZE_LEN(isize) + 3); if (CPU_CT_xSIZE_ASSOC(isize) == 0) { if (isize & CPU_CT_xSIZE_M) arm_picache_line_size = 0; /* not present */ else arm_picache_ways = 1; } else { arm_picache_ways = multiplier << (CPU_CT_xSIZE_ASSOC(isize) - 1); } arm_picache_size = multiplier << (CPU_CT_xSIZE_SIZE(isize) + 8); } dsize = CPU_CT_DSIZE(ctype); multiplier = (dsize & CPU_CT_xSIZE_M) ? 3 : 2; arm_pdcache_line_size = 1U << (CPU_CT_xSIZE_LEN(dsize) + 3); if (CPU_CT_xSIZE_ASSOC(dsize) == 0) { if (dsize & CPU_CT_xSIZE_M) arm_pdcache_line_size = 0; /* not present */ else arm_pdcache_ways = 1; } else { arm_pdcache_ways = multiplier << (CPU_CT_xSIZE_ASSOC(dsize) - 1); } arm_pdcache_size = multiplier << (CPU_CT_xSIZE_SIZE(dsize) + 8); arm_dcache_align = arm_pdcache_line_size; arm_dcache_l2_assoc = CPU_CT_xSIZE_ASSOC(dsize) + multiplier - 2; arm_dcache_l2_linesize = CPU_CT_xSIZE_LEN(dsize) + 3; arm_dcache_l2_nsets = 6 + CPU_CT_xSIZE_SIZE(dsize) - CPU_CT_xSIZE_ASSOC(dsize) - CPU_CT_xSIZE_LEN(dsize); out: arm_dcache_align_mask = arm_dcache_align - 1; } } #endif /* ARM9 || XSCALE */ /* * Cannot panic here as we may not have a console yet ... */ int -set_cpufuncs() +set_cpufuncs(void) { cputype = cpu_ident(); cputype &= CPU_ID_CPU_MASK; #ifdef CPU_ARM9 if (((cputype & CPU_ID_IMPLEMENTOR_MASK) == CPU_ID_ARM_LTD || (cputype & CPU_ID_IMPLEMENTOR_MASK) == CPU_ID_TI) && (cputype & 0x0000f000) == 0x00009000) { cpufuncs = arm9_cpufuncs; cpu_reset_needs_v4_MMU_disable = 1; /* V4 or higher */ get_cachetype_cp15(); arm9_dcache_sets_inc = 1U << arm_dcache_l2_linesize; arm9_dcache_sets_max = (1U << (arm_dcache_l2_linesize + arm_dcache_l2_nsets)) - arm9_dcache_sets_inc; arm9_dcache_index_inc = 1U << (32 - arm_dcache_l2_assoc); arm9_dcache_index_max = 0U - arm9_dcache_index_inc; pmap_pte_init_generic(); goto out; } #endif /* CPU_ARM9 */ #if defined(CPU_ARM9E) if (cputype == CPU_ID_MV88FR131 || cputype == CPU_ID_MV88FR571_VD || cputype == CPU_ID_MV88FR571_41) { uint32_t sheeva_ctrl; sheeva_ctrl = (MV_DC_STREAM_ENABLE | MV_BTB_DISABLE | MV_L2_ENABLE); /* * Workaround for Marvell MV78100 CPU: Cache prefetch * mechanism may affect the cache coherency validity, * so it needs to be disabled. * * Refer to errata document MV-S501058-00C.pdf (p. 3.1 * L2 Prefetching Mechanism) for details. */ if (cputype == CPU_ID_MV88FR571_VD || cputype == CPU_ID_MV88FR571_41) sheeva_ctrl |= MV_L2_PREFETCH_DISABLE; sheeva_control_ext(0xffffffff & ~MV_WA_ENABLE, sheeva_ctrl); cpufuncs = sheeva_cpufuncs; get_cachetype_cp15(); pmap_pte_init_generic(); goto out; } else if (cputype == CPU_ID_ARM926EJS) { cpufuncs = armv5_ec_cpufuncs; get_cachetype_cp15(); pmap_pte_init_generic(); goto out; } #endif /* CPU_ARM9E */ #if defined(CPU_ARM1176) if (cputype == CPU_ID_ARM1176JZS) { cpufuncs = arm1176_cpufuncs; get_cachetype_cp15(); goto out; } #endif /* CPU_ARM1176 */ #if defined(CPU_CORTEXA8) || defined(CPU_CORTEXA_MP) || defined(CPU_KRAIT) switch(cputype & CPU_ID_SCHEME_MASK) { case CPU_ID_CORTEXA5: case CPU_ID_CORTEXA7: case CPU_ID_CORTEXA8: case CPU_ID_CORTEXA9: case CPU_ID_CORTEXA12: case CPU_ID_CORTEXA15: case CPU_ID_CORTEXA53: case CPU_ID_CORTEXA57: case CPU_ID_CORTEXA72: case CPU_ID_KRAIT300: cpufuncs = cortexa_cpufuncs; get_cachetype_cp15(); goto out; default: break; } #endif /* CPU_CORTEXA8 || CPU_CORTEXA_MP || CPU_KRAIT */ #if defined(CPU_MV_PJ4B) if (cputype == CPU_ID_MV88SV581X_V7 || cputype == CPU_ID_MV88SV584X_V7 || cputype == CPU_ID_ARM_88SV581X_V7) { cpufuncs = pj4bv7_cpufuncs; get_cachetype_cp15(); goto out; } #endif /* CPU_MV_PJ4B */ #if defined(CPU_FA526) if (cputype == CPU_ID_FA526 || cputype == CPU_ID_FA626TE) { cpufuncs = fa526_cpufuncs; cpu_reset_needs_v4_MMU_disable = 1; /* SA needs it */ get_cachetype_cp15(); pmap_pte_init_generic(); goto out; } #endif /* CPU_FA526 */ #if defined(CPU_XSCALE_81342) if (cputype == CPU_ID_81342) { cpufuncs = xscalec3_cpufuncs; cpu_reset_needs_v4_MMU_disable = 1; /* XScale needs it */ get_cachetype_cp15(); pmap_pte_init_xscale(); goto out; } #endif /* CPU_XSCALE_81342 */ #ifdef CPU_XSCALE_PXA2X0 /* ignore core revision to test PXA2xx CPUs */ if ((cputype & ~CPU_ID_XSCALE_COREREV_MASK) == CPU_ID_PXA250 || (cputype & ~CPU_ID_XSCALE_COREREV_MASK) == CPU_ID_PXA27X || (cputype & ~CPU_ID_XSCALE_COREREV_MASK) == CPU_ID_PXA210) { cpufuncs = xscale_cpufuncs; cpu_reset_needs_v4_MMU_disable = 1; /* XScale needs it */ get_cachetype_cp15(); pmap_pte_init_xscale(); goto out; } #endif /* CPU_XSCALE_PXA2X0 */ #ifdef CPU_XSCALE_IXP425 if (cputype == CPU_ID_IXP425_533 || cputype == CPU_ID_IXP425_400 || cputype == CPU_ID_IXP425_266 || cputype == CPU_ID_IXP435) { cpufuncs = xscale_cpufuncs; cpu_reset_needs_v4_MMU_disable = 1; /* XScale needs it */ get_cachetype_cp15(); pmap_pte_init_xscale(); goto out; } #endif /* CPU_XSCALE_IXP425 */ /* * Bzzzz. And the answer was ... */ panic("No support for this CPU type (%08x) in kernel", cputype); return(ARCHITECTURE_NOT_PRESENT); out: uma_set_align(arm_dcache_align_mask); return (0); } /* * CPU Setup code */ #ifdef CPU_ARM9 void arm9_setup(void) { int cpuctrl, cpuctrlmask; cpuctrl = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_LABT_ENABLE | CPU_CONTROL_ROUNDROBIN; cpuctrlmask = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_ROM_ENABLE | CPU_CONTROL_BEND_ENABLE | CPU_CONTROL_AFLT_ENABLE | CPU_CONTROL_LABT_ENABLE | CPU_CONTROL_VECRELOC | CPU_CONTROL_ROUNDROBIN; #ifndef ARM32_DISABLE_ALIGNMENT_FAULTS cpuctrl |= CPU_CONTROL_AFLT_ENABLE; #endif #ifdef __ARMEB__ cpuctrl |= CPU_CONTROL_BEND_ENABLE; #endif if (vector_page == ARM_VECTORS_HIGH) cpuctrl |= CPU_CONTROL_VECRELOC; /* Clear out the cache */ cpu_idcache_wbinv_all(); /* Set the control register (SCTLR) */ cpu_control(cpuctrlmask, cpuctrl); } #endif /* CPU_ARM9 */ #if defined(CPU_ARM9E) void arm10_setup(void) { int cpuctrl, cpuctrlmask; cpuctrl = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_SYST_ENABLE | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_BPRD_ENABLE; cpuctrlmask = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_SYST_ENABLE | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_ROM_ENABLE | CPU_CONTROL_BEND_ENABLE | CPU_CONTROL_AFLT_ENABLE | CPU_CONTROL_BPRD_ENABLE | CPU_CONTROL_ROUNDROBIN | CPU_CONTROL_CPCLK; #ifndef ARM32_DISABLE_ALIGNMENT_FAULTS cpuctrl |= CPU_CONTROL_AFLT_ENABLE; #endif #ifdef __ARMEB__ cpuctrl |= CPU_CONTROL_BEND_ENABLE; #endif /* Clear out the cache */ cpu_idcache_wbinv_all(); /* Now really make sure they are clean. */ __asm __volatile ("mcr\tp15, 0, r0, c7, c7, 0" : : ); if (vector_page == ARM_VECTORS_HIGH) cpuctrl |= CPU_CONTROL_VECRELOC; /* Set the control register */ cpu_control(0xffffffff, cpuctrl); /* And again. */ cpu_idcache_wbinv_all(); } #endif /* CPU_ARM9E || CPU_ARM10 */ #if defined(CPU_ARM1176) \ || defined(CPU_MV_PJ4B) \ || defined(CPU_CORTEXA8) || defined(CPU_CORTEXA_MP) || defined(CPU_KRAIT) static __inline void cpu_scc_setup_ccnt(void) { /* This is how you give userland access to the CCNT and PMCn * registers. * BEWARE! This gives write access also, which may not be what * you want! */ #ifdef _PMC_USER_READ_WRITE_ /* Set PMUSERENR[0] to allow userland access */ cp15_pmuserenr_set(1); #endif #if defined(CPU_ARM1176) /* Set PMCR[2,0] to enable counters and reset CCNT */ cp15_pmcr_set(5); #else /* Set up the PMCCNTR register as a cyclecounter: * Set PMINTENCLR to 0xFFFFFFFF to block interrupts * Set PMCR[2,0] to enable counters and reset CCNT * Set PMCNTENSET to 0x80000000 to enable CCNT */ cp15_pminten_clr(0xFFFFFFFF); cp15_pmcr_set(5); cp15_pmcnten_set(0x80000000); #endif } #endif #if defined(CPU_ARM1176) void arm11x6_setup(void) { uint32_t auxctrl, auxctrl_wax; uint32_t tmp, tmp2; uint32_t cpuid; cpuid = cpu_ident(); auxctrl = 0; auxctrl_wax = ~0; /* * Enable an errata workaround */ if ((cpuid & CPU_ID_CPU_MASK) == CPU_ID_ARM1176JZS) { /* ARM1176JZSr0 */ auxctrl = ARM1176_AUXCTL_PHD; auxctrl_wax = ~ARM1176_AUXCTL_PHD; } tmp = cp15_actlr_get(); tmp2 = tmp; tmp &= auxctrl_wax; tmp |= auxctrl; if (tmp != tmp2) cp15_actlr_set(tmp); cpu_scc_setup_ccnt(); } #endif /* CPU_ARM1176 */ #ifdef CPU_MV_PJ4B void pj4bv7_setup(void) { pj4b_config(); cpu_scc_setup_ccnt(); } #endif /* CPU_MV_PJ4B */ #if defined(CPU_CORTEXA8) || defined(CPU_CORTEXA_MP) || defined(CPU_KRAIT) void cortexa_setup(void) { cpu_scc_setup_ccnt(); } #endif /* CPU_CORTEXA8 || CPU_CORTEXA_MP || CPU_KRAIT */ #if defined(CPU_FA526) void fa526_setup(void) { int cpuctrl, cpuctrlmask; cpuctrl = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_LABT_ENABLE | CPU_CONTROL_BPRD_ENABLE; cpuctrlmask = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_ROM_ENABLE | CPU_CONTROL_BEND_ENABLE | CPU_CONTROL_AFLT_ENABLE | CPU_CONTROL_LABT_ENABLE | CPU_CONTROL_BPRD_ENABLE | CPU_CONTROL_CPCLK | CPU_CONTROL_VECRELOC; #ifndef ARM32_DISABLE_ALIGNMENT_FAULTS cpuctrl |= CPU_CONTROL_AFLT_ENABLE; #endif #ifdef __ARMEB__ cpuctrl |= CPU_CONTROL_BEND_ENABLE; #endif if (vector_page == ARM_VECTORS_HIGH) cpuctrl |= CPU_CONTROL_VECRELOC; /* Clear out the cache */ cpu_idcache_wbinv_all(); /* Set the control register */ cpu_control(0xffffffff, cpuctrl); } #endif /* CPU_FA526 */ #if defined(CPU_XSCALE_PXA2X0) || defined(CPU_XSCALE_IXP425) || \ defined(CPU_XSCALE_81342) void xscale_setup(void) { uint32_t auxctl; int cpuctrl, cpuctrlmask; /* * The XScale Write Buffer is always enabled. Our option * is to enable/disable coalescing. Note that bits 6:3 * must always be enabled. */ cpuctrl = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_LABT_ENABLE | CPU_CONTROL_BPRD_ENABLE; cpuctrlmask = CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_32BP_ENABLE | CPU_CONTROL_32BD_ENABLE | CPU_CONTROL_SYST_ENABLE | CPU_CONTROL_IC_ENABLE | CPU_CONTROL_DC_ENABLE | CPU_CONTROL_WBUF_ENABLE | CPU_CONTROL_ROM_ENABLE | CPU_CONTROL_BEND_ENABLE | CPU_CONTROL_AFLT_ENABLE | CPU_CONTROL_LABT_ENABLE | CPU_CONTROL_BPRD_ENABLE | CPU_CONTROL_CPCLK | CPU_CONTROL_VECRELOC | \ CPU_CONTROL_L2_ENABLE; #ifndef ARM32_DISABLE_ALIGNMENT_FAULTS cpuctrl |= CPU_CONTROL_AFLT_ENABLE; #endif #ifdef __ARMEB__ cpuctrl |= CPU_CONTROL_BEND_ENABLE; #endif if (vector_page == ARM_VECTORS_HIGH) cpuctrl |= CPU_CONTROL_VECRELOC; #ifdef CPU_XSCALE_CORE3 cpuctrl |= CPU_CONTROL_L2_ENABLE; #endif /* Clear out the cache */ cpu_idcache_wbinv_all(); /* * Set the control register. Note that bits 6:3 must always * be set to 1. */ /* cpu_control(cpuctrlmask, cpuctrl);*/ cpu_control(0xffffffff, cpuctrl); /* Make sure write coalescing is turned on */ __asm __volatile("mrc p15, 0, %0, c1, c0, 1" : "=r" (auxctl)); #ifdef XSCALE_NO_COALESCE_WRITES auxctl |= XSCALE_AUXCTL_K; #else auxctl &= ~XSCALE_AUXCTL_K; #endif #ifdef CPU_XSCALE_CORE3 auxctl |= XSCALE_AUXCTL_LLR; auxctl |= XSCALE_AUXCTL_MD_MASK; #endif __asm __volatile("mcr p15, 0, %0, c1, c0, 1" : : "r" (auxctl)); } #endif /* CPU_XSCALE_PXA2X0 || CPU_XSCALE_IXP425 */ Index: head/sys/arm/arm/db_trace.c =================================================================== --- head/sys/arm/arm/db_trace.c (revision 310020) +++ head/sys/arm/arm/db_trace.c (revision 310021) @@ -1,188 +1,188 @@ /* $NetBSD: db_trace.c,v 1.8 2003/01/17 22:28:48 thorpej Exp $ */ /*- * Copyright (c) 2000, 2001 Ben Harris * Copyright (c) 1996 Scott K. Stevens * * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include "opt_ddb.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void db_stack_trace_cmd(struct unwind_state *state) { const char *name; db_expr_t value; db_expr_t offset; c_db_sym_t sym; u_int reg, i; char *sep; uint16_t upd_mask; bool finished; finished = false; while (!finished) { finished = unwind_stack_one(state, 1); /* Print the frame details */ sym = db_search_symbol(state->start_pc, DB_STGY_ANY, &offset); if (sym == C_DB_SYM_NULL) { value = 0; name = "(null)"; } else db_symbol_values(sym, &name, &value); db_printf("%s() at ", name); db_printsym(state->start_pc, DB_STGY_PROC); db_printf("\n"); db_printf("\t pc = 0x%08x lr = 0x%08x (", state->start_pc, state->registers[LR]); db_printsym(state->registers[LR], DB_STGY_PROC); db_printf(")\n"); db_printf("\t sp = 0x%08x fp = 0x%08x", state->registers[SP], state->registers[FP]); /* Don't print the registers we have already printed */ upd_mask = state->update_mask & ~((1 << SP) | (1 << FP) | (1 << LR) | (1 << PC)); sep = "\n\t"; for (i = 0, reg = 0; upd_mask != 0; upd_mask >>= 1, reg++) { if ((upd_mask & 1) != 0) { db_printf("%s%sr%d = 0x%08x", sep, (reg < 10) ? " " : "", reg, state->registers[reg]); i++; if (i == 2) { sep = "\n\t"; i = 0; } else sep = " "; } } db_printf("\n"); if (finished) break; /* * Stop if directed to do so, or if we've unwound back to the * kernel entry point, or if the unwind function didn't change * anything (to avoid getting stuck in this loop forever). * If the latter happens, it's an indication that the unwind * information is incorrect somehow for the function named in * the last frame printed before you see the unwind failure * message (maybe it needs a STOP_UNWINDING). */ if (state->registers[PC] < VM_MIN_KERNEL_ADDRESS) { db_printf("Unable to unwind into user mode\n"); finished = true; } else if (state->update_mask == 0) { db_printf("Unwind failure (no registers changed)\n"); finished = true; } } } void -db_md_list_watchpoints() +db_md_list_watchpoints(void) { dbg_show_watchpoint(); } int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) { return (dbg_remove_watchpoint(addr, size)); } int db_md_set_watchpoint(db_expr_t addr, db_expr_t size) { return (dbg_setup_watchpoint(addr, size, HW_WATCHPOINT_RW)); } int db_trace_thread(struct thread *thr, int count) { struct unwind_state state; struct pcb *ctx; if (thr != curthread) { ctx = kdb_thr_ctx(thr); state.registers[FP] = ctx->pcb_regs.sf_r11; state.registers[SP] = ctx->pcb_regs.sf_sp; state.registers[LR] = ctx->pcb_regs.sf_lr; state.registers[PC] = ctx->pcb_regs.sf_pc; db_stack_trace_cmd(&state); } else db_trace_self(); return (0); } void db_trace_self(void) { struct unwind_state state; uint32_t sp; /* Read the stack pointer */ __asm __volatile("mov %0, sp" : "=&r" (sp)); state.registers[FP] = (uint32_t)__builtin_frame_address(0); state.registers[SP] = sp; state.registers[LR] = (uint32_t)__builtin_return_address(0); state.registers[PC] = (uint32_t)db_trace_self; db_stack_trace_cmd(&state); } Index: head/sys/arm/arm/physmem.c =================================================================== --- head/sys/arm/arm/physmem.c (revision 310020) +++ head/sys/arm/arm/physmem.c (revision 310021) @@ -1,376 +1,376 @@ /*- * Copyright (c) 2014 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" /* * Routines for describing and initializing anything related to physical memory. */ #include #include #include #include #include /* * These structures are used internally to keep track of regions of physical * ram, and regions within the physical ram that need to be excluded. An * exclusion region can be excluded from crash dumps, from the vm pool of pages * that can be allocated, or both, depending on the exclusion flags associated * with the region. */ #define MAX_HWCNT 10 #define MAX_EXCNT 10 #define MAX_PHYS_ADDR 0xFFFFFFFFull struct region { vm_paddr_t addr; vm_size_t size; uint32_t flags; }; static struct region hwregions[MAX_HWCNT]; static struct region exregions[MAX_EXCNT]; static size_t hwcnt; static size_t excnt; /* * These "avail lists" are globals used to communicate physical memory layout to * other parts of the kernel. Within the arrays, each value is the starting * address of a contiguous area of physical address space. The values at even * indexes are areas that contain usable memory and the values at odd indexes * are areas that aren't usable. Each list is terminated by a pair of zero * entries. * * dump_avail tells the dump code what regions to include in a crash dump, and * phys_avail is the way we hand all the remaining physical ram we haven't used * in early kernel init over to the vm system for allocation management. * * We size these arrays to hold twice as many available regions as we allow for * hardware memory regions, to allow for the fact that exclusions can split a * hardware region into two or more available regions. In the real world there * will typically be one or two hardware regions and two or three exclusions. * * Each available region in this list occupies two array slots (the start of the * available region and the start of the unavailable region that follows it). */ #define MAX_AVAIL_REGIONS (MAX_HWCNT * 2) #define MAX_AVAIL_ENTRIES (MAX_AVAIL_REGIONS * 2) vm_paddr_t phys_avail[MAX_AVAIL_ENTRIES + 2]; /* +2 to allow for a pair */ vm_paddr_t dump_avail[MAX_AVAIL_ENTRIES + 2]; /* of zeroes to terminate. */ /* * realmem is the total number of hardware pages, excluded or not. * Maxmem is one greater than the last physical page number. */ long realmem; long Maxmem; /* The address at which the kernel was loaded. Set early in initarm(). */ vm_paddr_t arm_physmem_kernaddr; /* * Print the contents of the physical and excluded region tables using the * provided printf-like output function (which will be either printf or * db_printf). */ static void physmem_dump_tables(int (*prfunc)(const char *, ...)) { int flags, i; uintmax_t addr, size; const unsigned int mbyte = 1024 * 1024; prfunc("Physical memory chunk(s):\n"); for (i = 0; i < hwcnt; ++i) { addr = hwregions[i].addr; size = hwregions[i].size; prfunc(" 0x%08jx - 0x%08jx, %5ju MB (%7ju pages)\n", addr, addr + size - 1, size / mbyte, size / PAGE_SIZE); } prfunc("Excluded memory regions:\n"); for (i = 0; i < excnt; ++i) { addr = exregions[i].addr; size = exregions[i].size; flags = exregions[i].flags; prfunc(" 0x%08jx - 0x%08jx, %5ju MB (%7ju pages) %s %s\n", addr, addr + size - 1, size / mbyte, size / PAGE_SIZE, (flags & EXFLAG_NOALLOC) ? "NoAlloc" : "", (flags & EXFLAG_NODUMP) ? "NoDump" : ""); } #ifdef DEBUG prfunc("Avail lists:\n"); for (i = 0; phys_avail[i] != 0; ++i) { prfunc(" phys_avail[%d] 0x%08x\n", i, phys_avail[i]); } for (i = 0; dump_avail[i] != 0; ++i) { prfunc(" dump_avail[%d] 0x%08x\n", i, dump_avail[i]); } #endif } /* * Print the contents of the static mapping table. Used for bootverbose. */ void -arm_physmem_print_tables() +arm_physmem_print_tables(void) { physmem_dump_tables(printf); } /* * Walk the list of hardware regions, processing it against the list of * exclusions that contain the given exflags, and generating an "avail list". * * Updates the value at *pavail with the sum of all pages in all hw regions. * * Returns the number of pages of non-excluded memory added to the avail list. */ static size_t regions_to_avail(vm_paddr_t *avail, uint32_t exflags, long *pavail) { size_t acnt, exi, hwi; uint64_t end, start, xend, xstart; long availmem; const struct region *exp, *hwp; realmem = 0; availmem = 0; acnt = 0; for (hwi = 0, hwp = hwregions; hwi < hwcnt; ++hwi, ++hwp) { start = hwp->addr; end = hwp->size + start; realmem += arm32_btop((vm_offset_t)(end - start)); for (exi = 0, exp = exregions; exi < excnt; ++exi, ++exp) { /* * If the excluded region does not match given flags, * continue checking with the next excluded region. */ if ((exp->flags & exflags) == 0) continue; xstart = exp->addr; xend = exp->size + xstart; /* * If the excluded region ends before this hw region, * continue checking with the next excluded region. */ if (xend <= start) continue; /* * If the excluded region begins after this hw region * we're done because both lists are sorted. */ if (xstart >= end) break; /* * If the excluded region completely covers this hw * region, shrink this hw region to zero size. */ if ((start >= xstart) && (end <= xend)) { start = xend; end = xend; break; } /* * If the excluded region falls wholly within this hw * region without abutting or overlapping the beginning * or end, create an available entry from the leading * fragment, then adjust the start of this hw region to * the end of the excluded region, and continue checking * the next excluded region because another exclusion * could affect the remainder of this hw region. */ if ((xstart > start) && (xend < end)) { avail[acnt++] = (vm_paddr_t)start; avail[acnt++] = (vm_paddr_t)xstart; availmem += arm32_btop((vm_offset_t)(xstart - start)); start = xend; continue; } /* * We know the excluded region overlaps either the start * or end of this hardware region (but not both), trim * the excluded portion off the appropriate end. */ if (xstart <= start) start = xend; else end = xstart; } /* * If the trimming actions above left a non-zero size, create an * available entry for it. */ if (end > start) { avail[acnt++] = (vm_paddr_t)start; avail[acnt++] = (vm_paddr_t)end; availmem += arm32_btop((vm_offset_t)(end - start)); } if (acnt >= MAX_AVAIL_ENTRIES) panic("Not enough space in the dump/phys_avail arrays"); } if (pavail) *pavail = availmem; return (acnt); } /* * Insertion-sort a new entry into a regions list; sorted by start address. */ static void insert_region(struct region *regions, size_t rcnt, vm_paddr_t addr, vm_size_t size, uint32_t flags) { size_t i; struct region *ep, *rp; ep = regions + rcnt; for (i = 0, rp = regions; i < rcnt; ++i, ++rp) { if (addr < rp->addr) { bcopy(rp, rp + 1, (ep - rp) * sizeof(*rp)); break; } } rp->addr = addr; rp->size = size; rp->flags = flags; } /* * Add a hardware memory region. */ void arm_physmem_hardware_region(uint64_t pa, uint64_t sz) { vm_offset_t adj; /* * Filter out the page at PA 0x00000000. The VM can't handle it, as * pmap_extract() == 0 means failure. */ if (pa == 0) { if (sz <= PAGE_SIZE) return; pa = PAGE_SIZE; sz -= PAGE_SIZE; } else if (pa > MAX_PHYS_ADDR) { /* This range is past usable memory, ignore it */ return; } /* * Also filter out the page at the end of the physical address space -- * if addr is non-zero and addr+size is zero we wrapped to the next byte * beyond what vm_paddr_t can express. That leads to a NULL pointer * deref early in startup; work around it by leaving the last page out. * * XXX This just in: subtract out a whole megabyte, not just 1 page. * Reducing the size by anything less than 1MB results in the NULL * pointer deref in _vm_map_lock_read(). Better to give up a megabyte * than leave some folks with an unusable system while we investigate. */ if ((pa + sz) > (MAX_PHYS_ADDR - 1024 * 1024)) { sz = MAX_PHYS_ADDR - pa + 1; if (sz <= 1024 * 1024) return; sz -= 1024 * 1024; } /* * Round the starting address up to a page boundary, and truncate the * ending page down to a page boundary. */ adj = round_page(pa) - pa; pa = round_page(pa); sz = trunc_page(sz - adj); if (sz > 0 && hwcnt < nitems(hwregions)) insert_region(hwregions, hwcnt++, pa, sz, 0); } /* * Add an exclusion region. */ void arm_physmem_exclude_region(vm_paddr_t pa, vm_size_t sz, uint32_t exflags) { vm_offset_t adj; /* * Truncate the starting address down to a page boundary, and round the * ending page up to a page boundary. */ adj = pa - trunc_page(pa); pa = trunc_page(pa); sz = round_page(sz + adj); if (excnt < nitems(exregions)) insert_region(exregions, excnt++, pa, sz, exflags); } /* * Process all the regions added earlier into the global avail lists. * * Updates the kernel global 'physmem' with the number of physical pages * available for use (all pages not in any exclusion region). * * Updates the kernel global 'Maxmem' with the page number one greater then the * last page of physical memory in the system. */ void arm_physmem_init_kernel_globals(void) { size_t nextidx; regions_to_avail(dump_avail, EXFLAG_NODUMP, NULL); nextidx = regions_to_avail(phys_avail, EXFLAG_NOALLOC, &physmem); if (nextidx == 0) panic("No memory entries in phys_avail"); Maxmem = atop(phys_avail[nextidx - 1]); } #ifdef DDB #include DB_SHOW_COMMAND(physmem, db_show_physmem) { physmem_dump_tables(db_printf); } #endif /* DDB */ Index: head/sys/arm/arm/undefined.c =================================================================== --- head/sys/arm/arm/undefined.c (revision 310020) +++ head/sys/arm/arm/undefined.c (revision 310021) @@ -1,348 +1,348 @@ /* $NetBSD: undefined.c,v 1.22 2003/11/29 22:21:29 bjh21 Exp $ */ /*- * Copyright (c) 2001 Ben Harris. * Copyright (c) 1995 Mark Brinicombe. * Copyright (c) 1995 Brini. * All rights reserved. * * This code is derived from software written for Brini by Mark Brinicombe * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Brini. * 4. The name of the company nor the name of the author may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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. * * RiscBSD kernel project * * undefined.c * * Fault handler * * Created : 06/01/95 */ #include "opt_ddb.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KDB #include #endif #include #include #include #include #include #include #include #include #include #ifdef DDB #include #endif #ifdef KDB #include #endif #define ARM_COPROC_INSN(insn) (((insn) & (1 << 27)) != 0) #define ARM_VFP_INSN(insn) ((((insn) & 0xfe000000) == 0xf2000000) || \ (((insn) & 0xff100000) == 0xf4000000)) #define ARM_COPROC(insn) (((insn) >> 8) & 0xf) #define THUMB_32BIT_INSN(insn) ((insn) >= 0xe800) #define THUMB_COPROC_INSN(insn) (((insn) & (3 << 26)) == (3 << 26)) #define THUMB_COPROC_UNDEFINED(insn) (((insn) & 0x3e << 20) == 0) #define THUMB_VFP_INSN(insn) (((insn) & (3 << 24)) == (3 << 24)) #define THUMB_COPROC(insn) (((insn) >> 8) & 0xf) #define COPROC_VFP 10 static int gdb_trapper(u_int, u_int, struct trapframe *, int); LIST_HEAD(, undefined_handler) undefined_handlers[MAX_COPROCS]; void * install_coproc_handler(int coproc, undef_handler_t handler) { struct undefined_handler *uh; KASSERT(coproc >= 0 && coproc < MAX_COPROCS, ("bad coproc")); KASSERT(handler != NULL, ("handler is NULL")); /* Used to be legal. */ /* XXX: M_TEMP??? */ uh = malloc(sizeof(*uh), M_TEMP, M_WAITOK); uh->uh_handler = handler; install_coproc_handler_static(coproc, uh); return uh; } void install_coproc_handler_static(int coproc, struct undefined_handler *uh) { LIST_INSERT_HEAD(&undefined_handlers[coproc], uh, uh_link); } void remove_coproc_handler(void *cookie) { struct undefined_handler *uh = cookie; LIST_REMOVE(uh, uh_link); free(uh, M_TEMP); } static int gdb_trapper(u_int addr, u_int insn, struct trapframe *frame, int code) { struct thread *td; ksiginfo_t ksi; td = (curthread == NULL) ? &thread0 : curthread; if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) { if (code == FAULT_USER) { ksiginfo_init_trap(&ksi); ksi.ksi_signo = SIGTRAP; ksi.ksi_code = TRAP_BRKPT; ksi.ksi_addr = (u_int32_t *)addr; trapsignal(td, &ksi); return 0; } #if 0 #ifdef KGDB return !kgdb_trap(T_BREAKPOINT, frame); #endif #endif } return 1; } static struct undefined_handler gdb_uh; void -undefined_init() +undefined_init(void) { int loop; /* Not actually necessary -- the initialiser is just NULL */ for (loop = 0; loop < MAX_COPROCS; ++loop) LIST_INIT(&undefined_handlers[loop]); /* Install handler for GDB breakpoints */ gdb_uh.uh_handler = gdb_trapper; install_coproc_handler_static(0, &gdb_uh); } void undefinedinstruction(struct trapframe *frame) { struct thread *td; u_int fault_pc; int fault_instruction; int fault_code; int coprocessor; struct undefined_handler *uh; int error; #ifdef VERBOSE_ARM32 int s; #endif ksiginfo_t ksi; /* Enable interrupts if they were enabled before the exception. */ if (__predict_true(frame->tf_spsr & PSR_I) == 0) enable_interrupts(PSR_I); if (__predict_true(frame->tf_spsr & PSR_F) == 0) enable_interrupts(PSR_F); PCPU_INC(cnt.v_trap); fault_pc = frame->tf_pc; /* * Get the current thread/proc structure or thread0/proc0 if there is * none. */ td = curthread == NULL ? &thread0 : curthread; coprocessor = 0; if ((frame->tf_spsr & PSR_T) == 0) { /* * Make sure the program counter is correctly aligned so we * don't take an alignment fault trying to read the opcode. */ if (__predict_false((fault_pc & 3) != 0)) { ksiginfo_init_trap(&ksi); ksi.ksi_signo = SIGILL; ksi.ksi_code = ILL_ILLADR; ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc; trapsignal(td, &ksi); userret(td, frame); return; } /* * Should use fuword() here .. but in the interests of * squeezing every bit of speed we will just use ReadWord(). * We know the instruction can be read as was just executed * so this will never fail unless the kernel is screwed up * in which case it does not really matter does it ? */ fault_instruction = *(u_int32_t *)fault_pc; /* Check for coprocessor instruction */ /* * According to the datasheets you only need to look at bit * 27 of the instruction to tell the difference between and * undefined instruction and a coprocessor instruction * following an undefined instruction trap. */ if (ARM_COPROC_INSN(fault_instruction)) coprocessor = ARM_COPROC(fault_instruction); else { /* check for special instructions */ if (ARM_VFP_INSN(fault_instruction)) coprocessor = COPROC_VFP; /* vfp / simd */ } } else { #if __ARM_ARCH >= 7 fault_instruction = *(uint16_t *)fault_pc; if (THUMB_32BIT_INSN(fault_instruction)) { fault_instruction <<= 16; fault_instruction |= *(uint16_t *)(fault_pc + 2); /* * Is it a Coprocessor, Advanced SIMD, or * Floating-point instruction. */ if (THUMB_COPROC_INSN(fault_instruction)) { if (THUMB_COPROC_UNDEFINED(fault_instruction)) { /* undefined insn */ } else if (THUMB_VFP_INSN(fault_instruction)) coprocessor = COPROC_VFP; else coprocessor = THUMB_COPROC(fault_instruction); } } #else /* * No support for Thumb-2 on this cpu */ ksiginfo_init_trap(&ksi); ksi.ksi_signo = SIGILL; ksi.ksi_code = ILL_ILLADR; ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc; trapsignal(td, &ksi); userret(td, frame); return; #endif } if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) { /* * Modify the fault_code to reflect the USR/SVC state at * time of fault. */ fault_code = FAULT_USER; td->td_frame = frame; } else fault_code = 0; /* OK this is were we do something about the instruction. */ LIST_FOREACH(uh, &undefined_handlers[coprocessor], uh_link) if (uh->uh_handler(fault_pc, fault_instruction, frame, fault_code) == 0) break; if (fault_code & FAULT_USER) { /* TODO: No support for ptrace from Thumb-2 */ if ((frame->tf_spsr & PSR_T) == 0 && fault_instruction == PTRACE_BREAKPOINT) { PROC_LOCK(td->td_proc); _PHOLD(td->td_proc); error = ptrace_clear_single_step(td); _PRELE(td->td_proc); PROC_UNLOCK(td->td_proc); if (error != 0) { ksiginfo_init_trap(&ksi); ksi.ksi_signo = SIGILL; ksi.ksi_code = ILL_ILLOPC; ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc; trapsignal(td, &ksi); } return; } } if (uh == NULL && (fault_code & FAULT_USER)) { /* Fault has not been handled */ ksiginfo_init_trap(&ksi); ksi.ksi_signo = SIGILL; ksi.ksi_code = ILL_ILLOPC; ksi.ksi_addr = (u_int32_t *)(intptr_t) fault_pc; trapsignal(td, &ksi); } if ((fault_code & FAULT_USER) == 0) { if (fault_instruction == KERNEL_BREAKPOINT) { #ifdef KDB kdb_trap(T_BREAKPOINT, 0, frame); #else printf("No debugger in kernel.\n"); #endif return; } else panic("Undefined instruction in kernel.\n"); } userret(td, frame); } Index: head/sys/arm/broadcom/bcm2835/bcm2835_wdog.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_wdog.c (revision 310020) +++ head/sys/arm/broadcom/bcm2835/bcm2835_wdog.c (revision 310021) @@ -1,222 +1,222 @@ /*- * Copyright (c) 2012 Alexander Rybalko * 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #define BCM2835_PASWORD 0x5a #define BCM2835_WDOG_RESET 0 #define BCM2835_PASSWORD_MASK 0xff000000 #define BCM2835_PASSWORD_SHIFT 24 #define BCM2835_WDOG_TIME_MASK 0x000fffff #define BCM2835_WDOG_TIME_SHIFT 0 #define READ(_sc, _r) bus_space_read_4((_sc)->bst, (_sc)->bsh, (_r) + (_sc)->regs_offset) #define WRITE(_sc, _r, _v) bus_space_write_4((_sc)->bst, (_sc)->bsh, (_r) + (_sc)->regs_offset, (_v)) #define BCM2835_RSTC_WRCFG_CLR 0xffffffcf #define BCM2835_RSTC_WRCFG_SET 0x00000030 #define BCM2835_RSTC_WRCFG_FULL_RESET 0x00000020 #define BCM2835_RSTC_RESET 0x00000102 #define BCM2835_RSTC_REG 0x00 #define BCM2835_RSTS_REG 0x04 #define BCM2835_WDOG_REG 0x08 static struct bcmwd_softc *bcmwd_lsc = NULL; struct bcmwd_softc { device_t dev; struct resource * res; bus_space_tag_t bst; bus_space_handle_t bsh; int wdog_armed; int wdog_period; char wdog_passwd; struct mtx mtx; int regs_offset; }; #define BSD_DTB 1 #define UPSTREAM_DTB 2 #define UPSTREAM_DTB_REGS_OFFSET 0x1c static struct ofw_compat_data compat_data[] = { {"broadcom,bcm2835-wdt", BSD_DTB}, {"brcm,bcm2835-pm-wdt", UPSTREAM_DTB}, {NULL, 0} }; static void bcmwd_watchdog_fn(void *private, u_int cmd, int *error); static int bcmwd_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2708/2835 Watchdog"); return (BUS_PROBE_DEFAULT); } static int bcmwd_attach(device_t dev) { struct bcmwd_softc *sc; int rid; if (bcmwd_lsc != NULL) return (ENXIO); sc = device_get_softc(dev); sc->wdog_period = 7; sc->wdog_passwd = BCM2835_PASWORD; sc->wdog_armed = 0; sc->dev = dev; rid = 0; sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->res); sc->bsh = rman_get_bushandle(sc->res); /* compensate base address difference */ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == UPSTREAM_DTB) sc->regs_offset = UPSTREAM_DTB_REGS_OFFSET; bcmwd_lsc = sc; mtx_init(&sc->mtx, "BCM2835 Watchdog", "bcmwd", MTX_DEF); EVENTHANDLER_REGISTER(watchdog_list, bcmwd_watchdog_fn, sc, 0); return (0); } static void bcmwd_watchdog_fn(void *private, u_int cmd, int *error) { struct bcmwd_softc *sc; uint64_t sec; uint32_t ticks, reg; sc = private; mtx_lock(&sc->mtx); cmd &= WD_INTERVAL; if (cmd > 0) { sec = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000000; if (sec == 0 || sec > 15) { /* * Can't arm * disable watchdog as watchdog(9) requires */ device_printf(sc->dev, "Can't arm, timeout must be between 1-15 seconds\n"); WRITE(sc, BCM2835_RSTC_REG, (BCM2835_PASWORD << BCM2835_PASSWORD_SHIFT) | BCM2835_RSTC_RESET); mtx_unlock(&sc->mtx); return; } ticks = (sec << 16) & BCM2835_WDOG_TIME_MASK; reg = (BCM2835_PASWORD << BCM2835_PASSWORD_SHIFT) | ticks; WRITE(sc, BCM2835_WDOG_REG, reg); reg = READ(sc, BCM2835_RSTC_REG); reg &= BCM2835_RSTC_WRCFG_CLR; reg |= BCM2835_RSTC_WRCFG_FULL_RESET; reg |= (BCM2835_PASWORD << BCM2835_PASSWORD_SHIFT); WRITE(sc, BCM2835_RSTC_REG, reg); *error = 0; } else WRITE(sc, BCM2835_RSTC_REG, (BCM2835_PASWORD << BCM2835_PASSWORD_SHIFT) | BCM2835_RSTC_RESET); mtx_unlock(&sc->mtx); } void -bcmwd_watchdog_reset() +bcmwd_watchdog_reset(void) { if (bcmwd_lsc == NULL) return; WRITE(bcmwd_lsc, BCM2835_WDOG_REG, (BCM2835_PASWORD << BCM2835_PASSWORD_SHIFT) | 10); WRITE(bcmwd_lsc, BCM2835_RSTC_REG, (READ(bcmwd_lsc, BCM2835_RSTC_REG) & BCM2835_RSTC_WRCFG_CLR) | (BCM2835_PASWORD << BCM2835_PASSWORD_SHIFT) | BCM2835_RSTC_WRCFG_FULL_RESET); } static device_method_t bcmwd_methods[] = { DEVMETHOD(device_probe, bcmwd_probe), DEVMETHOD(device_attach, bcmwd_attach), DEVMETHOD_END }; static driver_t bcmwd_driver = { "bcmwd", bcmwd_methods, sizeof(struct bcmwd_softc), }; static devclass_t bcmwd_devclass; DRIVER_MODULE(bcmwd, simplebus, bcmwd_driver, bcmwd_devclass, 0, 0); Index: head/sys/arm/freescale/imx/imx6_anatop.c =================================================================== --- head/sys/arm/freescale/imx/imx6_anatop.c (revision 310020) +++ head/sys/arm/freescale/imx/imx6_anatop.c (revision 310021) @@ -1,813 +1,813 @@ /*- * 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 #include #include #include #include static struct resource_spec imx6_anatop_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; #define MEMRES 0 #define IRQRES 1 struct imx6_anatop_softc { device_t dev; struct resource *res[2]; struct intr_config_hook intr_setup_hook; uint32_t cpu_curmhz; uint32_t cpu_curmv; uint32_t cpu_minmhz; uint32_t cpu_minmv; uint32_t cpu_maxmhz; uint32_t cpu_maxmv; uint32_t cpu_maxmhz_hw; boolean_t cpu_overclock_enable; boolean_t cpu_init_done; uint32_t refosc_mhz; 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 "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, 950}, { 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 2731 /* deci-Kelvin <-> deci-Celcius offset. */ uint32_t imx6_anatop_read_4(bus_size_t 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) { KASSERT(imx6_anatop_sc != NULL, ("imx6_anatop_write_4 sc NULL")); bus_write_4(imx6_anatop_sc->res[MEMRES], offset, value); } static void vdd_set(struct imx6_anatop_softc *sc, int mv) { 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. 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); 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 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; 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) * 6; else delay = 0; } else { delay = (700 / 25) * 6; 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 |= newtargSoc << IMX6_ANALOG_PMU_REG2_TARG_SHIFT; imx6_anatop_write_4(IMX6_ANALOG_PMU_REG_CORE, pmureg); DELAY(delay); sc->cpu_curmv = newtarg * 25 + 700; } static inline uint32_t cpufreq_mhz_from_div(struct imx6_anatop_softc *sc, uint32_t corediv, uint32_t plldiv) { return ((sc->refosc_mhz * (plldiv / 2)) / (corediv + 1)); } static inline void cpufreq_mhz_to_div(struct imx6_anatop_softc *sc, uint32_t cpu_mhz, uint32_t *corediv, uint32_t *plldiv) { *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; cpufreq_mhz_to_div(sc, cpu_mhz, &corediv, &plldiv); return (cpufreq_mhz_from_div(sc, corediv, plldiv)); } 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, struct oppt *op) { uint32_t corediv, plldiv, timeout, wrk32; /* 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, * 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 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, 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 |= plldiv; 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); imx_ccm_set_cacrr(corediv); /* 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 oppt * op; SYSCTL_ADD_INT(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx), OID_AUTO, "cpu_mhz", CTLFLAG_RD, &sc->cpu_curmhz, 0, "CPU frequency"); SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx), OID_AUTO, "cpu_minmhz", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH, sc, 0, cpufreq_sysctl_minmhz, "IU", "Minimum CPU frequency"); SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx), OID_AUTO, "cpu_maxmhz", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH, sc, 0, cpufreq_sysctl_maxmhz, "IU", "Maximum CPU frequency"); SYSCTL_ADD_INT(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx), 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_imx), 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_mhz = 24; /* * 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; * 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; 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. * * 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, op); } 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 temp_sysctl_handler(SYSCTL_HANDLER_ARGS) { struct imx6_anatop_softc *sc = arg1; uint32_t t; 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_curmhz < sc->cpu_maxmhz) { cpufreq_set_clock(sc, cpufreq_nearest_oppt(sc, sc->cpu_maxmhz)); } } static void tempmon_goslow(struct imx6_anatop_softc *sc) { if (sc->cpu_curmhz > sc->cpu_minmhz) { cpufreq_set_clock(sc, cpufreq_nearest_oppt(sc, sc->cpu_minmhz)); } } 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; /* 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); 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; /* * 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); SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx), OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0, temp_sysctl_handler, "IK", "Current die temperature"); SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_imx), OID_AUTO, "throttle_temperature", CTLTYPE_INT | CTLFLAG_RW, sc, 0, temp_throttle_sysctl_handler, "IK", "Throttle CPU when exceeding this temperature"); } static void intr_setup(void *arg) { int rid; struct imx6_anatop_softc *sc; sc = arg; rid = 0; sc->res[IRQRES] = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->res[IRQRES] != NULL) { bus_setup_intr(sc->dev, sc->res[IRQRES], INTR_TYPE_MISC | INTR_MPSAFE, tempmon_intr, NULL, sc, &sc->temp_intrhand); } else { device_printf(sc->dev, "Cannot allocate IRQ resource\n"); } config_intrhook_disestablish(&sc->intr_setup_hook); } static void imx6_anatop_new_pass(device_t dev) { struct imx6_anatop_softc *sc; const int cpu_init_pass = BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE; /* * We attach during BUS_PASS_BUS (because some day we will be a * simplebus that has regulator devices as children), but some of our * init work cannot be done until BUS_PASS_CPU (we rely on other devices * that attach on the CPU pass). */ sc = device_get_softc(dev); if (!sc->cpu_init_done && bus_current_pass >= cpu_init_pass) { sc->cpu_init_done = true; cpufreq_initialize(sc); initialize_tempmon(sc); if (bootverbose) { device_printf(sc->dev, "CPU %uMHz @ %umV\n", sc->cpu_curmhz, sc->cpu_curmv); } } bus_generic_new_pass(dev); } static int imx6_anatop_detach(device_t dev) { /* This device can never detach. */ return (EBUSY); } static int imx6_anatop_attach(device_t dev) { struct imx6_anatop_softc *sc; int err; sc = device_get_softc(dev); sc->dev = dev; /* Allocate bus_space resources. */ if (bus_alloc_resources(dev, imx6_anatop_spec, sc->res)) { device_printf(dev, "Cannot allocate resources\n"); err = ENXIO; goto out; } sc->intr_setup_hook.ich_func = intr_setup; sc->intr_setup_hook.ich_arg = sc; config_intrhook_establish(&sc->intr_setup_hook); 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; /* * 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); /* * Some day, when we're ready to deal with the actual anatop regulators * that are described in fdt data as children of this "bus", this would * be the place to invoke a simplebus helper routine to instantiate the * children from the fdt data. */ err = 0; out: if (err != 0) { bus_release_resources(dev, imx6_anatop_spec, sc->res); } return (err); } uint32_t pll4_configure_output(uint32_t mfi, uint32_t mfn, uint32_t mfd) { int reg; /* * Audio PLL (PLL4). * PLL output frequency = Fref * (DIV_SELECT + NUM/DENOM) */ reg = (IMX6_ANALOG_CCM_PLL_AUDIO_ENABLE); reg &= ~(IMX6_ANALOG_CCM_PLL_AUDIO_DIV_SELECT_MASK << \ IMX6_ANALOG_CCM_PLL_AUDIO_DIV_SELECT_SHIFT); reg |= (mfi << IMX6_ANALOG_CCM_PLL_AUDIO_DIV_SELECT_SHIFT); imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_AUDIO, reg); imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_AUDIO_NUM, mfn); imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_AUDIO_DENOM, mfd); return (0); } 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) return (ENXIO); device_set_desc(dev, "Freescale i.MX6 Analog PLLs and Power"); return (BUS_PROBE_DEFAULT); } uint32_t -imx6_get_cpu_clock() +imx6_get_cpu_clock(void) { uint32_t corediv, plldiv; 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, corediv, plldiv)); } 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), /* Bus interface */ DEVMETHOD(bus_new_pass, imx6_anatop_new_pass), DEVMETHOD_END }; static driver_t imx6_anatop_driver = { "imx6_anatop", imx6_anatop_methods, sizeof(struct imx6_anatop_softc) }; static devclass_t imx6_anatop_devclass; EARLY_DRIVER_MODULE(imx6_anatop, simplebus, imx6_anatop_driver, imx6_anatop_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); EARLY_DRIVER_MODULE(imx6_anatop, ofwbus, imx6_anatop_driver, imx6_anatop_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); Index: head/sys/arm/freescale/imx/imx6_src.c =================================================================== --- head/sys/arm/freescale/imx/imx6_src.c (revision 310020) +++ head/sys/arm/freescale/imx/imx6_src.c (revision 310021) @@ -1,173 +1,173 @@ /*- * Copyright (c) 2015 Oleksandr Tymoshenko * 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$"); /* * System Reset Control for iMX6 */ #include #include #include #include #include #include #include #include #include #include #define SRC_SCR 0 #define SW_IPU1_RST (1 << 3) struct src_softc { device_t dev; struct resource *mem_res; }; static struct src_softc *src_sc; static inline uint32_t RD4(struct src_softc *sc, bus_size_t off) { return (bus_read_4(sc->mem_res, off)); } static inline void WR4(struct src_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->mem_res, off, val); } int -src_reset_ipu() +src_reset_ipu(void) { uint32_t reg; int timeout = 10000; if (src_sc == NULL) return (-1); reg = RD4(src_sc, SRC_SCR); reg |= SW_IPU1_RST; WR4(src_sc, SRC_SCR, reg); while (timeout-- > 0) { reg = RD4(src_sc, SRC_SCR); if (reg & SW_IPU1_RST) DELAY(1); else break; } if (timeout < 0) return (-1); else return (0); } static int src_detach(device_t dev) { struct src_softc *sc; sc = device_get_softc(dev); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (0); } static int src_attach(device_t dev) { struct src_softc *sc; int err, rid; sc = device_get_softc(dev); err = 0; /* 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"); err = ENXIO; goto out; } src_sc = sc; err = 0; out: if (err != 0) src_detach(dev); return (err); } static int src_probe(device_t dev) { if ((ofw_bus_is_compatible(dev, "fsl,imx6q-src") == 0) && (ofw_bus_is_compatible(dev, "fsl,imx6-src") == 0)) return (ENXIO); device_set_desc(dev, "Freescale i.MX6 System Reset Controller"); return (BUS_PROBE_DEFAULT); } static device_method_t src_methods[] = { /* Device interface */ DEVMETHOD(device_probe, src_probe), DEVMETHOD(device_attach, src_attach), DEVMETHOD(device_detach, src_detach), DEVMETHOD_END }; static driver_t src_driver = { "src", src_methods, sizeof(struct src_softc) }; static devclass_t src_devclass; DRIVER_MODULE(src, simplebus, src_driver, src_devclass, 0, 0); Index: head/sys/arm/lpc/lpc_gpio.c =================================================================== --- head/sys/arm/lpc/lpc_gpio.c (revision 310020) +++ head/sys/arm/lpc/lpc_gpio.c (revision 310021) @@ -1,571 +1,571 @@ /*- * Copyright (c) 2011 Jakub Wojciech Klama * 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. * */ /* * GPIO on LPC32x0 consist of 4 ports: * - Port0 with 8 input/output pins * - Port1 with 24 input/output pins * - Port2 with 13 input/output pins * - Port3 with: * - 26 input pins (GPI_00..GPI_09 + GPI_15..GPI_23 + GPI_25 + GPI_27..GPI_28) * - 24 output pins (GPO_00..GPO_23) * - 6 input/output pins (GPIO_00..GPIO_05) * * Pins are mapped to logical pin number as follows: * [0..9] -> GPI_00..GPI_09 (port 3) * [10..18] -> GPI_15..GPI_23 (port 3) * [19] -> GPI_25 (port 3) * [20..21] -> GPI_27..GPI_28 (port 3) * [22..45] -> GPO_00..GPO_23 (port 3) * [46..51] -> GPIO_00..GPIO_05 (port 3) * [52..64] -> P2.0..P2.12 (port 2) * [65..88] -> P1.0..P1.23 (port 1) * [89..96] -> P0.0..P0.7 (port 0) * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" struct lpc_gpio_softc { device_t lg_dev; device_t lg_busdev; struct resource * lg_res; bus_space_tag_t lg_bst; bus_space_handle_t lg_bsh; }; struct lpc_gpio_pinmap { int lp_start_idx; int lp_pin_count; int lp_port; int lp_start_bit; int lp_flags; }; static const struct lpc_gpio_pinmap lpc_gpio_pins[] = { { 0, 10, 3, 0, GPIO_PIN_INPUT }, { 10, 9, 3, 15, GPIO_PIN_INPUT }, { 19, 1, 3, 25, GPIO_PIN_INPUT }, { 20, 2, 3, 27, GPIO_PIN_INPUT }, { 22, 24, 3, 0, GPIO_PIN_OUTPUT }, /* * -1 below is to mark special case for Port3 GPIO pins, as they * have other bits in Port 3 registers as inputs and as outputs */ { 46, 6, 3, -1, GPIO_PIN_INPUT | GPIO_PIN_OUTPUT }, { 52, 13, 2, 0, GPIO_PIN_INPUT | GPIO_PIN_OUTPUT }, { 65, 24, 1, 0, GPIO_PIN_INPUT | GPIO_PIN_OUTPUT }, { 89, 8, 0, 0, GPIO_PIN_INPUT | GPIO_PIN_OUTPUT }, { -1, -1, -1, -1, -1 }, }; #define LPC_GPIO_NPINS \ (LPC_GPIO_P0_COUNT + LPC_GPIO_P1_COUNT + \ LPC_GPIO_P2_COUNT + LPC_GPIO_P3_COUNT) #define LPC_GPIO_PIN_IDX(_map, _idx) \ (_idx - _map->lp_start_idx) #define LPC_GPIO_PIN_BIT(_map, _idx) \ (_map->lp_start_bit + LPC_GPIO_PIN_IDX(_map, _idx)) static int lpc_gpio_probe(device_t); static int lpc_gpio_attach(device_t); static int lpc_gpio_detach(device_t); static device_t lpc_gpio_get_bus(device_t); static int lpc_gpio_pin_max(device_t, int *); static int lpc_gpio_pin_getcaps(device_t, uint32_t, uint32_t *); static int lpc_gpio_pin_getflags(device_t, uint32_t, uint32_t *); static int lpc_gpio_pin_setflags(device_t, uint32_t, uint32_t); static int lpc_gpio_pin_getname(device_t, uint32_t, char *); static int lpc_gpio_pin_get(device_t, uint32_t, uint32_t *); static int lpc_gpio_pin_set(device_t, uint32_t, uint32_t); static int lpc_gpio_pin_toggle(device_t, uint32_t); static const struct lpc_gpio_pinmap *lpc_gpio_get_pinmap(int); static struct lpc_gpio_softc *lpc_gpio_sc = NULL; #define lpc_gpio_read_4(_sc, _reg) \ bus_space_read_4(_sc->lg_bst, _sc->lg_bsh, _reg) #define lpc_gpio_write_4(_sc, _reg, _val) \ bus_space_write_4(_sc->lg_bst, _sc->lg_bsh, _reg, _val) #define lpc_gpio_get_4(_sc, _test, _reg1, _reg2) \ lpc_gpio_read_4(_sc, ((_test) ? _reg1 : _reg2)) #define lpc_gpio_set_4(_sc, _test, _reg1, _reg2, _val) \ lpc_gpio_write_4(_sc, ((_test) ? _reg1 : _reg2), _val) static int lpc_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "lpc,gpio")) return (ENXIO); device_set_desc(dev, "LPC32x0 GPIO"); return (BUS_PROBE_DEFAULT); } static int lpc_gpio_attach(device_t dev) { struct lpc_gpio_softc *sc = device_get_softc(dev); int rid; sc->lg_dev = dev; rid = 0; sc->lg_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->lg_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } sc->lg_bst = rman_get_bustag(sc->lg_res); sc->lg_bsh = rman_get_bushandle(sc->lg_res); lpc_gpio_sc = sc; sc->lg_busdev = gpiobus_attach_bus(dev); if (sc->lg_busdev == NULL) { bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->lg_res); return (ENXIO); } return (0); } static int lpc_gpio_detach(device_t dev) { return (EBUSY); } static device_t lpc_gpio_get_bus(device_t dev) { struct lpc_gpio_softc *sc; sc = device_get_softc(dev); return (sc->lg_busdev); } static int lpc_gpio_pin_max(device_t dev, int *npins) { *npins = LPC_GPIO_NPINS - 1; return (0); } static int lpc_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { const struct lpc_gpio_pinmap *map; if (pin > LPC_GPIO_NPINS) return (ENODEV); map = lpc_gpio_get_pinmap(pin); *caps = map->lp_flags; return (0); } static int lpc_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct lpc_gpio_softc *sc = device_get_softc(dev); const struct lpc_gpio_pinmap *map; uint32_t state; int dir; if (pin > LPC_GPIO_NPINS) return (ENODEV); map = lpc_gpio_get_pinmap(pin); /* Check whether it's bidirectional pin */ if ((map->lp_flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) != (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { *flags = map->lp_flags; return (0); } switch (map->lp_port) { case 0: state = lpc_gpio_read_4(sc, LPC_GPIO_P0_DIR_STATE); dir = (state & (1 << LPC_GPIO_PIN_BIT(map, pin))); break; case 1: state = lpc_gpio_read_4(sc, LPC_GPIO_P1_DIR_STATE); dir = (state & (1 << LPC_GPIO_PIN_BIT(map, pin))); break; case 2: state = lpc_gpio_read_4(sc, LPC_GPIO_P2_DIR_STATE); dir = (state & (1 << LPC_GPIO_PIN_BIT(map, pin))); break; case 3: state = lpc_gpio_read_4(sc, LPC_GPIO_P2_DIR_STATE); dir = (state & (1 << (25 + LPC_GPIO_PIN_IDX(map, pin)))); break; default: panic("unknown GPIO port"); } *flags = dir ? GPIO_PIN_OUTPUT : GPIO_PIN_INPUT; return (0); } static int lpc_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct lpc_gpio_softc *sc = device_get_softc(dev); const struct lpc_gpio_pinmap *map; uint32_t dir, state; if (pin > LPC_GPIO_NPINS) return (ENODEV); map = lpc_gpio_get_pinmap(pin); /* Check whether it's bidirectional pin */ if ((map->lp_flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) != (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) return (ENOTSUP); if (flags & GPIO_PIN_INPUT) dir = 0; if (flags & GPIO_PIN_OUTPUT) dir = 1; switch (map->lp_port) { case 0: state = (1 << LPC_GPIO_PIN_IDX(map, pin)); lpc_gpio_set_4(sc, dir, LPC_GPIO_P0_DIR_SET, LPC_GPIO_P0_DIR_CLR, state); break; case 1: state = (1 << LPC_GPIO_PIN_IDX(map, pin)); lpc_gpio_set_4(sc, dir, LPC_GPIO_P1_DIR_SET, LPC_GPIO_P0_DIR_CLR, state); break; case 2: state = (1 << LPC_GPIO_PIN_IDX(map, pin)); lpc_gpio_set_4(sc, dir, LPC_GPIO_P2_DIR_SET, LPC_GPIO_P0_DIR_CLR, state); break; case 3: state = (1 << (25 + (pin - map->lp_start_idx))); lpc_gpio_set_4(sc, dir, LPC_GPIO_P2_DIR_SET, LPC_GPIO_P0_DIR_CLR, state); break; } return (0); } static int lpc_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { const struct lpc_gpio_pinmap *map; int idx; map = lpc_gpio_get_pinmap(pin); idx = LPC_GPIO_PIN_IDX(map, pin); switch (map->lp_port) { case 0: case 1: case 2: snprintf(name, GPIOMAXNAME - 1, "P%d.%d", map->lp_port, map->lp_start_bit + LPC_GPIO_PIN_IDX(map, pin)); break; case 3: if (map->lp_start_bit == -1) { snprintf(name, GPIOMAXNAME - 1, "GPIO_%02d", idx); break; } snprintf(name, GPIOMAXNAME - 1, "GP%c_%02d", (map->lp_flags & GPIO_PIN_INPUT) ? 'I' : 'O', map->lp_start_bit + idx); break; } return (0); } static int lpc_gpio_pin_get(device_t dev, uint32_t pin, uint32_t *value) { struct lpc_gpio_softc *sc = device_get_softc(dev); const struct lpc_gpio_pinmap *map; uint32_t state, flags; int dir; map = lpc_gpio_get_pinmap(pin); if (lpc_gpio_pin_getflags(dev, pin, &flags)) return (ENXIO); if (flags & GPIO_PIN_OUTPUT) dir = 1; if (flags & GPIO_PIN_INPUT) dir = 0; switch (map->lp_port) { case 0: state = lpc_gpio_get_4(sc, dir, LPC_GPIO_P0_OUTP_STATE, LPC_GPIO_P0_INP_STATE); *value = !!(state & (1 << LPC_GPIO_PIN_BIT(map, pin))); case 1: state = lpc_gpio_get_4(sc, dir, LPC_GPIO_P1_OUTP_STATE, LPC_GPIO_P1_INP_STATE); *value = !!(state & (1 << LPC_GPIO_PIN_BIT(map, pin))); case 2: state = lpc_gpio_read_4(sc, LPC_GPIO_P2_INP_STATE); *value = !!(state & (1 << LPC_GPIO_PIN_BIT(map, pin))); case 3: state = lpc_gpio_get_4(sc, dir, LPC_GPIO_P3_OUTP_STATE, LPC_GPIO_P3_INP_STATE); if (map->lp_start_bit == -1) { if (dir) *value = !!(state & (1 << (25 + LPC_GPIO_PIN_IDX(map, pin)))); else *value = !!(state & (1 << (10 + LPC_GPIO_PIN_IDX(map, pin)))); } *value = !!(state & (1 << LPC_GPIO_PIN_BIT(map, pin))); } return (0); } static int lpc_gpio_pin_set(device_t dev, uint32_t pin, uint32_t value) { struct lpc_gpio_softc *sc = device_get_softc(dev); const struct lpc_gpio_pinmap *map; uint32_t state, flags; map = lpc_gpio_get_pinmap(pin); if (lpc_gpio_pin_getflags(dev, pin, &flags)) return (ENXIO); if ((flags & GPIO_PIN_OUTPUT) == 0) return (EINVAL); state = (1 << LPC_GPIO_PIN_BIT(map, pin)); switch (map->lp_port) { case 0: lpc_gpio_set_4(sc, value, LPC_GPIO_P0_OUTP_SET, LPC_GPIO_P0_OUTP_CLR, state); break; case 1: lpc_gpio_set_4(sc, value, LPC_GPIO_P1_OUTP_SET, LPC_GPIO_P1_OUTP_CLR, state); break; case 2: lpc_gpio_set_4(sc, value, LPC_GPIO_P2_OUTP_SET, LPC_GPIO_P2_OUTP_CLR, state); break; case 3: if (map->lp_start_bit == -1) state = (1 << (25 + LPC_GPIO_PIN_IDX(map, pin))); lpc_gpio_set_4(sc, value, LPC_GPIO_P3_OUTP_SET, LPC_GPIO_P3_OUTP_CLR, state); break; } return (0); } static int lpc_gpio_pin_toggle(device_t dev, uint32_t pin) { const struct lpc_gpio_pinmap *map; uint32_t flags; map = lpc_gpio_get_pinmap(pin); if (lpc_gpio_pin_getflags(dev, pin, &flags)) return (ENXIO); if ((flags & GPIO_PIN_OUTPUT) == 0) return (EINVAL); panic("not implemented yet"); return (0); } static const struct lpc_gpio_pinmap * lpc_gpio_get_pinmap(int pin) { const struct lpc_gpio_pinmap *map; for (map = &lpc_gpio_pins[0]; map->lp_start_idx != -1; map++) { if (pin >= map->lp_start_idx && pin < map->lp_start_idx + map->lp_pin_count) return map; } panic("pin number %d out of range", pin); } int lpc_gpio_set_flags(device_t dev, int pin, int flags) { if (lpc_gpio_sc == NULL) return (ENXIO); return lpc_gpio_pin_setflags(lpc_gpio_sc->lg_dev, pin, flags); } int lpc_gpio_set_state(device_t dev, int pin, int state) { if (lpc_gpio_sc == NULL) return (ENXIO); return lpc_gpio_pin_set(lpc_gpio_sc->lg_dev, pin, state); } int lpc_gpio_get_state(device_t dev, int pin, int *state) { if (lpc_gpio_sc == NULL) return (ENXIO); return lpc_gpio_pin_get(lpc_gpio_sc->lg_dev, pin, state); } void -lpc_gpio_init() +lpc_gpio_init(void) { bus_space_tag_t bst; bus_space_handle_t bsh; bst = fdtbus_bs_tag; /* Preset SPI devices CS pins to one */ bus_space_map(bst, LPC_GPIO_PHYS_BASE, LPC_GPIO_SIZE, 0, &bsh); bus_space_write_4(bst, bsh, LPC_GPIO_P3_OUTP_SET, 1 << (SSD1289_CS_PIN - LPC_GPIO_GPO_00(0)) | 1 << (SSD1289_DC_PIN - LPC_GPIO_GPO_00(0)) | 1 << (ADS7846_CS_PIN - LPC_GPIO_GPO_00(0))); bus_space_unmap(bst, bsh, LPC_GPIO_SIZE); } static device_method_t lpc_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, lpc_gpio_probe), DEVMETHOD(device_attach, lpc_gpio_attach), DEVMETHOD(device_detach, lpc_gpio_detach), /* GPIO interface */ DEVMETHOD(gpio_get_bus, lpc_gpio_get_bus), DEVMETHOD(gpio_pin_max, lpc_gpio_pin_max), DEVMETHOD(gpio_pin_getcaps, lpc_gpio_pin_getcaps), DEVMETHOD(gpio_pin_getflags, lpc_gpio_pin_getflags), DEVMETHOD(gpio_pin_setflags, lpc_gpio_pin_setflags), DEVMETHOD(gpio_pin_getname, lpc_gpio_pin_getname), DEVMETHOD(gpio_pin_set, lpc_gpio_pin_set), DEVMETHOD(gpio_pin_get, lpc_gpio_pin_get), DEVMETHOD(gpio_pin_toggle, lpc_gpio_pin_toggle), { 0, 0 } }; static devclass_t lpc_gpio_devclass; static driver_t lpc_gpio_driver = { "lpcgpio", lpc_gpio_methods, sizeof(struct lpc_gpio_softc), }; extern devclass_t gpiobus_devclass, gpioc_devclass; extern driver_t gpiobus_driver, gpioc_driver; DRIVER_MODULE(lpcgpio, simplebus, lpc_gpio_driver, lpc_gpio_devclass, 0, 0); DRIVER_MODULE(gpiobus, lpcgpio, gpiobus_driver, gpiobus_devclass, 0, 0); DRIVER_MODULE(gpioc, lpcgpio, gpioc_driver, gpioc_devclass, 0, 0); MODULE_VERSION(lpcgpio, 1); Index: head/sys/arm/nvidia/tegra_efuse.c =================================================================== --- head/sys/arm/nvidia/tegra_efuse.c (revision 310020) +++ head/sys/arm/nvidia/tegra_efuse.c (revision 310021) @@ -1,366 +1,366 @@ /*- * Copyright (c) 2015 Michal Meloun * 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_sc)->fuse_begin + (_r)) static struct ofw_compat_data compat_data[] = { {"nvidia,tegra124-efuse", 1}, {NULL, 0} }; struct tegra_efuse_softc { device_t dev; struct resource *mem_res; int fuse_begin; clk_t clk; hwreset_t reset; }; struct tegra_efuse_softc *dev_sc; struct tegra_sku_info tegra_sku_info; static char *tegra_rev_name[] = { [TEGRA_REVISION_UNKNOWN] = "unknown", [TEGRA_REVISION_A01] = "A01", [TEGRA_REVISION_A02] = "A02", [TEGRA_REVISION_A03] = "A03", [TEGRA_REVISION_A03p] = "A03 prime", [TEGRA_REVISION_A04] = "A04", }; /* Tegra30 and later */ #define FUSE_VENDOR_CODE 0x100 #define FUSE_FAB_CODE 0x104 #define FUSE_LOT_CODE_0 0x108 #define FUSE_LOT_CODE_1 0x10c #define FUSE_WAFER_ID 0x110 #define FUSE_X_COORDINATE 0x114 #define FUSE_Y_COORDINATE 0x118 /* ---------------------- Tegra 124 specific code & data --------------- */ #define TEGRA124_FUSE_BEGIN 0x100 #define TEGRA124_CPU_PROCESS_CORNERS 2 #define TEGRA124_GPU_PROCESS_CORNERS 2 #define TEGRA124_SOC_PROCESS_CORNERS 2 #define TEGRA124_FUSE_SKU_INFO 0x10 #define TEGRA124_FUSE_CPU_SPEEDO_0 0x14 #define TEGRA124_FUSE_CPU_IDDQ 0x18 #define TEGRA124_FUSE_FT_REV 0x28 #define TEGRA124_FUSE_CPU_SPEEDO_1 0x2c #define TEGRA124_FUSE_CPU_SPEEDO_2 0x30 #define TEGRA124_FUSE_SOC_SPEEDO_0 0x34 #define TEGRA124_FUSE_SOC_SPEEDO_1 0x38 #define TEGRA124_FUSE_SOC_SPEEDO_2 0x3c #define TEGRA124_FUSE_SOC_IDDQ 0x40 #define TEGRA124_FUSE_GPU_IDDQ 0x128 enum { TEGRA124_THRESHOLD_INDEX_0, TEGRA124_THRESHOLD_INDEX_1, TEGRA124_THRESHOLD_INDEX_COUNT, }; static uint32_t tegra124_cpu_process_speedos[][TEGRA124_CPU_PROCESS_CORNERS] = { {2190, UINT_MAX}, {0, UINT_MAX}, }; static uint32_t tegra124_gpu_process_speedos[][TEGRA124_GPU_PROCESS_CORNERS] = { {1965, UINT_MAX}, {0, UINT_MAX}, }; static uint32_t tegra124_soc_process_speedos[][TEGRA124_SOC_PROCESS_CORNERS] = { {2101, UINT_MAX}, {0, UINT_MAX}, }; static void tegra124_rev_sku_to_speedo_ids(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku, int *threshold) { /* Assign to default */ sku->cpu_speedo_id = 0; sku->soc_speedo_id = 0; sku->gpu_speedo_id = 0; *threshold = TEGRA124_THRESHOLD_INDEX_0; switch (sku->sku_id) { case 0x00: /* Eng sku */ case 0x0F: case 0x23: /* Using the default */ break; case 0x83: sku->cpu_speedo_id = 2; break; case 0x1F: case 0x87: case 0x27: sku->cpu_speedo_id = 2; sku->soc_speedo_id = 0; sku->gpu_speedo_id = 1; *threshold = TEGRA124_THRESHOLD_INDEX_0; break; case 0x81: case 0x21: case 0x07: sku->cpu_speedo_id = 1; sku->soc_speedo_id = 1; sku->gpu_speedo_id = 1; *threshold = TEGRA124_THRESHOLD_INDEX_1; break; case 0x49: case 0x4A: case 0x48: sku->cpu_speedo_id = 4; sku->soc_speedo_id = 2; sku->gpu_speedo_id = 3; *threshold = TEGRA124_THRESHOLD_INDEX_1; break; default: device_printf(sc->dev, " Unknown SKU ID %d\n", sku->sku_id); break; } } static void tegra124_init_speedo(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku) { int i, threshold; sku->sku_id = RD4(sc, TEGRA124_FUSE_SKU_INFO); sku->soc_iddq_value = RD4(sc, TEGRA124_FUSE_SOC_IDDQ); sku->cpu_iddq_value = RD4(sc, TEGRA124_FUSE_CPU_IDDQ); sku->gpu_iddq_value = RD4(sc, TEGRA124_FUSE_GPU_IDDQ); sku->soc_speedo_value = RD4(sc, TEGRA124_FUSE_SOC_SPEEDO_0); sku->cpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_0); sku->gpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_2); if (sku->cpu_speedo_value == 0) { device_printf(sc->dev, "CPU Speedo value is not fused.\n"); return; } tegra124_rev_sku_to_speedo_ids(sc, sku, &threshold); for (i = 0; i < TEGRA124_SOC_PROCESS_CORNERS; i++) { if (sku->soc_speedo_value < tegra124_soc_process_speedos[threshold][i]) break; } sku->soc_process_id = i; for (i = 0; i < TEGRA124_CPU_PROCESS_CORNERS; i++) { if (sku->cpu_speedo_value < tegra124_cpu_process_speedos[threshold][i]) break; } sku->cpu_process_id = i; for (i = 0; i < TEGRA124_GPU_PROCESS_CORNERS; i++) { if (sku->gpu_speedo_value < tegra124_gpu_process_speedos[threshold][i]) break; } sku->gpu_process_id = i; } /* ----------------- End of Tegra 124 specific code & data --------------- */ uint32_t tegra_fuse_read_4(int addr) { if (dev_sc == NULL) panic("tegra_fuse_read_4 called too early"); return (RD4(dev_sc, addr)); } static void -tegra_efuse_dump_sku() +tegra_efuse_dump_sku(void) { printf(" TEGRA SKU Info:\n"); printf(" chip_id: %u\n", tegra_sku_info.chip_id); printf(" sku_id: %u\n", tegra_sku_info.sku_id); printf(" cpu_process_id: %u\n", tegra_sku_info.cpu_process_id); printf(" cpu_speedo_id: %u\n", tegra_sku_info.cpu_speedo_id); printf(" cpu_speedo_value: %u\n", tegra_sku_info.cpu_speedo_value); printf(" cpu_iddq_value: %u\n", tegra_sku_info.cpu_iddq_value); printf(" soc_process_id: %u\n", tegra_sku_info.soc_process_id); printf(" soc_speedo_id: %u\n", tegra_sku_info.soc_speedo_id); printf(" soc_speedo_value: %u\n", tegra_sku_info.soc_speedo_value); printf(" soc_iddq_value: %u\n", tegra_sku_info.soc_iddq_value); printf(" gpu_process_id: %u\n", tegra_sku_info.gpu_process_id); printf(" gpu_speedo_id: %u\n", tegra_sku_info.gpu_speedo_id); printf(" gpu_speedo_value: %u\n", tegra_sku_info.gpu_speedo_value); printf(" gpu_iddq_value: %u\n", tegra_sku_info.gpu_iddq_value); printf(" revision: %s\n", tegra_rev_name[tegra_sku_info.revision]); } static int tegra_efuse_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); return (BUS_PROBE_DEFAULT); } static int tegra_efuse_attach(device_t dev) { int rv, rid; phandle_t node; struct tegra_efuse_softc *sc; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); /* Get the memory resource for the register mapping. */ 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 map registers.\n"); rv = ENXIO; goto fail; } /* OFW resources. */ rv = clk_get_by_ofw_name(dev, 0, "fuse", &sc->clk); if (rv != 0) { device_printf(dev, "Cannot get fuse clock: %d\n", rv); goto fail; } rv = clk_enable(sc->clk); if (rv != 0) { device_printf(dev, "Cannot enable clock: %d\n", rv); goto fail; } rv = hwreset_get_by_ofw_name(sc->dev, 0, "fuse", &sc->reset); if (rv != 0) { device_printf(dev, "Cannot get fuse reset\n"); goto fail; } rv = hwreset_deassert(sc->reset); if (rv != 0) { device_printf(sc->dev, "Cannot clear reset\n"); goto fail; } /* Tegra124 specific init. */ sc->fuse_begin = TEGRA124_FUSE_BEGIN; tegra124_init_speedo(sc, &tegra_sku_info); dev_sc = sc; if (bootverbose) tegra_efuse_dump_sku(); return (bus_generic_attach(dev)); fail: dev_sc = NULL; if (sc->clk != NULL) clk_release(sc->clk); if (sc->reset != NULL) hwreset_release(sc->reset); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (rv); } static int tegra_efuse_detach(device_t dev) { struct tegra_efuse_softc *sc; sc = device_get_softc(dev); dev_sc = NULL; if (sc->clk != NULL) clk_release(sc->clk); if (sc->reset != NULL) hwreset_release(sc->reset); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (bus_generic_detach(dev)); } static device_method_t tegra_efuse_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tegra_efuse_probe), DEVMETHOD(device_attach, tegra_efuse_attach), DEVMETHOD(device_detach, tegra_efuse_detach), DEVMETHOD_END }; static devclass_t tegra_efuse_devclass; static DEFINE_CLASS_0(efuse, tegra_efuse_driver, tegra_efuse_methods, sizeof(struct tegra_efuse_softc)); EARLY_DRIVER_MODULE(tegra_efuse, simplebus, tegra_efuse_driver, tegra_efuse_devclass, NULL, NULL, BUS_PASS_TIMER); Index: head/sys/arm/rockchip/rk30xx_wdog.c =================================================================== --- head/sys/arm/rockchip/rk30xx_wdog.c (revision 310020) +++ head/sys/arm/rockchip/rk30xx_wdog.c (revision 310021) @@ -1,199 +1,199 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef RK30_WDT_BASE #define RK30_WDT_BASE 0x2004c000 #define RK30_WDT_PSIZE 0x100 #endif #define RK30_WDT_READ(_sc, _r) bus_read_4((_sc)->res, (_r)) #define RK30_WDT_WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v)) #define WDOG_CTRL 0x00 #define WDOG_CTRL_EN (1 << 0) #define WDOG_CTRL_RSP_MODE (1 << 1) #define WDOG_CTRL_RST_PULSE (4 << 2) #define WDOG_CTRL_RST 0xa #define WDOG_TORR 0x04 #define WDOG_TORR_INTVL_SHIFT 0 #define WDOG_CCVR 0x08 #define WDOG_CRR 0x0c #define WDOG_CRR_PWD 0x76 #define WDOG_STAT 0x10 #define WDOG_EOI 0x14 static struct rk30_wd_softc *rk30_wd_sc = NULL; struct rk30_wd_softc { device_t dev; struct resource *res; struct mtx mtx; int freq; }; static void rk30_wd_watchdog_fn(void *private, u_int cmd, int *error); static int rk30_wd_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "rockchip,rk30xx-wdt")) { device_set_desc(dev, "Rockchip RK30XX Watchdog"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int rk30_wd_attach(device_t dev) { struct rk30_wd_softc *sc; int rid; phandle_t node; pcell_t cell; if (rk30_wd_sc != NULL) return (ENXIO); sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(sc->dev); if (OF_getencprop(node, "clock-frequency", &cell, sizeof(cell)) > 0) sc->freq = cell / 1000000; else return (ENXIO); rid = 0; sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } rk30_wd_sc = sc; mtx_init(&sc->mtx, "RK30XX Watchdog", "rk30_wd", MTX_DEF); EVENTHANDLER_REGISTER(watchdog_list, rk30_wd_watchdog_fn, sc, 0); return (0); } static void rk30_wd_watchdog_fn(void *private, u_int cmd, int *error) { struct rk30_wd_softc *sc; uint64_t ms, m, max; int i; sc = private; mtx_lock(&sc->mtx); cmd &= WD_INTERVAL; if (cmd > 0) { ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000; m = 0xffff / sc->freq; max = 0x7fffffff / sc->freq + 1; i = 0; while (m < max && m < ms) { m <<= 1; i++; } if (m < max) { RK30_WDT_WRITE(sc, WDOG_TORR, i << WDOG_TORR_INTVL_SHIFT); RK30_WDT_WRITE(sc, WDOG_CTRL, WDOG_CTRL_EN | WDOG_CTRL_RSP_MODE | WDOG_CTRL_RST_PULSE); RK30_WDT_WRITE(sc, WDOG_CRR, WDOG_CRR_PWD); *error = 0; } else { device_printf(sc->dev, "Can not be disabled\n"); mtx_unlock(&sc->mtx); RK30_WDT_WRITE(sc, WDOG_CTRL, WDOG_CTRL_RST); return; } } else RK30_WDT_WRITE(sc, WDOG_CTRL, WDOG_CTRL_RST); mtx_unlock(&sc->mtx); } void -rk30_wd_watchdog_reset() +rk30_wd_watchdog_reset(void) { bus_space_handle_t bsh; bus_space_map(fdtbus_bs_tag, RK30_WDT_BASE, RK30_WDT_PSIZE, 0, &bsh); bus_space_write_4(fdtbus_bs_tag, bsh, WDOG_TORR, 0); bus_space_write_4(fdtbus_bs_tag, bsh, WDOG_CTRL, WDOG_CTRL_EN | WDOG_CTRL_RSP_MODE | WDOG_CTRL_RST_PULSE); while (1); } static device_method_t rk30_wd_methods[] = { DEVMETHOD(device_probe, rk30_wd_probe), DEVMETHOD(device_attach, rk30_wd_attach), DEVMETHOD_END }; static driver_t rk30_wd_driver = { "rk30_wd", rk30_wd_methods, sizeof(struct rk30_wd_softc), }; static devclass_t rk30_wd_devclass; DRIVER_MODULE(rk30_wd, simplebus, rk30_wd_driver, rk30_wd_devclass, 0, 0); Index: head/sys/arm/ti/am335x/am335x_dmtpps.c =================================================================== --- head/sys/arm/ti/am335x/am335x_dmtpps.c (revision 310020) +++ head/sys/arm/ti/am335x/am335x_dmtpps.c (revision 310021) @@ -1,549 +1,549 @@ /*- * Copyright (c) 2015 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. */ /* * AM335x PPS driver using DMTimer capture. * * Note that this PPS driver does not use an interrupt. Instead it uses the * hardware's ability to latch the timer's count register in response to a * signal on an IO pin. Each of timers 4-7 have an associated pin, and this * code allows any one of those to be used. * * The timecounter routines in kern_tc.c call the pps poll routine periodically * to see if a new counter value has been latched. When a new value has been * latched, the only processing done in the poll routine is to capture the * current set of timecounter timehands (done with pps_capture()) and the * latched value from the timer. The remaining work (done by pps_event() while * holding a mutex) is scheduled to be done later in a non-interrupt context. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "am335x_dmtreg.h" #define PPS_CDEV_NAME "dmtpps" struct dmtpps_softc { device_t dev; int mem_rid; struct resource * mem_res; int tmr_num; /* N from hwmod str "timerN" */ char tmr_name[12]; /* "DMTimerN" */ uint32_t tclr; /* Cached TCLR register. */ struct timecounter tc; int pps_curmode; /* Edge mode now set in hw. */ struct task pps_task; /* For pps_event handling. */ struct cdev * pps_cdev; struct pps_state pps_state; struct mtx pps_mtx; }; static int dmtpps_tmr_num; /* Set by probe() */ /* List of compatible strings for FDT tree */ static struct ofw_compat_data compat_data[] = { {"ti,am335x-timer", 1}, {"ti,am335x-timer-1ms", 1}, {NULL, 0}, }; /* * A table relating pad names to the hardware timer number they can be mux'd to. */ struct padinfo { char * ballname; int tmr_num; }; static struct padinfo dmtpps_padinfo[] = { {"GPMC_ADVn_ALE", 4}, {"I2C0_SDA", 4}, {"MII1_TX_EN", 4}, {"XDMA_EVENT_INTR0", 4}, {"GPMC_BEn0_CLE", 5}, {"MDC", 5}, {"MMC0_DAT3", 5}, {"UART1_RTSn", 5}, {"GPMC_WEn", 6}, {"MDIO", 6}, {"MMC0_DAT2", 6}, {"UART1_CTSn", 6}, {"GPMC_OEn_REn", 7}, {"I2C0_SCL", 7}, {"UART0_CTSn", 7}, {"XDMA_EVENT_INTR1", 7}, {NULL, 0} }; /* * This is either brilliantly user-friendly, or utterly lame... * * The am335x chip is used on the popular Beaglebone boards. Those boards have * pins for all four capture-capable timers available on the P8 header. Allow * users to configure the input pin by giving the name of the header pin. */ struct nicknames { const char * nick; const char * name; }; static struct nicknames dmtpps_pin_nicks[] = { {"P8-7", "GPMC_ADVn_ALE"}, {"P8-9", "GPMC_BEn0_CLE"}, {"P8-10", "GPMC_WEn"}, {"P8-8", "GPMC_OEn_REn",}, {NULL, NULL} }; #define DMTIMER_READ4(sc, reg) bus_read_4((sc)->mem_res, (reg)) #define DMTIMER_WRITE4(sc, reg, val) bus_write_4((sc)->mem_res, (reg), (val)) /* * Translate a short friendly case-insensitive name to its canonical name. */ static const char * dmtpps_translate_nickname(const char *nick) { struct nicknames *nn; for (nn = dmtpps_pin_nicks; nn->nick != NULL; nn++) if (strcasecmp(nick, nn->nick) == 0) return nn->name; return (nick); } /* * See if our tunable is set to the name of the input pin. If not, that's NOT * an error, return 0. If so, try to configure that pin as a timer capture * input pin, and if that works, then we have our timer unit number and if it * fails that IS an error, return -1. */ static int -dmtpps_find_tmr_num_by_tunable() +dmtpps_find_tmr_num_by_tunable(void) { struct padinfo *pi; char iname[20]; char muxmode[12]; const char * ballname; int err; if (!TUNABLE_STR_FETCH("hw.am335x_dmtpps.input", iname, sizeof(iname))) return (0); ballname = dmtpps_translate_nickname(iname); for (pi = dmtpps_padinfo; pi->ballname != NULL; pi++) { if (strcmp(ballname, pi->ballname) != 0) continue; snprintf(muxmode, sizeof(muxmode), "timer%d", pi->tmr_num); err = ti_pinmux_padconf_set(pi->ballname, muxmode, PADCONF_INPUT); if (err != 0) { printf("am335x_dmtpps: unable to configure capture pin " "for %s to input mode\n", muxmode); return (-1); } else if (bootverbose) { printf("am335x_dmtpps: configured pin %s as input " "for %s\n", iname, muxmode); } return (pi->tmr_num); } /* Invalid name in the tunable, that's an error. */ printf("am335x_dmtpps: unknown pin name '%s'\n", iname); return (-1); } /* * Ask the pinmux driver whether any pin has been configured as a TIMER4..TIMER7 * input pin. If so, return the timer number, if not return 0. */ static int -dmtpps_find_tmr_num_by_padconf() +dmtpps_find_tmr_num_by_padconf(void) { int err; unsigned int padstate; const char * padmux; struct padinfo *pi; char muxmode[12]; for (pi = dmtpps_padinfo; pi->ballname != NULL; pi++) { err = ti_pinmux_padconf_get(pi->ballname, &padmux, &padstate); snprintf(muxmode, sizeof(muxmode), "timer%d", pi->tmr_num); if (err == 0 && (padstate & RXACTIVE) != 0 && strcmp(muxmode, padmux) == 0) return (pi->tmr_num); } /* Nothing found, not an error. */ return (0); } /* * Figure out which hardware timer number to use based on input pin * configuration. This is done just once, the first time probe() runs. */ static int -dmtpps_find_tmr_num() +dmtpps_find_tmr_num(void) { int tmr_num; if ((tmr_num = dmtpps_find_tmr_num_by_tunable()) == 0) tmr_num = dmtpps_find_tmr_num_by_padconf(); if (tmr_num <= 0) { printf("am335x_dmtpps: PPS driver not enabled: unable to find " "or configure a capture input pin\n"); tmr_num = -1; /* Must return non-zero to prevent re-probing. */ } return (tmr_num); } static void dmtpps_set_hw_capture(struct dmtpps_softc *sc, bool force_off) { int newmode; if (force_off) newmode = 0; else newmode = sc->pps_state.ppsparam.mode & PPS_CAPTUREASSERT; if (newmode == sc->pps_curmode) return; sc->pps_curmode = newmode; if (newmode == PPS_CAPTUREASSERT) sc->tclr |= DMT_TCLR_CAPTRAN_LOHI; else sc->tclr &= ~DMT_TCLR_CAPTRAN_MASK; DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); } static unsigned dmtpps_get_timecount(struct timecounter *tc) { struct dmtpps_softc *sc; sc = tc->tc_priv; return (DMTIMER_READ4(sc, DMT_TCRR)); } static void dmtpps_poll(struct timecounter *tc) { struct dmtpps_softc *sc; sc = tc->tc_priv; /* * If a new value has been latched we've got a PPS event. Capture the * timecounter data, then override the capcount field (pps_capture() * populates it from the current DMT_TCRR register) with the latched * value from the TCAR1 register. * * There is no locking here, by design. pps_capture() writes into an * area of struct pps_state which is read only by pps_event(). The * synchronization of access to that area is temporal rather than * interlock based... we write in this routine and trigger the task that * will read the data, so no simultaneous access can occur. * * Note that we don't have the TCAR interrupt enabled, but the hardware * still provides the status bits in the "RAW" status register even when * they're masked from generating an irq. However, when clearing the * TCAR status to re-arm the capture for the next second, we have to * write to the IRQ status register, not the RAW register. Quirky. */ if (DMTIMER_READ4(sc, DMT_IRQSTATUS_RAW) & DMT_IRQ_TCAR) { pps_capture(&sc->pps_state); sc->pps_state.capcount = DMTIMER_READ4(sc, DMT_TCAR1); DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_TCAR); taskqueue_enqueue(taskqueue_fast, &sc->pps_task); } } static void dmtpps_event(void *arg, int pending) { struct dmtpps_softc *sc; sc = arg; /* This is the task function that gets enqueued by poll_pps. Once the * time has been captured by the timecounter polling code which runs in * primary interrupt context, the remaining (more expensive) work to * process the event is done later in a threaded context. * * Here there is an interlock that protects the event data in struct * pps_state. That data can be accessed at any time from userland via * ioctl() calls so we must ensure that there is no read access to * partially updated data while pps_event() does its work. */ mtx_lock(&sc->pps_mtx); pps_event(&sc->pps_state, PPS_CAPTUREASSERT); mtx_unlock(&sc->pps_mtx); } static int dmtpps_open(struct cdev *dev, int flags, int fmt, struct thread *td) { struct dmtpps_softc *sc; sc = dev->si_drv1; /* * Begin polling for pps and enable capture in the hardware whenever the * device is open. Doing this stuff again is harmless if this isn't the * first open. */ sc->tc.tc_poll_pps = dmtpps_poll; dmtpps_set_hw_capture(sc, false); return 0; } static int dmtpps_close(struct cdev *dev, int flags, int fmt, struct thread *td) { struct dmtpps_softc *sc; sc = dev->si_drv1; /* * Stop polling and disable capture on last close. Use the force-off * flag to override the configured mode and turn off the hardware. */ sc->tc.tc_poll_pps = NULL; dmtpps_set_hw_capture(sc, true); return 0; } static int dmtpps_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) { struct dmtpps_softc *sc; int err; sc = dev->si_drv1; /* Let the kernel do the heavy lifting for ioctl. */ mtx_lock(&sc->pps_mtx); err = pps_ioctl(cmd, data, &sc->pps_state); mtx_unlock(&sc->pps_mtx); if (err != 0) return (err); /* * The capture mode could have changed, set the hardware to whatever * mode is now current. Effectively a no-op if nothing changed. */ dmtpps_set_hw_capture(sc, false); return (err); } static struct cdevsw dmtpps_cdevsw = { .d_version = D_VERSION, .d_open = dmtpps_open, .d_close = dmtpps_close, .d_ioctl = dmtpps_ioctl, .d_name = PPS_CDEV_NAME, }; static int dmtpps_probe(device_t dev) { char strbuf[64]; int tmr_num; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); /* * If we haven't chosen which hardware timer to use yet, go do that now. * We need to know that to decide whether to return success for this * hardware timer instance or not. */ if (dmtpps_tmr_num == 0) dmtpps_tmr_num = dmtpps_find_tmr_num(); /* * Figure out which hardware timer is being probed and see if it matches * the configured timer number determined earlier. */ tmr_num = ti_hwmods_get_unit(dev, "timer"); if (dmtpps_tmr_num != tmr_num) return (ENXIO); snprintf(strbuf, sizeof(strbuf), "AM335x PPS-Capture DMTimer%d", tmr_num); device_set_desc_copy(dev, strbuf); return(BUS_PROBE_DEFAULT); } static int dmtpps_attach(device_t dev) { struct dmtpps_softc *sc; clk_ident_t timer_id; int err, sysclk_freq; sc = device_get_softc(dev); sc->dev = dev; /* Get the base clock frequency. */ err = ti_prcm_clk_get_source_freq(SYS_CLK, &sysclk_freq); /* Enable clocks and power on the device. */ if ((timer_id = ti_hwmods_get_clock(dev)) == INVALID_CLK_IDENT) return (ENXIO); if ((err = ti_prcm_clk_set_source(timer_id, SYSCLK_CLK)) != 0) return (err); if ((err = ti_prcm_clk_enable(timer_id)) != 0) return (err); /* Request the memory resources. */ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem_res == NULL) { return (ENXIO); } /* Figure out which hardware timer this is and set the name string. */ sc->tmr_num = ti_hwmods_get_unit(dev, "timer"); snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num); /* Set up timecounter hardware, start it. */ DMTIMER_WRITE4(sc, DMT_TSICR, DMT_TSICR_RESET); while (DMTIMER_READ4(sc, DMT_TIOCP_CFG) & DMT_TIOCP_RESET) continue; sc->tclr |= DMT_TCLR_START | DMT_TCLR_AUTOLOAD; DMTIMER_WRITE4(sc, DMT_TLDR, 0); DMTIMER_WRITE4(sc, DMT_TCRR, 0); DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); /* Register the timecounter. */ sc->tc.tc_name = sc->tmr_name; sc->tc.tc_get_timecount = dmtpps_get_timecount; sc->tc.tc_counter_mask = ~0u; sc->tc.tc_frequency = sysclk_freq; sc->tc.tc_quality = 1000; sc->tc.tc_priv = sc; tc_init(&sc->tc); /* * Indicate our PPS capabilities. Have the kernel init its part of the * pps_state struct and add its capabilities. * * While the hardware has a mode to capture each edge, it's not clear we * can use it that way, because there's only a single interrupt/status * bit to say something was captured, but not which edge it was. For * now, just say we can only capture assert events (the positive-going * edge of the pulse). */ mtx_init(&sc->pps_mtx, "dmtpps", NULL, MTX_DEF); sc->pps_state.ppscap = PPS_CAPTUREASSERT; sc->pps_state.driver_abi = PPS_ABI_VERSION; sc->pps_state.driver_mtx = &sc->pps_mtx; pps_init_abi(&sc->pps_state); /* * Init the task that does deferred pps_event() processing after * the polling routine has captured a pps pulse time. */ TASK_INIT(&sc->pps_task, 0, dmtpps_event, sc); /* Create the PPS cdev. */ sc->pps_cdev = make_dev(&dmtpps_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, PPS_CDEV_NAME); sc->pps_cdev->si_drv1 = sc; if (bootverbose) device_printf(sc->dev, "Using %s for PPS device /dev/%s\n", sc->tmr_name, PPS_CDEV_NAME); return (0); } static int dmtpps_detach(device_t dev) { /* * There is no way to remove a timecounter once it has been registered, * even if it's not in use, so we can never detach. If we were * dynamically loaded as a module this will prevent unloading. */ return (EBUSY); } static device_method_t dmtpps_methods[] = { DEVMETHOD(device_probe, dmtpps_probe), DEVMETHOD(device_attach, dmtpps_attach), DEVMETHOD(device_detach, dmtpps_detach), { 0, 0 } }; static driver_t dmtpps_driver = { "am335x_dmtpps", dmtpps_methods, sizeof(struct dmtpps_softc), }; static devclass_t dmtpps_devclass; DRIVER_MODULE(am335x_dmtpps, simplebus, dmtpps_driver, dmtpps_devclass, 0, 0); MODULE_DEPEND(am335x_dmtpps, am335x_prcm, 1, 1, 1); Index: head/sys/arm/versatile/versatile_machdep.c =================================================================== --- head/sys/arm/versatile/versatile_machdep.c (revision 310020) +++ head/sys/arm/versatile/versatile_machdep.c (revision 310021) @@ -1,108 +1,108 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko. * All rights reserved. * * This code is derived from software written for Brini by Mark Brinicombe * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Brini. * 4. The name of the company nor the name of the author may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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 "opt_ddb.h" #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include /* Start of address space used for bootstrap map */ #define DEVMAP_BOOTSTRAP_MAP_START 0xE0000000 vm_offset_t platform_lastaddr(void) { return (DEVMAP_BOOTSTRAP_MAP_START); } void platform_probe_and_attach(void) { } void platform_gpio_init(void) { } void platform_late_init(void) { } #define FDT_DEVMAP_MAX (2) /* FIXME */ static struct devmap_entry fdt_devmap[FDT_DEVMAP_MAX] = { { 0, 0, 0, }, { 0, 0, 0, } }; /* * Construct devmap table with DT-derived config data. */ int platform_devmap_init(void) { int i = 0; fdt_devmap[i].pd_va = 0xf0100000; fdt_devmap[i].pd_pa = 0x10100000; fdt_devmap[i].pd_size = 0x01000000; /* 1 MB */ devmap_register_table(&fdt_devmap[0]); return (0); } void -cpu_reset() +cpu_reset(void) { printf("cpu_reset\n"); while (1); } Index: head/sys/arm/xilinx/zy7_machdep.c =================================================================== --- head/sys/arm/xilinx/zy7_machdep.c (revision 310020) +++ head/sys/arm/xilinx/zy7_machdep.c (revision 310021) @@ -1,102 +1,102 @@ /*- * Copyright (c) 2013 Thomas Skibo * 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$ */ /* * Machine dependent code for Xilinx Zynq-7000 Soc. * * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. * (v1.4) November 16, 2012. Xilinx doc UG585. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include void (*zynq7_cpu_reset)(void); vm_offset_t platform_lastaddr(void) { return (devmap_lastaddr()); } void platform_probe_and_attach(void) { } void platform_gpio_init(void) { } void platform_late_init(void) { } /* * Set up static device mappings. Not strictly necessary -- simplebus will * dynamically establish mappings as needed -- but doing it this way gets us * nice efficient 1MB section mappings. */ int platform_devmap_init(void) { devmap_add_entry(ZYNQ7_PSIO_HWBASE, ZYNQ7_PSIO_SIZE); devmap_add_entry(ZYNQ7_PSCTL_HWBASE, ZYNQ7_PSCTL_SIZE); return (0); } void -cpu_reset() +cpu_reset(void) { if (zynq7_cpu_reset != NULL) (*zynq7_cpu_reset)(); printf("cpu_reset: no platform cpu_reset. hanging.\n"); for (;;) ; } Index: head/sys/arm/xilinx/zy7_slcr.c =================================================================== --- head/sys/arm/xilinx/zy7_slcr.c (revision 310020) +++ head/sys/arm/xilinx/zy7_slcr.c (revision 310021) @@ -1,711 +1,711 @@ /*- * Copyright (c) 2013 Thomas Skibo * 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$ */ /* * Zynq-700 SLCR driver. Provides hooks for cpu_reset and PL control stuff. * In the future, maybe MIO control, clock control, etc. could go here. * * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. * (v1.4) November 16, 2012. Xilinx doc UG585. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct zy7_slcr_softc { device_t dev; struct mtx sc_mtx; struct resource *mem_res; }; static struct zy7_slcr_softc *zy7_slcr_softc_p; extern void (*zynq7_cpu_reset); #define ZSLCR_LOCK(sc) mtx_lock(&(sc)->sc_mtx) #define ZSLCR_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) #define ZSLCR_LOCK_INIT(sc) \ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ "zy7_slcr", MTX_DEF) #define ZSLCR_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define RD4(sc, off) (bus_read_4((sc)->mem_res, (off))) #define WR4(sc, off, val) (bus_write_4((sc)->mem_res, (off), (val))) #define ZYNQ_DEFAULT_PS_CLK_FREQUENCY 33333333 /* 33.3 Mhz */ SYSCTL_NODE(_hw, OID_AUTO, zynq, CTLFLAG_RD, 0, "Xilinx Zynq-7000"); static char zynq_bootmode[64]; SYSCTL_STRING(_hw_zynq, OID_AUTO, bootmode, CTLFLAG_RD, zynq_bootmode, 0, "Zynq boot mode"); static char zynq_pssid[100]; SYSCTL_STRING(_hw_zynq, OID_AUTO, pssid, CTLFLAG_RD, zynq_pssid, 0, "Zynq PSS IDCODE"); static uint32_t zynq_reboot_status; SYSCTL_INT(_hw_zynq, OID_AUTO, reboot_status, CTLFLAG_RD, &zynq_reboot_status, 0, "Zynq REBOOT_STATUS register"); static int ps_clk_frequency; SYSCTL_INT(_hw_zynq, OID_AUTO, ps_clk_frequency, CTLFLAG_RD, &ps_clk_frequency, 0, "Zynq PS_CLK Frequency"); static int io_pll_frequency; SYSCTL_INT(_hw_zynq, OID_AUTO, io_pll_frequency, CTLFLAG_RD, &io_pll_frequency, 0, "Zynq IO PLL Frequency"); static int arm_pll_frequency; SYSCTL_INT(_hw_zynq, OID_AUTO, arm_pll_frequency, CTLFLAG_RD, &arm_pll_frequency, 0, "Zynq ARM PLL Frequency"); static int ddr_pll_frequency; SYSCTL_INT(_hw_zynq, OID_AUTO, ddr_pll_frequency, CTLFLAG_RD, &ddr_pll_frequency, 0, "Zynq DDR PLL Frequency"); static void zy7_slcr_unlock(struct zy7_slcr_softc *sc) { /* Unlock SLCR with magic number. */ WR4(sc, ZY7_SLCR_UNLOCK, ZY7_SLCR_UNLOCK_MAGIC); } static void zy7_slcr_lock(struct zy7_slcr_softc *sc) { /* Lock SLCR with magic number. */ WR4(sc, ZY7_SLCR_LOCK, ZY7_SLCR_LOCK_MAGIC); } static void zy7_slcr_cpu_reset(void) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; /* Unlock SLCR registers. */ zy7_slcr_unlock(sc); /* This has something to do with a work-around so the fsbl will load * the bitstream after soft-reboot. It's very important. */ WR4(sc, ZY7_SLCR_REBOOT_STAT, RD4(sc, ZY7_SLCR_REBOOT_STAT) & 0xf0ffffff); /* Soft reset */ WR4(sc, ZY7_SLCR_PSS_RST_CTRL, ZY7_SLCR_PSS_RST_CTRL_SOFT_RESET); for (;;) ; } /* Assert PL resets and disable level shifters in preparation of programming * the PL (FPGA) section. Called from zy7_devcfg.c. */ void zy7_slcr_preload_pl(void) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; if (!sc) return; ZSLCR_LOCK(sc); /* Unlock SLCR registers. */ zy7_slcr_unlock(sc); /* Assert top level output resets. */ WR4(sc, ZY7_SLCR_FPGA_RST_CTRL, ZY7_SLCR_FPGA_RST_CTRL_RST_ALL); /* Disable all level shifters. */ WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, 0); /* Lock SLCR registers. */ zy7_slcr_lock(sc); ZSLCR_UNLOCK(sc); } /* After PL configuration, enable level shifters and deassert top-level * PL resets. Called from zy7_devcfg.c. Optionally, the level shifters * can be left disabled but that's rare of an FPGA application. That option * is controlled by a sysctl in the devcfg driver. */ void zy7_slcr_postload_pl(int en_level_shifters) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; if (!sc) return; ZSLCR_LOCK(sc); /* Unlock SLCR registers. */ zy7_slcr_unlock(sc); if (en_level_shifters) /* Enable level shifters. */ WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, ZY7_SLCR_LVL_SHFTR_EN_ALL); /* Deassert top level output resets. */ WR4(sc, ZY7_SLCR_FPGA_RST_CTRL, 0); /* Lock SLCR registers. */ zy7_slcr_lock(sc); ZSLCR_UNLOCK(sc); } /* Override cgem_set_refclk() in gigabit ethernet driver * (sys/dev/cadence/if_cgem.c). This function is called to * request a change in the gem's reference clock speed. */ int cgem_set_ref_clk(int unit, int frequency) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; int div0, div1; if (!sc) return (-1); /* Find suitable divisor pairs. Round result to nearest khz * to test for match. */ for (div1 = 1; div1 <= ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX; div1++) { div0 = (io_pll_frequency + div1 * frequency / 2) / div1 / frequency; if (div0 > 0 && div0 <= ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MAX && ((io_pll_frequency / div0 / div1) + 500) / 1000 == (frequency + 500) / 1000) break; } if (div1 > ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX) return (-1); ZSLCR_LOCK(sc); /* Unlock SLCR registers. */ zy7_slcr_unlock(sc); /* Modify GEM reference clock. */ WR4(sc, unit ? ZY7_SLCR_GEM1_CLK_CTRL : ZY7_SLCR_GEM0_CLK_CTRL, (div1 << ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_SHIFT) | (div0 << ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_SHIFT) | ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_IO_PLL | ZY7_SLCR_GEM_CLK_CTRL_CLKACT); /* Lock SLCR registers. */ zy7_slcr_lock(sc); ZSLCR_UNLOCK(sc); return (0); } /* * PL clocks management function */ int zy7_pl_fclk_set_source(int unit, int source) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; uint32_t reg; if (!sc) return (-1); ZSLCR_LOCK(sc); /* Unlock SLCR registers. */ zy7_slcr_unlock(sc); /* Modify FPGAx source. */ reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit)); reg &= ~(ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_MASK); reg |= (source << ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_SHIFT); WR4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit), reg); /* Lock SLCR registers. */ zy7_slcr_lock(sc); ZSLCR_UNLOCK(sc); return (0); } int zy7_pl_fclk_get_source(int unit) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; uint32_t reg; int source; if (!sc) return (-1); ZSLCR_LOCK(sc); /* Modify GEM reference clock. */ reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit)); source = (reg & ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_MASK) >> ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_SHIFT; /* ZY7_PL_FCLK_SRC_IO is actually b0x */ if ((source & 2) == 0) source = ZY7_PL_FCLK_SRC_IO; ZSLCR_UNLOCK(sc); return (source); } int zy7_pl_fclk_set_freq(int unit, int frequency) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; int div0, div1; int base_frequency; uint32_t reg; int source; if (!sc) return (-1); source = zy7_pl_fclk_get_source(unit); switch (source) { case ZY7_PL_FCLK_SRC_IO: base_frequency = io_pll_frequency; break; case ZY7_PL_FCLK_SRC_ARM: base_frequency = arm_pll_frequency; break; case ZY7_PL_FCLK_SRC_DDR: base_frequency = ddr_pll_frequency; break; default: return (-1); } /* Find suitable divisor pairs. Round result to nearest khz * to test for match. */ for (div1 = 1; div1 <= ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX; div1++) { div0 = (base_frequency + div1 * frequency / 2) / div1 / frequency; if (div0 > 0 && div0 <= ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX && ((base_frequency / div0 / div1) + 500) / 1000 == (frequency + 500) / 1000) break; } if (div1 > ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX) return (-1); ZSLCR_LOCK(sc); /* Unlock SLCR registers. */ zy7_slcr_unlock(sc); /* Modify FPGAx reference clock. */ reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit)); reg &= ~(ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_MASK | ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_MASK); reg |= (div1 << ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_SHIFT) | (div0 << ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_SHIFT); WR4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit), reg); /* Lock SLCR registers. */ zy7_slcr_lock(sc); ZSLCR_UNLOCK(sc); return (base_frequency / div0 / div1); } int zy7_pl_fclk_get_freq(int unit) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; int div0, div1; int base_frequency; int frequency; uint32_t reg; int source; if (!sc) return (-1); source = zy7_pl_fclk_get_source(unit); switch (source) { case ZY7_PL_FCLK_SRC_IO: base_frequency = io_pll_frequency; break; case ZY7_PL_FCLK_SRC_ARM: base_frequency = arm_pll_frequency; break; case ZY7_PL_FCLK_SRC_DDR: base_frequency = ddr_pll_frequency; break; default: return (-1); } ZSLCR_LOCK(sc); /* Modify FPGAx reference clock. */ reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit)); div1 = (reg & ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_MASK) >> ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_SHIFT; div0 = (reg & ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_MASK) >> ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_SHIFT; ZSLCR_UNLOCK(sc); if (div0 == 0) div0 = 1; if (div1 == 0) div1 = 1; frequency = (base_frequency / div0 / div1); /* Round to KHz */ frequency = (frequency + 500) / 1000; frequency = frequency * 1000; return (frequency); } int zy7_pl_fclk_enable(int unit) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; if (!sc) return (-1); ZSLCR_LOCK(sc); /* Unlock SLCR registers. */ zy7_slcr_unlock(sc); WR4(sc, ZY7_SLCR_FPGA_THR_CTRL(unit), 0); WR4(sc, ZY7_SLCR_FPGA_THR_CNT(unit), 0); /* Lock SLCR registers. */ zy7_slcr_lock(sc); ZSLCR_UNLOCK(sc); return (0); } int zy7_pl_fclk_disable(int unit) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; if (!sc) return (-1); ZSLCR_LOCK(sc); /* Unlock SLCR registers. */ zy7_slcr_unlock(sc); WR4(sc, ZY7_SLCR_FPGA_THR_CTRL(unit), 0); WR4(sc, ZY7_SLCR_FPGA_THR_CNT(unit), 1); /* Lock SLCR registers. */ zy7_slcr_lock(sc); ZSLCR_UNLOCK(sc); return (0); } int zy7_pl_fclk_enabled(int unit) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; uint32_t reg; if (!sc) return (-1); ZSLCR_LOCK(sc); reg = RD4(sc, ZY7_SLCR_FPGA_THR_CNT(unit)); ZSLCR_UNLOCK(sc); return !(reg & 1); } int -zy7_pl_level_shifters_enabled() +zy7_pl_level_shifters_enabled(void) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; uint32_t reg; if (!sc) return (-1); ZSLCR_LOCK(sc); reg = RD4(sc, ZY7_SLCR_LVL_SHFTR_EN); ZSLCR_UNLOCK(sc); return (reg == ZY7_SLCR_LVL_SHFTR_EN_ALL); } void -zy7_pl_level_shifters_enable() +zy7_pl_level_shifters_enable(void) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; if (!sc) return; ZSLCR_LOCK(sc); zy7_slcr_unlock(sc); WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, ZY7_SLCR_LVL_SHFTR_EN_ALL); zy7_slcr_lock(sc); ZSLCR_UNLOCK(sc); } void -zy7_pl_level_shifters_disable() +zy7_pl_level_shifters_disable(void) { struct zy7_slcr_softc *sc = zy7_slcr_softc_p; if (!sc) return; ZSLCR_LOCK(sc); zy7_slcr_unlock(sc); WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, 0); zy7_slcr_lock(sc); ZSLCR_UNLOCK(sc); } static int zy7_slcr_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "xlnx,zy7_slcr")) return (ENXIO); device_set_desc(dev, "Zynq-7000 slcr block"); return (0); } static int zy7_slcr_attach(device_t dev) { struct zy7_slcr_softc *sc = device_get_softc(dev); int rid; phandle_t node; pcell_t cell; uint32_t bootmode; uint32_t pss_idcode; uint32_t arm_pll_ctrl; uint32_t ddr_pll_ctrl; uint32_t io_pll_ctrl; static char *bootdev_names[] = { "JTAG", "Quad-SPI", "NOR", "(3?)", "NAND", "SD Card", "(6?)", "(7?)" }; /* Allow only one attach. */ if (zy7_slcr_softc_p != NULL) return (ENXIO); sc->dev = dev; ZSLCR_LOCK_INIT(sc); /* Get memory resource. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "could not allocate memory resources.\n"); return (ENOMEM); } /* Hook up cpu_reset. */ zy7_slcr_softc_p = sc; zynq7_cpu_reset = zy7_slcr_cpu_reset; /* Read info and set sysctls. */ bootmode = RD4(sc, ZY7_SLCR_BOOT_MODE); snprintf(zynq_bootmode, sizeof(zynq_bootmode), "0x%x: boot device: %s", bootmode, bootdev_names[bootmode & ZY7_SLCR_BOOT_MODE_BOOTDEV_MASK]); pss_idcode = RD4(sc, ZY7_SLCR_PSS_IDCODE); snprintf(zynq_pssid, sizeof(zynq_pssid), "0x%x: manufacturer: 0x%x device: 0x%x " "family: 0x%x sub-family: 0x%x rev: 0x%x", pss_idcode, (pss_idcode & ZY7_SLCR_PSS_IDCODE_MNFR_ID_MASK) >> ZY7_SLCR_PSS_IDCODE_MNFR_ID_SHIFT, (pss_idcode & ZY7_SLCR_PSS_IDCODE_DEVICE_MASK) >> ZY7_SLCR_PSS_IDCODE_DEVICE_SHIFT, (pss_idcode & ZY7_SLCR_PSS_IDCODE_FAMILY_MASK) >> ZY7_SLCR_PSS_IDCODE_FAMILY_SHIFT, (pss_idcode & ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_MASK) >> ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_SHIFT, (pss_idcode & ZY7_SLCR_PSS_IDCODE_REVISION_MASK) >> ZY7_SLCR_PSS_IDCODE_REVISION_SHIFT); zynq_reboot_status = RD4(sc, ZY7_SLCR_REBOOT_STAT); /* Derive PLL frequencies from PS_CLK. */ node = ofw_bus_get_node(dev); if (OF_getencprop(node, "clock-frequency", &cell, sizeof(cell)) > 0) ps_clk_frequency = cell; else ps_clk_frequency = ZYNQ_DEFAULT_PS_CLK_FREQUENCY; arm_pll_ctrl = RD4(sc, ZY7_SLCR_ARM_PLL_CTRL); ddr_pll_ctrl = RD4(sc, ZY7_SLCR_DDR_PLL_CTRL); io_pll_ctrl = RD4(sc, ZY7_SLCR_IO_PLL_CTRL); /* Determine ARM PLL frequency. */ if (((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 && (arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) || ((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 && (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0)) /* PLL is bypassed. */ arm_pll_frequency = ps_clk_frequency; else arm_pll_frequency = ps_clk_frequency * ((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >> ZY7_SLCR_PLL_CTRL_FDIV_SHIFT); /* Determine DDR PLL frequency. */ if (((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 && (ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) || ((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 && (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0)) /* PLL is bypassed. */ ddr_pll_frequency = ps_clk_frequency; else ddr_pll_frequency = ps_clk_frequency * ((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >> ZY7_SLCR_PLL_CTRL_FDIV_SHIFT); /* Determine IO PLL frequency. */ if (((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 && (io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) || ((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 && (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0)) /* PLL is bypassed. */ io_pll_frequency = ps_clk_frequency; else io_pll_frequency = ps_clk_frequency * ((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >> ZY7_SLCR_PLL_CTRL_FDIV_SHIFT); /* Lock SLCR registers. */ zy7_slcr_lock(sc); return (0); } static int zy7_slcr_detach(device_t dev) { struct zy7_slcr_softc *sc = device_get_softc(dev); bus_generic_detach(dev); /* Release memory resource. */ if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->mem_res), sc->mem_res); zy7_slcr_softc_p = NULL; zynq7_cpu_reset = NULL; ZSLCR_LOCK_DESTROY(sc); return (0); } static device_method_t zy7_slcr_methods[] = { /* device_if */ DEVMETHOD(device_probe, zy7_slcr_probe), DEVMETHOD(device_attach, zy7_slcr_attach), DEVMETHOD(device_detach, zy7_slcr_detach), DEVMETHOD_END }; static driver_t zy7_slcr_driver = { "zy7_slcr", zy7_slcr_methods, sizeof(struct zy7_slcr_softc), }; static devclass_t zy7_slcr_devclass; DRIVER_MODULE(zy7_slcr, simplebus, zy7_slcr_driver, zy7_slcr_devclass, 0, 0); MODULE_VERSION(zy7_slcr, 1); Index: head/sys/arm/xscale/pxa/pxa_gpio.c =================================================================== --- head/sys/arm/xscale/pxa/pxa_gpio.c (revision 310020) +++ head/sys/arm/xscale/pxa/pxa_gpio.c (revision 310021) @@ -1,358 +1,358 @@ /*- * Copyright (c) 2006 Benno Rice. 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 ``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 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct pxa_gpio_softc { struct resource * pg_res[4]; bus_space_tag_t pg_bst; bus_space_handle_t pg_bsh; struct mtx pg_mtx; uint32_t pg_intr[3]; }; static struct resource_spec pxa_gpio_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { -1, 0 } }; static struct pxa_gpio_softc *pxa_gpio_softc = NULL; static int pxa_gpio_probe(device_t); static int pxa_gpio_attach(device_t); static driver_filter_t pxa_gpio_intr0; static driver_filter_t pxa_gpio_intr1; static driver_filter_t pxa_gpio_intrN; static int pxa_gpio_probe(device_t dev) { device_set_desc(dev, "GPIO Controller"); return (0); } static int pxa_gpio_attach(device_t dev) { int error; void *ihl; struct pxa_gpio_softc *sc; sc = (struct pxa_gpio_softc *)device_get_softc(dev); if (pxa_gpio_softc != NULL) return (ENXIO); pxa_gpio_softc = sc; error = bus_alloc_resources(dev, pxa_gpio_spec, sc->pg_res); if (error) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->pg_bst = rman_get_bustag(sc->pg_res[0]); sc->pg_bsh = rman_get_bushandle(sc->pg_res[0]); /* Disable and clear all interrupts. */ bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GRER0, 0); bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GRER1, 0); bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GRER2, 0); bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GFER0, 0); bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GFER1, 0); bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GFER2, 0); bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR0, ~0); bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR1, ~0); bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR2, ~0); mtx_init(&sc->pg_mtx, "GPIO mutex", NULL, MTX_SPIN); if (bus_setup_intr(dev, sc->pg_res[1], INTR_TYPE_MISC|INTR_MPSAFE, pxa_gpio_intr0, NULL, sc, &ihl) != 0) { bus_release_resources(dev, pxa_gpio_spec, sc->pg_res); device_printf(dev, "could not set up intr0\n"); return (ENXIO); } if (bus_setup_intr(dev, sc->pg_res[2], INTR_TYPE_MISC|INTR_MPSAFE, pxa_gpio_intr1, NULL, sc, &ihl) != 0) { bus_release_resources(dev, pxa_gpio_spec, sc->pg_res); device_printf(dev, "could not set up intr1\n"); return (ENXIO); } if (bus_setup_intr(dev, sc->pg_res[3], INTR_TYPE_MISC|INTR_MPSAFE, pxa_gpio_intrN, NULL, sc, &ihl) != 0) { bus_release_resources(dev, pxa_gpio_spec, sc->pg_res); device_printf(dev, "could not set up intrN\n"); return (ENXIO); } return (0); } static int pxa_gpio_intr0(void *arg) { struct pxa_gpio_softc *sc; sc = (struct pxa_gpio_softc *)arg; bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR0, 0x1); sc->pg_intr[0] |= 1; return (FILTER_HANDLED); } static int pxa_gpio_intr1(void *arg) { struct pxa_gpio_softc *sc; sc = (struct pxa_gpio_softc *)arg; bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR0, 0x2); sc->pg_intr[1] |= 2; return (FILTER_HANDLED); } static int pxa_gpio_intrN(void *arg) { uint32_t gedr0, gedr1, gedr2; struct pxa_gpio_softc *sc; sc = (struct pxa_gpio_softc *)arg; gedr0 = bus_space_read_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR0); gedr0 &= 0xfffffffc; bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR0, gedr0); gedr1 = bus_space_read_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR1); bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR1, gedr1); gedr2 = bus_space_read_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR2); gedr2 &= 0x001fffff; bus_space_write_4(sc->pg_bst, sc->pg_bsh, GPIO_GEDR2, gedr2); sc->pg_intr[0] |= gedr0; sc->pg_intr[1] |= gedr1; sc->pg_intr[2] |= gedr2; return (FILTER_HANDLED); } static device_method_t pxa_gpio_methods[] = { DEVMETHOD(device_probe, pxa_gpio_probe), DEVMETHOD(device_attach, pxa_gpio_attach), {0, 0} }; static driver_t pxa_gpio_driver = { "gpio", pxa_gpio_methods, sizeof(struct pxa_gpio_softc), }; static devclass_t pxa_gpio_devclass; DRIVER_MODULE(pxagpio, pxa, pxa_gpio_driver, pxa_gpio_devclass, 0, 0); #define pxagpio_reg_read(softc, reg) \ bus_space_read_4(sc->pg_bst, sc->pg_bsh, reg) #define pxagpio_reg_write(softc, reg, val) \ bus_space_write_4(sc->pg_bst, sc->pg_bsh, reg, val) uint32_t pxa_gpio_get_function(int gpio) { struct pxa_gpio_softc *sc; uint32_t rv, io; sc = pxa_gpio_softc; rv = pxagpio_reg_read(sc, GPIO_FN_REG(gpio)) >> GPIO_FN_SHIFT(gpio); rv = GPIO_FN(rv); io = pxagpio_reg_read(sc, PXA250_GPIO_REG(GPIO_GPDR0, gpio)); if (io & GPIO_BIT(gpio)) rv |= GPIO_OUT; io = pxagpio_reg_read(sc, PXA250_GPIO_REG(GPIO_GPLR0, gpio)); if (io & GPIO_BIT(gpio)) rv |= GPIO_SET; return (rv); } uint32_t pxa_gpio_set_function(int gpio, uint32_t fn) { struct pxa_gpio_softc *sc; uint32_t rv, bit, oldfn; sc = pxa_gpio_softc; oldfn = pxa_gpio_get_function(gpio); if (GPIO_FN(fn) == GPIO_FN(oldfn) && GPIO_FN_IS_OUT(fn) == GPIO_FN_IS_OUT(oldfn)) { /* * The pin's function is not changing. * For Alternate Functions and GPIO input, we can just * return now. * For GPIO output pins, check the initial state is * the same. * * Return 'fn' instead of 'oldfn' so the caller can * reliably detect that we didn't change anything. * (The initial state might be different for non- * GPIO output pins). */ if (!GPIO_IS_GPIO_OUT(fn) || GPIO_FN_IS_SET(fn) == GPIO_FN_IS_SET(oldfn)) return (fn); } /* * See section 4.1.3.7 of the PXA2x0 Developer's Manual for * the correct procedure for changing GPIO pin functions. */ bit = GPIO_BIT(gpio); /* * 1. Configure the correct set/clear state of the pin */ if (GPIO_FN_IS_SET(fn)) pxagpio_reg_write(sc, PXA250_GPIO_REG(GPIO_GPSR0, gpio), bit); else pxagpio_reg_write(sc, PXA250_GPIO_REG(GPIO_GPCR0, gpio), bit); /* * 2. Configure the pin as an input or output as appropriate */ rv = pxagpio_reg_read(sc, PXA250_GPIO_REG(GPIO_GPDR0, gpio)) & ~bit; if (GPIO_FN_IS_OUT(fn)) rv |= bit; pxagpio_reg_write(sc, PXA250_GPIO_REG(GPIO_GPDR0, gpio), rv); /* * 3. Configure the pin's function */ bit = GPIO_FN_MASK << GPIO_FN_SHIFT(gpio); fn = GPIO_FN(fn) << GPIO_FN_SHIFT(gpio); rv = pxagpio_reg_read(sc, GPIO_FN_REG(gpio)) & ~bit; pxagpio_reg_write(sc, GPIO_FN_REG(gpio), rv | fn); return (oldfn); } /* * GPIO "interrupt" handling. */ void pxa_gpio_mask_irq(int irq) { uint32_t val; struct pxa_gpio_softc *sc; int gpio; sc = pxa_gpio_softc; gpio = IRQ_TO_GPIO(irq); val = pxagpio_reg_read(sc, PXA250_GPIO_REG(GPIO_GRER0, gpio)); val &= ~GPIO_BIT(gpio); pxagpio_reg_write(sc, PXA250_GPIO_REG(GPIO_GRER0, gpio), val); } void pxa_gpio_unmask_irq(int irq) { uint32_t val; struct pxa_gpio_softc *sc; int gpio; sc = pxa_gpio_softc; gpio = IRQ_TO_GPIO(irq); val = pxagpio_reg_read(sc, PXA250_GPIO_REG(GPIO_GRER0, gpio)); val |= GPIO_BIT(gpio); pxagpio_reg_write(sc, PXA250_GPIO_REG(GPIO_GRER0, gpio), val); } int -pxa_gpio_get_next_irq() +pxa_gpio_get_next_irq(void) { struct pxa_gpio_softc *sc; int gpio; sc = pxa_gpio_softc; if (sc->pg_intr[0] != 0) { gpio = ffs(sc->pg_intr[0]) - 1; sc->pg_intr[0] &= ~(1 << gpio); return (GPIO_TO_IRQ(gpio)); } if (sc->pg_intr[1] != 0) { gpio = ffs(sc->pg_intr[1]) - 1; sc->pg_intr[1] &= ~(1 << gpio); return (GPIO_TO_IRQ(gpio + 32)); } if (sc->pg_intr[2] != 0) { gpio = ffs(sc->pg_intr[2]) - 1; sc->pg_intr[2] &= ~(1 << gpio); return (GPIO_TO_IRQ(gpio + 64)); } return (-1); } Index: head/sys/arm/xscale/pxa/pxa_icu.c =================================================================== --- head/sys/arm/xscale/pxa/pxa_icu.c (revision 310020) +++ head/sys/arm/xscale/pxa/pxa_icu.c (revision 310021) @@ -1,259 +1,259 @@ /*- * Copyright (c) 2006 Benno Rice. 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 ``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 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include struct pxa_icu_softc { struct resource * pi_res[1]; bus_space_tag_t pi_bst; bus_space_handle_t pi_bsh; }; static struct resource_spec pxa_icu_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static struct pxa_icu_softc *pxa_icu_softc = NULL; static int pxa_icu_probe(device_t); static int pxa_icu_attach(device_t); uint32_t pxa_icu_get_icip(void); void pxa_icu_clear_icip(int); uint32_t pxa_icu_get_icfp(void); void pxa_icu_clear_icfp(int); uint32_t pxa_icu_get_icmr(void); void pxa_icu_set_icmr(uint32_t); uint32_t pxa_icu_get_iclr(void); void pxa_icu_set_iclr(uint32_t); uint32_t pxa_icu_get_icpr(void); void pxa_icu_idle_enable(void); void pxa_icu_idle_disable(void); extern uint32_t pxa_gpio_intr_flags[]; static int pxa_icu_probe(device_t dev) { device_set_desc(dev, "Interrupt Controller"); return (0); } static int pxa_icu_attach(device_t dev) { int error; struct pxa_icu_softc *sc; sc = (struct pxa_icu_softc *)device_get_softc(dev); if (pxa_icu_softc != NULL) return (ENXIO); pxa_icu_softc = sc; error = bus_alloc_resources(dev, pxa_icu_spec, sc->pi_res); if (error) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->pi_bst = rman_get_bustag(sc->pi_res[0]); sc->pi_bsh = rman_get_bushandle(sc->pi_res[0]); /* Disable all interrupts. */ pxa_icu_set_icmr(0); /* Route all interrupts to IRQ rather than FIQ. */ pxa_icu_set_iclr(0); /* XXX: This should move to configure_final or something. */ enable_interrupts(PSR_I|PSR_F); return (0); } static device_method_t pxa_icu_methods[] = { DEVMETHOD(device_probe, pxa_icu_probe), DEVMETHOD(device_attach, pxa_icu_attach), {0, 0} }; static driver_t pxa_icu_driver = { "icu", pxa_icu_methods, sizeof(struct pxa_icu_softc), }; static devclass_t pxa_icu_devclass; DRIVER_MODULE(pxaicu, pxa, pxa_icu_driver, pxa_icu_devclass, 0, 0); int arm_get_next_irq(int last __unused) { int irq; if ((irq = pxa_icu_get_icip()) != 0) { return (ffs(irq) - 1); } return (pxa_gpio_get_next_irq()); } void arm_mask_irq(uintptr_t nb) { uint32_t mr; if (nb >= IRQ_GPIO0) { pxa_gpio_mask_irq(nb); return; } mr = pxa_icu_get_icmr(); mr &= ~(1 << nb); pxa_icu_set_icmr(mr); } void arm_unmask_irq(uintptr_t nb) { uint32_t mr; if (nb >= IRQ_GPIO0) { pxa_gpio_unmask_irq(nb); return; } mr = pxa_icu_get_icmr(); mr |= (1 << nb); pxa_icu_set_icmr(mr); } uint32_t -pxa_icu_get_icip() +pxa_icu_get_icip(void) { return (bus_space_read_4(pxa_icu_softc->pi_bst, pxa_icu_softc->pi_bsh, ICU_IP)); } void pxa_icu_clear_icip(int irq) { bus_space_write_4(pxa_icu_softc->pi_bst, pxa_icu_softc->pi_bsh, ICU_IP, (1 << irq)); } uint32_t -pxa_icu_get_icfp() +pxa_icu_get_icfp(void) { return (bus_space_read_4(pxa_icu_softc->pi_bst, pxa_icu_softc->pi_bsh, ICU_FP)); } void pxa_icu_clear_icfp(int irq) { bus_space_write_4(pxa_icu_softc->pi_bst, pxa_icu_softc->pi_bsh, ICU_FP, (1 << irq)); } uint32_t -pxa_icu_get_icmr() +pxa_icu_get_icmr(void) { return (bus_space_read_4(pxa_icu_softc->pi_bst, pxa_icu_softc->pi_bsh, ICU_MR)); } void pxa_icu_set_icmr(uint32_t val) { bus_space_write_4(pxa_icu_softc->pi_bst, pxa_icu_softc->pi_bsh, ICU_MR, val); } uint32_t -pxa_icu_get_iclr() +pxa_icu_get_iclr(void) { return (bus_space_read_4(pxa_icu_softc->pi_bst, pxa_icu_softc->pi_bsh, ICU_LR)); } void pxa_icu_set_iclr(uint32_t val) { bus_space_write_4(pxa_icu_softc->pi_bst, pxa_icu_softc->pi_bsh, ICU_LR, val); } uint32_t -pxa_icu_get_icpr() +pxa_icu_get_icpr(void) { return (bus_space_read_4(pxa_icu_softc->pi_bst, pxa_icu_softc->pi_bsh, ICU_PR)); } void -pxa_icu_idle_enable() +pxa_icu_idle_enable(void) { bus_space_write_4(pxa_icu_softc->pi_bst, pxa_icu_softc->pi_bsh, ICU_CR, 0x0); } void -pxa_icu_idle_disable() +pxa_icu_idle_disable(void) { bus_space_write_4(pxa_icu_softc->pi_bst, pxa_icu_softc->pi_bsh, ICU_CR, 0x1); } Index: head/sys/arm/xscale/pxa/pxa_space.c =================================================================== --- head/sys/arm/xscale/pxa/pxa_space.c (revision 310020) +++ head/sys/arm/xscale/pxa/pxa_space.c (revision 310021) @@ -1,265 +1,265 @@ /* $NetBSD: obio_space.c,v 1.6 2003/07/15 00:25:05 lukem Exp $ */ /*- * Copyright (c) 2001, 2002, 2003 Wasabi Systems, Inc. * All rights reserved. * * Written by Jason R. Thorpe for Wasabi Systems, Inc. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project by * Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC * 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. */ /* * bus_space functions for PXA devices */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_PXATAG, "PXA bus_space tags", "Bus_space tags for PXA"); /* Prototypes for all the bus_space structure functions */ bs_protos(generic); bs_protos(pxa); /* * The obio bus space tag. This is constant for all instances, so * we never have to explicitly "create" it. */ struct bus_space _base_tag = { /* cookie */ .bs_privdata = NULL, /* mapping/unmapping */ .bs_map = generic_bs_map, .bs_unmap = generic_bs_unmap, .bs_subregion = generic_bs_subregion, /* allocation/deallocation */ .bs_alloc = generic_bs_alloc, .bs_free = generic_bs_free, /* barrier */ .bs_barrier = generic_bs_barrier, /* read (single) */ .bs_r_1 = pxa_bs_r_1, .bs_r_2 = pxa_bs_r_2, .bs_r_4 = pxa_bs_r_4, .bs_r_8 = BS_UNIMPLEMENTED, /* read multiple */ .bs_rm_1 = pxa_bs_rm_1, .bs_rm_2 = pxa_bs_rm_2, .bs_rm_4 = BS_UNIMPLEMENTED, .bs_rm_8 = BS_UNIMPLEMENTED, /* read region */ .bs_rr_1 = pxa_bs_rr_1, .bs_rr_2 = BS_UNIMPLEMENTED, .bs_rr_4 = BS_UNIMPLEMENTED, .bs_rr_8 = BS_UNIMPLEMENTED, /* write (single) */ .bs_w_1 = pxa_bs_w_1, .bs_w_2 = pxa_bs_w_2, .bs_w_4 = pxa_bs_w_4, .bs_w_8 = BS_UNIMPLEMENTED, /* write multiple */ .bs_wm_1 = pxa_bs_wm_1, .bs_wm_2 = pxa_bs_wm_2, .bs_wm_4 = BS_UNIMPLEMENTED, .bs_wm_8 = BS_UNIMPLEMENTED, /* write region */ .bs_wr_1 = BS_UNIMPLEMENTED, .bs_wr_2 = BS_UNIMPLEMENTED, .bs_wr_4 = BS_UNIMPLEMENTED, .bs_wr_8 = BS_UNIMPLEMENTED, /* set multiple */ .bs_sm_1 = BS_UNIMPLEMENTED, .bs_sm_2 = BS_UNIMPLEMENTED, .bs_sm_4 = BS_UNIMPLEMENTED, .bs_sm_8 = BS_UNIMPLEMENTED, /* set region */ .bs_sr_1 = BS_UNIMPLEMENTED, .bs_sr_2 = BS_UNIMPLEMENTED, .bs_sr_4 = BS_UNIMPLEMENTED, .bs_sr_8 = BS_UNIMPLEMENTED, /* copy */ .bs_c_1 = BS_UNIMPLEMENTED, .bs_c_2 = BS_UNIMPLEMENTED, .bs_c_4 = BS_UNIMPLEMENTED, .bs_c_8 = BS_UNIMPLEMENTED, /* read stream (single) */ .bs_r_1_s = BS_UNIMPLEMENTED, .bs_r_2_s = BS_UNIMPLEMENTED, .bs_r_4_s = BS_UNIMPLEMENTED, .bs_r_8_s = BS_UNIMPLEMENTED, /* read multiple stream */ .bs_rm_1_s = BS_UNIMPLEMENTED, .bs_rm_2_s = BS_UNIMPLEMENTED, .bs_rm_4_s = BS_UNIMPLEMENTED, .bs_rm_8_s = BS_UNIMPLEMENTED, /* read region stream */ .bs_rr_1_s = BS_UNIMPLEMENTED, .bs_rr_2_s = BS_UNIMPLEMENTED, .bs_rr_4_s = BS_UNIMPLEMENTED, .bs_rr_8_s = BS_UNIMPLEMENTED, /* write stream (single) */ .bs_w_1_s = BS_UNIMPLEMENTED, .bs_w_2_s = BS_UNIMPLEMENTED, .bs_w_4_s = BS_UNIMPLEMENTED, .bs_w_8_s = BS_UNIMPLEMENTED, /* write multiple stream */ .bs_wm_1_s = BS_UNIMPLEMENTED, .bs_wm_2_s = BS_UNIMPLEMENTED, .bs_wm_4_s = BS_UNIMPLEMENTED, .bs_wm_8_s = BS_UNIMPLEMENTED, /* write region stream */ .bs_wr_1_s = BS_UNIMPLEMENTED, .bs_wr_2_s = BS_UNIMPLEMENTED, .bs_wr_4_s = BS_UNIMPLEMENTED, .bs_wr_8_s = BS_UNIMPLEMENTED, }; static struct bus_space _obio_tag; bus_space_tag_t base_tag = &_base_tag; bus_space_tag_t obio_tag = NULL; void -pxa_obio_tag_init() +pxa_obio_tag_init(void) { bcopy(&_base_tag, &_obio_tag, sizeof(struct bus_space)); _obio_tag.bs_privdata = (void *)PXA2X0_PERIPH_OFFSET; obio_tag = &_obio_tag; } bus_space_tag_t pxa_bus_tag_alloc(bus_addr_t offset) { struct bus_space *tag; tag = (struct bus_space *)malloc(sizeof(struct bus_space), M_PXATAG, M_WAITOK); bcopy(&_base_tag, tag, sizeof(struct bus_space)); tag->bs_privdata = (void *)offset; return ((bus_space_tag_t)tag); } #define READ_SINGLE(type, proto, base) \ type \ proto(bus_space_tag_t tag, bus_space_handle_t bsh, bus_size_t offset) \ { \ bus_addr_t tag_offset; \ type value; \ tag_offset = (bus_addr_t)tag->bs_privdata; \ value = base(NULL, bsh + tag_offset, offset); \ return (value); \ } READ_SINGLE(u_int8_t, pxa_bs_r_1, generic_bs_r_1) READ_SINGLE(u_int16_t, pxa_bs_r_2, generic_bs_r_2) READ_SINGLE(u_int32_t, pxa_bs_r_4, generic_bs_r_4) #undef READ_SINGLE #define WRITE_SINGLE(type, proto, base) \ void \ proto(bus_space_tag_t tag, bus_space_handle_t bsh, bus_size_t offset, \ type value) \ { \ bus_addr_t tag_offset; \ tag_offset = (bus_addr_t)tag->bs_privdata; \ base(NULL, bsh + tag_offset, offset, value); \ } WRITE_SINGLE(u_int8_t, pxa_bs_w_1, generic_bs_w_1) WRITE_SINGLE(u_int16_t, pxa_bs_w_2, generic_bs_w_2) WRITE_SINGLE(u_int32_t, pxa_bs_w_4, generic_bs_w_4) #undef WRITE_SINGLE #define READ_MULTI(type, proto, base) \ void \ proto(bus_space_tag_t tag, bus_space_handle_t bsh, bus_size_t offset, \ type *dest, bus_size_t count) \ { \ bus_addr_t tag_offset; \ tag_offset = (bus_addr_t)tag->bs_privdata; \ base(NULL, bsh + tag_offset, offset, dest, count); \ } READ_MULTI(u_int8_t, pxa_bs_rm_1, generic_bs_rm_1) READ_MULTI(u_int16_t, pxa_bs_rm_2, generic_bs_rm_2) READ_MULTI(u_int8_t, pxa_bs_rr_1, generic_bs_rr_1) #undef READ_MULTI #define WRITE_MULTI(type, proto, base) \ void \ proto(bus_space_tag_t tag, bus_space_handle_t bsh, bus_size_t offset, \ const type *src, bus_size_t count) \ { \ bus_addr_t tag_offset; \ tag_offset = (bus_addr_t)tag->bs_privdata; \ base(NULL, bsh + tag_offset, offset, src, count); \ } WRITE_MULTI(u_int8_t, pxa_bs_wm_1, generic_bs_wm_1) WRITE_MULTI(u_int16_t, pxa_bs_wm_2, generic_bs_wm_2) #undef WRITE_MULTI Index: head/sys/arm/xscale/pxa/pxa_timer.c =================================================================== --- head/sys/arm/xscale/pxa/pxa_timer.c (revision 310020) +++ head/sys/arm/xscale/pxa/pxa_timer.c (revision 310021) @@ -1,318 +1,318 @@ /*- * Copyright (c) 2006 Benno Rice. 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 ``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 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PXA_TIMER_FREQUENCY 3686400 #define PXA_TIMER_TICK (PXA_TIMER_FREQUENCY / hz) struct pxa_timer_softc { struct resource * pt_res[5]; bus_space_tag_t pt_bst; bus_space_handle_t pt_bsh; }; static struct resource_spec pxa_timer_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { SYS_RES_IRQ, 3, RF_ACTIVE }, { -1, 0 } }; static struct pxa_timer_softc *timer_softc = NULL; static int pxa_timer_probe(device_t); static int pxa_timer_attach(device_t); static driver_filter_t pxa_hardclock; static unsigned pxa_timer_get_timecount(struct timecounter *); uint32_t pxa_timer_get_osmr(int); void pxa_timer_set_osmr(int, uint32_t); uint32_t pxa_timer_get_oscr(void); void pxa_timer_set_oscr(uint32_t); uint32_t pxa_timer_get_ossr(void); void pxa_timer_clear_ossr(uint32_t); void pxa_timer_watchdog_enable(void); void pxa_timer_watchdog_disable(void); void pxa_timer_interrupt_enable(int); void pxa_timer_interrupt_disable(int); static struct timecounter pxa_timer_timecounter = { .tc_get_timecount = pxa_timer_get_timecount, .tc_name = "OS Timer", .tc_frequency = PXA_TIMER_FREQUENCY, .tc_counter_mask = ~0u, .tc_quality = 1000, }; static int pxa_timer_probe(device_t dev) { device_set_desc(dev, "OS Timer"); return (0); } static int pxa_timer_attach(device_t dev) { int error; void *ihl; struct pxa_timer_softc *sc; sc = (struct pxa_timer_softc *)device_get_softc(dev); if (timer_softc != NULL) return (ENXIO); error = bus_alloc_resources(dev, pxa_timer_spec, sc->pt_res); if (error) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->pt_bst = rman_get_bustag(sc->pt_res[0]); sc->pt_bsh = rman_get_bushandle(sc->pt_res[0]); timer_softc = sc; pxa_timer_interrupt_disable(-1); pxa_timer_watchdog_disable(); if (bus_setup_intr(dev, sc->pt_res[1], INTR_TYPE_CLK, pxa_hardclock, NULL, NULL, &ihl) != 0) { bus_release_resources(dev, pxa_timer_spec, sc->pt_res); device_printf(dev, "could not setup hardclock interrupt\n"); return (ENXIO); } return (0); } static int pxa_hardclock(void *arg) { struct trapframe *frame; frame = (struct trapframe *)arg; /* Clear the interrupt */ pxa_timer_clear_ossr(OST_SR_CH0); /* Schedule next tick */ pxa_timer_set_osmr(0, pxa_timer_get_oscr() + PXA_TIMER_TICK); /* Do what we came here for */ hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); return (FILTER_HANDLED); } static device_method_t pxa_timer_methods[] = { DEVMETHOD(device_probe, pxa_timer_probe), DEVMETHOD(device_attach, pxa_timer_attach), {0, 0} }; static driver_t pxa_timer_driver = { "timer", pxa_timer_methods, sizeof(struct pxa_timer_softc), }; static devclass_t pxa_timer_devclass; DRIVER_MODULE(pxatimer, pxa, pxa_timer_driver, pxa_timer_devclass, 0, 0); static unsigned pxa_timer_get_timecount(struct timecounter *tc) { return (pxa_timer_get_oscr()); } void cpu_initclocks(void) { pxa_timer_set_oscr(0); pxa_timer_set_osmr(0, PXA_TIMER_TICK); pxa_timer_interrupt_enable(0); tc_init(&pxa_timer_timecounter); } void cpu_reset(void) { uint32_t val; (void)disable_interrupts(PSR_I|PSR_F); val = pxa_timer_get_oscr(); val += PXA_TIMER_FREQUENCY; pxa_timer_set_osmr(3, val); pxa_timer_watchdog_enable(); for(;;); } void DELAY(int usec) { uint32_t val; if (timer_softc == NULL) { for (; usec > 0; usec--) for (val = 100; val > 0; val--) ; return; } val = pxa_timer_get_oscr(); val += (PXA_TIMER_FREQUENCY * usec) / 1000000; while (pxa_timer_get_oscr() <= val); } uint32_t pxa_timer_get_osmr(int which) { return (bus_space_read_4(timer_softc->pt_bst, timer_softc->pt_bsh, which * 0x4)); } void pxa_timer_set_osmr(int which, uint32_t val) { bus_space_write_4(timer_softc->pt_bst, timer_softc->pt_bsh, which * 0x4, val); } uint32_t -pxa_timer_get_oscr() +pxa_timer_get_oscr(void) { return (bus_space_read_4(timer_softc->pt_bst, timer_softc->pt_bsh, OST_CR)); } void pxa_timer_set_oscr(uint32_t val) { bus_space_write_4(timer_softc->pt_bst, timer_softc->pt_bsh, OST_CR, val); } uint32_t -pxa_timer_get_ossr() +pxa_timer_get_ossr(void) { return (bus_space_read_4(timer_softc->pt_bst, timer_softc->pt_bsh, OST_SR)); } void pxa_timer_clear_ossr(uint32_t val) { bus_space_write_4(timer_softc->pt_bst, timer_softc->pt_bsh, OST_SR, val); } void -pxa_timer_watchdog_enable() +pxa_timer_watchdog_enable(void) { bus_space_write_4(timer_softc->pt_bst, timer_softc->pt_bsh, OST_WR, 0x1); } void -pxa_timer_watchdog_disable() +pxa_timer_watchdog_disable(void) { bus_space_write_4(timer_softc->pt_bst, timer_softc->pt_bsh, OST_WR, 0x0); } void pxa_timer_interrupt_enable(int which) { uint32_t oier; if (which == -1) { bus_space_write_4(timer_softc->pt_bst, timer_softc->pt_bsh, OST_IR, 0xf); return; } oier = bus_space_read_4(timer_softc->pt_bst, timer_softc->pt_bsh, OST_IR); oier |= 1 << which; bus_space_write_4(timer_softc->pt_bst, timer_softc->pt_bsh, OST_IR, oier); } void pxa_timer_interrupt_disable(int which) { uint32_t oier; if (which == -1) { bus_space_write_4(timer_softc->pt_bst, timer_softc->pt_bsh, OST_IR, 0); } oier = bus_space_read_4(timer_softc->pt_bst, timer_softc->pt_bsh, OST_IR); oier &= ~(1 << which); bus_space_write_4(timer_softc->pt_bst, timer_softc->pt_bsh, OST_IR, oier); }