11016f143SNathan Whitehorn /*- 21016f143SNathan Whitehorn * Copyright (c) 2009 Nathan Whitehorn 31016f143SNathan Whitehorn * All rights reserved. 41016f143SNathan Whitehorn * 51016f143SNathan Whitehorn * Redistribution and use in source and binary forms, with or without 61016f143SNathan Whitehorn * modification, are permitted provided that the following conditions 71016f143SNathan Whitehorn * are met: 81016f143SNathan Whitehorn * 1. Redistributions of source code must retain the above copyright 91016f143SNathan Whitehorn * notice, this list of conditions and the following disclaimer. 101016f143SNathan Whitehorn * 2. Redistributions in binary form must reproduce the above copyright 111016f143SNathan Whitehorn * notice, this list of conditions and the following disclaimer in the 121016f143SNathan Whitehorn * documentation and/or other materials provided with the distribution. 131016f143SNathan Whitehorn * 141016f143SNathan Whitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 151016f143SNathan Whitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 161016f143SNathan Whitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 171016f143SNathan Whitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 181016f143SNathan Whitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 191016f143SNathan Whitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 201016f143SNathan Whitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 211016f143SNathan Whitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 221016f143SNathan Whitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231016f143SNathan Whitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241016f143SNathan Whitehorn * SUCH DAMAGE. 251016f143SNathan Whitehorn */ 261016f143SNathan Whitehorn 271016f143SNathan Whitehorn #include <sys/cdefs.h> 281016f143SNathan Whitehorn __FBSDID("$FreeBSD$"); 291016f143SNathan Whitehorn 301016f143SNathan Whitehorn #include <sys/param.h> 311016f143SNathan Whitehorn #include <sys/systm.h> 321016f143SNathan Whitehorn #include <sys/bus.h> 331016f143SNathan Whitehorn #include <sys/cpu.h> 341016f143SNathan Whitehorn #include <sys/kernel.h> 351016f143SNathan Whitehorn #include <sys/module.h> 361016f143SNathan Whitehorn 371016f143SNathan Whitehorn #include <dev/ofw/ofw_bus.h> 381016f143SNathan Whitehorn 391016f143SNathan Whitehorn #include "cpufreq_if.h" 401016f143SNathan Whitehorn 411016f143SNathan Whitehorn struct pcr_softc { 421016f143SNathan Whitehorn device_t dev; 431016f143SNathan Whitehorn uint32_t pcr_vals[3]; 441016f143SNathan Whitehorn int nmodes; 451016f143SNathan Whitehorn }; 461016f143SNathan Whitehorn 471016f143SNathan Whitehorn static void pcr_identify(driver_t *driver, device_t parent); 481016f143SNathan Whitehorn static int pcr_probe(device_t dev); 491016f143SNathan Whitehorn static int pcr_attach(device_t dev); 501016f143SNathan Whitehorn static int pcr_settings(device_t dev, struct cf_setting *sets, int *count); 511016f143SNathan Whitehorn static int pcr_set(device_t dev, const struct cf_setting *set); 521016f143SNathan Whitehorn static int pcr_get(device_t dev, struct cf_setting *set); 531016f143SNathan Whitehorn static int pcr_type(device_t dev, int *type); 541016f143SNathan Whitehorn 551016f143SNathan Whitehorn static device_method_t pcr_methods[] = { 561016f143SNathan Whitehorn /* Device interface */ 571016f143SNathan Whitehorn DEVMETHOD(device_identify, pcr_identify), 581016f143SNathan Whitehorn DEVMETHOD(device_probe, pcr_probe), 591016f143SNathan Whitehorn DEVMETHOD(device_attach, pcr_attach), 601016f143SNathan Whitehorn 611016f143SNathan Whitehorn /* cpufreq interface */ 621016f143SNathan Whitehorn DEVMETHOD(cpufreq_drv_set, pcr_set), 631016f143SNathan Whitehorn DEVMETHOD(cpufreq_drv_get, pcr_get), 641016f143SNathan Whitehorn DEVMETHOD(cpufreq_drv_type, pcr_type), 651016f143SNathan Whitehorn DEVMETHOD(cpufreq_drv_settings, pcr_settings), 661016f143SNathan Whitehorn 671016f143SNathan Whitehorn {0, 0} 681016f143SNathan Whitehorn }; 691016f143SNathan Whitehorn 701016f143SNathan Whitehorn static driver_t pcr_driver = { 711016f143SNathan Whitehorn "pcr", 721016f143SNathan Whitehorn pcr_methods, 731016f143SNathan Whitehorn sizeof(struct pcr_softc) 741016f143SNathan Whitehorn }; 751016f143SNathan Whitehorn 761016f143SNathan Whitehorn static devclass_t pcr_devclass; 771016f143SNathan Whitehorn DRIVER_MODULE(pcr, cpu, pcr_driver, pcr_devclass, 0, 0); 781016f143SNathan Whitehorn 791016f143SNathan Whitehorn /* 801016f143SNathan Whitehorn * States 811016f143SNathan Whitehorn */ 821016f143SNathan Whitehorn 831016f143SNathan Whitehorn #define PCR_TO_FREQ(a) ((a >> 17) & 3) 841016f143SNathan Whitehorn 851016f143SNathan Whitehorn #define PCR_FULL 0 861016f143SNathan Whitehorn #define PCR_HALF 1 871016f143SNathan Whitehorn #define PCR_QUARTER 2 /* Only on 970MP */ 881016f143SNathan Whitehorn 891016f143SNathan Whitehorn #define PSR_RECEIVED (1ULL << 61) 901016f143SNathan Whitehorn #define PSR_COMPLETED (1ULL << 61) 911016f143SNathan Whitehorn 921016f143SNathan Whitehorn /* 931016f143SNathan Whitehorn * SCOM addresses 941016f143SNathan Whitehorn */ 951016f143SNathan Whitehorn 961016f143SNathan Whitehorn #define SCOM_PCR 0x0aa00100 /* Power Control Register */ 971016f143SNathan Whitehorn #define SCOM_PCR_BIT 0x80000000 /* Data bit for PCR */ 981016f143SNathan Whitehorn #define SCOM_PSR 0x40800100 /* Power Status Register */ 991016f143SNathan Whitehorn 1001016f143SNathan Whitehorn /* 1011016f143SNathan Whitehorn * SCOM Glue 1021016f143SNathan Whitehorn */ 1031016f143SNathan Whitehorn 1041016f143SNathan Whitehorn #define SCOMC_READ 0x00008000 1051016f143SNathan Whitehorn #define SCOMC_WRITE 0x00000000 1061016f143SNathan Whitehorn 1071016f143SNathan Whitehorn static void 1081016f143SNathan Whitehorn write_scom(register_t address, uint64_t value) 1091016f143SNathan Whitehorn { 1101016f143SNathan Whitehorn register_t msr; 1111016f143SNathan Whitehorn register_t hi, lo, scratch; 1121016f143SNathan Whitehorn 1131016f143SNathan Whitehorn hi = (value >> 32) & 0xffffffff; 1141016f143SNathan Whitehorn lo = value & 0xffffffff; 1151016f143SNathan Whitehorn 1161016f143SNathan Whitehorn msr = mfmsr(); 1171016f143SNathan Whitehorn mtmsr(msr & ~PSL_EE); isync(); 1181016f143SNathan Whitehorn 1191016f143SNathan Whitehorn mtspr64(SPR_SCOMD, hi, lo, scratch); 1201016f143SNathan Whitehorn isync(); 1211016f143SNathan Whitehorn mtspr(SPR_SCOMC, address | SCOMC_WRITE); 1221016f143SNathan Whitehorn isync(); 1231016f143SNathan Whitehorn 1241016f143SNathan Whitehorn mtmsr(msr); isync(); 1251016f143SNathan Whitehorn } 1261016f143SNathan Whitehorn 1271016f143SNathan Whitehorn static uint64_t 1281016f143SNathan Whitehorn read_scom(register_t address) 1291016f143SNathan Whitehorn { 1301016f143SNathan Whitehorn register_t msr; 1311016f143SNathan Whitehorn uint64_t ret; 1321016f143SNathan Whitehorn 1331016f143SNathan Whitehorn msr = mfmsr(); 1341016f143SNathan Whitehorn mtmsr(msr & ~PSL_EE); isync(); 1351016f143SNathan Whitehorn 1361016f143SNathan Whitehorn mtspr(SPR_SCOMC, address | SCOMC_READ); 1371016f143SNathan Whitehorn isync(); 1381016f143SNathan Whitehorn 1391016f143SNathan Whitehorn __asm __volatile ("mfspr %0,%1;" 1401016f143SNathan Whitehorn " mr %0+1, %0; srdi %0,%0,32" : "=r" (ret) : "K" (SPR_SCOMD)); 1411016f143SNathan Whitehorn 1421016f143SNathan Whitehorn (void)mfspr(SPR_SCOMC); /* Complete transcation */ 1431016f143SNathan Whitehorn 1441016f143SNathan Whitehorn mtmsr(msr); isync(); 1451016f143SNathan Whitehorn 1461016f143SNathan Whitehorn return (ret); 1471016f143SNathan Whitehorn } 1481016f143SNathan Whitehorn 1491016f143SNathan Whitehorn static void 1501016f143SNathan Whitehorn pcr_identify(driver_t *driver, device_t parent) 1511016f143SNathan Whitehorn { 1521016f143SNathan Whitehorn uint16_t vers; 1531016f143SNathan Whitehorn vers = mfpvr() >> 16; 1541016f143SNathan Whitehorn 1551016f143SNathan Whitehorn /* Check for an IBM 970-class CPU */ 1561016f143SNathan Whitehorn switch (vers) { 1571016f143SNathan Whitehorn case IBM970FX: 1581016f143SNathan Whitehorn case IBM970GX: 1591016f143SNathan Whitehorn case IBM970MP: 1601016f143SNathan Whitehorn break; 1611016f143SNathan Whitehorn default: 1621016f143SNathan Whitehorn return; 1631016f143SNathan Whitehorn } 1641016f143SNathan Whitehorn 1651016f143SNathan Whitehorn /* Make sure we're not being doubly invoked. */ 1661016f143SNathan Whitehorn if (device_find_child(parent, "pcr", -1) != NULL) 1671016f143SNathan Whitehorn return; 1681016f143SNathan Whitehorn 1691016f143SNathan Whitehorn /* 1701016f143SNathan Whitehorn * We attach a child for every CPU since settings need to 1711016f143SNathan Whitehorn * be performed on every CPU in the SMP case. 1721016f143SNathan Whitehorn */ 1731016f143SNathan Whitehorn if (BUS_ADD_CHILD(parent, 10, "pcr", -1) == NULL) 1741016f143SNathan Whitehorn device_printf(parent, "add pcr child failed\n"); 1751016f143SNathan Whitehorn } 1761016f143SNathan Whitehorn 1771016f143SNathan Whitehorn static int 1781016f143SNathan Whitehorn pcr_probe(device_t dev) 1791016f143SNathan Whitehorn { 1801016f143SNathan Whitehorn if (resource_disabled("pcr", 0)) 1811016f143SNathan Whitehorn return (ENXIO); 1821016f143SNathan Whitehorn 1831016f143SNathan Whitehorn device_set_desc(dev, "PPC 970 Power Control Register"); 1841016f143SNathan Whitehorn return (0); 1851016f143SNathan Whitehorn } 1861016f143SNathan Whitehorn 1871016f143SNathan Whitehorn static int 1881016f143SNathan Whitehorn pcr_attach(device_t dev) 1891016f143SNathan Whitehorn { 1901016f143SNathan Whitehorn struct pcr_softc *sc; 1911016f143SNathan Whitehorn phandle_t cpu; 1921016f143SNathan Whitehorn uint32_t modes[3]; 1931016f143SNathan Whitehorn int i; 1941016f143SNathan Whitehorn 1951016f143SNathan Whitehorn sc = device_get_softc(dev); 1961016f143SNathan Whitehorn sc->dev = dev; 1971016f143SNathan Whitehorn 1981016f143SNathan Whitehorn cpu = ofw_bus_get_node(device_get_parent(dev)); 1991016f143SNathan Whitehorn 2001016f143SNathan Whitehorn if (cpu <= 0) { 2011016f143SNathan Whitehorn device_printf(dev,"No CPU device tree node!\n"); 2021016f143SNathan Whitehorn return (ENXIO); 2031016f143SNathan Whitehorn } 2041016f143SNathan Whitehorn 205*c59bb3ffSNathan Whitehorn if (OF_getproplen(cpu, "power-mode-data") <= 0) { 206*c59bb3ffSNathan Whitehorn /* Use the first CPU's node */ 207*c59bb3ffSNathan Whitehorn cpu = OF_child(OF_parent(cpu)); 208*c59bb3ffSNathan Whitehorn } 209*c59bb3ffSNathan Whitehorn 2101016f143SNathan Whitehorn /* 2111016f143SNathan Whitehorn * Collect the PCR values for each mode from the device tree. 2121016f143SNathan Whitehorn * These include bus timing information, and so cannot be 2131016f143SNathan Whitehorn * directly computed. 2141016f143SNathan Whitehorn */ 2151016f143SNathan Whitehorn sc->nmodes = OF_getproplen(cpu, "power-mode-data"); 2161016f143SNathan Whitehorn if (sc->nmodes <= 0 || sc->nmodes > sizeof(sc->pcr_vals)) { 2171016f143SNathan Whitehorn device_printf(dev,"No power mode data in device tree!\n"); 2181016f143SNathan Whitehorn return (ENXIO); 2191016f143SNathan Whitehorn } 2201016f143SNathan Whitehorn OF_getprop(cpu, "power-mode-data", modes, sc->nmodes); 2211016f143SNathan Whitehorn sc->nmodes /= sizeof(modes[0]); 2221016f143SNathan Whitehorn 2231016f143SNathan Whitehorn /* Sort the modes */ 2241016f143SNathan Whitehorn for (i = 0; i < sc->nmodes; i++) 2251016f143SNathan Whitehorn sc->pcr_vals[PCR_TO_FREQ(modes[i])] = modes[i]; 2261016f143SNathan Whitehorn 2271016f143SNathan Whitehorn cpufreq_register(dev); 2281016f143SNathan Whitehorn return (0); 2291016f143SNathan Whitehorn } 2301016f143SNathan Whitehorn 2311016f143SNathan Whitehorn static int 2321016f143SNathan Whitehorn pcr_settings(device_t dev, struct cf_setting *sets, int *count) 2331016f143SNathan Whitehorn { 2341016f143SNathan Whitehorn struct pcr_softc *sc; 2351016f143SNathan Whitehorn 2361016f143SNathan Whitehorn sc = device_get_softc(dev); 2371016f143SNathan Whitehorn if (sets == NULL || count == NULL) 2381016f143SNathan Whitehorn return (EINVAL); 2391016f143SNathan Whitehorn if (*count < sc->nmodes) 2401016f143SNathan Whitehorn return (E2BIG); 2411016f143SNathan Whitehorn 2421016f143SNathan Whitehorn /* Return a list of valid settings for this driver. */ 2431016f143SNathan Whitehorn memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * sc->nmodes); 2441016f143SNathan Whitehorn 2451016f143SNathan Whitehorn sets[0].freq = 10000; sets[0].dev = dev; 2461016f143SNathan Whitehorn sets[1].freq = 5000; sets[1].dev = dev; 2471016f143SNathan Whitehorn if (sc->nmodes > 2) 2481016f143SNathan Whitehorn sets[2].freq = 2500; sets[2].dev = dev; 2491016f143SNathan Whitehorn *count = sc->nmodes; 2501016f143SNathan Whitehorn 2511016f143SNathan Whitehorn return (0); 2521016f143SNathan Whitehorn } 2531016f143SNathan Whitehorn 2541016f143SNathan Whitehorn static int 2551016f143SNathan Whitehorn pcr_set(device_t dev, const struct cf_setting *set) 2561016f143SNathan Whitehorn { 2571016f143SNathan Whitehorn struct pcr_softc *sc; 2581016f143SNathan Whitehorn register_t pcr, msr; 2591016f143SNathan Whitehorn uint64_t psr; 2601016f143SNathan Whitehorn 2611016f143SNathan Whitehorn if (set == NULL) 2621016f143SNathan Whitehorn return (EINVAL); 2631016f143SNathan Whitehorn sc = device_get_softc(dev); 2641016f143SNathan Whitehorn 2651016f143SNathan Whitehorn /* Construct the new PCR */ 2661016f143SNathan Whitehorn 2671016f143SNathan Whitehorn pcr = SCOM_PCR_BIT; 2681016f143SNathan Whitehorn 2691016f143SNathan Whitehorn if (set->freq == 10000) 2701016f143SNathan Whitehorn pcr |= sc->pcr_vals[0]; 2711016f143SNathan Whitehorn else if (set->freq == 5000) 2721016f143SNathan Whitehorn pcr |= sc->pcr_vals[1]; 2731016f143SNathan Whitehorn else if (set->freq == 2500) 2741016f143SNathan Whitehorn pcr |= sc->pcr_vals[2]; 2751016f143SNathan Whitehorn 2761016f143SNathan Whitehorn msr = mfmsr(); 2771016f143SNathan Whitehorn mtmsr(msr & ~PSL_EE); isync(); 2781016f143SNathan Whitehorn 2791016f143SNathan Whitehorn /* 970MP requires PCR and PCRH to be cleared first */ 2801016f143SNathan Whitehorn 2811016f143SNathan Whitehorn write_scom(SCOM_PCR,0); /* Clear PCRH */ 2821016f143SNathan Whitehorn write_scom(SCOM_PCR,SCOM_PCR_BIT); /* Clear PCR */ 2831016f143SNathan Whitehorn 2841016f143SNathan Whitehorn /* Set PCR */ 2851016f143SNathan Whitehorn 2861016f143SNathan Whitehorn write_scom(SCOM_PCR, pcr); 2871016f143SNathan Whitehorn 2881016f143SNathan Whitehorn /* Wait for completion */ 2891016f143SNathan Whitehorn 2901016f143SNathan Whitehorn do { 2911016f143SNathan Whitehorn DELAY(100); 2921016f143SNathan Whitehorn psr = read_scom(SCOM_PSR); 2931016f143SNathan Whitehorn } while ((psr & PSR_RECEIVED) && !(psr & PSR_COMPLETED)); 2941016f143SNathan Whitehorn 2951016f143SNathan Whitehorn mtmsr(msr); isync(); 2961016f143SNathan Whitehorn 2971016f143SNathan Whitehorn return (0); 2981016f143SNathan Whitehorn } 2991016f143SNathan Whitehorn 3001016f143SNathan Whitehorn static int 3011016f143SNathan Whitehorn pcr_get(device_t dev, struct cf_setting *set) 3021016f143SNathan Whitehorn { 3031016f143SNathan Whitehorn struct pcr_softc *sc; 3041016f143SNathan Whitehorn uint64_t psr; 3051016f143SNathan Whitehorn 3061016f143SNathan Whitehorn if (set == NULL) 3071016f143SNathan Whitehorn return (EINVAL); 3081016f143SNathan Whitehorn sc = device_get_softc(dev); 3091016f143SNathan Whitehorn 3101016f143SNathan Whitehorn memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set)); 3111016f143SNathan Whitehorn 3121016f143SNathan Whitehorn psr = read_scom(SCOM_PSR); 3131016f143SNathan Whitehorn 3141016f143SNathan Whitehorn /* We want bits 6 and 7 */ 3151016f143SNathan Whitehorn psr = (psr >> 56) & 3; 3161016f143SNathan Whitehorn 3171016f143SNathan Whitehorn set->freq = 10000; 3181016f143SNathan Whitehorn if (psr == PCR_HALF) 3191016f143SNathan Whitehorn set->freq = 5000; 3201016f143SNathan Whitehorn else if (psr == PCR_QUARTER) 3211016f143SNathan Whitehorn set->freq = 2500; 3221016f143SNathan Whitehorn 3231016f143SNathan Whitehorn set->dev = dev; 3241016f143SNathan Whitehorn 3251016f143SNathan Whitehorn return (0); 3261016f143SNathan Whitehorn } 3271016f143SNathan Whitehorn 3281016f143SNathan Whitehorn static int 3291016f143SNathan Whitehorn pcr_type(device_t dev, int *type) 3301016f143SNathan Whitehorn { 3311016f143SNathan Whitehorn 3321016f143SNathan Whitehorn if (type == NULL) 3331016f143SNathan Whitehorn return (EINVAL); 3341016f143SNathan Whitehorn 3351016f143SNathan Whitehorn *type = CPUFREQ_TYPE_RELATIVE; 3361016f143SNathan Whitehorn return (0); 3371016f143SNathan Whitehorn } 3381016f143SNathan Whitehorn 339