1/* $NetBSD: acpipmtimer.c,v 1.7 2009/05/12 14:25:17 cegger Exp $ */
2
3#include <sys/cdefs.h>
4__KERNEL_RCSID(0, "$NetBSD: acpipmtimer.c,v 1.7 2009/05/12 14:25:17 cegger Exp $");
5
6#include <sys/types.h>
7
8#include <sys/systm.h>
9#include <sys/device.h>
10#include <sys/malloc.h>
11#include <sys/bus.h>
12#include <sys/time.h>
13#include <sys/timetc.h>
14
15#include <dev/ic/acpipmtimer.h>
16
17#define ACPI_PM_TIMER_FREQUENCY 3579545
18
19struct hwtc {
20	struct timecounter tc;
21	bus_space_tag_t t;
22	bus_space_handle_t h;
23	bus_size_t off;
24};
25
26static u_int acpihwtimer_read_safe(struct timecounter *);
27static u_int acpihwtimer_read_fast(struct timecounter *);
28
29acpipmtimer_t
30acpipmtimer_attach(device_t dev,
31		   bus_space_tag_t t, bus_space_handle_t h, bus_size_t off,
32		   int flags)
33{
34	struct hwtc *tc;
35
36	tc = malloc(sizeof(struct hwtc), M_DEVBUF, M_WAITOK|M_ZERO);
37	if (tc == NULL)
38		return NULL;
39
40	tc->tc.tc_name = device_xname(dev);
41	tc->tc.tc_frequency = ACPI_PM_TIMER_FREQUENCY;
42	if (flags & ACPIPMT_32BIT)
43		tc->tc.tc_counter_mask = 0xffffffff;
44	else
45		tc->tc.tc_counter_mask = 0x00ffffff;
46	if (flags & ACPIPMT_BADLATCH) {
47		tc->tc.tc_get_timecount = acpihwtimer_read_safe;
48		tc->tc.tc_quality = 900;
49	} else {
50		tc->tc.tc_get_timecount = acpihwtimer_read_fast;
51		tc->tc.tc_quality = 1000;
52	}
53
54	tc->t = t;
55	tc->h = h;
56	tc->off = off;
57
58	tc->tc.tc_priv = tc;
59	tc_init(&tc->tc);
60	aprint_normal("%s: %d-bit timer\n", tc->tc.tc_name,
61		      (flags & ACPIPMT_32BIT ? 32 : 24));
62	return tc;
63}
64
65int
66acpipmtimer_detach(acpipmtimer_t timer, int flags)
67{
68	struct hwtc *tc = timer;
69
70	return tc_detach(&tc->tc);
71}
72
73#define r(h) bus_space_read_4(h->t, h->h, h->off)
74
75static u_int
76acpihwtimer_read_safe(struct timecounter *tc)
77{
78	struct hwtc *h = tc->tc_priv;
79	uint32_t t1, t2, t3;
80
81	t2 = r(h);
82	t3 = r(h);
83	do {
84		t1 = t2;
85		t2 = t3;
86		t3 = r(h);
87	} while ((t1 > t2) || (t2 > t3));
88	return (t2);
89}
90
91static u_int
92acpihwtimer_read_fast(struct timecounter *tc)
93{
94	struct hwtc *h = tc->tc_priv;
95
96	return r(h);
97}
98