1/*-
2 * Copyright (c) 2005, 2009-2011 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/kernel.h>
32#include <sys/bus.h>
33#include <sys/interrupt.h>
34#include <sys/priority.h>
35#include <sys/proc.h>
36#include <sys/queue.h>
37#include <sys/sysctl.h>
38#include <sys/systm.h>
39#include <sys/timeet.h>
40#include <sys/timetc.h>
41#include <sys/pcpu.h>
42
43#include <machine/cpu.h>
44#include <machine/efi.h>
45#include <machine/intr.h>
46#include <machine/intrcnt.h>
47#include <machine/md_var.h>
48#include <machine/smp.h>
49
50#define	CLOCK_ET_OFF		0
51#define	CLOCK_ET_PERIODIC	1
52#define	CLOCK_ET_ONESHOT	2
53
54static struct eventtimer ia64_clock_et;
55static u_int ia64_clock_xiv;
56
57#ifndef SMP
58static timecounter_get_t ia64_get_timecount;
59
60static struct timecounter ia64_timecounter = {
61	ia64_get_timecount,	/* get_timecount */
62	0,			/* no poll_pps */
63	~0u,			/* counter_mask */
64	0,			/* frequency */
65	"ITC"			/* name */
66};
67
68static u_int
69ia64_get_timecount(struct timecounter* tc)
70{
71	return ia64_get_itc();
72}
73#endif
74
75static u_int
76ia64_ih_clock(struct thread *td, u_int xiv, struct trapframe *tf)
77{
78	struct eventtimer *et;
79	uint64_t itc, load;
80	uint32_t mode;
81
82	PCPU_INC(md.stats.pcs_nclks);
83	intrcnt[INTRCNT_CLOCK]++;
84
85	itc = ia64_get_itc();
86	PCPU_SET(md.clock, itc);
87
88	mode = PCPU_GET(md.clock_mode);
89	if (mode == CLOCK_ET_PERIODIC) {
90		load = PCPU_GET(md.clock_load);
91		ia64_set_itm(itc + load);
92	} else
93		ia64_set_itv((1 << 16) | xiv);
94
95	ia64_set_eoi(0);
96	ia64_srlz_d();
97
98	et = &ia64_clock_et;
99	if (et->et_active)
100		et->et_event_cb(et, et->et_arg);
101	return (1);
102}
103
104/*
105 * Event timer start method.
106 */
107static int
108ia64_clock_start(struct eventtimer *et, struct bintime *first,
109    struct bintime *period)
110{
111	u_long itc, load;
112	register_t is;
113
114	if (period != NULL) {
115		PCPU_SET(md.clock_mode, CLOCK_ET_PERIODIC);
116		load = (et->et_frequency * (period->frac >> 32)) >> 32;
117		if (period->sec > 0)
118			load += et->et_frequency * period->sec;
119	} else {
120		PCPU_SET(md.clock_mode, CLOCK_ET_ONESHOT);
121		load = 0;
122	}
123
124	PCPU_SET(md.clock_load, load);
125
126	if (first != NULL) {
127		load = (et->et_frequency * (first->frac >> 32)) >> 32;
128		if (first->sec > 0)
129			load += et->et_frequency * first->sec;
130	}
131
132	is = intr_disable();
133	itc = ia64_get_itc();
134	ia64_set_itm(itc + load);
135	ia64_set_itv(ia64_clock_xiv);
136	ia64_srlz_d();
137	intr_restore(is);
138	return (0);
139}
140
141/*
142 * Event timer stop method.
143 */
144static int
145ia64_clock_stop(struct eventtimer *et)
146{
147
148	ia64_set_itv((1 << 16) | ia64_clock_xiv);
149	ia64_srlz_d();
150	PCPU_SET(md.clock_mode, CLOCK_ET_OFF);
151	PCPU_SET(md.clock_load, 0);
152	return (0);
153}
154
155/*
156 * We call cpu_initclocks() on the APs as well. It allows us to
157 * group common initialization in the same function.
158 */
159void
160cpu_initclocks()
161{
162
163	ia64_clock_stop(NULL);
164	if (PCPU_GET(cpuid) == 0)
165		cpu_initclocks_bsp();
166	else
167		cpu_initclocks_ap();
168}
169
170static void
171clock_configure(void *dummy)
172{
173	struct eventtimer *et;
174	u_long itc_freq;
175
176	ia64_clock_xiv = ia64_xiv_alloc(PI_REALTIME, IA64_XIV_IPI,
177	    ia64_ih_clock);
178	if (ia64_clock_xiv == 0)
179		panic("No XIV for clock interrupts");
180
181	itc_freq = (u_long)ia64_itc_freq() * 1000000ul;
182
183	et = &ia64_clock_et;
184	et->et_name = "ITC";
185	et->et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
186	et->et_quality = 1000;
187	et->et_frequency = itc_freq;
188	et->et_min_period.sec = 0;
189	et->et_min_period.frac = (0x8000000000000000ul / (u_long)(10*hz)) << 1;
190	et->et_max_period.sec = 0xffffffff;
191	et->et_max_period.frac = ((0xfffffffeul << 32) / itc_freq) << 32;
192	et->et_start = ia64_clock_start;
193	et->et_stop = ia64_clock_stop;
194	et->et_priv = NULL;
195	et_register(et);
196
197#ifndef SMP
198	ia64_timecounter.tc_frequency = itc_freq;
199	tc_init(&ia64_timecounter);
200#endif
201}
202SYSINIT(clkcfg, SI_SUB_CONFIGURE, SI_ORDER_SECOND, clock_configure, NULL);
203