Home | History | Annotate | Line # | Download | only in ixp12x0
ixp12x0_clk.c revision 1.9
      1 /*	$NetBSD: ixp12x0_clk.c,v 1.9 2005/06/04 21:19:23 he 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/cdefs.h>
     41 __KERNEL_RCSID(0, "$NetBSD: ixp12x0_clk.c,v 1.9 2005/06/04 21:19:23 he Exp $");
     42 
     43 #include <sys/types.h>
     44 #include <sys/param.h>
     45 #include <sys/systm.h>
     46 #include <sys/kernel.h>
     47 #include <sys/time.h>
     48 #include <sys/device.h>
     49 
     50 #include <machine/bus.h>
     51 #include <machine/intr.h>
     52 
     53 #include <arm/cpufunc.h>
     54 
     55 #include <arm/ixp12x0/ixpsipvar.h>
     56 
     57 #include <arm/ixp12x0/ixp12x0_pcireg.h>
     58 #include <arm/ixp12x0/ixp12x0_clkreg.h>
     59 #include <arm/ixp12x0/ixp12x0var.h>
     60 
     61 static int	ixpclk_match(struct device *, struct cfdata *, void *);
     62 static void	ixpclk_attach(struct device *, struct device *, void *);
     63 
     64 int		gettick(void);
     65 void		rtcinit(void);
     66 
     67 /* callback functions for intr_functions */
     68 static int      ixpclk_intr(void* arg);
     69 
     70 struct ixpclk_softc {
     71 	struct device		sc_dev;
     72 	bus_addr_t		sc_baseaddr;
     73 	bus_space_tag_t		sc_iot;
     74 	bus_space_handle_t	sc_ioh;
     75 	bus_space_handle_t	sc_pll_ioh;
     76 
     77 	u_int32_t		sc_clock_count;
     78 	u_int32_t		sc_count_per_usec;
     79 	u_int32_t		sc_coreclock_freq;
     80 };
     81 
     82 #define XTAL_FREQ		3686400		/* 3.6864MHz */
     83 #define XTAL_FREQ3686400
     84 #undef XTAL_FREQ3787800
     85 #undef XTAL_FREQ3579500
     86 #define	MAX_CCF			22
     87 
     88 #if defined(XTAL_FREQ3686400)
     89 static u_int32_t ccf_to_coreclock[MAX_CCF + 1] = {
     90 	29491000,
     91 	36865000,
     92 	44237000,
     93 	51610000,
     94 	58982000,
     95 	66355000,
     96 	73728000,
     97 	81101000,
     98 	88474000,
     99 	95846000,
    100 	103219000,
    101 	110592000,
    102 	132710000,
    103 	147456000,
    104 	154829000,
    105 	162202000,
    106 	165890000,
    107 	176947000,
    108 	191693000,
    109 	199066000,
    110 	206438000,
    111 	221184000,
    112 	232243000,
    113 };
    114 #elif defined(XTAL_FREQ3787800)
    115 #elif defined(XTAL_FREQ3579500)
    116 #else
    117 #error
    118 #endif
    119 
    120 static struct ixpclk_softc *ixpclk_sc = NULL;
    121 
    122 #define TIMER_FREQUENCY         3686400         /* 3.6864MHz */
    123 #define TICKS_PER_MICROSECOND   (TIMER_FREQUENCY/1000000)
    124 
    125 CFATTACH_DECL(ixpclk, sizeof(struct ixpclk_softc),
    126     ixpclk_match, ixpclk_attach, NULL, NULL);
    127 
    128 #define GET_TIMER_VALUE(sc)	(bus_space_read_4((sc)->sc_iot,		\
    129 						  (sc)->sc_ioh,		\
    130 						  IXPCLK_VALUE)		\
    131 				 & IXPCL_CTV)
    132 
    133 static int
    134 ixpclk_match(struct device *parent, struct cfdata *match, void *aux)
    135 {
    136 
    137 	return 2;
    138 }
    139 
    140 static void
    141 ixpclk_attach(struct device *parent, struct device *self, void *aux)
    142 {
    143 	struct ixpclk_softc		*sc;
    144 	struct ixpsip_attach_args	*sa;
    145 	u_int32_t			ccf;
    146 
    147 	printf("\n");
    148 
    149 	sc = (struct ixpclk_softc*) self;
    150 	sa = aux;
    151 	sc->sc_iot = sa->sa_iot;
    152 	sc->sc_baseaddr = sa->sa_addr;
    153 
    154 	/* using first timer for system ticks */
    155 	if (ixpclk_sc == NULL)
    156 		ixpclk_sc = sc;
    157 
    158 	if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
    159 			  &sc->sc_ioh))
    160 		panic("%s: Cannot map registers", self->dv_xname);
    161 	if (bus_space_map(sa->sa_iot, sa->sa_addr + IXPCLK_PLL_CFG_OFFSET,
    162 			  IXPCLK_PLL_CFG_SIZE, 0, &sc->sc_pll_ioh))
    163 		panic("%s: Cannot map registers", self->dv_xname);
    164 
    165 	/* disable all channel and clear interrupt status */
    166 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
    167 			  IXPCL_DISABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
    168 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, 0);
    169 
    170 
    171 	ccf = bus_space_read_4(sc->sc_iot, sc->sc_pll_ioh, 0)
    172 		& IXP12X0_PLL_CFG_CCF;
    173 	sc->sc_coreclock_freq = ccf_to_coreclock[ccf];
    174 
    175 	sc->sc_clock_count = sc->sc_coreclock_freq / hz;
    176 	sc->sc_count_per_usec = sc->sc_coreclock_freq / 1000000;
    177 
    178 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
    179 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
    180 			  sc->sc_clock_count);
    181 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
    182 			  IXPCL_ENABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
    183 
    184 	printf("%s: IXP12x0 Interval Timer (core clock %d.%03dMHz)\n",
    185 	       sc->sc_dev.dv_xname,
    186 	       sc->sc_coreclock_freq / 1000000,
    187 	       (sc->sc_coreclock_freq % 1000000) / 1000);
    188 }
    189 
    190 /*
    191  * ixpclk_intr:
    192  *
    193  *	Handle the hardclock interrupt.
    194  */
    195 static int
    196 ixpclk_intr(void *arg)
    197 {
    198 
    199 	bus_space_write_4(ixpclk_sc->sc_iot, ixpclk_sc->sc_ioh,
    200 			  IXPCLK_CLEAR, 1);
    201 
    202 	hardclock((struct clockframe*) arg);
    203 	return (1);
    204 }
    205 
    206 /*
    207  * setstatclockrate:
    208  *
    209  *	Set the rate of the statistics clock.
    210  *
    211  *	We assume that hz is either stathz or profhz, and that neither
    212  *	will change after being set by cpu_initclocks().  We could
    213  *	recalculate the intervals here, but that would be a pain.
    214  */
    215 void
    216 setstatclockrate(int newhz)
    217 {
    218 
    219 	/* use hardclock */
    220 
    221 	/* XXX should I use TIMER2? */
    222 }
    223 
    224 /*
    225  * cpu_initclocks:
    226  *
    227  *	Initialize the clock and get them going.
    228  */
    229 void
    230 cpu_initclocks(void)
    231 {
    232 	struct ixpclk_softc*	sc;
    233 
    234 	sc = ixpclk_sc;
    235 	stathz = profhz = 0;
    236 
    237 	printf("clock: hz = %d stathz = %d\n", hz, stathz);
    238 
    239 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
    240 			  IXPCL_DISABLE);
    241 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
    242 
    243 	ixp12x0_intr_establish(IXPPCI_INTR_T1, IPL_CLOCK, ixpclk_intr, NULL);
    244 
    245 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
    246 			  sc->sc_clock_count);
    247 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
    248 			  IXPCL_ENABLE | IXPCL_PERIODIC
    249 			  | IXPCL_STP_CORE);
    250 }
    251 
    252 int
    253 gettick(void)
    254 {
    255 	int	counter;
    256 	u_int	savedints;
    257 
    258 	savedints = disable_interrupts(I32_bit);
    259 	counter = GET_TIMER_VALUE(ixpclk_sc);
    260 	restore_interrupts(savedints);
    261 	return counter;
    262 }
    263 
    264 /*
    265  * microtime:
    266  *
    267  *	Fill in the specified timeval struct with the current time
    268  *	accurate to the microsecond.
    269  */
    270 void
    271 microtime(register struct timeval *tvp)
    272 {
    273 	u_int			oldirqstate;
    274 	u_int32_t		counts;
    275 	static struct timeval	lasttv;
    276 
    277 	if (ixpclk_sc == NULL) {
    278 #ifdef DEBUG
    279 		printf("microtime: called befor initialize ixpclk\n");
    280 #endif
    281 		tvp->tv_sec = 0;
    282 		tvp->tv_usec = 0;
    283 		return;
    284 	}
    285 
    286 	oldirqstate = disable_interrupts(I32_bit);
    287 
    288 	counts = ixpclk_sc->sc_clock_count - GET_TIMER_VALUE(ixpclk_sc);
    289 
    290         /* Fill in the timeval struct. */
    291 	*tvp = time;
    292 	tvp->tv_usec += counts / ixpclk_sc->sc_count_per_usec;
    293 
    294         /* Make sure microseconds doesn't overflow. */
    295 	while (tvp->tv_usec >= 1000000) {
    296 		tvp->tv_usec -= 1000000;
    297 		tvp->tv_sec++;
    298 	}
    299 
    300         /* Make sure the time has advanced. */
    301 	if (tvp->tv_sec == lasttv.tv_sec &&
    302 	    tvp->tv_usec <= lasttv.tv_usec) {
    303 		tvp->tv_usec = lasttv.tv_usec + 1;
    304 		if (tvp->tv_usec >= 1000000) {
    305 			tvp->tv_usec -= 1000000;
    306 			tvp->tv_sec++;
    307 		}
    308 	}
    309 
    310 	lasttv = *tvp;
    311 
    312 	restore_interrupts(oldirqstate);
    313 }
    314 
    315 /*
    316  * delay:
    317  *
    318  *	Delay for at least N microseconds.
    319  */
    320 void
    321 delay(unsigned int usecs)
    322 {
    323 	u_int32_t	count;
    324 	u_int32_t	ticks;
    325 	u_int32_t	otick;
    326 	u_int32_t	delta;
    327 	int		j;
    328 	int		csec;
    329 	int		usec;
    330 
    331 	if (ixpclk_sc == NULL) {
    332 #ifdef DEBUG
    333 		printf("delay: called befor start ixpclk\n");
    334 #endif
    335 
    336 		csec = usecs / 10000;
    337 		usec = usecs % 10000;
    338 
    339 		usecs = (TIMER_FREQUENCY / 100) * csec
    340 			+ (TIMER_FREQUENCY / 100) * usec / 10000;
    341 		/* clock isn't initialized yet */
    342 		for(; usecs > 0; usecs--)
    343 			for(j = 100; j > 0; j--)
    344 				;
    345 		return;
    346 	}
    347 
    348 	count = ixpclk_sc->sc_count_per_usec * usecs;
    349 
    350 	otick = gettick();
    351 
    352 	for (;;) {
    353 		for(j = 100; j > 0; j--)
    354 			;
    355 
    356 		ticks = gettick();
    357 		delta = otick < ticks
    358 			? ixpclk_sc->sc_clock_count + otick - ticks
    359 			: otick - ticks;
    360 
    361 		if (delta > count)
    362 			break;
    363 
    364 		count -= delta;
    365 		otick = ticks;
    366 	}
    367 }
    368 
    369 void
    370 resettodr(void)
    371 {
    372 }
    373 
    374 void
    375 inittodr(time_t base)
    376 {
    377 
    378 	time.tv_sec = base;
    379 	time.tv_usec = 0;
    380 }
    381