Home | History | Annotate | Line # | Download | only in ixp12x0
ixp12x0_clk.c revision 1.1
      1 /*	$NetBSD: ixp12x0_clk.c,v 1.1 2002/07/15 16:27:16 ichiro Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 Mark Brinicombe.
      5  * Copyright (c) 1997 Causality Limited.
      6  * All rights reserved.
      7  *
      8  * This code is derived from software contributed to The NetBSD Foundation
      9  * by IWAMOTO Toshihiro and Ichiro FUKUHARA.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  * 3. All advertising materials mentioning features or use of this software
     20  *    must display the following acknowledgement:
     21  *	This product includes software developed by the NetBSD
     22  *	Foundation, Inc. and its contributors.
     23  * 4. Neither the name of The NetBSD Foundation nor the names of its
     24  *    contributors may be used to endorse or promote products derived
     25  *    from this software without specific prior written permission.
     26  *
     27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     37  * POSSIBILITY OF SUCH DAMAGE.
     38  */
     39 
     40 #include <sys/types.h>
     41 #include <sys/param.h>
     42 #include <sys/systm.h>
     43 #include <sys/kernel.h>
     44 #include <sys/time.h>
     45 #include <sys/device.h>
     46 
     47 #include <machine/bus.h>
     48 #include <machine/intr.h>
     49 
     50 #include <arm/cpufunc.h>
     51 
     52 #include <evbarm/ixm1200/ixm1200reg.h>
     53 
     54 #include <arm/ixp12x0/ixpsipvar.h>
     55 
     56 #include <arm/ixp12x0/ixp12x0_pcireg.h>
     57 #include <arm/ixp12x0/ixp12x0_clkreg.h>
     58 #include <arm/ixp12x0/ixp12x0var.h>
     59 
     60 static int	ixpclk_match(struct device *, struct cfdata *, void *);
     61 static void	ixpclk_attach(struct device *, struct device *, void *);
     62 
     63 int		gettick(void);
     64 void		rtcinit(void);
     65 
     66 /* callback functions for intr_functions */
     67 static int      ixpclk_intr(void* arg);
     68 
     69 struct ixpclk_softc {
     70 	struct device		sc_dev;
     71 	bus_addr_t		sc_baseaddr;
     72 	bus_space_tag_t		sc_iot;
     73 	bus_space_handle_t	sc_ioh;
     74 
     75 	u_int32_t		sc_clock_count;
     76 	u_int32_t		sc_count_per_usec;
     77 	u_int32_t		sc_coreclock_freq;
     78 };
     79 
     80 #define XTAL_FREQ		3686400		/* 3.6864MHz */
     81 #define XTAL_FREQ3686400
     82 #undef XTAL_FREQ3787800
     83 #undef XTAL_FREQ3579500
     84 #define	MAX_CCF			22
     85 
     86 #if defined(XTAL_FREQ3686400)
     87 static u_int32_t ccf_to_coreclock[MAX_CCF + 1] = {
     88 	29491000,
     89 	36865000,
     90 	44237000,
     91 	51610000,
     92 	58982000,
     93 	66355000,
     94 	73728000,
     95 	81101000,
     96 	88474000,
     97 	95846000,
     98 	103219000,
     99 	110592000,
    100 	132710000,
    101 	147456000,
    102 	154829000,
    103 	162202000,
    104 	165890000,
    105 	176947000,
    106 	191693000,
    107 	199066000,
    108 	206438000,
    109 	221184000,
    110 	232243000,
    111 };
    112 #elif defined(XTAL_FREQ3787800)
    113 #elif defined(XTAL_FREQ3579500)
    114 #else
    115 #error
    116 #endif
    117 
    118 static struct ixpclk_softc *ixpclk_sc = NULL;
    119 
    120 #define TIMER_FREQUENCY         3686400         /* 3.6864MHz */
    121 #define TICKS_PER_MICROSECOND   (TIMER_FREQUENCY/1000000)
    122 
    123 struct cfattach ixpclk_ca = {
    124 	sizeof(struct ixpclk_softc), ixpclk_match, ixpclk_attach
    125 };
    126 
    127 #define GET_TIMER_VALUE(sc)	(bus_space_read_4((sc)->sc_iot,		\
    128 						  (sc)->sc_ioh,		\
    129 						  IXPCLK_VALUE)		\
    130 				 & IXPCL_CTV)
    131 
    132 static int
    133 ixpclk_match(parent, match, aux)
    134 	struct device *parent;
    135 	struct cfdata *match;
    136 	void *aux;
    137 {
    138 	return (1);
    139 }
    140 
    141 static void
    142 ixpclk_attach(parent, self, aux)
    143 	struct device *parent;
    144 	struct device *self;
    145 	void *aux;
    146 {
    147 	struct ixpclk_softc *sc = (struct ixpclk_softc*) self;
    148 	struct ixpsip_attach_args *sa = aux;
    149 
    150 	printf("\n");
    151 
    152 	sc->sc_iot = sa->sa_iot;
    153 	sc->sc_baseaddr = sa->sa_addr;
    154 
    155 	/* using first timer for system ticks */
    156 	if (ixpclk_sc == NULL)
    157 		ixpclk_sc = sc;
    158 
    159 	if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
    160 			  &sc->sc_ioh))
    161 		panic("%s: Cannot map registers\n", self->dv_xname);
    162 
    163 	/* disable all channel and clear interrupt status */
    164 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
    165 			  IXPCL_DISABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
    166 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, 0);
    167 	printf("%s: IXP12x0 Interval Timer\n",  sc->sc_dev.dv_xname);
    168 }
    169 
    170 /*
    171  * ixpclk_intr:
    172  *
    173  *	Handle the hardclock interrupt.
    174  */
    175 static int
    176 ixpclk_intr(void *arg)
    177 
    178 {
    179 	/* XXX XXX */
    180 	if (!(IXPREG(IXPPCI_IRQ_RAW_STATUS)
    181 	      & (1U << (IXPPCI_INTR_T1 - SYS_NIRQ))))
    182 		return (0);
    183 
    184 	bus_space_write_4(ixpclk_sc->sc_iot, ixpclk_sc->sc_ioh,
    185 			  IXPCLK_CLEAR, 1);
    186 
    187 	hardclock((struct clockframe*) arg);
    188 	return (1);
    189 }
    190 
    191 /*
    192  * setstatclockrate:
    193  *
    194  *	Set the rate of the statistics clock.
    195  *
    196  *	We assume that hz is either stathz or profhz, and that neither
    197  *	will change after being set by cpu_initclocks().  We could
    198  *	recalculate the intervals here, but that would be a pain.
    199  */
    200 void
    201 setstatclockrate(hz)
    202 	int hz;
    203 {
    204 	/* use hardclock */
    205 
    206 	/* XXX should I use TIMER2? */
    207 }
    208 
    209 /*
    210  * cpu_initclocks:
    211  *
    212  *	Initialize the clock and get them going.
    213  */
    214 void
    215 cpu_initclocks()
    216 {
    217 	struct ixpclk_softc*	sc = ixpclk_sc;
    218 	u_int32_t		ccf;
    219 
    220 	stathz = profhz = 0;
    221 
    222 	printf("clock: hz = %d stathz = %d\n", hz, stathz);
    223 
    224 	ccf = IXPREG(IXP12X0_PLL_CFG) & IXP12X0_PLL_CFG_CCF;
    225 	sc->sc_coreclock_freq = ccf_to_coreclock[ccf];
    226 
    227 	printf("pll_cfg:ccf = %x coreclock frequency = %dHz\n",
    228 	       ccf, sc->sc_coreclock_freq);
    229 
    230 	sc->sc_clock_count = sc->sc_coreclock_freq / hz;
    231 	sc->sc_count_per_usec = sc->sc_coreclock_freq / 10000000;
    232 
    233 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
    234 
    235 	ixp12x0_intr_establish(IXPPCI_INTR_T1, IPL_CLOCK, ixpclk_intr, NULL);
    236 
    237 	IXPREG(IXPPCI_IRQ_ENABLE_SET) = (1U << (IXPPCI_INTR_T1 - SYS_NIRQ));
    238 
    239 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
    240 			  sc->sc_clock_count);
    241 #if 1 /* XXX */
    242 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
    243 			  IXPCL_ENABLE | IXPCL_PERIODIC
    244 			  | IXPCL_STP_CORE);
    245 #else
    246 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
    247 			  IXPCL_ENABLE | IXPCL_PERIODIC
    248 			  | IXPCL_STP_DIV256);
    249 #endif
    250 }
    251 
    252 int
    253 gettick()
    254 {
    255 	int counter;
    256 	u_int savedints;
    257 	savedints = disable_interrupts(I32_bit);
    258 
    259 	counter = GET_TIMER_VALUE(ixpclk_sc);
    260 
    261 	restore_interrupts(savedints);
    262 	return counter;
    263 }
    264 
    265 /*
    266  * microtime:
    267  *
    268  *	Fill in the specified timeval struct with the current time
    269  *	accurate to the microsecond.
    270  */
    271 void
    272 microtime(tvp)
    273 	register struct timeval *tvp;
    274 {
    275 	u_int			oldirqstate;
    276 	u_int32_t		counts;
    277 	static struct timeval	lasttv;
    278 
    279 	if (ixpclk_sc == NULL) {
    280 #ifdef DEBUG
    281 		printf("microtime: called befor initialize ixpclk\n");
    282 #endif
    283 		tvp->tv_sec = 0;
    284 		tvp->tv_usec = 0;
    285 		return;
    286 	}
    287 
    288 	oldirqstate = disable_interrupts(I32_bit);
    289 
    290 	counts = ixpclk_sc->sc_clock_count - GET_TIMER_VALUE(ixpclk_sc);
    291 
    292 #ifdef DEBUG
    293 	printf("microtime: counts = %d\n", counts);
    294 #endif
    295 
    296         /* Fill in the timeval struct. */
    297 	*tvp = time;
    298 	tvp->tv_usec += counts / ixpclk_sc->sc_count_per_usec;
    299 
    300         /* Make sure microseconds doesn't overflow. */
    301 	while (tvp->tv_usec >= 1000000) {
    302 		tvp->tv_usec -= 1000000;
    303 		tvp->tv_sec++;
    304 	}
    305 
    306         /* Make sure the time has advanced. */
    307 	if (tvp->tv_sec == lasttv.tv_sec &&
    308 	    tvp->tv_usec <= lasttv.tv_usec) {
    309 		tvp->tv_usec = lasttv.tv_usec + 1;
    310 		if (tvp->tv_usec >= 1000000) {
    311 			tvp->tv_usec -= 1000000;
    312 			tvp->tv_sec++;
    313 		}
    314 	}
    315 
    316 	lasttv = *tvp;
    317 
    318 	restore_interrupts(oldirqstate);
    319 }
    320 
    321 /*
    322  * delay:
    323  *
    324  *	Delay for at least N microseconds.
    325  */
    326 void
    327 delay(usecs)
    328 	u_int usecs;
    329 {
    330 	u_int32_t tick, otick, delta;
    331 	int j, csec, usec;
    332 
    333 	csec = usecs / 10000;
    334 	usec = usecs % 10000;
    335 
    336 	if (ixpclk_sc == NULL) {
    337 		static u_int32_t	coreclock_freq = 0;
    338 
    339 #ifdef DEBUG
    340 		printf("delay: called befor initialize ixpclk\n");
    341 #endif
    342 		if (coreclock_freq == 0) {
    343 			coreclock_freq
    344 				= ccf_to_coreclock[IXPREG(IXP12X0_PLL_CFG)
    345 						   & IXP12X0_PLL_CFG_CCF];
    346 		}
    347 
    348 		usecs = (TIMER_FREQUENCY / 100) * csec
    349 			+ (TIMER_FREQUENCY / 100) * usec / 10000;
    350 		/* clock isn't initialized yet */
    351 		for(; usecs > 0; usecs--)
    352 			for(j = 100; j > 0; j--)
    353 				;
    354 		return;
    355 	}
    356 
    357 	usecs = (ixpclk_sc->sc_coreclock_freq / 100) * csec
    358 		+ (ixpclk_sc->sc_coreclock_freq / 100) * usec / 10000;
    359 
    360 	otick = gettick();
    361 
    362 	while (1) {
    363 		for(j = 100; j > 0; j--)
    364 			;
    365 		tick = gettick();
    366 
    367 		delta = otick > tick
    368 			? otick - tick
    369 			: otick - tick + IXPCL_ITV + 1;
    370 		if (delta > usecs)
    371 			break;
    372 		usecs -= delta;
    373 		otick = tick;
    374 	}
    375 }
    376 
    377 void
    378 resettodr()
    379 {
    380 }
    381 
    382 void
    383 inittodr(base)
    384 	time_t base;
    385 {
    386 	time.tv_sec = base;
    387 	time.tv_usec = 0;
    388 }
    389