1201468Srpaulo/*- 2201468Srpaulo * Copyright (c) 2009 Yohanes Nugroho <yohanes@gmail.com>. 3201468Srpaulo * All rights reserved. 4201468Srpaulo * 5201468Srpaulo * Redistribution and use in source and binary forms, with or without 6201468Srpaulo * modification, are permitted provided that the following conditions 7201468Srpaulo * are met: 8201468Srpaulo * 1. Redistributions of source code must retain the above copyright 9201468Srpaulo * notice, this list of conditions and the following disclaimer. 10201468Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11201468Srpaulo * notice, this list of conditions and the following disclaimer in the 12201468Srpaulo * documentation and/or other materials provided with the distribution. 13201468Srpaulo * 14201468Srpaulo * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15201468Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16201468Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17201468Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18201468Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19201468Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20201468Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21201468Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22201468Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23201468Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24201468Srpaulo * SUCH DAMAGE. 25201468Srpaulo */ 26201468Srpaulo#include <sys/cdefs.h> 27201468Srpaulo__FBSDID("$FreeBSD$"); 28201468Srpaulo 29201468Srpaulo#include <sys/param.h> 30201468Srpaulo#include <sys/systm.h> 31201468Srpaulo#include <sys/bus.h> 32201468Srpaulo#include <sys/kernel.h> 33201468Srpaulo#include <sys/module.h> 34201468Srpaulo#include <sys/malloc.h> 35201468Srpaulo#include <sys/rman.h> 36201468Srpaulo#include <sys/timetc.h> 37201468Srpaulo#include <sys/watchdog.h> 38201468Srpaulo#include <machine/bus.h> 39201468Srpaulo#include <machine/cpu.h> 40201468Srpaulo#include <machine/intr.h> 41201468Srpaulo 42201468Srpaulo#include "econa_reg.h" 43201468Srpaulo#include "econa_var.h" 44201468Srpaulo 45201468Srpaulo#define INITIAL_TIMECOUNTER (0xffffffff) 46201468Srpaulo 47201468Srpaulostatic int timers_initialized = 0; 48201468Srpaulo 49201468Srpaulo#define HZ 100 50201468Srpaulo 51201468Srpauloextern unsigned int CPU_clock; 52201468Srpauloextern unsigned int AHB_clock; 53201468Srpauloextern unsigned int APB_clock; 54201468Srpaulo 55201468Srpaulostatic unsigned long timer_counter = 0; 56201468Srpaulo 57201468Srpaulostruct ec_timer_softc { 58201468Srpaulo struct resource * timer_res[3]; 59201468Srpaulo bus_space_tag_t timer_bst; 60201468Srpaulo bus_space_handle_t timer_bsh; 61201468Srpaulo struct mtx timer_mtx; 62201468Srpaulo}; 63201468Srpaulo 64201468Srpaulostatic struct resource_spec ec_timer_spec[] = { 65201468Srpaulo { SYS_RES_MEMORY, 0, RF_ACTIVE }, 66201468Srpaulo { SYS_RES_IRQ, 0, RF_ACTIVE }, 67201468Srpaulo { SYS_RES_IRQ, 1, RF_ACTIVE }, 68201468Srpaulo { -1, 0 } 69201468Srpaulo}; 70201468Srpaulo 71201468Srpaulostatic unsigned ec_timer_get_timecount(struct timecounter *); 72201468Srpaulo 73201468Srpaulostatic struct timecounter ec_timecounter = { 74201468Srpaulo .tc_get_timecount = ec_timer_get_timecount, 75201468Srpaulo .tc_name = "CPU Timer", 76201468Srpaulo /* This is assigned on the fly in the init sequence */ 77201468Srpaulo .tc_frequency = 0, 78201468Srpaulo .tc_counter_mask = ~0u, 79201468Srpaulo .tc_quality = 1000, 80201468Srpaulo}; 81201468Srpaulo 82201468Srpaulostatic struct ec_timer_softc *timer_softc = NULL; 83201468Srpaulo 84201468Srpaulostatic inline 85201468Srpaulovoid write_4(unsigned int val, unsigned int addr) 86201468Srpaulo{ 87201468Srpaulo bus_space_write_4(timer_softc->timer_bst, 88201468Srpaulo timer_softc->timer_bsh, addr, val); 89201468Srpaulo 90201468Srpaulo} 91201468Srpaulo 92201468Srpaulostatic inline 93201468Srpaulounsigned int read_4(unsigned int addr) 94201468Srpaulo{ 95201468Srpaulo 96201468Srpaulo return bus_space_read_4(timer_softc->timer_bst, 97201468Srpaulo timer_softc->timer_bsh, addr); 98201468Srpaulo} 99201468Srpaulo 100201468Srpaulo#define uSECS_PER_TICK (1000000 / APB_clock) 101201468Srpaulo#define TICKS2USECS(x) ((x) * uSECS_PER_TICK) 102201468Srpaulo 103201468Srpaulostatic unsigned 104201468Srpauloread_timer_counter_noint(void) 105201468Srpaulo{ 106201468Srpaulo 107201468Srpaulo arm_mask_irq(0); 108201468Srpaulo unsigned int v = read_4(TIMER_TM1_COUNTER_REG); 109201468Srpaulo arm_unmask_irq(0); 110201468Srpaulo return v; 111201468Srpaulo} 112201468Srpaulo 113201468Srpaulovoid 114201468SrpauloDELAY(int usec) 115201468Srpaulo{ 116201468Srpaulo uint32_t val, val_temp; 117201468Srpaulo int nticks; 118201468Srpaulo 119201468Srpaulo if (!timers_initialized) { 120201468Srpaulo for (; usec > 0; usec--) 121201468Srpaulo for (val = 100; val > 0; val--) 122201468Srpaulo ; 123201468Srpaulo return; 124201468Srpaulo } 125201468Srpaulo 126201468Srpaulo val = read_timer_counter_noint(); 127201468Srpaulo nticks = (((APB_clock / 1000) * usec) / 1000) + 100; 128201468Srpaulo 129201468Srpaulo while (nticks > 0) { 130201468Srpaulo val_temp = read_timer_counter_noint(); 131201468Srpaulo if (val > val_temp) 132201468Srpaulo nticks -= (val - val_temp); 133201468Srpaulo else 134201468Srpaulo nticks -= (val + (timer_counter - val_temp)); 135201468Srpaulo 136201468Srpaulo val = val_temp; 137201468Srpaulo } 138201468Srpaulo 139201468Srpaulo} 140201468Srpaulo 141201468Srpaulo/* 142201468Srpaulo * Setup timer 143201468Srpaulo */ 144201468Srpaulostatic inline void 145201468Srpaulosetup_timer(unsigned int counter_value) 146201468Srpaulo{ 147201468Srpaulo unsigned int control_value; 148201468Srpaulo unsigned int mask_value; 149201468Srpaulo 150201468Srpaulo control_value = read_4(TIMER_TM_CR_REG); 151201468Srpaulo 152201468Srpaulo mask_value = read_4(TIMER_TM_INTR_MASK_REG); 153201468Srpaulo write_4(counter_value, TIMER_TM1_COUNTER_REG); 154201468Srpaulo write_4(counter_value, TIMER_TM1_LOAD_REG); 155201468Srpaulo write_4(0, TIMER_TM1_MATCH1_REG); 156201468Srpaulo write_4(0,TIMER_TM1_MATCH2_REG); 157201468Srpaulo 158201468Srpaulo control_value &= ~(TIMER1_CLOCK_SOURCE); 159201468Srpaulo control_value |= TIMER1_UP_DOWN_COUNT; 160201468Srpaulo 161201468Srpaulo write_4(0, TIMER_TM2_COUNTER_REG); 162201468Srpaulo write_4(0, TIMER_TM2_LOAD_REG); 163201468Srpaulo write_4(~0u, TIMER_TM2_MATCH1_REG); 164201468Srpaulo write_4(~0u,TIMER_TM2_MATCH2_REG); 165201468Srpaulo 166201468Srpaulo control_value &= ~(TIMER2_CLOCK_SOURCE); 167201468Srpaulo control_value &= ~(TIMER2_UP_DOWN_COUNT); 168201468Srpaulo 169201468Srpaulo mask_value &= ~(63); 170201468Srpaulo 171201468Srpaulo write_4(control_value, TIMER_TM_CR_REG); 172201468Srpaulo write_4(mask_value, TIMER_TM_INTR_MASK_REG); 173201468Srpaulo} 174201468Srpaulo 175201468Srpaulo/* 176201468Srpaulo * Enable timer 177201468Srpaulo */ 178201468Srpaulostatic inline void 179201468Srpaulotimer_enable(void) 180201468Srpaulo{ 181201468Srpaulo unsigned int control_value; 182201468Srpaulo 183201468Srpaulo control_value = read_4(TIMER_TM_CR_REG); 184201468Srpaulo 185201468Srpaulo control_value |= TIMER1_OVERFLOW_ENABLE; 186201468Srpaulo control_value |= TIMER1_ENABLE; 187201468Srpaulo control_value |= TIMER2_OVERFLOW_ENABLE; 188201468Srpaulo control_value |= TIMER2_ENABLE; 189201468Srpaulo 190201468Srpaulo write_4(control_value, TIMER_TM_CR_REG); 191201468Srpaulo} 192201468Srpaulo 193201468Srpaulostatic inline unsigned int 194201468Srpauloread_second_timer_counter(void) 195201468Srpaulo{ 196201468Srpaulo 197201468Srpaulo return read_4(TIMER_TM2_COUNTER_REG); 198201468Srpaulo} 199201468Srpaulo 200201468Srpaulo/* 201201468Srpaulo * Get timer interrupt status 202201468Srpaulo */ 203201468Srpaulostatic inline unsigned int 204201468Srpauloread_timer_interrupt_status(void) 205201468Srpaulo{ 206201468Srpaulo 207201468Srpaulo return read_4(TIMER_TM_INTR_STATUS_REG); 208201468Srpaulo} 209201468Srpaulo 210201468Srpaulo/* 211201468Srpaulo * Clear timer interrupt status 212201468Srpaulo */ 213201468Srpaulostatic inline void 214201468Srpauloclear_timer_interrupt_status(unsigned int irq) 215201468Srpaulo{ 216201468Srpaulo unsigned int interrupt_status; 217201468Srpaulo 218201468Srpaulo interrupt_status = read_4(TIMER_TM_INTR_STATUS_REG); 219201468Srpaulo if (irq == 0) { 220201468Srpaulo if (interrupt_status & (TIMER1_MATCH1_INTR)) 221201468Srpaulo interrupt_status &= ~(TIMER1_MATCH1_INTR); 222201468Srpaulo if (interrupt_status & (TIMER1_MATCH2_INTR)) 223201468Srpaulo interrupt_status &= ~(TIMER1_MATCH2_INTR); 224201468Srpaulo if (interrupt_status & (TIMER1_OVERFLOW_INTR)) 225201468Srpaulo interrupt_status &= ~(TIMER1_OVERFLOW_INTR); 226201468Srpaulo } 227201468Srpaulo if (irq == 1) { 228201468Srpaulo if (interrupt_status & (TIMER2_MATCH1_INTR)) 229201468Srpaulo interrupt_status &= ~(TIMER2_MATCH1_INTR); 230201468Srpaulo if (interrupt_status & (TIMER2_MATCH2_INTR)) 231201468Srpaulo interrupt_status &= ~(TIMER2_MATCH2_INTR); 232201468Srpaulo if (interrupt_status & (TIMER2_OVERFLOW_INTR)) 233201468Srpaulo interrupt_status &= ~(TIMER2_OVERFLOW_INTR); 234201468Srpaulo } 235201468Srpaulo 236201468Srpaulo write_4(interrupt_status, TIMER_TM_INTR_STATUS_REG); 237201468Srpaulo} 238201468Srpaulo 239201468Srpaulostatic unsigned 240201468Srpauloec_timer_get_timecount(struct timecounter *a) 241201468Srpaulo{ 242201468Srpaulo unsigned int ticks1; 243201468Srpaulo arm_mask_irq(1); 244201468Srpaulo ticks1 = read_second_timer_counter(); 245201468Srpaulo arm_unmask_irq(1); 246201468Srpaulo return ticks1; 247201468Srpaulo} 248201468Srpaulo 249201468Srpaulo/* 250201468Srpaulo * Setup timer 251201468Srpaulo */ 252201468Srpaulostatic inline void 253201468Srpaulodo_setup_timer(void) 254201468Srpaulo{ 255201468Srpaulo 256201468Srpaulo timer_counter = APB_clock/HZ; 257201468Srpaulo /* 258201468Srpaulo * setup timer-related values 259201468Srpaulo */ 260201468Srpaulo setup_timer(timer_counter); 261201468Srpaulo} 262201468Srpaulo 263201468Srpaulovoid 264201468Srpaulocpu_initclocks(void) 265201468Srpaulo{ 266201468Srpaulo 267201468Srpaulo ec_timecounter.tc_frequency = APB_clock; 268201468Srpaulo tc_init(&ec_timecounter); 269201468Srpaulo timer_enable(); 270201468Srpaulo timers_initialized = 1; 271201468Srpaulo} 272201468Srpaulo 273201468Srpaulovoid 274201468Srpaulocpu_startprofclock(void) 275201468Srpaulo{ 276201468Srpaulo 277201468Srpaulo} 278201468Srpaulo 279201468Srpaulovoid 280201468Srpaulocpu_stopprofclock(void) 281201468Srpaulo{ 282201468Srpaulo 283201468Srpaulo} 284201468Srpaulo 285201468Srpaulostatic int 286201468Srpauloec_timer_probe(device_t dev) 287201468Srpaulo{ 288201468Srpaulo 289201468Srpaulo device_set_desc(dev, "Econa CPU Timer"); 290201468Srpaulo return (0); 291201468Srpaulo} 292201468Srpaulo 293201468Srpaulostatic int 294201468Srpauloec_reset(void *arg) 295201468Srpaulo{ 296201468Srpaulo 297201468Srpaulo arm_mask_irq(1); 298201468Srpaulo clear_timer_interrupt_status(1); 299201468Srpaulo arm_unmask_irq(1); 300201468Srpaulo return (FILTER_HANDLED); 301201468Srpaulo} 302201468Srpaulo 303201468Srpaulostatic int 304201468Srpauloec_hardclock(void *arg) 305201468Srpaulo{ 306201468Srpaulo struct trapframe *frame; 307201468Srpaulo unsigned int val; 308201468Srpaulo /*clear timer interrupt status*/ 309201468Srpaulo 310201468Srpaulo arm_mask_irq(0); 311201468Srpaulo 312201468Srpaulo val = read_4(TIMER_INTERRUPT_STATUS_REG); 313201468Srpaulo val &= ~(TIMER1_OVERFLOW_INTERRUPT); 314201468Srpaulo write_4(val, TIMER_INTERRUPT_STATUS_REG); 315201468Srpaulo 316201468Srpaulo frame = (struct trapframe *)arg; 317201468Srpaulo hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); 318201468Srpaulo 319201468Srpaulo arm_unmask_irq(0); 320201468Srpaulo 321201468Srpaulo return (FILTER_HANDLED); 322201468Srpaulo} 323201468Srpaulo 324201468Srpaulostatic int 325201468Srpauloec_timer_attach(device_t dev) 326201468Srpaulo{ 327201468Srpaulo struct ec_timer_softc *sc; 328201468Srpaulo int error; 329201468Srpaulo void *ihl; 330201468Srpaulo 331201468Srpaulo 332201468Srpaulo if (timer_softc != NULL) 333201468Srpaulo return (ENXIO); 334201468Srpaulo 335201468Srpaulo sc = (struct ec_timer_softc *)device_get_softc(dev); 336201468Srpaulo 337201468Srpaulo timer_softc = sc; 338201468Srpaulo 339201468Srpaulo error = bus_alloc_resources(dev, ec_timer_spec, sc->timer_res); 340201468Srpaulo if (error) { 341201468Srpaulo device_printf(dev, "could not allocate resources\n"); 342201468Srpaulo return (ENXIO); 343201468Srpaulo } 344201468Srpaulo 345201468Srpaulo sc->timer_bst = rman_get_bustag(sc->timer_res[0]); 346201468Srpaulo sc->timer_bsh = rman_get_bushandle(sc->timer_res[0]); 347201468Srpaulo 348201468Srpaulo do_setup_timer(); 349201468Srpaulo 350201468Srpaulo if (bus_setup_intr(dev, sc->timer_res[1], INTR_TYPE_CLK, 351201468Srpaulo ec_hardclock, NULL, NULL, &ihl) != 0) { 352201468Srpaulo bus_release_resources(dev, ec_timer_spec, sc->timer_res); 353201468Srpaulo device_printf(dev, "could not setup hardclock interrupt\n"); 354201468Srpaulo return (ENXIO); 355201468Srpaulo } 356201468Srpaulo 357201468Srpaulo if (bus_setup_intr(dev, sc->timer_res[2], INTR_TYPE_CLK, 358201468Srpaulo ec_reset, NULL, NULL, &ihl) != 0) { 359201468Srpaulo bus_release_resources(dev, ec_timer_spec, sc->timer_res); 360201468Srpaulo device_printf(dev, "could not setup timer interrupt\n"); 361201468Srpaulo return (ENXIO); 362201468Srpaulo } 363201468Srpaulo 364201468Srpaulo return (0); 365201468Srpaulo} 366201468Srpaulo 367201468Srpaulostatic device_method_t ec_timer_methods[] = { 368201468Srpaulo DEVMETHOD(device_probe, ec_timer_probe), 369201468Srpaulo DEVMETHOD(device_attach, ec_timer_attach), 370201468Srpaulo { 0, 0 } 371201468Srpaulo}; 372201468Srpaulo 373201468Srpaulostatic driver_t ec_timer_driver = { 374201468Srpaulo "timer", 375201468Srpaulo ec_timer_methods, 376201468Srpaulo sizeof(struct ec_timer_softc), 377201468Srpaulo}; 378201468Srpaulo 379201468Srpaulostatic devclass_t ec_timer_devclass; 380201468Srpaulo 381201468SrpauloDRIVER_MODULE(timer, econaarm, ec_timer_driver, ec_timer_devclass, 0, 0); 382