1bf21cd93STycho Nightingale /*-
24c87aefeSPatrick Mooney * Copyright (c) 2018 Joyent, Inc.
3bf21cd93STycho Nightingale * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
4bf21cd93STycho Nightingale * Copyright (c) 2011 NetApp, Inc.
5bf21cd93STycho Nightingale * All rights reserved.
684659b24SMichael Zeller * Copyright (c) 2018 Joyent, Inc.
7bf21cd93STycho Nightingale *
8bf21cd93STycho Nightingale * Redistribution and use in source and binary forms, with or without
9bf21cd93STycho Nightingale * modification, are permitted provided that the following conditions
10bf21cd93STycho Nightingale * are met:
11bf21cd93STycho Nightingale * 1. Redistributions of source code must retain the above copyright
12bf21cd93STycho Nightingale * notice, this list of conditions and the following disclaimer.
13bf21cd93STycho Nightingale * 2. Redistributions in binary form must reproduce the above copyright
14bf21cd93STycho Nightingale * notice, this list of conditions and the following disclaimer in the
15bf21cd93STycho Nightingale * documentation and/or other materials provided with the distribution.
16bf21cd93STycho Nightingale *
17bf21cd93STycho Nightingale * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
18bf21cd93STycho Nightingale * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19bf21cd93STycho Nightingale * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20bf21cd93STycho Nightingale * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
21bf21cd93STycho Nightingale * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22bf21cd93STycho Nightingale * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23bf21cd93STycho Nightingale * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24bf21cd93STycho Nightingale * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25bf21cd93STycho Nightingale * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26bf21cd93STycho Nightingale * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27bf21cd93STycho Nightingale * SUCH DAMAGE.
28bf21cd93STycho Nightingale */
29d515dd77SPatrick Mooney /*
30d515dd77SPatrick Mooney * This file and its contents are supplied under the terms of the
31d515dd77SPatrick Mooney * Common Development and Distribution License ("CDDL"), version 1.0.
32d515dd77SPatrick Mooney * You may only use this file in accordance with the terms of version
33d515dd77SPatrick Mooney * 1.0 of the CDDL.
34d515dd77SPatrick Mooney *
35d515dd77SPatrick Mooney * A full copy of the text of the CDDL should have accompanied this
36d515dd77SPatrick Mooney * source. A copy of the CDDL is also available via the Internet at
37d515dd77SPatrick Mooney * http://www.illumos.org/license/CDDL.
38d515dd77SPatrick Mooney *
39d515dd77SPatrick Mooney * Copyright 2022 Oxide Computer Company
40d515dd77SPatrick Mooney */
41bf21cd93STycho Nightingale
42bf21cd93STycho Nightingale #include <sys/cdefs.h>
43bf21cd93STycho Nightingale
44bf21cd93STycho Nightingale #include <sys/param.h>
45bf21cd93STycho Nightingale #include <sys/types.h>
46bf21cd93STycho Nightingale #include <sys/queue.h>
47bf21cd93STycho Nightingale #include <sys/kernel.h>
488130f8e1SPatrick Mooney #include <sys/kmem.h>
49bf21cd93STycho Nightingale #include <sys/mutex.h>
50bf21cd93STycho Nightingale #include <sys/systm.h>
51bf21cd93STycho Nightingale
52bf21cd93STycho Nightingale #include <machine/vmm.h>
53bf21cd93STycho Nightingale
54bf21cd93STycho Nightingale #include "vatpic.h"
55bf21cd93STycho Nightingale #include "vioapic.h"
56bf21cd93STycho Nightingale #include "vatpit.h"
57bf21cd93STycho Nightingale
58fb29dee0SPatrick Mooney #define VATPIT_LOCK(vatpit) mutex_enter(&((vatpit)->lock))
59fb29dee0SPatrick Mooney #define VATPIT_UNLOCK(vatpit) mutex_exit(&((vatpit)->lock))
60*2cac0506SPatrick Mooney #define VATPIT_LOCKED(vatpit) MUTEX_HELD(&((vatpit)->lock))
61bf21cd93STycho Nightingale
62bf21cd93STycho Nightingale #define TIMER_SEL_MASK 0xc0
63bf21cd93STycho Nightingale #define TIMER_RW_MASK 0x30
64bf21cd93STycho Nightingale #define TIMER_MODE_MASK 0x0f
65bf21cd93STycho Nightingale #define TIMER_SEL_READBACK 0xc0
66bf21cd93STycho Nightingale
67bf21cd93STycho Nightingale #define TIMER_STS_OUT 0x80
68bf21cd93STycho Nightingale #define TIMER_STS_NULLCNT 0x40
69bf21cd93STycho Nightingale
70d515dd77SPatrick Mooney #define VALID_STATUS_BITS (TIMER_STS_OUT | TIMER_STS_NULLCNT)
71d515dd77SPatrick Mooney
72bf21cd93STycho Nightingale #define TIMER_RB_LCTR 0x20
73bf21cd93STycho Nightingale #define TIMER_RB_LSTATUS 0x10
74bf21cd93STycho Nightingale #define TIMER_RB_CTR_2 0x08
75bf21cd93STycho Nightingale #define TIMER_RB_CTR_1 0x04
76bf21cd93STycho Nightingale #define TIMER_RB_CTR_0 0x02
77bf21cd93STycho Nightingale
78bf21cd93STycho Nightingale #define TMR2_OUT_STS 0x20
79bf21cd93STycho Nightingale
80bf21cd93STycho Nightingale #define PIT_8254_FREQ 1193182
81bf21cd93STycho Nightingale #define TIMER_DIV(freq, hz) (((freq) + (hz) / 2) / (hz))
82bf21cd93STycho Nightingale
83bf21cd93STycho Nightingale struct vatpit_callout_arg {
84bf21cd93STycho Nightingale struct vatpit *vatpit;
85bf21cd93STycho Nightingale int channel_num;
86bf21cd93STycho Nightingale };
87bf21cd93STycho Nightingale
88bf21cd93STycho Nightingale struct channel {
898cbaa6a8SPatrick Mooney uint8_t mode;
90bf21cd93STycho Nightingale uint16_t initial; /* initial counter value */
918cbaa6a8SPatrick Mooney
928cbaa6a8SPatrick Mooney uint8_t reg_cr[2];
938cbaa6a8SPatrick Mooney uint8_t reg_ol[2];
948cbaa6a8SPatrick Mooney uint8_t reg_status;
958cbaa6a8SPatrick Mooney
96bf21cd93STycho Nightingale bool slatched; /* status latched */
978cbaa6a8SPatrick Mooney bool olatched; /* output latched */
988cbaa6a8SPatrick Mooney bool cr_sel; /* read MSB from control register */
998cbaa6a8SPatrick Mooney bool ol_sel; /* read MSB from output latch */
1008cbaa6a8SPatrick Mooney bool fr_sel; /* read MSB from free-running timer */
1018cbaa6a8SPatrick Mooney
1025103e761SPatrick Mooney hrtime_t time_loaded; /* time when counter was loaded */
1035103e761SPatrick Mooney hrtime_t time_target; /* target time */
1045103e761SPatrick Mooney uint64_t total_target;
1058cbaa6a8SPatrick Mooney
1068cbaa6a8SPatrick Mooney struct callout callout;
107bf21cd93STycho Nightingale struct vatpit_callout_arg callout_arg;
108bf21cd93STycho Nightingale };
109bf21cd93STycho Nightingale
110bf21cd93STycho Nightingale struct vatpit {
111bf21cd93STycho Nightingale struct vm *vm;
112fb29dee0SPatrick Mooney kmutex_t lock;
113bf21cd93STycho Nightingale
114bf21cd93STycho Nightingale struct channel channel[3];
115bf21cd93STycho Nightingale };
116bf21cd93STycho Nightingale
117bf21cd93STycho Nightingale static void pit_timer_start_cntr0(struct vatpit *vatpit);
118bf21cd93STycho Nightingale
1194c87aefeSPatrick Mooney static uint64_t
vatpit_delta_ticks(struct vatpit * vatpit,struct channel * c)1204c87aefeSPatrick Mooney vatpit_delta_ticks(struct vatpit *vatpit, struct channel *c)
1214c87aefeSPatrick Mooney {
1225103e761SPatrick Mooney const hrtime_t delta = gethrtime() - c->time_loaded;
1234c87aefeSPatrick Mooney
1245103e761SPatrick Mooney return (hrt_freq_count(delta, PIT_8254_FREQ));
1254c87aefeSPatrick Mooney }
1264c87aefeSPatrick Mooney
127bf21cd93STycho Nightingale static int
vatpit_get_out(struct vatpit * vatpit,int channel)128bf21cd93STycho Nightingale vatpit_get_out(struct vatpit *vatpit, int channel)
129bf21cd93STycho Nightingale {
130bf21cd93STycho Nightingale struct channel *c;
1314c87aefeSPatrick Mooney uint64_t delta_ticks;
132bf21cd93STycho Nightingale int out;
133bf21cd93STycho Nightingale
134bf21cd93STycho Nightingale c = &vatpit->channel[channel];
135bf21cd93STycho Nightingale
136bf21cd93STycho Nightingale switch (c->mode) {
137bf21cd93STycho Nightingale case TIMER_INTTC:
1384c87aefeSPatrick Mooney delta_ticks = vatpit_delta_ticks(vatpit, c);
1394c87aefeSPatrick Mooney out = (delta_ticks >= c->initial);
140bf21cd93STycho Nightingale break;
141bf21cd93STycho Nightingale default:
142bf21cd93STycho Nightingale out = 0;
143bf21cd93STycho Nightingale break;
144bf21cd93STycho Nightingale }
145bf21cd93STycho Nightingale
146bf21cd93STycho Nightingale return (out);
147bf21cd93STycho Nightingale }
148bf21cd93STycho Nightingale
149bf21cd93STycho Nightingale static void
vatpit_callout_handler(void * a)150bf21cd93STycho Nightingale vatpit_callout_handler(void *a)
151bf21cd93STycho Nightingale {
152bf21cd93STycho Nightingale struct vatpit_callout_arg *arg = a;
153bf21cd93STycho Nightingale struct vatpit *vatpit;
154bf21cd93STycho Nightingale struct callout *callout;
155bf21cd93STycho Nightingale struct channel *c;
156bf21cd93STycho Nightingale
157bf21cd93STycho Nightingale vatpit = arg->vatpit;
158bf21cd93STycho Nightingale c = &vatpit->channel[arg->channel_num];
159bf21cd93STycho Nightingale callout = &c->callout;
160bf21cd93STycho Nightingale
161bf21cd93STycho Nightingale VATPIT_LOCK(vatpit);
162bf21cd93STycho Nightingale
163bf21cd93STycho Nightingale if (callout_pending(callout)) /* callout was reset */
164bf21cd93STycho Nightingale goto done;
165bf21cd93STycho Nightingale
166bf21cd93STycho Nightingale if (!callout_active(callout)) /* callout was stopped */
167bf21cd93STycho Nightingale goto done;
168bf21cd93STycho Nightingale
169bf21cd93STycho Nightingale callout_deactivate(callout);
170bf21cd93STycho Nightingale
17193d78abaSPatrick Mooney if (c->mode == TIMER_RATEGEN || c->mode == TIMER_SQWAVE) {
172bf21cd93STycho Nightingale pit_timer_start_cntr0(vatpit);
173*2cac0506SPatrick Mooney } else {
174*2cac0506SPatrick Mooney /*
175*2cac0506SPatrick Mooney * For non-periodic timers, clear the time target to distinguish
176*2cac0506SPatrick Mooney * between a fired timer (thus a zero value) and a pending one
177*2cac0506SPatrick Mooney * awaiting VM resumption (holding a non-zero value).
178*2cac0506SPatrick Mooney */
179*2cac0506SPatrick Mooney c->time_target = 0;
180bf21cd93STycho Nightingale }
181bf21cd93STycho Nightingale
182e0994bd2SPatrick Mooney (void) vatpic_pulse_irq(vatpit->vm, 0);
183e0994bd2SPatrick Mooney (void) vioapic_pulse_irq(vatpit->vm, 2);
184bf21cd93STycho Nightingale
185bf21cd93STycho Nightingale done:
186bf21cd93STycho Nightingale VATPIT_UNLOCK(vatpit);
187bf21cd93STycho Nightingale }
188bf21cd93STycho Nightingale
189*2cac0506SPatrick Mooney static void
vatpit_callout_reset(struct vatpit * vatpit)190*2cac0506SPatrick Mooney vatpit_callout_reset(struct vatpit *vatpit)
191*2cac0506SPatrick Mooney {
192*2cac0506SPatrick Mooney struct channel *c = &vatpit->channel[0];
193*2cac0506SPatrick Mooney
194*2cac0506SPatrick Mooney ASSERT(VATPIT_LOCKED(vatpit));
195*2cac0506SPatrick Mooney callout_reset_hrtime(&c->callout, c->time_target,
196*2cac0506SPatrick Mooney vatpit_callout_handler, &c->callout_arg, C_ABSOLUTE);
197*2cac0506SPatrick Mooney }
198*2cac0506SPatrick Mooney
199bf21cd93STycho Nightingale static void
pit_timer_start_cntr0(struct vatpit * vatpit)200bf21cd93STycho Nightingale pit_timer_start_cntr0(struct vatpit *vatpit)
201bf21cd93STycho Nightingale {
2025103e761SPatrick Mooney struct channel *c = &vatpit->channel[0];
203bf21cd93STycho Nightingale
2045103e761SPatrick Mooney if (c->initial == 0) {
2055103e761SPatrick Mooney return;
2065103e761SPatrick Mooney }
207bf21cd93STycho Nightingale
2085103e761SPatrick Mooney c->total_target += c->initial;
2095103e761SPatrick Mooney c->time_target = c->time_loaded +
2105103e761SPatrick Mooney hrt_freq_interval(PIT_8254_FREQ, c->total_target);
211bf21cd93STycho Nightingale
2125103e761SPatrick Mooney /*
2135103e761SPatrick Mooney * If we are more than 'c->initial' ticks behind, reset the timer base
2145103e761SPatrick Mooney * to fire at the next 'c->initial' interval boundary.
2155103e761SPatrick Mooney */
2165103e761SPatrick Mooney hrtime_t now = gethrtime();
2175103e761SPatrick Mooney if (c->time_target < now) {
2185103e761SPatrick Mooney const uint64_t ticks_behind =
219d515dd77SPatrick Mooney hrt_freq_count(now - c->time_target, PIT_8254_FREQ);
2205103e761SPatrick Mooney
2215103e761SPatrick Mooney c->total_target += roundup(ticks_behind, c->initial);
2225103e761SPatrick Mooney c->time_target = c->time_loaded +
2235103e761SPatrick Mooney hrt_freq_interval(PIT_8254_FREQ, c->total_target);
224bf21cd93STycho Nightingale }
2255103e761SPatrick Mooney
226*2cac0506SPatrick Mooney vatpit_callout_reset(vatpit);
227bf21cd93STycho Nightingale }
228bf21cd93STycho Nightingale
229bf21cd93STycho Nightingale static uint16_t
pit_update_counter(struct vatpit * vatpit,struct channel * c,bool latch)230bf21cd93STycho Nightingale pit_update_counter(struct vatpit *vatpit, struct channel *c, bool latch)
231bf21cd93STycho Nightingale {
232bf21cd93STycho Nightingale uint16_t lval;
2334c87aefeSPatrick Mooney uint64_t delta_ticks;
234bf21cd93STycho Nightingale
235bf21cd93STycho Nightingale /* cannot latch a new value until the old one has been consumed */
2368cbaa6a8SPatrick Mooney if (latch && c->olatched)
237bf21cd93STycho Nightingale return (0);
238bf21cd93STycho Nightingale
239bf21cd93STycho Nightingale if (c->initial == 0) {
240bf21cd93STycho Nightingale /*
2415103e761SPatrick Mooney * This is possibly an OS bug - reading the value of the timer
2425103e761SPatrick Mooney * without having set up the initial value.
243bf21cd93STycho Nightingale *
2445103e761SPatrick Mooney * The original user-space version of this code set the timer to
2455103e761SPatrick Mooney * 100hz in this condition; do the same here.
246bf21cd93STycho Nightingale */
247bf21cd93STycho Nightingale c->initial = TIMER_DIV(PIT_8254_FREQ, 100);
2485103e761SPatrick Mooney c->time_loaded = gethrtime();
2498cbaa6a8SPatrick Mooney c->reg_status &= ~TIMER_STS_NULLCNT;
250bf21cd93STycho Nightingale }
251bf21cd93STycho Nightingale
2524c87aefeSPatrick Mooney delta_ticks = vatpit_delta_ticks(vatpit, c);
253bf21cd93STycho Nightingale lval = c->initial - delta_ticks % c->initial;
254bf21cd93STycho Nightingale
255bf21cd93STycho Nightingale if (latch) {
2568cbaa6a8SPatrick Mooney c->olatched = true;
2578cbaa6a8SPatrick Mooney c->ol_sel = true;
2588cbaa6a8SPatrick Mooney c->reg_ol[1] = lval; /* LSB */
2598cbaa6a8SPatrick Mooney c->reg_ol[0] = lval >> 8; /* MSB */
260bf21cd93STycho Nightingale }
261bf21cd93STycho Nightingale
262bf21cd93STycho Nightingale return (lval);
263bf21cd93STycho Nightingale }
264bf21cd93STycho Nightingale
265bf21cd93STycho Nightingale static int
pit_readback1(struct vatpit * vatpit,int channel,uint8_t cmd)266bf21cd93STycho Nightingale pit_readback1(struct vatpit *vatpit, int channel, uint8_t cmd)
267bf21cd93STycho Nightingale {
268bf21cd93STycho Nightingale struct channel *c;
269bf21cd93STycho Nightingale
270bf21cd93STycho Nightingale c = &vatpit->channel[channel];
271bf21cd93STycho Nightingale
272bf21cd93STycho Nightingale /*
273bf21cd93STycho Nightingale * Latch the count/status of the timer if not already latched.
274bf21cd93STycho Nightingale * N.B. that the count/status latch-select bits are active-low.
275bf21cd93STycho Nightingale */
2768cbaa6a8SPatrick Mooney if ((cmd & TIMER_RB_LCTR) == 0 && !c->olatched) {
277bf21cd93STycho Nightingale (void) pit_update_counter(vatpit, c, true);
278bf21cd93STycho Nightingale }
279bf21cd93STycho Nightingale
2808cbaa6a8SPatrick Mooney if ((cmd & TIMER_RB_LSTATUS) == 0 && !c->slatched) {
281bf21cd93STycho Nightingale c->slatched = true;
282bf21cd93STycho Nightingale /*
283bf21cd93STycho Nightingale * For mode 0, see if the elapsed time is greater
284bf21cd93STycho Nightingale * than the initial value - this results in the
285bf21cd93STycho Nightingale * output pin being set to 1 in the status byte.
286bf21cd93STycho Nightingale */
287bf21cd93STycho Nightingale if (c->mode == TIMER_INTTC && vatpit_get_out(vatpit, channel))
2888cbaa6a8SPatrick Mooney c->reg_status |= TIMER_STS_OUT;
289bf21cd93STycho Nightingale else
2908cbaa6a8SPatrick Mooney c->reg_status &= ~TIMER_STS_OUT;
291bf21cd93STycho Nightingale }
292bf21cd93STycho Nightingale
293bf21cd93STycho Nightingale return (0);
294bf21cd93STycho Nightingale }
295bf21cd93STycho Nightingale
296bf21cd93STycho Nightingale static int
pit_readback(struct vatpit * vatpit,uint8_t cmd)297bf21cd93STycho Nightingale pit_readback(struct vatpit *vatpit, uint8_t cmd)
298bf21cd93STycho Nightingale {
299bf21cd93STycho Nightingale int error;
300bf21cd93STycho Nightingale
301bf21cd93STycho Nightingale /*
302bf21cd93STycho Nightingale * The readback command can apply to all timers.
303bf21cd93STycho Nightingale */
304bf21cd93STycho Nightingale error = 0;
305bf21cd93STycho Nightingale if (cmd & TIMER_RB_CTR_0)
306bf21cd93STycho Nightingale error = pit_readback1(vatpit, 0, cmd);
307bf21cd93STycho Nightingale if (!error && cmd & TIMER_RB_CTR_1)
308bf21cd93STycho Nightingale error = pit_readback1(vatpit, 1, cmd);
309bf21cd93STycho Nightingale if (!error && cmd & TIMER_RB_CTR_2)
310bf21cd93STycho Nightingale error = pit_readback1(vatpit, 2, cmd);
311bf21cd93STycho Nightingale
312bf21cd93STycho Nightingale return (error);
313bf21cd93STycho Nightingale }
314bf21cd93STycho Nightingale
315bf21cd93STycho Nightingale static int
vatpit_update_mode(struct vatpit * vatpit,uint8_t val)316bf21cd93STycho Nightingale vatpit_update_mode(struct vatpit *vatpit, uint8_t val)
317bf21cd93STycho Nightingale {
318bf21cd93STycho Nightingale struct channel *c;
3198cbaa6a8SPatrick Mooney int sel, rw;
3208cbaa6a8SPatrick Mooney uint8_t mode;
321bf21cd93STycho Nightingale
322bf21cd93STycho Nightingale sel = val & TIMER_SEL_MASK;
323bf21cd93STycho Nightingale rw = val & TIMER_RW_MASK;
324bf21cd93STycho Nightingale mode = val & TIMER_MODE_MASK;
325bf21cd93STycho Nightingale
3268cbaa6a8SPatrick Mooney /* Clear don't-care bit (M2) when M1 is set */
3278cbaa6a8SPatrick Mooney if ((mode & TIMER_RATEGEN) != 0) {
3288cbaa6a8SPatrick Mooney mode &= ~TIMER_SWSTROBE;
3298cbaa6a8SPatrick Mooney }
3308cbaa6a8SPatrick Mooney
331bf21cd93STycho Nightingale if (sel == TIMER_SEL_READBACK)
332bf21cd93STycho Nightingale return (pit_readback(vatpit, val));
333bf21cd93STycho Nightingale
334bf21cd93STycho Nightingale if (rw != TIMER_LATCH && rw != TIMER_16BIT)
335bf21cd93STycho Nightingale return (-1);
336bf21cd93STycho Nightingale
337bf21cd93STycho Nightingale if (rw != TIMER_LATCH) {
338bf21cd93STycho Nightingale /*
339bf21cd93STycho Nightingale * Counter mode is not affected when issuing a
340bf21cd93STycho Nightingale * latch command.
341bf21cd93STycho Nightingale */
342bf21cd93STycho Nightingale if (mode != TIMER_INTTC &&
343bf21cd93STycho Nightingale mode != TIMER_RATEGEN &&
344bf21cd93STycho Nightingale mode != TIMER_SQWAVE &&
345bf21cd93STycho Nightingale mode != TIMER_SWSTROBE)
346bf21cd93STycho Nightingale return (-1);
347bf21cd93STycho Nightingale }
348bf21cd93STycho Nightingale
349bf21cd93STycho Nightingale c = &vatpit->channel[sel >> 6];
3508cbaa6a8SPatrick Mooney if (rw == TIMER_LATCH) {
351e0994bd2SPatrick Mooney (void) pit_update_counter(vatpit, c, true);
3528cbaa6a8SPatrick Mooney } else {
353bf21cd93STycho Nightingale c->mode = mode;
3548cbaa6a8SPatrick Mooney c->olatched = false; /* reset latch after reprogramming */
3558cbaa6a8SPatrick Mooney c->reg_status |= TIMER_STS_NULLCNT;
356bf21cd93STycho Nightingale }
357bf21cd93STycho Nightingale
358bf21cd93STycho Nightingale return (0);
359bf21cd93STycho Nightingale }
360bf21cd93STycho Nightingale
361bf21cd93STycho Nightingale int
vatpit_handler(void * arg,bool in,uint16_t port,uint8_t bytes,uint32_t * eax)3620e1453c3SPatrick Mooney vatpit_handler(void *arg, bool in, uint16_t port, uint8_t bytes, uint32_t *eax)
363bf21cd93STycho Nightingale {
3640e1453c3SPatrick Mooney struct vatpit *vatpit = arg;
365bf21cd93STycho Nightingale struct channel *c;
366bf21cd93STycho Nightingale uint8_t val;
367bf21cd93STycho Nightingale int error;
368bf21cd93STycho Nightingale
369bf21cd93STycho Nightingale if (bytes != 1)
370bf21cd93STycho Nightingale return (-1);
371bf21cd93STycho Nightingale
372bf21cd93STycho Nightingale val = *eax;
373bf21cd93STycho Nightingale
374bf21cd93STycho Nightingale if (port == TIMER_MODE) {
375bf21cd93STycho Nightingale if (in) {
376d4f59ae5SPatrick Mooney /* Mode is write-only */
377bf21cd93STycho Nightingale return (-1);
378bf21cd93STycho Nightingale }
379bf21cd93STycho Nightingale
380bf21cd93STycho Nightingale VATPIT_LOCK(vatpit);
381bf21cd93STycho Nightingale error = vatpit_update_mode(vatpit, val);
382bf21cd93STycho Nightingale VATPIT_UNLOCK(vatpit);
383bf21cd93STycho Nightingale
384bf21cd93STycho Nightingale return (error);
385bf21cd93STycho Nightingale }
386bf21cd93STycho Nightingale
387bf21cd93STycho Nightingale /* counter ports */
388bf21cd93STycho Nightingale KASSERT(port >= TIMER_CNTR0 && port <= TIMER_CNTR2,
389bf21cd93STycho Nightingale ("invalid port 0x%x", port));
390bf21cd93STycho Nightingale c = &vatpit->channel[port - TIMER_CNTR0];
391bf21cd93STycho Nightingale
392bf21cd93STycho Nightingale VATPIT_LOCK(vatpit);
393bf21cd93STycho Nightingale if (in && c->slatched) {
3948cbaa6a8SPatrick Mooney /* Return the status byte if latched */
3958cbaa6a8SPatrick Mooney *eax = c->reg_status;
396bf21cd93STycho Nightingale c->slatched = false;
3978cbaa6a8SPatrick Mooney c->reg_status = 0;
398bf21cd93STycho Nightingale } else if (in) {
399bf21cd93STycho Nightingale /*
400bf21cd93STycho Nightingale * The spec says that once the output latch is completely
401bf21cd93STycho Nightingale * read it should revert to "following" the counter. Use
402bf21cd93STycho Nightingale * the free running counter for this case (i.e. Linux
403bf21cd93STycho Nightingale * TSC calibration). Assuming the access mode is 16-bit,
404bf21cd93STycho Nightingale * toggle the MSB/LSB bit on each read.
405bf21cd93STycho Nightingale */
4068cbaa6a8SPatrick Mooney if (!c->olatched) {
407bf21cd93STycho Nightingale uint16_t tmp;
408bf21cd93STycho Nightingale
409bf21cd93STycho Nightingale tmp = pit_update_counter(vatpit, c, false);
4108cbaa6a8SPatrick Mooney if (c->fr_sel) {
411bf21cd93STycho Nightingale tmp >>= 8;
4128cbaa6a8SPatrick Mooney }
413bf21cd93STycho Nightingale tmp &= 0xff;
414bf21cd93STycho Nightingale *eax = tmp;
4158cbaa6a8SPatrick Mooney c->fr_sel = !c->fr_sel;
4162699b94cSPatrick Mooney } else {
4178cbaa6a8SPatrick Mooney if (c->ol_sel) {
4188cbaa6a8SPatrick Mooney *eax = c->reg_ol[1];
4198cbaa6a8SPatrick Mooney c->ol_sel = false;
4208cbaa6a8SPatrick Mooney } else {
4218cbaa6a8SPatrick Mooney *eax = c->reg_ol[0];
4228cbaa6a8SPatrick Mooney c->olatched = false;
4238cbaa6a8SPatrick Mooney }
4242699b94cSPatrick Mooney }
425bf21cd93STycho Nightingale } else {
4268cbaa6a8SPatrick Mooney if (!c->cr_sel) {
4278cbaa6a8SPatrick Mooney c->reg_cr[0] = *eax;
4288cbaa6a8SPatrick Mooney c->cr_sel = true;
4298cbaa6a8SPatrick Mooney } else {
4308cbaa6a8SPatrick Mooney c->reg_cr[1] = *eax;
4318cbaa6a8SPatrick Mooney c->cr_sel = false;
4328cbaa6a8SPatrick Mooney
4338cbaa6a8SPatrick Mooney c->reg_status &= ~TIMER_STS_NULLCNT;
4348cbaa6a8SPatrick Mooney c->fr_sel = false;
4358cbaa6a8SPatrick Mooney c->initial = c->reg_cr[0] | (uint16_t)c->reg_cr[1] << 8;
4365103e761SPatrick Mooney c->time_loaded = gethrtime();
437bf21cd93STycho Nightingale /* Start an interval timer for channel 0 */
438bf21cd93STycho Nightingale if (port == TIMER_CNTR0) {
4395103e761SPatrick Mooney c->time_target = c->time_loaded;
4405103e761SPatrick Mooney c->total_target = 0;
441bf21cd93STycho Nightingale pit_timer_start_cntr0(vatpit);
442bf21cd93STycho Nightingale }
443bf21cd93STycho Nightingale if (c->initial == 0)
444bf21cd93STycho Nightingale c->initial = 0xffff;
445bf21cd93STycho Nightingale }
446bf21cd93STycho Nightingale }
447bf21cd93STycho Nightingale VATPIT_UNLOCK(vatpit);
448bf21cd93STycho Nightingale
449bf21cd93STycho Nightingale return (0);
450bf21cd93STycho Nightingale }
451bf21cd93STycho Nightingale
452bf21cd93STycho Nightingale int
vatpit_nmisc_handler(void * arg,bool in,uint16_t port,uint8_t bytes,uint32_t * eax)4530e1453c3SPatrick Mooney vatpit_nmisc_handler(void *arg, bool in, uint16_t port, uint8_t bytes,
4540e1453c3SPatrick Mooney uint32_t *eax)
455bf21cd93STycho Nightingale {
4560e1453c3SPatrick Mooney struct vatpit *vatpit = arg;
457bf21cd93STycho Nightingale
458bf21cd93STycho Nightingale if (in) {
459bf21cd93STycho Nightingale VATPIT_LOCK(vatpit);
460bf21cd93STycho Nightingale if (vatpit_get_out(vatpit, 2))
461bf21cd93STycho Nightingale *eax = TMR2_OUT_STS;
462bf21cd93STycho Nightingale else
463bf21cd93STycho Nightingale *eax = 0;
464bf21cd93STycho Nightingale
465bf21cd93STycho Nightingale VATPIT_UNLOCK(vatpit);
466bf21cd93STycho Nightingale }
467bf21cd93STycho Nightingale
468bf21cd93STycho Nightingale return (0);
469bf21cd93STycho Nightingale }
470bf21cd93STycho Nightingale
471bf21cd93STycho Nightingale struct vatpit *
vatpit_init(struct vm * vm)472bf21cd93STycho Nightingale vatpit_init(struct vm *vm)
473bf21cd93STycho Nightingale {
474bf21cd93STycho Nightingale struct vatpit *vatpit;
475bf21cd93STycho Nightingale struct vatpit_callout_arg *arg;
476bf21cd93STycho Nightingale int i;
477bf21cd93STycho Nightingale
4788130f8e1SPatrick Mooney vatpit = kmem_zalloc(sizeof (struct vatpit), KM_SLEEP);
479bf21cd93STycho Nightingale vatpit->vm = vm;
480bf21cd93STycho Nightingale
481fb29dee0SPatrick Mooney mutex_init(&vatpit->lock, NULL, MUTEX_ADAPTIVE, NULL);
482bf21cd93STycho Nightingale
483bf21cd93STycho Nightingale for (i = 0; i < 3; i++) {
4844c87aefeSPatrick Mooney callout_init(&vatpit->channel[i].callout, 1);
485bf21cd93STycho Nightingale arg = &vatpit->channel[i].callout_arg;
486bf21cd93STycho Nightingale arg->vatpit = vatpit;
487bf21cd93STycho Nightingale arg->channel_num = i;
488bf21cd93STycho Nightingale }
489bf21cd93STycho Nightingale
490bf21cd93STycho Nightingale return (vatpit);
491bf21cd93STycho Nightingale }
492bf21cd93STycho Nightingale
493bf21cd93STycho Nightingale void
vatpit_cleanup(struct vatpit * vatpit)494bf21cd93STycho Nightingale vatpit_cleanup(struct vatpit *vatpit)
495bf21cd93STycho Nightingale {
496bf21cd93STycho Nightingale int i;
497bf21cd93STycho Nightingale
498bf21cd93STycho Nightingale for (i = 0; i < 3; i++)
499bf21cd93STycho Nightingale callout_drain(&vatpit->channel[i].callout);
500bf21cd93STycho Nightingale
501fb29dee0SPatrick Mooney mutex_destroy(&vatpit->lock);
5028130f8e1SPatrick Mooney kmem_free(vatpit, sizeof (*vatpit));
503bf21cd93STycho Nightingale }
5044c87aefeSPatrick Mooney
5054c87aefeSPatrick Mooney void
vatpit_localize_resources(struct vatpit * vatpit)5064c87aefeSPatrick Mooney vatpit_localize_resources(struct vatpit *vatpit)
5074c87aefeSPatrick Mooney {
5084c87aefeSPatrick Mooney for (uint_t i = 0; i < 3; i++) {
5094c87aefeSPatrick Mooney /* Only localize channels which might be running */
5104c87aefeSPatrick Mooney if (vatpit->channel[i].mode != 0) {
5114c87aefeSPatrick Mooney vmm_glue_callout_localize(&vatpit->channel[i].callout);
5124c87aefeSPatrick Mooney }
5134c87aefeSPatrick Mooney }
5144c87aefeSPatrick Mooney }
515d515dd77SPatrick Mooney
516*2cac0506SPatrick Mooney void
vatpit_pause(struct vatpit * vatpit)517*2cac0506SPatrick Mooney vatpit_pause(struct vatpit *vatpit)
518*2cac0506SPatrick Mooney {
519*2cac0506SPatrick Mooney struct channel *c = &vatpit->channel[0];
520*2cac0506SPatrick Mooney
521*2cac0506SPatrick Mooney VATPIT_LOCK(vatpit);
522*2cac0506SPatrick Mooney callout_stop(&c->callout);
523*2cac0506SPatrick Mooney VATPIT_UNLOCK(vatpit);
524*2cac0506SPatrick Mooney }
525*2cac0506SPatrick Mooney
526*2cac0506SPatrick Mooney void
vatpit_resume(struct vatpit * vatpit)527*2cac0506SPatrick Mooney vatpit_resume(struct vatpit *vatpit)
528*2cac0506SPatrick Mooney {
529*2cac0506SPatrick Mooney struct channel *c = &vatpit->channel[0];
530*2cac0506SPatrick Mooney
531*2cac0506SPatrick Mooney VATPIT_LOCK(vatpit);
532*2cac0506SPatrick Mooney ASSERT(!callout_active(&c->callout));
533*2cac0506SPatrick Mooney if (c->time_target != 0) {
534*2cac0506SPatrick Mooney vatpit_callout_reset(vatpit);
535*2cac0506SPatrick Mooney }
536*2cac0506SPatrick Mooney VATPIT_UNLOCK(vatpit);
537*2cac0506SPatrick Mooney }
538*2cac0506SPatrick Mooney
539d515dd77SPatrick Mooney static int
vatpit_data_read(void * datap,const vmm_data_req_t * req)540d515dd77SPatrick Mooney vatpit_data_read(void *datap, const vmm_data_req_t *req)
541d515dd77SPatrick Mooney {
542d515dd77SPatrick Mooney VERIFY3U(req->vdr_class, ==, VDC_ATPIT);
543d515dd77SPatrick Mooney VERIFY3U(req->vdr_version, ==, 1);
544a77feb92SPatrick Mooney VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_atpit_v1));
545d515dd77SPatrick Mooney
546d515dd77SPatrick Mooney struct vatpit *vatpit = datap;
547d515dd77SPatrick Mooney struct vdi_atpit_v1 *out = req->vdr_data;
548d515dd77SPatrick Mooney
549d515dd77SPatrick Mooney VATPIT_LOCK(vatpit);
550d515dd77SPatrick Mooney for (uint_t i = 0; i < 3; i++) {
551d515dd77SPatrick Mooney const struct channel *src = &vatpit->channel[i];
552d515dd77SPatrick Mooney struct vdi_atpit_channel_v1 *chan = &out->va_channel[i];
553d515dd77SPatrick Mooney
554d515dd77SPatrick Mooney chan->vac_initial = src->initial;
555d515dd77SPatrick Mooney chan->vac_reg_cr =
556d515dd77SPatrick Mooney (src->reg_cr[0] | (uint16_t)src->reg_cr[1] << 8);
557d515dd77SPatrick Mooney chan->vac_reg_ol =
558d515dd77SPatrick Mooney (src->reg_ol[0] | (uint16_t)src->reg_ol[1] << 8);
559d515dd77SPatrick Mooney chan->vac_reg_status = src->reg_status;
560d515dd77SPatrick Mooney chan->vac_mode = src->mode;
561d515dd77SPatrick Mooney chan->vac_status =
562d515dd77SPatrick Mooney (src->slatched ? (1 << 0) : 0) |
563d515dd77SPatrick Mooney (src->olatched ? (1 << 1) : 0) |
564d515dd77SPatrick Mooney (src->cr_sel ? (1 << 2) : 0) |
565d515dd77SPatrick Mooney (src->ol_sel ? (1 << 3) : 0) |
566d515dd77SPatrick Mooney (src->fr_sel ? (1 << 4) : 0);
567d515dd77SPatrick Mooney /* Only channel 0 has the timer configured */
568*2cac0506SPatrick Mooney if (i == 0 && src->time_target != 0) {
569d515dd77SPatrick Mooney chan->vac_time_target =
570d515dd77SPatrick Mooney vm_normalize_hrtime(vatpit->vm, src->time_target);
571d515dd77SPatrick Mooney } else {
572d515dd77SPatrick Mooney chan->vac_time_target = 0;
573d515dd77SPatrick Mooney }
574d515dd77SPatrick Mooney }
575d515dd77SPatrick Mooney VATPIT_UNLOCK(vatpit);
576d515dd77SPatrick Mooney
577d515dd77SPatrick Mooney return (0);
578d515dd77SPatrick Mooney }
579d515dd77SPatrick Mooney
580d515dd77SPatrick Mooney static bool
vatpit_data_validate(const struct vdi_atpit_v1 * src)581d515dd77SPatrick Mooney vatpit_data_validate(const struct vdi_atpit_v1 *src)
582d515dd77SPatrick Mooney {
583d515dd77SPatrick Mooney for (uint_t i = 0; i < 3; i++) {
584d515dd77SPatrick Mooney const struct vdi_atpit_channel_v1 *chan = &src->va_channel[i];
585d515dd77SPatrick Mooney
586d515dd77SPatrick Mooney if ((chan->vac_status & ~VALID_STATUS_BITS) != 0) {
587d515dd77SPatrick Mooney return (false);
588d515dd77SPatrick Mooney }
589d515dd77SPatrick Mooney }
590d515dd77SPatrick Mooney return (true);
591d515dd77SPatrick Mooney }
592d515dd77SPatrick Mooney
593d515dd77SPatrick Mooney static int
vatpit_data_write(void * datap,const vmm_data_req_t * req)594d515dd77SPatrick Mooney vatpit_data_write(void *datap, const vmm_data_req_t *req)
595d515dd77SPatrick Mooney {
596d515dd77SPatrick Mooney VERIFY3U(req->vdr_class, ==, VDC_ATPIT);
597d515dd77SPatrick Mooney VERIFY3U(req->vdr_version, ==, 1);
598a77feb92SPatrick Mooney VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_atpit_v1));
599d515dd77SPatrick Mooney
600d515dd77SPatrick Mooney struct vatpit *vatpit = datap;
601d515dd77SPatrick Mooney const struct vdi_atpit_v1 *src = req->vdr_data;
602d515dd77SPatrick Mooney if (!vatpit_data_validate(src)) {
603d515dd77SPatrick Mooney return (EINVAL);
604d515dd77SPatrick Mooney }
605d515dd77SPatrick Mooney
606d515dd77SPatrick Mooney VATPIT_LOCK(vatpit);
607d515dd77SPatrick Mooney for (uint_t i = 0; i < 3; i++) {
608d515dd77SPatrick Mooney const struct vdi_atpit_channel_v1 *chan = &src->va_channel[i];
609d515dd77SPatrick Mooney struct channel *out = &vatpit->channel[i];
610d515dd77SPatrick Mooney
611d515dd77SPatrick Mooney out->initial = chan->vac_initial;
612d515dd77SPatrick Mooney out->reg_cr[0] = chan->vac_reg_cr;
613d515dd77SPatrick Mooney out->reg_cr[1] = chan->vac_reg_cr >> 8;
614d515dd77SPatrick Mooney out->reg_ol[0] = chan->vac_reg_ol;
615d515dd77SPatrick Mooney out->reg_ol[1] = chan->vac_reg_ol >> 8;
616d515dd77SPatrick Mooney out->reg_status = chan->vac_reg_status;
617d515dd77SPatrick Mooney out->mode = chan->vac_mode;
618d515dd77SPatrick Mooney out->slatched = (chan->vac_status & (1 << 0)) != 0;
619d515dd77SPatrick Mooney out->olatched = (chan->vac_status & (1 << 1)) != 0;
620d515dd77SPatrick Mooney out->cr_sel = (chan->vac_status & (1 << 2)) != 0;
621d515dd77SPatrick Mooney out->ol_sel = (chan->vac_status & (1 << 3)) != 0;
622d515dd77SPatrick Mooney out->fr_sel = (chan->vac_status & (1 << 4)) != 0;
623d515dd77SPatrick Mooney
624d515dd77SPatrick Mooney /* Only channel 0 has the timer configured */
625d515dd77SPatrick Mooney if (i != 0) {
626d515dd77SPatrick Mooney continue;
627d515dd77SPatrick Mooney }
628d515dd77SPatrick Mooney
629d515dd77SPatrick Mooney struct callout *callout = &out->callout;
630d515dd77SPatrick Mooney if (callout_active(callout)) {
631d515dd77SPatrick Mooney callout_deactivate(callout);
632d515dd77SPatrick Mooney }
633d515dd77SPatrick Mooney
634d515dd77SPatrick Mooney if (chan->vac_time_target == 0) {
635d515dd77SPatrick Mooney out->time_loaded = 0;
636d515dd77SPatrick Mooney out->time_target = 0;
637d515dd77SPatrick Mooney continue;
638d515dd77SPatrick Mooney }
639d515dd77SPatrick Mooney
640d515dd77SPatrick Mooney /* back-calculate time_loaded for the appropriate interval */
641d515dd77SPatrick Mooney const uint64_t time_target =
642d515dd77SPatrick Mooney vm_denormalize_hrtime(vatpit->vm, chan->vac_time_target);
643d515dd77SPatrick Mooney out->total_target = out->initial;
644d515dd77SPatrick Mooney out->time_target = time_target;
645d515dd77SPatrick Mooney out->time_loaded = time_target -
646d515dd77SPatrick Mooney hrt_freq_interval(PIT_8254_FREQ, out->initial);
647*2cac0506SPatrick Mooney
648*2cac0506SPatrick Mooney if (!vm_is_paused(vatpit->vm)) {
649*2cac0506SPatrick Mooney vatpit_callout_reset(vatpit);
650*2cac0506SPatrick Mooney }
651d515dd77SPatrick Mooney }
652d515dd77SPatrick Mooney VATPIT_UNLOCK(vatpit);
653d515dd77SPatrick Mooney
654d515dd77SPatrick Mooney return (0);
655d515dd77SPatrick Mooney }
656d515dd77SPatrick Mooney
657d515dd77SPatrick Mooney static const vmm_data_version_entry_t atpit_v1 = {
658d515dd77SPatrick Mooney .vdve_class = VDC_ATPIT,
659d515dd77SPatrick Mooney .vdve_version = 1,
660d515dd77SPatrick Mooney .vdve_len_expect = sizeof (struct vdi_atpit_v1),
661d515dd77SPatrick Mooney .vdve_readf = vatpit_data_read,
662d515dd77SPatrick Mooney .vdve_writef = vatpit_data_write,
663d515dd77SPatrick Mooney };
664d515dd77SPatrick Mooney VMM_DATA_VERSION(atpit_v1);
665