Index: stable/10/sys/arm/ti/am335x/am335x_dmtimer.c =================================================================== --- stable/10/sys/arm/ti/am335x/am335x_dmtimer.c (revision 283337) +++ stable/10/sys/arm/ti/am335x/am335x_dmtimer.c (revision 283338) @@ -1,696 +1,696 @@ /*- * 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 #include #include #include #include "opt_ntp.h" #include #include #include #include #include #include #include #include #define AM335X_NUM_TIMERS 8 #define DMT_TIDR 0x00 /* Identification Register */ #define DMT_TIOCP_CFG 0x10 /* OCP Configuration Reg */ #define DMT_TIOCP_RESET (1 << 0) /* TIOCP perform soft reset */ #define DMT_IQR_EOI 0x20 /* IRQ End-Of-Interrupt Reg */ #define DMT_IRQSTATUS_RAW 0x24 /* IRQSTATUS Raw Reg */ #define DMT_IRQSTATUS 0x28 /* IRQSTATUS Reg */ #define DMT_IRQENABLE_SET 0x2c /* IRQSTATUS Set Reg */ #define DMT_IRQENABLE_CLR 0x30 /* IRQSTATUS Clear Reg */ #define DMT_IRQWAKEEN 0x34 /* IRQ Wakeup Enable Reg */ #define DMT_IRQ_MAT (1 << 0) /* IRQ: Match */ #define DMT_IRQ_OVF (1 << 1) /* IRQ: Overflow */ #define DMT_IRQ_TCAR (1 << 2) /* IRQ: Capture */ #define DMT_IRQ_MASK (DMT_IRQ_TCAR | DMT_IRQ_OVF | DMT_IRQ_MAT) #define DMT_TCLR 0x38 /* Control Register */ #define DMT_TCLR_START (1 << 0) /* Start timer */ #define DMT_TCLR_AUTOLOAD (1 << 1) /* Auto-reload on overflow */ #define DMT_TCLR_PRES_MASK (7 << 2) /* Prescaler mask */ #define DMT_TCLR_PRES_ENABLE (1 << 5) /* Prescaler enable */ #define DMT_TCLR_COMP_ENABLE (1 << 6) /* Compare enable */ #define DMT_TCLR_PWM_HIGH (1 << 7) /* PWM default output high */ #define DMT_TCLR_CAPTRAN_MASK (3 << 8) /* Capture transition mask */ #define DMT_TCLR_CAPTRAN_NONE (0 << 8) /* Capture: none */ #define DMT_TCLR_CAPTRAN_LOHI (1 << 8) /* Capture lo->hi transition */ #define DMT_TCLR_CAPTRAN_HILO (2 << 8) /* Capture hi->lo transition */ #define DMT_TCLR_CAPTRAN_BOTH (3 << 8) /* Capture both transitions */ #define DMT_TCLR_TRGMODE_MASK (3 << 10) /* Trigger output mode mask */ #define DMT_TCLR_TRGMODE_NONE (0 << 10) /* Trigger off */ #define DMT_TCLR_TRGMODE_OVFL (1 << 10) /* Trigger on overflow */ #define DMT_TCLR_TRGMODE_BOTH (2 << 10) /* Trigger on match + ovflow */ #define DMT_TCLR_PWM_PTOGGLE (1 << 12) /* PWM toggles */ #define DMT_TCLR_CAP_MODE_2ND (1 << 13) /* Capture second event mode */ #define DMT_TCLR_GPO_CFG (1 << 14) /* (no descr in datasheet) */ #define DMT_TCRR 0x3C /* Counter Register */ #define DMT_TLDR 0x40 /* Load Reg */ #define DMT_TTGR 0x44 /* Trigger Reg */ #define DMT_TWPS 0x48 /* Write Posted Status Reg */ #define DMT_TMAR 0x4C /* Match Reg */ #define DMT_TCAR1 0x50 /* Capture Reg */ #define DMT_TSICR 0x54 /* Synchr. Interface Ctrl Reg */ #define DMT_TSICR_RESET (1 << 1) /* TSICR perform soft reset */ #define DMT_TCAR2 0x48 /* Capture Reg */ /* * Use timer 2 for the eventtimer. When PPS support is not compiled in, there's * no need to use a timer that has an associated capture-input pin, so use timer * 3 for timecounter. When PPS is compiled in we ignore the default and use * whichever of timers 4-7 have the capture pin configured. */ #define DEFAULT_ET_TIMER 2 #define DEFAULT_TC_TIMER 3 struct am335x_dmtimer_softc { struct resource * tmr_mem_res[AM335X_NUM_TIMERS]; struct resource * tmr_irq_res[AM335X_NUM_TIMERS]; uint32_t sysclk_freq; uint32_t tc_num; /* Which timer number is tc. */ uint32_t tc_tclr; /* Cached tc TCLR register. */ struct resource * tc_memres; /* Resources for tc timer. */ uint32_t et_num; /* Which timer number is et. */ uint32_t et_tclr; /* Cached et TCLR register. */ struct resource * et_memres; /* Resources for et timer. */ 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; struct timecounter tc; struct eventtimer et; }; static struct am335x_dmtimer_softc *am335x_dmtimer_sc; static struct resource_spec am335x_dmtimer_mem_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { SYS_RES_MEMORY, 2, RF_ACTIVE }, { SYS_RES_MEMORY, 3, RF_ACTIVE }, { SYS_RES_MEMORY, 4, RF_ACTIVE }, { SYS_RES_MEMORY, 5, RF_ACTIVE }, { SYS_RES_MEMORY, 6, RF_ACTIVE }, { SYS_RES_MEMORY, 7, RF_ACTIVE }, { -1, 0, 0 } }; static struct resource_spec am335x_dmtimer_irq_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { SYS_RES_IRQ, 3, RF_ACTIVE }, { SYS_RES_IRQ, 4, RF_ACTIVE }, { SYS_RES_IRQ, 5, RF_ACTIVE }, { SYS_RES_IRQ, 6, RF_ACTIVE }, { SYS_RES_IRQ, 7, RF_ACTIVE }, { -1, 0, 0 } }; static inline uint32_t am335x_dmtimer_tc_read_4(struct am335x_dmtimer_softc *sc, uint32_t reg) { return (bus_read_4(sc->tc_memres, reg)); } static inline void am335x_dmtimer_tc_write_4(struct am335x_dmtimer_softc *sc, uint32_t reg, uint32_t val) { bus_write_4(sc->tc_memres, reg, val); } static inline uint32_t am335x_dmtimer_et_read_4(struct am335x_dmtimer_softc *sc, uint32_t reg) { return (bus_read_4(sc->et_memres, reg)); } static inline void am335x_dmtimer_et_write_4(struct am335x_dmtimer_softc *sc, uint32_t reg, uint32_t val) { bus_write_4(sc->et_memres, reg, val); } /* * PPS driver routines, included when the kernel is built with option PPS_SYNC. * * 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()) is * scheduled to be done later in a non-interrupt context. */ #ifdef PPS_SYNC -#define PPS_CDEV_NAME "pps" +#define PPS_CDEV_NAME "dmtpps" static void am335x_dmtimer_set_capture_mode(struct am335x_dmtimer_softc *sc, bool force_off) { int newmode; if (force_off) newmode = 0; else newmode = sc->pps.ppsparam.mode & PPS_CAPTUREBOTH; if (newmode == sc->pps_curmode) return; sc->pps_curmode = newmode; sc->tc_tclr &= ~DMT_TCLR_CAPTRAN_MASK; switch (newmode) { case PPS_CAPTUREASSERT: sc->tc_tclr |= DMT_TCLR_CAPTRAN_LOHI; break; case PPS_CAPTURECLEAR: sc->tc_tclr |= DMT_TCLR_CAPTRAN_HILO; break; default: /* It can't be BOTH, so it's disabled. */ break; } am335x_dmtimer_tc_write_4(sc, DMT_TCLR, sc->tc_tclr); } static void am335x_dmtimer_tc_poll_pps(struct timecounter *tc) { struct am335x_dmtimer_softc *sc; sc = tc->tc_priv; /* * 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 (am335x_dmtimer_tc_read_4(sc, DMT_IRQSTATUS_RAW) & DMT_IRQ_TCAR) { pps_capture(&sc->pps); sc->pps.capcount = am335x_dmtimer_tc_read_4(sc, DMT_TCAR1); am335x_dmtimer_tc_write_4(sc, DMT_IRQSTATUS, DMT_IRQ_TCAR); taskqueue_enqueue_fast(taskqueue_fast, &sc->pps_task); } } static void am335x_dmtimer_process_pps_event(void *arg, int pending) { struct am335x_dmtimer_softc *sc; sc = arg; /* This is the task function that gets enqueued by poll_pps. Once the * time has been captured in the hw interrupt context, the remaining * (more expensive) work to process the event is done later in a * non-fast-interrupt context. * * We only support capture of the rising or falling edge, not both at * once; tell the kernel to process whichever mode is currently active. */ pps_event(&sc->pps, sc->pps.ppsparam.mode & PPS_CAPTUREBOTH); } static int am335x_dmtimer_pps_open(struct cdev *dev, int flags, int fmt, struct thread *td) { struct am335x_dmtimer_softc *sc; sc = dev->si_drv1; /* Enable capture on open. Harmless if already open. */ am335x_dmtimer_set_capture_mode(sc, 0); return 0; } static int am335x_dmtimer_pps_close(struct cdev *dev, int flags, int fmt, struct thread *td) { struct am335x_dmtimer_softc *sc; sc = dev->si_drv1; /* * Disable capture on last close. Use the force-off flag to override * the configured mode and turn off the hardware capture. */ am335x_dmtimer_set_capture_mode(sc, 1); return 0; } static int am335x_dmtimer_pps_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) { struct am335x_dmtimer_softc *sc; int err; sc = dev->si_drv1; /* * The hardware has a "capture both edges" mode, but we can't do * anything useful with it in terms of PPS capture, so don't even try. */ if ((sc->pps.ppsparam.mode & PPS_CAPTUREBOTH) == PPS_CAPTUREBOTH) return (EINVAL); /* Let the kernel do the heavy lifting for ioctl. */ err = pps_ioctl(cmd, data, &sc->pps); 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. */ am335x_dmtimer_set_capture_mode(sc, 0); return (err); } static struct cdevsw am335x_dmtimer_pps_cdevsw = { .d_version = D_VERSION, .d_open = am335x_dmtimer_pps_open, .d_close = am335x_dmtimer_pps_close, .d_ioctl = am335x_dmtimer_pps_ioctl, .d_name = PPS_CDEV_NAME, }; /* * Set up the PPS cdev and the the kernel timepps stuff. * * Note that this routine cannot touch the hardware, because bus space resources * are not fully set up yet when this is called. */ static int am335x_dmtimer_pps_init(device_t dev, struct am335x_dmtimer_softc *sc) { int i, timer_num, unit; unsigned int padstate; const char * padmux; struct padinfo { char * ballname; char * muxname; int timer_num; } padinfo[] = { {"GPMC_ADVn_ALE", "timer4", 4}, {"GPMC_BEn0_CLE", "timer5", 5}, {"GPMC_WEn", "timer6", 6}, {"GPMC_OEn_REn", "timer7", 7}, }; /* * Figure out which pin the user has set up for pps. We'll use the * first timer that has an external caputure pin configured as input. * * XXX The hieroglyphic "(padstate & (0x01 << 5)))" checks that the pin * is configured for input. The right symbolic values aren't exported * yet from ti_scm.h. */ timer_num = 0; for (i = 0; i < nitems(padinfo) && timer_num == 0; ++i) { if (ti_scm_padconf_get(padinfo[i].ballname, &padmux, &padstate) == 0) { if (strcasecmp(padinfo[i].muxname, padmux) == 0 && (padstate & (0x01 << 5))) timer_num = padinfo[i].timer_num; } } if (timer_num == 0) { device_printf(dev, "No DMTimer found with capture pin " "configured as input; PPS driver disabled.\n"); return (DEFAULT_TC_TIMER); } /* * Indicate our capabilities (pretty much just capture of either edge). * Have the kernel init its part of the pps_state struct and add its * capabilities. */ sc->pps.ppscap = PPS_CAPTUREBOTH; pps_init(&sc->pps); /* * Set up to capture the PPS via timecounter polling, and init the task * that does deferred pps_event() processing after capture. */ sc->tc.tc_poll_pps = am335x_dmtimer_tc_poll_pps; TASK_INIT(&sc->pps_task, 0, am335x_dmtimer_process_pps_event, sc); /* Create the PPS cdev. */ unit = device_get_unit(dev); sc->pps_cdev = make_dev(&am335x_dmtimer_pps_cdevsw, unit, UID_ROOT, GID_WHEEL, 0600, PPS_CDEV_NAME "%d", unit); sc->pps_cdev->si_drv1 = sc; device_printf(dev, "Using DMTimer%d for PPS device /dev/%s%d\n", timer_num, PPS_CDEV_NAME, unit); return (timer_num); } #else /* PPS_SYNC */ static int am335x_dmtimer_pps_init(device_t dev, struct am335x_dmtimer_softc *sc) { /* * When PPS support is not compiled in, there's no need to use a timer * that has an associated capture-input pin, so use the default. */ return (DEFAULT_TC_TIMER); } #endif /* PPS_SYNC */ /* * End of PPS driver code. */ static unsigned am335x_dmtimer_tc_get_timecount(struct timecounter *tc) { struct am335x_dmtimer_softc *sc; sc = tc->tc_priv; return (am335x_dmtimer_tc_read_4(sc, DMT_TCRR)); } static int am335x_dmtimer_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->et_tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD); am335x_dmtimer_et_write_4(sc, DMT_TCLR, sc->et_tclr); am335x_dmtimer_et_write_4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); if (period != 0) { reload_count = ((uint32_t)et->et_frequency * period) >> 32; sc->et_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. */ am335x_dmtimer_et_write_4(sc, DMT_TLDR, 0xFFFFFFFF - reload_count); am335x_dmtimer_et_write_4(sc, DMT_TCRR, 0xFFFFFFFF - initial_count); /* Enable overflow interrupt, and start the timer. */ am335x_dmtimer_et_write_4(sc, DMT_IRQENABLE_SET, DMT_IRQ_OVF); sc->et_tclr |= DMT_TCLR_START; am335x_dmtimer_et_write_4(sc, DMT_TCLR, sc->et_tclr); return (0); } static int am335x_dmtimer_stop(struct eventtimer *et) { struct am335x_dmtimer_softc *sc; sc = et->et_priv; /* Stop timer, disable and clear interrupt. */ sc->et_tclr &= ~(DMT_TCLR_START | DMT_TCLR_AUTOLOAD); am335x_dmtimer_et_write_4(sc, DMT_TCLR, sc->et_tclr); am335x_dmtimer_et_write_4(sc, DMT_IRQENABLE_CLR, DMT_IRQ_OVF); am335x_dmtimer_et_write_4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); return (0); } static int am335x_dmtimer_intr(void *arg) { struct am335x_dmtimer_softc *sc; sc = arg; /* Ack the interrupt, and invoke the callback if it's still enabled. */ am335x_dmtimer_et_write_4(sc, DMT_IRQSTATUS, DMT_IRQ_OVF); if (sc->et.et_active) sc->et.et_event_cb(&sc->et, sc->et.et_arg); return (FILTER_HANDLED); } static int am335x_dmtimer_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "ti,am335x-dmtimer")) { device_set_desc(dev, "AM335x DMTimer"); return(BUS_PROBE_DEFAULT); } return (ENXIO); } static int am335x_dmtimer_attach(device_t dev) { struct am335x_dmtimer_softc *sc; void *ihl; int err; /* * Note that if this routine returns an error status rather than running * to completion it makes no attempt to clean up allocated resources; * the system is essentially dead anyway without functional timers. */ sc = device_get_softc(dev); if (am335x_dmtimer_sc != NULL) return (EINVAL); /* Get the base clock frequency. */ err = ti_prcm_clk_get_source_freq(SYS_CLK, &sc->sysclk_freq); if (err) { device_printf(dev, "Error: could not get sysclk frequency\n"); return (ENXIO); } /* Request the memory resources. */ err = bus_alloc_resources(dev, am335x_dmtimer_mem_spec, sc->tmr_mem_res); if (err) { device_printf(dev, "Error: could not allocate mem resources\n"); return (ENXIO); } /* Request the IRQ resources. */ err = bus_alloc_resources(dev, am335x_dmtimer_irq_spec, sc->tmr_irq_res); if (err) { device_printf(dev, "Error: could not allocate irq resources\n"); return (ENXIO); } /* * Use the default eventtimer. Let the PPS init routine decide which * timer to use for the timecounter. */ sc->et_num = DEFAULT_ET_TIMER; sc->tc_num = am335x_dmtimer_pps_init(dev, sc); sc->et_memres = sc->tmr_mem_res[sc->et_num]; sc->tc_memres = sc->tmr_mem_res[sc->tc_num]; /* Enable clocks and power on the chosen devices. */ err = ti_prcm_clk_set_source(DMTIMER0_CLK + sc->et_num, SYSCLK_CLK); err |= ti_prcm_clk_enable(DMTIMER0_CLK + sc->et_num); err |= ti_prcm_clk_set_source(DMTIMER0_CLK + sc->tc_num, SYSCLK_CLK); err |= ti_prcm_clk_enable(DMTIMER0_CLK + sc->tc_num); if (err) { device_printf(dev, "Error: could not enable timer clock\n"); return (ENXIO); } /* Setup eventtimer interrupt handler. */ if (bus_setup_intr(dev, sc->tmr_irq_res[sc->et_num], INTR_TYPE_CLK, am335x_dmtimer_intr, NULL, sc, &ihl) != 0) { device_printf(dev, "Unable to setup the clock irq handler.\n"); return (ENXIO); } /* Set up timecounter, start it, register it. */ am335x_dmtimer_tc_write_4(sc, DMT_TSICR, DMT_TSICR_RESET); while (am335x_dmtimer_tc_read_4(sc, DMT_TIOCP_CFG) & DMT_TIOCP_RESET) continue; sc->tc_tclr |= DMT_TCLR_START | DMT_TCLR_AUTOLOAD; am335x_dmtimer_tc_write_4(sc, DMT_TLDR, 0); am335x_dmtimer_tc_write_4(sc, DMT_TCRR, 0); am335x_dmtimer_tc_write_4(sc, DMT_TCLR, sc->tc_tclr); sc->tc.tc_name = "AM335x Timecounter"; sc->tc.tc_get_timecount = am335x_dmtimer_tc_get_timecount; sc->tc.tc_counter_mask = ~0u; sc->tc.tc_frequency = sc->sysclk_freq; sc->tc.tc_quality = 1000; sc->tc.tc_priv = sc; tc_init(&sc->tc); sc->et.et_name = "AM335x Eventtimer"; sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT; sc->et.et_quality = 1000; sc->et.et_frequency = sc->sysclk_freq; sc->et.et_min_period = ((0x00000005LLU << 32) / sc->et.et_frequency); sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; sc->et.et_start = am335x_dmtimer_start; sc->et.et_stop = am335x_dmtimer_stop; sc->et.et_priv = sc; et_register(&sc->et); /* Store a pointer to the softc for use in DELAY(). */ am335x_dmtimer_sc = sc; 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); void DELAY(int usec) { struct am335x_dmtimer_softc *sc; int32_t counts; uint32_t first, last; sc = am335x_dmtimer_sc; if (sc == NULL) { for (; usec > 0; usec--) for (counts = 200; counts > 0; counts--) /* Prevent gcc from optimizing out the loop */ cpufunc_nullop(); return; } /* Get the number of times to count */ counts = (usec + 1) * (sc->sysclk_freq / 1000000); first = am335x_dmtimer_tc_read_4(sc, DMT_TCRR); while (counts > 0) { last = am335x_dmtimer_tc_read_4(sc, DMT_TCRR); if (last > first) { counts -= (int32_t)(last - first); } else { counts -= (int32_t)((0xFFFFFFFF - first) + last); } first = last; } } Index: stable/10/sys/arm/ti/omap4/omap4_prcm_clks.c =================================================================== --- stable/10/sys/arm/ti/omap4/omap4_prcm_clks.c (revision 283337) +++ stable/10/sys/arm/ti/omap4/omap4_prcm_clks.c (revision 283338) @@ -1,1427 +1,1428 @@ /*- * Copyright (c) 2011 * Ben Gray . * 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. 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 BEN GRAY ``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 BEN GRAY 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 #include #include #include #include /* * This file defines the clock configuration for the OMAP4xxx series of * devices. * * How This is Suppose to Work * =========================== * - There is a top level omap_prcm module that defines all OMAP SoC drivers * should use to enable/disable the system clocks regardless of the version * of OMAP device they are running on. This top level PRCM module is just * a thin shim to chip specific functions that perform the donkey work of * configuring the clock - this file is the 'donkey' for OMAP44xx devices. * * - The key bit in this file is the omap_clk_devmap array, it's * used by the omap_prcm driver to determine what clocks are valid and which * functions to call to manipulate them. * * - In essence you just need to define some callbacks for each of the * clocks and then you're done. * * - The other thing that is worth noting is that when the omap_prcm device * is registered you typically pass in some memory ranges which are the * SYS_MEMORY resources. These resources are in turn allocated using * bus_allocate_resources(...) and the resource handles are passed to all * individual clock callback handlers. * * * * OMAP4 devices are different from the previous OMAP3 devices in that there * is no longer a separate functional and interface clock for each module, * instead there is typically an interface clock that spans many modules. */ #define FREQ_96MHZ 96000000 #define FREQ_64MHZ 64000000 #define FREQ_48MHZ 48000000 #define FREQ_32KHZ 32000 /** * We need three memory regions to cover all the clock configuration registers. * * PRM Instance - 0x4A30 6000 : 0x4A30 8000 * CM1 Instance - 0x4A00 4000 : 0x4A00 5000 * CM2 Instance - 0x4A00 8000 : 0x4A00 A000 * */ #define PRM_INSTANCE_MEM_REGION 0 #define CM1_INSTANCE_MEM_REGION 1 #define CM2_INSTANCE_MEM_REGION 2 /** * Address offsets from the PRM memory region to the top level clock control * registers. */ #define CKGEN_PRM_OFFSET 0x00000100UL #define MPU_PRM_OFFSET 0x00000300UL #define DSP_PRM_OFFSET 0x00000400UL #define ABE_PRM_OFFSET 0x00000500UL #define ALWAYS_ON_PRM_OFFSET 0x00000600UL #define CORE_PRM_OFFSET 0x00000700UL #define IVAHD_PRM_OFFSET 0x00000F00UL #define CAM_PRM_OFFSET 0x00001000UL #define DSS_PRM_OFFSET 0x00001100UL #define SGX_PRM_OFFSET 0x00001200UL #define L3INIT_PRM_OFFSET 0x00001300UL #define L4PER_PRM_OFFSET 0x00001400UL #define WKUP_PRM_OFFSET 0x00001700UL #define WKUP_CM_OFFSET 0x00001800UL #define EMU_PRM_OFFSET 0x00001900UL #define EMU_CM_OFFSET 0x00001A00UL #define DEVICE_PRM_OFFSET 0x00001B00UL #define INSTR_PRM_OFFSET 0x00001F00UL #define CM_ABE_DSS_SYS_CLKSEL_OFFSET (CKGEN_PRM_OFFSET + 0x0000UL) #define CM_L4_WKUP_CLKSELL_OFFSET (CKGEN_PRM_OFFSET + 0x0008UL) #define CM_ABE_PLL_REF_CLKSEL_OFFSET (CKGEN_PRM_OFFSET + 0x000CUL) #define CM_SYS_CLKSEL_OFFSET (CKGEN_PRM_OFFSET + 0x0010UL) /** * Address offsets from the CM1 memory region to the top level clock control * registers. */ #define CKGEN_CM1_OFFSET 0x00000100UL #define MPU_CM1_OFFSET 0x00000300UL #define DSP_CM1_OFFSET 0x00000400UL #define ABE_CM1_OFFSET 0x00000500UL #define RESTORE_CM1_OFFSET 0x00000E00UL #define INSTR_CM1_OFFSET 0x00000F00UL #define CM_CLKSEL_DPLL_MPU (CKGEN_CM1_OFFSET + 0x006CUL) /** * Address offsets from the CM2 memory region to the top level clock control * registers. */ #define INTRCONN_SOCKET_CM2_OFFSET 0x00000000UL #define CKGEN_CM2_OFFSET 0x00000100UL #define ALWAYS_ON_CM2_OFFSET 0x00000600UL #define CORE_CM2_OFFSET 0x00000700UL #define IVAHD_CM2_OFFSET 0x00000F00UL #define CAM_CM2_OFFSET 0x00001000UL #define DSS_CM2_OFFSET 0x00001100UL #define SGX_CM2_OFFSET 0x00001200UL #define L3INIT_CM2_OFFSET 0x00001300UL #define L4PER_CM2_OFFSET 0x00001400UL #define RESTORE_CM2_OFFSET 0x00001E00UL #define INSTR_CM2_OFFSET 0x00001F00UL #define CLKCTRL_MODULEMODE_MASK 0x00000003UL #define CLKCTRL_MODULEMODE_DISABLE 0x00000000UL #define CLKCTRL_MODULEMODE_AUTO 0x00000001UL #define CLKCTRL_MODULEMODE_ENABLE 0x00000001UL #define CLKCTRL_IDLEST_MASK 0x00030000UL #define CLKCTRL_IDLEST_ENABLED 0x00000000UL #define CLKCTRL_IDLEST_WAKING 0x00010000UL #define CLKCTRL_IDLEST_IDLE 0x00020000UL #define CLKCTRL_IDLEST_DISABLED 0x00030000UL static struct resource_spec omap4_scm_res_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Control memory window */ { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* Control memory window */ { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* Control memory window */ { -1, 0 } }; struct omap4_prcm_softc { struct resource *sc_res[3]; }; static struct omap4_prcm_softc *omap4_prcm_sc; static int omap4_clk_generic_activate(struct ti_clock_dev *clkdev); static int omap4_clk_generic_deactivate(struct ti_clock_dev *clkdev); static int omap4_clk_generic_accessible(struct ti_clock_dev *clkdev); static int omap4_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); static int omap4_clk_generic_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq); static int omap4_clk_gptimer_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); static int omap4_clk_gptimer_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq); static int omap4_clk_hsmmc_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); static int omap4_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq); static int omap4_clk_hsusbhost_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); static int omap4_clk_hsusbhost_activate(struct ti_clock_dev *clkdev); static int omap4_clk_hsusbhost_deactivate(struct ti_clock_dev *clkdev); static int omap4_clk_hsusbhost_accessible(struct ti_clock_dev *clkdev); static int omap4_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); static int omap4_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); /** * omap_clk_devmap - Array of clock devices available on OMAP4xxx devices * * This map only defines which clocks are valid and the callback functions * for clock activate, deactivate, etc. It is used by the top level omap_prcm * driver. * * The actual details of the clocks (config registers, bit fields, sources, * etc) are in the private g_omap3_clk_details array below. * */ #define OMAP4_GENERIC_CLOCK_DEV(i) \ { .id = (i), \ .clk_activate = omap4_clk_generic_activate, \ .clk_deactivate = omap4_clk_generic_deactivate, \ .clk_set_source = omap4_clk_generic_set_source, \ .clk_accessible = omap4_clk_generic_accessible, \ .clk_get_source_freq = omap4_clk_generic_get_source_freq \ } #define OMAP4_GPTIMER_CLOCK_DEV(i) \ { .id = (i), \ .clk_activate = omap4_clk_generic_activate, \ .clk_deactivate = omap4_clk_generic_deactivate, \ .clk_set_source = omap4_clk_gptimer_set_source, \ .clk_accessible = omap4_clk_generic_accessible, \ .clk_get_source_freq = omap4_clk_gptimer_get_source_freq \ } #define OMAP4_HSMMC_CLOCK_DEV(i) \ { .id = (i), \ .clk_activate = omap4_clk_generic_activate, \ .clk_deactivate = omap4_clk_generic_deactivate, \ .clk_set_source = omap4_clk_hsmmc_set_source, \ .clk_accessible = omap4_clk_generic_accessible, \ .clk_get_source_freq = omap4_clk_hsmmc_get_source_freq \ } #define OMAP4_HSUSBHOST_CLOCK_DEV(i) \ { .id = (i), \ .clk_activate = omap4_clk_hsusbhost_activate, \ .clk_deactivate = omap4_clk_hsusbhost_deactivate, \ .clk_set_source = omap4_clk_hsusbhost_set_source, \ .clk_accessible = omap4_clk_hsusbhost_accessible, \ .clk_get_source_freq = NULL \ } struct ti_clock_dev ti_clk_devmap[] = { /* System clocks */ { .id = SYS_CLK, .clk_activate = NULL, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = omap4_clk_get_sysclk_freq, }, /* MPU (ARM) core clocks */ { .id = MPU_CLK, .clk_activate = NULL, .clk_deactivate = NULL, .clk_set_source = NULL, .clk_accessible = NULL, .clk_get_source_freq = omap4_clk_get_arm_fclk_freq, }, /* UART device clocks */ OMAP4_GENERIC_CLOCK_DEV(UART1_CLK), OMAP4_GENERIC_CLOCK_DEV(UART2_CLK), OMAP4_GENERIC_CLOCK_DEV(UART3_CLK), OMAP4_GENERIC_CLOCK_DEV(UART4_CLK), /* Timer device source clocks */ OMAP4_GPTIMER_CLOCK_DEV(GPTIMER1_CLK), OMAP4_GPTIMER_CLOCK_DEV(GPTIMER2_CLK), OMAP4_GPTIMER_CLOCK_DEV(GPTIMER3_CLK), OMAP4_GPTIMER_CLOCK_DEV(GPTIMER4_CLK), OMAP4_GPTIMER_CLOCK_DEV(GPTIMER5_CLK), OMAP4_GPTIMER_CLOCK_DEV(GPTIMER6_CLK), OMAP4_GPTIMER_CLOCK_DEV(GPTIMER7_CLK), OMAP4_GPTIMER_CLOCK_DEV(GPTIMER8_CLK), OMAP4_GPTIMER_CLOCK_DEV(GPTIMER9_CLK), OMAP4_GPTIMER_CLOCK_DEV(GPTIMER10_CLK), OMAP4_GPTIMER_CLOCK_DEV(GPTIMER11_CLK), /* MMC device clocks (MMC1 and MMC2 can have different input clocks) */ OMAP4_HSMMC_CLOCK_DEV(MMC1_CLK), OMAP4_HSMMC_CLOCK_DEV(MMC2_CLK), OMAP4_GENERIC_CLOCK_DEV(MMC3_CLK), OMAP4_GENERIC_CLOCK_DEV(MMC4_CLK), OMAP4_GENERIC_CLOCK_DEV(MMC5_CLK), /* USB HS (high speed TLL, EHCI and OHCI) */ OMAP4_HSUSBHOST_CLOCK_DEV(USBTLL_CLK), OMAP4_HSUSBHOST_CLOCK_DEV(USBHSHOST_CLK), OMAP4_HSUSBHOST_CLOCK_DEV(USBFSHOST_CLK), OMAP4_HSUSBHOST_CLOCK_DEV(USBP1_PHY_CLK), OMAP4_HSUSBHOST_CLOCK_DEV(USBP2_PHY_CLK), OMAP4_HSUSBHOST_CLOCK_DEV(USBP1_UTMI_CLK), OMAP4_HSUSBHOST_CLOCK_DEV(USBP2_UTMI_CLK), OMAP4_HSUSBHOST_CLOCK_DEV(USBP1_HSIC_CLK), OMAP4_HSUSBHOST_CLOCK_DEV(USBP2_HSIC_CLK), /* GPIO */ OMAP4_GENERIC_CLOCK_DEV(GPIO1_CLK), OMAP4_GENERIC_CLOCK_DEV(GPIO2_CLK), OMAP4_GENERIC_CLOCK_DEV(GPIO3_CLK), OMAP4_GENERIC_CLOCK_DEV(GPIO4_CLK), OMAP4_GENERIC_CLOCK_DEV(GPIO5_CLK), OMAP4_GENERIC_CLOCK_DEV(GPIO6_CLK), /* sDMA */ OMAP4_GENERIC_CLOCK_DEV(SDMA_CLK), /* I2C */ OMAP4_GENERIC_CLOCK_DEV(I2C1_CLK), OMAP4_GENERIC_CLOCK_DEV(I2C2_CLK), OMAP4_GENERIC_CLOCK_DEV(I2C3_CLK), OMAP4_GENERIC_CLOCK_DEV(I2C4_CLK), { INVALID_CLK_IDENT, NULL, NULL, NULL, NULL } }; /** * omap4_clk_details - Stores details for all the different clocks supported * * Whenever an operation on a clock is being performed (activated, deactivated, * etc) this array is looked up to find the correct register and bit(s) we * should be modifying. * */ struct omap4_clk_details { clk_ident_t id; uint32_t mem_region; uint32_t clksel_reg; int32_t src_freq; uint32_t enable_mode; }; #define OMAP4_GENERIC_CLOCK_DETAILS(i, f, m, r, e) \ { .id = (i), \ .mem_region = (m), \ .clksel_reg = (r), \ .src_freq = (f), \ .enable_mode = (e), \ } static struct omap4_clk_details g_omap4_clk_details[] = { /* UART */ OMAP4_GENERIC_CLOCK_DETAILS(UART1_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x0140), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(UART2_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x0148), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(UART3_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x0140), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(UART4_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x0148), CLKCTRL_MODULEMODE_ENABLE), /* General purpose timers */ OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER1_CLK, -1, PRM_INSTANCE_MEM_REGION, (WKUP_CM_OFFSET + 0x040), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER2_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x038), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER3_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x040), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER4_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x048), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER5_CLK, -1, CM1_INSTANCE_MEM_REGION, (ABE_CM1_OFFSET + 0x068), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER6_CLK, -1, CM1_INSTANCE_MEM_REGION, (ABE_CM1_OFFSET + 0x070), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER7_CLK, -1, CM1_INSTANCE_MEM_REGION, (ABE_CM1_OFFSET + 0x078), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER8_CLK, -1, CM1_INSTANCE_MEM_REGION, (ABE_CM1_OFFSET + 0x080), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER9_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x050), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER10_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x028), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(GPTIMER11_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x030), CLKCTRL_MODULEMODE_ENABLE), /* HSMMC (MMC1 and MMC2 can have different input clocks) */ OMAP4_GENERIC_CLOCK_DETAILS(MMC1_CLK, -1, CM2_INSTANCE_MEM_REGION, (L3INIT_CM2_OFFSET + 0x028), /*CLKCTRL_MODULEMODE_ENABLE*/2), OMAP4_GENERIC_CLOCK_DETAILS(MMC2_CLK, -1, CM2_INSTANCE_MEM_REGION, (L3INIT_CM2_OFFSET + 0x030), /*CLKCTRL_MODULEMODE_ENABLE*/2), OMAP4_GENERIC_CLOCK_DETAILS(MMC3_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x120), /*CLKCTRL_MODULEMODE_ENABLE*/2), OMAP4_GENERIC_CLOCK_DETAILS(MMC4_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x128), /*CLKCTRL_MODULEMODE_ENABLE*/2), OMAP4_GENERIC_CLOCK_DETAILS(MMC5_CLK, FREQ_48MHZ, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x160), /*CLKCTRL_MODULEMODE_ENABLE*/1), /* GPIO modules */ OMAP4_GENERIC_CLOCK_DETAILS(GPIO1_CLK, -1, PRM_INSTANCE_MEM_REGION, (WKUP_CM_OFFSET + 0x038), CLKCTRL_MODULEMODE_AUTO), OMAP4_GENERIC_CLOCK_DETAILS(GPIO2_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x060), CLKCTRL_MODULEMODE_AUTO), OMAP4_GENERIC_CLOCK_DETAILS(GPIO3_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x068), CLKCTRL_MODULEMODE_AUTO), OMAP4_GENERIC_CLOCK_DETAILS(GPIO4_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x070), CLKCTRL_MODULEMODE_AUTO), OMAP4_GENERIC_CLOCK_DETAILS(GPIO5_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x078), CLKCTRL_MODULEMODE_AUTO), OMAP4_GENERIC_CLOCK_DETAILS(GPIO6_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x080), CLKCTRL_MODULEMODE_AUTO), /* sDMA block */ OMAP4_GENERIC_CLOCK_DETAILS(SDMA_CLK, -1, CM2_INSTANCE_MEM_REGION, (CORE_CM2_OFFSET + 0x300), CLKCTRL_MODULEMODE_AUTO), /* I2C modules */ OMAP4_GENERIC_CLOCK_DETAILS(I2C1_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x0A0), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(I2C2_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x0A8), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(I2C3_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x0B0), CLKCTRL_MODULEMODE_ENABLE), OMAP4_GENERIC_CLOCK_DETAILS(I2C4_CLK, -1, CM2_INSTANCE_MEM_REGION, (L4PER_CM2_OFFSET + 0x0B8), CLKCTRL_MODULEMODE_ENABLE), { INVALID_CLK_IDENT, 0, 0, 0, 0 }, }; /** * MAX_MODULE_ENABLE_WAIT - the number of loops to wait for the module to come * alive. * */ #define MAX_MODULE_ENABLE_WAIT 100 /** * ARRAY_SIZE - Macro to return the number of elements in a static const array. * */ #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) /** * omap4_clk_details - writes a 32-bit value to one of the timer registers * @timer: Timer device context * @off: The offset of a register from the timer register address range * @val: The value to write into the register * * * RETURNS: * nothing */ static struct omap4_clk_details* omap4_clk_details(clk_ident_t id) { struct omap4_clk_details *walker; for (walker = g_omap4_clk_details; walker->id != INVALID_CLK_IDENT; walker++) { if (id == walker->id) return (walker); } return NULL; } /** * omap4_clk_generic_activate - checks if a module is accessible * @module: identifier for the module to check, see omap3_prcm.h for a list * of possible modules. * Example: OMAP3_MODULE_MMC1 * * * * LOCKING: * Inherits the locks from the omap_prcm driver, no internal locking. * * RETURNS: * Returns 0 on success or a positive error code on failure. */ static int omap4_clk_generic_activate(struct ti_clock_dev *clkdev) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct omap4_clk_details* clk_details; struct resource* clk_mem_res; uint32_t clksel; unsigned int i; if (sc == NULL) return ENXIO; clk_details = omap4_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); clk_mem_res = sc->sc_res[clk_details->mem_region]; if (clk_mem_res == NULL) return (EINVAL); /* All the 'generic' clocks have a CLKCTRL register which is more or less * generic - the have at least two fielda called MODULEMODE and IDLEST. */ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); clksel &= ~CLKCTRL_MODULEMODE_MASK; clksel |= clk_details->enable_mode; bus_write_4(clk_mem_res, clk_details->clksel_reg, clksel); /* Now poll on the IDLEST register to tell us if the module has come up. * TODO: We need to take into account the parent clocks. */ /* Try MAX_MODULE_ENABLE_WAIT number of times to check if enabled */ for (i = 0; i < MAX_MODULE_ENABLE_WAIT; i++) { clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); if ((clksel & CLKCTRL_IDLEST_MASK) == CLKCTRL_IDLEST_ENABLED) break; DELAY(10); } /* Check the enabled state */ if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED) { printf("Error: failed to enable module with clock %d\n", clkdev->id); printf("Error: 0x%08x => 0x%08x\n", clk_details->clksel_reg, clksel); return (ETIMEDOUT); } return (0); } /** * omap4_clk_generic_deactivate - checks if a module is accessible * @module: identifier for the module to check, see omap3_prcm.h for a list * of possible modules. * Example: OMAP3_MODULE_MMC1 * * * * LOCKING: * Inherits the locks from the omap_prcm driver, no internal locking. * * RETURNS: * Returns 0 on success or a positive error code on failure. */ static int omap4_clk_generic_deactivate(struct ti_clock_dev *clkdev) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct omap4_clk_details* clk_details; struct resource* clk_mem_res; uint32_t clksel; if (sc == NULL) return ENXIO; clk_details = omap4_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); clk_mem_res = sc->sc_res[clk_details->mem_region]; if (clk_mem_res == NULL) return (EINVAL); /* All the 'generic' clocks have a CLKCTRL register which is more or less * generic - the have at least two fielda called MODULEMODE and IDLEST. */ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); clksel &= ~CLKCTRL_MODULEMODE_MASK; clksel |= CLKCTRL_MODULEMODE_DISABLE; bus_write_4(clk_mem_res, clk_details->clksel_reg, clksel); return (0); } /** * omap4_clk_generic_set_source - checks if a module is accessible * @module: identifier for the module to check, see omap3_prcm.h for a list * of possible modules. * Example: OMAP3_MODULE_MMC1 * * * * LOCKING: * Inherits the locks from the omap_prcm driver, no internal locking. * * RETURNS: * Returns 0 on success or a positive error code on failure. */ static int omap4_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) { return (0); } /** * omap4_clk_generic_accessible - checks if a module is accessible * @module: identifier for the module to check, see omap3_prcm.h for a list * of possible modules. * Example: OMAP3_MODULE_MMC1 * * * * LOCKING: * Inherits the locks from the omap_prcm driver, no internal locking. * * RETURNS: * Returns 0 on success or a negative error code on failure. */ static int omap4_clk_generic_accessible(struct ti_clock_dev *clkdev) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct omap4_clk_details* clk_details; struct resource* clk_mem_res; uint32_t clksel; if (sc == NULL) return ENXIO; clk_details = omap4_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); clk_mem_res = sc->sc_res[clk_details->mem_region]; if (clk_mem_res == NULL) return (EINVAL); clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); /* Check the enabled state */ if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED) return (0); return (1); } /** * omap4_clk_generic_get_source_freq - checks if a module is accessible * @module: identifier for the module to check, see omap3_prcm.h for a list * of possible modules. * Example: OMAP3_MODULE_MMC1 * * * * LOCKING: * Inherits the locks from the omap_prcm driver, no internal locking. * * RETURNS: * Returns 0 on success or a negative error code on failure. */ static int omap4_clk_generic_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq ) { struct omap4_clk_details* clk_details = omap4_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); /* Simply return the stored frequency */ if (freq) *freq = (unsigned int)clk_details->src_freq; return (0); } /** * omap4_clk_gptimer_set_source - checks if a module is accessible * @module: identifier for the module to check, see omap3_prcm.h for a list * of possible modules. * Example: OMAP3_MODULE_MMC1 * * * * LOCKING: * Inherits the locks from the omap_prcm driver, no internal locking. * * RETURNS: * Returns 0 on success or a negative error code on failure. */ static int omap4_clk_gptimer_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct omap4_clk_details* clk_details; struct resource* clk_mem_res; if (sc == NULL) return ENXIO; clk_details = omap4_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); clk_mem_res = sc->sc_res[clk_details->mem_region]; if (clk_mem_res == NULL) return (EINVAL); /* TODO: Implement */ return (0); } /** * omap4_clk_gptimer_get_source_freq - checks if a module is accessible * @module: identifier for the module to check, see omap3_prcm.h for a list * of possible modules. * Example: OMAP3_MODULE_MMC1 * * * * LOCKING: * Inherits the locks from the omap_prcm driver, no internal locking. * * RETURNS: * Returns 0 on success or a negative error code on failure. */ static int omap4_clk_gptimer_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq ) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct omap4_clk_details* clk_details; struct resource* clk_mem_res; uint32_t clksel; unsigned int src_freq; if (sc == NULL) return ENXIO; clk_details = omap4_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); clk_mem_res = sc->sc_res[clk_details->mem_region]; if (clk_mem_res == NULL) return (EINVAL); /* Need to read the CLKSEL field to determine the clock source */ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); if (clksel & (0x1UL << 24)) src_freq = FREQ_32KHZ; else omap4_clk_get_sysclk_freq(NULL, &src_freq); /* Return the frequency */ if (freq) *freq = src_freq; return (0); } /** * omap4_clk_hsmmc_set_source - sets the source clock (freq) * @clkdev: pointer to the clockdev structure (id field will contain clock id) * * The MMC 1 and 2 clocks can be source from either a 64MHz or 96MHz clock. * * LOCKING: * Inherits the locks from the omap_prcm driver, no internal locking. * * RETURNS: * Returns 0 on success or a negative error code on failure. */ static int omap4_clk_hsmmc_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct omap4_clk_details* clk_details; struct resource* clk_mem_res; uint32_t clksel; if (sc == NULL) return ENXIO; clk_details = omap4_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); clk_mem_res = sc->sc_res[clk_details->mem_region]; if (clk_mem_res == NULL) return (EINVAL); /* For MMC modules 3, 4 & 5 you can't change the freq, it's always 48MHz */ if ((clkdev->id == MMC3_CLK) || (clkdev->id == MMC4_CLK) || (clkdev->id == MMC5_CLK)) { if (clksrc != F48MHZ_CLK) return (EINVAL); return 0; } clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); /* Bit 24 is set if 96MHz clock or cleared for 64MHz clock */ if (clksrc == F64MHZ_CLK) clksel &= ~(0x1UL << 24); else if (clksrc == F96MHZ_CLK) clksel |= (0x1UL << 24); else return (EINVAL); bus_write_4(clk_mem_res, clk_details->clksel_reg, clksel); return (0); } /** * omap4_clk_hsmmc_get_source_freq - checks if a module is accessible * @clkdev: pointer to the clockdev structure (id field will contain clock id) * * * * LOCKING: * Inherits the locks from the omap_prcm driver, no internal locking. * * RETURNS: * Returns 0 on success or a negative error code on failure. */ static int omap4_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq ) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct omap4_clk_details* clk_details; struct resource* clk_mem_res; uint32_t clksel; unsigned int src_freq; if (sc == NULL) return ENXIO; clk_details = omap4_clk_details(clkdev->id); if (clk_details == NULL) return (ENXIO); clk_mem_res = sc->sc_res[clk_details->mem_region]; if (clk_mem_res == NULL) return (EINVAL); switch (clkdev->id) { case MMC1_CLK: case MMC2_CLK: /* Need to read the CLKSEL field to determine the clock source */ clksel = bus_read_4(clk_mem_res, clk_details->clksel_reg); if (clksel & (0x1UL << 24)) src_freq = FREQ_96MHZ; else src_freq = FREQ_64MHZ; break; case MMC3_CLK: case MMC4_CLK: case MMC5_CLK: src_freq = FREQ_48MHZ; break; default: return (EINVAL); } /* Return the frequency */ if (freq) *freq = src_freq; return (0); } /** * omap4_clk_get_sysclk_freq - gets the sysclk frequency * @sc: pointer to the clk module/device context * * Read the clocking information from the power-control/boot-strap registers, * and stored in two global variables. * * RETURNS: * nothing, values are saved in global variables */ static int omap4_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq) { uint32_t clksel; uint32_t sysclk; struct omap4_prcm_softc *sc = omap4_prcm_sc; if (sc == NULL) return ENXIO; /* Read the input clock freq from the configuration register (CM_SYS_CLKSEL) */ clksel = bus_read_4(sc->sc_res[PRM_INSTANCE_MEM_REGION], CM_SYS_CLKSEL_OFFSET); switch (clksel & 0x7) { case 0x1: /* 12Mhz */ sysclk = 12000000; break; case 0x3: /* 16.8Mhz */ sysclk = 16800000; break; case 0x4: /* 19.2Mhz */ sysclk = 19200000; break; case 0x5: /* 26Mhz */ sysclk = 26000000; break; case 0x7: /* 38.4Mhz */ sysclk = 38400000; break; default: panic("%s: Invalid clock freq", __func__); } /* Return the value */ if (freq) *freq = sysclk; return (0); } /** * omap4_clk_get_arm_fclk_freq - gets the MPU clock frequency * @clkdev: ignored * @freq: pointer which upon return will contain the freq in hz * @mem_res: array of allocated memory resources * * Reads the frequency setting information registers and returns the value * in the freq variable. * * RETURNS: * returns 0 on success, a positive error code on failure. */ static int omap4_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq) { uint32_t clksel; uint32_t pll_mult, pll_div; uint32_t mpuclk, sysclk; struct omap4_prcm_softc *sc = omap4_prcm_sc; if (sc == NULL) return ENXIO; /* Read the clksel register which contains the DPLL multiple and divide * values. These are applied to the sysclk. */ clksel = bus_read_4(sc->sc_res[CM1_INSTANCE_MEM_REGION], CM_CLKSEL_DPLL_MPU); pll_mult = ((clksel >> 8) & 0x7ff); pll_div = (clksel & 0x7f) + 1; /* Get the system clock freq */ omap4_clk_get_sysclk_freq(NULL, &sysclk); /* Calculate the MPU freq */ mpuclk = ((uint64_t)sysclk * pll_mult) / pll_div; /* Return the value */ if (freq) *freq = mpuclk; return (0); } /** * omap4_clk_hsusbhost_activate - activates the USB clocks for the given module * @clkdev: pointer to the clock device structure. * @mem_res: array of memory resources allocated by the top level PRCM driver. * * The USB clocking setup seems to be a bit more tricky than the other modules, * to start with the clocking diagram for the HS host module shows 13 different * clocks. So to try and make it easier to follow the clocking activation * and deactivation is handled in it's own set of callbacks. * * LOCKING: * Inherits the locks from the omap_prcm driver, no internal locking. * * RETURNS: * Returns 0 on success or a positive error code on failure. */ struct dpll_param { unsigned int m; unsigned int n; unsigned int m2; unsigned int m3; unsigned int m4; unsigned int m5; unsigned int m6; unsigned int m7; }; /* USB parameters */ struct dpll_param usb_dpll_param[7] = { /* 12M values */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, /* 13M values */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, /* 16.8M values */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, /* 19.2M values */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, /* 26M values */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, /* 27M values */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, /* 38.4M values */ #ifdef CONFIG_OMAP4_SDC {0x32, 0x1, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0}, #else {0x32, 0x1, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0}, #endif }; static int omap4_clk_hsusbhost_activate(struct ti_clock_dev *clkdev) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct resource* clk_mem_res; uint32_t clksel_reg_off; uint32_t clksel; unsigned int i; if (sc == NULL) return ENXIO; switch (clkdev->id) { case USBTLL_CLK: /* For the USBTLL module we need to enable the following clocks: * - INIT_L4_ICLK (will be enabled by bootloader) * - TLL_CH0_FCLK * - TLL_CH1_FCLK */ /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */ clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; clksel_reg_off = L3INIT_CM2_OFFSET + 0x68; /* Enable the module and also enable the optional func clocks for * channels 0 & 1 (is this needed ?) */ clksel = bus_read_4(clk_mem_res, clksel_reg_off); clksel &= ~CLKCTRL_MODULEMODE_MASK; clksel |= CLKCTRL_MODULEMODE_ENABLE; clksel |= (0x1 << 8); /* USB-HOST optional clock: USB_CH0_CLK */ clksel |= (0x1 << 9); /* USB-HOST optional clock: USB_CH1_CLK */ break; case USBHSHOST_CLK: case USBP1_PHY_CLK: case USBP2_PHY_CLK: case USBP1_UTMI_CLK: case USBP2_UTMI_CLK: case USBP1_HSIC_CLK: case USBP2_HSIC_CLK: /* For the USB HS HOST module we need to enable the following clocks: * - INIT_L4_ICLK (will be enabled by bootloader) * - INIT_L3_ICLK (will be enabled by bootloader) * - INIT_48MC_FCLK * - UTMI_ROOT_GFCLK (UTMI only, create a new clock for that ?) * - UTMI_P1_FCLK (UTMI only, create a new clock for that ?) * - UTMI_P2_FCLK (UTMI only, create a new clock for that ?) * - HSIC_P1_60 (HSIC only, create a new clock for that ?) * - HSIC_P1_480 (HSIC only, create a new clock for that ?) * - HSIC_P2_60 (HSIC only, create a new clock for that ?) * - HSIC_P2_480 (HSIC only, create a new clock for that ?) */ /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */ clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; clksel_reg_off = L3INIT_CM2_OFFSET + 0x58; clksel = bus_read_4(clk_mem_res, clksel_reg_off); /* Enable the module and also enable the optional func clocks */ if (clkdev->id == USBHSHOST_CLK) { clksel &= ~CLKCTRL_MODULEMODE_MASK; clksel |= /*CLKCTRL_MODULEMODE_ENABLE*/2; clksel |= (0x1 << 15); /* USB-HOST clock control: FUNC48MCLK */ } else if (clkdev->id == USBP1_UTMI_CLK) clksel |= (0x1 << 8); /* UTMI_P1_CLK */ else if (clkdev->id == USBP2_UTMI_CLK) clksel |= (0x1 << 9); /* UTMI_P2_CLK */ else if (clkdev->id == USBP1_HSIC_CLK) clksel |= (0x5 << 11); /* HSIC60M_P1_CLK + HSIC480M_P1_CLK */ else if (clkdev->id == USBP2_HSIC_CLK) clksel |= (0x5 << 12); /* HSIC60M_P2_CLK + HSIC480M_P2_CLK */ break; default: return (EINVAL); } bus_write_4(clk_mem_res, clksel_reg_off, clksel); /* Try MAX_MODULE_ENABLE_WAIT number of times to check if enabled */ for (i = 0; i < MAX_MODULE_ENABLE_WAIT; i++) { clksel = bus_read_4(clk_mem_res, clksel_reg_off); if ((clksel & CLKCTRL_IDLEST_MASK) == CLKCTRL_IDLEST_ENABLED) break; } /* Check the enabled state */ if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED) { printf("Error: HERE failed to enable module with clock %d\n", clkdev->id); printf("Error: 0x%08x => 0x%08x\n", clksel_reg_off, clksel); return (ETIMEDOUT); } return (0); } /** * omap4_clk_generic_deactivate - checks if a module is accessible * @clkdev: pointer to the clock device structure. * @mem_res: array of memory resources allocated by the top level PRCM driver. * * * * LOCKING: * Inherits the locks from the omap_prcm driver, no internal locking. * * RETURNS: * Returns 0 on success or a positive error code on failure. */ static int omap4_clk_hsusbhost_deactivate(struct ti_clock_dev *clkdev) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct resource* clk_mem_res; uint32_t clksel_reg_off; uint32_t clksel; if (sc == NULL) return ENXIO; switch (clkdev->id) { case USBTLL_CLK: /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */ clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; clksel_reg_off = L3INIT_CM2_OFFSET + 0x68; clksel = bus_read_4(clk_mem_res, clksel_reg_off); clksel &= ~CLKCTRL_MODULEMODE_MASK; clksel |= CLKCTRL_MODULEMODE_DISABLE; break; case USBHSHOST_CLK: case USBP1_PHY_CLK: case USBP2_PHY_CLK: case USBP1_UTMI_CLK: case USBP2_UTMI_CLK: case USBP1_HSIC_CLK: case USBP2_HSIC_CLK: /* For the USB HS HOST module we need to enable the following clocks: * - INIT_L4_ICLK (will be enabled by bootloader) * - INIT_L3_ICLK (will be enabled by bootloader) * - INIT_48MC_FCLK * - UTMI_ROOT_GFCLK (UTMI only, create a new clock for that ?) * - UTMI_P1_FCLK (UTMI only, create a new clock for that ?) * - UTMI_P2_FCLK (UTMI only, create a new clock for that ?) * - HSIC_P1_60 (HSIC only, create a new clock for that ?) * - HSIC_P1_480 (HSIC only, create a new clock for that ?) * - HSIC_P2_60 (HSIC only, create a new clock for that ?) * - HSIC_P2_480 (HSIC only, create a new clock for that ?) */ /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */ clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; clksel_reg_off = L3INIT_CM2_OFFSET + 0x58; clksel = bus_read_4(clk_mem_res, clksel_reg_off); /* Enable the module and also enable the optional func clocks */ if (clkdev->id == USBHSHOST_CLK) { clksel &= ~CLKCTRL_MODULEMODE_MASK; clksel |= CLKCTRL_MODULEMODE_DISABLE; clksel &= ~(0x1 << 15); /* USB-HOST clock control: FUNC48MCLK */ } else if (clkdev->id == USBP1_UTMI_CLK) clksel &= ~(0x1 << 8); /* UTMI_P1_CLK */ else if (clkdev->id == USBP2_UTMI_CLK) clksel &= ~(0x1 << 9); /* UTMI_P2_CLK */ else if (clkdev->id == USBP1_HSIC_CLK) clksel &= ~(0x5 << 11); /* HSIC60M_P1_CLK + HSIC480M_P1_CLK */ else if (clkdev->id == USBP2_HSIC_CLK) clksel &= ~(0x5 << 12); /* HSIC60M_P2_CLK + HSIC480M_P2_CLK */ break; default: return (EINVAL); } bus_write_4(clk_mem_res, clksel_reg_off, clksel); return (0); } /** * omap4_clk_hsusbhost_accessible - checks if a module is accessible * @clkdev: pointer to the clock device structure. * @mem_res: array of memory resources allocated by the top level PRCM driver. * * * * LOCKING: * Inherits the locks from the omap_prcm driver, no internal locking. * * RETURNS: * Returns 0 if module is not enable, 1 if module is enabled or a negative * error code on failure. */ static int omap4_clk_hsusbhost_accessible(struct ti_clock_dev *clkdev) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct resource* clk_mem_res; uint32_t clksel_reg_off; uint32_t clksel; if (sc == NULL) return ENXIO; if (clkdev->id == USBTLL_CLK) { /* We need the CM_L3INIT_HSUSBTLL_CLKCTRL register in CM2 register set */ clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; clksel_reg_off = L3INIT_CM2_OFFSET + 0x68; } else if (clkdev->id == USBHSHOST_CLK) { /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */ clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; clksel_reg_off = L3INIT_CM2_OFFSET + 0x58; } else { return (EINVAL); } clksel = bus_read_4(clk_mem_res, clksel_reg_off); /* Check the enabled state */ if ((clksel & CLKCTRL_IDLEST_MASK) != CLKCTRL_IDLEST_ENABLED) return (0); return (1); } /** * omap4_clk_hsusbhost_set_source - sets the source clocks * @clkdev: pointer to the clock device structure. * @clksrc: the clock source ID for the given clock. * @mem_res: array of memory resources allocated by the top level PRCM driver. * * * * LOCKING: * Inherits the locks from the omap_prcm driver, no internal locking. * * RETURNS: * Returns 0 if sucessful otherwise a negative error code on failure. */ static int omap4_clk_hsusbhost_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct resource* clk_mem_res; uint32_t clksel_reg_off; uint32_t clksel; unsigned int bit; if (sc == NULL) return ENXIO; if (clkdev->id == USBP1_PHY_CLK) bit = 24; else if (clkdev->id != USBP2_PHY_CLK) bit = 25; else return (EINVAL); /* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */ clk_mem_res = sc->sc_res[CM2_INSTANCE_MEM_REGION]; clksel_reg_off = L3INIT_CM2_OFFSET + 0x58; clksel = bus_read_4(clk_mem_res, clksel_reg_off); /* Set the clock source to either external or internal */ if (clksrc == EXT_CLK) clksel |= (0x1 << bit); else clksel &= ~(0x1 << bit); bus_write_4(clk_mem_res, clksel_reg_off, clksel); return (0); } #define PRM_RSTCTRL 0x1b00 #define PRM_RSTCTRL_RESET 0x2 static void omap4_prcm_reset(void) { struct omap4_prcm_softc *sc = omap4_prcm_sc; bus_write_4(sc->sc_res[0], PRM_RSTCTRL, bus_read_4(sc->sc_res[0], PRM_RSTCTRL) | PRM_RSTCTRL_RESET); bus_read_4(sc->sc_res[0], PRM_RSTCTRL); } /** * omap4_prcm_probe - probe function for the driver * @dev: prcm device handle * * Simply sets the name of the driver module. * * LOCKING: * None * * RETURNS: * Always returns 0 */ static int omap4_prcm_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ti,omap4_prcm")) return (ENXIO); device_set_desc(dev, "TI OMAP Power, Reset and Clock Management"); return (0); } /** * omap_prcm_attach - attach function for the driver * @dev: prcm device handle * * Allocates and sets up the driver context, this simply entails creating a * bus mappings for the PRCM register set. * * LOCKING: * None * * RETURNS: * Always returns 0 */ extern uint32_t platform_arm_tmr_freq; static int omap4_prcm_attach(device_t dev) { struct omap4_prcm_softc *sc = device_get_softc(dev); unsigned int freq; if (bus_alloc_resources(dev, omap4_scm_res_spec, sc->sc_res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } omap4_prcm_sc = sc; ti_cpu_reset = omap4_prcm_reset; omap4_clk_get_arm_fclk_freq(NULL, &freq); arm_tmr_change_frequency(freq / 2); return (0); } static device_method_t omap4_prcm_methods[] = { DEVMETHOD(device_probe, omap4_prcm_probe), DEVMETHOD(device_attach, omap4_prcm_attach), {0, 0}, }; static driver_t omap4_prcm_driver = { "omap4_prcm", omap4_prcm_methods, sizeof(struct omap4_prcm_softc), }; static devclass_t omap4_prcm_devclass; -DRIVER_MODULE(omap4_prcm, simplebus, omap4_prcm_driver, omap4_prcm_devclass, 0, 0); +EARLY_DRIVER_MODULE(omap4_prcm, simplebus, omap4_prcm_driver, + omap4_prcm_devclass, 0, 0, BUS_PASS_TIMER + BUS_PASS_ORDER_EARLY); MODULE_VERSION(omap4_prcm, 1); Index: stable/10 =================================================================== --- stable/10 (revision 283337) +++ stable/10 (revision 283338) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r279816,279826