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
55enum opp_version {
56	OPP_V1 = 1,
57	OPP_V2,
58};
59
60struct 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
73struct 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
85static void
86cpufreq_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
102static const struct cpufreq_dt_opp *
103cpufreq_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
128static void
129cpufreq_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
141static int
142cpufreq_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
166static int
167cpufreq_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
267static int
268cpufreq_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
277static int
278cpufreq_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
302static void
303cpufreq_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
326static int
327cpufreq_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
348static int
349cpufreq_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
382static int
383cpufreq_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
471static int
472cpufreq_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);
599error:
600	if (CPUFREQ_DT_HAVE_REGULATOR(sc))
601		regulator_release(sc->reg);
602	return (rv);
603}
604
605static 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
620static driver_t cpufreq_dt_driver = {
621	"cpufreq_dt",
622	cpufreq_dt_methods,
623	sizeof(struct cpufreq_dt_softc),
624};
625
626DRIVER_MODULE(cpufreq_dt, cpu, cpufreq_dt_driver, 0, 0);
627MODULE_VERSION(cpufreq_dt, 1);
628