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