1193156Snwhitehorn/*- 2193156Snwhitehorn * Copyright (c) 2009 Nathan Whitehorn 3193156Snwhitehorn * All rights reserved. 4193156Snwhitehorn * 5193156Snwhitehorn * Redistribution and use in source and binary forms, with or without 6193156Snwhitehorn * modification, are permitted provided that the following conditions 7193156Snwhitehorn * are met: 8193156Snwhitehorn * 1. Redistributions of source code must retain the above copyright 9193156Snwhitehorn * notice, this list of conditions and the following disclaimer. 10193156Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright 11193156Snwhitehorn * notice, this list of conditions and the following disclaimer in the 12193156Snwhitehorn * documentation and/or other materials provided with the distribution. 13193156Snwhitehorn * 14193156Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15193156Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16193156Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17193156Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18193156Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19193156Snwhitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20193156Snwhitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21193156Snwhitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22193156Snwhitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23193156Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24193156Snwhitehorn * SUCH DAMAGE. 25193156Snwhitehorn */ 26193156Snwhitehorn 27193156Snwhitehorn#include <sys/cdefs.h> 28193156Snwhitehorn__FBSDID("$FreeBSD$"); 29193156Snwhitehorn 30193156Snwhitehorn#include <sys/param.h> 31193156Snwhitehorn#include <sys/systm.h> 32193156Snwhitehorn#include <sys/bus.h> 33193156Snwhitehorn#include <sys/cpu.h> 34193156Snwhitehorn#include <sys/kernel.h> 35193156Snwhitehorn#include <sys/module.h> 36193156Snwhitehorn 37193156Snwhitehorn#include "cpufreq_if.h" 38193156Snwhitehorn 39193156Snwhitehornstruct dfs_softc { 40193156Snwhitehorn device_t dev; 41193156Snwhitehorn int dfs4; 42193156Snwhitehorn}; 43193156Snwhitehorn 44193156Snwhitehornstatic void dfs_identify(driver_t *driver, device_t parent); 45193156Snwhitehornstatic int dfs_probe(device_t dev); 46193156Snwhitehornstatic int dfs_attach(device_t dev); 47193156Snwhitehornstatic int dfs_settings(device_t dev, struct cf_setting *sets, int *count); 48193156Snwhitehornstatic int dfs_set(device_t dev, const struct cf_setting *set); 49193156Snwhitehornstatic int dfs_get(device_t dev, struct cf_setting *set); 50193156Snwhitehornstatic int dfs_type(device_t dev, int *type); 51193156Snwhitehorn 52193156Snwhitehornstatic device_method_t dfs_methods[] = { 53193156Snwhitehorn /* Device interface */ 54193156Snwhitehorn DEVMETHOD(device_identify, dfs_identify), 55193156Snwhitehorn DEVMETHOD(device_probe, dfs_probe), 56193156Snwhitehorn DEVMETHOD(device_attach, dfs_attach), 57193156Snwhitehorn 58193156Snwhitehorn /* cpufreq interface */ 59193156Snwhitehorn DEVMETHOD(cpufreq_drv_set, dfs_set), 60193156Snwhitehorn DEVMETHOD(cpufreq_drv_get, dfs_get), 61193156Snwhitehorn DEVMETHOD(cpufreq_drv_type, dfs_type), 62193156Snwhitehorn DEVMETHOD(cpufreq_drv_settings, dfs_settings), 63193156Snwhitehorn 64193156Snwhitehorn {0, 0} 65193156Snwhitehorn}; 66193156Snwhitehorn 67193156Snwhitehornstatic driver_t dfs_driver = { 68193156Snwhitehorn "dfs", 69193156Snwhitehorn dfs_methods, 70193156Snwhitehorn sizeof(struct dfs_softc) 71193156Snwhitehorn}; 72193156Snwhitehorn 73193156Snwhitehornstatic devclass_t dfs_devclass; 74193156SnwhitehornDRIVER_MODULE(dfs, cpu, dfs_driver, dfs_devclass, 0, 0); 75193156Snwhitehorn 76193156Snwhitehorn/* 77193156Snwhitehorn * Bits of the HID1 register to enable DFS. See page 2-24 of "MPC7450 78193156Snwhitehorn * RISC Microprocessor Family Reference Manual", rev. 5. 79193156Snwhitehorn */ 80193156Snwhitehorn 81193156Snwhitehorn#define HID1_DFS2 (1UL << 22) 82193156Snwhitehorn#define HID1_DFS4 (1UL << 23) 83193156Snwhitehorn 84193156Snwhitehornstatic void 85193156Snwhitehorndfs_identify(driver_t *driver, device_t parent) 86193156Snwhitehorn{ 87193156Snwhitehorn uint16_t vers; 88193156Snwhitehorn vers = mfpvr() >> 16; 89193156Snwhitehorn 90193156Snwhitehorn /* Check for an MPC 7447A or 7448 CPU */ 91193156Snwhitehorn switch (vers) { 92193156Snwhitehorn case MPC7447A: 93193156Snwhitehorn case MPC7448: 94193156Snwhitehorn break; 95193156Snwhitehorn default: 96193156Snwhitehorn return; 97193156Snwhitehorn } 98193156Snwhitehorn 99193156Snwhitehorn /* Make sure we're not being doubly invoked. */ 100193156Snwhitehorn if (device_find_child(parent, "dfs", -1) != NULL) 101193156Snwhitehorn return; 102193156Snwhitehorn 103193156Snwhitehorn /* 104193156Snwhitehorn * We attach a child for every CPU since settings need to 105193156Snwhitehorn * be performed on every CPU in the SMP case. 106193156Snwhitehorn */ 107193156Snwhitehorn if (BUS_ADD_CHILD(parent, 10, "dfs", -1) == NULL) 108193156Snwhitehorn device_printf(parent, "add dfs child failed\n"); 109193156Snwhitehorn} 110193156Snwhitehorn 111193156Snwhitehornstatic int 112193156Snwhitehorndfs_probe(device_t dev) 113193156Snwhitehorn{ 114193156Snwhitehorn if (resource_disabled("dfs", 0)) 115193156Snwhitehorn return (ENXIO); 116193156Snwhitehorn 117193156Snwhitehorn device_set_desc(dev, "Dynamic Frequency Switching"); 118193156Snwhitehorn return (0); 119193156Snwhitehorn} 120193156Snwhitehorn 121193156Snwhitehornstatic int 122193156Snwhitehorndfs_attach(device_t dev) 123193156Snwhitehorn{ 124193156Snwhitehorn struct dfs_softc *sc; 125193156Snwhitehorn uint16_t vers; 126193156Snwhitehorn 127193156Snwhitehorn sc = device_get_softc(dev); 128193156Snwhitehorn sc->dev = dev; 129193156Snwhitehorn sc->dfs4 = 0; 130193156Snwhitehorn vers = mfpvr() >> 16; 131193156Snwhitehorn 132193156Snwhitehorn /* The 7448 supports divide-by-four as well */ 133193156Snwhitehorn if (vers == MPC7448) 134193156Snwhitehorn sc->dfs4 = 1; 135193156Snwhitehorn 136193156Snwhitehorn cpufreq_register(dev); 137193156Snwhitehorn return (0); 138193156Snwhitehorn} 139193156Snwhitehorn 140193156Snwhitehornstatic int 141193156Snwhitehorndfs_settings(device_t dev, struct cf_setting *sets, int *count) 142193156Snwhitehorn{ 143193156Snwhitehorn struct dfs_softc *sc; 144193156Snwhitehorn int states; 145193156Snwhitehorn 146193156Snwhitehorn sc = device_get_softc(dev); 147193156Snwhitehorn states = sc->dfs4 ? 3 : 2; 148193156Snwhitehorn if (sets == NULL || count == NULL) 149193156Snwhitehorn return (EINVAL); 150193156Snwhitehorn if (*count < states) 151193156Snwhitehorn return (E2BIG); 152193156Snwhitehorn 153193156Snwhitehorn /* Return a list of valid settings for this driver. */ 154193156Snwhitehorn memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * states); 155193156Snwhitehorn 156193156Snwhitehorn sets[0].freq = 10000; sets[0].dev = dev; 157193156Snwhitehorn sets[1].freq = 5000; sets[1].dev = dev; 158193156Snwhitehorn if (sc->dfs4) 159193156Snwhitehorn sets[2].freq = 2500; sets[2].dev = dev; 160193156Snwhitehorn *count = states; 161193156Snwhitehorn 162193156Snwhitehorn return (0); 163193156Snwhitehorn} 164193156Snwhitehorn 165193156Snwhitehornstatic int 166193156Snwhitehorndfs_set(device_t dev, const struct cf_setting *set) 167193156Snwhitehorn{ 168193156Snwhitehorn register_t hid1; 169193156Snwhitehorn 170193156Snwhitehorn if (set == NULL) 171193156Snwhitehorn return (EINVAL); 172193156Snwhitehorn 173193156Snwhitehorn hid1 = mfspr(SPR_HID1); 174193156Snwhitehorn hid1 &= ~(HID1_DFS2 | HID1_DFS4); 175193156Snwhitehorn 176193156Snwhitehorn if (set->freq == 5000) 177193156Snwhitehorn hid1 |= HID1_DFS2; 178193156Snwhitehorn else if (set->freq == 2500) 179193156Snwhitehorn hid1 |= HID1_DFS4; 180193156Snwhitehorn 181193156Snwhitehorn /* 182193156Snwhitehorn * Now set the HID1 register with new values. Calling sequence 183193156Snwhitehorn * taken from page 2-26 of the MPC7450 family CPU manual. 184193156Snwhitehorn */ 185193156Snwhitehorn 186193156Snwhitehorn powerpc_sync(); 187193156Snwhitehorn mtspr(SPR_HID1, hid1); 188193156Snwhitehorn powerpc_sync(); isync(); 189193156Snwhitehorn 190193156Snwhitehorn return (0); 191193156Snwhitehorn} 192193156Snwhitehorn 193193156Snwhitehornstatic int 194193156Snwhitehorndfs_get(device_t dev, struct cf_setting *set) 195193156Snwhitehorn{ 196193156Snwhitehorn struct dfs_softc *sc; 197193156Snwhitehorn register_t hid1; 198193156Snwhitehorn 199193156Snwhitehorn if (set == NULL) 200193156Snwhitehorn return (EINVAL); 201193156Snwhitehorn sc = device_get_softc(dev); 202193156Snwhitehorn 203193156Snwhitehorn memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set)); 204193156Snwhitehorn 205193156Snwhitehorn hid1 = mfspr(SPR_HID1); 206193156Snwhitehorn 207193156Snwhitehorn set->freq = 10000; 208193156Snwhitehorn if (hid1 & HID1_DFS2) 209193156Snwhitehorn set->freq = 5000; 210193156Snwhitehorn else if (sc->dfs4 && (hid1 & HID1_DFS4)) 211193156Snwhitehorn set->freq = 2500; 212193156Snwhitehorn 213193156Snwhitehorn set->dev = dev; 214193156Snwhitehorn 215193156Snwhitehorn return (0); 216193156Snwhitehorn} 217193156Snwhitehorn 218193156Snwhitehornstatic int 219193156Snwhitehorndfs_type(device_t dev, int *type) 220193156Snwhitehorn{ 221193156Snwhitehorn 222193156Snwhitehorn if (type == NULL) 223193156Snwhitehorn return (EINVAL); 224193156Snwhitehorn 225193156Snwhitehorn *type = CPUFREQ_TYPE_RELATIVE; 226193156Snwhitehorn return (0); 227193156Snwhitehorn} 228193156Snwhitehorn 229