timer.c revision 201468
1/*- 2 * Copyright (c) 2009 Yohanes Nugroho <yohanes@gmail.com>. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26#include <sys/cdefs.h> 27__FBSDID("$FreeBSD: head/sys/arm/econa/timer.c 201468 2010-01-04 03:35:45Z rpaulo $"); 28 29#include <sys/param.h> 30#include <sys/systm.h> 31#include <sys/bus.h> 32#include <sys/kernel.h> 33#include <sys/module.h> 34#include <sys/malloc.h> 35#include <sys/rman.h> 36#include <sys/timetc.h> 37#include <sys/watchdog.h> 38#include <machine/bus.h> 39#include <machine/cpu.h> 40#include <machine/frame.h> 41#include <machine/intr.h> 42 43#include "econa_reg.h" 44#include "econa_var.h" 45 46#define INITIAL_TIMECOUNTER (0xffffffff) 47 48static int timers_initialized = 0; 49 50#define HZ 100 51 52extern unsigned int CPU_clock; 53extern unsigned int AHB_clock; 54extern unsigned int APB_clock; 55 56static unsigned long timer_counter = 0; 57 58struct ec_timer_softc { 59 struct resource * timer_res[3]; 60 bus_space_tag_t timer_bst; 61 bus_space_handle_t timer_bsh; 62 struct mtx timer_mtx; 63}; 64 65static struct resource_spec ec_timer_spec[] = { 66 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 67 { SYS_RES_IRQ, 0, RF_ACTIVE }, 68 { SYS_RES_IRQ, 1, RF_ACTIVE }, 69 { -1, 0 } 70}; 71 72static unsigned ec_timer_get_timecount(struct timecounter *); 73 74static struct timecounter ec_timecounter = { 75 .tc_get_timecount = ec_timer_get_timecount, 76 .tc_name = "CPU Timer", 77 /* This is assigned on the fly in the init sequence */ 78 .tc_frequency = 0, 79 .tc_counter_mask = ~0u, 80 .tc_quality = 1000, 81}; 82 83static struct ec_timer_softc *timer_softc = NULL; 84 85static inline 86void write_4(unsigned int val, unsigned int addr) 87{ 88 bus_space_write_4(timer_softc->timer_bst, 89 timer_softc->timer_bsh, addr, val); 90 91} 92 93static inline 94unsigned int read_4(unsigned int addr) 95{ 96 97 return bus_space_read_4(timer_softc->timer_bst, 98 timer_softc->timer_bsh, addr); 99} 100 101#define uSECS_PER_TICK (1000000 / APB_clock) 102#define TICKS2USECS(x) ((x) * uSECS_PER_TICK) 103 104static unsigned 105read_timer_counter_noint(void) 106{ 107 108 arm_mask_irq(0); 109 unsigned int v = read_4(TIMER_TM1_COUNTER_REG); 110 arm_unmask_irq(0); 111 return v; 112} 113 114void 115DELAY(int usec) 116{ 117 uint32_t val, val_temp; 118 int nticks; 119 120 if (!timers_initialized) { 121 for (; usec > 0; usec--) 122 for (val = 100; val > 0; val--) 123 ; 124 return; 125 } 126 127 val = read_timer_counter_noint(); 128 nticks = (((APB_clock / 1000) * usec) / 1000) + 100; 129 130 while (nticks > 0) { 131 val_temp = read_timer_counter_noint(); 132 if (val > val_temp) 133 nticks -= (val - val_temp); 134 else 135 nticks -= (val + (timer_counter - val_temp)); 136 137 val = val_temp; 138 } 139 140} 141 142/* 143 * Setup timer 144 */ 145static inline void 146setup_timer(unsigned int counter_value) 147{ 148 unsigned int control_value; 149 unsigned int mask_value; 150 151 control_value = read_4(TIMER_TM_CR_REG); 152 153 mask_value = read_4(TIMER_TM_INTR_MASK_REG); 154 write_4(counter_value, TIMER_TM1_COUNTER_REG); 155 write_4(counter_value, TIMER_TM1_LOAD_REG); 156 write_4(0, TIMER_TM1_MATCH1_REG); 157 write_4(0,TIMER_TM1_MATCH2_REG); 158 159 control_value &= ~(TIMER1_CLOCK_SOURCE); 160 control_value |= TIMER1_UP_DOWN_COUNT; 161 162 write_4(0, TIMER_TM2_COUNTER_REG); 163 write_4(0, TIMER_TM2_LOAD_REG); 164 write_4(~0u, TIMER_TM2_MATCH1_REG); 165 write_4(~0u,TIMER_TM2_MATCH2_REG); 166 167 control_value &= ~(TIMER2_CLOCK_SOURCE); 168 control_value &= ~(TIMER2_UP_DOWN_COUNT); 169 170 mask_value &= ~(63); 171 172 write_4(control_value, TIMER_TM_CR_REG); 173 write_4(mask_value, TIMER_TM_INTR_MASK_REG); 174} 175 176/* 177 * Enable timer 178 */ 179static inline void 180timer_enable(void) 181{ 182 unsigned int control_value; 183 184 control_value = read_4(TIMER_TM_CR_REG); 185 186 control_value |= TIMER1_OVERFLOW_ENABLE; 187 control_value |= TIMER1_ENABLE; 188 control_value |= TIMER2_OVERFLOW_ENABLE; 189 control_value |= TIMER2_ENABLE; 190 191 write_4(control_value, TIMER_TM_CR_REG); 192} 193 194static inline unsigned int 195read_second_timer_counter(void) 196{ 197 198 return read_4(TIMER_TM2_COUNTER_REG); 199} 200 201/* 202 * Get timer interrupt status 203 */ 204static inline unsigned int 205read_timer_interrupt_status(void) 206{ 207 208 return read_4(TIMER_TM_INTR_STATUS_REG); 209} 210 211/* 212 * Clear timer interrupt status 213 */ 214static inline void 215clear_timer_interrupt_status(unsigned int irq) 216{ 217 unsigned int interrupt_status; 218 219 interrupt_status = read_4(TIMER_TM_INTR_STATUS_REG); 220 if (irq == 0) { 221 if (interrupt_status & (TIMER1_MATCH1_INTR)) 222 interrupt_status &= ~(TIMER1_MATCH1_INTR); 223 if (interrupt_status & (TIMER1_MATCH2_INTR)) 224 interrupt_status &= ~(TIMER1_MATCH2_INTR); 225 if (interrupt_status & (TIMER1_OVERFLOW_INTR)) 226 interrupt_status &= ~(TIMER1_OVERFLOW_INTR); 227 } 228 if (irq == 1) { 229 if (interrupt_status & (TIMER2_MATCH1_INTR)) 230 interrupt_status &= ~(TIMER2_MATCH1_INTR); 231 if (interrupt_status & (TIMER2_MATCH2_INTR)) 232 interrupt_status &= ~(TIMER2_MATCH2_INTR); 233 if (interrupt_status & (TIMER2_OVERFLOW_INTR)) 234 interrupt_status &= ~(TIMER2_OVERFLOW_INTR); 235 } 236 237 write_4(interrupt_status, TIMER_TM_INTR_STATUS_REG); 238} 239 240static unsigned 241ec_timer_get_timecount(struct timecounter *a) 242{ 243 unsigned int ticks1; 244 arm_mask_irq(1); 245 ticks1 = read_second_timer_counter(); 246 arm_unmask_irq(1); 247 return ticks1; 248} 249 250/* 251 * Setup timer 252 */ 253static inline void 254do_setup_timer(void) 255{ 256 257 timer_counter = APB_clock/HZ; 258 /* 259 * setup timer-related values 260 */ 261 setup_timer(timer_counter); 262} 263 264void 265cpu_initclocks(void) 266{ 267 268 ec_timecounter.tc_frequency = APB_clock; 269 tc_init(&ec_timecounter); 270 timer_enable(); 271 timers_initialized = 1; 272} 273 274void 275cpu_startprofclock(void) 276{ 277 278} 279 280void 281cpu_stopprofclock(void) 282{ 283 284} 285 286static int 287ec_timer_probe(device_t dev) 288{ 289 290 device_set_desc(dev, "Econa CPU Timer"); 291 return (0); 292} 293 294static int 295ec_reset(void *arg) 296{ 297 298 arm_mask_irq(1); 299 clear_timer_interrupt_status(1); 300 arm_unmask_irq(1); 301 return (FILTER_HANDLED); 302} 303 304static int 305ec_hardclock(void *arg) 306{ 307 struct trapframe *frame; 308 unsigned int val; 309 /*clear timer interrupt status*/ 310 311 arm_mask_irq(0); 312 313 val = read_4(TIMER_INTERRUPT_STATUS_REG); 314 val &= ~(TIMER1_OVERFLOW_INTERRUPT); 315 write_4(val, TIMER_INTERRUPT_STATUS_REG); 316 317 frame = (struct trapframe *)arg; 318 hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); 319 320 arm_unmask_irq(0); 321 322 return (FILTER_HANDLED); 323} 324 325static int 326ec_timer_attach(device_t dev) 327{ 328 struct ec_timer_softc *sc; 329 int error; 330 void *ihl; 331 332 333 if (timer_softc != NULL) 334 return (ENXIO); 335 336 sc = (struct ec_timer_softc *)device_get_softc(dev); 337 338 timer_softc = sc; 339 340 error = bus_alloc_resources(dev, ec_timer_spec, sc->timer_res); 341 if (error) { 342 device_printf(dev, "could not allocate resources\n"); 343 return (ENXIO); 344 } 345 346 sc->timer_bst = rman_get_bustag(sc->timer_res[0]); 347 sc->timer_bsh = rman_get_bushandle(sc->timer_res[0]); 348 349 do_setup_timer(); 350 351 if (bus_setup_intr(dev, sc->timer_res[1], INTR_TYPE_CLK, 352 ec_hardclock, NULL, NULL, &ihl) != 0) { 353 bus_release_resources(dev, ec_timer_spec, sc->timer_res); 354 device_printf(dev, "could not setup hardclock interrupt\n"); 355 return (ENXIO); 356 } 357 358 if (bus_setup_intr(dev, sc->timer_res[2], INTR_TYPE_CLK, 359 ec_reset, NULL, NULL, &ihl) != 0) { 360 bus_release_resources(dev, ec_timer_spec, sc->timer_res); 361 device_printf(dev, "could not setup timer interrupt\n"); 362 return (ENXIO); 363 } 364 365 return (0); 366} 367 368static device_method_t ec_timer_methods[] = { 369 DEVMETHOD(device_probe, ec_timer_probe), 370 DEVMETHOD(device_attach, ec_timer_attach), 371 { 0, 0 } 372}; 373 374static driver_t ec_timer_driver = { 375 "timer", 376 ec_timer_methods, 377 sizeof(struct ec_timer_softc), 378}; 379 380static devclass_t ec_timer_devclass; 381 382DRIVER_MODULE(timer, econaarm, ec_timer_driver, ec_timer_devclass, 0, 0); 383