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