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