void cpu_initclocks(void) { struct cpu_info * const ci = curcpu(); uint32_t cnt; static struct timecounter tc = { ingenic_count_read, /* get_timecount */ 0, /* no poll_pps */ ~0u, /* counter_mask */ 12000000, /* frequency */ "Ingenic OS timer", /* name */ 100, /* quality */ }; curcpu()->ci_cctr_freq = tc.tc_frequency; tc_init(&tc); printf("starting timer interrupt...\n"); /* start the timer interrupt */ cnt = readreg(JZ_OST_CNT_LO); ci->ci_next_cp0_clk_intr = cnt + ci->ci_cycles_per_hz; writereg(JZ_TC_TFCR, TFR_OSTFLAG); writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr); /* * XXX * We can use OST or one of the regular timers to generate the 100hz * interrupt. OST interrupts need to be rescheduled every time and by * only one core, the regular timer can be programmed to fire every * 10ms without rescheduling and we'd still use the OST as time base. * OST is supposed to fire on INT2 although I haven't been able to get * that to work yet ( all I get is INT0 which is for hardware interrupts * in general ) * So if we can get OST to fire on INT2 we can just block INT0 on core1 * and have a timer interrupt on both cores, if not the regular timer * would be more convenient but we'd have to shoot an IPI to core1 on * every tick. * For now, use OST and hope we'll figure out how to make it fire on * INT2. */ #ifdef USE_OST writereg(JZ_TC_TMCR, TFR_OSTFLAG); #else writereg(JZ_TC_TECR, TESR_TCST5); /* disable timer 5 */ writereg(JZ_TC_TCNT(5), 0); writereg(JZ_TC_TDFR(5), 30000); /* 10ms at 48MHz / 16 */ writereg(JZ_TC_TDHR(5), 60000); /* not reached */ writereg(JZ_TC_TCSR(5), TCSR_EXT_EN| TCSR_DIV_16); writereg(JZ_TC_TMCR, TFR_FFLAG5); writereg(JZ_TC_TFCR, TFR_FFLAG5); writereg(JZ_TC_TESR, TESR_TCST5); /* enable timer 5 */ #endif #ifdef INGENIC_CLOCK_DEBUG printf("INTC %08x %08x\n", readreg(JZ_ICSR0), readreg(JZ_ICSR1)); printf("ICMR0 %08x\n", readreg(JZ_ICMR0)); #endif writereg(JZ_ICMCR0, 0x0c000000); /* TCU2, OST */ spl0(); #ifdef INGENIC_CLOCK_DEBUG printf("TFR: %08x\n", readreg(JZ_TC_TFR)); printf("TMR: %08x\n", readreg(JZ_TC_TMR)); printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5))); printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0)); printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0)); delay(100000); printf("TFR: %08x\n", readreg(JZ_TC_TFR)); printf("TMR: %08x\n", readreg(JZ_TC_TMR)); printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5))); printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0)); printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0)); printf("TFR: %08x\n", readreg(JZ_TC_TFR)); printf("TMR: %08x\n", readreg(JZ_TC_TMR)); printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5))); printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0)); printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0)); printf("INTC %08x %08x\n", readreg(JZ_ICSR0), readreg(JZ_ICSR1)); delay(3000000); printf("%s %d\n", __func__, MFC0(12, 3)); printf("%s %08x\n", __func__, MFC0(12, 4)); #endif }
static int jz4780_timer_attach(device_t dev) { struct jz4780_timer_softc *sc = device_get_softc(dev); pcell_t counter_freq; clk_t clk; /* There should be exactly one instance. */ if (jz4780_timer_sc != NULL) return (ENXIO); sc->dev = dev; if (bus_alloc_resources(dev, jz4780_timer_spec, sc->res)) { device_printf(dev, "can not allocate resources for device\n"); return (ENXIO); } counter_freq = 0; if (clk_get_by_name(dev, "ext", &clk) == 0) { uint64_t clk_freq; if (clk_get_freq(clk, &clk_freq) == 0) counter_freq = (uint32_t)clk_freq / 16; clk_release(clk); } if (counter_freq == 0) { device_printf(dev, "unable to determine ext clock frequency\n"); /* Hardcode value we 'know' is correct */ counter_freq = 48000000 / 16; } /* * Disable the timers, select the input for each timer, * clear and then start OST. */ /* Stop OST, if it happens to be running */ CSR_WRITE_4(sc, JZ_TC_TECR, TESR_OST); /* Stop all other channels as well */ CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST0 | TESR_TCST1 | TESR_TCST2 | TESR_TCST3 | TESR_TCST4 | TESR_TCST5 | TESR_TCST6 | TESR_TCST3); /* Clear detect mask flags */ CSR_WRITE_4(sc, JZ_TC_TFCR, 0xFFFFFFFF); /* Mask all interrupts */ CSR_WRITE_4(sc, JZ_TC_TMSR, 0xFFFFFFFF); /* Init counter with known data */ CSR_WRITE_4(sc, JZ_OST_CTRL, 0); CSR_WRITE_4(sc, JZ_OST_CNT_LO, 0); CSR_WRITE_4(sc, JZ_OST_CNT_HI, 0); CSR_WRITE_4(sc, JZ_OST_DATA, 0xffffffff); /* Configure counter for external clock */ CSR_WRITE_4(sc, JZ_OST_CTRL, OSTC_EXT_EN | OSTC_MODE | OSTC_DIV_16); /* Start the counter again */ CSR_WRITE_4(sc, JZ_TC_TESR, TESR_OST); /* Configure TCU channel 5 similarly to OST and leave it disabled */ CSR_WRITE_4(sc, JZ_TC_TCSR(5), TCSR_EXT_EN | TCSR_DIV_16); CSR_WRITE_4(sc, JZ_TC_TMCR, TMR_FMASK(5)); if (bus_setup_intr(dev, sc->res[2], INTR_TYPE_CLK, jz4780_hardclock, NULL, sc, &sc->ih_cookie)) { device_printf(dev, "could not setup interrupt handler\n"); bus_release_resources(dev, jz4780_timer_spec, sc->res); return (ENXIO); } sc->et.et_name = "JZ4780 TCU5"; sc->et.et_flags = ET_FLAGS_ONESHOT; sc->et.et_frequency = counter_freq; sc->et.et_quality = 1000; sc->et.et_min_period = (0x00000002LLU * SBT_1S) / sc->et.et_frequency; sc->et.et_max_period = (0x0000fffeLLU * SBT_1S) / sc->et.et_frequency; sc->et.et_start = jz4780_timer_start; sc->et.et_stop = jz4780_timer_stop; sc->et.et_priv = sc; et_register(&sc->et); sc->tc.tc_get_timecount = jz4780_get_timecount; sc->tc.tc_name = "JZ4780 OST"; sc->tc.tc_frequency = counter_freq; sc->tc.tc_counter_mask = ~0u; sc->tc.tc_quality = 1000; sc->tc.tc_priv = sc; tc_init(&sc->tc); /* Now when tc is initialized, allow DELAY to find it */ jz4780_timer_sc = sc; return (0); }