Merge remote-tracking branch 'origin/vendor/XZ'
[dragonfly.git] / sys / dev / powermng / clockmod / clockmod.c
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/module.h>
42 #include <sys/queue.h>
43 #include <sys/serialize.h>
44 #include <sys/sysctl.h>
45 #include <sys/systm.h>
46
47 #include <machine/specialreg.h>
48 #include <machine/cpufunc.h>
49 #include <machine/cputypes.h>
50 #include <machine/md_var.h>
51
52 struct clockmod_dom;
53
54 struct clockmod_softc {
55         TAILQ_ENTRY(clockmod_softc) sc_link;
56         struct clockmod_dom     *sc_dom;
57         int                     sc_cpuid;
58 };
59
60 struct clockmod_dom {
61         TAILQ_ENTRY(clockmod_dom) dom_link;
62         TAILQ_HEAD(, clockmod_softc) dom_list;
63         struct sysctl_ctx_list  dom_sysctl_ctx;
64         struct sysctl_oid       *dom_sysctl_tree;
65         cpumask_t               dom_cpumask;
66         char                    dom_name[16];
67         int                     dom_select;
68         uint32_t                dom_flags;
69 };
70
71 #define CLOCKMOD_DOM_FLAG_ACTIVE        0x1
72
73 struct clockmod_dom_ctrl {
74         char                    ctl_name[8];
75         uint64_t                ctl_value;
76 };
77
78 static int      clockmod_dom_attach(struct clockmod_softc *);
79 static void     clockmod_dom_detach(struct clockmod_softc *);
80 static struct clockmod_dom *clockmod_dom_find(cpumask_t);
81 static struct clockmod_dom *clockmod_dom_create(cpumask_t);
82 static void     clockmod_dom_destroy(struct clockmod_dom *);
83
84 static int      clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS);
85 static int      clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS);
86 static int      clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS);
87
88 static void     clockmod_identify(driver_t *, device_t);
89 static int      clockmod_probe(device_t);
90 static int      clockmod_attach(device_t);
91 static int      clockmod_detach(device_t);
92
93 static void     clockmod_select_handler(struct cpuhelper_msg *);
94 static int      clockmod_select(const struct clockmod_softc *,
95                     const struct clockmod_dom_ctrl *);
96
97 static boolean_t clockmod_errata_duty(int);
98
99 static struct lwkt_serialize clockmod_dom_slize = LWKT_SERIALIZE_INITIALIZER;
100 static int      clockmod_dom_id;
101 static TAILQ_HEAD(, clockmod_dom) clockmod_dom_list =
102     TAILQ_HEAD_INITIALIZER(clockmod_dom_list);
103 static int      clockmod_dom_nctrl;
104 static struct clockmod_dom_ctrl *clockmod_dom_controls;
105
106 static device_method_t clockmod_methods[] = {
107         /* Device interface */
108         DEVMETHOD(device_identify,      clockmod_identify),
109         DEVMETHOD(device_probe,         clockmod_probe),
110         DEVMETHOD(device_attach,        clockmod_attach),
111         DEVMETHOD(device_detach,        clockmod_detach),
112
113         DEVMETHOD_END
114 };
115
116 static driver_t clockmod_driver = {
117         "clockmod",
118         clockmod_methods,
119         sizeof(struct clockmod_softc),
120 };
121
122 static devclass_t clockmod_devclass;
123 DRIVER_MODULE(clockmod, cpu, clockmod_driver, clockmod_devclass, NULL, NULL);
124
125 static void
126 clockmod_identify(driver_t *driver, device_t parent)
127 {
128         device_t child;
129
130         if (device_find_child(parent, "clockmod", -1) != NULL)
131                 return;
132
133         if (cpu_vendor_id != CPU_VENDOR_INTEL)
134                 return;
135
136         if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != (CPUID_ACPI | CPUID_TM))
137                 return;
138
139         child = device_add_child(parent, "clockmod", device_get_unit(parent));
140         if (child == NULL)
141                 device_printf(parent, "add clockmod failed\n");
142 }
143
144 static int
145 clockmod_probe(device_t dev)
146 {
147         device_set_desc(dev, "CPU clock modulation");
148         return 0;
149 }
150
151 static int
152 clockmod_attach(device_t dev)
153 {
154         struct clockmod_softc *sc = device_get_softc(dev);
155         int error;
156
157         sc->sc_cpuid = device_get_unit(dev);
158
159         error = clockmod_dom_attach(sc);
160         if (error) {
161                 device_printf(dev, "domain attach failed\n");
162                 return error;
163         }
164
165         return 0;
166 }
167
168 static int
169 clockmod_detach(device_t dev)
170 {
171         clockmod_dom_detach(device_get_softc(dev));
172         return 0;
173 }
174
175 static int
176 clockmod_dom_attach(struct clockmod_softc *sc)
177 {
178         struct clockmod_softc *sc1;
179         struct clockmod_dom *dom;
180         cpumask_t mask, found_mask;
181         int error = 0;
182
183         CPUMASK_ASSZERO(found_mask);
184
185         mask = get_cpumask_from_level(sc->sc_cpuid, CORE_LEVEL);
186         if (CPUMASK_TESTZERO(mask))
187                 CPUMASK_ASSBIT(mask, sc->sc_cpuid);
188
189         lwkt_serialize_enter(&clockmod_dom_slize);
190
191         dom = clockmod_dom_find(mask);
192         if (dom == NULL) {
193                 dom = clockmod_dom_create(mask);
194                 if (dom == NULL) {
195                         error = ENOMEM;
196                         goto back;
197                 }
198         }
199
200         sc->sc_dom = dom;
201         TAILQ_INSERT_TAIL(&dom->dom_list, sc, sc_link);
202
203         TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
204                 CPUMASK_ORBIT(found_mask, sc1->sc_cpuid);
205
206         if (CPUMASK_CMPMASKEQ(found_mask, dom->dom_cpumask)) {
207                 /* All cpus in this domain is found */
208                 dom->dom_flags |= CLOCKMOD_DOM_FLAG_ACTIVE;
209         }
210 back:
211         lwkt_serialize_exit(&clockmod_dom_slize);
212         return error;
213 }
214
215 static void
216 clockmod_dom_detach(struct clockmod_softc *sc)
217 {
218         struct clockmod_dom *dom;
219
220         lwkt_serialize_enter(&clockmod_dom_slize);
221
222         dom = sc->sc_dom;
223         sc->sc_dom = NULL;
224
225         if (dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) {
226                 struct clockmod_softc *sc1;
227
228                 /* Raise to 100% */
229                 TAILQ_FOREACH(sc1, &dom->dom_list, sc_link)
230                         clockmod_select(sc1, &clockmod_dom_controls[0]);
231         }
232
233         /* One cpu is leaving; domain is no longer active */
234         dom->dom_flags &= ~CLOCKMOD_DOM_FLAG_ACTIVE;
235
236         TAILQ_REMOVE(&dom->dom_list, sc, sc_link);
237         if (TAILQ_EMPTY(&dom->dom_list))
238                 clockmod_dom_destroy(dom);
239
240         lwkt_serialize_exit(&clockmod_dom_slize);
241 }
242
243 static struct clockmod_dom *
244 clockmod_dom_find(cpumask_t mask)
245 {
246         struct clockmod_dom *dom;
247
248         TAILQ_FOREACH(dom, &clockmod_dom_list, dom_link) {
249                 if (CPUMASK_CMPMASKEQ(dom->dom_cpumask, mask))
250                         return dom;
251         }
252         return NULL;
253 }
254
255 static struct clockmod_dom *
256 clockmod_dom_create(cpumask_t mask)
257 {
258         struct clockmod_dom *dom;
259         int id;
260
261         id = clockmod_dom_id++;
262         dom = kmalloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO);
263
264         TAILQ_INIT(&dom->dom_list);
265         dom->dom_cpumask = mask;
266         ksnprintf(dom->dom_name, sizeof(dom->dom_name), "clockmod_dom%d", id);
267
268         sysctl_ctx_init(&dom->dom_sysctl_ctx);
269         dom->dom_sysctl_tree = SYSCTL_ADD_NODE(&dom->dom_sysctl_ctx,
270             SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, dom->dom_name,
271             CTLFLAG_RD, 0, "");
272         if (dom->dom_sysctl_tree == NULL) {
273                 kprintf("%s: can't add sysctl node\n", dom->dom_name);
274                 kfree(dom, M_DEVBUF);
275                 return NULL;
276         }
277
278         SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
279             SYSCTL_CHILDREN(dom->dom_sysctl_tree),
280             OID_AUTO, "members", CTLTYPE_STRING | CTLFLAG_RD,
281             dom, 0, clockmod_dom_sysctl_members, "A", "member cpus");
282
283         SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
284             SYSCTL_CHILDREN(dom->dom_sysctl_tree),
285             OID_AUTO, "available", CTLTYPE_STRING | CTLFLAG_RD,
286             dom, 0, clockmod_dom_sysctl_available, "A",
287             "available duty percent");
288
289         SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx,
290             SYSCTL_CHILDREN(dom->dom_sysctl_tree),
291             OID_AUTO, "select", CTLTYPE_STRING | CTLFLAG_RW,
292             dom, 0, clockmod_dom_sysctl_select, "A", "select duty");
293
294         TAILQ_INSERT_TAIL(&clockmod_dom_list, dom, dom_link);
295
296         if (clockmod_dom_controls == NULL) {
297                 int nctrl, step, i, shift, cnt;
298
299 #ifdef __x86_64__
300                 if (cpu_thermal_feature & CPUID_THERMAL_ECMD)
301                         shift = 0;
302                 else
303 #endif
304                         shift = 1;
305
306                 nctrl = 8 << (1 - shift);
307                 step = 10000 / nctrl;
308
309                 clockmod_dom_controls =
310                     kmalloc(sizeof(struct clockmod_dom_ctrl) * nctrl, M_DEVBUF,
311                     M_WAITOK | M_ZERO);
312
313                 if (bootverbose)
314                         kprintf("clock modulation:\n");
315
316                 cnt = 0;
317                 for (i = 0; i < nctrl; ++i) {
318                         struct clockmod_dom_ctrl *ctrl =
319                             &clockmod_dom_controls[cnt];
320                         int duty;
321
322                         duty = 10000 - (i * step);
323                         if (clockmod_errata_duty(duty))
324                                 continue;
325                         ++cnt;
326
327                         ksnprintf(ctrl->ctl_name, sizeof(ctrl->ctl_name),
328                             "%d.%02d%%", duty / 100, duty % 100);
329                         ctrl->ctl_value = (((nctrl - i) << shift) & 0xf);
330                         if (i != 0)
331                                 ctrl->ctl_value |= 1 << 4;
332
333                         if (bootverbose) {
334                                 kprintf("  0x%04jx %s\n", 
335                                     (uintmax_t)ctrl->ctl_value,
336                                     ctrl->ctl_name);
337                         }
338                 }
339                 clockmod_dom_nctrl = cnt;
340         }
341         return dom;
342 }
343
344 static void
345 clockmod_dom_destroy(struct clockmod_dom *dom)
346 {
347         KASSERT(TAILQ_EMPTY(&dom->dom_list),
348             ("%s: still has member cpus", dom->dom_name));
349         TAILQ_REMOVE(&clockmod_dom_list, dom, dom_link);
350
351         sysctl_ctx_free(&dom->dom_sysctl_ctx);
352         kfree(dom, M_DEVBUF);
353
354         if (TAILQ_EMPTY(&clockmod_dom_list)) {
355                 clockmod_dom_nctrl = 0;
356                 kfree(clockmod_dom_controls, M_DEVBUF);
357                 clockmod_dom_controls = NULL;
358         }
359 }
360
361 static int
362 clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS)
363 {
364         struct clockmod_dom *dom = arg1;
365         struct clockmod_softc *sc;
366         int loop, error;
367
368         lwkt_serialize_enter(&clockmod_dom_slize);
369
370         loop = error = 0;
371         TAILQ_FOREACH(sc, &dom->dom_list, sc_link) {
372                 char buf[16];
373
374                 if (error == 0 && loop)
375                         error = SYSCTL_OUT(req, " ", 1);
376                 if (error == 0) {
377                         ksnprintf(buf, sizeof(buf), "cpu%d", sc->sc_cpuid);
378                         error = SYSCTL_OUT(req, buf, strlen(buf));
379                 }
380                 ++loop;
381         }
382
383         lwkt_serialize_exit(&clockmod_dom_slize);
384         return error;
385 }
386
387 static int
388 clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS)
389 {
390         struct clockmod_dom *dom = arg1;
391         int loop, error, i;
392
393         lwkt_serialize_enter(&clockmod_dom_slize);
394
395         if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
396                 error = SYSCTL_OUT(req, " ", 1);
397                 goto done;
398         }
399
400         loop = error = 0;
401         for (i = 0; i < clockmod_dom_nctrl; ++i) {
402                 if (error == 0 && loop)
403                         error = SYSCTL_OUT(req, " ", 1);
404                 if (error == 0) {
405                         error = SYSCTL_OUT(req,
406                             clockmod_dom_controls[i].ctl_name,
407                             strlen(clockmod_dom_controls[i].ctl_name));
408                 }
409                 ++loop;
410         }
411 done:
412         lwkt_serialize_exit(&clockmod_dom_slize);
413         return error;
414 }
415
416 static int
417 clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS)
418 {
419         struct clockmod_dom *dom = arg1;
420         struct clockmod_softc *sc;
421         const struct clockmod_dom_ctrl *ctrl = NULL;
422         char duty[16];
423         int error, i;
424
425         lwkt_serialize_enter(&clockmod_dom_slize);
426         KKASSERT(dom->dom_select >= 0 && dom->dom_select < clockmod_dom_nctrl);
427         ksnprintf(duty, sizeof(duty), "%s",
428             clockmod_dom_controls[dom->dom_select].ctl_name);
429         lwkt_serialize_exit(&clockmod_dom_slize);
430
431         error = sysctl_handle_string(oidp, duty, sizeof(duty), req);
432         if (error != 0 || req->newptr == NULL)
433                 return error;
434
435         lwkt_serialize_enter(&clockmod_dom_slize);
436
437         if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) {
438                 error = EOPNOTSUPP;
439                 goto back;
440         }
441
442         for (i = 0; i < clockmod_dom_nctrl; ++i) {
443                 ctrl = &clockmod_dom_controls[i];
444                 if (strcmp(duty, ctrl->ctl_name) == 0)
445                         break;
446         }
447         if (i == clockmod_dom_nctrl) {
448                 error = EINVAL;
449                 goto back;
450         }
451         dom->dom_select = i;
452
453         TAILQ_FOREACH(sc, &dom->dom_list, sc_link)
454                 clockmod_select(sc, ctrl);
455 back:
456         lwkt_serialize_exit(&clockmod_dom_slize);
457         return error;
458 }
459
460 static void
461 clockmod_select_handler(struct cpuhelper_msg *msg)
462 {
463         uint64_t ctl_value = *((const uint64_t *)msg->ch_cbarg);
464
465 #if 0
466         if (bootverbose) {
467                 kprintf("cpu%d: clockmod 0x%04jx\n", mycpuid,
468                     (uintmax_t)ctl_value);
469         }
470 #endif
471         wrmsr(MSR_THERM_CONTROL, ctl_value);
472         cpuhelper_replymsg(msg, 0);
473 }
474
475 static int 
476 clockmod_select(const struct clockmod_softc *sc,
477     const struct clockmod_dom_ctrl *ctrl)
478 {
479         struct cpuhelper_msg msg;
480
481         cpuhelper_initmsg(&msg, &curthread->td_msgport,
482             clockmod_select_handler, __DECONST(void *, &ctrl->ctl_value),
483             MSGF_PRIORITY);
484         return (cpuhelper_domsg(&msg, sc->sc_cpuid));
485 }
486
487 static boolean_t
488 clockmod_errata_duty(int duty)
489 {
490         uint32_t model, stepping;
491
492         /*
493          * This is obtained from the original p4tcc code.
494          *
495          * The original errata checking code in p4tcc is obviously wrong.
496          * However, I am no longer being able to find the errata mentioned
497          * in the code.  The guess is that the errata only affects family
498          * 0x0f CPUs, since:
499          * - The errata applies to only to model 0x00, 0x01 and 0x02 in
500          *   the original p4tcc code.
501          * - Software controlled clock modulation has been supported since
502          *   0f_00 and the model of the oldest family 0x06 CPUs supporting
503          *   this feature is 0x09.
504          */
505         if (CPUID_TO_FAMILY(cpu_id) != 0xf)
506                 return FALSE;
507
508         model = CPUID_TO_MODEL(cpu_id);
509         stepping = cpu_id & 0xf;
510
511         if (model == 0x6) {
512                 switch (stepping) {
513                 case 0x2:
514                 case 0x4:
515                 case 0x5:
516                         /* Hang w/ 12.50% and 25.00% */
517                         if (duty == 1250 || duty == 2500)
518                                 return TRUE;
519                         break;
520                 }
521         } else if (model == 0x2) {
522                 switch (stepping) {
523                 case 0x2:
524                 case 0x4:
525                 case 0x5:
526                 case 0x7:
527                 case 0x9:
528                         /* Hang w/ 12.50% */
529                         if (duty == 1250)
530                                 return TRUE;
531                         break;
532                 }
533         } else if (model == 0x1) {
534                 switch (stepping) {
535                 case 0x2:
536                 case 0x3:
537                         /* Hang w/ 12.50% and 25.00% */
538                         if (duty == 1250 || duty == 2500)
539                                 return TRUE;
540                         break;
541                 }
542         } else if (model == 0x0) {
543                 switch (stepping) {
544                 case 0x7:
545                 case 0xa:
546                         /* Hang w/ 12.50% and 25.00% */
547                         if (duty == 1250 || duty == 2500)
548                                 return TRUE;
549                         break;
550                 }
551         }
552         return FALSE;
553 }