clock.c revision 268195
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: stable/10/sys/ia64/ia64/clock.c 268195 2014-07-02 23:23:18Z marcel $");
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	struct trapframe *stf;
80	uint64_t itc, load;
81	uint32_t mode;
82
83	PCPU_INC(md.stats.pcs_nclks);
84	intrcnt[INTRCNT_CLOCK]++;
85
86	itc = ia64_get_itc();
87	PCPU_SET(md.clock, itc);
88
89	mode = PCPU_GET(md.clock_mode);
90	if (mode == CLOCK_ET_PERIODIC) {
91		load = PCPU_GET(md.clock_load);
92		ia64_set_itm(itc + load);
93	} else
94		ia64_set_itv((1 << 16) | xiv);
95
96	ia64_set_eoi(0);
97	ia64_srlz_d();
98
99	et = &ia64_clock_et;
100	if (et->et_active) {
101		stf = td->td_intr_frame;
102		td->td_intr_frame = tf;
103		et->et_event_cb(et, et->et_arg);
104		td->td_intr_frame = stf;
105	}
106	return (1);
107}
108
109/*
110 * Event timer start method.
111 */
112static int
113ia64_clock_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
114{
115	u_long itc, load;
116	register_t is;
117
118	if (period != 0) {
119		PCPU_SET(md.clock_mode, CLOCK_ET_PERIODIC);
120		load = (et->et_frequency * period) >> 32;
121	} else {
122		PCPU_SET(md.clock_mode, CLOCK_ET_ONESHOT);
123		load = 0;
124	}
125
126	PCPU_SET(md.clock_load, load);
127
128	if (first != 0)
129		load = (et->et_frequency * first) >> 32;
130
131	is = intr_disable();
132	itc = ia64_get_itc();
133	ia64_set_itm(itc + load);
134	ia64_set_itv(ia64_clock_xiv);
135	ia64_srlz_d();
136	intr_restore(is);
137	return (0);
138}
139
140/*
141 * Event timer stop method.
142 */
143static int
144ia64_clock_stop(struct eventtimer *et)
145{
146
147	ia64_set_itv((1 << 16) | ia64_clock_xiv);
148	ia64_srlz_d();
149	PCPU_SET(md.clock_mode, CLOCK_ET_OFF);
150	PCPU_SET(md.clock_load, 0);
151	return (0);
152}
153
154/*
155 * We call cpu_initclocks() on the APs as well. It allows us to
156 * group common initialization in the same function.
157 */
158void
159cpu_initclocks()
160{
161
162	ia64_clock_stop(NULL);
163	if (PCPU_GET(cpuid) == 0)
164		cpu_initclocks_bsp();
165	else
166		cpu_initclocks_ap();
167}
168
169static void
170clock_configure(void *dummy)
171{
172	struct eventtimer *et;
173	u_long itc_freq;
174
175	ia64_clock_xiv = ia64_xiv_alloc(PI_REALTIME, IA64_XIV_IPI,
176	    ia64_ih_clock);
177	if (ia64_clock_xiv == 0)
178		panic("No XIV for clock interrupts");
179
180	itc_freq = (u_long)ia64_itc_freq() * 1000000ul;
181
182	et = &ia64_clock_et;
183	et->et_name = "ITC";
184	et->et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
185	et->et_quality = 1000;
186	et->et_frequency = itc_freq;
187	et->et_min_period = SBT_1S / (10 * hz);
188	et->et_max_period = (0xfffffffeul << 32) / itc_freq;
189	et->et_start = ia64_clock_start;
190	et->et_stop = ia64_clock_stop;
191	et->et_priv = NULL;
192	et_register(et);
193
194#ifndef SMP
195	ia64_timecounter.tc_frequency = itc_freq;
196	tc_init(&ia64_timecounter);
197#endif
198}
199SYSINIT(clkcfg, SI_SUB_CONFIGURE, SI_ORDER_SECOND, clock_configure, NULL);
200