1 | /* $NetBSD: hpet.c,v 1.13 2011/10/31 12:47:15 yamt Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2006 Nicolas Joly |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * 3. The name of the author may not be used to endorse or promote products |
16 | * derived from this software without specific prior written permission. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS |
19 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
20 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
21 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
22 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
28 | * POSSIBILITY OF SUCH DAMAGE. |
29 | */ |
30 | |
31 | /* |
32 | * High Precision Event Timer. |
33 | */ |
34 | |
35 | #include <sys/cdefs.h> |
36 | __KERNEL_RCSID(0, "$NetBSD: hpet.c,v 1.13 2011/10/31 12:47:15 yamt Exp $" ); |
37 | |
38 | #include <sys/systm.h> |
39 | #include <sys/device.h> |
40 | #include <sys/module.h> |
41 | |
42 | #include <sys/time.h> |
43 | #include <sys/timetc.h> |
44 | |
45 | #include <sys/bus.h> |
46 | |
47 | #include <dev/ic/hpetreg.h> |
48 | #include <dev/ic/hpetvar.h> |
49 | |
50 | static u_int hpet_get_timecount(struct timecounter *); |
51 | static bool hpet_resume(device_t, const pmf_qual_t *); |
52 | |
53 | int |
54 | hpet_detach(device_t dv, int flags) |
55 | { |
56 | struct hpet_softc *sc = device_private(dv); |
57 | int rc; |
58 | |
59 | if ((rc = tc_detach(&sc->sc_tc)) != 0) |
60 | return rc; |
61 | |
62 | pmf_device_deregister(dv); |
63 | |
64 | bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, sc->sc_config); |
65 | |
66 | return 0; |
67 | } |
68 | |
69 | void |
70 | hpet_attach_subr(device_t dv) |
71 | { |
72 | struct hpet_softc *sc = device_private(dv); |
73 | struct timecounter *tc; |
74 | uint64_t tmp; |
75 | uint32_t val; |
76 | int i; |
77 | |
78 | tc = &sc->sc_tc; |
79 | |
80 | tc->tc_name = device_xname(dv); |
81 | tc->tc_get_timecount = hpet_get_timecount; |
82 | tc->tc_quality = 2000; |
83 | |
84 | tc->tc_counter_mask = 0xffffffff; |
85 | |
86 | /* Get frequency */ |
87 | val = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_PERIOD); |
88 | if (val == 0 || val > HPET_PERIOD_MAX) { |
89 | aprint_error_dev(dv, "invalid timer period\n" ); |
90 | return; |
91 | } |
92 | |
93 | /* |
94 | * The following loop is a workaround for AMD SB700 based systems. |
95 | * http://kerneltrap.org/mailarchive/git-commits-head/2008/8/17/2964724 |
96 | * http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=a6825f1c1fa83b1e92b6715ee5771a4d6524d3b9 |
97 | */ |
98 | for (i = 0; bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG) |
99 | == 0xffffffff; i++) { |
100 | if (i >= 1000) { |
101 | aprint_error_dev(dv, |
102 | "HPET_CONFIG value = 0xffffffff\n" ); |
103 | return; |
104 | } |
105 | } |
106 | |
107 | tmp = (1000000000000000ULL * 2) / val; |
108 | tc->tc_frequency = (tmp / 2) + (tmp & 1); |
109 | |
110 | /* Enable timer */ |
111 | val = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG); |
112 | sc->sc_config = val; |
113 | if ((val & HPET_CONFIG_ENABLE) == 0) { |
114 | val |= HPET_CONFIG_ENABLE; |
115 | bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, val); |
116 | } |
117 | |
118 | tc->tc_priv = sc; |
119 | tc_init(tc); |
120 | |
121 | if (!pmf_device_register(dv, NULL, hpet_resume)) |
122 | aprint_error_dev(dv, "couldn't establish power handler\n" ); |
123 | } |
124 | |
125 | static u_int |
126 | hpet_get_timecount(struct timecounter *tc) |
127 | { |
128 | struct hpet_softc *sc = tc->tc_priv; |
129 | |
130 | return bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO); |
131 | } |
132 | |
133 | static bool |
134 | hpet_resume(device_t dv, const pmf_qual_t *qual) |
135 | { |
136 | struct hpet_softc *sc = device_private(dv); |
137 | uint32_t val; |
138 | |
139 | val = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG); |
140 | val |= HPET_CONFIG_ENABLE; |
141 | bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, val); |
142 | |
143 | return true; |
144 | } |
145 | |
146 | MODULE(MODULE_CLASS_DRIVER, hpet, NULL); |
147 | |
148 | #ifdef _MODULE |
149 | #include "ioconf.c" |
150 | #endif |
151 | |
152 | static int |
153 | hpet_modcmd(modcmd_t cmd, void *aux) |
154 | { |
155 | int rv = 0; |
156 | |
157 | switch (cmd) { |
158 | |
159 | case MODULE_CMD_INIT: |
160 | |
161 | #ifdef _MODULE |
162 | rv = config_init_component(cfdriver_ioconf_hpet, |
163 | cfattach_ioconf_hpet, cfdata_ioconf_hpet); |
164 | #endif |
165 | break; |
166 | |
167 | case MODULE_CMD_FINI: |
168 | |
169 | #ifdef _MODULE |
170 | rv = config_fini_component(cfdriver_ioconf_hpet, |
171 | cfattach_ioconf_hpet, cfdata_ioconf_hpet); |
172 | #endif |
173 | break; |
174 | |
175 | default: |
176 | rv = ENOTTY; |
177 | } |
178 | |
179 | return rv; |
180 | } |
181 | |