Index: head/sys/arm/conf/ALPINE =================================================================== --- head/sys/arm/conf/ALPINE (revision 320650) +++ head/sys/arm/conf/ALPINE (revision 320651) @@ -1,85 +1,86 @@ # Kernel configuration for Alpine Board. # # For more information on this file, please read the config(5) manual page, # and/or the handbook section on Kernel Configuration Files: # # http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the ../../conf/NOTES and NOTES files. # If you are in doubt as to the purpose or necessity of a line, check first # in NOTES. # # $FreeBSD$ ident ALPINE include "std.armv6" include "../annapurna/alpine/std.alpine" makeoptions MODULES_OVERRIDE="" makeoptions WERROR="-Werror" options SCHED_4BSD # 4BSD scheduler options SMP # Enable multiple cores options PLATFORM +options MULTIDELAY # Interrupt controller device gic options INTRNG # Annapurna Alpine drivers device al_ccu # Alpine Cache Coherency Unit device al_nb_service # Alpine North Bridge Service device al_iofic # I/O Fabric Interrupt Controller device al_serdes # Serializer/Deserializer device al_udma # Universal DMA # Pseudo devices device loop device random device pty device md device gpio # ATA controllers device ahci # AHCI-compatible SATA controllers device ata # Legacy ATA/SATA controllers # ATA/SCSI peripherals device scbus # SCSI bus (required for ATA/SCSI) device ch # SCSI media changers device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct ATA/SCSI access) device ses # Enclosure Services (SES and SAF-TE) #device ctl # CAM Target Layer # Serial ports device uart # PCI/PCIE device pci device pci_host_generic device al_pci # Annapurna Alpine PCI-E # Ethernet device ether device mii device bpf device al_eth # Annapurna Alpine Ethernet NIC options DEVICE_POLLING # USB ethernet support, requires miibus device miibus #FDT options FDT options FDT_DTB_STATIC makeoptions FDT_DTS_FILE=annapurna-alpine.dts Index: head/sys/arm/conf/VERSATILEPB =================================================================== --- head/sys/arm/conf/VERSATILEPB (revision 320650) +++ head/sys/arm/conf/VERSATILEPB (revision 320651) @@ -1,77 +1,78 @@ # # VERSATILEPB - Configuration for QEMU version of Versatile Platform Board # # For more information on this file, please read the config(5) manual page, # and/or the handbook section on Kernel Configuration Files: # # http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the ../../conf/NOTES and NOTES files. # If you are in doubt as to the purpose or necessity of a line, check first # in NOTES. # # $FreeBSD$ ident VERSATILEPB machine arm armv6 cpu CPU_ARM1176 include "std.armv6" files "../versatile/files.versatile" makeoptions MODULES_OVERRIDE="" options KERNVIRTADDR=0xc0100000 makeoptions KERNVIRTADDR=0xc0100000 options SCHED_4BSD # 4BSD scheduler options LINUX_BOOT_ABI # Process metadata passed from Linux boot loaders options ROOTDEVNAME=\"ufs:da0s1a\" device bpf device loop device mii device mii_bitbang device smc device smcphy device ether device uart device pl011 device pl190 device pty device snp device pci # SCSI Controllers device sym # NCR/Symbios/LSI Logic 53C8XX/53C1010/53C1510D # ATA/SCSI peripherals device scbus # SCSI bus (required for ATA/SCSI) device da # Direct Access (disks) device pass # Passthrough device (direct ATA/SCSI access) # NOTE: serial console is disabled if syscons enabled # Comment following lines for headless setup device sc device kbdmux options SC_DFLT_FONT # compile font in makeoptions SC_DFLT_FONT=cp437 device md device random # Entropy device options INTRNG options PLATFORM +options MULTIDELAY # Flattened Device Tree options FDT # Configure using FDT/DTB data options FDT_DTB_STATIC makeoptions FDT_DTS_FILE=versatile-pb.dts Index: head/sys/arm/freescale/imx/imx_epit.c =================================================================== --- head/sys/arm/freescale/imx/imx_epit.c (revision 320650) +++ head/sys/arm/freescale/imx/imx_epit.c (revision 320651) @@ -1,528 +1,491 @@ /*- * Copyright (c) 2017 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$"); /* * Driver for imx Enhanced Programmable Interval Timer, a simple free-running * counter device that can be used as the system timecounter. On imx5 a second * instance of the device is used as the system eventtimer. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EPIT_CR 0x00 /* Control register */ #define EPIT_CR_CLKSRC_SHIFT 24 #define EPIT_CR_CLKSRC_OFF 0 #define EPIT_CR_CLKSRC_IPG 1 #define EPIT_CR_CLKSRC_HFCLK 2 #define EPIT_CR_CLKSRC_LFCLK 3 #define EPIT_CR_STOPEN (1u << 21) #define EPIT_CR_WAITEN (1u << 19) #define EPIT_CR_DBGEN (1u << 18) #define EPIT_CR_IOVW (1u << 17) #define EPIT_CR_SWR (1u << 16) #define EPIT_CR_RLD (1u << 3) #define EPIT_CR_OCIEN (1u << 2) #define EPIT_CR_ENMOD (1u << 1) #define EPIT_CR_EN (1u << 0) #define EPIT_SR 0x04 /* Status register */ #define EPIT_SR_OCIF (1u << 0) #define EPIT_LR 0x08 /* Load register */ #define EPIT_CMPR 0x0c /* Compare register */ #define EPIT_CNR 0x10 /* Counter register */ /* * Define event timer limits. * * In theory our minimum period is 1 tick, because to setup a oneshot we don't * need a read-modify-write sequence to calculate and set a compare register * value while the counter is running. In practice the waveform diagrams in the * manual make it appear that a setting of 1 might cause it to miss the event, * so I'm setting the lower limit to 2 ticks. */ #define ET_MIN_TICKS 2 #define ET_MAX_TICKS 0xfffffffe static u_int epit_tc_get_timecount(struct timecounter *tc); struct epit_softc { device_t dev; struct resource * memres; struct resource * intres; void * inthandle; uint32_t clkfreq; uint32_t ctlreg; uint32_t period; struct timecounter tc; struct eventtimer et; bool oneshot; }; -#ifndef MULTIDELAY -/* Global softc pointer for use in DELAY(). */ -static struct epit_softc *epit_sc; -#endif - /* * Probe data. For some reason, the standard linux dts files don't have * compatible properties on the epit devices (other properties are missing too, * like clocks, but we don't care as much about that). So our probe routine * uses the name of the node (must contain "epit") and the address of the * registers as identifying marks. */ static const uint32_t imx51_epit_ioaddr[2] = {0x73fac000, 0x73fb0000}; static const uint32_t imx53_epit_ioaddr[2] = {0x53fac000, 0x53fb0000}; static const uint32_t imx6_epit_ioaddr[2] = {0x020d0000, 0x020d4000}; /* ocd_data is number of units to instantiate on the platform */ static struct ofw_compat_data compat_data[] = { {"fsl,imx6ul-epit", 1}, {"fsl,imx6sx-epit", 1}, {"fsl,imx6q-epit", 1}, {"fsl,imx6dl-epit", 1}, {"fsl,imx53-epit", 2}, {"fsl,imx51-epit", 2}, {"fsl,imx31-epit", 2}, {"fsl,imx27-epit", 2}, {"fsl,imx25-epit", 2}, {NULL, 0} }; static inline uint32_t RD4(struct epit_softc *sc, bus_size_t offset) { return (bus_read_4(sc->memres, offset)); } static inline void WR4(struct epit_softc *sc, bus_size_t offset, uint32_t value) { bus_write_4(sc->memres, offset, value); } static inline void WR4B(struct epit_softc *sc, bus_size_t offset, uint32_t value) { bus_write_4(sc->memres, offset, value); bus_barrier(sc->memres, offset, 4, BUS_SPACE_BARRIER_WRITE); } static u_int epit_read_counter(struct epit_softc *sc) { /* * Hardware is a downcounter, adjust to look like it counts up for use * with timecounter and DELAY. */ return (0xffffffff - RD4(sc, EPIT_CNR)); } static void epit_do_delay(int usec, void *arg) { struct epit_softc *sc = arg; uint64_t curcnt, endcnt, startcnt, ticks; /* * 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 * sc->clkfreq) / 1000000; curcnt = startcnt = epit_read_counter(sc); endcnt = startcnt + ticks; while (curcnt < endcnt) { curcnt = epit_read_counter(sc); if (curcnt < startcnt) curcnt += 1ULL << 32; } } static u_int epit_tc_get_timecount(struct timecounter *tc) { return (epit_read_counter(tc->tc_priv)); } static int epit_tc_attach(struct epit_softc *sc) { /* When the counter hits zero, reload with 0xffffffff. Start it. */ WR4(sc, EPIT_LR, 0xffffffff); WR4(sc, EPIT_CR, sc->ctlreg | EPIT_CR_EN); /* Register as a timecounter. */ sc->tc.tc_name = "EPIT"; sc->tc.tc_quality = 1000; sc->tc.tc_frequency = sc->clkfreq; sc->tc.tc_counter_mask = 0xffffffff; sc->tc.tc_get_timecount = epit_tc_get_timecount; sc->tc.tc_priv = sc; tc_init(&sc->tc); /* We are the DELAY() implementation. */ -#ifdef MULTIDELAY arm_set_delay(epit_do_delay, sc); -#else - epit_sc = sc; -#endif + return (0); } static int epit_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct epit_softc *sc; uint32_t ticks; sc = (struct epit_softc *)et->et_priv; /* * Disable the timer and clear any pending status. The timer may be * running or may have just expired if we're called to reschedule the * next event before the previous event time arrives. */ WR4(sc, EPIT_CR, sc->ctlreg); WR4(sc, EPIT_SR, EPIT_SR_OCIF); if (period != 0) { sc->oneshot = false; ticks = ((uint32_t)et->et_frequency * period) >> 32; } else if (first != 0) { sc->oneshot = true; ticks = ((uint32_t)et->et_frequency * first) >> 32; } else { return (EINVAL); } /* Set the countdown load register and start the timer. */ WR4(sc, EPIT_LR, ticks); WR4B(sc, EPIT_CR, sc->ctlreg | EPIT_CR_EN); return (0); } static int epit_et_stop(struct eventtimer *et) { struct epit_softc *sc; sc = (struct epit_softc *)et->et_priv; /* Disable the timer and clear any pending status. */ WR4(sc, EPIT_CR, sc->ctlreg); WR4B(sc, EPIT_SR, EPIT_SR_OCIF); return (0); } static int epit_intr(void *arg) { struct epit_softc *sc; uint32_t status; sc = arg; /* * Disable a one-shot timer until a new event is scheduled so that the * counter doesn't wrap and fire again. Do this before clearing the * status since a short period would make it fire again really soon. * * 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. */ if (sc->oneshot) WR4(sc, EPIT_CR, sc->ctlreg); status = RD4(sc, EPIT_SR); WR4B(sc, EPIT_SR, status); if ((status & EPIT_SR_OCIF) == 0) return (FILTER_STRAY); if (sc->et.et_active) sc->et.et_event_cb(&sc->et, sc->et.et_arg); return (FILTER_HANDLED); } static int epit_et_attach(struct epit_softc *sc) { int err, rid; rid = 0; sc->intres = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->intres == NULL) { device_printf(sc->dev, "could not allocate interrupt\n"); return (ENXIO); } err = bus_setup_intr(sc->dev, sc->intres, INTR_TYPE_CLK | INTR_MPSAFE, epit_intr, NULL, sc, &sc->inthandle); if (err != 0) { device_printf(sc->dev, "unable to setup the irq handler\n"); return (err); } /* To be an eventtimer, we need interrupts enabled. */ sc->ctlreg |= EPIT_CR_OCIEN; /* Register as an eventtimer. */ sc->et.et_name = "EPIT"; sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERIODIC; sc->et.et_quality = 1000; sc->et.et_frequency = sc->clkfreq; sc->et.et_min_period = ((uint64_t)ET_MIN_TICKS << 32) / sc->clkfreq; sc->et.et_max_period = ((uint64_t)ET_MAX_TICKS << 32) / sc->clkfreq; sc->et.et_start = epit_et_start; sc->et.et_stop = epit_et_stop; sc->et.et_priv = sc; et_register(&sc->et); return (0); } static int epit_probe(device_t dev) { struct resource *memres; rman_res_t ioaddr; int num_units, rid, unit; if (!ofw_bus_status_okay(dev)) return (ENXIO); /* * The FDT data for imx5 and imx6 EPIT hardware is missing or broken, * but it may get fixed some day, so first just do a normal check. We * return success if the compatible string matches and we haven't * already instantiated the number of units needed on this platform. */ unit = device_get_unit(dev); num_units = ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (unit < num_units) { device_set_desc(dev, "i.MX EPIT timer"); return (BUS_PROBE_DEFAULT); } /* * No compat string match, but for imx6 all the data we need is in the * node except the compat string, so do our own compatibility check * using the device name of the node and the register block address. */ if (strstr(ofw_bus_get_name(dev), "epit") == NULL) return (ENXIO); rid = 0; memres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_UNMAPPED); if (memres == NULL) return (ENXIO); ioaddr = rman_get_start(memres); bus_free_resource(dev, SYS_RES_MEMORY, memres); if (imx_soc_family() == 6) { if (unit > 0) return (ENXIO); if (ioaddr != imx6_epit_ioaddr[unit]) return (ENXIO); } else { if (unit > 1) return (ENXIO); switch (imx_soc_type()) { case IMXSOC_51: if (ioaddr != imx51_epit_ioaddr[unit]) return (ENXIO); break; case IMXSOC_53: if (ioaddr != imx53_epit_ioaddr[unit]) return (ENXIO); break; default: return (ENXIO); } /* * XXX Right now we have no way to handle the fact that the * entire EPIT node is missing, which means no interrupt data. */ return (ENXIO); } device_set_desc(dev, "i.MX EPIT timer"); return (BUS_PROBE_DEFAULT); } static int epit_attach(device_t dev) { struct epit_softc *sc; int err, rid; uint32_t clksrc; sc = device_get_softc(dev); sc->dev = dev; rid = 0; sc->memres = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->memres == NULL) { device_printf(sc->dev, "could not allocate registers\n"); return (ENXIO); } /* * For now, use ipg (66 MHz). Some day we should get this from fdt. */ clksrc = EPIT_CR_CLKSRC_IPG; switch (clksrc) { default: device_printf(dev, "Unsupported clock source '%d', using IPG\n", clksrc); /* FALLTHROUGH */ case EPIT_CR_CLKSRC_IPG: sc->clkfreq = imx_ccm_ipg_hz(); break; case EPIT_CR_CLKSRC_HFCLK: sc->clkfreq = imx_ccm_perclk_hz(); break; case EPIT_CR_CLKSRC_LFCLK: sc->clkfreq = 32768; break; } /* * Init: stop operations and clear all options, then set up options and * clock source, then do a soft-reset and wait for it to complete. */ WR4(sc, EPIT_CR, 0); sc->ctlreg = (clksrc << EPIT_CR_CLKSRC_SHIFT) | /* Use selected clock */ EPIT_CR_ENMOD | /* Reload counter on enable */ EPIT_CR_RLD | /* Reload counter from LR */ EPIT_CR_STOPEN | /* Run in STOP mode */ EPIT_CR_WAITEN | /* Run in WAIT mode */ EPIT_CR_DBGEN; /* Run in DEBUG mode */ WR4B(sc, EPIT_CR, sc->ctlreg | EPIT_CR_SWR); while (RD4(sc, EPIT_CR) & EPIT_CR_SWR) continue; /* * Unit 0 is the timecounter, 1 (if instantiated) is the eventtimer. */ if (device_get_unit(sc->dev) == 0) err = epit_tc_attach(sc); else err = epit_et_attach(sc); return (err); } static device_method_t epit_methods[] = { DEVMETHOD(device_probe, epit_probe), DEVMETHOD(device_attach, epit_attach), DEVMETHOD_END }; static driver_t epit_driver = { "imx_epit", epit_methods, sizeof(struct epit_softc), }; static devclass_t epit_devclass; EARLY_DRIVER_MODULE(imx_epit, simplebus, epit_driver, epit_devclass, 0, 0, BUS_PASS_TIMER); - -#ifndef MULTIDELAY - -/* - * 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 epit_delay_count = 78; - -void -DELAY(int usec) -{ - uint64_t ticks; - - /* If the timer hardware is not accessible, just use a loop. */ - if (epit_sc == NULL) { - while (usec-- > 0) - for (ticks = 0; ticks < epit_delay_count; ++ticks) - cpufunc_nullop(); - return; - } else { - epit_do_delay(usec, epit_sc); - } -} - -#endif Index: head/sys/arm/freescale/imx/imx_gpt.c =================================================================== --- head/sys/arm/freescale/imx/imx_gpt.c (revision 320650) +++ head/sys/arm/freescale/imx/imx_gpt.c (revision 320651) @@ -1,461 +1,423 @@ /*- * 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 -#ifdef MULTIDELAY #include /* For arm_set_delay */ -#endif #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 void imx_gpt_do_delay(int, void *); 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; }; -#ifndef MULTIDELAY -/* 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; -#endif - /* Try to divide down an available fast clock to this frequency. */ #define TARGET_FREQUENCY 1000000000 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,imx6ul-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); /* * We only support a single unit, because the only thing this driver * does with the complex timer hardware is supply the system * timecounter and eventtimer. There is nothing useful we can do with * the additional device instances that exist in some chips. */ if (device_get_unit(dev) > 0) 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, 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 = ((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; imx_gpt_timecounter.tc_priv = sc; tc_init(&imx_gpt_timecounter); /* If this is the first unit, store the softc for use in DELAY. */ if (device_get_unit(dev) == 0) { -#ifdef MULTIDELAY arm_set_delay(imx_gpt_do_delay, sc); -#else - imx_gpt_sc = sc; -#endif } 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 */ 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); /* 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 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); } static u_int imx_gpt_get_timecount(struct timecounter *tc) { struct imx_gpt_softc *sc; sc = tc->tc_priv; return (READ4(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); static void imx_gpt_do_delay(int usec, void *arg) { struct imx_gpt_softc *sc = arg; uint64_t curcnt, endcnt, startcnt, ticks; /* * 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 * sc->clkfreq) / 1000000; curcnt = startcnt = READ4(sc, IMX_GPT_CNT); endcnt = startcnt + ticks; while (curcnt < endcnt) { curcnt = READ4(sc, IMX_GPT_CNT); if (curcnt < startcnt) curcnt += 1ULL << 32; } } - -#ifndef MULTIDELAY -void -DELAY(int usec) -{ - uint64_t 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; - } else - imx_gpt_do_delay(usec, imx_gpt_sc); - -} -#endif Index: head/sys/arm/ti/am335x/am335x_dmtimer.c =================================================================== --- head/sys/arm/ti/am335x/am335x_dmtimer.c (revision 320650) +++ head/sys/arm/ti/am335x/am335x_dmtimer.c (revision 320651) @@ -1,379 +1,358 @@ /*- * Copyright (c) 2012 Damjan Marion * 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 -#ifdef MULTIDELAY #include /* For arm_set_delay */ -#endif #include #include #include #include #include #include "am335x_dmtreg.h" struct am335x_dmtimer_softc { device_t dev; int tmr_mem_rid; struct resource * tmr_mem_res; int tmr_irq_rid; struct resource * tmr_irq_res; void *tmr_irq_handler; uint32_t sysclk_freq; uint32_t tclr; /* Cached TCLR register. */ union { struct timecounter tc; struct eventtimer et; } func; int tmr_num; /* Hardware unit number. */ char tmr_name[12]; /* "DMTimerN", N = tmr_num */ }; static struct am335x_dmtimer_softc *am335x_dmtimer_et_sc = NULL; static struct am335x_dmtimer_softc *am335x_dmtimer_tc_sc = NULL; static void am335x_dmtimer_delay(int, void *); /* * We use dmtimer2 for eventtimer and dmtimer3 for timecounter. */ #define ET_TMR_NUM 2 #define TC_TMR_NUM 3 /* 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}, }; #define DMTIMER_READ4(sc, reg) bus_read_4((sc)->tmr_mem_res, (reg)) #define DMTIMER_WRITE4(sc, reg, val) bus_write_4((sc)->tmr_mem_res, (reg), (val)) static int am335x_dmtimer_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct am335x_dmtimer_softc *sc; uint32_t initial_count, reload_count; sc = et->et_priv; /* * Stop the timer before changing it. This routine will often be called * while the timer is still running, to either lengthen or shorten the * current event time. We need to ensure the timer doesn't expire while * we're working with it. * * Also clear any pending interrupt status, because it's at least * theoretically possible that we're running in a primary interrupt * context now, and a timer interrupt could be pending even before we * stopped the timer. The more likely case is that we're being called * from the et_event_cb() routine dispatched from our own handler, but * it's not clear to me that that's the only case possible. */ sc->tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD); DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); if (period != 0) { reload_count = ((uint32_t)et->et_frequency * period) >> 32; sc->tclr |= DMT_TCLR_AUTOLOAD; } else { reload_count = 0; } if (first != 0) initial_count = ((uint32_t)et->et_frequency * first) >> 32; else initial_count = reload_count; /* * Set auto-reload and current-count values. This timer hardware counts * up from the initial/reload value and interrupts on the zero rollover. */ DMTIMER_WRITE4(sc, DMT_TLDR, 0xFFFFFFFF - reload_count); DMTIMER_WRITE4(sc, DMT_TCRR, 0xFFFFFFFF - initial_count); /* Enable overflow interrupt, and start the timer. */ DMTIMER_WRITE4(sc, DMT_IRQENABLE_SET, DMT_IRQ_OVF); sc->tclr |= DMT_TCLR_START; DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); return (0); } static int am335x_dmtimer_et_stop(struct eventtimer *et) { struct am335x_dmtimer_softc *sc; sc = et->et_priv; /* Stop timer, disable and clear interrupt. */ sc->tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD); DMTIMER_WRITE4(sc, DMT_TCLR, sc->tclr); DMTIMER_WRITE4(sc, DMT_IRQENABLE_CLR, DMT_IRQ_OVF); DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); return (0); } static int am335x_dmtimer_et_intr(void *arg) { struct am335x_dmtimer_softc *sc; sc = arg; /* Ack the interrupt, and invoke the callback if it's still enabled. */ DMTIMER_WRITE4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); if (sc->func.et.et_active) sc->func.et.et_event_cb(&sc->func.et, sc->func.et.et_arg); return (FILTER_HANDLED); } static int am335x_dmtimer_et_init(struct am335x_dmtimer_softc *sc) { KASSERT(am335x_dmtimer_et_sc == NULL, ("already have an eventtimer")); /* * Setup eventtimer interrupt handling. Panic if anything goes wrong, * because the system just isn't going to run without an eventtimer. */ sc->tmr_irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &sc->tmr_irq_rid, RF_ACTIVE); if (sc->tmr_irq_res == NULL) panic("am335x_dmtimer: could not allocate irq resources"); if (bus_setup_intr(sc->dev, sc->tmr_irq_res, INTR_TYPE_CLK, am335x_dmtimer_et_intr, NULL, sc, &sc->tmr_irq_handler) != 0) panic("am335x_dmtimer: count not setup irq handler"); sc->func.et.et_name = sc->tmr_name; sc->func.et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT; sc->func.et.et_quality = 500; sc->func.et.et_frequency = sc->sysclk_freq; sc->func.et.et_min_period = ((0x00000005LLU << 32) / sc->func.et.et_frequency); sc->func.et.et_max_period = (0xfffffffeLLU << 32) / sc->func.et.et_frequency; sc->func.et.et_start = am335x_dmtimer_et_start; sc->func.et.et_stop = am335x_dmtimer_et_stop; sc->func.et.et_priv = sc; am335x_dmtimer_et_sc = sc; et_register(&sc->func.et); return (0); } static unsigned am335x_dmtimer_tc_get_timecount(struct timecounter *tc) { struct am335x_dmtimer_softc *sc; sc = tc->tc_priv; return (DMTIMER_READ4(sc, DMT_TCRR)); } static int am335x_dmtimer_tc_init(struct am335x_dmtimer_softc *sc) { KASSERT(am335x_dmtimer_tc_sc == NULL, ("already have a timecounter")); /* Set up timecounter, start it, register 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); sc->func.tc.tc_name = sc->tmr_name; sc->func.tc.tc_get_timecount = am335x_dmtimer_tc_get_timecount; sc->func.tc.tc_counter_mask = ~0u; sc->func.tc.tc_frequency = sc->sysclk_freq; sc->func.tc.tc_quality = 500; sc->func.tc.tc_priv = sc; am335x_dmtimer_tc_sc = sc; tc_init(&sc->func.tc); -#ifdef MULTIDELAY arm_set_delay(am335x_dmtimer_delay, sc); -#endif return (0); } static int am335x_dmtimer_probe(device_t dev) { char strbuf[32]; int tmr_num; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); /* * Get the hardware unit number (the N from ti,hwmods="timerN"). * If this isn't the hardware unit we're going to use for either the * eventtimer or the timecounter, no point in instantiating the device. */ tmr_num = ti_hwmods_get_unit(dev, "timer"); if (tmr_num != ET_TMR_NUM && tmr_num != TC_TMR_NUM) return (ENXIO); snprintf(strbuf, sizeof(strbuf), "AM335x DMTimer%d", tmr_num); device_set_desc_copy(dev, strbuf); return(BUS_PROBE_DEFAULT); } static int am335x_dmtimer_attach(device_t dev) { struct am335x_dmtimer_softc *sc; clk_ident_t timer_id; int err; sc = device_get_softc(dev); sc->dev = dev; /* Get the base clock frequency. */ if ((err = ti_prcm_clk_get_source_freq(SYS_CLK, &sc->sysclk_freq)) != 0) return (err); /* 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->tmr_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->tmr_mem_rid, RF_ACTIVE); if (sc->tmr_mem_res == NULL) { return (ENXIO); } sc->tmr_num = ti_hwmods_get_unit(dev, "timer"); snprintf(sc->tmr_name, sizeof(sc->tmr_name), "DMTimer%d", sc->tmr_num); /* * Go set up either a timecounter or eventtimer. We wouldn't have * attached if we weren't one or the other. */ if (sc->tmr_num == ET_TMR_NUM) am335x_dmtimer_et_init(sc); else if (sc->tmr_num == TC_TMR_NUM) am335x_dmtimer_tc_init(sc); else panic("am335x_dmtimer: bad timer number %d", sc->tmr_num); return (0); } static device_method_t am335x_dmtimer_methods[] = { DEVMETHOD(device_probe, am335x_dmtimer_probe), DEVMETHOD(device_attach, am335x_dmtimer_attach), { 0, 0 } }; static driver_t am335x_dmtimer_driver = { "am335x_dmtimer", am335x_dmtimer_methods, sizeof(struct am335x_dmtimer_softc), }; static devclass_t am335x_dmtimer_devclass; DRIVER_MODULE(am335x_dmtimer, simplebus, am335x_dmtimer_driver, am335x_dmtimer_devclass, 0, 0); MODULE_DEPEND(am335x_dmtimer, am335x_prcm, 1, 1, 1); static void am335x_dmtimer_delay(int usec, void *arg) { struct am335x_dmtimer_softc *sc = arg; int32_t counts; uint32_t first, last; /* Get the number of times to count */ counts = (usec + 1) * (sc->sysclk_freq / 1000000); first = DMTIMER_READ4(sc, DMT_TCRR); while (counts > 0) { last = DMTIMER_READ4(sc, DMT_TCRR); if (last > first) { counts -= (int32_t)(last - first); } else { counts -= (int32_t)((0xFFFFFFFF - first) + last); } first = last; } } - -#ifndef MULTIDELAY -void -DELAY(int usec) -{ - int32_t counts; - - if (am335x_dmtimer_tc_sc == NULL) { - for (; usec > 0; usec--) - for (counts = 200; counts > 0; counts--) - /* Prevent gcc from optimizing out the loop */ - cpufunc_nullop(); - return; - } else - am335x_dmtimer_delay(usec, am335x_dmtimer_tc_sc); -} -#endif Index: head/sys/arm/versatile/sp804.c =================================================================== --- head/sys/arm/versatile/sp804.c (revision 320650) +++ head/sys/arm/versatile/sp804.c (revision 320651) @@ -1,377 +1,341 @@ /* * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2012 Damjan Marion * 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 -#ifdef MULTIDELAY #include /* For arm_set_delay */ -#endif #include #include #include #include #define SP804_TIMER1_LOAD 0x00 #define SP804_TIMER1_VALUE 0x04 #define SP804_TIMER1_CONTROL 0x08 #define TIMER_CONTROL_EN (1 << 7) #define TIMER_CONTROL_FREERUN (0 << 6) #define TIMER_CONTROL_PERIODIC (1 << 6) #define TIMER_CONTROL_INTREN (1 << 5) #define TIMER_CONTROL_DIV1 (0 << 2) #define TIMER_CONTROL_DIV16 (1 << 2) #define TIMER_CONTROL_DIV256 (2 << 2) #define TIMER_CONTROL_32BIT (1 << 1) #define TIMER_CONTROL_ONESHOT (1 << 0) #define SP804_TIMER1_INTCLR 0x0C #define SP804_TIMER1_RIS 0x10 #define SP804_TIMER1_MIS 0x14 #define SP804_TIMER1_BGLOAD 0x18 #define SP804_TIMER2_LOAD 0x20 #define SP804_TIMER2_VALUE 0x24 #define SP804_TIMER2_CONTROL 0x28 #define SP804_TIMER2_INTCLR 0x2C #define SP804_TIMER2_RIS 0x30 #define SP804_TIMER2_MIS 0x34 #define SP804_TIMER2_BGLOAD 0x38 #define SP804_PERIPH_ID0 0xFE0 #define SP804_PERIPH_ID1 0xFE4 #define SP804_PERIPH_ID2 0xFE8 #define SP804_PERIPH_ID3 0xFEC #define SP804_PRIMECELL_ID0 0xFF0 #define SP804_PRIMECELL_ID1 0xFF4 #define SP804_PRIMECELL_ID2 0xFF8 #define SP804_PRIMECELL_ID3 0xFFC #define DEFAULT_FREQUENCY 1000000 /* * QEMU seems to have problem with full frequency */ #define DEFAULT_DIVISOR 16 #define DEFAULT_CONTROL_DIV TIMER_CONTROL_DIV16 struct sp804_timer_softc { struct resource* mem_res; struct resource* irq_res; void* intr_hl; uint32_t sysclk_freq; bus_space_tag_t bst; bus_space_handle_t bsh; struct timecounter tc; bool et_enabled; struct eventtimer et; int timer_initialized; }; /* Read/Write macros for Timer used as timecounter */ #define sp804_timer_tc_read_4(reg) \ bus_space_read_4(sc->bst, sc->bsh, reg) #define sp804_timer_tc_write_4(reg, val) \ bus_space_write_4(sc->bst, sc->bsh, reg, val) static unsigned sp804_timer_tc_get_timecount(struct timecounter *); static void sp804_timer_delay(int, void *); static unsigned sp804_timer_tc_get_timecount(struct timecounter *tc) { struct sp804_timer_softc *sc = tc->tc_priv; return 0xffffffff - sp804_timer_tc_read_4(SP804_TIMER1_VALUE); } static int sp804_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct sp804_timer_softc *sc = et->et_priv; uint32_t count, reg; if (first != 0) { sc->et_enabled = 1; count = ((uint32_t)et->et_frequency * first) >> 32; sp804_timer_tc_write_4(SP804_TIMER2_LOAD, count); reg = TIMER_CONTROL_32BIT | TIMER_CONTROL_INTREN | TIMER_CONTROL_PERIODIC | DEFAULT_CONTROL_DIV | TIMER_CONTROL_EN; sp804_timer_tc_write_4(SP804_TIMER2_CONTROL, reg); return (0); } if (period != 0) { panic("period"); } return (EINVAL); } static int sp804_timer_stop(struct eventtimer *et) { struct sp804_timer_softc *sc = et->et_priv; uint32_t reg; sc->et_enabled = 0; reg = sp804_timer_tc_read_4(SP804_TIMER2_CONTROL); reg &= ~(TIMER_CONTROL_EN); sp804_timer_tc_write_4(SP804_TIMER2_CONTROL, reg); return (0); } static int sp804_timer_intr(void *arg) { struct sp804_timer_softc *sc = arg; static uint32_t prev = 0; uint32_t x = 0; x = sp804_timer_tc_read_4(SP804_TIMER1_VALUE); prev =x ; sp804_timer_tc_write_4(SP804_TIMER2_INTCLR, 1); if (sc->et_enabled) { if (sc->et.et_active) { sc->et.et_event_cb(&sc->et, sc->et.et_arg); } } return (FILTER_HANDLED); } static int sp804_timer_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "arm,sp804")) { device_set_desc(dev, "SP804 System Timer"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int sp804_timer_attach(device_t dev) { struct sp804_timer_softc *sc = device_get_softc(dev); int rid = 0; int i; uint32_t id, reg; phandle_t node; pcell_t clock; 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 resource\n"); return (ENXIO); } sc->bst = rman_get_bustag(sc->mem_res); sc->bsh = rman_get_bushandle(sc->mem_res); /* Request the IRQ resources */ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "Error: could not allocate irq resources\n"); return (ENXIO); } sc->sysclk_freq = DEFAULT_FREQUENCY; /* Get the base clock frequency */ node = ofw_bus_get_node(dev); if ((OF_getencprop(node, "clock-frequency", &clock, sizeof(clock))) > 0) { sc->sysclk_freq = clock; } /* Setup and enable the timer */ if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_CLK, sp804_timer_intr, NULL, sc, &sc->intr_hl) != 0) { bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res); device_printf(dev, "Unable to setup the clock irq handler.\n"); return (ENXIO); } sp804_timer_tc_write_4(SP804_TIMER1_CONTROL, 0); sp804_timer_tc_write_4(SP804_TIMER2_CONTROL, 0); /* * Timer 1, timecounter */ sc->tc.tc_frequency = sc->sysclk_freq; sc->tc.tc_name = "SP804-1"; sc->tc.tc_get_timecount = sp804_timer_tc_get_timecount; sc->tc.tc_poll_pps = NULL; sc->tc.tc_counter_mask = ~0u; sc->tc.tc_quality = 1000; sc->tc.tc_priv = sc; sp804_timer_tc_write_4(SP804_TIMER1_VALUE, 0xffffffff); sp804_timer_tc_write_4(SP804_TIMER1_LOAD, 0xffffffff); reg = TIMER_CONTROL_PERIODIC | TIMER_CONTROL_32BIT; sp804_timer_tc_write_4(SP804_TIMER1_CONTROL, reg); reg |= TIMER_CONTROL_EN; sp804_timer_tc_write_4(SP804_TIMER1_CONTROL, reg); tc_init(&sc->tc); /* * Timer 2, event timer */ sc->et_enabled = 0; sc->et.et_name = "SP804-2"; sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT; sc->et.et_quality = 1000; sc->et.et_frequency = sc->sysclk_freq / DEFAULT_DIVISOR; sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency; sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; sc->et.et_start = sp804_timer_start; sc->et.et_stop = sp804_timer_stop; sc->et.et_priv = sc; et_register(&sc->et); id = 0; for (i = 3; i >= 0; i--) { id = (id << 8) | (sp804_timer_tc_read_4(SP804_PERIPH_ID0 + i*4) & 0xff); } device_printf(dev, "peripheral ID: %08x\n", id); id = 0; for (i = 3; i >= 0; i--) { id = (id << 8) | (sp804_timer_tc_read_4(SP804_PRIMECELL_ID0 + i*4) & 0xff); } -#ifdef MULTIDELAY arm_set_delay(sp804_timer_delay, sc); -#endif device_printf(dev, "PrimeCell ID: %08x\n", id); sc->timer_initialized = 1; return (0); } static device_method_t sp804_timer_methods[] = { DEVMETHOD(device_probe, sp804_timer_probe), DEVMETHOD(device_attach, sp804_timer_attach), { 0, 0 } }; static driver_t sp804_timer_driver = { "timer", sp804_timer_methods, sizeof(struct sp804_timer_softc), }; static devclass_t sp804_timer_devclass; DRIVER_MODULE(sp804_timer, simplebus, sp804_timer_driver, sp804_timer_devclass, 0, 0); static void sp804_timer_delay(int usec, void *arg) { struct sp804_timer_softc *sc = arg; int32_t counts; uint32_t first, last; /* Get the number of times to count */ counts = usec * ((sc->tc.tc_frequency / 1000000) + 1); first = sp804_timer_tc_get_timecount(&sc->tc); while (counts > 0) { last = sp804_timer_tc_get_timecount(&sc->tc); if (last == first) continue; if (last > first) { counts -= (int32_t)(last - first); } else { counts -= (int32_t)((0xFFFFFFFF - first) + last); } first = last; } } - -#ifndef MULTIDELAY -void -DELAY(int usec) -{ - int32_t counts; - device_t timer_dev; - struct sp804_timer_softc *sc; - int timer_initialized = 0; - - timer_dev = devclass_get_device(sp804_timer_devclass, 0); - - if (timer_dev) { - sc = device_get_softc(timer_dev); - - if (sc) - timer_initialized = sc->timer_initialized; - } - - if (!timer_initialized) { - /* - * Timer is not initialized yet - */ - for (; usec > 0; usec--) - for (counts = 200; counts > 0; counts--) - /* Prevent gcc from optimizing out the loop */ - cpufunc_nullop(); - } else { - sp804_timer_delay(usec, sc); - } -} -#endif