From 2008cd3c3f7eb0b765accc183b9e8f2a881c780c Mon Sep 17 00:00:00 2001 From: ian Date: Sun, 26 Oct 2014 03:55:09 +0000 Subject: MFC r271906: Make the ARM MPCore Timer driver work with published standard FDT bindings. --- sys/arm/arm/mpcore_timer.c | 289 ++++++++++++++++++++++++++++----------------- 1 file changed, 183 insertions(+), 106 deletions(-) (limited to 'sys/arm') diff --git a/sys/arm/arm/mpcore_timer.c b/sys/arm/arm/mpcore_timer.c index 3f87a2a..62c5344 100644 --- a/sys/arm/arm/mpcore_timer.c +++ b/sys/arm/arm/mpcore_timer.c @@ -97,36 +97,25 @@ __FBSDID("$FreeBSD$"); #define GBL_TIMER_INTR_EVENT (1UL << 0) struct arm_tmr_softc { - struct resource * tmr_res[4]; - bus_space_tag_t prv_bst; - bus_space_tag_t gbl_bst; - bus_space_handle_t prv_bsh; - bus_space_handle_t gbl_bsh; + device_t dev; + int irqrid; + int memrid; + struct resource * gbl_mem; + struct resource * prv_mem; + struct resource * prv_irq; uint64_t clkfreq; struct eventtimer et; }; -static struct resource_spec arm_tmr_spec[] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Global registers */ - { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Global timer interrupt (unused) */ - { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* Private (per-CPU) registers */ - { SYS_RES_IRQ, 1, RF_ACTIVE }, /* Private timer interrupt */ - { -1, 0 } -}; - -static struct arm_tmr_softc *arm_tmr_sc = NULL; - -static uint64_t platform_arm_tmr_freq = 0; - -#define tmr_prv_read_4(reg) \ - bus_space_read_4(arm_tmr_sc->prv_bst, arm_tmr_sc->prv_bsh, reg) -#define tmr_prv_write_4(reg, val) \ - bus_space_write_4(arm_tmr_sc->prv_bst, arm_tmr_sc->prv_bsh, reg, val) -#define tmr_gbl_read_4(reg) \ - bus_space_read_4(arm_tmr_sc->gbl_bst, arm_tmr_sc->gbl_bsh, reg) -#define tmr_gbl_write_4(reg, val) \ - bus_space_write_4(arm_tmr_sc->gbl_bst, arm_tmr_sc->gbl_bsh, reg, val) +static struct eventtimer *arm_tmr_et; +static struct timecounter *arm_tmr_tc; +static uint64_t arm_tmr_freq; +static boolean_t arm_tmr_freq_varies; +#define tmr_prv_read_4(sc, reg) bus_read_4((sc)->prv_mem, reg) +#define tmr_prv_write_4(sc, reg, val) bus_write_4((sc)->prv_mem, reg, val) +#define tmr_gbl_read_4(sc, reg) bus_read_4((sc)->gbl_mem, reg) +#define tmr_gbl_write_4(sc, reg, val) bus_write_4((sc)->gbl_mem, reg, val) static timecounter_get_t arm_tmr_get_timecount; @@ -139,6 +128,21 @@ static struct timecounter arm_tmr_timecount = { .tc_quality = 800, }; +#define TMR_GBL 0x01 +#define TMR_PRV 0x02 +#define TMR_BOTH (TMR_GBL | TMR_PRV) +#define TMR_NONE 0 + +static struct ofw_compat_data compat_data[] = { + {"arm,mpcore-timers", TMR_BOTH}, /* Non-standard, FreeBSD. */ + {"arm,cortex-a9-global-timer", TMR_GBL}, + {"arm,cortex-a5-global-timer", TMR_GBL}, + {"arm,cortex-a9-twd-timer", TMR_PRV}, + {"arm,cortex-a5-twd-timer", TMR_PRV}, + {"arm,arm11mp-twd-timer", TMR_PRV}, + {NULL, TMR_NONE} +}; + /** * arm_tmr_get_timecount - reads the timecount (global) timer * @tc: pointer to arm_tmr_timecount struct @@ -152,7 +156,10 @@ static struct timecounter arm_tmr_timecount = { static unsigned arm_tmr_get_timecount(struct timecounter *tc) { - return (tmr_gbl_read_4(GBL_TIMER_COUNT_LOW)); + struct arm_tmr_softc *sc; + + sc = tc->tc_priv; + return (tmr_gbl_read_4(sc, GBL_TIMER_COUNT_LOW)); } /** @@ -172,11 +179,13 @@ arm_tmr_get_timecount(struct timecounter *tc) static int arm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { + struct arm_tmr_softc *sc; uint32_t load, count; uint32_t ctrl; - tmr_prv_write_4(PRV_TIMER_CTRL, 0); - tmr_prv_write_4(PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT); + sc = et->et_priv; + tmr_prv_write_4(sc, PRV_TIMER_CTRL, 0); + tmr_prv_write_4(sc, PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT); ctrl = PRV_TIMER_CTRL_IRQ_ENABLE | PRV_TIMER_CTRL_TIMER_ENABLE; @@ -191,9 +200,9 @@ arm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period) else count = load; - tmr_prv_write_4(PRV_TIMER_LOAD, load); - tmr_prv_write_4(PRV_TIMER_COUNT, count); - tmr_prv_write_4(PRV_TIMER_CTRL, ctrl); + tmr_prv_write_4(sc, PRV_TIMER_LOAD, load); + tmr_prv_write_4(sc, PRV_TIMER_COUNT, count); + tmr_prv_write_4(sc, PRV_TIMER_CTRL, ctrl); return (0); } @@ -210,8 +219,11 @@ arm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period) static int arm_tmr_stop(struct eventtimer *et) { - tmr_prv_write_4(PRV_TIMER_CTRL, 0); - tmr_prv_write_4(PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT); + struct arm_tmr_softc *sc; + + sc = et->et_priv; + tmr_prv_write_4(sc, PRV_TIMER_CTRL, 0); + tmr_prv_write_4(sc, PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT); return (0); } @@ -227,13 +239,12 @@ arm_tmr_stop(struct eventtimer *et) static int arm_tmr_intr(void *arg) { - struct arm_tmr_softc *sc = (struct arm_tmr_softc *)arg; - - tmr_prv_write_4(PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT); + struct arm_tmr_softc *sc; + sc = arg; + tmr_prv_write_4(sc, PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT); if (sc->et.et_active) sc->et.et_event_cb(&sc->et, sc->et.et_arg); - return (FILTER_HANDLED); } @@ -257,13 +268,98 @@ arm_tmr_probe(device_t dev) if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "arm,mpcore-timers")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == TMR_NONE) return (ENXIO); device_set_desc(dev, "ARM MPCore Timers"); return (BUS_PROBE_DEFAULT); } +static int +attach_tc(struct arm_tmr_softc *sc) +{ + int rid; + + if (arm_tmr_tc != NULL) + return (EBUSY); + + rid = sc->memrid; + sc->gbl_mem = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->gbl_mem == NULL) { + device_printf(sc->dev, "could not allocate gbl mem resources\n"); + return (ENXIO); + } + tmr_gbl_write_4(sc, GBL_TIMER_CTRL, 0x00000000); + + arm_tmr_timecount.tc_frequency = sc->clkfreq; + arm_tmr_timecount.tc_priv = sc; + tc_init(&arm_tmr_timecount); + arm_tmr_tc = &arm_tmr_timecount; + + tmr_gbl_write_4(sc, GBL_TIMER_CTRL, GBL_TIMER_CTRL_TIMER_ENABLE); + + return (0); +} + +static int +attach_et(struct arm_tmr_softc *sc) +{ + void *ihl; + int irid, mrid; + + if (arm_tmr_et != NULL) + return (EBUSY); + + mrid = sc->memrid; + sc->prv_mem = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &mrid, + RF_ACTIVE); + if (sc->prv_mem == NULL) { + device_printf(sc->dev, "could not allocate prv mem resources\n"); + return (ENXIO); + } + tmr_prv_write_4(sc, PRV_TIMER_CTRL, 0x00000000); + + irid = sc->irqrid; + sc->prv_irq = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &irid, RF_ACTIVE); + if (sc->prv_irq == NULL) { + bus_release_resource(sc->dev, SYS_RES_MEMORY, mrid, sc->prv_mem); + device_printf(sc->dev, "could not allocate prv irq resources\n"); + return (ENXIO); + } + + if (bus_setup_intr(sc->dev, sc->prv_irq, INTR_TYPE_CLK, arm_tmr_intr, + NULL, sc, &ihl) != 0) { + bus_release_resource(sc->dev, SYS_RES_MEMORY, mrid, sc->prv_mem); + bus_release_resource(sc->dev, SYS_RES_IRQ, irid, sc->prv_irq); + device_printf(sc->dev, "unable to setup the et irq handler.\n"); + return (ENXIO); + } + + /* + * Setup and register the eventtimer. Most event timers set their min + * and max period values to some value calculated from the clock + * frequency. We might not know yet what our runtime clock frequency + * will be, so we just use some safe values. A max of 2 seconds ensures + * that even if our base clock frequency is 2GHz (meaning a 4GHz CPU), + * we won't overflow our 32-bit timer count register. A min of 20 + * nanoseconds is pretty much completely arbitrary. + */ + sc->et.et_name = "MPCore"; + sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; + sc->et.et_quality = 1000; + sc->et.et_frequency = sc->clkfreq; + sc->et.et_min_period = 20 * SBT_1NS; + sc->et.et_max_period = 2 * SBT_1S; + sc->et.et_start = arm_tmr_start; + sc->et.et_stop = arm_tmr_stop; + sc->et.et_priv = sc; + et_register(&sc->et); + arm_tmr_et = &sc->et; + + return (0); +} + /** * arm_tmr_attach - attaches the timer to the simplebus * @dev: new device @@ -277,21 +373,19 @@ arm_tmr_probe(device_t dev) static int arm_tmr_attach(device_t dev) { - struct arm_tmr_softc *sc = device_get_softc(dev); + struct arm_tmr_softc *sc; phandle_t node; pcell_t clock; - void *ihl; - boolean_t fixed_freq; + int et_err, tc_err, tmrtype; - if (arm_tmr_sc) - return (ENXIO); + sc = device_get_softc(dev); + sc->dev = dev; - if (platform_arm_tmr_freq == ARM_TMR_FREQUENCY_VARIES) { - fixed_freq = false; + if (arm_tmr_freq_varies) { + sc->clkfreq = arm_tmr_freq; } else { - fixed_freq = true; - if (platform_arm_tmr_freq != 0) { - sc->clkfreq = platform_arm_tmr_freq; + if (arm_tmr_freq != 0) { + sc->clkfreq = arm_tmr_freq; } else { /* Get the base clock frequency */ node = ofw_bus_get_node(dev); @@ -305,66 +399,40 @@ arm_tmr_attach(device_t dev) } } - if (bus_alloc_resources(dev, arm_tmr_spec, sc->tmr_res)) { - device_printf(dev, "could not allocate resources\n"); - return (ENXIO); - } - - /* Global timer interface */ - sc->gbl_bst = rman_get_bustag(sc->tmr_res[0]); - sc->gbl_bsh = rman_get_bushandle(sc->tmr_res[0]); - - /* Private per-CPU timer interface */ - sc->prv_bst = rman_get_bustag(sc->tmr_res[2]); - sc->prv_bsh = rman_get_bushandle(sc->tmr_res[2]); - - arm_tmr_sc = sc; - - /* Disable both timers to start off */ - tmr_prv_write_4(PRV_TIMER_CTRL, 0x00000000); - tmr_gbl_write_4(GBL_TIMER_CTRL, 0x00000000); - - if (bus_setup_intr(dev, sc->tmr_res[3], INTR_TYPE_CLK, arm_tmr_intr, - NULL, sc, &ihl) != 0) { - bus_release_resources(dev, arm_tmr_spec, sc->tmr_res); - device_printf(dev, "Unable to setup the clock irq handler.\n"); - return (ENXIO); - } + tmrtype = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + tc_err = ENXIO; + et_err = ENXIO; /* - * If the clock is fixed-frequency, setup and enable the global timer to - * use as the timecounter. If it's variable frequency it won't work as - * a timecounter. We also can't use it for DELAY(), so hopefully the - * platform provides its own implementation. If it doesn't, ours will + * If we're handling the global timer and it is fixed-frequency, set it + * up to use as a timecounter. If it's variable frequency it won't work + * as a timecounter. We also can't use it for DELAY(), so hopefully the + * platform provides its own implementation. If it doesn't, ours will * get used, but since the frequency isn't set, it will only use the * bogus loop counter. */ - if (fixed_freq) { - tmr_gbl_write_4(GBL_TIMER_CTRL, GBL_TIMER_CTRL_TIMER_ENABLE); - arm_tmr_timecount.tc_frequency = sc->clkfreq; - tc_init(&arm_tmr_timecount); + if (tmrtype & TMR_GBL) { + if (!arm_tmr_freq_varies) + tc_err = attach_tc(sc); + else if (bootverbose) + device_printf(sc->dev, + "not using variable-frequency device as timecounter"); + sc->memrid++; + sc->irqrid++; + } + + /* If we are handling the private timer, set it up as an eventtimer. */ + if (tmrtype & TMR_PRV) { + et_err = attach_et(sc); } /* - * Setup and register the eventtimer. Most event timers set their min - * and max period values to some value calculated from the clock - * frequency. We might not know yet what our runtime clock frequency - * will be, so we just use some safe values. A max of 2 seconds ensures - * that even if our base clock frequency is 2GHz (meaning a 4GHz CPU), - * we won't overflow our 32-bit timer count register. A min of 20 - * nanoseconds is pretty much completely arbitrary. + * If we didn't successfully set up a timecounter or eventtimer then we + * didn't actually attach at all, return error. */ - sc->et.et_name = "MPCore"; - sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; - sc->et.et_quality = 1000; - sc->et.et_frequency = sc->clkfreq; - sc->et.et_min_period = 20 * SBT_1NS; - sc->et.et_max_period = 2 * SBT_1S; - sc->et.et_start = arm_tmr_start; - sc->et.et_stop = arm_tmr_stop; - sc->et.et_priv = sc; - et_register(&sc->et); - + if (tc_err != 0 && et_err != 0) { + return (ENXIO); + } return (0); } @@ -384,6 +452,8 @@ static devclass_t arm_tmr_devclass; EARLY_DRIVER_MODULE(mp_tmr, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); +EARLY_DRIVER_MODULE(mp_tmr, ofwbus, arm_tmr_driver, arm_tmr_devclass, 0, 0, + BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); /* * Handle a change in clock frequency. The mpcore timer runs at half the CPU @@ -404,10 +474,14 @@ void arm_tmr_change_frequency(uint64_t newfreq) { - if (arm_tmr_sc == NULL) - platform_arm_tmr_freq = newfreq; - else - et_change_frequency(&arm_tmr_sc->et, newfreq); + if (newfreq == ARM_TMR_FREQUENCY_VARIES) { + arm_tmr_freq_varies = true; + return; + } + + arm_tmr_freq = newfreq; + if (arm_tmr_et != NULL) + et_change_frequency(arm_tmr_et, newfreq); } /** @@ -424,12 +498,13 @@ arm_tmr_change_frequency(uint64_t newfreq) static void __used /* Must emit function code for the weak ref below. */ arm_tmr_DELAY(int usec) { + struct arm_tmr_softc *sc; int32_t counts_per_usec; int32_t counts; uint32_t first, last; /* Check the timers are setup, if not just use a for loop for the meantime */ - if (arm_tmr_sc == NULL || arm_tmr_timecount.tc_frequency == 0) { + if (arm_tmr_tc == NULL || arm_tmr_timecount.tc_frequency == 0) { for (; usec > 0; usec--) for (counts = 200; counts > 0; counts--) cpufunc_nullop(); /* Prevent gcc from optimizing @@ -438,6 +513,8 @@ arm_tmr_DELAY(int usec) return; } + sc = arm_tmr_tc->tc_priv; + /* Get the number of times to count */ counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1); @@ -452,10 +529,10 @@ arm_tmr_DELAY(int usec) else counts = usec * counts_per_usec; - first = tmr_gbl_read_4(GBL_TIMER_COUNT_LOW); + first = tmr_gbl_read_4(sc, GBL_TIMER_COUNT_LOW); while (counts > 0) { - last = tmr_gbl_read_4(GBL_TIMER_COUNT_LOW); + last = tmr_gbl_read_4(sc, GBL_TIMER_COUNT_LOW); counts -= (int32_t)(last - first); first = last; } -- cgit v1.1