| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: hpet.c,v 1.13 2011/10/31 12:47:15 yamt Exp $ */ | | 1 | /* $NetBSD: hpet.c,v 1.14 2020/04/23 20:33:57 ad Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 2006 Nicolas Joly | | 4 | * Copyright (c) 2006 Nicolas Joly |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
| @@ -23,136 +23,178 @@ | | | @@ -23,136 +23,178 @@ |
23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 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 | | 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 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 | | 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
28 | * POSSIBILITY OF SUCH DAMAGE. | | 28 | * POSSIBILITY OF SUCH DAMAGE. |
29 | */ | | 29 | */ |
30 | | | 30 | |
31 | /* | | 31 | /* |
32 | * High Precision Event Timer. | | 32 | * High Precision Event Timer. |
33 | */ | | 33 | */ |
34 | | | 34 | |
35 | #include <sys/cdefs.h> | | 35 | #include <sys/cdefs.h> |
36 | __KERNEL_RCSID(0, "$NetBSD: hpet.c,v 1.13 2011/10/31 12:47:15 yamt Exp $"); | | 36 | __KERNEL_RCSID(0, "$NetBSD: hpet.c,v 1.14 2020/04/23 20:33:57 ad Exp $"); |
37 | | | 37 | |
38 | #include <sys/systm.h> | | 38 | #include <sys/systm.h> |
39 | #include <sys/device.h> | | 39 | #include <sys/device.h> |
40 | #include <sys/module.h> | | 40 | #include <sys/module.h> |
41 | | | 41 | |
42 | #include <sys/time.h> | | 42 | #include <sys/time.h> |
43 | #include <sys/timetc.h> | | 43 | #include <sys/timetc.h> |
44 | | | 44 | |
45 | #include <sys/bus.h> | | 45 | #include <sys/bus.h> |
| | | 46 | #include <sys/lock.h> |
46 | | | 47 | |
47 | #include <dev/ic/hpetreg.h> | | 48 | #include <dev/ic/hpetreg.h> |
48 | #include <dev/ic/hpetvar.h> | | 49 | #include <dev/ic/hpetvar.h> |
49 | | | 50 | |
50 | static u_int hpet_get_timecount(struct timecounter *); | | 51 | static u_int hpet_get_timecount(struct timecounter *); |
51 | static bool hpet_resume(device_t, const pmf_qual_t *); | | 52 | static bool hpet_resume(device_t, const pmf_qual_t *); |
52 | | | 53 | |
| | | 54 | static struct hpet_softc *hpet0 __read_mostly; |
| | | 55 | |
53 | int | | 56 | int |
54 | hpet_detach(device_t dv, int flags) | | 57 | hpet_detach(device_t dv, int flags) |
55 | { | | 58 | { |
| | | 59 | #if 0 /* XXX DELAY() is based off this, detaching is not a good idea. */ |
56 | struct hpet_softc *sc = device_private(dv); | | 60 | struct hpet_softc *sc = device_private(dv); |
57 | int rc; | | 61 | int rc; |
58 | | | 62 | |
59 | if ((rc = tc_detach(&sc->sc_tc)) != 0) | | 63 | if ((rc = tc_detach(&sc->sc_tc)) != 0) |
60 | return rc; | | 64 | return rc; |
61 | | | 65 | |
62 | pmf_device_deregister(dv); | | 66 | pmf_device_deregister(dv); |
63 | | | 67 | |
64 | bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, sc->sc_config); | | 68 | bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, sc->sc_config); |
65 | | | 69 | |
66 | return 0; | | 70 | return 0; |
| | | 71 | #else |
| | | 72 | return EBUSY; |
| | | 73 | #endif |
67 | } | | 74 | } |
68 | | | 75 | |
69 | void | | 76 | void |
70 | hpet_attach_subr(device_t dv) | | 77 | hpet_attach_subr(device_t dv) |
71 | { | | 78 | { |
72 | struct hpet_softc *sc = device_private(dv); | | 79 | struct hpet_softc *sc = device_private(dv); |
73 | struct timecounter *tc; | | 80 | struct timecounter *tc; |
74 | uint64_t tmp; | | 81 | uint64_t tmp; |
75 | uint32_t val; | | 82 | uint32_t val; |
76 | int i; | | 83 | int i; |
77 | | | 84 | |
78 | tc = &sc->sc_tc; | | 85 | tc = &sc->sc_tc; |
79 | | | 86 | |
80 | tc->tc_name = device_xname(dv); | | 87 | tc->tc_name = device_xname(dv); |
81 | tc->tc_get_timecount = hpet_get_timecount; | | 88 | tc->tc_get_timecount = hpet_get_timecount; |
82 | tc->tc_quality = 2000; | | 89 | tc->tc_quality = 2000; |
83 | | | 90 | |
84 | tc->tc_counter_mask = 0xffffffff; | | 91 | tc->tc_counter_mask = 0xffffffff; |
85 | | | 92 | |
86 | /* Get frequency */ | | 93 | /* Get frequency */ |
87 | val = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_PERIOD); | | 94 | sc->sc_period = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_PERIOD); |
88 | if (val == 0 || val > HPET_PERIOD_MAX) { | | 95 | if (sc->sc_period == 0 || sc->sc_period > HPET_PERIOD_MAX) { |
89 | aprint_error_dev(dv, "invalid timer period\n"); | | 96 | aprint_error_dev(dv, "invalid timer period\n"); |
90 | return; | | 97 | return; |
91 | } | | 98 | } |
92 | | | 99 | |
93 | /* | | 100 | /* |
94 | * The following loop is a workaround for AMD SB700 based systems. | | 101 | * The following loop is a workaround for AMD SB700 based systems. |
95 | * http://kerneltrap.org/mailarchive/git-commits-head/2008/8/17/2964724 | | 102 | * 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 | | 103 | * http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=a6825f1c1fa83b1e92b6715ee5771a4d6524d3b9 |
97 | */ | | 104 | */ |
98 | for (i = 0; bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG) | | 105 | for (i = 0; bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG) |
99 | == 0xffffffff; i++) { | | 106 | == 0xffffffff; i++) { |
100 | if (i >= 1000) { | | 107 | if (i >= 1000) { |
101 | aprint_error_dev(dv, | | 108 | aprint_error_dev(dv, |
102 | "HPET_CONFIG value = 0xffffffff\n"); | | 109 | "HPET_CONFIG value = 0xffffffff\n"); |
103 | return; | | 110 | return; |
104 | } | | 111 | } |
105 | } | | 112 | } |
106 | | | 113 | |
107 | tmp = (1000000000000000ULL * 2) / val; | | 114 | tmp = (1000000000000000ULL * 2) / sc->sc_period; |
108 | tc->tc_frequency = (tmp / 2) + (tmp & 1); | | 115 | tc->tc_frequency = (tmp / 2) + (tmp & 1); |
109 | | | 116 | |
110 | /* Enable timer */ | | 117 | /* Enable timer */ |
111 | val = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG); | | 118 | val = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG); |
112 | sc->sc_config = val; | | 119 | sc->sc_config = val; |
113 | if ((val & HPET_CONFIG_ENABLE) == 0) { | | 120 | if ((val & HPET_CONFIG_ENABLE) == 0) { |
114 | val |= HPET_CONFIG_ENABLE; | | 121 | val |= HPET_CONFIG_ENABLE; |
115 | bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, val); | | 122 | bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, val); |
116 | } | | 123 | } |
117 | | | 124 | |
118 | tc->tc_priv = sc; | | 125 | tc->tc_priv = sc; |
119 | tc_init(tc); | | 126 | tc_init(tc); |
120 | | | 127 | |
121 | if (!pmf_device_register(dv, NULL, hpet_resume)) | | 128 | if (!pmf_device_register(dv, NULL, hpet_resume)) |
122 | aprint_error_dev(dv, "couldn't establish power handler\n"); | | 129 | aprint_error_dev(dv, "couldn't establish power handler\n"); |
| | | 130 | |
| | | 131 | if (device_unit(dv) == 0) |
| | | 132 | hpet0 = sc; |
123 | } | | 133 | } |
124 | | | 134 | |
125 | static u_int | | 135 | static u_int |
126 | hpet_get_timecount(struct timecounter *tc) | | 136 | hpet_get_timecount(struct timecounter *tc) |
127 | { | | 137 | { |
128 | struct hpet_softc *sc = tc->tc_priv; | | 138 | struct hpet_softc *sc = tc->tc_priv; |
129 | | | 139 | |
130 | return bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO); | | 140 | return bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO); |
131 | } | | 141 | } |
132 | | | 142 | |
133 | static bool | | 143 | static bool |
134 | hpet_resume(device_t dv, const pmf_qual_t *qual) | | 144 | hpet_resume(device_t dv, const pmf_qual_t *qual) |
135 | { | | 145 | { |
136 | struct hpet_softc *sc = device_private(dv); | | 146 | struct hpet_softc *sc = device_private(dv); |
137 | uint32_t val; | | 147 | uint32_t val; |
138 | | | 148 | |
139 | val = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG); | | 149 | val = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG); |
140 | val |= HPET_CONFIG_ENABLE; | | 150 | val |= HPET_CONFIG_ENABLE; |
141 | bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, val); | | 151 | bus_space_write_4(sc->sc_memt, sc->sc_memh, HPET_CONFIG, val); |
142 | | | 152 | |
143 | return true; | | 153 | return true; |
144 | } | | 154 | } |
145 | | | 155 | |
| | | 156 | bool |
| | | 157 | hpet_delay_p(void) |
| | | 158 | { |
| | | 159 | |
| | | 160 | return hpet0 != NULL; |
| | | 161 | } |
| | | 162 | |
| | | 163 | void |
| | | 164 | hpet_delay(unsigned int us) |
| | | 165 | { |
| | | 166 | struct hpet_softc *sc; |
| | | 167 | uint32_t ntick, otick; |
| | | 168 | int64_t delta; |
| | | 169 | |
| | | 170 | /* |
| | | 171 | * Read timer before slow division. Assume that each read of the |
| | | 172 | * HPET costs ~500ns. Aim for the middle and subtract 750ns for |
| | | 173 | * overhead. |
| | | 174 | */ |
| | | 175 | sc = hpet0; |
| | | 176 | otick = bus_space_read_4(sc->sc_memt, sc->sc_memh, HPET_MCOUNT_LO); |
| | | 177 | delta = (((int64_t)us * 1000000000) - 750000000) / sc->sc_period; |
| | | 178 | |
| | | 179 | while (delta > 0) { |
| | | 180 | SPINLOCK_BACKOFF_HOOK; |
| | | 181 | ntick = bus_space_read_4(sc->sc_memt, sc->sc_memh, |
| | | 182 | HPET_MCOUNT_LO); |
| | | 183 | delta -= (uint32_t)(ntick - otick); |
| | | 184 | otick = ntick; |
| | | 185 | } |
| | | 186 | } |
| | | 187 | |
146 | MODULE(MODULE_CLASS_DRIVER, hpet, NULL); | | 188 | MODULE(MODULE_CLASS_DRIVER, hpet, NULL); |
147 | | | 189 | |
148 | #ifdef _MODULE | | 190 | #ifdef _MODULE |
149 | #include "ioconf.c" | | 191 | #include "ioconf.c" |
150 | #endif | | 192 | #endif |
151 | | | 193 | |
152 | static int | | 194 | static int |
153 | hpet_modcmd(modcmd_t cmd, void *aux) | | 195 | hpet_modcmd(modcmd_t cmd, void *aux) |
154 | { | | 196 | { |
155 | int rv = 0; | | 197 | int rv = 0; |
156 | | | 198 | |
157 | switch (cmd) { | | 199 | switch (cmd) { |
158 | | | 200 | |