19eb9db93SNathan Whitehorn /*- 29eb9db93SNathan Whitehorn * Copyright (c) 2009 Nathan Whitehorn 39eb9db93SNathan Whitehorn * All rights reserved. 49eb9db93SNathan Whitehorn * 59eb9db93SNathan Whitehorn * Redistribution and use in source and binary forms, with or without 69eb9db93SNathan Whitehorn * modification, are permitted provided that the following conditions 79eb9db93SNathan Whitehorn * are met: 89eb9db93SNathan Whitehorn * 1. Redistributions of source code must retain the above copyright 99eb9db93SNathan Whitehorn * notice, this list of conditions and the following disclaimer. 109eb9db93SNathan Whitehorn * 2. Redistributions in binary form must reproduce the above copyright 119eb9db93SNathan Whitehorn * notice, this list of conditions and the following disclaimer in the 129eb9db93SNathan Whitehorn * documentation and/or other materials provided with the distribution. 139eb9db93SNathan Whitehorn * 149eb9db93SNathan Whitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 159eb9db93SNathan Whitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 169eb9db93SNathan Whitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 179eb9db93SNathan Whitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 189eb9db93SNathan Whitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 199eb9db93SNathan Whitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 209eb9db93SNathan Whitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 219eb9db93SNathan Whitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 229eb9db93SNathan Whitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 239eb9db93SNathan Whitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 249eb9db93SNathan Whitehorn * SUCH DAMAGE. 259eb9db93SNathan Whitehorn */ 269eb9db93SNathan Whitehorn 279eb9db93SNathan Whitehorn #include <sys/cdefs.h> 289eb9db93SNathan Whitehorn __FBSDID("$FreeBSD$"); 299eb9db93SNathan Whitehorn 309eb9db93SNathan Whitehorn #include <sys/param.h> 319eb9db93SNathan Whitehorn #include <sys/systm.h> 329eb9db93SNathan Whitehorn #include <sys/bus.h> 339eb9db93SNathan Whitehorn #include <sys/cpu.h> 349eb9db93SNathan Whitehorn #include <sys/kernel.h> 359eb9db93SNathan Whitehorn #include <sys/module.h> 369eb9db93SNathan Whitehorn 379eb9db93SNathan Whitehorn #include "cpufreq_if.h" 389eb9db93SNathan Whitehorn 399eb9db93SNathan Whitehorn struct dfs_softc { 409eb9db93SNathan Whitehorn device_t dev; 419eb9db93SNathan Whitehorn int dfs4; 429eb9db93SNathan Whitehorn }; 439eb9db93SNathan Whitehorn 449eb9db93SNathan Whitehorn static void dfs_identify(driver_t *driver, device_t parent); 459eb9db93SNathan Whitehorn static int dfs_probe(device_t dev); 469eb9db93SNathan Whitehorn static int dfs_attach(device_t dev); 479eb9db93SNathan Whitehorn static int dfs_settings(device_t dev, struct cf_setting *sets, int *count); 489eb9db93SNathan Whitehorn static int dfs_set(device_t dev, const struct cf_setting *set); 499eb9db93SNathan Whitehorn static int dfs_get(device_t dev, struct cf_setting *set); 509eb9db93SNathan Whitehorn static int dfs_type(device_t dev, int *type); 519eb9db93SNathan Whitehorn 529eb9db93SNathan Whitehorn static device_method_t dfs_methods[] = { 539eb9db93SNathan Whitehorn /* Device interface */ 549eb9db93SNathan Whitehorn DEVMETHOD(device_identify, dfs_identify), 559eb9db93SNathan Whitehorn DEVMETHOD(device_probe, dfs_probe), 569eb9db93SNathan Whitehorn DEVMETHOD(device_attach, dfs_attach), 579eb9db93SNathan Whitehorn 589eb9db93SNathan Whitehorn /* cpufreq interface */ 599eb9db93SNathan Whitehorn DEVMETHOD(cpufreq_drv_set, dfs_set), 609eb9db93SNathan Whitehorn DEVMETHOD(cpufreq_drv_get, dfs_get), 619eb9db93SNathan Whitehorn DEVMETHOD(cpufreq_drv_type, dfs_type), 629eb9db93SNathan Whitehorn DEVMETHOD(cpufreq_drv_settings, dfs_settings), 639eb9db93SNathan Whitehorn 649eb9db93SNathan Whitehorn {0, 0} 659eb9db93SNathan Whitehorn }; 669eb9db93SNathan Whitehorn 679eb9db93SNathan Whitehorn static driver_t dfs_driver = { 689eb9db93SNathan Whitehorn "dfs", 699eb9db93SNathan Whitehorn dfs_methods, 709eb9db93SNathan Whitehorn sizeof(struct dfs_softc) 719eb9db93SNathan Whitehorn }; 729eb9db93SNathan Whitehorn 739eb9db93SNathan Whitehorn static devclass_t dfs_devclass; 749eb9db93SNathan Whitehorn DRIVER_MODULE(dfs, cpu, dfs_driver, dfs_devclass, 0, 0); 759eb9db93SNathan Whitehorn 769eb9db93SNathan Whitehorn /* 779eb9db93SNathan Whitehorn * Bits of the HID1 register to enable DFS. See page 2-24 of "MPC7450 789eb9db93SNathan Whitehorn * RISC Microprocessor Family Reference Manual", rev. 5. 799eb9db93SNathan Whitehorn */ 809eb9db93SNathan Whitehorn 819eb9db93SNathan Whitehorn #define HID1_DFS2 (1UL << 22) 829eb9db93SNathan Whitehorn #define HID1_DFS4 (1UL << 23) 839eb9db93SNathan Whitehorn 849eb9db93SNathan Whitehorn static void 859eb9db93SNathan Whitehorn dfs_identify(driver_t *driver, device_t parent) 869eb9db93SNathan Whitehorn { 879eb9db93SNathan Whitehorn uint16_t vers; 889eb9db93SNathan Whitehorn vers = mfpvr() >> 16; 899eb9db93SNathan Whitehorn 909eb9db93SNathan Whitehorn /* Check for an MPC 7447A or 7448 CPU */ 919eb9db93SNathan Whitehorn switch (vers) { 929eb9db93SNathan Whitehorn case MPC7447A: 939eb9db93SNathan Whitehorn case MPC7448: 949eb9db93SNathan Whitehorn break; 959eb9db93SNathan Whitehorn default: 969eb9db93SNathan Whitehorn return; 979eb9db93SNathan Whitehorn } 989eb9db93SNathan Whitehorn 999eb9db93SNathan Whitehorn /* Make sure we're not being doubly invoked. */ 1009eb9db93SNathan Whitehorn if (device_find_child(parent, "dfs", -1) != NULL) 1019eb9db93SNathan Whitehorn return; 1029eb9db93SNathan Whitehorn 1039eb9db93SNathan Whitehorn /* 1049eb9db93SNathan Whitehorn * We attach a child for every CPU since settings need to 1059eb9db93SNathan Whitehorn * be performed on every CPU in the SMP case. 1069eb9db93SNathan Whitehorn */ 1079eb9db93SNathan Whitehorn if (BUS_ADD_CHILD(parent, 10, "dfs", -1) == NULL) 1089eb9db93SNathan Whitehorn device_printf(parent, "add dfs child failed\n"); 1099eb9db93SNathan Whitehorn } 1109eb9db93SNathan Whitehorn 1119eb9db93SNathan Whitehorn static int 1129eb9db93SNathan Whitehorn dfs_probe(device_t dev) 1139eb9db93SNathan Whitehorn { 1149eb9db93SNathan Whitehorn if (resource_disabled("dfs", 0)) 1159eb9db93SNathan Whitehorn return (ENXIO); 1169eb9db93SNathan Whitehorn 1179eb9db93SNathan Whitehorn device_set_desc(dev, "Dynamic Frequency Switching"); 1189eb9db93SNathan Whitehorn return (0); 1199eb9db93SNathan Whitehorn } 1209eb9db93SNathan Whitehorn 1219eb9db93SNathan Whitehorn static int 1229eb9db93SNathan Whitehorn dfs_attach(device_t dev) 1239eb9db93SNathan Whitehorn { 1249eb9db93SNathan Whitehorn struct dfs_softc *sc; 1259eb9db93SNathan Whitehorn uint16_t vers; 1269eb9db93SNathan Whitehorn 1279eb9db93SNathan Whitehorn sc = device_get_softc(dev); 1289eb9db93SNathan Whitehorn sc->dev = dev; 1299eb9db93SNathan Whitehorn sc->dfs4 = 0; 1309eb9db93SNathan Whitehorn vers = mfpvr() >> 16; 1319eb9db93SNathan Whitehorn 1329eb9db93SNathan Whitehorn /* The 7448 supports divide-by-four as well */ 1339eb9db93SNathan Whitehorn if (vers == MPC7448) 1349eb9db93SNathan Whitehorn sc->dfs4 = 1; 1359eb9db93SNathan Whitehorn 1369eb9db93SNathan Whitehorn cpufreq_register(dev); 1379eb9db93SNathan Whitehorn return (0); 1389eb9db93SNathan Whitehorn } 1399eb9db93SNathan Whitehorn 1409eb9db93SNathan Whitehorn static int 1419eb9db93SNathan Whitehorn dfs_settings(device_t dev, struct cf_setting *sets, int *count) 1429eb9db93SNathan Whitehorn { 1439eb9db93SNathan Whitehorn struct dfs_softc *sc; 1449eb9db93SNathan Whitehorn int states; 1459eb9db93SNathan Whitehorn 1469eb9db93SNathan Whitehorn sc = device_get_softc(dev); 1479eb9db93SNathan Whitehorn states = sc->dfs4 ? 3 : 2; 1489eb9db93SNathan Whitehorn if (sets == NULL || count == NULL) 1499eb9db93SNathan Whitehorn return (EINVAL); 1509eb9db93SNathan Whitehorn if (*count < states) 1519eb9db93SNathan Whitehorn return (E2BIG); 1529eb9db93SNathan Whitehorn 1539eb9db93SNathan Whitehorn /* Return a list of valid settings for this driver. */ 1549eb9db93SNathan Whitehorn memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * states); 1559eb9db93SNathan Whitehorn 1569eb9db93SNathan Whitehorn sets[0].freq = 10000; sets[0].dev = dev; 1579eb9db93SNathan Whitehorn sets[1].freq = 5000; sets[1].dev = dev; 1589eb9db93SNathan Whitehorn if (sc->dfs4) 1599eb9db93SNathan Whitehorn sets[2].freq = 2500; sets[2].dev = dev; 1609eb9db93SNathan Whitehorn *count = states; 1619eb9db93SNathan Whitehorn 1629eb9db93SNathan Whitehorn return (0); 1639eb9db93SNathan Whitehorn } 1649eb9db93SNathan Whitehorn 1659eb9db93SNathan Whitehorn static int 1669eb9db93SNathan Whitehorn dfs_set(device_t dev, const struct cf_setting *set) 1679eb9db93SNathan Whitehorn { 1689eb9db93SNathan Whitehorn struct dfs_softc *sc; 1699eb9db93SNathan Whitehorn register_t hid1; 1709eb9db93SNathan Whitehorn 1719eb9db93SNathan Whitehorn if (set == NULL) 1729eb9db93SNathan Whitehorn return (EINVAL); 1739eb9db93SNathan Whitehorn sc = device_get_softc(dev); 1749eb9db93SNathan Whitehorn 1759eb9db93SNathan Whitehorn hid1 = mfspr(SPR_HID1); 1769eb9db93SNathan Whitehorn hid1 &= ~(HID1_DFS2 | HID1_DFS4); 1779eb9db93SNathan Whitehorn 1789eb9db93SNathan Whitehorn if (set->freq == 5000) 1799eb9db93SNathan Whitehorn hid1 |= HID1_DFS2; 1809eb9db93SNathan Whitehorn else if (set->freq == 2500) 1819eb9db93SNathan Whitehorn hid1 |= HID1_DFS4; 1829eb9db93SNathan Whitehorn 1839eb9db93SNathan Whitehorn /* 1849eb9db93SNathan Whitehorn * Now set the HID1 register with new values. Calling sequence 1859eb9db93SNathan Whitehorn * taken from page 2-26 of the MPC7450 family CPU manual. 1869eb9db93SNathan Whitehorn */ 1879eb9db93SNathan Whitehorn 1889eb9db93SNathan Whitehorn powerpc_sync(); 1899eb9db93SNathan Whitehorn mtspr(SPR_HID1, hid1); 1909eb9db93SNathan Whitehorn powerpc_sync(); isync(); 1919eb9db93SNathan Whitehorn 1929eb9db93SNathan Whitehorn return (0); 1939eb9db93SNathan Whitehorn } 1949eb9db93SNathan Whitehorn 1959eb9db93SNathan Whitehorn static int 1969eb9db93SNathan Whitehorn dfs_get(device_t dev, struct cf_setting *set) 1979eb9db93SNathan Whitehorn { 1989eb9db93SNathan Whitehorn struct dfs_softc *sc; 1999eb9db93SNathan Whitehorn register_t hid1; 2009eb9db93SNathan Whitehorn 2019eb9db93SNathan Whitehorn if (set == NULL) 2029eb9db93SNathan Whitehorn return (EINVAL); 2039eb9db93SNathan Whitehorn sc = device_get_softc(dev); 2049eb9db93SNathan Whitehorn 2059eb9db93SNathan Whitehorn memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set)); 2069eb9db93SNathan Whitehorn 2079eb9db93SNathan Whitehorn hid1 = mfspr(SPR_HID1); 2089eb9db93SNathan Whitehorn 2099eb9db93SNathan Whitehorn set->freq = 10000; 2109eb9db93SNathan Whitehorn if (hid1 & HID1_DFS2) 2119eb9db93SNathan Whitehorn set->freq = 5000; 2129eb9db93SNathan Whitehorn else if (sc->dfs4 && (hid1 & HID1_DFS4)) 2139eb9db93SNathan Whitehorn set->freq = 2500; 2149eb9db93SNathan Whitehorn 2159eb9db93SNathan Whitehorn set->dev = dev; 2169eb9db93SNathan Whitehorn 2179eb9db93SNathan Whitehorn return (0); 2189eb9db93SNathan Whitehorn } 2199eb9db93SNathan Whitehorn 2209eb9db93SNathan Whitehorn static int 2219eb9db93SNathan Whitehorn dfs_type(device_t dev, int *type) 2229eb9db93SNathan Whitehorn { 2239eb9db93SNathan Whitehorn 2249eb9db93SNathan Whitehorn if (type == NULL) 2259eb9db93SNathan Whitehorn return (EINVAL); 2269eb9db93SNathan Whitehorn 2279eb9db93SNathan Whitehorn *type = CPUFREQ_TYPE_RELATIVE; 2289eb9db93SNathan Whitehorn return (0); 2299eb9db93SNathan Whitehorn } 2309eb9db93SNathan Whitehorn 231