Index: stable/11/sys/arm/freescale/imx/imx6_machdep.c =================================================================== --- stable/11/sys/arm/freescale/imx/imx6_machdep.c (revision 323403) +++ stable/11/sys/arm/freescale/imx/imx6_machdep.c (revision 323404) @@ -1,355 +1,319 @@ /*- * Copyright (c) 2013 Ian Lepore * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "platform_if.h" -static uint32_t gpio1_node; - -#ifndef INTRNG /* - * Work around the linux workaround for imx6 erratum 006687, in which some - * ethernet interrupts don't go to the GPC and thus won't wake the system from - * Wait mode. We don't use Wait mode (which halts the GIC, leaving only GPC - * interrupts able to wake the system), so we don't experience the bug at all. - * The linux workaround is to reconfigure GPIO1_6 as the ENET interrupt by - * writing magic values to an undocumented IOMUX register, then letting the gpio - * interrupt driver notify the ethernet driver. We'll be able to do all that - * (even though we don't need to) once the INTRNG project is committed and the - * imx_gpio driver becomes an interrupt driver. Until then, this crazy little - * workaround watches for requests to map an interrupt 6 with the interrupt - * controller node referring to gpio1, and it substitutes the proper ffec - * interrupt number. - */ -static int -imx6_decode_fdt(uint32_t iparent, uint32_t *intr, int *interrupt, - int *trig, int *pol) -{ - - if (fdt32_to_cpu(intr[0]) == 6 && - OF_node_from_xref(iparent) == gpio1_node) { - *interrupt = 150; - *trig = INTR_TRIGGER_CONFORM; - *pol = INTR_POLARITY_CONFORM; - return (0); - } - return (gic_decode_fdt(iparent, intr, interrupt, trig, pol)); -} - -fdt_pic_decode_t fdt_pic_table[] = { - &imx6_decode_fdt, - NULL -}; -#endif - -/* * Fix FDT data related to interrupts. * * Driven by the needs of linux and its drivers (as always), the published FDT * data for imx6 now sets the interrupt parent for most devices to the GPC * interrupt controller, which is for use when the chip is in deep-sleep mode. * We don't support deep sleep or have a GPC-PIC driver; we need all interrupts * to be handled by the GIC. * * Luckily, the change to the FDT data was to assign the GPC as the interrupt * parent for the soc node and letting that get inherited by all other devices * (except a few that directly name GIC as their interrupt parent). So we can * set the world right by just changing the interrupt-parent property of the soc * node to refer to GIC instead of GPC. This will get us by until we write our * own GPC driver (or until linux changes its mind and the FDT data again). * * We validate that we have data that looks like we expect before changing it: * - SOC node exists and has GPC as its interrupt parent. * - GPC node exists and has GIC as its interrupt parent. - * - GIC node exists and is its own interrupt parent. + * - GIC node exists and is its own interrupt parent or has no parent. * * This applies to all models of imx6. Luckily all of them have the devices * involved at the same addresses on the same busses, so we don't need any * per-soc logic. We handle this at platform attach time rather than via the * fdt_fixup_table, because the latter requires matching on the FDT "model" * property, and this applies to all boards including those not yet invented. */ static void fix_fdt_interrupt_data(void) { phandle_t gicipar, gicnode, gicxref; phandle_t gpcipar, gpcnode, gpcxref; phandle_t socipar, socnode; int result; socnode = OF_finddevice("/soc"); if (socnode == -1) return; result = OF_getencprop(socnode, "interrupt-parent", &socipar, sizeof(socipar)); if (result <= 0) return; + /* GIC node may be child of soc node, or appear directly at root. */ gicnode = OF_finddevice("/soc/interrupt-controller@00a01000"); + if (gicnode == -1) { + gicnode = OF_finddevice("/interrupt-controller@00a01000"); if (gicnode == -1) return; + } + gicxref = OF_xref_from_node(gicnode); + + /* If gic node has no parent, pretend it is its own parent. */ result = OF_getencprop(gicnode, "interrupt-parent", &gicipar, sizeof(gicipar)); if (result <= 0) - return; - gicxref = OF_xref_from_node(gicnode); + gicipar = gicxref; gpcnode = OF_finddevice("/soc/aips-bus@02000000/gpc@020dc000"); if (gpcnode == -1) return; result = OF_getencprop(gpcnode, "interrupt-parent", &gpcipar, sizeof(gpcipar)); if (result <= 0) return; gpcxref = OF_xref_from_node(gpcnode); if (socipar != gpcxref || gpcipar != gicxref || gicipar != gicxref) return; gicxref = cpu_to_fdt32(gicxref); OF_setprop(socnode, "interrupt-parent", &gicxref, sizeof(gicxref)); } static vm_offset_t imx6_lastaddr(platform_t plat) { return (devmap_lastaddr()); } static int imx6_attach(platform_t plat) { /* Fix soc interrupt-parent property. */ fix_fdt_interrupt_data(); /* Inform the MPCore timer driver that its clock is variable. */ arm_tmr_change_frequency(ARM_TMR_FREQUENCY_VARIES); return (0); } static void imx6_late_init(platform_t plat) { const uint32_t IMX6_WDOG_SR_PHYS = 0x020bc004; imx_wdog_init_last_reset(IMX6_WDOG_SR_PHYS); - - /* Cache the gpio1 node handle for imx6_decode_fdt() workaround code. */ - gpio1_node = OF_node_from_xref( - OF_finddevice("/soc/aips-bus@02000000/gpio@0209c000")); } /* * Set up static device mappings. * * This attempts to cover the most-used devices with 1MB section mappings, which * is good for performance (uses fewer TLB entries for device access). * * ARMMP covers the interrupt controller, MPCore timers, global timer, and the * L2 cache controller. Most of the 1MB range is unused reserved space. * * AIPS1/AIPS2 cover most of the on-chip devices such as uart, spi, i2c, etc. * * Notably not mapped right now are HDMI, GPU, and other devices below ARMMP in * the memory map. When we get support for graphics it might make sense to * static map some of that area. Be careful with other things in that area such * as OCRAM that probably shouldn't be mapped as VM_MEMATTR_DEVICE memory. */ static int imx6_devmap_init(platform_t plat) { const uint32_t IMX6_ARMMP_PHYS = 0x00a00000; const uint32_t IMX6_ARMMP_SIZE = 0x00100000; const uint32_t IMX6_AIPS1_PHYS = 0x02000000; const uint32_t IMX6_AIPS1_SIZE = 0x00100000; const uint32_t IMX6_AIPS2_PHYS = 0x02100000; const uint32_t IMX6_AIPS2_SIZE = 0x00100000; devmap_add_entry(IMX6_ARMMP_PHYS, IMX6_ARMMP_SIZE); devmap_add_entry(IMX6_AIPS1_PHYS, IMX6_AIPS1_SIZE); devmap_add_entry(IMX6_AIPS2_PHYS, IMX6_AIPS2_SIZE); return (0); } void cpu_reset(void) { const uint32_t IMX6_WDOG_CR_PHYS = 0x020bc000; imx_wdog_cpu_reset(IMX6_WDOG_CR_PHYS); } /* * Determine what flavor of imx6 we're running on. * * This code is based on the way u-boot does it. Information found on the web * indicates that Freescale themselves were the original source of this logic, * including the strange check for number of CPUs in the SCU configuration * register, which is apparently needed on some revisions of the SOLO. * * According to the documentation, there is such a thing as an i.MX6 Dual * (non-lite flavor). However, Freescale doesn't seem to have assigned it a * number or provided any logic to handle it in their detection code. * * Note that the ANALOG_DIGPROG and SCU configuration registers are not * documented in the chip reference manual. (SCU configuration is mentioned, * but not mapped out in detail.) I think the bottom two bits of the scu config * register may be ncpu-1. * * This hasn't been tested yet on a dual[-lite]. * * On a solo: * digprog = 0x00610001 * hwsoc = 0x00000062 * scu config = 0x00000500 * On a quad: * digprog = 0x00630002 * hwsoc = 0x00000063 * scu config = 0x00005503 */ u_int imx_soc_type() { uint32_t digprog, hwsoc; uint32_t *pcr; static u_int soctype; const vm_offset_t SCU_CONFIG_PHYSADDR = 0x00a00004; #define HWSOC_MX6SL 0x60 #define HWSOC_MX6DL 0x61 #define HWSOC_MX6SOLO 0x62 #define HWSOC_MX6Q 0x63 if (soctype != 0) return (soctype); digprog = imx6_anatop_read_4(IMX6_ANALOG_DIGPROG_SL); hwsoc = (digprog >> IMX6_ANALOG_DIGPROG_SOCTYPE_SHIFT) & IMX6_ANALOG_DIGPROG_SOCTYPE_MASK; if (hwsoc != HWSOC_MX6SL) { digprog = imx6_anatop_read_4(IMX6_ANALOG_DIGPROG); hwsoc = (digprog & IMX6_ANALOG_DIGPROG_SOCTYPE_MASK) >> IMX6_ANALOG_DIGPROG_SOCTYPE_SHIFT; /*printf("digprog = 0x%08x\n", digprog);*/ if (hwsoc == HWSOC_MX6DL) { pcr = devmap_ptov(SCU_CONFIG_PHYSADDR, 4); if (pcr != NULL) { /*printf("scu config = 0x%08x\n", *pcr);*/ if ((*pcr & 0x03) == 0) { hwsoc = HWSOC_MX6SOLO; } } } } /* printf("hwsoc 0x%08x\n", hwsoc); */ switch (hwsoc) { case HWSOC_MX6SL: soctype = IMXSOC_6SL; break; case HWSOC_MX6SOLO: soctype = IMXSOC_6S; break; case HWSOC_MX6DL: soctype = IMXSOC_6DL; break; case HWSOC_MX6Q : soctype = IMXSOC_6Q; break; default: printf("imx_soc_type: Don't understand hwsoc 0x%02x, " "digprog 0x%08x; assuming IMXSOC_6Q\n", hwsoc, digprog); soctype = IMXSOC_6Q; break; } return (soctype); } /* * Early putc routine for EARLY_PRINTF support. To use, add to kernel config: * option SOCDEV_PA=0x02000000 * option SOCDEV_VA=0x02000000 * option EARLY_PRINTF * Resist the temptation to change the #if 0 to #ifdef EARLY_PRINTF here. It * makes sense now, but if multiple SOCs do that it will make early_putc another * duplicate symbol to be eliminated on the path to a generic kernel. */ #if 0 static void imx6_early_putc(int c) { volatile uint32_t * UART_STAT_REG = (uint32_t *)0x02020098; volatile uint32_t * UART_TX_REG = (uint32_t *)0x02020040; const uint32_t UART_TXRDY = (1 << 3); while ((*UART_STAT_REG & UART_TXRDY) == 0) continue; *UART_TX_REG = c; } early_putc_t *early_putc = imx6_early_putc; #endif static platform_method_t imx6_methods[] = { PLATFORMMETHOD(platform_attach, imx6_attach), PLATFORMMETHOD(platform_lastaddr, imx6_lastaddr), PLATFORMMETHOD(platform_devmap_init, imx6_devmap_init), PLATFORMMETHOD(platform_late_init, imx6_late_init), PLATFORMMETHOD_END, }; FDT_PLATFORM_DEF2(imx6, imx6s, "i.MX6 Solo", 0, "fsl,imx6s", 0); FDT_PLATFORM_DEF2(imx6, imx6d, "i.MX6 Dual", 0, "fsl,imx6dl", 0); FDT_PLATFORM_DEF2(imx6, imx6q, "i.MX6 Quad", 0, "fsl,imx6q", 0); Index: stable/11/sys/arm/freescale/imx/imx_gpt.c =================================================================== --- stable/11/sys/arm/freescale/imx/imx_gpt.c (revision 323403) +++ stable/11/sys/arm/freescale/imx/imx_gpt.c (revision 323404) @@ -1,415 +1,429 @@ /*- * Copyright (c) 2012, 2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Oleksandr Rybalko under sponsorship * from the FreeBSD Foundation. * * 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 WRITE4(_sc, _r, _v) \ bus_space_write_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r), (_v)) #define READ4(_sc, _r) \ bus_space_read_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r)) #define SET4(_sc, _r, _m) \ WRITE4((_sc), (_r), READ4((_sc), (_r)) | (_m)) #define CLEAR4(_sc, _r, _m) \ WRITE4((_sc), (_r), READ4((_sc), (_r)) & ~(_m)) static u_int imx_gpt_get_timecount(struct timecounter *); static int imx_gpt_timer_start(struct eventtimer *, sbintime_t, sbintime_t); static int imx_gpt_timer_stop(struct eventtimer *); static int imx_gpt_intr(void *); static int imx_gpt_probe(device_t); static int imx_gpt_attach(device_t); static struct timecounter imx_gpt_timecounter = { .tc_name = "iMXGPT", .tc_get_timecount = imx_gpt_get_timecount, .tc_counter_mask = ~0u, .tc_frequency = 0, .tc_quality = 1000, }; struct imx_gpt_softc { device_t sc_dev; struct resource * res[2]; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; void * sc_ih; /* interrupt handler */ uint32_t sc_period; uint32_t sc_clksrc; uint32_t clkfreq; + uint32_t ir_reg; struct eventtimer et; }; /* Global softc pointer for use in DELAY(). */ static struct imx_gpt_softc *imx_gpt_sc; /* * Hand-calibrated delay-loop counter. This was calibrated on an i.MX6 running * at 792mhz. It will delay a bit too long on slower processors -- that's * better than not delaying long enough. In practice this is unlikely to get * used much since the clock driver is one of the first to start up, and once * we're attached the delay loop switches to using the timer hardware. */ static const int imx_gpt_delay_count = 78; /* Try to divide down an available fast clock to this frequency. */ #define TARGET_FREQUENCY 1000000000 -/* Don't try to set an event timer period smaller than this. */ -#define MIN_ET_PERIOD 10LLU - - static struct resource_spec imx_gpt_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static struct ofw_compat_data compat_data[] = { {"fsl,imx6dl-gpt", 1}, {"fsl,imx6q-gpt", 1}, {"fsl,imx53-gpt", 1}, {"fsl,imx51-gpt", 1}, {"fsl,imx31-gpt", 1}, {"fsl,imx27-gpt", 1}, {"fsl,imx25-gpt", 1}, {NULL, 0} }; static int imx_gpt_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "Freescale i.MX GPT timer"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int imx_gpt_attach(device_t dev) { struct imx_gpt_softc *sc; int ctlreg, err; - uint32_t basefreq, prescale; + uint32_t basefreq, prescale, setup_ticks, t1, t2; sc = device_get_softc(dev); if (bus_alloc_resources(dev, imx_gpt_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->sc_dev = dev; sc->sc_iot = rman_get_bustag(sc->res[0]); sc->sc_ioh = rman_get_bushandle(sc->res[0]); /* * For now, just automatically choose a good clock for the hardware * we're running on. Eventually we could allow selection from the fdt; * the code in this driver will cope with any clock frequency. */ sc->sc_clksrc = GPT_CR_CLKSRC_IPG; ctlreg = 0; switch (sc->sc_clksrc) { case GPT_CR_CLKSRC_32K: basefreq = 32768; break; case GPT_CR_CLKSRC_IPG: basefreq = imx_ccm_ipg_hz(); break; case GPT_CR_CLKSRC_IPG_HIGH: basefreq = imx_ccm_ipg_hz() * 2; break; case GPT_CR_CLKSRC_24M: ctlreg |= GPT_CR_24MEN; basefreq = 24000000; break; case GPT_CR_CLKSRC_NONE:/* Can't run without a clock. */ case GPT_CR_CLKSRC_EXT: /* No way to get the freq of an ext clock. */ default: device_printf(dev, "Unsupported clock source '%d'\n", sc->sc_clksrc); return (EINVAL); } /* * The following setup sequence is from the I.MX6 reference manual, * "Selecting the clock source". First, disable the clock and * interrupts. This also clears input and output mode bits and in * general completes several of the early steps in the procedure. */ WRITE4(sc, IMX_GPT_CR, 0); WRITE4(sc, IMX_GPT_IR, 0); /* Choose the clock and the power-saving behaviors. */ ctlreg |= sc->sc_clksrc | /* Use selected clock */ GPT_CR_FRR | /* Just count (FreeRunner mode) */ GPT_CR_STOPEN | /* Run in STOP mode */ GPT_CR_DOZEEN | /* Run in DOZE mode */ GPT_CR_WAITEN | /* Run in WAIT mode */ GPT_CR_DBGEN; /* Run in DEBUG mode */ WRITE4(sc, IMX_GPT_CR, ctlreg); /* * The datasheet says to do the software reset after choosing the clock * source. It says nothing about needing to wait for the reset to * complete, but the register description does document the fact that * the reset isn't complete until the SWR bit reads 0, so let's be safe. * The reset also clears all registers except for a few of the bits in * CR, but we'll rewrite all the CR bits when we start the counter. */ WRITE4(sc, IMX_GPT_CR, ctlreg | GPT_CR_SWR); while (READ4(sc, IMX_GPT_CR) & GPT_CR_SWR) continue; /* Set a prescaler value that gets us near the target frequency. */ if (basefreq < TARGET_FREQUENCY) { prescale = 0; sc->clkfreq = basefreq; } else { prescale = basefreq / TARGET_FREQUENCY; sc->clkfreq = basefreq / prescale; prescale -= 1; /* 1..n range is 0..n-1 in hardware. */ } WRITE4(sc, IMX_GPT_PR, prescale); /* Clear the status register. */ WRITE4(sc, IMX_GPT_SR, GPT_IR_ALL); /* Start the counter. */ WRITE4(sc, IMX_GPT_CR, ctlreg | GPT_CR_EN); if (bootverbose) device_printf(dev, "Running on %dKHz clock, base freq %uHz CR=0x%08x, PR=0x%08x\n", sc->clkfreq / 1000, basefreq, READ4(sc, IMX_GPT_CR), READ4(sc, IMX_GPT_PR)); /* Setup the timer interrupt. */ err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_CLK, imx_gpt_intr, NULL, sc, &sc->sc_ih); if (err != 0) { bus_release_resources(dev, imx_gpt_spec, sc->res); device_printf(dev, "Unable to setup the clock irq handler, " "err = %d\n", err); return (ENXIO); } + /* + * Measure how many clock ticks it takes to setup a one-shot event (it's + * longer than you might think, due to wait states in accessing gpt + * registers). Scale up the result by a factor of 1.5 to be safe, + * and use that to set the minimum eventtimer period we can schedule. In + * the real world, the value works out to about 750ns on imx5 hardware. + */ + t1 = READ4(sc, IMX_GPT_CNT); + WRITE4(sc, IMX_GPT_OCR3, 0); + t2 = READ4(sc, IMX_GPT_CNT); + setup_ticks = ((t2 - t1 + 1) * 3) / 2; + /* Register as an eventtimer. */ sc->et.et_name = "iMXGPT"; sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERIODIC; sc->et.et_quality = 800; sc->et.et_frequency = sc->clkfreq; - sc->et.et_min_period = (MIN_ET_PERIOD << 32) / sc->et.et_frequency; - sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; + sc->et.et_min_period = ((uint64_t)setup_ticks << 32) / sc->clkfreq; + sc->et.et_max_period = ((uint64_t)0xfffffffe << 32) / sc->clkfreq; sc->et.et_start = imx_gpt_timer_start; sc->et.et_stop = imx_gpt_timer_stop; sc->et.et_priv = sc; et_register(&sc->et); /* Register as a timecounter. */ imx_gpt_timecounter.tc_frequency = sc->clkfreq; tc_init(&imx_gpt_timecounter); /* If this is the first unit, store the softc for use in DELAY. */ if (device_get_unit(dev) == 0) imx_gpt_sc = sc; return (0); } static int imx_gpt_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct imx_gpt_softc *sc; uint32_t ticks; sc = (struct imx_gpt_softc *)et->et_priv; if (period != 0) { sc->sc_period = ((uint32_t)et->et_frequency * period) >> 32; /* Set expected value */ WRITE4(sc, IMX_GPT_OCR2, READ4(sc, IMX_GPT_CNT) + sc->sc_period); /* Enable compare register 2 Interrupt */ - SET4(sc, IMX_GPT_IR, GPT_IR_OF2); + sc->ir_reg |= GPT_IR_OF2; + WRITE4(sc, IMX_GPT_IR, sc->ir_reg); return (0); } else if (first != 0) { + /* Enable compare register 3 interrupt if not already on. */ + if ((sc->ir_reg & GPT_IR_OF3) == 0) { + sc->ir_reg |= GPT_IR_OF3; + WRITE4(sc, IMX_GPT_IR, sc->ir_reg); + } ticks = ((uint32_t)et->et_frequency * first) >> 32; /* Do not disturb, otherwise event will be lost */ spinlock_enter(); /* Set expected value */ WRITE4(sc, IMX_GPT_OCR3, READ4(sc, IMX_GPT_CNT) + ticks); - /* Enable compare register 1 Interrupt */ - SET4(sc, IMX_GPT_IR, GPT_IR_OF3); /* Now everybody can relax */ spinlock_exit(); return (0); } return (EINVAL); } static int imx_gpt_timer_stop(struct eventtimer *et) { struct imx_gpt_softc *sc; sc = (struct imx_gpt_softc *)et->et_priv; - /* Disable OF2 Interrupt */ - CLEAR4(sc, IMX_GPT_IR, GPT_IR_OF2); - WRITE4(sc, IMX_GPT_SR, GPT_IR_OF2); + /* Disable interrupts and clear any pending status. */ + sc->ir_reg &= ~(GPT_IR_OF2 | GPT_IR_OF3); + WRITE4(sc, IMX_GPT_IR, sc->ir_reg); + WRITE4(sc, IMX_GPT_SR, GPT_IR_OF2 | GPT_IR_OF3); sc->sc_period = 0; return (0); } static int imx_gpt_intr(void *arg) { struct imx_gpt_softc *sc; uint32_t status; sc = (struct imx_gpt_softc *)arg; status = READ4(sc, IMX_GPT_SR); /* * Clear interrupt status before invoking event callbacks. The callback * often sets up a new one-shot timer event and if the interval is short * enough it can fire before we get out of this function. If we cleared * at the bottom we'd miss the interrupt and hang until the clock wraps. */ WRITE4(sc, IMX_GPT_SR, status); /* Handle one-shot timer events. */ if (status & GPT_IR_OF3) { if (sc->et.et_active) { sc->et.et_event_cb(&sc->et, sc->et.et_arg); } } /* Handle periodic timer events. */ if (status & GPT_IR_OF2) { if (sc->et.et_active) sc->et.et_event_cb(&sc->et, sc->et.et_arg); if (sc->sc_period != 0) WRITE4(sc, IMX_GPT_OCR2, READ4(sc, IMX_GPT_CNT) + sc->sc_period); } return (FILTER_HANDLED); } u_int imx_gpt_get_timecount(struct timecounter *tc) { if (imx_gpt_sc == NULL) return (0); return (READ4(imx_gpt_sc, IMX_GPT_CNT)); } static device_method_t imx_gpt_methods[] = { DEVMETHOD(device_probe, imx_gpt_probe), DEVMETHOD(device_attach, imx_gpt_attach), DEVMETHOD_END }; static driver_t imx_gpt_driver = { "imx_gpt", imx_gpt_methods, sizeof(struct imx_gpt_softc), }; static devclass_t imx_gpt_devclass; EARLY_DRIVER_MODULE(imx_gpt, simplebus, imx_gpt_driver, imx_gpt_devclass, 0, 0, BUS_PASS_TIMER); void DELAY(int usec) { uint64_t curcnt, endcnt, startcnt, ticks; /* If the timer hardware is not accessible, just use a loop. */ if (imx_gpt_sc == NULL) { while (usec-- > 0) for (ticks = 0; ticks < imx_gpt_delay_count; ++ticks) cpufunc_nullop(); return; } /* * Calculate the tick count with 64-bit values so that it works for any * clock frequency. Loop until the hardware count reaches start+ticks. * If the 32-bit hardware count rolls over while we're looping, just * manually do a carry into the high bits after each read; don't worry * that doing this on each loop iteration is inefficient -- we're trying * to waste time here. */ ticks = 1 + ((uint64_t)usec * imx_gpt_sc->clkfreq) / 1000000; curcnt = startcnt = READ4(imx_gpt_sc, IMX_GPT_CNT); endcnt = startcnt + ticks; while (curcnt < endcnt) { curcnt = READ4(imx_gpt_sc, IMX_GPT_CNT); if (curcnt < startcnt) curcnt += 1ULL << 32; } } Index: stable/11 =================================================================== --- stable/11 (revision 323403) +++ stable/11 (revision 323404) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r315589,315591,316659,316661