1e76f5e73SSepherosa Ziehau /*
2e76f5e73SSepherosa Ziehau * Copyright (c) 2014 The DragonFly Project. All rights reserved.
3e76f5e73SSepherosa Ziehau *
4e76f5e73SSepherosa Ziehau * This code is derived from software contributed to The DragonFly Project
5e76f5e73SSepherosa Ziehau * by Sepherosa Ziehau <sepherosa@gmail.com>
6e76f5e73SSepherosa Ziehau *
7e76f5e73SSepherosa Ziehau * Redistribution and use in source and binary forms, with or without
8e76f5e73SSepherosa Ziehau * modification, are permitted provided that the following conditions
9e76f5e73SSepherosa Ziehau * are met:
10e76f5e73SSepherosa Ziehau *
11e76f5e73SSepherosa Ziehau * 1. Redistributions of source code must retain the above copyright
12e76f5e73SSepherosa Ziehau * notice, this list of conditions and the following disclaimer.
13e76f5e73SSepherosa Ziehau * 2. Redistributions in binary form must reproduce the above copyright
14e76f5e73SSepherosa Ziehau * notice, this list of conditions and the following disclaimer in
15e76f5e73SSepherosa Ziehau * the documentation and/or other materials provided with the
16e76f5e73SSepherosa Ziehau * distribution.
17e76f5e73SSepherosa Ziehau * 3. Neither the name of The DragonFly Project nor the names of its
18e76f5e73SSepherosa Ziehau * contributors may be used to endorse or promote products derived
19e76f5e73SSepherosa Ziehau * from this software without specific, prior written permission.
20e76f5e73SSepherosa Ziehau *
21e76f5e73SSepherosa Ziehau * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22e76f5e73SSepherosa Ziehau * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23e76f5e73SSepherosa Ziehau * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24e76f5e73SSepherosa Ziehau * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25e76f5e73SSepherosa Ziehau * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26e76f5e73SSepherosa Ziehau * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27e76f5e73SSepherosa Ziehau * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28e76f5e73SSepherosa Ziehau * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29e76f5e73SSepherosa Ziehau * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30e76f5e73SSepherosa Ziehau * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31e76f5e73SSepherosa Ziehau * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32e76f5e73SSepherosa Ziehau * SUCH DAMAGE.
33e76f5e73SSepherosa Ziehau */
34e76f5e73SSepherosa Ziehau
35e76f5e73SSepherosa Ziehau #include <sys/param.h>
36e76f5e73SSepherosa Ziehau #include <sys/conf.h>
37e76f5e73SSepherosa Ziehau #include <sys/kernel.h>
38e76f5e73SSepherosa Ziehau #include <sys/bus.h>
39e76f5e73SSepherosa Ziehau #include <sys/cpu_topology.h>
400e4ac8cfSSepherosa Ziehau #include <sys/cpuhelper.h>
41*805c8e8eSzrj #include <sys/malloc.h>
42e76f5e73SSepherosa Ziehau #include <sys/module.h>
43e76f5e73SSepherosa Ziehau #include <sys/queue.h>
44e76f5e73SSepherosa Ziehau #include <sys/serialize.h>
45e76f5e73SSepherosa Ziehau #include <sys/sysctl.h>
46e76f5e73SSepherosa Ziehau #include <sys/systm.h>
47e76f5e73SSepherosa Ziehau
48e76f5e73SSepherosa Ziehau #include <machine/specialreg.h>
49e76f5e73SSepherosa Ziehau #include <machine/cpufunc.h>
50e76f5e73SSepherosa Ziehau #include <machine/cputypes.h>
51e76f5e73SSepherosa Ziehau #include <machine/md_var.h>
52e76f5e73SSepherosa Ziehau
53e76f5e73SSepherosa Ziehau struct clockmod_dom;
54e76f5e73SSepherosa Ziehau
55e76f5e73SSepherosa Ziehau struct clockmod_softc {
56e76f5e73SSepherosa Ziehau TAILQ_ENTRY(clockmod_softc) sc_link;
57e76f5e73SSepherosa Ziehau struct clockmod_dom *sc_dom;
58e76f5e73SSepherosa Ziehau int sc_cpuid;
59e76f5e73SSepherosa Ziehau };
60e76f5e73SSepherosa Ziehau
61e76f5e73SSepherosa Ziehau struct clockmod_dom {
62e76f5e73SSepherosa Ziehau TAILQ_ENTRY(clockmod_dom) dom_link;
63e76f5e73SSepherosa Ziehau TAILQ_HEAD(, clockmod_softc) dom_list;
64e76f5e73SSepherosa Ziehau struct sysctl_ctx_list dom_sysctl_ctx;
65e76f5e73SSepherosa Ziehau struct sysctl_oid *dom_sysctl_tree;
66e76f5e73SSepherosa Ziehau cpumask_t dom_cpumask;
67e76f5e73SSepherosa Ziehau char dom_name[16];
68e76f5e73SSepherosa Ziehau int dom_select;
69e76f5e73SSepherosa Ziehau uint32_t dom_flags;
70e76f5e73SSepherosa Ziehau };
71e76f5e73SSepherosa Ziehau
72e76f5e73SSepherosa Ziehau #define CLOCKMOD_DOM_FLAG_ACTIVE 0x1
73e76f5e73SSepherosa Ziehau
74e76f5e73SSepherosa Ziehau struct clockmod_dom_ctrl {
75e76f5e73SSepherosa Ziehau char ctl_name[8];
76e76f5e73SSepherosa Ziehau uint64_t ctl_value;
77e76f5e73SSepherosa Ziehau };
78e76f5e73SSepherosa Ziehau
79e76f5e73SSepherosa Ziehau static int clockmod_dom_attach(struct clockmod_softc *);
80e76f5e73SSepherosa Ziehau static void clockmod_dom_detach(struct clockmod_softc *);
81e76f5e73SSepherosa Ziehau static struct clockmod_dom *clockmod_dom_find(cpumask_t);
82e76f5e73SSepherosa Ziehau static struct clockmod_dom *clockmod_dom_create(cpumask_t);
83e76f5e73SSepherosa Ziehau static void clockmod_dom_destroy(struct clockmod_dom *);
84e76f5e73SSepherosa Ziehau
85e76f5e73SSepherosa Ziehau static int clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS);
86e76f5e73SSepherosa Ziehau static int clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS);
87e76f5e73SSepherosa Ziehau static int clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS);
88e76f5e73SSepherosa Ziehau
89e76f5e73SSepherosa Ziehau static void clockmod_identify(driver_t *, device_t);
90e76f5e73SSepherosa Ziehau static int clockmod_probe(device_t);
91e76f5e73SSepherosa Ziehau static int clockmod_attach(device_t);
92e76f5e73SSepherosa Ziehau static int clockmod_detach(device_t);
93e76f5e73SSepherosa Ziehau
940e4ac8cfSSepherosa Ziehau static void clockmod_select_handler(struct cpuhelper_msg *);
95e76f5e73SSepherosa Ziehau static int clockmod_select(const struct clockmod_softc *,
96e76f5e73SSepherosa Ziehau const struct clockmod_dom_ctrl *);
97e76f5e73SSepherosa Ziehau
98e76f5e73SSepherosa Ziehau static boolean_t clockmod_errata_duty(int);
99e76f5e73SSepherosa Ziehau
100e76f5e73SSepherosa Ziehau static struct lwkt_serialize clockmod_dom_slize = LWKT_SERIALIZE_INITIALIZER;
101e76f5e73SSepherosa Ziehau static int clockmod_dom_id;
102e76f5e73SSepherosa Ziehau static TAILQ_HEAD(, clockmod_dom) clockmod_dom_list =
103e76f5e73SSepherosa Ziehau TAILQ_HEAD_INITIALIZER(clockmod_dom_list);
104e76f5e73SSepherosa Ziehau static int clockmod_dom_nctrl;
105e76f5e73SSepherosa Ziehau static struct clockmod_dom_ctrl *clockmod_dom_controls;
106e76f5e73SSepherosa Ziehau
107e76f5e73SSepherosa Ziehau static device_method_t clockmod_methods[] = {
108e76f5e73SSepherosa Ziehau /* Device interface */
109e76f5e73SSepherosa Ziehau DEVMETHOD(device_identify, clockmod_identify),
110e76f5e73SSepherosa Ziehau DEVMETHOD(device_probe, clockmod_probe),
111e76f5e73SSepherosa Ziehau DEVMETHOD(device_attach, clockmod_attach),
112e76f5e73SSepherosa Ziehau DEVMETHOD(device_detach, clockmod_detach),
113e76f5e73SSepherosa Ziehau
114e76f5e73SSepherosa Ziehau DEVMETHOD_END
115e76f5e73SSepherosa Ziehau };
116e76f5e73SSepherosa Ziehau
117e76f5e73SSepherosa Ziehau static driver_t clockmod_driver = {
118e76f5e73SSepherosa Ziehau "clockmod",
119e76f5e73SSepherosa Ziehau clockmod_methods,
120e76f5e73SSepherosa Ziehau sizeof(struct clockmod_softc),
121e76f5e73SSepherosa Ziehau };
122e76f5e73SSepherosa Ziehau
123e76f5e73SSepherosa Ziehau static devclass_t clockmod_devclass;
124e76f5e73SSepherosa Ziehau DRIVER_MODULE(clockmod, cpu, clockmod_driver, clockmod_devclass, NULL, NULL);
125e76f5e73SSepherosa Ziehau
126e76f5e73SSepherosa Ziehau static void
clockmod_identify(driver_t * driver,device_t parent)127e76f5e73SSepherosa Ziehau clockmod_identify(driver_t *driver, device_t parent)
128e76f5e73SSepherosa Ziehau {
129e76f5e73SSepherosa Ziehau device_t child;
130e76f5e73SSepherosa Ziehau
131e76f5e73SSepherosa Ziehau if (device_find_child(parent, "clockmod", -1) != NULL)
132e76f5e73SSepherosa Ziehau return;
133e76f5e73SSepherosa Ziehau
134e76f5e73SSepherosa Ziehau if (cpu_vendor_id != CPU_VENDOR_INTEL)
135e76f5e73SSepherosa Ziehau return;
136e76f5e73SSepherosa Ziehau
137e76f5e73SSepherosa Ziehau if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != (CPUID_ACPI | CPUID_TM))
138e76f5e73SSepherosa Ziehau return;
139e76f5e73SSepherosa Ziehau
140e76f5e73SSepherosa Ziehau child = device_add_child(parent, "clockmod", device_get_unit(parent));
141e76f5e73SSepherosa Ziehau if (child == NULL)
142e76f5e73SSepherosa Ziehau device_printf(parent, "add clockmod failed\n");
143e76f5e73SSepherosa Ziehau }
144e76f5e73SSepherosa Ziehau
145e76f5e73SSepherosa Ziehau static int
clockmod_probe(device_t dev)146e76f5e73SSepherosa Ziehau clockmod_probe(device_t dev)
147e76f5e73SSepherosa Ziehau {
148e76f5e73SSepherosa Ziehau device_set_desc(dev, "CPU clock modulation");
149e76f5e73SSepherosa Ziehau return 0;
150e76f5e73SSepherosa Ziehau }
151e76f5e73SSepherosa Ziehau
152e76f5e73SSepherosa Ziehau static int
clockmod_attach(device_t dev)153e76f5e73SSepherosa Ziehau clockmod_attach(device_t dev)
154e76f5e73SSepherosa Ziehau {
155e76f5e73SSepherosa Ziehau struct clockmod_softc *sc = device_get_softc(dev);
156e76f5e73SSepherosa Ziehau int error;
157e76f5e73SSepherosa Ziehau
158e76f5e73SSepherosa Ziehau sc->sc_cpuid = device_get_unit(dev);
159e76f5e73SSepherosa Ziehau
160e76f5e73SSepherosa Ziehau error = clockmod_dom_attach(sc);
161e76f5e73SSepherosa Ziehau if (error) {
162e76f5e73SSepherosa Ziehau device_printf(dev, "domain attach failed\n");
163e76f5e73SSepherosa Ziehau return error;
164e76f5e73SSepherosa Ziehau }
165e76f5e73SSepherosa Ziehau
166e76f5e73SSepherosa Ziehau return 0;
167e76f5e73SSepherosa Ziehau }
168e76f5e73SSepherosa Ziehau
169e76f5e73SSepherosa Ziehau static int
clockmod_detach(device_t dev)170e76f5e73SSepherosa Ziehau clockmod_detach(device_t dev)
171e76f5e73SSepherosa Ziehau {
172e76f5e73SSepherosa Ziehau clockmod_dom_detach(device_get_softc(dev));
173e76f5e73SSepherosa Ziehau return 0;
174e76f5e73SSepherosa Ziehau }
175e76f5e73SSepherosa Ziehau
176e76f5e73SSepherosa Ziehau static int
clockmod_dom_attach(struct clockmod_softc * sc)177e76f5e73SSepherosa Ziehau clockmod_dom_attach(struct clockmod_softc *sc)
178e76f5e73SSepherosa Ziehau {
179e76f5e73SSepherosa Ziehau struct clockmod_softc *sc1;
180e76f5e73SSepherosa Ziehau struct clockmod_dom *dom;
181c07315c4SMatthew Dillon cpumask_t mask, found_mask;
182e76f5e73SSepherosa Ziehau int error = 0;
183e76f5e73SSepherosa Ziehau
184c07315c4SMatthew Dillon CPUMASK_ASSZERO(found_mask);
185c07315c4SMatthew Dillon
186e76f5e73SSepherosa Ziehau mask = get_cpumask_from_level(sc->sc_cpuid, CORE_LEVEL);
187c07315c4SMatthew Dillon if (CPUMASK_TESTZERO(mask))
188c07315c4SMatthew Dillon CPUMASK_ASSBIT(mask, sc->sc_cpuid);
189e76f5e73SSepherosa Ziehau
190e76f5e73SSepherosa Ziehau lwkt_serialize_enter(&clockmod_dom_slize);
191e76f5e73SSepherosa Ziehau
192e76f5e73SSepherosa Ziehau dom = clockmod_dom_find(mask);
193e76f5e73SSepherosa Ziehau if (dom == NULL) {
194e76f5e73SSepherosa Ziehau dom = clockmod_dom_create(mask);
195e76f5e73SSepherosa Ziehau if (dom == NULL) {
196e76f5e73SSepherosa Ziehau error = ENOMEM;
197e76f5e73SSepherosa Ziehau goto back;
198e76f5e73SSepherosa Ziehau }
199e76f5e73SSepherosa Ziehau }
200e76f5e73SSepherosa Ziehau
201e76f5e73SSepherosa Ziehau sc->sc_dom = dom;
202e76f5e73SSepherosa Ziehau TAILQ_INSERT_TAIL(&dom->dom_list, sc, sc_link);
203e76f5e73SSepherosa Ziehau
204e76f5e73SSepherosa Ziehau TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
205c07315c4SMatthew Dillon CPUMASK_ORBIT(found_mask, sc1->sc_cpuid);
206e76f5e73SSepherosa Ziehau
207c07315c4SMatthew Dillon if (CPUMASK_CMPMASKEQ(found_mask, dom->dom_cpumask)) {
208e76f5e73SSepherosa Ziehau /* All cpus in this domain is found */
209e76f5e73SSepherosa Ziehau dom->dom_flags |= CLOCKMOD_DOM_FLAG_ACTIVE;
210e76f5e73SSepherosa Ziehau }
211e76f5e73SSepherosa Ziehau back:
212e76f5e73SSepherosa Ziehau lwkt_serialize_exit(&clockmod_dom_slize);
213e76f5e73SSepherosa Ziehau return error;
214e76f5e73SSepherosa Ziehau }
215e76f5e73SSepherosa Ziehau
216e76f5e73SSepherosa Ziehau static void
clockmod_dom_detach(struct clockmod_softc * sc)217e76f5e73SSepherosa Ziehau clockmod_dom_detach(struct clockmod_softc *sc)
218e76f5e73SSepherosa Ziehau {
219e76f5e73SSepherosa Ziehau struct clockmod_dom *dom;
220e76f5e73SSepherosa Ziehau
221e76f5e73SSepherosa Ziehau lwkt_serialize_enter(&clockmod_dom_slize);
222e76f5e73SSepherosa Ziehau
223e76f5e73SSepherosa Ziehau dom = sc->sc_dom;
224e76f5e73SSepherosa Ziehau sc->sc_dom = NULL;
225e76f5e73SSepherosa Ziehau
226e76f5e73SSepherosa Ziehau if (dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) {
227e76f5e73SSepherosa Ziehau struct clockmod_softc *sc1;
228e76f5e73SSepherosa Ziehau
229e76f5e73SSepherosa Ziehau /* Raise to 100% */
230e76f5e73SSepherosa Ziehau TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
231e76f5e73SSepherosa Ziehau clockmod_select(sc1, &clockmod_dom_controls[0]);
232e76f5e73SSepherosa Ziehau }
233e76f5e73SSepherosa Ziehau
234e76f5e73SSepherosa Ziehau /* One cpu is leaving; domain is no longer active */
235e76f5e73SSepherosa Ziehau dom->dom_flags &= ~CLOCKMOD_DOM_FLAG_ACTIVE;
236e76f5e73SSepherosa Ziehau
237e76f5e73SSepherosa Ziehau TAILQ_REMOVE(&dom->dom_list, sc, sc_link);
238e76f5e73SSepherosa Ziehau if (TAILQ_EMPTY(&dom->dom_list))
239e76f5e73SSepherosa Ziehau clockmod_dom_destroy(dom);
240e76f5e73SSepherosa Ziehau
241e76f5e73SSepherosa Ziehau lwkt_serialize_exit(&clockmod_dom_slize);
242e76f5e73SSepherosa Ziehau }
243e76f5e73SSepherosa Ziehau
244e76f5e73SSepherosa Ziehau static struct clockmod_dom *
clockmod_dom_find(cpumask_t mask)245e76f5e73SSepherosa Ziehau clockmod_dom_find(cpumask_t mask)
246e76f5e73SSepherosa Ziehau {
247e76f5e73SSepherosa Ziehau struct clockmod_dom *dom;
248e76f5e73SSepherosa Ziehau
249e76f5e73SSepherosa Ziehau TAILQ_FOREACH(dom, &clockmod_dom_list, dom_link) {
250c07315c4SMatthew Dillon if (CPUMASK_CMPMASKEQ(dom->dom_cpumask, mask))
251e76f5e73SSepherosa Ziehau return dom;
252e76f5e73SSepherosa Ziehau }
253e76f5e73SSepherosa Ziehau return NULL;
254e76f5e73SSepherosa Ziehau }
255e76f5e73SSepherosa Ziehau
256e76f5e73SSepherosa Ziehau static struct clockmod_dom *
clockmod_dom_create(cpumask_t mask)257e76f5e73SSepherosa Ziehau clockmod_dom_create(cpumask_t mask)
258e76f5e73SSepherosa Ziehau {
259e76f5e73SSepherosa Ziehau struct clockmod_dom *dom;
260e76f5e73SSepherosa Ziehau int id;
261e76f5e73SSepherosa Ziehau
262e76f5e73SSepherosa Ziehau id = clockmod_dom_id++;
263e76f5e73SSepherosa Ziehau dom = kmalloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO);
264e76f5e73SSepherosa Ziehau
265e76f5e73SSepherosa Ziehau TAILQ_INIT(&dom->dom_list);
266e76f5e73SSepherosa Ziehau dom->dom_cpumask = mask;
267e76f5e73SSepherosa Ziehau ksnprintf(dom->dom_name, sizeof(dom->dom_name), "clockmod_dom%d", id);
268e76f5e73SSepherosa Ziehau
269e76f5e73SSepherosa Ziehau sysctl_ctx_init(&dom->dom_sysctl_ctx);
270e76f5e73SSepherosa Ziehau dom->dom_sysctl_tree = SYSCTL_ADD_NODE(&dom->dom_sysctl_ctx,
271e76f5e73SSepherosa Ziehau SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, dom->dom_name,
272e76f5e73SSepherosa Ziehau CTLFLAG_RD, 0, "");
273e76f5e73SSepherosa Ziehau if (dom->dom_sysctl_tree == NULL) {
274e76f5e73SSepherosa Ziehau kprintf("%s: can't add sysctl node\n", dom->dom_name);
275e76f5e73SSepherosa Ziehau kfree(dom, M_DEVBUF);
276e76f5e73SSepherosa Ziehau return NULL;
277e76f5e73SSepherosa Ziehau }
278e76f5e73SSepherosa Ziehau
279e76f5e73SSepherosa Ziehau SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
280e76f5e73SSepherosa Ziehau SYSCTL_CHILDREN(dom->dom_sysctl_tree),
281e76f5e73SSepherosa Ziehau OID_AUTO, "members", CTLTYPE_STRING | CTLFLAG_RD,
282e76f5e73SSepherosa Ziehau dom, 0, clockmod_dom_sysctl_members, "A", "member cpus");
283e76f5e73SSepherosa Ziehau
284e76f5e73SSepherosa Ziehau SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
285e76f5e73SSepherosa Ziehau SYSCTL_CHILDREN(dom->dom_sysctl_tree),
286e76f5e73SSepherosa Ziehau OID_AUTO, "available", CTLTYPE_STRING | CTLFLAG_RD,
287e76f5e73SSepherosa Ziehau dom, 0, clockmod_dom_sysctl_available, "A",
288e76f5e73SSepherosa Ziehau "available duty percent");
289e76f5e73SSepherosa Ziehau
290e76f5e73SSepherosa Ziehau SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
291e76f5e73SSepherosa Ziehau SYSCTL_CHILDREN(dom->dom_sysctl_tree),
292e76f5e73SSepherosa Ziehau OID_AUTO, "select", CTLTYPE_STRING | CTLFLAG_RW,
293e76f5e73SSepherosa Ziehau dom, 0, clockmod_dom_sysctl_select, "A", "select duty");
294e76f5e73SSepherosa Ziehau
295e76f5e73SSepherosa Ziehau TAILQ_INSERT_TAIL(&clockmod_dom_list, dom, dom_link);
296e76f5e73SSepherosa Ziehau
297e76f5e73SSepherosa Ziehau if (clockmod_dom_controls == NULL) {
298e76f5e73SSepherosa Ziehau int nctrl, step, i, shift, cnt;
299e76f5e73SSepherosa Ziehau
300e76f5e73SSepherosa Ziehau #ifdef __x86_64__
301e76f5e73SSepherosa Ziehau if (cpu_thermal_feature & CPUID_THERMAL_ECMD)
302e76f5e73SSepherosa Ziehau shift = 0;
303e76f5e73SSepherosa Ziehau else
304e76f5e73SSepherosa Ziehau #endif
305e76f5e73SSepherosa Ziehau shift = 1;
306e76f5e73SSepherosa Ziehau
307e76f5e73SSepherosa Ziehau nctrl = 8 << (1 - shift);
308e76f5e73SSepherosa Ziehau step = 10000 / nctrl;
309e76f5e73SSepherosa Ziehau
310e76f5e73SSepherosa Ziehau clockmod_dom_controls =
311e76f5e73SSepherosa Ziehau kmalloc(sizeof(struct clockmod_dom_ctrl) * nctrl, M_DEVBUF,
312e76f5e73SSepherosa Ziehau M_WAITOK | M_ZERO);
313e76f5e73SSepherosa Ziehau
314e76f5e73SSepherosa Ziehau if (bootverbose)
315e76f5e73SSepherosa Ziehau kprintf("clock modulation:\n");
316e76f5e73SSepherosa Ziehau
317e76f5e73SSepherosa Ziehau cnt = 0;
318e76f5e73SSepherosa Ziehau for (i = 0; i < nctrl; ++i) {
319e76f5e73SSepherosa Ziehau struct clockmod_dom_ctrl *ctrl =
320e76f5e73SSepherosa Ziehau &clockmod_dom_controls[cnt];
321e76f5e73SSepherosa Ziehau int duty;
322e76f5e73SSepherosa Ziehau
323e76f5e73SSepherosa Ziehau duty = 10000 - (i * step);
324e76f5e73SSepherosa Ziehau if (clockmod_errata_duty(duty))
325e76f5e73SSepherosa Ziehau continue;
326e76f5e73SSepherosa Ziehau ++cnt;
327e76f5e73SSepherosa Ziehau
328e76f5e73SSepherosa Ziehau ksnprintf(ctrl->ctl_name, sizeof(ctrl->ctl_name),
329e76f5e73SSepherosa Ziehau "%d.%02d%%", duty / 100, duty % 100);
330e76f5e73SSepherosa Ziehau ctrl->ctl_value = (((nctrl - i) << shift) & 0xf);
331e76f5e73SSepherosa Ziehau if (i != 0)
332e76f5e73SSepherosa Ziehau ctrl->ctl_value |= 1 << 4;
333e76f5e73SSepherosa Ziehau
334e76f5e73SSepherosa Ziehau if (bootverbose) {
335e76f5e73SSepherosa Ziehau kprintf(" 0x%04jx %s\n",
336e76f5e73SSepherosa Ziehau (uintmax_t)ctrl->ctl_value,
337e76f5e73SSepherosa Ziehau ctrl->ctl_name);
338e76f5e73SSepherosa Ziehau }
339e76f5e73SSepherosa Ziehau }
340e76f5e73SSepherosa Ziehau clockmod_dom_nctrl = cnt;
341e76f5e73SSepherosa Ziehau }
342e76f5e73SSepherosa Ziehau return dom;
343e76f5e73SSepherosa Ziehau }
344e76f5e73SSepherosa Ziehau
345e76f5e73SSepherosa Ziehau static void
clockmod_dom_destroy(struct clockmod_dom * dom)346e76f5e73SSepherosa Ziehau clockmod_dom_destroy(struct clockmod_dom *dom)
347e76f5e73SSepherosa Ziehau {
348e76f5e73SSepherosa Ziehau KASSERT(TAILQ_EMPTY(&dom->dom_list),
349e76f5e73SSepherosa Ziehau ("%s: still has member cpus", dom->dom_name));
350e76f5e73SSepherosa Ziehau TAILQ_REMOVE(&clockmod_dom_list, dom, dom_link);
351e76f5e73SSepherosa Ziehau
352e76f5e73SSepherosa Ziehau sysctl_ctx_free(&dom->dom_sysctl_ctx);
353e76f5e73SSepherosa Ziehau kfree(dom, M_DEVBUF);
354e76f5e73SSepherosa Ziehau
355e76f5e73SSepherosa Ziehau if (TAILQ_EMPTY(&clockmod_dom_list)) {
356e76f5e73SSepherosa Ziehau clockmod_dom_nctrl = 0;
357e76f5e73SSepherosa Ziehau kfree(clockmod_dom_controls, M_DEVBUF);
358e76f5e73SSepherosa Ziehau clockmod_dom_controls = NULL;
359e76f5e73SSepherosa Ziehau }
360e76f5e73SSepherosa Ziehau }
361e76f5e73SSepherosa Ziehau
362e76f5e73SSepherosa Ziehau static int
clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS)363e76f5e73SSepherosa Ziehau clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS)
364e76f5e73SSepherosa Ziehau {
365e76f5e73SSepherosa Ziehau struct clockmod_dom *dom = arg1;
366e76f5e73SSepherosa Ziehau struct clockmod_softc *sc;
367e76f5e73SSepherosa Ziehau int loop, error;
368e76f5e73SSepherosa Ziehau
369e76f5e73SSepherosa Ziehau lwkt_serialize_enter(&clockmod_dom_slize);
370e76f5e73SSepherosa Ziehau
371e76f5e73SSepherosa Ziehau loop = error = 0;
372e76f5e73SSepherosa Ziehau TAILQ_FOREACH(sc, &dom->dom_list, sc_link) {
373e76f5e73SSepherosa Ziehau char buf[16];
374e76f5e73SSepherosa Ziehau
375e76f5e73SSepherosa Ziehau if (error == 0 && loop)
376e76f5e73SSepherosa Ziehau error = SYSCTL_OUT(req, " ", 1);
377e76f5e73SSepherosa Ziehau if (error == 0) {
378e76f5e73SSepherosa Ziehau ksnprintf(buf, sizeof(buf), "cpu%d", sc->sc_cpuid);
379e76f5e73SSepherosa Ziehau error = SYSCTL_OUT(req, buf, strlen(buf));
380e76f5e73SSepherosa Ziehau }
381e76f5e73SSepherosa Ziehau ++loop;
382e76f5e73SSepherosa Ziehau }
383e76f5e73SSepherosa Ziehau
384e76f5e73SSepherosa Ziehau lwkt_serialize_exit(&clockmod_dom_slize);
385e76f5e73SSepherosa Ziehau return error;
386e76f5e73SSepherosa Ziehau }
387e76f5e73SSepherosa Ziehau
388e76f5e73SSepherosa Ziehau static int
clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS)389e76f5e73SSepherosa Ziehau clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS)
390e76f5e73SSepherosa Ziehau {
391e76f5e73SSepherosa Ziehau struct clockmod_dom *dom = arg1;
392e76f5e73SSepherosa Ziehau int loop, error, i;
393e76f5e73SSepherosa Ziehau
394e76f5e73SSepherosa Ziehau lwkt_serialize_enter(&clockmod_dom_slize);
395e76f5e73SSepherosa Ziehau
396e76f5e73SSepherosa Ziehau if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
397e76f5e73SSepherosa Ziehau error = SYSCTL_OUT(req, " ", 1);
398e76f5e73SSepherosa Ziehau goto done;
399e76f5e73SSepherosa Ziehau }
400e76f5e73SSepherosa Ziehau
401e76f5e73SSepherosa Ziehau loop = error = 0;
402e76f5e73SSepherosa Ziehau for (i = 0; i < clockmod_dom_nctrl; ++i) {
403e76f5e73SSepherosa Ziehau if (error == 0 && loop)
404e76f5e73SSepherosa Ziehau error = SYSCTL_OUT(req, " ", 1);
405e76f5e73SSepherosa Ziehau if (error == 0) {
406e76f5e73SSepherosa Ziehau error = SYSCTL_OUT(req,
407e76f5e73SSepherosa Ziehau clockmod_dom_controls[i].ctl_name,
408e76f5e73SSepherosa Ziehau strlen(clockmod_dom_controls[i].ctl_name));
409e76f5e73SSepherosa Ziehau }
410e76f5e73SSepherosa Ziehau ++loop;
411e76f5e73SSepherosa Ziehau }
412e76f5e73SSepherosa Ziehau done:
413e76f5e73SSepherosa Ziehau lwkt_serialize_exit(&clockmod_dom_slize);
414e76f5e73SSepherosa Ziehau return error;
415e76f5e73SSepherosa Ziehau }
416e76f5e73SSepherosa Ziehau
417e76f5e73SSepherosa Ziehau static int
clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS)418e76f5e73SSepherosa Ziehau clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS)
419e76f5e73SSepherosa Ziehau {
420e76f5e73SSepherosa Ziehau struct clockmod_dom *dom = arg1;
421e76f5e73SSepherosa Ziehau struct clockmod_softc *sc;
422e76f5e73SSepherosa Ziehau const struct clockmod_dom_ctrl *ctrl = NULL;
423e76f5e73SSepherosa Ziehau char duty[16];
424e76f5e73SSepherosa Ziehau int error, i;
425e76f5e73SSepherosa Ziehau
426e76f5e73SSepherosa Ziehau lwkt_serialize_enter(&clockmod_dom_slize);
427e76f5e73SSepherosa Ziehau KKASSERT(dom->dom_select >= 0 && dom->dom_select < clockmod_dom_nctrl);
428e76f5e73SSepherosa Ziehau ksnprintf(duty, sizeof(duty), "%s",
429e76f5e73SSepherosa Ziehau clockmod_dom_controls[dom->dom_select].ctl_name);
430e76f5e73SSepherosa Ziehau lwkt_serialize_exit(&clockmod_dom_slize);
431e76f5e73SSepherosa Ziehau
432e76f5e73SSepherosa Ziehau error = sysctl_handle_string(oidp, duty, sizeof(duty), req);
433e76f5e73SSepherosa Ziehau if (error != 0 || req->newptr == NULL)
434e76f5e73SSepherosa Ziehau return error;
435e76f5e73SSepherosa Ziehau
436e76f5e73SSepherosa Ziehau lwkt_serialize_enter(&clockmod_dom_slize);
437e76f5e73SSepherosa Ziehau
438e76f5e73SSepherosa Ziehau if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
439e76f5e73SSepherosa Ziehau error = EOPNOTSUPP;
440e76f5e73SSepherosa Ziehau goto back;
441e76f5e73SSepherosa Ziehau }
442e76f5e73SSepherosa Ziehau
443e76f5e73SSepherosa Ziehau for (i = 0; i < clockmod_dom_nctrl; ++i) {
444e76f5e73SSepherosa Ziehau ctrl = &clockmod_dom_controls[i];
445e76f5e73SSepherosa Ziehau if (strcmp(duty, ctrl->ctl_name) == 0)
446e76f5e73SSepherosa Ziehau break;
447e76f5e73SSepherosa Ziehau }
448e76f5e73SSepherosa Ziehau if (i == clockmod_dom_nctrl) {
449e76f5e73SSepherosa Ziehau error = EINVAL;
450e76f5e73SSepherosa Ziehau goto back;
451e76f5e73SSepherosa Ziehau }
452e76f5e73SSepherosa Ziehau dom->dom_select = i;
453e76f5e73SSepherosa Ziehau
454e76f5e73SSepherosa Ziehau TAILQ_FOREACH(sc, &dom->dom_list, sc_link)
455e76f5e73SSepherosa Ziehau clockmod_select(sc, ctrl);
456e76f5e73SSepherosa Ziehau back:
457e76f5e73SSepherosa Ziehau lwkt_serialize_exit(&clockmod_dom_slize);
458e76f5e73SSepherosa Ziehau return error;
459e76f5e73SSepherosa Ziehau }
460e76f5e73SSepherosa Ziehau
461e76f5e73SSepherosa Ziehau static void
clockmod_select_handler(struct cpuhelper_msg * msg)4620e4ac8cfSSepherosa Ziehau clockmod_select_handler(struct cpuhelper_msg *msg)
463e76f5e73SSepherosa Ziehau {
4640e4ac8cfSSepherosa Ziehau uint64_t ctl_value = *((const uint64_t *)msg->ch_cbarg);
465e76f5e73SSepherosa Ziehau
466e76f5e73SSepherosa Ziehau #if 0
467e76f5e73SSepherosa Ziehau if (bootverbose) {
468e76f5e73SSepherosa Ziehau kprintf("cpu%d: clockmod 0x%04jx\n", mycpuid,
4690e4ac8cfSSepherosa Ziehau (uintmax_t)ctl_value);
470e76f5e73SSepherosa Ziehau }
471e76f5e73SSepherosa Ziehau #endif
4720e4ac8cfSSepherosa Ziehau wrmsr(MSR_THERM_CONTROL, ctl_value);
4730e4ac8cfSSepherosa Ziehau cpuhelper_replymsg(msg, 0);
474e76f5e73SSepherosa Ziehau }
475e76f5e73SSepherosa Ziehau
476e76f5e73SSepherosa Ziehau static int
clockmod_select(const struct clockmod_softc * sc,const struct clockmod_dom_ctrl * ctrl)477e76f5e73SSepherosa Ziehau clockmod_select(const struct clockmod_softc *sc,
478e76f5e73SSepherosa Ziehau const struct clockmod_dom_ctrl *ctrl)
479e76f5e73SSepherosa Ziehau {
4800e4ac8cfSSepherosa Ziehau struct cpuhelper_msg msg;
481e76f5e73SSepherosa Ziehau
4820e4ac8cfSSepherosa Ziehau cpuhelper_initmsg(&msg, &curthread->td_msgport,
4830e4ac8cfSSepherosa Ziehau clockmod_select_handler, __DECONST(void *, &ctrl->ctl_value),
4840e4ac8cfSSepherosa Ziehau MSGF_PRIORITY);
4850e4ac8cfSSepherosa Ziehau return (cpuhelper_domsg(&msg, sc->sc_cpuid));
486e76f5e73SSepherosa Ziehau }
487e76f5e73SSepherosa Ziehau
488e76f5e73SSepherosa Ziehau static boolean_t
clockmod_errata_duty(int duty)489e76f5e73SSepherosa Ziehau clockmod_errata_duty(int duty)
490e76f5e73SSepherosa Ziehau {
491e76f5e73SSepherosa Ziehau uint32_t model, stepping;
492e76f5e73SSepherosa Ziehau
493e76f5e73SSepherosa Ziehau /*
494e76f5e73SSepherosa Ziehau * This is obtained from the original p4tcc code.
495e76f5e73SSepherosa Ziehau *
496e76f5e73SSepherosa Ziehau * The original errata checking code in p4tcc is obviously wrong.
497e76f5e73SSepherosa Ziehau * However, I am no longer being able to find the errata mentioned
498e76f5e73SSepherosa Ziehau * in the code. The guess is that the errata only affects family
499e76f5e73SSepherosa Ziehau * 0x0f CPUs, since:
500e76f5e73SSepherosa Ziehau * - The errata applies to only to model 0x00, 0x01 and 0x02 in
501e76f5e73SSepherosa Ziehau * the original p4tcc code.
502e76f5e73SSepherosa Ziehau * - Software controlled clock modulation has been supported since
503e76f5e73SSepherosa Ziehau * 0f_00 and the model of the oldest family 0x06 CPUs supporting
504e76f5e73SSepherosa Ziehau * this feature is 0x09.
505e76f5e73SSepherosa Ziehau */
506e76f5e73SSepherosa Ziehau if (CPUID_TO_FAMILY(cpu_id) != 0xf)
507e76f5e73SSepherosa Ziehau return FALSE;
508e76f5e73SSepherosa Ziehau
509e76f5e73SSepherosa Ziehau model = CPUID_TO_MODEL(cpu_id);
510e76f5e73SSepherosa Ziehau stepping = cpu_id & 0xf;
511e76f5e73SSepherosa Ziehau
512a1efa688SSepherosa Ziehau if (model == 0x6) {
513a1efa688SSepherosa Ziehau switch (stepping) {
514a1efa688SSepherosa Ziehau case 0x2:
515a1efa688SSepherosa Ziehau case 0x4:
516a1efa688SSepherosa Ziehau case 0x5:
517a1efa688SSepherosa Ziehau /* Hang w/ 12.50% and 25.00% */
518a1efa688SSepherosa Ziehau if (duty == 1250 || duty == 2500)
519a1efa688SSepherosa Ziehau return TRUE;
520a1efa688SSepherosa Ziehau break;
521a1efa688SSepherosa Ziehau }
522a1efa688SSepherosa Ziehau } else if (model == 0x2) {
523e76f5e73SSepherosa Ziehau switch (stepping) {
524e76f5e73SSepherosa Ziehau case 0x2:
525e76f5e73SSepherosa Ziehau case 0x4:
526e76f5e73SSepherosa Ziehau case 0x5:
527e76f5e73SSepherosa Ziehau case 0x7:
528e76f5e73SSepherosa Ziehau case 0x9:
529e76f5e73SSepherosa Ziehau /* Hang w/ 12.50% */
530e76f5e73SSepherosa Ziehau if (duty == 1250)
531e76f5e73SSepherosa Ziehau return TRUE;
532e76f5e73SSepherosa Ziehau break;
533e76f5e73SSepherosa Ziehau }
534e76f5e73SSepherosa Ziehau } else if (model == 0x1) {
535e76f5e73SSepherosa Ziehau switch (stepping) {
536e76f5e73SSepherosa Ziehau case 0x2:
537e76f5e73SSepherosa Ziehau case 0x3:
538e76f5e73SSepherosa Ziehau /* Hang w/ 12.50% and 25.00% */
539e76f5e73SSepherosa Ziehau if (duty == 1250 || duty == 2500)
540e76f5e73SSepherosa Ziehau return TRUE;
541e76f5e73SSepherosa Ziehau break;
542e76f5e73SSepherosa Ziehau }
543e76f5e73SSepherosa Ziehau } else if (model == 0x0) {
544e76f5e73SSepherosa Ziehau switch (stepping) {
545e76f5e73SSepherosa Ziehau case 0x7:
546e76f5e73SSepherosa Ziehau case 0xa:
547e76f5e73SSepherosa Ziehau /* Hang w/ 12.50% and 25.00% */
548e76f5e73SSepherosa Ziehau if (duty == 1250 || duty == 2500)
549e76f5e73SSepherosa Ziehau return TRUE;
550e76f5e73SSepherosa Ziehau break;
551e76f5e73SSepherosa Ziehau }
552e76f5e73SSepherosa Ziehau }
553e76f5e73SSepherosa Ziehau return FALSE;
554e76f5e73SSepherosa Ziehau }
555