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: releng/10.2/sys/arm/xscale/ixp425/ixp425_timer.c 278613 2015-02-12 03:50:33Z ian $"); 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 49278613Sian#include <machine/armreg.h> 50164426Ssam#include <machine/bus.h> 51164426Ssam#include <machine/cpu.h> 52164426Ssam#include <machine/cpufunc.h> 53164426Ssam#include <machine/frame.h> 54164426Ssam#include <machine/resource.h> 55164426Ssam#include <machine/intr.h> 56164426Ssam#include <arm/xscale/ixp425/ixp425reg.h> 57164426Ssam#include <arm/xscale/ixp425/ixp425var.h> 58164426Ssam 59164426Ssamstatic uint32_t counts_per_hz; 60164426Ssam 61164426Ssam/* callback functions for intr_functions */ 62166901Spisoint ixpclk_intr(void *); 63164426Ssam 64164426Ssamstruct ixpclk_softc { 65164426Ssam device_t sc_dev; 66164426Ssam bus_addr_t sc_baseaddr; 67164426Ssam bus_space_tag_t sc_iot; 68164426Ssam bus_space_handle_t sc_ioh; 69164426Ssam}; 70164426Ssam 71164426Ssamstatic unsigned ixp425_timer_get_timecount(struct timecounter *tc); 72164426Ssam 73164426Ssam#ifndef IXP425_CLOCK_FREQ 74164426Ssam#define COUNTS_PER_SEC 66666600 /* 66MHz */ 75164426Ssam#else 76164426Ssam#define COUNTS_PER_SEC IXP425_CLOCK_FREQ 77164426Ssam#endif 78164426Ssam#define COUNTS_PER_USEC ((COUNTS_PER_SEC / 1000000) + 1) 79164426Ssam 80164426Ssamstatic struct ixpclk_softc *ixpclk_sc = NULL; 81164426Ssam 82164426Ssam#define GET_TS_VALUE(sc) (*(volatile u_int32_t *) \ 83164426Ssam (IXP425_TIMER_VBASE + IXP425_OST_TS)) 84164426Ssam 85164426Ssamstatic struct timecounter ixp425_timer_timecounter = { 86164426Ssam ixp425_timer_get_timecount, /* get_timecount */ 87164426Ssam NULL, /* no poll_pps */ 88164426Ssam ~0u, /* counter_mask */ 89164426Ssam COUNTS_PER_SEC, /* frequency */ 90186352Ssam "IXP4XX Timer", /* name */ 91164426Ssam 1000, /* quality */ 92164426Ssam}; 93164426Ssam 94164426Ssamstatic int 95164426Ssamixpclk_probe(device_t dev) 96164426Ssam{ 97186352Ssam device_set_desc(dev, "IXP4XX Timer"); 98164426Ssam return (0); 99164426Ssam} 100164426Ssam 101164426Ssamstatic int 102164426Ssamixpclk_attach(device_t dev) 103164426Ssam{ 104164426Ssam struct ixpclk_softc *sc = device_get_softc(dev); 105164426Ssam struct ixp425_softc *sa = device_get_softc(device_get_parent(dev)); 106164426Ssam 107164426Ssam ixpclk_sc = sc; 108164426Ssam 109164426Ssam sc->sc_dev = dev; 110164426Ssam sc->sc_iot = sa->sc_iot; 111164426Ssam sc->sc_baseaddr = IXP425_TIMER_HWBASE; 112164426Ssam 113164426Ssam if (bus_space_map(sc->sc_iot, sc->sc_baseaddr, 8, 0, 114164426Ssam &sc->sc_ioh)) 115164426Ssam panic("%s: Cannot map registers", device_get_name(dev)); 116164426Ssam 117164426Ssam return (0); 118164426Ssam} 119164426Ssam 120164426Ssamstatic device_method_t ixpclk_methods[] = { 121164426Ssam DEVMETHOD(device_probe, ixpclk_probe), 122164426Ssam DEVMETHOD(device_attach, ixpclk_attach), 123164426Ssam {0, 0}, 124164426Ssam}; 125164426Ssam 126164426Ssamstatic driver_t ixpclk_driver = { 127164426Ssam "ixpclk", 128164426Ssam ixpclk_methods, 129164426Ssam sizeof(struct ixpclk_softc), 130164426Ssam}; 131164426Ssamstatic devclass_t ixpclk_devclass; 132164426Ssam 133164426SsamDRIVER_MODULE(ixpclk, ixp, ixpclk_driver, ixpclk_devclass, 0, 0); 134164426Ssamstatic unsigned 135164426Ssamixp425_timer_get_timecount(struct timecounter *tc) 136164426Ssam{ 137164426Ssam uint32_t ret; 138164426Ssam 139164426Ssam ret = GET_TS_VALUE(sc); 140164426Ssam return (ret); 141164426Ssam} 142164426Ssam 143164426Ssam/* 144164426Ssam * cpu_initclocks: 145164426Ssam * 146164426Ssam * Initialize the clock and get them going. 147164426Ssam */ 148164426Ssamvoid 149164426Ssamcpu_initclocks(void) 150164426Ssam{ 151164426Ssam struct ixpclk_softc* sc = ixpclk_sc; 152164426Ssam struct resource *irq; 153164426Ssam device_t dev = sc->sc_dev; 154164426Ssam u_int oldirqstate; 155164426Ssam int rid = 0; 156164426Ssam void *ihl; 157164426Ssam 158164426Ssam if (hz < 50 || COUNTS_PER_SEC % hz) { 159164426Ssam printf("Cannot get %d Hz clock; using 100 Hz\n", hz); 160164426Ssam hz = 100; 161164426Ssam } 162164426Ssam tick = 1000000 / hz; /* number of microseconds between interrupts */ 163164426Ssam 164164426Ssam /* 165164426Ssam * We only have one timer available; stathz and profhz are 166164426Ssam * always left as 0 (the upper-layer clock code deals with 167164426Ssam * this situation). 168164426Ssam */ 169164426Ssam if (stathz != 0) 170164426Ssam printf("Cannot get %d Hz statclock\n", stathz); 171164426Ssam stathz = 0; 172164426Ssam 173164426Ssam if (profhz != 0) 174164426Ssam printf("Cannot get %d Hz profclock\n", profhz); 175164426Ssam profhz = 0; 176164426Ssam 177164426Ssam /* Report the clock frequency. */ 178164426Ssam 179278613Sian oldirqstate = disable_interrupts(PSR_I); 180164426Ssam 181164426Ssam irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, IXP425_INT_TMR0, 182164426Ssam IXP425_INT_TMR0, 1, RF_ACTIVE); 183164426Ssam if (!irq) 184164426Ssam panic("Unable to setup the clock irq handler.\n"); 185164426Ssam else 186166901Spiso bus_setup_intr(dev, irq, INTR_TYPE_CLK, ixpclk_intr, NULL, 187166901Spiso NULL, &ihl); 188164426Ssam 189164426Ssam /* Set up the new clock parameters. */ 190164426Ssam 191164426Ssam /* clear interrupt */ 192164426Ssam bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS, 193164426Ssam OST_WARM_RESET | OST_WDOG_INT | OST_TS_INT | 194164426Ssam OST_TIM1_INT | OST_TIM0_INT); 195164426Ssam 196164426Ssam counts_per_hz = COUNTS_PER_SEC / hz; 197164426Ssam 198164426Ssam /* reload value & Timer enable */ 199164426Ssam bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_TIM0_RELOAD, 200164426Ssam (counts_per_hz & TIMERRELOAD_MASK) | OST_TIMER_EN); 201164426Ssam 202164426Ssam tc_init(&ixp425_timer_timecounter); 203164426Ssam restore_interrupts(oldirqstate); 204164426Ssam rid = 0; 205164426Ssam} 206164426Ssam 207164426Ssam 208164426Ssam/* 209164426Ssam * DELAY: 210164426Ssam * 211164426Ssam * Delay for at least N microseconds. 212164426Ssam */ 213164426Ssamvoid 214164426SsamDELAY(int n) 215164426Ssam{ 216164426Ssam u_int32_t first, last; 217164426Ssam int usecs; 218164426Ssam 219164426Ssam if (n == 0) 220164426Ssam return; 221164426Ssam 222164426Ssam /* 223164426Ssam * Clamp the timeout at a maximum value (about 32 seconds with 224164426Ssam * a 66MHz clock). *Nobody* should be delay()ing for anywhere 225164426Ssam * near that length of time and if they are, they should be hung 226164426Ssam * out to dry. 227164426Ssam */ 228164426Ssam if (n >= (0x80000000U / COUNTS_PER_USEC)) 229164426Ssam usecs = (0x80000000U / COUNTS_PER_USEC) - 1; 230164426Ssam else 231164426Ssam usecs = n * COUNTS_PER_USEC; 232164426Ssam 233164426Ssam /* Note: Timestamp timer counts *up*, unlike the other timers */ 234164426Ssam first = GET_TS_VALUE(); 235164426Ssam 236164426Ssam while (usecs > 0) { 237164426Ssam last = GET_TS_VALUE(); 238164426Ssam usecs -= (int)(last - first); 239164426Ssam first = last; 240164426Ssam } 241164426Ssam} 242164426Ssam 243164426Ssam/* 244164426Ssam * ixpclk_intr: 245164426Ssam * 246164426Ssam * Handle the hardclock interrupt. 247164426Ssam */ 248166901Spisoint 249164426Ssamixpclk_intr(void *arg) 250164426Ssam{ 251164426Ssam struct ixpclk_softc* sc = ixpclk_sc; 252164426Ssam struct trapframe *frame = arg; 253164426Ssam 254164426Ssam bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS, 255164426Ssam OST_TIM0_INT); 256164426Ssam 257164426Ssam hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); 258166901Spiso return (FILTER_HANDLED); 259164426Ssam} 260164426Ssam 261164426Ssamvoid 262164426Ssamcpu_startprofclock(void) 263164426Ssam{ 264164426Ssam} 265164426Ssam 266164426Ssamvoid 267164426Ssamcpu_stopprofclock(void) 268164426Ssam{ 269164426Ssam} 270