1/* $NetBSD: ixp425_timer.c,v 1.11 2006/04/10 03:36:03 simonb Exp $ */ 2 3/* 4 * Copyright (c) 2003 5 * Ichiro FUKUHARA <ichiro@ichiro.org>. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by Ichiro FUKUHARA. 19 * 4. The name of the company nor the name of the author may be used to 20 * endorse or promote products derived from this software without specific 21 * prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY ICHIRO FUKUHARA ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL ICHIRO FUKUHARA OR THE VOICES IN HIS HEAD BE LIABLE FOR 27 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include <sys/cdefs.h> 37__FBSDID("$FreeBSD: releng/10.2/sys/arm/xscale/ixp425/ixp425_timer.c 278613 2015-02-12 03:50:33Z ian $"); 38 39#include <sys/param.h> 40#include <sys/systm.h> 41#include <sys/kernel.h> 42#include <sys/module.h> 43#include <sys/time.h> 44#include <sys/bus.h> 45#include <sys/resource.h> 46#include <sys/rman.h> 47#include <sys/timetc.h> 48 49#include <machine/armreg.h> 50#include <machine/bus.h> 51#include <machine/cpu.h> 52#include <machine/cpufunc.h> 53#include <machine/frame.h> 54#include <machine/resource.h> 55#include <machine/intr.h> 56#include <arm/xscale/ixp425/ixp425reg.h> 57#include <arm/xscale/ixp425/ixp425var.h> 58 59static uint32_t counts_per_hz; 60 61/* callback functions for intr_functions */ 62int ixpclk_intr(void *); 63 64struct ixpclk_softc { 65 device_t sc_dev; 66 bus_addr_t sc_baseaddr; 67 bus_space_tag_t sc_iot; 68 bus_space_handle_t sc_ioh; 69}; 70 71static unsigned ixp425_timer_get_timecount(struct timecounter *tc); 72 73#ifndef IXP425_CLOCK_FREQ 74#define COUNTS_PER_SEC 66666600 /* 66MHz */ 75#else 76#define COUNTS_PER_SEC IXP425_CLOCK_FREQ 77#endif 78#define COUNTS_PER_USEC ((COUNTS_PER_SEC / 1000000) + 1) 79 80static struct ixpclk_softc *ixpclk_sc = NULL; 81 82#define GET_TS_VALUE(sc) (*(volatile u_int32_t *) \ 83 (IXP425_TIMER_VBASE + IXP425_OST_TS)) 84 85static struct timecounter ixp425_timer_timecounter = { 86 ixp425_timer_get_timecount, /* get_timecount */ 87 NULL, /* no poll_pps */ 88 ~0u, /* counter_mask */ 89 COUNTS_PER_SEC, /* frequency */ 90 "IXP4XX Timer", /* name */ 91 1000, /* quality */ 92}; 93 94static int 95ixpclk_probe(device_t dev) 96{ 97 device_set_desc(dev, "IXP4XX Timer"); 98 return (0); 99} 100 101static int 102ixpclk_attach(device_t dev) 103{ 104 struct ixpclk_softc *sc = device_get_softc(dev); 105 struct ixp425_softc *sa = device_get_softc(device_get_parent(dev)); 106 107 ixpclk_sc = sc; 108 109 sc->sc_dev = dev; 110 sc->sc_iot = sa->sc_iot; 111 sc->sc_baseaddr = IXP425_TIMER_HWBASE; 112 113 if (bus_space_map(sc->sc_iot, sc->sc_baseaddr, 8, 0, 114 &sc->sc_ioh)) 115 panic("%s: Cannot map registers", device_get_name(dev)); 116 117 return (0); 118} 119 120static device_method_t ixpclk_methods[] = { 121 DEVMETHOD(device_probe, ixpclk_probe), 122 DEVMETHOD(device_attach, ixpclk_attach), 123 {0, 0}, 124}; 125 126static driver_t ixpclk_driver = { 127 "ixpclk", 128 ixpclk_methods, 129 sizeof(struct ixpclk_softc), 130}; 131static devclass_t ixpclk_devclass; 132 133DRIVER_MODULE(ixpclk, ixp, ixpclk_driver, ixpclk_devclass, 0, 0); 134static unsigned 135ixp425_timer_get_timecount(struct timecounter *tc) 136{ 137 uint32_t ret; 138 139 ret = GET_TS_VALUE(sc); 140 return (ret); 141} 142 143/* 144 * cpu_initclocks: 145 * 146 * Initialize the clock and get them going. 147 */ 148void 149cpu_initclocks(void) 150{ 151 struct ixpclk_softc* sc = ixpclk_sc; 152 struct resource *irq; 153 device_t dev = sc->sc_dev; 154 u_int oldirqstate; 155 int rid = 0; 156 void *ihl; 157 158 if (hz < 50 || COUNTS_PER_SEC % hz) { 159 printf("Cannot get %d Hz clock; using 100 Hz\n", hz); 160 hz = 100; 161 } 162 tick = 1000000 / hz; /* number of microseconds between interrupts */ 163 164 /* 165 * We only have one timer available; stathz and profhz are 166 * always left as 0 (the upper-layer clock code deals with 167 * this situation). 168 */ 169 if (stathz != 0) 170 printf("Cannot get %d Hz statclock\n", stathz); 171 stathz = 0; 172 173 if (profhz != 0) 174 printf("Cannot get %d Hz profclock\n", profhz); 175 profhz = 0; 176 177 /* Report the clock frequency. */ 178 179 oldirqstate = disable_interrupts(PSR_I); 180 181 irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, IXP425_INT_TMR0, 182 IXP425_INT_TMR0, 1, RF_ACTIVE); 183 if (!irq) 184 panic("Unable to setup the clock irq handler.\n"); 185 else 186 bus_setup_intr(dev, irq, INTR_TYPE_CLK, ixpclk_intr, NULL, 187 NULL, &ihl); 188 189 /* Set up the new clock parameters. */ 190 191 /* clear interrupt */ 192 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS, 193 OST_WARM_RESET | OST_WDOG_INT | OST_TS_INT | 194 OST_TIM1_INT | OST_TIM0_INT); 195 196 counts_per_hz = COUNTS_PER_SEC / hz; 197 198 /* reload value & Timer enable */ 199 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_TIM0_RELOAD, 200 (counts_per_hz & TIMERRELOAD_MASK) | OST_TIMER_EN); 201 202 tc_init(&ixp425_timer_timecounter); 203 restore_interrupts(oldirqstate); 204 rid = 0; 205} 206 207 208/* 209 * DELAY: 210 * 211 * Delay for at least N microseconds. 212 */ 213void 214DELAY(int n) 215{ 216 u_int32_t first, last; 217 int usecs; 218 219 if (n == 0) 220 return; 221 222 /* 223 * Clamp the timeout at a maximum value (about 32 seconds with 224 * a 66MHz clock). *Nobody* should be delay()ing for anywhere 225 * near that length of time and if they are, they should be hung 226 * out to dry. 227 */ 228 if (n >= (0x80000000U / COUNTS_PER_USEC)) 229 usecs = (0x80000000U / COUNTS_PER_USEC) - 1; 230 else 231 usecs = n * COUNTS_PER_USEC; 232 233 /* Note: Timestamp timer counts *up*, unlike the other timers */ 234 first = GET_TS_VALUE(); 235 236 while (usecs > 0) { 237 last = GET_TS_VALUE(); 238 usecs -= (int)(last - first); 239 first = last; 240 } 241} 242 243/* 244 * ixpclk_intr: 245 * 246 * Handle the hardclock interrupt. 247 */ 248int 249ixpclk_intr(void *arg) 250{ 251 struct ixpclk_softc* sc = ixpclk_sc; 252 struct trapframe *frame = arg; 253 254 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS, 255 OST_TIM0_INT); 256 257 hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); 258 return (FILTER_HANDLED); 259} 260 261void 262cpu_startprofclock(void) 263{ 264} 265 266void 267cpu_stopprofclock(void) 268{ 269} 270