xref: /freebsd-src/sys/powerpc/cpufreq/pmufreq.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
14702d987SJustin Hibbits /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
371e3c308SPedro F. Giffuni  *
44702d987SJustin Hibbits  * Copyright (c) 2011 Justin Hibbits
54702d987SJustin Hibbits  * Copyright (c) 2009 Nathan Whitehorn
64702d987SJustin Hibbits  * All rights reserved.
74702d987SJustin Hibbits  *
84702d987SJustin Hibbits  * Redistribution and use in source and binary forms, with or without
94702d987SJustin Hibbits  * modification, are permitted provided that the following conditions
104702d987SJustin Hibbits  * are met:
114702d987SJustin Hibbits  * 1. Redistributions of source code must retain the above copyright
124702d987SJustin Hibbits  *    notice, this list of conditions and the following disclaimer.
134702d987SJustin Hibbits  * 2. Redistributions in binary form must reproduce the above copyright
144702d987SJustin Hibbits  *    notice, this list of conditions and the following disclaimer in the
154702d987SJustin Hibbits  *    documentation and/or other materials provided with the distribution.
164702d987SJustin Hibbits  *
174702d987SJustin Hibbits  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
184702d987SJustin Hibbits  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
194702d987SJustin Hibbits  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
204702d987SJustin Hibbits  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
214702d987SJustin Hibbits  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
224702d987SJustin Hibbits  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
234702d987SJustin Hibbits  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
244702d987SJustin Hibbits  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
254702d987SJustin Hibbits  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
264702d987SJustin Hibbits  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
274702d987SJustin Hibbits  * SUCH DAMAGE.
284702d987SJustin Hibbits  */
294702d987SJustin Hibbits 
304702d987SJustin Hibbits #include <sys/param.h>
314702d987SJustin Hibbits #include <sys/systm.h>
324702d987SJustin Hibbits #include <sys/bus.h>
334702d987SJustin Hibbits #include <sys/cpu.h>
344702d987SJustin Hibbits #include <sys/kernel.h>
354702d987SJustin Hibbits #include <sys/limits.h>
364702d987SJustin Hibbits #include <sys/module.h>
374702d987SJustin Hibbits 
384702d987SJustin Hibbits #include <dev/ofw/ofw_bus.h>
394702d987SJustin Hibbits #include <dev/ofw/openfirm.h>
404702d987SJustin Hibbits 
414702d987SJustin Hibbits #include "cpufreq_if.h"
424702d987SJustin Hibbits #include "powerpc/powermac/pmuvar.h"
434702d987SJustin Hibbits 
444702d987SJustin Hibbits struct pmufreq_softc {
454702d987SJustin Hibbits 	device_t dev;
464702d987SJustin Hibbits 	uint32_t minfreq;
474702d987SJustin Hibbits 	uint32_t maxfreq;
484702d987SJustin Hibbits 	uint32_t curfreq;
494702d987SJustin Hibbits };
504702d987SJustin Hibbits 
514702d987SJustin Hibbits static void	pmufreq_identify(driver_t *driver, device_t parent);
524702d987SJustin Hibbits static int	pmufreq_probe(device_t dev);
534702d987SJustin Hibbits static int	pmufreq_attach(device_t dev);
544702d987SJustin Hibbits static int	pmufreq_settings(device_t dev, struct cf_setting *sets, int *count);
554702d987SJustin Hibbits static int	pmufreq_set(device_t dev, const struct cf_setting *set);
564702d987SJustin Hibbits static int	pmufreq_get(device_t dev, struct cf_setting *set);
574702d987SJustin Hibbits static int	pmufreq_type(device_t dev, int *type);
584702d987SJustin Hibbits 
594702d987SJustin Hibbits static device_method_t pmufreq_methods[] = {
604702d987SJustin Hibbits 	/* Device interface */
614702d987SJustin Hibbits 	DEVMETHOD(device_identify,	pmufreq_identify),
624702d987SJustin Hibbits 	DEVMETHOD(device_probe,		pmufreq_probe),
634702d987SJustin Hibbits 	DEVMETHOD(device_attach,	pmufreq_attach),
644702d987SJustin Hibbits 
654702d987SJustin Hibbits 	/* cpufreq interface */
664702d987SJustin Hibbits 	DEVMETHOD(cpufreq_drv_set,	pmufreq_set),
674702d987SJustin Hibbits 	DEVMETHOD(cpufreq_drv_get,	pmufreq_get),
684702d987SJustin Hibbits 	DEVMETHOD(cpufreq_drv_type,	pmufreq_type),
694702d987SJustin Hibbits 	DEVMETHOD(cpufreq_drv_settings,	pmufreq_settings),
704702d987SJustin Hibbits 	{0, 0}
714702d987SJustin Hibbits };
724702d987SJustin Hibbits 
734702d987SJustin Hibbits static driver_t pmufreq_driver = {
744702d987SJustin Hibbits 	"pmufreq",
754702d987SJustin Hibbits 	pmufreq_methods,
764702d987SJustin Hibbits 	sizeof(struct pmufreq_softc)
774702d987SJustin Hibbits };
784702d987SJustin Hibbits 
79b3407dccSJohn Baldwin DRIVER_MODULE(pmufreq, cpu, pmufreq_driver, 0, 0);
804702d987SJustin Hibbits 
814702d987SJustin Hibbits static void
pmufreq_identify(driver_t * driver,device_t parent)824702d987SJustin Hibbits pmufreq_identify(driver_t *driver, device_t parent)
834702d987SJustin Hibbits {
844702d987SJustin Hibbits 	phandle_t node;
854702d987SJustin Hibbits 	uint32_t min_freq;
864702d987SJustin Hibbits 
874702d987SJustin Hibbits 	node = ofw_bus_get_node(parent);
884702d987SJustin Hibbits 	if (OF_getprop(node, "min-clock-frequency", &min_freq, sizeof(min_freq)) == -1)
894702d987SJustin Hibbits 		return;
904702d987SJustin Hibbits 
914702d987SJustin Hibbits 	/* Make sure we're not being doubly invoked. */
924702d987SJustin Hibbits 	if (device_find_child(parent, "pmufreq", -1) != NULL)
934702d987SJustin Hibbits 		return;
944702d987SJustin Hibbits 
954702d987SJustin Hibbits 	/*
964702d987SJustin Hibbits 	 * We attach a child for every CPU since settings need to
974702d987SJustin Hibbits 	 * be performed on every CPU in the SMP case.
984702d987SJustin Hibbits 	 */
994702d987SJustin Hibbits 	if (BUS_ADD_CHILD(parent, 10, "pmufreq", -1) == NULL)
1004702d987SJustin Hibbits 		device_printf(parent, "add pmufreq child failed\n");
1014702d987SJustin Hibbits }
1024702d987SJustin Hibbits 
1034702d987SJustin Hibbits static int
pmufreq_probe(device_t dev)1044702d987SJustin Hibbits pmufreq_probe(device_t dev)
1054702d987SJustin Hibbits {
1064702d987SJustin Hibbits 	phandle_t node;
10710b31d0bSJustin Hibbits 	uint32_t min_freq;
1084702d987SJustin Hibbits 
1094702d987SJustin Hibbits 	if (resource_disabled("pmufreq", 0))
1104702d987SJustin Hibbits 		return (ENXIO);
1114702d987SJustin Hibbits 
1124702d987SJustin Hibbits 	node = ofw_bus_get_node(device_get_parent(dev));
1134702d987SJustin Hibbits 	/*
1144702d987SJustin Hibbits 	 * A scalable MPC7455 has min-clock-frequency/max-clock-frequency as OFW
1154702d987SJustin Hibbits 	 * properties of the 'cpu' node.
1164702d987SJustin Hibbits 	 */
1174702d987SJustin Hibbits 	if (OF_getprop(node, "min-clock-frequency", &min_freq, sizeof(min_freq)) == -1)
1184702d987SJustin Hibbits 		return (ENXIO);
1194702d987SJustin Hibbits 	device_set_desc(dev, "PMU-based frequency scaling");
1204702d987SJustin Hibbits 	return (0);
1214702d987SJustin Hibbits }
1224702d987SJustin Hibbits 
1234702d987SJustin Hibbits static int
pmufreq_attach(device_t dev)1244702d987SJustin Hibbits pmufreq_attach(device_t dev)
1254702d987SJustin Hibbits {
1264702d987SJustin Hibbits 	struct pmufreq_softc *sc;
1274702d987SJustin Hibbits 	phandle_t node;
1284702d987SJustin Hibbits 
1294702d987SJustin Hibbits 	sc = device_get_softc(dev);
1304702d987SJustin Hibbits 	sc->dev = dev;
1314702d987SJustin Hibbits 
1324702d987SJustin Hibbits 	node = ofw_bus_get_node(device_get_parent(dev));
1334702d987SJustin Hibbits 	OF_getprop(node, "min-clock-frequency", &sc->minfreq, sizeof(sc->minfreq));
1344702d987SJustin Hibbits 	OF_getprop(node, "max-clock-frequency", &sc->maxfreq, sizeof(sc->maxfreq));
1354702d987SJustin Hibbits 	OF_getprop(node, "rounded-clock-frequency", &sc->curfreq, sizeof(sc->curfreq));
1364702d987SJustin Hibbits 	sc->minfreq /= 1000000;
1374702d987SJustin Hibbits 	sc->maxfreq /= 1000000;
1384702d987SJustin Hibbits 	sc->curfreq /= 1000000;
1394702d987SJustin Hibbits 
1404702d987SJustin Hibbits 	cpufreq_register(dev);
1414702d987SJustin Hibbits 	return (0);
1424702d987SJustin Hibbits }
1434702d987SJustin Hibbits 
1444702d987SJustin Hibbits static int
pmufreq_settings(device_t dev,struct cf_setting * sets,int * count)1454702d987SJustin Hibbits pmufreq_settings(device_t dev, struct cf_setting *sets, int *count)
1464702d987SJustin Hibbits {
1474702d987SJustin Hibbits 	struct pmufreq_softc *sc;
1484702d987SJustin Hibbits 
1494702d987SJustin Hibbits 	sc = device_get_softc(dev);
1504702d987SJustin Hibbits 	if (sets == NULL || count == NULL)
1514702d987SJustin Hibbits 		return (EINVAL);
1524702d987SJustin Hibbits 	if (*count < 2)
1534702d987SJustin Hibbits 		return (E2BIG);
1544702d987SJustin Hibbits 
1554702d987SJustin Hibbits 	/* Return a list of valid settings for this driver. */
1564702d987SJustin Hibbits 	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * 2);
1574702d987SJustin Hibbits 
1584702d987SJustin Hibbits 	sets[0].freq = sc->maxfreq; sets[0].dev = dev;
1594702d987SJustin Hibbits 	sets[1].freq = sc->minfreq; sets[1].dev = dev;
1604702d987SJustin Hibbits 	/* Set high latency for CPU frequency changes, it's a tedious process. */
1614702d987SJustin Hibbits 	sets[0].lat = INT_MAX;
1624702d987SJustin Hibbits 	sets[1].lat = INT_MAX;
1634702d987SJustin Hibbits 	*count = 2;
1644702d987SJustin Hibbits 
1654702d987SJustin Hibbits 	return (0);
1664702d987SJustin Hibbits }
1674702d987SJustin Hibbits 
1684702d987SJustin Hibbits static int
pmufreq_set(device_t dev,const struct cf_setting * set)1694702d987SJustin Hibbits pmufreq_set(device_t dev, const struct cf_setting *set)
1704702d987SJustin Hibbits {
1714702d987SJustin Hibbits 	struct pmufreq_softc *sc;
17210b31d0bSJustin Hibbits 	int error, speed_sel;
1734702d987SJustin Hibbits 
1744702d987SJustin Hibbits 	if (set == NULL)
1754702d987SJustin Hibbits 		return (EINVAL);
1764702d987SJustin Hibbits 
1774702d987SJustin Hibbits 	sc = device_get_softc(dev);
1784702d987SJustin Hibbits 
1794702d987SJustin Hibbits 	if (set->freq == sc->maxfreq)
1804702d987SJustin Hibbits 		speed_sel = 0;
1814702d987SJustin Hibbits 	else
1824702d987SJustin Hibbits 		speed_sel = 1;
1834702d987SJustin Hibbits 
1844702d987SJustin Hibbits 	error = pmu_set_speed(speed_sel);
1854702d987SJustin Hibbits 	if (error == 0)
1864702d987SJustin Hibbits 		sc->curfreq = set->freq;
1874702d987SJustin Hibbits 
18810b31d0bSJustin Hibbits 	return (error);
1894702d987SJustin Hibbits }
1904702d987SJustin Hibbits 
1914702d987SJustin Hibbits static int
pmufreq_get(device_t dev,struct cf_setting * set)1924702d987SJustin Hibbits pmufreq_get(device_t dev, struct cf_setting *set)
1934702d987SJustin Hibbits {
1944702d987SJustin Hibbits 	struct pmufreq_softc *sc;
1954702d987SJustin Hibbits 
1964702d987SJustin Hibbits 	if (set == NULL)
1974702d987SJustin Hibbits 		return (EINVAL);
1984702d987SJustin Hibbits 	sc = device_get_softc(dev);
1994702d987SJustin Hibbits 
2004702d987SJustin Hibbits 	set->freq = sc->curfreq;
2014702d987SJustin Hibbits 	set->dev = dev;
2024702d987SJustin Hibbits 
2034702d987SJustin Hibbits 	return (0);
2044702d987SJustin Hibbits }
2054702d987SJustin Hibbits 
2064702d987SJustin Hibbits static int
pmufreq_type(device_t dev,int * type)2074702d987SJustin Hibbits pmufreq_type(device_t dev, int *type)
2084702d987SJustin Hibbits {
2094702d987SJustin Hibbits 
2104702d987SJustin Hibbits 	if (type == NULL)
2114702d987SJustin Hibbits 		return (EINVAL);
2124702d987SJustin Hibbits 
2134702d987SJustin Hibbits 	*type = CPUFREQ_TYPE_ABSOLUTE;
2144702d987SJustin Hibbits 	return (0);
2154702d987SJustin Hibbits }
216