pcr.c revision 194679
1194679Snwhitehorn/*- 2194679Snwhitehorn * Copyright (c) 2009 Nathan Whitehorn 3194679Snwhitehorn * All rights reserved. 4194679Snwhitehorn * 5194679Snwhitehorn * Redistribution and use in source and binary forms, with or without 6194679Snwhitehorn * modification, are permitted provided that the following conditions 7194679Snwhitehorn * are met: 8194679Snwhitehorn * 1. Redistributions of source code must retain the above copyright 9194679Snwhitehorn * notice, this list of conditions and the following disclaimer. 10194679Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright 11194679Snwhitehorn * notice, this list of conditions and the following disclaimer in the 12194679Snwhitehorn * documentation and/or other materials provided with the distribution. 13194679Snwhitehorn * 14194679Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15194679Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16194679Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17194679Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18194679Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19194679Snwhitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20194679Snwhitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21194679Snwhitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22194679Snwhitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23194679Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24194679Snwhitehorn * SUCH DAMAGE. 25194679Snwhitehorn */ 26194679Snwhitehorn 27194679Snwhitehorn#include <sys/cdefs.h> 28194679Snwhitehorn__FBSDID("$FreeBSD: head/sys/powerpc/cpufreq/pcr.c 194679 2009-06-23 04:28:32Z nwhitehorn $"); 29194679Snwhitehorn 30194679Snwhitehorn#include <sys/param.h> 31194679Snwhitehorn#include <sys/systm.h> 32194679Snwhitehorn#include <sys/bus.h> 33194679Snwhitehorn#include <sys/cpu.h> 34194679Snwhitehorn#include <sys/kernel.h> 35194679Snwhitehorn#include <sys/module.h> 36194679Snwhitehorn 37194679Snwhitehorn#include <dev/ofw/ofw_bus.h> 38194679Snwhitehorn 39194679Snwhitehorn#include "cpufreq_if.h" 40194679Snwhitehorn 41194679Snwhitehornstruct pcr_softc { 42194679Snwhitehorn device_t dev; 43194679Snwhitehorn uint32_t pcr_vals[3]; 44194679Snwhitehorn int nmodes; 45194679Snwhitehorn}; 46194679Snwhitehorn 47194679Snwhitehornstatic void pcr_identify(driver_t *driver, device_t parent); 48194679Snwhitehornstatic int pcr_probe(device_t dev); 49194679Snwhitehornstatic int pcr_attach(device_t dev); 50194679Snwhitehornstatic int pcr_settings(device_t dev, struct cf_setting *sets, int *count); 51194679Snwhitehornstatic int pcr_set(device_t dev, const struct cf_setting *set); 52194679Snwhitehornstatic int pcr_get(device_t dev, struct cf_setting *set); 53194679Snwhitehornstatic int pcr_type(device_t dev, int *type); 54194679Snwhitehorn 55194679Snwhitehornstatic device_method_t pcr_methods[] = { 56194679Snwhitehorn /* Device interface */ 57194679Snwhitehorn DEVMETHOD(device_identify, pcr_identify), 58194679Snwhitehorn DEVMETHOD(device_probe, pcr_probe), 59194679Snwhitehorn DEVMETHOD(device_attach, pcr_attach), 60194679Snwhitehorn 61194679Snwhitehorn /* cpufreq interface */ 62194679Snwhitehorn DEVMETHOD(cpufreq_drv_set, pcr_set), 63194679Snwhitehorn DEVMETHOD(cpufreq_drv_get, pcr_get), 64194679Snwhitehorn DEVMETHOD(cpufreq_drv_type, pcr_type), 65194679Snwhitehorn DEVMETHOD(cpufreq_drv_settings, pcr_settings), 66194679Snwhitehorn 67194679Snwhitehorn {0, 0} 68194679Snwhitehorn}; 69194679Snwhitehorn 70194679Snwhitehornstatic driver_t pcr_driver = { 71194679Snwhitehorn "pcr", 72194679Snwhitehorn pcr_methods, 73194679Snwhitehorn sizeof(struct pcr_softc) 74194679Snwhitehorn}; 75194679Snwhitehorn 76194679Snwhitehornstatic devclass_t pcr_devclass; 77194679SnwhitehornDRIVER_MODULE(pcr, cpu, pcr_driver, pcr_devclass, 0, 0); 78194679Snwhitehorn 79194679Snwhitehorn/* 80194679Snwhitehorn * States 81194679Snwhitehorn */ 82194679Snwhitehorn 83194679Snwhitehorn#define PCR_TO_FREQ(a) ((a >> 17) & 3) 84194679Snwhitehorn 85194679Snwhitehorn#define PCR_FULL 0 86194679Snwhitehorn#define PCR_HALF 1 87194679Snwhitehorn#define PCR_QUARTER 2 /* Only on 970MP */ 88194679Snwhitehorn 89194679Snwhitehorn#define PSR_RECEIVED (1ULL << 61) 90194679Snwhitehorn#define PSR_COMPLETED (1ULL << 61) 91194679Snwhitehorn 92194679Snwhitehorn/* 93194679Snwhitehorn * SCOM addresses 94194679Snwhitehorn */ 95194679Snwhitehorn 96194679Snwhitehorn#define SCOM_PCR 0x0aa00100 /* Power Control Register */ 97194679Snwhitehorn#define SCOM_PCR_BIT 0x80000000 /* Data bit for PCR */ 98194679Snwhitehorn#define SCOM_PSR 0x40800100 /* Power Status Register */ 99194679Snwhitehorn 100194679Snwhitehorn/* 101194679Snwhitehorn * SCOM Glue 102194679Snwhitehorn */ 103194679Snwhitehorn 104194679Snwhitehorn#define SCOMC_READ 0x00008000 105194679Snwhitehorn#define SCOMC_WRITE 0x00000000 106194679Snwhitehorn 107194679Snwhitehornstatic void 108194679Snwhitehornwrite_scom(register_t address, uint64_t value) 109194679Snwhitehorn{ 110194679Snwhitehorn register_t msr; 111194679Snwhitehorn register_t hi, lo, scratch; 112194679Snwhitehorn 113194679Snwhitehorn hi = (value >> 32) & 0xffffffff; 114194679Snwhitehorn lo = value & 0xffffffff; 115194679Snwhitehorn 116194679Snwhitehorn msr = mfmsr(); 117194679Snwhitehorn mtmsr(msr & ~PSL_EE); isync(); 118194679Snwhitehorn 119194679Snwhitehorn mtspr64(SPR_SCOMD, hi, lo, scratch); 120194679Snwhitehorn isync(); 121194679Snwhitehorn mtspr(SPR_SCOMC, address | SCOMC_WRITE); 122194679Snwhitehorn isync(); 123194679Snwhitehorn 124194679Snwhitehorn mtmsr(msr); isync(); 125194679Snwhitehorn} 126194679Snwhitehorn 127194679Snwhitehornstatic uint64_t 128194679Snwhitehornread_scom(register_t address) 129194679Snwhitehorn{ 130194679Snwhitehorn register_t msr; 131194679Snwhitehorn uint64_t ret; 132194679Snwhitehorn 133194679Snwhitehorn msr = mfmsr(); 134194679Snwhitehorn mtmsr(msr & ~PSL_EE); isync(); 135194679Snwhitehorn 136194679Snwhitehorn mtspr(SPR_SCOMC, address | SCOMC_READ); 137194679Snwhitehorn isync(); 138194679Snwhitehorn 139194679Snwhitehorn __asm __volatile ("mfspr %0,%1;" 140194679Snwhitehorn " mr %0+1, %0; srdi %0,%0,32" : "=r" (ret) : "K" (SPR_SCOMD)); 141194679Snwhitehorn 142194679Snwhitehorn (void)mfspr(SPR_SCOMC); /* Complete transcation */ 143194679Snwhitehorn 144194679Snwhitehorn mtmsr(msr); isync(); 145194679Snwhitehorn 146194679Snwhitehorn return (ret); 147194679Snwhitehorn} 148194679Snwhitehorn 149194679Snwhitehornstatic void 150194679Snwhitehornpcr_identify(driver_t *driver, device_t parent) 151194679Snwhitehorn{ 152194679Snwhitehorn uint16_t vers; 153194679Snwhitehorn vers = mfpvr() >> 16; 154194679Snwhitehorn 155194679Snwhitehorn /* Check for an IBM 970-class CPU */ 156194679Snwhitehorn switch (vers) { 157194679Snwhitehorn case IBM970FX: 158194679Snwhitehorn case IBM970GX: 159194679Snwhitehorn case IBM970MP: 160194679Snwhitehorn break; 161194679Snwhitehorn default: 162194679Snwhitehorn return; 163194679Snwhitehorn } 164194679Snwhitehorn 165194679Snwhitehorn /* Make sure we're not being doubly invoked. */ 166194679Snwhitehorn if (device_find_child(parent, "pcr", -1) != NULL) 167194679Snwhitehorn return; 168194679Snwhitehorn 169194679Snwhitehorn /* 170194679Snwhitehorn * We attach a child for every CPU since settings need to 171194679Snwhitehorn * be performed on every CPU in the SMP case. 172194679Snwhitehorn */ 173194679Snwhitehorn if (BUS_ADD_CHILD(parent, 10, "pcr", -1) == NULL) 174194679Snwhitehorn device_printf(parent, "add pcr child failed\n"); 175194679Snwhitehorn} 176194679Snwhitehorn 177194679Snwhitehornstatic int 178194679Snwhitehornpcr_probe(device_t dev) 179194679Snwhitehorn{ 180194679Snwhitehorn if (resource_disabled("pcr", 0)) 181194679Snwhitehorn return (ENXIO); 182194679Snwhitehorn 183194679Snwhitehorn device_set_desc(dev, "PPC 970 Power Control Register"); 184194679Snwhitehorn return (0); 185194679Snwhitehorn} 186194679Snwhitehorn 187194679Snwhitehornstatic int 188194679Snwhitehornpcr_attach(device_t dev) 189194679Snwhitehorn{ 190194679Snwhitehorn struct pcr_softc *sc; 191194679Snwhitehorn phandle_t cpu; 192194679Snwhitehorn uint32_t modes[3]; 193194679Snwhitehorn int i; 194194679Snwhitehorn 195194679Snwhitehorn sc = device_get_softc(dev); 196194679Snwhitehorn sc->dev = dev; 197194679Snwhitehorn 198194679Snwhitehorn cpu = ofw_bus_get_node(device_get_parent(dev)); 199194679Snwhitehorn 200194679Snwhitehorn if (cpu <= 0) { 201194679Snwhitehorn device_printf(dev,"No CPU device tree node!\n"); 202194679Snwhitehorn return (ENXIO); 203194679Snwhitehorn } 204194679Snwhitehorn 205194679Snwhitehorn /* 206194679Snwhitehorn * Collect the PCR values for each mode from the device tree. 207194679Snwhitehorn * These include bus timing information, and so cannot be 208194679Snwhitehorn * directly computed. 209194679Snwhitehorn */ 210194679Snwhitehorn sc->nmodes = OF_getproplen(cpu, "power-mode-data"); 211194679Snwhitehorn if (sc->nmodes <= 0 || sc->nmodes > sizeof(sc->pcr_vals)) { 212194679Snwhitehorn device_printf(dev,"No power mode data in device tree!\n"); 213194679Snwhitehorn return (ENXIO); 214194679Snwhitehorn } 215194679Snwhitehorn OF_getprop(cpu, "power-mode-data", modes, sc->nmodes); 216194679Snwhitehorn sc->nmodes /= sizeof(modes[0]); 217194679Snwhitehorn 218194679Snwhitehorn /* Sort the modes */ 219194679Snwhitehorn for (i = 0; i < sc->nmodes; i++) 220194679Snwhitehorn sc->pcr_vals[PCR_TO_FREQ(modes[i])] = modes[i]; 221194679Snwhitehorn 222194679Snwhitehorn cpufreq_register(dev); 223194679Snwhitehorn return (0); 224194679Snwhitehorn} 225194679Snwhitehorn 226194679Snwhitehornstatic int 227194679Snwhitehornpcr_settings(device_t dev, struct cf_setting *sets, int *count) 228194679Snwhitehorn{ 229194679Snwhitehorn struct pcr_softc *sc; 230194679Snwhitehorn 231194679Snwhitehorn sc = device_get_softc(dev); 232194679Snwhitehorn if (sets == NULL || count == NULL) 233194679Snwhitehorn return (EINVAL); 234194679Snwhitehorn if (*count < sc->nmodes) 235194679Snwhitehorn return (E2BIG); 236194679Snwhitehorn 237194679Snwhitehorn /* Return a list of valid settings for this driver. */ 238194679Snwhitehorn memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * sc->nmodes); 239194679Snwhitehorn 240194679Snwhitehorn sets[0].freq = 10000; sets[0].dev = dev; 241194679Snwhitehorn sets[1].freq = 5000; sets[1].dev = dev; 242194679Snwhitehorn if (sc->nmodes > 2) 243194679Snwhitehorn sets[2].freq = 2500; sets[2].dev = dev; 244194679Snwhitehorn *count = sc->nmodes; 245194679Snwhitehorn 246194679Snwhitehorn return (0); 247194679Snwhitehorn} 248194679Snwhitehorn 249194679Snwhitehornstatic int 250194679Snwhitehornpcr_set(device_t dev, const struct cf_setting *set) 251194679Snwhitehorn{ 252194679Snwhitehorn struct pcr_softc *sc; 253194679Snwhitehorn register_t pcr, msr; 254194679Snwhitehorn uint64_t psr; 255194679Snwhitehorn 256194679Snwhitehorn if (set == NULL) 257194679Snwhitehorn return (EINVAL); 258194679Snwhitehorn sc = device_get_softc(dev); 259194679Snwhitehorn 260194679Snwhitehorn /* Construct the new PCR */ 261194679Snwhitehorn 262194679Snwhitehorn pcr = SCOM_PCR_BIT; 263194679Snwhitehorn 264194679Snwhitehorn if (set->freq == 10000) 265194679Snwhitehorn pcr |= sc->pcr_vals[0]; 266194679Snwhitehorn else if (set->freq == 5000) 267194679Snwhitehorn pcr |= sc->pcr_vals[1]; 268194679Snwhitehorn else if (set->freq == 2500) 269194679Snwhitehorn pcr |= sc->pcr_vals[2]; 270194679Snwhitehorn 271194679Snwhitehorn msr = mfmsr(); 272194679Snwhitehorn mtmsr(msr & ~PSL_EE); isync(); 273194679Snwhitehorn 274194679Snwhitehorn /* 970MP requires PCR and PCRH to be cleared first */ 275194679Snwhitehorn 276194679Snwhitehorn write_scom(SCOM_PCR,0); /* Clear PCRH */ 277194679Snwhitehorn write_scom(SCOM_PCR,SCOM_PCR_BIT); /* Clear PCR */ 278194679Snwhitehorn 279194679Snwhitehorn /* Set PCR */ 280194679Snwhitehorn 281194679Snwhitehorn write_scom(SCOM_PCR, pcr); 282194679Snwhitehorn 283194679Snwhitehorn /* Wait for completion */ 284194679Snwhitehorn 285194679Snwhitehorn do { 286194679Snwhitehorn DELAY(100); 287194679Snwhitehorn psr = read_scom(SCOM_PSR); 288194679Snwhitehorn } while ((psr & PSR_RECEIVED) && !(psr & PSR_COMPLETED)); 289194679Snwhitehorn 290194679Snwhitehorn mtmsr(msr); isync(); 291194679Snwhitehorn 292194679Snwhitehorn return (0); 293194679Snwhitehorn} 294194679Snwhitehorn 295194679Snwhitehornstatic int 296194679Snwhitehornpcr_get(device_t dev, struct cf_setting *set) 297194679Snwhitehorn{ 298194679Snwhitehorn struct pcr_softc *sc; 299194679Snwhitehorn uint64_t psr; 300194679Snwhitehorn 301194679Snwhitehorn if (set == NULL) 302194679Snwhitehorn return (EINVAL); 303194679Snwhitehorn sc = device_get_softc(dev); 304194679Snwhitehorn 305194679Snwhitehorn memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set)); 306194679Snwhitehorn 307194679Snwhitehorn psr = read_scom(SCOM_PSR); 308194679Snwhitehorn 309194679Snwhitehorn /* We want bits 6 and 7 */ 310194679Snwhitehorn psr = (psr >> 56) & 3; 311194679Snwhitehorn 312194679Snwhitehorn set->freq = 10000; 313194679Snwhitehorn if (psr == PCR_HALF) 314194679Snwhitehorn set->freq = 5000; 315194679Snwhitehorn else if (psr == PCR_QUARTER) 316194679Snwhitehorn set->freq = 2500; 317194679Snwhitehorn 318194679Snwhitehorn set->dev = dev; 319194679Snwhitehorn 320194679Snwhitehorn return (0); 321194679Snwhitehorn} 322194679Snwhitehorn 323194679Snwhitehornstatic int 324194679Snwhitehornpcr_type(device_t dev, int *type) 325194679Snwhitehorn{ 326194679Snwhitehorn 327194679Snwhitehorn if (type == NULL) 328194679Snwhitehorn return (EINVAL); 329194679Snwhitehorn 330194679Snwhitehorn *type = CPUFREQ_TYPE_RELATIVE; 331194679Snwhitehorn return (0); 332194679Snwhitehorn} 333194679Snwhitehorn 334