Home | History | Annotate | Line # | Download | only in ixp12x0
ixp12x0_clk.c revision 1.13
      1 /*	$NetBSD: ixp12x0_clk.c,v 1.13 2008/04/28 20:23:14 martin 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  *
     20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 #include <sys/cdefs.h>
     34 __KERNEL_RCSID(0, "$NetBSD: ixp12x0_clk.c,v 1.13 2008/04/28 20:23:14 martin Exp $");
     35 
     36 #include <sys/types.h>
     37 #include <sys/param.h>
     38 #include <sys/atomic.h>
     39 #include <sys/systm.h>
     40 #include <sys/kernel.h>
     41 #include <sys/time.h>
     42 #include <sys/timetc.h>
     43 #include <sys/device.h>
     44 
     45 #include <machine/bus.h>
     46 #include <machine/intr.h>
     47 
     48 #include <arm/cpufunc.h>
     49 
     50 #include <arm/ixp12x0/ixpsipvar.h>
     51 
     52 #include <arm/ixp12x0/ixp12x0_pcireg.h>
     53 #include <arm/ixp12x0/ixp12x0_clkreg.h>
     54 #include <arm/ixp12x0/ixp12x0var.h>
     55 
     56 static int	ixpclk_match(struct device *, struct cfdata *, void *);
     57 static void	ixpclk_attach(struct device *, struct device *, void *);
     58 
     59 static u_int	ixpclk_get_timecount(struct timecounter *);
     60 
     61 int		gettick(void);
     62 void		rtcinit(void);
     63 
     64 /* callback functions for intr_functions */
     65 static int      ixpclk_intr(void* arg);
     66 
     67 struct ixpclk_softc {
     68 	struct device		sc_dev;
     69 	bus_addr_t		sc_baseaddr;
     70 	bus_space_tag_t		sc_iot;
     71 	bus_space_handle_t	sc_ioh;
     72 	bus_space_handle_t	sc_pll_ioh;
     73 
     74 	u_int32_t		sc_clock_count;
     75 	u_int32_t		sc_count_per_usec;
     76 	u_int32_t		sc_coreclock_freq;
     77 };
     78 
     79 #define XTAL_FREQ		3686400		/* 3.6864MHz */
     80 #define XTAL_FREQ3686400
     81 #undef XTAL_FREQ3787800
     82 #undef XTAL_FREQ3579500
     83 #define	MAX_CCF			22
     84 
     85 #if defined(XTAL_FREQ3686400)
     86 static u_int32_t ccf_to_coreclock[MAX_CCF + 1] = {
     87 	29491000,
     88 	36865000,
     89 	44237000,
     90 	51610000,
     91 	58982000,
     92 	66355000,
     93 	73728000,
     94 	81101000,
     95 	88474000,
     96 	95846000,
     97 	103219000,
     98 	110592000,
     99 	132710000,
    100 	147456000,
    101 	154829000,
    102 	162202000,
    103 	165890000,
    104 	176947000,
    105 	191693000,
    106 	199066000,
    107 	206438000,
    108 	221184000,
    109 	232243000,
    110 };
    111 #elif defined(XTAL_FREQ3787800)
    112 #elif defined(XTAL_FREQ3579500)
    113 #else
    114 #error
    115 #endif
    116 
    117 static struct ixpclk_softc *ixpclk_sc = NULL;
    118 
    119 static struct timecounter ixpclk_timecounter = {
    120 	ixpclk_get_timecount,	/* get_timecount */
    121 	0,			/* no poll_pps */
    122 	0xffffffff,		/* counter_mask */
    123 	0,			/* frequency */
    124 	"ixpclk",		/* name */
    125 	100,			/* quality */
    126 	NULL,			/* prev */
    127 	NULL,			/* next */
    128 };
    129 
    130 static volatile uint32_t ixpclk_base;
    131 
    132 #define TIMER_FREQUENCY         3686400         /* 3.6864MHz */
    133 #define TICKS_PER_MICROSECOND   (TIMER_FREQUENCY/1000000)
    134 
    135 CFATTACH_DECL(ixpclk, sizeof(struct ixpclk_softc),
    136     ixpclk_match, ixpclk_attach, NULL, NULL);
    137 
    138 #define GET_TIMER_VALUE(sc)	(bus_space_read_4((sc)->sc_iot,		\
    139 						  (sc)->sc_ioh,		\
    140 						  IXPCLK_VALUE)		\
    141 				 & IXPCL_CTV)
    142 
    143 static int
    144 ixpclk_match(struct device *parent, struct cfdata *match, void *aux)
    145 {
    146 
    147 	return 2;
    148 }
    149 
    150 static void
    151 ixpclk_attach(struct device *parent, struct device *self, void *aux)
    152 {
    153 	struct ixpclk_softc		*sc;
    154 	struct ixpsip_attach_args	*sa;
    155 	u_int32_t			ccf;
    156 	bool first_run = ixpclk_sc == NULL;
    157 
    158 	printf("\n");
    159 
    160 	sc = (struct ixpclk_softc*) self;
    161 	sa = aux;
    162 	sc->sc_iot = sa->sa_iot;
    163 	sc->sc_baseaddr = sa->sa_addr;
    164 
    165 	/* using first timer for system ticks */
    166 	if (ixpclk_sc == NULL)
    167 		ixpclk_sc = sc;
    168 
    169 	if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
    170 			  &sc->sc_ioh))
    171 		panic("%s: Cannot map registers", self->dv_xname);
    172 	if (bus_space_map(sa->sa_iot, sa->sa_addr + IXPCLK_PLL_CFG_OFFSET,
    173 			  IXPCLK_PLL_CFG_SIZE, 0, &sc->sc_pll_ioh))
    174 		panic("%s: Cannot map registers", self->dv_xname);
    175 
    176 	/* disable all channel and clear interrupt status */
    177 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
    178 			  IXPCL_DISABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
    179 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, 0);
    180 
    181 
    182 	ccf = bus_space_read_4(sc->sc_iot, sc->sc_pll_ioh, 0)
    183 		& IXP12X0_PLL_CFG_CCF;
    184 	sc->sc_coreclock_freq = ccf_to_coreclock[ccf];
    185 
    186 	sc->sc_clock_count = sc->sc_coreclock_freq / hz;
    187 	sc->sc_count_per_usec = sc->sc_coreclock_freq / 1000000;
    188 
    189 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
    190 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
    191 			  sc->sc_clock_count);
    192 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
    193 			  IXPCL_ENABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
    194 
    195 	if (first_run) {
    196 		ixpclk_timecounter.tc_frequency = sc->sc_coreclock_freq;
    197 		tc_init(&ixpclk_timecounter);
    198 	}
    199 
    200 	printf("%s: IXP12x0 Interval Timer (core clock %d.%03dMHz)\n",
    201 	       sc->sc_dev.dv_xname,
    202 	       sc->sc_coreclock_freq / 1000000,
    203 	       (sc->sc_coreclock_freq % 1000000) / 1000);
    204 }
    205 
    206 /*
    207  * ixpclk_intr:
    208  *
    209  *	Handle the hardclock interrupt.
    210  */
    211 static int
    212 ixpclk_intr(void *arg)
    213 {
    214 
    215 	bus_space_write_4(ixpclk_sc->sc_iot, ixpclk_sc->sc_ioh,
    216 			  IXPCLK_CLEAR, 1);
    217 
    218 	atomic_add_32(&ixpclk_base, ixpclk_sc->sc_coreclock_freq);
    219 
    220 	hardclock((struct clockframe*) arg);
    221 	return (1);
    222 }
    223 
    224 /*
    225  * setstatclockrate:
    226  *
    227  *	Set the rate of the statistics clock.
    228  *
    229  *	We assume that hz is either stathz or profhz, and that neither
    230  *	will change after being set by cpu_initclocks().  We could
    231  *	recalculate the intervals here, but that would be a pain.
    232  */
    233 void
    234 setstatclockrate(int newhz)
    235 {
    236 
    237 	/* use hardclock */
    238 
    239 	/* XXX should I use TIMER2? */
    240 }
    241 
    242 /*
    243  * cpu_initclocks:
    244  *
    245  *	Initialize the clock and get them going.
    246  */
    247 void
    248 cpu_initclocks(void)
    249 {
    250 	struct ixpclk_softc*	sc;
    251 
    252 	sc = ixpclk_sc;
    253 	stathz = profhz = 0;
    254 
    255 	printf("clock: hz = %d stathz = %d\n", hz, stathz);
    256 
    257 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
    258 			  IXPCL_DISABLE);
    259 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
    260 
    261 	ixp12x0_intr_establish(IXPPCI_INTR_T1, IPL_CLOCK, ixpclk_intr, NULL);
    262 
    263 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
    264 			  sc->sc_clock_count);
    265 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
    266 			  IXPCL_ENABLE | IXPCL_PERIODIC
    267 			  | IXPCL_STP_CORE);
    268 }
    269 
    270 int
    271 gettick(void)
    272 {
    273 	int	counter;
    274 	u_int	savedints;
    275 
    276 	savedints = disable_interrupts(I32_bit);
    277 	counter = GET_TIMER_VALUE(ixpclk_sc);
    278 	restore_interrupts(savedints);
    279 	return counter;
    280 }
    281 
    282 static u_int
    283 ixpclk_get_timecount(struct timecounter *tc)
    284 {
    285 	u_int	savedints, base, counter;
    286 
    287 	savedints = disable_interrupts(I32_bit);
    288 	do {
    289 		base = ixpclk_base;
    290 		counter = GET_TIMER_VALUE(ixpclk_sc);
    291 	} while (base != ixpclk_base);
    292 	restore_interrupts(savedints);
    293 
    294 	return base - counter;
    295 }
    296 
    297 /*
    298  * delay:
    299  *
    300  *	Delay for at least N microseconds.
    301  */
    302 void
    303 delay(unsigned int usecs)
    304 {
    305 	u_int32_t	count;
    306 	u_int32_t	ticks;
    307 	u_int32_t	otick;
    308 	u_int32_t	delta;
    309 	int		j;
    310 	int		csec;
    311 	int		usec;
    312 
    313 	if (ixpclk_sc == NULL) {
    314 #ifdef DEBUG
    315 		printf("delay: called befor start ixpclk\n");
    316 #endif
    317 
    318 		csec = usecs / 10000;
    319 		usec = usecs % 10000;
    320 
    321 		usecs = (TIMER_FREQUENCY / 100) * csec
    322 			+ (TIMER_FREQUENCY / 100) * usec / 10000;
    323 		/* clock isn't initialized yet */
    324 		for(; usecs > 0; usecs--)
    325 			for(j = 100; j > 0; j--)
    326 				;
    327 		return;
    328 	}
    329 
    330 	count = ixpclk_sc->sc_count_per_usec * usecs;
    331 
    332 	otick = gettick();
    333 
    334 	for (;;) {
    335 		for(j = 100; j > 0; j--)
    336 			;
    337 
    338 		ticks = gettick();
    339 		delta = otick < ticks
    340 			? ixpclk_sc->sc_clock_count + otick - ticks
    341 			: otick - ticks;
    342 
    343 		if (delta > count)
    344 			break;
    345 
    346 		count -= delta;
    347 		otick = ticks;
    348 	}
    349 }
    350