1 /*-
2 * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.Org>
3 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 /*
28 * Generic DT based cpufreq driver
29 */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/bus.h>
34 #include <sys/rman.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/cpu.h>
38 #include <sys/cpuset.h>
39 #include <sys/smp.h>
40
41 #include <dev/ofw/ofw_bus.h>
42 #include <dev/ofw/ofw_bus_subr.h>
43
44 #include <dev/clk/clk.h>
45 #include <dev/regulator/regulator.h>
46
47 #include "cpufreq_if.h"
48
49 #if 0
50 #define DPRINTF(dev, msg...) device_printf(dev, "cpufreq_dt: " msg);
51 #else
52 #define DPRINTF(dev, msg...)
53 #endif
54
55 enum opp_version {
56 OPP_V1 = 1,
57 OPP_V2,
58 };
59
60 struct cpufreq_dt_opp {
61 uint64_t freq;
62 uint32_t uvolt_target;
63 uint32_t uvolt_min;
64 uint32_t uvolt_max;
65 uint32_t uamps;
66 uint32_t clk_latency;
67 bool turbo_mode;
68 bool opp_suspend;
69 };
70
71 #define CPUFREQ_DT_HAVE_REGULATOR(sc) ((sc)->reg != NULL)
72
73 struct cpufreq_dt_softc {
74 device_t dev;
75 clk_t clk;
76 regulator_t reg;
77
78 struct cpufreq_dt_opp *opp;
79 ssize_t nopp;
80
81 int cpu;
82 cpuset_t cpus;
83 };
84
85 static void
cpufreq_dt_notify(device_t dev,uint64_t freq)86 cpufreq_dt_notify(device_t dev, uint64_t freq)
87 {
88 struct cpufreq_dt_softc *sc;
89 struct pcpu *pc;
90 int cpu;
91
92 sc = device_get_softc(dev);
93
94 CPU_FOREACH(cpu) {
95 if (CPU_ISSET(cpu, &sc->cpus)) {
96 pc = pcpu_find(cpu);
97 pc->pc_clock = freq;
98 }
99 }
100 }
101
102 static const struct cpufreq_dt_opp *
cpufreq_dt_find_opp(device_t dev,uint64_t freq)103 cpufreq_dt_find_opp(device_t dev, uint64_t freq)
104 {
105 struct cpufreq_dt_softc *sc;
106 uint64_t diff, best_diff;
107 ssize_t n, best_n;
108
109 sc = device_get_softc(dev);
110
111 diff = 0;
112 best_diff = ~0;
113 DPRINTF(dev, "Looking for freq %ju\n", freq);
114 for (n = 0; n < sc->nopp; n++) {
115 diff = abs64((int64_t)sc->opp[n].freq - (int64_t)freq);
116 DPRINTF(dev, "Testing %ju, diff is %ju\n", sc->opp[n].freq, diff);
117 if (diff < best_diff) {
118 best_diff = diff;
119 best_n = n;
120 DPRINTF(dev, "%ju is best for now\n", sc->opp[n].freq);
121 }
122 }
123
124 DPRINTF(dev, "Will use %ju\n", sc->opp[best_n].freq);
125 return (&sc->opp[best_n]);
126 }
127
128 static void
cpufreq_dt_opp_to_setting(device_t dev,const struct cpufreq_dt_opp * opp,struct cf_setting * set)129 cpufreq_dt_opp_to_setting(device_t dev, const struct cpufreq_dt_opp *opp,
130 struct cf_setting *set)
131 {
132
133 memset(set, 0, sizeof(*set));
134 set->freq = opp->freq / 1000000;
135 set->volts = opp->uvolt_target / 1000;
136 set->power = CPUFREQ_VAL_UNKNOWN;
137 set->lat = opp->clk_latency;
138 set->dev = dev;
139 }
140
141 static int
cpufreq_dt_get(device_t dev,struct cf_setting * set)142 cpufreq_dt_get(device_t dev, struct cf_setting *set)
143 {
144 struct cpufreq_dt_softc *sc;
145 const struct cpufreq_dt_opp *opp;
146 uint64_t freq;
147
148 sc = device_get_softc(dev);
149
150 DPRINTF(dev, "cpufreq_dt_get\n");
151 if (clk_get_freq(sc->clk, &freq) != 0)
152 return (ENXIO);
153
154 opp = cpufreq_dt_find_opp(dev, freq);
155 if (opp == NULL) {
156 device_printf(dev, "Can't find the current freq in opp\n");
157 return (ENOENT);
158 }
159
160 cpufreq_dt_opp_to_setting(dev, opp, set);
161
162 DPRINTF(dev, "Current freq %dMhz\n", set->freq);
163 return (0);
164 }
165
166 static int
cpufreq_dt_set(device_t dev,const struct cf_setting * set)167 cpufreq_dt_set(device_t dev, const struct cf_setting *set)
168 {
169 struct cpufreq_dt_softc *sc;
170 const struct cpufreq_dt_opp *opp, *copp;
171 uint64_t freq;
172 int uvolt, error;
173
174 sc = device_get_softc(dev);
175
176 DPRINTF(dev, "Working on cpu %d\n", sc->cpu);
177 DPRINTF(dev, "We have %d cpu on this dev\n", CPU_COUNT(&sc->cpus));
178 if (!CPU_ISSET(sc->cpu, &sc->cpus)) {
179 DPRINTF(dev, "Not for this CPU\n");
180 return (0);
181 }
182
183 if (clk_get_freq(sc->clk, &freq) != 0) {
184 device_printf(dev, "Can't get current clk freq\n");
185 return (ENXIO);
186 }
187
188 /*
189 * Only do the regulator work if it's required.
190 */
191 if (CPUFREQ_DT_HAVE_REGULATOR(sc)) {
192 /* Try to get current valtage by using regulator first. */
193 error = regulator_get_voltage(sc->reg, &uvolt);
194 if (error != 0) {
195 /*
196 * Try oppoints table as backup way. However,
197 * this is insufficient because the actual processor
198 * frequency may not be in the table. PLL frequency
199 * granularity can be different that granularity of
200 * oppoint table.
201 */
202 copp = cpufreq_dt_find_opp(sc->dev, freq);
203 if (copp == NULL) {
204 device_printf(dev,
205 "Can't find the current freq in opp\n");
206 return (ENOENT);
207 }
208 uvolt = copp->uvolt_target;
209 }
210 } else
211 uvolt = 0;
212
213 opp = cpufreq_dt_find_opp(sc->dev, set->freq * 1000000);
214 if (opp == NULL) {
215 device_printf(dev, "Couldn't find an opp for this freq\n");
216 return (EINVAL);
217 }
218 DPRINTF(sc->dev, "Current freq %ju, uvolt: %d\n", freq, uvolt);
219 DPRINTF(sc->dev, "Target freq %ju, , uvolt: %d\n",
220 opp->freq, opp->uvolt_target);
221
222 if (CPUFREQ_DT_HAVE_REGULATOR(sc) && (uvolt < opp->uvolt_target)) {
223 DPRINTF(dev, "Changing regulator from %u to %u\n",
224 uvolt, opp->uvolt_target);
225 error = regulator_set_voltage(sc->reg,
226 opp->uvolt_min,
227 opp->uvolt_max);
228 if (error != 0) {
229 DPRINTF(dev, "Failed, backout\n");
230 return (ENXIO);
231 }
232 }
233
234 DPRINTF(dev, "Setting clk to %ju\n", opp->freq);
235 error = clk_set_freq(sc->clk, opp->freq, CLK_SET_ROUND_DOWN);
236 if (error != 0) {
237 DPRINTF(dev, "Failed, backout\n");
238 /* Restore previous voltage (best effort) */
239 if (CPUFREQ_DT_HAVE_REGULATOR(sc))
240 error = regulator_set_voltage(sc->reg,
241 copp->uvolt_min,
242 copp->uvolt_max);
243 return (ENXIO);
244 }
245
246 if (CPUFREQ_DT_HAVE_REGULATOR(sc) && (uvolt > opp->uvolt_target)) {
247 DPRINTF(dev, "Changing regulator from %u to %u\n",
248 uvolt, opp->uvolt_target);
249 error = regulator_set_voltage(sc->reg,
250 opp->uvolt_min,
251 opp->uvolt_max);
252 if (error != 0) {
253 DPRINTF(dev, "Failed to switch regulator to %d\n",
254 opp->uvolt_target);
255 /* Restore previous CPU frequency (best effort) */
256 (void)clk_set_freq(sc->clk, copp->freq, 0);
257 return (ENXIO);
258 }
259 }
260
261 if (clk_get_freq(sc->clk, &freq) == 0)
262 cpufreq_dt_notify(dev, freq);
263
264 return (0);
265 }
266
267 static int
cpufreq_dt_type(device_t dev,int * type)268 cpufreq_dt_type(device_t dev, int *type)
269 {
270 if (type == NULL)
271 return (EINVAL);
272
273 *type = CPUFREQ_TYPE_ABSOLUTE;
274 return (0);
275 }
276
277 static int
cpufreq_dt_settings(device_t dev,struct cf_setting * sets,int * count)278 cpufreq_dt_settings(device_t dev, struct cf_setting *sets, int *count)
279 {
280 struct cpufreq_dt_softc *sc;
281 ssize_t n;
282
283 DPRINTF(dev, "cpufreq_dt_settings\n");
284 if (sets == NULL || count == NULL)
285 return (EINVAL);
286
287 sc = device_get_softc(dev);
288
289 if (*count < sc->nopp) {
290 *count = (int)sc->nopp;
291 return (E2BIG);
292 }
293
294 for (n = 0; n < sc->nopp; n++)
295 cpufreq_dt_opp_to_setting(dev, &sc->opp[n], &sets[n]);
296
297 *count = (int)sc->nopp;
298
299 return (0);
300 }
301
302 static void
cpufreq_dt_identify(driver_t * driver,device_t parent)303 cpufreq_dt_identify(driver_t *driver, device_t parent)
304 {
305 phandle_t node;
306
307 /* Properties must be listed under node /cpus/cpu@0 */
308 node = ofw_bus_get_node(parent);
309
310 /* The cpu@0 node must have the following properties */
311 if (!OF_hasprop(node, "clocks"))
312 return;
313
314 if (!OF_hasprop(node, "operating-points") &&
315 !OF_hasprop(node, "operating-points-v2"))
316 return;
317
318 if (device_find_child(parent, "cpufreq_dt", -1) != NULL)
319 return;
320
321 if (BUS_ADD_CHILD(parent, 0, "cpufreq_dt", device_get_unit(parent))
322 == NULL)
323 device_printf(parent, "add cpufreq_dt child failed\n");
324 }
325
326 static int
cpufreq_dt_probe(device_t dev)327 cpufreq_dt_probe(device_t dev)
328 {
329 phandle_t node;
330
331 node = ofw_bus_get_node(device_get_parent(dev));
332
333 /*
334 * Note - supply isn't required here for probe; we'll check
335 * it out in more detail during attach.
336 */
337 if (!OF_hasprop(node, "clocks"))
338 return (ENXIO);
339
340 if (!OF_hasprop(node, "operating-points") &&
341 !OF_hasprop(node, "operating-points-v2"))
342 return (ENXIO);
343
344 device_set_desc(dev, "Generic cpufreq driver");
345 return (BUS_PROBE_GENERIC);
346 }
347
348 static int
cpufreq_dt_oppv1_parse(struct cpufreq_dt_softc * sc,phandle_t node)349 cpufreq_dt_oppv1_parse(struct cpufreq_dt_softc *sc, phandle_t node)
350 {
351 uint32_t *opp, lat;
352 ssize_t n;
353
354 sc->nopp = OF_getencprop_alloc_multi(node, "operating-points",
355 sizeof(uint32_t) * 2, (void **)&opp);
356 if (sc->nopp == -1)
357 return (ENXIO);
358
359 if (OF_getencprop(node, "clock-latency", &lat, sizeof(lat)) == -1)
360 lat = CPUFREQ_VAL_UNKNOWN;
361
362 sc->opp = malloc(sizeof(*sc->opp) * sc->nopp, M_DEVBUF, M_WAITOK);
363
364 for (n = 0; n < sc->nopp; n++) {
365 sc->opp[n].freq = opp[n * 2 + 0] * 1000;
366 sc->opp[n].uvolt_min = opp[n * 2 + 1];
367 sc->opp[n].uvolt_max = sc->opp[n].uvolt_min;
368 sc->opp[n].uvolt_target = sc->opp[n].uvolt_min;
369 sc->opp[n].clk_latency = lat;
370
371 if (bootverbose)
372 device_printf(sc->dev, "%ju.%03ju MHz, %u uV\n",
373 sc->opp[n].freq / 1000000,
374 sc->opp[n].freq % 1000000,
375 sc->opp[n].uvolt_target);
376 }
377 free(opp, M_OFWPROP);
378
379 return (0);
380 }
381
382 static int
cpufreq_dt_oppv2_parse(struct cpufreq_dt_softc * sc,phandle_t node)383 cpufreq_dt_oppv2_parse(struct cpufreq_dt_softc *sc, phandle_t node)
384 {
385 phandle_t opp, opp_table, opp_xref;
386 pcell_t cell[2];
387 uint32_t *volts, lat;
388 int nvolt, i;
389
390 /*
391 * operating-points-v2 does not require the voltage entries
392 * and a regulator. So, it's OK if they're not there.
393 */
394 if (OF_getencprop(node, "operating-points-v2", &opp_xref,
395 sizeof(opp_xref)) == -1) {
396 device_printf(sc->dev, "Cannot get xref to oppv2 table\n");
397 return (ENXIO);
398 }
399
400 opp_table = OF_node_from_xref(opp_xref);
401 if (opp_table == opp_xref)
402 return (ENXIO);
403
404 if (!OF_hasprop(opp_table, "opp-shared")) {
405 device_printf(sc->dev, "Only opp-shared is supported\n");
406 return (ENXIO);
407 }
408
409 for (opp = OF_child(opp_table); opp > 0; opp = OF_peer(opp))
410 sc->nopp += 1;
411
412 sc->opp = malloc(sizeof(*sc->opp) * sc->nopp, M_DEVBUF, M_WAITOK);
413
414 for (i = 0, opp_table = OF_child(opp_table); opp_table > 0;
415 opp_table = OF_peer(opp_table), i++) {
416 /* opp-hz is a required property */
417 if (OF_getencprop(opp_table, "opp-hz", cell,
418 sizeof(cell)) == -1)
419 continue;
420
421 sc->opp[i].freq = cell[0];
422 sc->opp[i].freq <<= 32;
423 sc->opp[i].freq |= cell[1];
424
425 if (OF_getencprop(opp_table, "clock-latency", &lat,
426 sizeof(lat)) == -1)
427 sc->opp[i].clk_latency = CPUFREQ_VAL_UNKNOWN;
428 else
429 sc->opp[i].clk_latency = (int)lat;
430
431 if (OF_hasprop(opp_table, "turbo-mode"))
432 sc->opp[i].turbo_mode = true;
433 if (OF_hasprop(opp_table, "opp-suspend"))
434 sc->opp[i].opp_suspend = true;
435
436 if (CPUFREQ_DT_HAVE_REGULATOR(sc)) {
437 nvolt = OF_getencprop_alloc_multi(opp_table,
438 "opp-microvolt", sizeof(*volts), (void **)&volts);
439 if (nvolt == 1) {
440 sc->opp[i].uvolt_target = volts[0];
441 sc->opp[i].uvolt_min = volts[0];
442 sc->opp[i].uvolt_max = volts[0];
443 } else if (nvolt == 3) {
444 sc->opp[i].uvolt_target = volts[0];
445 sc->opp[i].uvolt_min = volts[1];
446 sc->opp[i].uvolt_max = volts[2];
447 } else {
448 device_printf(sc->dev,
449 "Wrong count of opp-microvolt property\n");
450 OF_prop_free(volts);
451 free(sc->opp, M_DEVBUF);
452 return (ENXIO);
453 }
454 OF_prop_free(volts);
455 } else {
456 /* No regulator required; don't add anything */
457 sc->opp[i].uvolt_target = 0;
458 sc->opp[i].uvolt_min = 0;
459 sc->opp[i].uvolt_max = 0;
460 }
461
462 if (bootverbose)
463 device_printf(sc->dev, "%ju.%03ju Mhz (%u uV)\n",
464 sc->opp[i].freq / 1000000,
465 sc->opp[i].freq % 1000000,
466 sc->opp[i].uvolt_target);
467 }
468 return (0);
469 }
470
471 static int
cpufreq_dt_attach(device_t dev)472 cpufreq_dt_attach(device_t dev)
473 {
474 struct cpufreq_dt_softc *sc;
475 phandle_t node;
476 phandle_t cnode, opp, copp;
477 int cpu;
478 uint64_t freq;
479 int rv = 0;
480 char device_type[16];
481 enum opp_version version;
482
483 sc = device_get_softc(dev);
484 sc->dev = dev;
485 node = ofw_bus_get_node(device_get_parent(dev));
486 sc->cpu = device_get_unit(device_get_parent(dev));
487 sc->reg = NULL;
488
489 DPRINTF(dev, "cpu=%d\n", sc->cpu);
490 if (sc->cpu >= mp_ncpus) {
491 device_printf(dev, "Not attaching as cpu is not present\n");
492 rv = ENXIO;
493 goto error;
494 }
495
496 /*
497 * Cache if we have the regulator supply but don't error out
498 * quite yet. If it's operating-points-v2 then regulator
499 * and voltage entries are optional.
500 */
501 if (regulator_get_by_ofw_property(dev, node, "cpu-supply",
502 &sc->reg) == 0)
503 device_printf(dev, "Found cpu-supply\n");
504 else if (regulator_get_by_ofw_property(dev, node, "cpu0-supply",
505 &sc->reg) == 0)
506 device_printf(dev, "Found cpu0-supply\n");
507
508 /*
509 * Determine which operating mode we're in. Error out if we expect
510 * a regulator but we're not getting it.
511 */
512 if (OF_hasprop(node, "operating-points"))
513 version = OPP_V1;
514 else if (OF_hasprop(node, "operating-points-v2"))
515 version = OPP_V2;
516 else {
517 device_printf(dev,
518 "didn't find a valid operating-points or v2 node\n");
519 rv = ENXIO;
520 goto error;
521 }
522
523 /*
524 * Now, we only enforce needing a regulator for v1.
525 */
526 if ((version == OPP_V1) && !CPUFREQ_DT_HAVE_REGULATOR(sc)) {
527 device_printf(dev, "no regulator for %s\n",
528 ofw_bus_get_name(device_get_parent(dev)));
529 rv = ENXIO;
530 goto error;
531 }
532
533 if (clk_get_by_ofw_index(dev, node, 0, &sc->clk) != 0) {
534 device_printf(dev, "no clock for %s\n",
535 ofw_bus_get_name(device_get_parent(dev)));
536 rv = ENXIO;
537 goto error;
538 }
539
540 if (version == OPP_V1) {
541 rv = cpufreq_dt_oppv1_parse(sc, node);
542 if (rv != 0) {
543 device_printf(dev, "Failed to parse opp-v1 table\n");
544 goto error;
545 }
546 OF_getencprop(node, "operating-points", &opp,
547 sizeof(opp));
548 } else if (version == OPP_V2) {
549 rv = cpufreq_dt_oppv2_parse(sc, node);
550 if (rv != 0) {
551 device_printf(dev, "Failed to parse opp-v2 table\n");
552 goto error;
553 }
554 OF_getencprop(node, "operating-points-v2", &opp,
555 sizeof(opp));
556 } else {
557 device_printf(dev, "operating points version is incorrect\n");
558 goto error;
559 }
560
561 /*
562 * Find all CPUs that share the same opp table
563 */
564 CPU_ZERO(&sc->cpus);
565 cnode = OF_parent(node);
566 for (cpu = 0, cnode = OF_child(cnode); cnode > 0; cnode = OF_peer(cnode)) {
567 if (OF_getprop(cnode, "device_type", device_type, sizeof(device_type)) <= 0)
568 continue;
569 if (strcmp(device_type, "cpu") != 0)
570 continue;
571 if (cpu == sc->cpu) {
572 DPRINTF(dev, "Skipping our cpu\n");
573 CPU_SET(cpu, &sc->cpus);
574 cpu++;
575 continue;
576 }
577 DPRINTF(dev, "Testing CPU %d\n", cpu);
578 copp = -1;
579 if (version == OPP_V1)
580 OF_getencprop(cnode, "operating-points", &copp,
581 sizeof(copp));
582 else if (version == OPP_V2)
583 OF_getencprop(cnode, "operating-points-v2",
584 &copp, sizeof(copp));
585 if (opp == copp) {
586 DPRINTF(dev, "CPU %d is using the same opp as this one (%d)\n",
587 cpu, sc->cpu);
588 CPU_SET(cpu, &sc->cpus);
589 }
590 cpu++;
591 }
592
593 if (clk_get_freq(sc->clk, &freq) == 0)
594 cpufreq_dt_notify(dev, freq);
595
596 cpufreq_register(dev);
597
598 return (0);
599 error:
600 if (CPUFREQ_DT_HAVE_REGULATOR(sc))
601 regulator_release(sc->reg);
602 return (rv);
603 }
604
605 static device_method_t cpufreq_dt_methods[] = {
606 /* Device interface */
607 DEVMETHOD(device_identify, cpufreq_dt_identify),
608 DEVMETHOD(device_probe, cpufreq_dt_probe),
609 DEVMETHOD(device_attach, cpufreq_dt_attach),
610
611 /* cpufreq interface */
612 DEVMETHOD(cpufreq_drv_get, cpufreq_dt_get),
613 DEVMETHOD(cpufreq_drv_set, cpufreq_dt_set),
614 DEVMETHOD(cpufreq_drv_type, cpufreq_dt_type),
615 DEVMETHOD(cpufreq_drv_settings, cpufreq_dt_settings),
616
617 DEVMETHOD_END
618 };
619
620 static driver_t cpufreq_dt_driver = {
621 "cpufreq_dt",
622 cpufreq_dt_methods,
623 sizeof(struct cpufreq_dt_softc),
624 };
625
626 DRIVER_MODULE(cpufreq_dt, cpu, cpufreq_dt_driver, 0, 0);
627 MODULE_VERSION(cpufreq_dt, 1);
628