1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2018 Justin Hibbits 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/bus.h> 32 #include <sys/cpu.h> 33 #include <sys/kernel.h> 34 #include <sys/module.h> 35 36 #include <dev/ofw/openfirm.h> 37 38 #include "cpufreq_if.h" 39 40 static int pstate_ids[256]; 41 static int pstate_freqs[256]; 42 static int npstates; 43 44 static void parse_pstates(void) 45 { 46 phandle_t node; 47 48 node = OF_finddevice("/ibm,opal/power-mgt"); 49 50 /* If this fails, npstates will remain 0, and any attachment will bail. */ 51 if (node == -1) 52 return; 53 54 npstates = OF_getencprop(node, "ibm,pstate-ids", pstate_ids, 55 sizeof(pstate_ids)); 56 if (npstates < 0) { 57 npstates = 0; 58 return; 59 } 60 61 if (OF_getencprop(node, "ibm,pstate-frequencies-mhz", pstate_freqs, 62 sizeof(pstate_freqs)) != npstates) { 63 npstates = 0; 64 return; 65 } 66 npstates /= sizeof(cell_t); 67 68 } 69 70 /* Make this a sysinit so it runs before the cpufreq driver attaches. */ 71 SYSINIT(parse_pstates, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, parse_pstates, NULL); 72 73 #define PMCR_UPPERPS_MASK 0xff00000000000000UL 74 #define PMCR_UPPERPS_SHIFT 56 75 #define PMCR_LOWERPS_MASK 0x00ff000000000000UL 76 #define PMCR_LOWERPS_SHIFT 48 77 #define PMCR_VERSION_MASK 0x0000000f 78 #define PMCR_VERSION_1 1 79 80 struct pmcr_softc { 81 device_t dev; 82 }; 83 84 static void pmcr_identify(driver_t *driver, device_t parent); 85 static int pmcr_probe(device_t dev); 86 static int pmcr_attach(device_t dev); 87 static int pmcr_settings(device_t dev, struct cf_setting *sets, int *count); 88 static int pmcr_set(device_t dev, const struct cf_setting *set); 89 static int pmcr_get(device_t dev, struct cf_setting *set); 90 static int pmcr_type(device_t dev, int *type); 91 92 static device_method_t pmcr_methods[] = { 93 /* Device interface */ 94 DEVMETHOD(device_identify, pmcr_identify), 95 DEVMETHOD(device_probe, pmcr_probe), 96 DEVMETHOD(device_attach, pmcr_attach), 97 98 /* cpufreq interface */ 99 DEVMETHOD(cpufreq_drv_set, pmcr_set), 100 DEVMETHOD(cpufreq_drv_get, pmcr_get), 101 DEVMETHOD(cpufreq_drv_type, pmcr_type), 102 DEVMETHOD(cpufreq_drv_settings, pmcr_settings), 103 {0, 0} 104 }; 105 106 static driver_t pmcr_driver = { 107 "pmcr", 108 pmcr_methods, 109 sizeof(struct pmcr_softc) 110 }; 111 112 DRIVER_MODULE(pmcr, cpu, pmcr_driver, 0, 0); 113 114 static void 115 pmcr_identify(driver_t *driver, device_t parent) 116 { 117 118 /* Make sure we're not being doubly invoked. */ 119 if (device_find_child(parent, "pmcr", -1) != NULL) 120 return; 121 122 /* 123 * We attach a child for every CPU since settings need to 124 * be performed on every CPU in the SMP case. 125 */ 126 if (BUS_ADD_CHILD(parent, 10, "pmcr", -1) == NULL) 127 device_printf(parent, "add pmcr child failed\n"); 128 } 129 130 static int 131 pmcr_probe(device_t dev) 132 { 133 if (resource_disabled("pmcr", 0)) 134 return (ENXIO); 135 136 if (npstates == 0) 137 return (ENXIO); 138 139 device_set_desc(dev, "Power Management Control Register"); 140 return (0); 141 } 142 143 static int 144 pmcr_attach(device_t dev) 145 { 146 struct pmcr_softc *sc; 147 148 sc = device_get_softc(dev); 149 sc->dev = dev; 150 151 cpufreq_register(dev); 152 return (0); 153 } 154 155 static int 156 pmcr_settings(device_t dev, struct cf_setting *sets, int *count) 157 { 158 int i; 159 160 if (sets == NULL || count == NULL) 161 return (EINVAL); 162 if (*count < npstates) 163 return (E2BIG); 164 165 /* Return a list of valid settings for this driver. */ 166 memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * npstates); 167 168 for (i = 0; i < npstates; i++) { 169 sets[i].freq = pstate_freqs[i]; 170 sets[i].spec[0] = pstate_ids[i]; 171 sets[i].spec[1] = i; 172 sets[i].dev = dev; 173 } 174 *count = npstates; 175 176 return (0); 177 } 178 179 static int 180 pmcr_set(device_t dev, const struct cf_setting *set) 181 { 182 register_t pmcr; 183 184 if (set == NULL) 185 return (EINVAL); 186 187 if (set->spec[1] < 0 || set->spec[1] >= npstates) 188 return (EINVAL); 189 190 pmcr = ((long)set->spec[0] << PMCR_LOWERPS_SHIFT) & PMCR_LOWERPS_MASK; 191 pmcr |= ((long)set->spec[0] << PMCR_UPPERPS_SHIFT) & PMCR_UPPERPS_MASK; 192 pmcr |= PMCR_VERSION_1; 193 194 mtspr(SPR_PMCR, pmcr); 195 powerpc_sync(); isync(); 196 197 return (0); 198 } 199 200 static int 201 pmcr_get(device_t dev, struct cf_setting *set) 202 { 203 register_t pmcr; 204 int i, pstate; 205 206 if (set == NULL) 207 return (EINVAL); 208 209 memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set)); 210 211 pmcr = mfspr(SPR_PMCR); 212 213 pstate = (pmcr & PMCR_LOWERPS_MASK) >> PMCR_LOWERPS_SHIFT; 214 215 for (i = 0; i < npstates && pstate_ids[i] != pstate; i++) 216 ; 217 218 if (i == npstates) 219 return (EINVAL); 220 221 set->spec[0] = pstate; 222 set->spec[1] = i; 223 set->freq = pstate_freqs[i]; 224 225 set->dev = dev; 226 227 return (0); 228 } 229 230 static int 231 pmcr_type(device_t dev, int *type) 232 { 233 234 if (type == NULL) 235 return (EINVAL); 236 237 *type = CPUFREQ_TYPE_ABSOLUTE; 238 return (0); 239 } 240