1/*	$NetBSD: ixp12x0_clk.c,v 1.19 2021/07/31 14:36:33 andvar 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.19 2021/07/31 14:36:33 andvar 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
63static int	ixpclk_match(device_t, cfdata_t, void *);
64static void	ixpclk_attach(device_t, device_t, void *);
65
66static u_int	ixpclk_get_timecount(struct timecounter *);
67
68int		gettick(void);
69void		rtcinit(void);
70
71/* callback functions for intr_functions */
72static int      ixpclk_intr(void* arg);
73
74struct 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	uint32_t		sc_clock_count;
81	uint32_t		sc_count_per_usec;
82	uint32_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)
92static uint32_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
123static struct ixpclk_softc *ixpclk_sc = NULL;
124
125static struct timecounter ixpclk_timecounter = {
126	.tc_get_timecount = ixpclk_get_timecount,
127	.tc_counter_mask = 0xffffffff,
128	.tc_name = "ixpclk",
129	.tc_quality = 100,
130};
131
132static volatile uint32_t ixpclk_base;
133
134#define TIMER_FREQUENCY         3686400         /* 3.6864MHz */
135#define TICKS_PER_MICROSECOND   (TIMER_FREQUENCY/1000000)
136
137CFATTACH_DECL_NEW(ixpclk, sizeof(struct ixpclk_softc),
138    ixpclk_match, ixpclk_attach, NULL, NULL);
139
140#define GET_TIMER_VALUE(sc)	(bus_space_read_4((sc)->sc_iot,		\
141						  (sc)->sc_ioh,		\
142						  IXPCLK_VALUE)		\
143				 & IXPCL_CTV)
144
145static int
146ixpclk_match(device_t parent, cfdata_t match, void *aux)
147{
148
149	return 2;
150}
151
152static void
153ixpclk_attach(device_t parent, device_t self, void *aux)
154{
155	struct ixpclk_softc		*sc;
156	struct ixpsip_attach_args	*sa;
157	uint32_t			ccf;
158	bool first_run = ixpclk_sc == NULL;
159
160	printf("\n");
161
162	sc = device_private(self);
163	sa = aux;
164	sc->sc_iot = sa->sa_iot;
165	sc->sc_baseaddr = sa->sa_addr;
166
167	/* using first timer for system ticks */
168	if (ixpclk_sc == NULL)
169		ixpclk_sc = sc;
170
171	if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
172			  &sc->sc_ioh))
173		panic("%s: Cannot map registers", device_xname(self));
174	if (bus_space_map(sa->sa_iot, sa->sa_addr + IXPCLK_PLL_CFG_OFFSET,
175			  IXPCLK_PLL_CFG_SIZE, 0, &sc->sc_pll_ioh))
176		panic("%s: Cannot map registers", device_xname(self));
177
178	/* disable all channel and clear interrupt status */
179	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
180			  IXPCL_DISABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
181	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, 0);
182
183
184	ccf = bus_space_read_4(sc->sc_iot, sc->sc_pll_ioh, 0)
185		& IXP12X0_PLL_CFG_CCF;
186	sc->sc_coreclock_freq = ccf_to_coreclock[ccf];
187
188	sc->sc_clock_count = sc->sc_coreclock_freq / hz;
189	sc->sc_count_per_usec = sc->sc_coreclock_freq / 1000000;
190
191	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
192	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
193			  sc->sc_clock_count);
194	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
195			  IXPCL_ENABLE | IXPCL_PERIODIC | IXPCL_STP_CORE);
196
197	if (first_run) {
198		ixpclk_timecounter.tc_frequency = sc->sc_coreclock_freq;
199		tc_init(&ixpclk_timecounter);
200	}
201
202	printf("%s: IXP12x0 Interval Timer (core clock %d.%03dMHz)\n",
203	       device_xname(self),
204	       sc->sc_coreclock_freq / 1000000,
205	       (sc->sc_coreclock_freq % 1000000) / 1000);
206}
207
208/*
209 * ixpclk_intr:
210 *
211 *	Handle the hardclock interrupt.
212 */
213static int
214ixpclk_intr(void *arg)
215{
216
217	bus_space_write_4(ixpclk_sc->sc_iot, ixpclk_sc->sc_ioh,
218			  IXPCLK_CLEAR, 1);
219
220	atomic_add_32(&ixpclk_base, ixpclk_sc->sc_coreclock_freq);
221
222	hardclock((struct clockframe*) arg);
223	return (1);
224}
225
226/*
227 * setstatclockrate:
228 *
229 *	Set the rate of the statistics clock.
230 *
231 *	We assume that hz is either stathz or profhz, and that neither
232 *	will change after being set by cpu_initclocks().  We could
233 *	recalculate the intervals here, but that would be a pain.
234 */
235void
236setstatclockrate(int newhz)
237{
238
239	/* use hardclock */
240
241	/* XXX should I use TIMER2? */
242}
243
244/*
245 * cpu_initclocks:
246 *
247 *	Initialize the clock and get them going.
248 */
249void
250cpu_initclocks(void)
251{
252	struct ixpclk_softc*	sc;
253
254	sc = ixpclk_sc;
255	stathz = profhz = 0;
256
257	printf("clock: hz = %d stathz = %d\n", hz, stathz);
258
259	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
260			  IXPCL_DISABLE);
261	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CLEAR, IXPT_CLEAR);
262
263	ixp12x0_intr_establish(IXPPCI_INTR_T1, IPL_CLOCK, ixpclk_intr, NULL);
264
265	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_LOAD,
266			  sc->sc_clock_count);
267	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXPCLK_CONTROL,
268			  IXPCL_ENABLE | IXPCL_PERIODIC
269			  | IXPCL_STP_CORE);
270}
271
272int
273gettick(void)
274{
275	int	counter;
276	u_int	savedints;
277
278	savedints = disable_interrupts(I32_bit);
279	counter = GET_TIMER_VALUE(ixpclk_sc);
280	restore_interrupts(savedints);
281	return counter;
282}
283
284static u_int
285ixpclk_get_timecount(struct timecounter *tc)
286{
287	u_int	savedints, base, counter;
288
289	savedints = disable_interrupts(I32_bit);
290	do {
291		base = ixpclk_base;
292		counter = GET_TIMER_VALUE(ixpclk_sc);
293	} while (base != ixpclk_base);
294	restore_interrupts(savedints);
295
296	return base - counter;
297}
298
299/*
300 * delay:
301 *
302 *	Delay for at least N microseconds.
303 */
304void
305delay(unsigned int usecs)
306{
307	uint32_t	count;
308	uint32_t	ticks;
309	uint32_t	otick;
310	uint32_t	delta;
311	int		j;
312	int		csec;
313	int		usec;
314
315	if (ixpclk_sc == NULL) {
316#ifdef DEBUG
317		printf("delay: called before start ixpclk\n");
318#endif
319
320		csec = usecs / 10000;
321		usec = usecs % 10000;
322
323		usecs = (TIMER_FREQUENCY / 100) * csec
324			+ (TIMER_FREQUENCY / 100) * usec / 10000;
325		/* clock isn't initialized yet */
326		for(; usecs > 0; usecs--)
327			for(j = 100; j > 0; j--)
328				;
329		return;
330	}
331
332	count = ixpclk_sc->sc_count_per_usec * usecs;
333
334	otick = gettick();
335
336	for (;;) {
337		for(j = 100; j > 0; j--)
338			;
339
340		ticks = gettick();
341		delta = otick < ticks
342			? ixpclk_sc->sc_clock_count + otick - ticks
343			: otick - ticks;
344
345		if (delta > count)
346			break;
347
348		count -= delta;
349		otick = ticks;
350	}
351}
352