pcr.c revision 209975
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 209975 2010-07-13 05:32:19Z 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; 111209975Snwhitehorn #ifndef __powerpc64__ 112194679Snwhitehorn register_t hi, lo, scratch; 113209975Snwhitehorn #endif 114194679Snwhitehorn 115194679Snwhitehorn msr = mfmsr(); 116194679Snwhitehorn mtmsr(msr & ~PSL_EE); isync(); 117194679Snwhitehorn 118209975Snwhitehorn #ifdef __powerpc64__ 119209975Snwhitehorn mtspr(SPR_SCOMD, value); 120209975Snwhitehorn #else 121209975Snwhitehorn hi = (value >> 32) & 0xffffffff; 122209975Snwhitehorn lo = value & 0xffffffff; 123194679Snwhitehorn mtspr64(SPR_SCOMD, hi, lo, scratch); 124209975Snwhitehorn #endif 125194679Snwhitehorn isync(); 126194679Snwhitehorn mtspr(SPR_SCOMC, address | SCOMC_WRITE); 127194679Snwhitehorn isync(); 128194679Snwhitehorn 129194679Snwhitehorn mtmsr(msr); isync(); 130194679Snwhitehorn} 131194679Snwhitehorn 132194679Snwhitehornstatic uint64_t 133194679Snwhitehornread_scom(register_t address) 134194679Snwhitehorn{ 135194679Snwhitehorn register_t msr; 136194679Snwhitehorn uint64_t ret; 137194679Snwhitehorn 138194679Snwhitehorn msr = mfmsr(); 139194679Snwhitehorn mtmsr(msr & ~PSL_EE); isync(); 140194679Snwhitehorn 141194679Snwhitehorn mtspr(SPR_SCOMC, address | SCOMC_READ); 142194679Snwhitehorn isync(); 143194679Snwhitehorn 144194679Snwhitehorn __asm __volatile ("mfspr %0,%1;" 145194679Snwhitehorn " mr %0+1, %0; srdi %0,%0,32" : "=r" (ret) : "K" (SPR_SCOMD)); 146194679Snwhitehorn 147194679Snwhitehorn (void)mfspr(SPR_SCOMC); /* Complete transcation */ 148194679Snwhitehorn 149194679Snwhitehorn mtmsr(msr); isync(); 150194679Snwhitehorn 151194679Snwhitehorn return (ret); 152194679Snwhitehorn} 153194679Snwhitehorn 154194679Snwhitehornstatic void 155194679Snwhitehornpcr_identify(driver_t *driver, device_t parent) 156194679Snwhitehorn{ 157194679Snwhitehorn uint16_t vers; 158194679Snwhitehorn vers = mfpvr() >> 16; 159194679Snwhitehorn 160194679Snwhitehorn /* Check for an IBM 970-class CPU */ 161194679Snwhitehorn switch (vers) { 162194679Snwhitehorn case IBM970FX: 163194679Snwhitehorn case IBM970GX: 164194679Snwhitehorn case IBM970MP: 165194679Snwhitehorn break; 166194679Snwhitehorn default: 167194679Snwhitehorn return; 168194679Snwhitehorn } 169194679Snwhitehorn 170194679Snwhitehorn /* Make sure we're not being doubly invoked. */ 171194679Snwhitehorn if (device_find_child(parent, "pcr", -1) != NULL) 172194679Snwhitehorn return; 173194679Snwhitehorn 174194679Snwhitehorn /* 175194679Snwhitehorn * We attach a child for every CPU since settings need to 176194679Snwhitehorn * be performed on every CPU in the SMP case. 177194679Snwhitehorn */ 178194679Snwhitehorn if (BUS_ADD_CHILD(parent, 10, "pcr", -1) == NULL) 179194679Snwhitehorn device_printf(parent, "add pcr child failed\n"); 180194679Snwhitehorn} 181194679Snwhitehorn 182194679Snwhitehornstatic int 183194679Snwhitehornpcr_probe(device_t dev) 184194679Snwhitehorn{ 185194679Snwhitehorn if (resource_disabled("pcr", 0)) 186194679Snwhitehorn return (ENXIO); 187194679Snwhitehorn 188194679Snwhitehorn device_set_desc(dev, "PPC 970 Power Control Register"); 189194679Snwhitehorn return (0); 190194679Snwhitehorn} 191194679Snwhitehorn 192194679Snwhitehornstatic int 193194679Snwhitehornpcr_attach(device_t dev) 194194679Snwhitehorn{ 195194679Snwhitehorn struct pcr_softc *sc; 196194679Snwhitehorn phandle_t cpu; 197194679Snwhitehorn uint32_t modes[3]; 198194679Snwhitehorn int i; 199194679Snwhitehorn 200194679Snwhitehorn sc = device_get_softc(dev); 201194679Snwhitehorn sc->dev = dev; 202194679Snwhitehorn 203194679Snwhitehorn cpu = ofw_bus_get_node(device_get_parent(dev)); 204194679Snwhitehorn 205194679Snwhitehorn if (cpu <= 0) { 206194679Snwhitehorn device_printf(dev,"No CPU device tree node!\n"); 207194679Snwhitehorn return (ENXIO); 208194679Snwhitehorn } 209194679Snwhitehorn 210208150Snwhitehorn if (OF_getproplen(cpu, "power-mode-data") <= 0) { 211208150Snwhitehorn /* Use the first CPU's node */ 212208150Snwhitehorn cpu = OF_child(OF_parent(cpu)); 213208150Snwhitehorn } 214208150Snwhitehorn 215194679Snwhitehorn /* 216194679Snwhitehorn * Collect the PCR values for each mode from the device tree. 217194679Snwhitehorn * These include bus timing information, and so cannot be 218194679Snwhitehorn * directly computed. 219194679Snwhitehorn */ 220194679Snwhitehorn sc->nmodes = OF_getproplen(cpu, "power-mode-data"); 221194679Snwhitehorn if (sc->nmodes <= 0 || sc->nmodes > sizeof(sc->pcr_vals)) { 222194679Snwhitehorn device_printf(dev,"No power mode data in device tree!\n"); 223194679Snwhitehorn return (ENXIO); 224194679Snwhitehorn } 225194679Snwhitehorn OF_getprop(cpu, "power-mode-data", modes, sc->nmodes); 226194679Snwhitehorn sc->nmodes /= sizeof(modes[0]); 227194679Snwhitehorn 228194679Snwhitehorn /* Sort the modes */ 229194679Snwhitehorn for (i = 0; i < sc->nmodes; i++) 230194679Snwhitehorn sc->pcr_vals[PCR_TO_FREQ(modes[i])] = modes[i]; 231194679Snwhitehorn 232194679Snwhitehorn cpufreq_register(dev); 233194679Snwhitehorn return (0); 234194679Snwhitehorn} 235194679Snwhitehorn 236194679Snwhitehornstatic int 237194679Snwhitehornpcr_settings(device_t dev, struct cf_setting *sets, int *count) 238194679Snwhitehorn{ 239194679Snwhitehorn struct pcr_softc *sc; 240194679Snwhitehorn 241194679Snwhitehorn sc = device_get_softc(dev); 242194679Snwhitehorn if (sets == NULL || count == NULL) 243194679Snwhitehorn return (EINVAL); 244194679Snwhitehorn if (*count < sc->nmodes) 245194679Snwhitehorn return (E2BIG); 246194679Snwhitehorn 247194679Snwhitehorn /* Return a list of valid settings for this driver. */ 248194679Snwhitehorn memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * sc->nmodes); 249194679Snwhitehorn 250194679Snwhitehorn sets[0].freq = 10000; sets[0].dev = dev; 251194679Snwhitehorn sets[1].freq = 5000; sets[1].dev = dev; 252194679Snwhitehorn if (sc->nmodes > 2) 253194679Snwhitehorn sets[2].freq = 2500; sets[2].dev = dev; 254194679Snwhitehorn *count = sc->nmodes; 255194679Snwhitehorn 256194679Snwhitehorn return (0); 257194679Snwhitehorn} 258194679Snwhitehorn 259194679Snwhitehornstatic int 260194679Snwhitehornpcr_set(device_t dev, const struct cf_setting *set) 261194679Snwhitehorn{ 262194679Snwhitehorn struct pcr_softc *sc; 263194679Snwhitehorn register_t pcr, msr; 264194679Snwhitehorn uint64_t psr; 265194679Snwhitehorn 266194679Snwhitehorn if (set == NULL) 267194679Snwhitehorn return (EINVAL); 268194679Snwhitehorn sc = device_get_softc(dev); 269194679Snwhitehorn 270194679Snwhitehorn /* Construct the new PCR */ 271194679Snwhitehorn 272194679Snwhitehorn pcr = SCOM_PCR_BIT; 273194679Snwhitehorn 274194679Snwhitehorn if (set->freq == 10000) 275194679Snwhitehorn pcr |= sc->pcr_vals[0]; 276194679Snwhitehorn else if (set->freq == 5000) 277194679Snwhitehorn pcr |= sc->pcr_vals[1]; 278194679Snwhitehorn else if (set->freq == 2500) 279194679Snwhitehorn pcr |= sc->pcr_vals[2]; 280194679Snwhitehorn 281194679Snwhitehorn msr = mfmsr(); 282194679Snwhitehorn mtmsr(msr & ~PSL_EE); isync(); 283194679Snwhitehorn 284194679Snwhitehorn /* 970MP requires PCR and PCRH to be cleared first */ 285194679Snwhitehorn 286194679Snwhitehorn write_scom(SCOM_PCR,0); /* Clear PCRH */ 287194679Snwhitehorn write_scom(SCOM_PCR,SCOM_PCR_BIT); /* Clear PCR */ 288194679Snwhitehorn 289194679Snwhitehorn /* Set PCR */ 290194679Snwhitehorn 291194679Snwhitehorn write_scom(SCOM_PCR, pcr); 292194679Snwhitehorn 293194679Snwhitehorn /* Wait for completion */ 294194679Snwhitehorn 295194679Snwhitehorn do { 296194679Snwhitehorn DELAY(100); 297194679Snwhitehorn psr = read_scom(SCOM_PSR); 298194679Snwhitehorn } while ((psr & PSR_RECEIVED) && !(psr & PSR_COMPLETED)); 299194679Snwhitehorn 300194679Snwhitehorn mtmsr(msr); isync(); 301194679Snwhitehorn 302194679Snwhitehorn return (0); 303194679Snwhitehorn} 304194679Snwhitehorn 305194679Snwhitehornstatic int 306194679Snwhitehornpcr_get(device_t dev, struct cf_setting *set) 307194679Snwhitehorn{ 308194679Snwhitehorn struct pcr_softc *sc; 309194679Snwhitehorn uint64_t psr; 310194679Snwhitehorn 311194679Snwhitehorn if (set == NULL) 312194679Snwhitehorn return (EINVAL); 313194679Snwhitehorn sc = device_get_softc(dev); 314194679Snwhitehorn 315194679Snwhitehorn memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set)); 316194679Snwhitehorn 317194679Snwhitehorn psr = read_scom(SCOM_PSR); 318194679Snwhitehorn 319194679Snwhitehorn /* We want bits 6 and 7 */ 320194679Snwhitehorn psr = (psr >> 56) & 3; 321194679Snwhitehorn 322194679Snwhitehorn set->freq = 10000; 323194679Snwhitehorn if (psr == PCR_HALF) 324194679Snwhitehorn set->freq = 5000; 325194679Snwhitehorn else if (psr == PCR_QUARTER) 326194679Snwhitehorn set->freq = 2500; 327194679Snwhitehorn 328194679Snwhitehorn set->dev = dev; 329194679Snwhitehorn 330194679Snwhitehorn return (0); 331194679Snwhitehorn} 332194679Snwhitehorn 333194679Snwhitehornstatic int 334194679Snwhitehornpcr_type(device_t dev, int *type) 335194679Snwhitehorn{ 336194679Snwhitehorn 337194679Snwhitehorn if (type == NULL) 338194679Snwhitehorn return (EINVAL); 339194679Snwhitehorn 340194679Snwhitehorn *type = CPUFREQ_TYPE_RELATIVE; 341194679Snwhitehorn return (0); 342194679Snwhitehorn} 343194679Snwhitehorn 344