1164426Ssam/* $NetBSD: ixp425_timer.c,v 1.11 2006/04/10 03:36:03 simonb Exp $ */ 2164426Ssam 3164426Ssam/* 4164426Ssam * Copyright (c) 2003 5164426Ssam * Ichiro FUKUHARA <ichiro@ichiro.org>. 6164426Ssam * All rights reserved. 7164426Ssam * 8164426Ssam * Redistribution and use in source and binary forms, with or without 9164426Ssam * modification, are permitted provided that the following conditions 10164426Ssam * are met: 11164426Ssam * 1. Redistributions of source code must retain the above copyright 12164426Ssam * notice, this list of conditions and the following disclaimer. 13164426Ssam * 2. Redistributions in binary form must reproduce the above copyright 14164426Ssam * notice, this list of conditions and the following disclaimer in the 15164426Ssam * documentation and/or other materials provided with the distribution. 16164426Ssam * 3. All advertising materials mentioning features or use of this software 17164426Ssam * must display the following acknowledgement: 18164426Ssam * This product includes software developed by Ichiro FUKUHARA. 19164426Ssam * 4. The name of the company nor the name of the author may be used to 20164426Ssam * endorse or promote products derived from this software without specific 21164426Ssam * prior written permission. 22164426Ssam * 23164426Ssam * THIS SOFTWARE IS PROVIDED BY ICHIRO FUKUHARA ``AS IS'' AND ANY EXPRESS OR 24164426Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25164426Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26164426Ssam * IN NO EVENT SHALL ICHIRO FUKUHARA OR THE VOICES IN HIS HEAD BE LIABLE FOR 27164426Ssam * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28164426Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29164426Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30164426Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31164426Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32164426Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33164426Ssam * SUCH DAMAGE. 34164426Ssam */ 35164426Ssam 36164426Ssam#include <sys/cdefs.h> 37164426Ssam__FBSDID("$FreeBSD$"); 38164426Ssam 39164426Ssam#include <sys/param.h> 40164426Ssam#include <sys/systm.h> 41164426Ssam#include <sys/kernel.h> 42164426Ssam#include <sys/module.h> 43164426Ssam#include <sys/time.h> 44164426Ssam#include <sys/bus.h> 45164426Ssam#include <sys/resource.h> 46164426Ssam#include <sys/rman.h> 47164426Ssam#include <sys/timetc.h> 48164426Ssam 49164426Ssam#include <machine/bus.h> 50164426Ssam#include <machine/cpu.h> 51164426Ssam#include <machine/cpufunc.h> 52164426Ssam#include <machine/frame.h> 53164426Ssam#include <machine/resource.h> 54164426Ssam#include <machine/intr.h> 55164426Ssam#include <arm/xscale/ixp425/ixp425reg.h> 56164426Ssam#include <arm/xscale/ixp425/ixp425var.h> 57164426Ssam 58164426Ssamstatic uint32_t counts_per_hz; 59164426Ssam 60164426Ssam/* callback functions for intr_functions */ 61166901Spisoint ixpclk_intr(void *); 62164426Ssam 63164426Ssamstruct ixpclk_softc { 64164426Ssam device_t sc_dev; 65164426Ssam bus_addr_t sc_baseaddr; 66164426Ssam bus_space_tag_t sc_iot; 67164426Ssam bus_space_handle_t sc_ioh; 68164426Ssam}; 69164426Ssam 70164426Ssamstatic unsigned ixp425_timer_get_timecount(struct timecounter *tc); 71164426Ssam 72164426Ssam#ifndef IXP425_CLOCK_FREQ 73164426Ssam#define COUNTS_PER_SEC 66666600 /* 66MHz */ 74164426Ssam#else 75164426Ssam#define COUNTS_PER_SEC IXP425_CLOCK_FREQ 76164426Ssam#endif 77164426Ssam#define COUNTS_PER_USEC ((COUNTS_PER_SEC / 1000000) + 1) 78164426Ssam 79164426Ssamstatic struct ixpclk_softc *ixpclk_sc = NULL; 80164426Ssam 81164426Ssam#define GET_TS_VALUE(sc) (*(volatile u_int32_t *) \ 82164426Ssam (IXP425_TIMER_VBASE + IXP425_OST_TS)) 83164426Ssam 84164426Ssamstatic struct timecounter ixp425_timer_timecounter = { 85164426Ssam ixp425_timer_get_timecount, /* get_timecount */ 86164426Ssam NULL, /* no poll_pps */ 87164426Ssam ~0u, /* counter_mask */ 88164426Ssam COUNTS_PER_SEC, /* frequency */ 89186352Ssam "IXP4XX Timer", /* name */ 90164426Ssam 1000, /* quality */ 91164426Ssam}; 92164426Ssam 93164426Ssamstatic int 94164426Ssamixpclk_probe(device_t dev) 95164426Ssam{ 96186352Ssam device_set_desc(dev, "IXP4XX Timer"); 97164426Ssam return (0); 98164426Ssam} 99164426Ssam 100164426Ssamstatic int 101164426Ssamixpclk_attach(device_t dev) 102164426Ssam{ 103164426Ssam struct ixpclk_softc *sc = device_get_softc(dev); 104164426Ssam struct ixp425_softc *sa = device_get_softc(device_get_parent(dev)); 105164426Ssam 106164426Ssam ixpclk_sc = sc; 107164426Ssam 108164426Ssam sc->sc_dev = dev; 109164426Ssam sc->sc_iot = sa->sc_iot; 110164426Ssam sc->sc_baseaddr = IXP425_TIMER_HWBASE; 111164426Ssam 112164426Ssam if (bus_space_map(sc->sc_iot, sc->sc_baseaddr, 8, 0, 113164426Ssam &sc->sc_ioh)) 114164426Ssam panic("%s: Cannot map registers", device_get_name(dev)); 115164426Ssam 116164426Ssam return (0); 117164426Ssam} 118164426Ssam 119164426Ssamstatic device_method_t ixpclk_methods[] = { 120164426Ssam DEVMETHOD(device_probe, ixpclk_probe), 121164426Ssam DEVMETHOD(device_attach, ixpclk_attach), 122164426Ssam {0, 0}, 123164426Ssam}; 124164426Ssam 125164426Ssamstatic driver_t ixpclk_driver = { 126164426Ssam "ixpclk", 127164426Ssam ixpclk_methods, 128164426Ssam sizeof(struct ixpclk_softc), 129164426Ssam}; 130164426Ssamstatic devclass_t ixpclk_devclass; 131164426Ssam 132164426SsamDRIVER_MODULE(ixpclk, ixp, ixpclk_driver, ixpclk_devclass, 0, 0); 133164426Ssamstatic unsigned 134164426Ssamixp425_timer_get_timecount(struct timecounter *tc) 135164426Ssam{ 136164426Ssam uint32_t ret; 137164426Ssam 138164426Ssam ret = GET_TS_VALUE(sc); 139164426Ssam return (ret); 140164426Ssam} 141164426Ssam 142164426Ssam/* 143164426Ssam * cpu_initclocks: 144164426Ssam * 145164426Ssam * Initialize the clock and get them going. 146164426Ssam */ 147164426Ssamvoid 148164426Ssamcpu_initclocks(void) 149164426Ssam{ 150164426Ssam struct ixpclk_softc* sc = ixpclk_sc; 151164426Ssam struct resource *irq; 152164426Ssam device_t dev = sc->sc_dev; 153164426Ssam u_int oldirqstate; 154164426Ssam int rid = 0; 155164426Ssam void *ihl; 156164426Ssam 157164426Ssam if (hz < 50 || COUNTS_PER_SEC % hz) { 158164426Ssam printf("Cannot get %d Hz clock; using 100 Hz\n", hz); 159164426Ssam hz = 100; 160164426Ssam } 161164426Ssam tick = 1000000 / hz; /* number of microseconds between interrupts */ 162164426Ssam 163164426Ssam /* 164164426Ssam * We only have one timer available; stathz and profhz are 165164426Ssam * always left as 0 (the upper-layer clock code deals with 166164426Ssam * this situation). 167164426Ssam */ 168164426Ssam if (stathz != 0) 169164426Ssam printf("Cannot get %d Hz statclock\n", stathz); 170164426Ssam stathz = 0; 171164426Ssam 172164426Ssam if (profhz != 0) 173164426Ssam printf("Cannot get %d Hz profclock\n", profhz); 174164426Ssam profhz = 0; 175164426Ssam 176164426Ssam /* Report the clock frequency. */ 177164426Ssam 178164426Ssam oldirqstate = disable_interrupts(I32_bit); 179164426Ssam 180164426Ssam irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, IXP425_INT_TMR0, 181164426Ssam IXP425_INT_TMR0, 1, RF_ACTIVE); 182164426Ssam if (!irq) 183164426Ssam panic("Unable to setup the clock irq handler.\n"); 184164426Ssam else 185166901Spiso bus_setup_intr(dev, irq, INTR_TYPE_CLK, ixpclk_intr, NULL, 186166901Spiso NULL, &ihl); 187164426Ssam 188164426Ssam /* Set up the new clock parameters. */ 189164426Ssam 190164426Ssam /* clear interrupt */ 191164426Ssam bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS, 192164426Ssam OST_WARM_RESET | OST_WDOG_INT | OST_TS_INT | 193164426Ssam OST_TIM1_INT | OST_TIM0_INT); 194164426Ssam 195164426Ssam counts_per_hz = COUNTS_PER_SEC / hz; 196164426Ssam 197164426Ssam /* reload value & Timer enable */ 198164426Ssam bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_TIM0_RELOAD, 199164426Ssam (counts_per_hz & TIMERRELOAD_MASK) | OST_TIMER_EN); 200164426Ssam 201164426Ssam tc_init(&ixp425_timer_timecounter); 202164426Ssam restore_interrupts(oldirqstate); 203164426Ssam rid = 0; 204164426Ssam} 205164426Ssam 206164426Ssam 207164426Ssam/* 208164426Ssam * DELAY: 209164426Ssam * 210164426Ssam * Delay for at least N microseconds. 211164426Ssam */ 212164426Ssamvoid 213164426SsamDELAY(int n) 214164426Ssam{ 215164426Ssam u_int32_t first, last; 216164426Ssam int usecs; 217164426Ssam 218164426Ssam if (n == 0) 219164426Ssam return; 220164426Ssam 221164426Ssam /* 222164426Ssam * Clamp the timeout at a maximum value (about 32 seconds with 223164426Ssam * a 66MHz clock). *Nobody* should be delay()ing for anywhere 224164426Ssam * near that length of time and if they are, they should be hung 225164426Ssam * out to dry. 226164426Ssam */ 227164426Ssam if (n >= (0x80000000U / COUNTS_PER_USEC)) 228164426Ssam usecs = (0x80000000U / COUNTS_PER_USEC) - 1; 229164426Ssam else 230164426Ssam usecs = n * COUNTS_PER_USEC; 231164426Ssam 232164426Ssam /* Note: Timestamp timer counts *up*, unlike the other timers */ 233164426Ssam first = GET_TS_VALUE(); 234164426Ssam 235164426Ssam while (usecs > 0) { 236164426Ssam last = GET_TS_VALUE(); 237164426Ssam usecs -= (int)(last - first); 238164426Ssam first = last; 239164426Ssam } 240164426Ssam} 241164426Ssam 242164426Ssam/* 243164426Ssam * ixpclk_intr: 244164426Ssam * 245164426Ssam * Handle the hardclock interrupt. 246164426Ssam */ 247166901Spisoint 248164426Ssamixpclk_intr(void *arg) 249164426Ssam{ 250164426Ssam struct ixpclk_softc* sc = ixpclk_sc; 251164426Ssam struct trapframe *frame = arg; 252164426Ssam 253164426Ssam bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS, 254164426Ssam OST_TIM0_INT); 255164426Ssam 256164426Ssam hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); 257166901Spiso return (FILTER_HANDLED); 258164426Ssam} 259164426Ssam 260164426Ssamvoid 261164426Ssamcpu_startprofclock(void) 262164426Ssam{ 263164426Ssam} 264164426Ssam 265164426Ssamvoid 266164426Ssamcpu_stopprofclock(void) 267164426Ssam{ 268164426Ssam} 269