1259284Sjhibbits/*- 2259284Sjhibbits * Copyright (c) 2011 Justin Hibbits 3259284Sjhibbits * Copyright (c) 2009 Nathan Whitehorn 4259284Sjhibbits * All rights reserved. 5259284Sjhibbits * 6259284Sjhibbits * Redistribution and use in source and binary forms, with or without 7259284Sjhibbits * modification, are permitted provided that the following conditions 8259284Sjhibbits * are met: 9259284Sjhibbits * 1. Redistributions of source code must retain the above copyright 10259284Sjhibbits * notice, this list of conditions and the following disclaimer. 11259284Sjhibbits * 2. Redistributions in binary form must reproduce the above copyright 12259284Sjhibbits * notice, this list of conditions and the following disclaimer in the 13259284Sjhibbits * documentation and/or other materials provided with the distribution. 14259284Sjhibbits * 15259284Sjhibbits * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16259284Sjhibbits * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17259284Sjhibbits * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18259284Sjhibbits * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19259284Sjhibbits * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20259284Sjhibbits * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21259284Sjhibbits * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22259284Sjhibbits * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23259284Sjhibbits * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24259284Sjhibbits * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25259284Sjhibbits * SUCH DAMAGE. 26259284Sjhibbits */ 27259284Sjhibbits 28259284Sjhibbits#include <sys/cdefs.h> 29259284Sjhibbits__FBSDID("$FreeBSD: releng/10.2/sys/powerpc/cpufreq/pmufreq.c 260674 2014-01-15 06:17:15Z jhibbits $"); 30259284Sjhibbits 31259284Sjhibbits#include <sys/param.h> 32259284Sjhibbits#include <sys/systm.h> 33259284Sjhibbits#include <sys/bus.h> 34259284Sjhibbits#include <sys/cpu.h> 35259284Sjhibbits#include <sys/kernel.h> 36259284Sjhibbits#include <sys/limits.h> 37259284Sjhibbits#include <sys/module.h> 38259284Sjhibbits 39259284Sjhibbits#include <dev/ofw/ofw_bus.h> 40259284Sjhibbits#include <dev/ofw/openfirm.h> 41259284Sjhibbits 42259284Sjhibbits#include "cpufreq_if.h" 43259284Sjhibbits#include "powerpc/powermac/pmuvar.h" 44259284Sjhibbits 45259284Sjhibbitsstruct pmufreq_softc { 46259284Sjhibbits device_t dev; 47259284Sjhibbits uint32_t minfreq; 48259284Sjhibbits uint32_t maxfreq; 49259284Sjhibbits uint32_t curfreq; 50259284Sjhibbits}; 51259284Sjhibbits 52259284Sjhibbitsstatic void pmufreq_identify(driver_t *driver, device_t parent); 53259284Sjhibbitsstatic int pmufreq_probe(device_t dev); 54259284Sjhibbitsstatic int pmufreq_attach(device_t dev); 55259284Sjhibbitsstatic int pmufreq_settings(device_t dev, struct cf_setting *sets, int *count); 56259284Sjhibbitsstatic int pmufreq_set(device_t dev, const struct cf_setting *set); 57259284Sjhibbitsstatic int pmufreq_get(device_t dev, struct cf_setting *set); 58259284Sjhibbitsstatic int pmufreq_type(device_t dev, int *type); 59259284Sjhibbits 60259284Sjhibbitsstatic device_method_t pmufreq_methods[] = { 61259284Sjhibbits /* Device interface */ 62259284Sjhibbits DEVMETHOD(device_identify, pmufreq_identify), 63259284Sjhibbits DEVMETHOD(device_probe, pmufreq_probe), 64259284Sjhibbits DEVMETHOD(device_attach, pmufreq_attach), 65259284Sjhibbits 66259284Sjhibbits /* cpufreq interface */ 67259284Sjhibbits DEVMETHOD(cpufreq_drv_set, pmufreq_set), 68259284Sjhibbits DEVMETHOD(cpufreq_drv_get, pmufreq_get), 69259284Sjhibbits DEVMETHOD(cpufreq_drv_type, pmufreq_type), 70259284Sjhibbits DEVMETHOD(cpufreq_drv_settings, pmufreq_settings), 71259284Sjhibbits 72259284Sjhibbits {0, 0} 73259284Sjhibbits}; 74259284Sjhibbits 75259284Sjhibbitsstatic driver_t pmufreq_driver = { 76259284Sjhibbits "pmufreq", 77259284Sjhibbits pmufreq_methods, 78259284Sjhibbits sizeof(struct pmufreq_softc) 79259284Sjhibbits}; 80259284Sjhibbits 81259284Sjhibbitsstatic devclass_t pmufreq_devclass; 82259284SjhibbitsDRIVER_MODULE(pmufreq, cpu, pmufreq_driver, pmufreq_devclass, 0, 0); 83259284Sjhibbits 84259284Sjhibbitsstatic void 85259284Sjhibbitspmufreq_identify(driver_t *driver, device_t parent) 86259284Sjhibbits{ 87259284Sjhibbits phandle_t node; 88259284Sjhibbits uint32_t min_freq; 89259284Sjhibbits 90259284Sjhibbits node = ofw_bus_get_node(parent); 91259284Sjhibbits if (OF_getprop(node, "min-clock-frequency", &min_freq, sizeof(min_freq)) == -1) 92259284Sjhibbits return; 93259284Sjhibbits 94259284Sjhibbits /* Make sure we're not being doubly invoked. */ 95259284Sjhibbits if (device_find_child(parent, "pmufreq", -1) != NULL) 96259284Sjhibbits return; 97259284Sjhibbits 98259284Sjhibbits /* 99259284Sjhibbits * We attach a child for every CPU since settings need to 100259284Sjhibbits * be performed on every CPU in the SMP case. 101259284Sjhibbits */ 102259284Sjhibbits if (BUS_ADD_CHILD(parent, 10, "pmufreq", -1) == NULL) 103259284Sjhibbits device_printf(parent, "add pmufreq child failed\n"); 104259284Sjhibbits} 105259284Sjhibbits 106259284Sjhibbitsstatic int 107259284Sjhibbitspmufreq_probe(device_t dev) 108259284Sjhibbits{ 109259284Sjhibbits struct pmufreq_softc *sc; 110259284Sjhibbits phandle_t node; 111260674Sjhibbits uint32_t min_freq; 112259284Sjhibbits 113259284Sjhibbits if (resource_disabled("pmufreq", 0)) 114259284Sjhibbits return (ENXIO); 115259284Sjhibbits 116259284Sjhibbits sc = device_get_softc(dev); 117259284Sjhibbits node = ofw_bus_get_node(device_get_parent(dev)); 118259284Sjhibbits /* 119259284Sjhibbits * A scalable MPC7455 has min-clock-frequency/max-clock-frequency as OFW 120259284Sjhibbits * properties of the 'cpu' node. 121259284Sjhibbits */ 122259284Sjhibbits if (OF_getprop(node, "min-clock-frequency", &min_freq, sizeof(min_freq)) == -1) 123259284Sjhibbits return (ENXIO); 124259284Sjhibbits device_set_desc(dev, "PMU-based frequency scaling"); 125259284Sjhibbits return (0); 126259284Sjhibbits} 127259284Sjhibbits 128259284Sjhibbitsstatic int 129259284Sjhibbitspmufreq_attach(device_t dev) 130259284Sjhibbits{ 131259284Sjhibbits struct pmufreq_softc *sc; 132259284Sjhibbits phandle_t node; 133259284Sjhibbits 134259284Sjhibbits sc = device_get_softc(dev); 135259284Sjhibbits sc->dev = dev; 136259284Sjhibbits 137259284Sjhibbits node = ofw_bus_get_node(device_get_parent(dev)); 138259284Sjhibbits OF_getprop(node, "min-clock-frequency", &sc->minfreq, sizeof(sc->minfreq)); 139259284Sjhibbits OF_getprop(node, "max-clock-frequency", &sc->maxfreq, sizeof(sc->maxfreq)); 140259284Sjhibbits OF_getprop(node, "rounded-clock-frequency", &sc->curfreq, sizeof(sc->curfreq)); 141259284Sjhibbits sc->minfreq /= 1000000; 142259284Sjhibbits sc->maxfreq /= 1000000; 143259284Sjhibbits sc->curfreq /= 1000000; 144259284Sjhibbits 145259284Sjhibbits cpufreq_register(dev); 146259284Sjhibbits return (0); 147259284Sjhibbits} 148259284Sjhibbits 149259284Sjhibbitsstatic int 150259284Sjhibbitspmufreq_settings(device_t dev, struct cf_setting *sets, int *count) 151259284Sjhibbits{ 152259284Sjhibbits struct pmufreq_softc *sc; 153259284Sjhibbits 154259284Sjhibbits sc = device_get_softc(dev); 155259284Sjhibbits if (sets == NULL || count == NULL) 156259284Sjhibbits return (EINVAL); 157259284Sjhibbits if (*count < 2) 158259284Sjhibbits return (E2BIG); 159259284Sjhibbits 160259284Sjhibbits /* Return a list of valid settings for this driver. */ 161259284Sjhibbits memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * 2); 162259284Sjhibbits 163259284Sjhibbits sets[0].freq = sc->maxfreq; sets[0].dev = dev; 164259284Sjhibbits sets[1].freq = sc->minfreq; sets[1].dev = dev; 165259284Sjhibbits /* Set high latency for CPU frequency changes, it's a tedious process. */ 166259284Sjhibbits sets[0].lat = INT_MAX; 167259284Sjhibbits sets[1].lat = INT_MAX; 168259284Sjhibbits *count = 2; 169259284Sjhibbits 170259284Sjhibbits return (0); 171259284Sjhibbits} 172259284Sjhibbits 173259284Sjhibbitsstatic int 174259284Sjhibbitspmufreq_set(device_t dev, const struct cf_setting *set) 175259284Sjhibbits{ 176259284Sjhibbits struct pmufreq_softc *sc; 177260674Sjhibbits int error, speed_sel; 178259284Sjhibbits 179259284Sjhibbits if (set == NULL) 180259284Sjhibbits return (EINVAL); 181259284Sjhibbits 182259284Sjhibbits sc = device_get_softc(dev); 183259284Sjhibbits 184259284Sjhibbits if (set->freq == sc->maxfreq) 185259284Sjhibbits speed_sel = 0; 186259284Sjhibbits else 187259284Sjhibbits speed_sel = 1; 188259284Sjhibbits 189259284Sjhibbits error = pmu_set_speed(speed_sel); 190259284Sjhibbits if (error == 0) 191259284Sjhibbits sc->curfreq = set->freq; 192259284Sjhibbits 193260674Sjhibbits return (error); 194259284Sjhibbits} 195259284Sjhibbits 196259284Sjhibbitsstatic int 197259284Sjhibbitspmufreq_get(device_t dev, struct cf_setting *set) 198259284Sjhibbits{ 199259284Sjhibbits struct pmufreq_softc *sc; 200259284Sjhibbits 201259284Sjhibbits if (set == NULL) 202259284Sjhibbits return (EINVAL); 203259284Sjhibbits sc = device_get_softc(dev); 204259284Sjhibbits 205259284Sjhibbits set->freq = sc->curfreq; 206259284Sjhibbits set->dev = dev; 207259284Sjhibbits 208259284Sjhibbits return (0); 209259284Sjhibbits} 210259284Sjhibbits 211259284Sjhibbitsstatic int 212259284Sjhibbitspmufreq_type(device_t dev, int *type) 213259284Sjhibbits{ 214259284Sjhibbits 215259284Sjhibbits if (type == NULL) 216259284Sjhibbits return (EINVAL); 217259284Sjhibbits 218259284Sjhibbits *type = CPUFREQ_TYPE_ABSOLUTE; 219259284Sjhibbits return (0); 220259284Sjhibbits} 221259284Sjhibbits 222