1 /*
2 * Copyright (c) 2014 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Sepherosa Ziehau <sepherosa@gmail.com>
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 *
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.
20 *
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.
33 */
34
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>
47
48 #include <machine/specialreg.h>
49 #include <machine/cpufunc.h>
50 #include <machine/cputypes.h>
51 #include <machine/md_var.h>
52
53 struct clockmod_dom;
54
55 struct clockmod_softc {
56 TAILQ_ENTRY(clockmod_softc) sc_link;
57 struct clockmod_dom *sc_dom;
58 int sc_cpuid;
59 };
60
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;
70 };
71
72 #define CLOCKMOD_DOM_FLAG_ACTIVE 0x1
73
74 struct clockmod_dom_ctrl {
75 char ctl_name[8];
76 uint64_t ctl_value;
77 };
78
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 *);
84
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);
88
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);
93
94 static void clockmod_select_handler(struct cpuhelper_msg *);
95 static int clockmod_select(const struct clockmod_softc *,
96 const struct clockmod_dom_ctrl *);
97
98 static boolean_t clockmod_errata_duty(int);
99
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;
106
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),
113
114 DEVMETHOD_END
115 };
116
117 static driver_t clockmod_driver = {
118 "clockmod",
119 clockmod_methods,
120 sizeof(struct clockmod_softc),
121 };
122
123 static devclass_t clockmod_devclass;
124 DRIVER_MODULE(clockmod, cpu, clockmod_driver, clockmod_devclass, NULL, NULL);
125
126 static void
clockmod_identify(driver_t * driver,device_t parent)127 clockmod_identify(driver_t *driver, device_t parent)
128 {
129 device_t child;
130
131 if (device_find_child(parent, "clockmod", -1) != NULL)
132 return;
133
134 if (cpu_vendor_id != CPU_VENDOR_INTEL)
135 return;
136
137 if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != (CPUID_ACPI | CPUID_TM))
138 return;
139
140 child = device_add_child(parent, "clockmod", device_get_unit(parent));
141 if (child == NULL)
142 device_printf(parent, "add clockmod failed\n");
143 }
144
145 static int
clockmod_probe(device_t dev)146 clockmod_probe(device_t dev)
147 {
148 device_set_desc(dev, "CPU clock modulation");
149 return 0;
150 }
151
152 static int
clockmod_attach(device_t dev)153 clockmod_attach(device_t dev)
154 {
155 struct clockmod_softc *sc = device_get_softc(dev);
156 int error;
157
158 sc->sc_cpuid = device_get_unit(dev);
159
160 error = clockmod_dom_attach(sc);
161 if (error) {
162 device_printf(dev, "domain attach failed\n");
163 return error;
164 }
165
166 return 0;
167 }
168
169 static int
clockmod_detach(device_t dev)170 clockmod_detach(device_t dev)
171 {
172 clockmod_dom_detach(device_get_softc(dev));
173 return 0;
174 }
175
176 static int
clockmod_dom_attach(struct clockmod_softc * sc)177 clockmod_dom_attach(struct clockmod_softc *sc)
178 {
179 struct clockmod_softc *sc1;
180 struct clockmod_dom *dom;
181 cpumask_t mask, found_mask;
182 int error = 0;
183
184 CPUMASK_ASSZERO(found_mask);
185
186 mask = get_cpumask_from_level(sc->sc_cpuid, CORE_LEVEL);
187 if (CPUMASK_TESTZERO(mask))
188 CPUMASK_ASSBIT(mask, sc->sc_cpuid);
189
190 lwkt_serialize_enter(&clockmod_dom_slize);
191
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;
198 }
199 }
200
201 sc->sc_dom = dom;
202 TAILQ_INSERT_TAIL(&dom->dom_list, sc, sc_link);
203
204 TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
205 CPUMASK_ORBIT(found_mask, sc1->sc_cpuid);
206
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;
210 }
211 back:
212 lwkt_serialize_exit(&clockmod_dom_slize);
213 return error;
214 }
215
216 static void
clockmod_dom_detach(struct clockmod_softc * sc)217 clockmod_dom_detach(struct clockmod_softc *sc)
218 {
219 struct clockmod_dom *dom;
220
221 lwkt_serialize_enter(&clockmod_dom_slize);
222
223 dom = sc->sc_dom;
224 sc->sc_dom = NULL;
225
226 if (dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) {
227 struct clockmod_softc *sc1;
228
229 /* Raise to 100% */
230 TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
231 clockmod_select(sc1, &clockmod_dom_controls[0]);
232 }
233
234 /* One cpu is leaving; domain is no longer active */
235 dom->dom_flags &= ~CLOCKMOD_DOM_FLAG_ACTIVE;
236
237 TAILQ_REMOVE(&dom->dom_list, sc, sc_link);
238 if (TAILQ_EMPTY(&dom->dom_list))
239 clockmod_dom_destroy(dom);
240
241 lwkt_serialize_exit(&clockmod_dom_slize);
242 }
243
244 static struct clockmod_dom *
clockmod_dom_find(cpumask_t mask)245 clockmod_dom_find(cpumask_t mask)
246 {
247 struct clockmod_dom *dom;
248
249 TAILQ_FOREACH(dom, &clockmod_dom_list, dom_link) {
250 if (CPUMASK_CMPMASKEQ(dom->dom_cpumask, mask))
251 return dom;
252 }
253 return NULL;
254 }
255
256 static struct clockmod_dom *
clockmod_dom_create(cpumask_t mask)257 clockmod_dom_create(cpumask_t mask)
258 {
259 struct clockmod_dom *dom;
260 int id;
261
262 id = clockmod_dom_id++;
263 dom = kmalloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO);
264
265 TAILQ_INIT(&dom->dom_list);
266 dom->dom_cpumask = mask;
267 ksnprintf(dom->dom_name, sizeof(dom->dom_name), "clockmod_dom%d", id);
268
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;
277 }
278
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");
283
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");
289
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");
294
295 TAILQ_INSERT_TAIL(&clockmod_dom_list, dom, dom_link);
296
297 if (clockmod_dom_controls == NULL) {
298 int nctrl, step, i, shift, cnt;
299
300 #ifdef __x86_64__
301 if (cpu_thermal_feature & CPUID_THERMAL_ECMD)
302 shift = 0;
303 else
304 #endif
305 shift = 1;
306
307 nctrl = 8 << (1 - shift);
308 step = 10000 / nctrl;
309
310 clockmod_dom_controls =
311 kmalloc(sizeof(struct clockmod_dom_ctrl) * nctrl, M_DEVBUF,
312 M_WAITOK | M_ZERO);
313
314 if (bootverbose)
315 kprintf("clock modulation:\n");
316
317 cnt = 0;
318 for (i = 0; i < nctrl; ++i) {
319 struct clockmod_dom_ctrl *ctrl =
320 &clockmod_dom_controls[cnt];
321 int duty;
322
323 duty = 10000 - (i * step);
324 if (clockmod_errata_duty(duty))
325 continue;
326 ++cnt;
327
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;
333
334 if (bootverbose) {
335 kprintf(" 0x%04jx %s\n",
336 (uintmax_t)ctrl->ctl_value,
337 ctrl->ctl_name);
338 }
339 }
340 clockmod_dom_nctrl = cnt;
341 }
342 return dom;
343 }
344
345 static void
clockmod_dom_destroy(struct clockmod_dom * dom)346 clockmod_dom_destroy(struct clockmod_dom *dom)
347 {
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);
351
352 sysctl_ctx_free(&dom->dom_sysctl_ctx);
353 kfree(dom, M_DEVBUF);
354
355 if (TAILQ_EMPTY(&clockmod_dom_list)) {
356 clockmod_dom_nctrl = 0;
357 kfree(clockmod_dom_controls, M_DEVBUF);
358 clockmod_dom_controls = NULL;
359 }
360 }
361
362 static int
clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS)363 clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS)
364 {
365 struct clockmod_dom *dom = arg1;
366 struct clockmod_softc *sc;
367 int loop, error;
368
369 lwkt_serialize_enter(&clockmod_dom_slize);
370
371 loop = error = 0;
372 TAILQ_FOREACH(sc, &dom->dom_list, sc_link) {
373 char buf[16];
374
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));
380 }
381 ++loop;
382 }
383
384 lwkt_serialize_exit(&clockmod_dom_slize);
385 return error;
386 }
387
388 static int
clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS)389 clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS)
390 {
391 struct clockmod_dom *dom = arg1;
392 int loop, error, i;
393
394 lwkt_serialize_enter(&clockmod_dom_slize);
395
396 if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
397 error = SYSCTL_OUT(req, " ", 1);
398 goto done;
399 }
400
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));
409 }
410 ++loop;
411 }
412 done:
413 lwkt_serialize_exit(&clockmod_dom_slize);
414 return error;
415 }
416
417 static int
clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS)418 clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS)
419 {
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;
425
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);
431
432 error = sysctl_handle_string(oidp, duty, sizeof(duty), req);
433 if (error != 0 || req->newptr == NULL)
434 return error;
435
436 lwkt_serialize_enter(&clockmod_dom_slize);
437
438 if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
439 error = EOPNOTSUPP;
440 goto back;
441 }
442
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;
447 }
448 if (i == clockmod_dom_nctrl) {
449 error = EINVAL;
450 goto back;
451 }
452 dom->dom_select = i;
453
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;
459 }
460
461 static void
clockmod_select_handler(struct cpuhelper_msg * msg)462 clockmod_select_handler(struct cpuhelper_msg *msg)
463 {
464 uint64_t ctl_value = *((const uint64_t *)msg->ch_cbarg);
465
466 #if 0
467 if (bootverbose) {
468 kprintf("cpu%d: clockmod 0x%04jx\n", mycpuid,
469 (uintmax_t)ctl_value);
470 }
471 #endif
472 wrmsr(MSR_THERM_CONTROL, ctl_value);
473 cpuhelper_replymsg(msg, 0);
474 }
475
476 static int
clockmod_select(const struct clockmod_softc * sc,const struct clockmod_dom_ctrl * ctrl)477 clockmod_select(const struct clockmod_softc *sc,
478 const struct clockmod_dom_ctrl *ctrl)
479 {
480 struct cpuhelper_msg msg;
481
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));
486 }
487
488 static boolean_t
clockmod_errata_duty(int duty)489 clockmod_errata_duty(int duty)
490 {
491 uint32_t model, stepping;
492
493 /*
494 * This is obtained from the original p4tcc code.
495 *
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.
505 */
506 if (CPUID_TO_FAMILY(cpu_id) != 0xf)
507 return FALSE;
508
509 model = CPUID_TO_MODEL(cpu_id);
510 stepping = cpu_id & 0xf;
511
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;
521 }
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;
533 }
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;
542 }
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;
551 }
552 }
553 return FALSE;
554 }
555