vmbus_et.c revision 300834
1293873Ssephe/*-
2298446Ssephe * Copyright (c) 2015,2016 Microsoft Corp.
3293873Ssephe * All rights reserved.
4293873Ssephe *
5293873Ssephe * Redistribution and use in source and binary forms, with or without
6293873Ssephe * modification, are permitted provided that the following conditions
7293873Ssephe * are met:
8293873Ssephe * 1. Redistributions of source code must retain the above copyright
9293873Ssephe *      notice, this list of conditions and the following disclaimer.
10293873Ssephe * 2. Redistributions in binary form must reproduce the above copyright
11293873Ssephe *      notice, this list of conditions and the following disclaimer in the
12293873Ssephe *      documentation and/or other materials provided with the distribution.
13293873Ssephe *
14293873Ssephe * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15293873Ssephe * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16293873Ssephe * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17293873Ssephe * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18293873Ssephe * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19293873Ssephe * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20293873Ssephe * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21293873Ssephe * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22293873Ssephe * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23293873Ssephe * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24293873Ssephe * SUCH DAMAGE.
25293873Ssephe */
26293873Ssephe
27293873Ssephe#include <sys/cdefs.h>
28293873Ssephe__FBSDID("$FreeBSD: head/sys/dev/hyperv/vmbus/hv_et.c 300834 2016-05-27 07:29:31Z sephe $");
29293873Ssephe
30293873Ssephe#include <sys/param.h>
31298449Ssephe#include <sys/bus.h>
32298449Ssephe#include <sys/kernel.h>
33298449Ssephe#include <sys/module.h>
34293873Ssephe#include <sys/proc.h>
35293873Ssephe#include <sys/systm.h>
36293873Ssephe#include <sys/smp.h>
37293873Ssephe#include <sys/time.h>
38293873Ssephe#include <sys/timeet.h>
39293873Ssephe
40300827Ssephe#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
41300827Ssephe#include <dev/hyperv/vmbus/hyperv_reg.h>
42300834Ssephe#include <dev/hyperv/vmbus/hyperv_var.h>
43293873Ssephe
44293873Ssephe#define HV_TIMER_FREQUENCY		(10 * 1000 * 1000LL) /* 100ns period */
45293873Ssephe#define HV_MAX_DELTA_TICKS		0xffffffffLL
46293873Ssephe#define HV_MIN_DELTA_TICKS		1LL
47293873Ssephe
48300827Ssephe#define MSR_HV_STIMER0_CFG_SINT		\
49300827Ssephe	((((uint64_t)HV_VMBUS_TIMER_SINT) << MSR_HV_STIMER_CFG_SINT_SHIFT) & \
50300827Ssephe	 MSR_HV_STIMER_CFG_SINT_MASK)
51300827Ssephe
52300834Ssephe/*
53300834Ssephe * Two additionally required features:
54300834Ssephe * - SynIC is needed for interrupt generation.
55300834Ssephe * - Time reference counter is needed to set ABS reference count to
56300834Ssephe *   STIMER0_COUNT.
57300834Ssephe */
58300834Ssephe#define CPUID_HV_ET_MASK		(CPUID_HV_MSR_TIME_REFCNT |	\
59300834Ssephe					 CPUID_HV_MSR_SYNIC |		\
60300834Ssephe					 CPUID_HV_MSR_SYNTIMER)
61300834Ssephe
62298449Ssephestatic struct eventtimer *et;
63293873Ssephe
64293873Ssephestatic inline uint64_t
65293873Ssephesbintime2tick(sbintime_t time)
66293873Ssephe{
67293873Ssephe	struct timespec val;
68293873Ssephe
69293873Ssephe	val = sbttots(time);
70293873Ssephe	return val.tv_sec * HV_TIMER_FREQUENCY + val.tv_nsec / 100;
71293873Ssephe}
72293873Ssephe
73293873Ssephestatic int
74293873Ssephehv_et_start(struct eventtimer *et, sbintime_t firsttime, sbintime_t periodtime)
75293873Ssephe{
76300827Ssephe	uint64_t current, config;
77293873Ssephe
78300827Ssephe	config = MSR_HV_STIMER_CFG_AUTOEN | MSR_HV_STIMER0_CFG_SINT;
79293873Ssephe
80300827Ssephe	current = rdmsr(MSR_HV_TIME_REF_COUNT);
81293873Ssephe	current += sbintime2tick(firsttime);
82293873Ssephe
83300827Ssephe	wrmsr(MSR_HV_STIMER0_CONFIG, config);
84300827Ssephe	wrmsr(MSR_HV_STIMER0_COUNT, current);
85293873Ssephe
86293873Ssephe	return (0);
87293873Ssephe}
88293873Ssephe
89293873Ssephestatic int
90293873Ssephehv_et_stop(struct eventtimer *et)
91293873Ssephe{
92300827Ssephe	wrmsr(MSR_HV_STIMER0_CONFIG, 0);
93300827Ssephe	wrmsr(MSR_HV_STIMER0_COUNT, 0);
94293873Ssephe
95293873Ssephe	return (0);
96293873Ssephe}
97293873Ssephe
98293873Ssephevoid
99293873Ssephehv_et_intr(struct trapframe *frame)
100293873Ssephe{
101293873Ssephe	struct trapframe *oldframe;
102293873Ssephe	struct thread *td;
103293873Ssephe
104298449Ssephe	if (et->et_active) {
105293873Ssephe		td = curthread;
106293873Ssephe		td->td_intr_nesting_level++;
107293873Ssephe		oldframe = td->td_intr_frame;
108293873Ssephe		td->td_intr_frame = frame;
109298449Ssephe		et->et_event_cb(et, et->et_arg);
110293873Ssephe		td->td_intr_frame = oldframe;
111293873Ssephe		td->td_intr_nesting_level--;
112293873Ssephe	}
113293873Ssephe}
114293873Ssephe
115298449Ssephestatic void
116298568Ssephehv_et_identify(driver_t *driver, device_t parent)
117293873Ssephe{
118300834Ssephe	if (device_find_child(parent, "hv_et", -1) != NULL ||
119300834Ssephe	    (hyperv_features & CPUID_HV_ET_MASK) != CPUID_HV_ET_MASK)
120298449Ssephe		return;
121298449Ssephe
122298449Ssephe	device_add_child(parent, "hv_et", -1);
123293873Ssephe}
124293873Ssephe
125298449Ssephestatic int
126298449Ssephehv_et_probe(device_t dev)
127298449Ssephe{
128298449Ssephe	device_set_desc(dev, "Hyper-V event timer");
129298449Ssephe
130298449Ssephe	return (BUS_PROBE_NOWILDCARD);
131298449Ssephe}
132298449Ssephe
133298449Ssephestatic int
134298449Ssephehv_et_attach(device_t dev)
135298449Ssephe{
136298449Ssephe	/* XXX: need allocate SINT and remove global et */
137298449Ssephe	et = device_get_softc(dev);
138298449Ssephe
139298449Ssephe	et->et_name = "Hyper-V";
140298449Ssephe	et->et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
141298449Ssephe	et->et_quality = 1000;
142298449Ssephe	et->et_frequency = HV_TIMER_FREQUENCY;
143298449Ssephe	et->et_min_period = HV_MIN_DELTA_TICKS * ((1LL << 32) / HV_TIMER_FREQUENCY);
144298449Ssephe	et->et_max_period = HV_MAX_DELTA_TICKS * ((1LL << 32) / HV_TIMER_FREQUENCY);
145298449Ssephe	et->et_start = hv_et_start;
146298449Ssephe	et->et_stop = hv_et_stop;
147298449Ssephe	et->et_priv = dev;
148298449Ssephe
149298449Ssephe	return (et_register(et));
150298449Ssephe}
151298449Ssephe
152298449Ssephestatic int
153298449Ssephehv_et_detach(device_t dev)
154298449Ssephe{
155298449Ssephe	return (et_deregister(et));
156298449Ssephe}
157298449Ssephe
158298449Ssephestatic device_method_t hv_et_methods[] = {
159298449Ssephe	DEVMETHOD(device_identify,      hv_et_identify),
160298449Ssephe	DEVMETHOD(device_probe,         hv_et_probe),
161298449Ssephe	DEVMETHOD(device_attach,        hv_et_attach),
162298449Ssephe	DEVMETHOD(device_detach,        hv_et_detach),
163298449Ssephe
164298449Ssephe	DEVMETHOD_END
165298449Ssephe};
166298449Ssephe
167298449Ssephestatic driver_t hv_et_driver = {
168298449Ssephe	"hv_et",
169298449Ssephe	hv_et_methods,
170298449Ssephe	sizeof(struct eventtimer)
171298449Ssephe};
172298449Ssephe
173298449Ssephestatic devclass_t hv_et_devclass;
174298449SsepheDRIVER_MODULE(hv_et, vmbus, hv_et_driver, hv_et_devclass, NULL, 0);
175298449SsepheMODULE_VERSION(hv_et, 1);
176