1 1.3 jmcneill /* $NetBSD: acpi_cppc.c,v 1.3 2025/01/31 12:29:19 jmcneill Exp $ */ 2 1.1 jmcneill 3 1.1 jmcneill /*- 4 1.1 jmcneill * Copyright (c) 2020 Jared McNeill <jmcneill (at) invisible.ca> 5 1.1 jmcneill * All rights reserved. 6 1.1 jmcneill * 7 1.1 jmcneill * Redistribution and use in source and binary forms, with or without 8 1.1 jmcneill * modification, are permitted provided that the following conditions 9 1.1 jmcneill * are met: 10 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright 11 1.1 jmcneill * notice, this list of conditions and the following disclaimer. 12 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the 14 1.1 jmcneill * documentation and/or other materials provided with the distribution. 15 1.1 jmcneill * 16 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 jmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 jmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 jmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 jmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 jmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 jmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 jmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 jmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 jmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 jmcneill * POSSIBILITY OF SUCH DAMAGE. 27 1.1 jmcneill */ 28 1.1 jmcneill 29 1.1 jmcneill /* 30 1.1 jmcneill * ACPI Collaborative Processor Performance Control support. 31 1.1 jmcneill */ 32 1.1 jmcneill 33 1.1 jmcneill #include <sys/cdefs.h> 34 1.3 jmcneill __KERNEL_RCSID(0, "$NetBSD: acpi_cppc.c,v 1.3 2025/01/31 12:29:19 jmcneill Exp $"); 35 1.1 jmcneill 36 1.1 jmcneill #include <sys/param.h> 37 1.1 jmcneill #include <sys/bus.h> 38 1.1 jmcneill #include <sys/cpu.h> 39 1.1 jmcneill #include <sys/device.h> 40 1.1 jmcneill #include <sys/kmem.h> 41 1.1 jmcneill #include <sys/sysctl.h> 42 1.1 jmcneill 43 1.1 jmcneill #include <dev/acpi/acpireg.h> 44 1.1 jmcneill #include <dev/acpi/acpivar.h> 45 1.1 jmcneill #include <dev/acpi/acpi_pcc.h> 46 1.1 jmcneill 47 1.1 jmcneill #include <external/bsd/acpica/dist/include/amlresrc.h> 48 1.1 jmcneill 49 1.1 jmcneill /* _CPC package elements */ 50 1.1 jmcneill typedef enum CPCPackageElement { 51 1.1 jmcneill CPCNumEntries, 52 1.1 jmcneill CPCRevision, 53 1.1 jmcneill CPCHighestPerformance, 54 1.1 jmcneill CPCNominalPerformance, 55 1.1 jmcneill CPCLowestNonlinearPerformance, 56 1.1 jmcneill CPCLowestPerformance, 57 1.1 jmcneill CPCGuaranteedPerformanceReg, 58 1.1 jmcneill CPCDesiredPerformanceReg, 59 1.1 jmcneill CPCMinimumPerformanceReg, 60 1.1 jmcneill CPCMaximumPerformanceReg, 61 1.1 jmcneill CPCPerformanceReductionToleranceReg, 62 1.1 jmcneill CPCTimeWindowReg, 63 1.1 jmcneill CPCCounterWraparoundTime, 64 1.1 jmcneill CPCReferencePerformanceCounterReg, 65 1.1 jmcneill CPCDeliveredPerformanceCounterReg, 66 1.1 jmcneill CPCPerformanceLimitedReg, 67 1.1 jmcneill CPCCPPCEnableReg, 68 1.1 jmcneill CPCAutonomousSelectionEnable, 69 1.1 jmcneill CPCAutonomousActivityWindowReg, 70 1.1 jmcneill CPCEnergyPerformancePreferenceReg, 71 1.1 jmcneill CPCReferencePerformance, 72 1.1 jmcneill CPCLowestFrequency, 73 1.1 jmcneill CPCNominalFrequency, 74 1.1 jmcneill } CPCPackageElement; 75 1.1 jmcneill 76 1.1 jmcneill /* PCC command numbers */ 77 1.1 jmcneill #define CPPC_PCC_READ 0x00 78 1.1 jmcneill #define CPPC_PCC_WRITE 0x01 79 1.1 jmcneill 80 1.1 jmcneill struct cppc_softc { 81 1.1 jmcneill device_t sc_dev; 82 1.1 jmcneill struct cpu_info * sc_cpuinfo; 83 1.1 jmcneill ACPI_HANDLE sc_handle; 84 1.1 jmcneill ACPI_OBJECT * sc_cpc; 85 1.1 jmcneill u_int sc_ncpc; 86 1.1 jmcneill 87 1.1 jmcneill char * sc_available; 88 1.1 jmcneill int sc_node_target; 89 1.1 jmcneill int sc_node_current; 90 1.1 jmcneill ACPI_INTEGER sc_max_target; 91 1.1 jmcneill ACPI_INTEGER sc_min_target; 92 1.3 jmcneill u_int sc_freq_range; 93 1.3 jmcneill u_int sc_perf_range; 94 1.1 jmcneill }; 95 1.1 jmcneill 96 1.1 jmcneill static int cppc_match(device_t, cfdata_t, void *); 97 1.1 jmcneill static void cppc_attach(device_t, device_t, void *); 98 1.1 jmcneill 99 1.1 jmcneill static ACPI_STATUS cppc_parse_cpc(struct cppc_softc *); 100 1.1 jmcneill static ACPI_STATUS cppc_cpufreq_init(struct cppc_softc *); 101 1.1 jmcneill static ACPI_STATUS cppc_read(struct cppc_softc *, CPCPackageElement, 102 1.1 jmcneill ACPI_INTEGER *); 103 1.1 jmcneill static ACPI_STATUS cppc_write(struct cppc_softc *, CPCPackageElement, 104 1.1 jmcneill ACPI_INTEGER); 105 1.1 jmcneill 106 1.1 jmcneill CFATTACH_DECL_NEW(acpicppc, sizeof(struct cppc_softc), 107 1.1 jmcneill cppc_match, cppc_attach, NULL, NULL); 108 1.1 jmcneill 109 1.2 thorpej static const struct device_compatible_entry compat_data[] = { 110 1.2 thorpej { .compat = "ACPI0007" }, /* ACPI Processor Device */ 111 1.2 thorpej DEVICE_COMPAT_EOL 112 1.1 jmcneill }; 113 1.1 jmcneill 114 1.1 jmcneill static int 115 1.1 jmcneill cppc_match(device_t parent, cfdata_t cf, void *aux) 116 1.1 jmcneill { 117 1.1 jmcneill struct acpi_attach_args * const aa = aux; 118 1.1 jmcneill ACPI_HANDLE handle; 119 1.1 jmcneill ACPI_STATUS rv; 120 1.1 jmcneill 121 1.2 thorpej if (acpi_compatible_match(aa, compat_data) == 0) 122 1.1 jmcneill return 0; 123 1.1 jmcneill 124 1.1 jmcneill rv = AcpiGetHandle(aa->aa_node->ad_handle, "_CPC", &handle); 125 1.1 jmcneill if (ACPI_FAILURE(rv)) { 126 1.1 jmcneill return 0; 127 1.1 jmcneill } 128 1.1 jmcneill 129 1.1 jmcneill if (acpi_match_cpu_handle(aa->aa_node->ad_handle) == NULL) { 130 1.1 jmcneill return 0; 131 1.1 jmcneill } 132 1.1 jmcneill 133 1.1 jmcneill /* When CPPC and P-states/T-states are both available, prefer CPPC */ 134 1.2 thorpej return ACPI_MATCHSCORE_CID_MAX + 1; 135 1.1 jmcneill } 136 1.1 jmcneill 137 1.1 jmcneill static void 138 1.1 jmcneill cppc_attach(device_t parent, device_t self, void *aux) 139 1.1 jmcneill { 140 1.1 jmcneill struct cppc_softc * const sc = device_private(self); 141 1.1 jmcneill struct acpi_attach_args * const aa = aux; 142 1.1 jmcneill ACPI_HANDLE handle = aa->aa_node->ad_handle; 143 1.1 jmcneill struct cpu_info *ci; 144 1.1 jmcneill ACPI_STATUS rv; 145 1.1 jmcneill 146 1.1 jmcneill ci = acpi_match_cpu_handle(handle); 147 1.1 jmcneill KASSERT(ci != NULL); 148 1.1 jmcneill 149 1.1 jmcneill aprint_naive("\n"); 150 1.1 jmcneill aprint_normal(": Processor Performance Control (%s)\n", cpu_name(ci)); 151 1.1 jmcneill 152 1.1 jmcneill sc->sc_dev = self; 153 1.1 jmcneill sc->sc_cpuinfo = ci; 154 1.1 jmcneill sc->sc_handle = handle; 155 1.1 jmcneill 156 1.1 jmcneill rv = cppc_parse_cpc(sc); 157 1.1 jmcneill if (ACPI_FAILURE(rv)) { 158 1.1 jmcneill aprint_error_dev(self, "failed to parse CPC package: %s\n", 159 1.1 jmcneill AcpiFormatException(rv)); 160 1.1 jmcneill return; 161 1.1 jmcneill } 162 1.1 jmcneill 163 1.1 jmcneill cppc_cpufreq_init(sc); 164 1.1 jmcneill } 165 1.1 jmcneill 166 1.1 jmcneill /* 167 1.1 jmcneill * cppc_parse_cpc -- 168 1.1 jmcneill * 169 1.1 jmcneill * Read and verify the contents of the _CPC package. 170 1.1 jmcneill */ 171 1.1 jmcneill static ACPI_STATUS 172 1.1 jmcneill cppc_parse_cpc(struct cppc_softc *sc) 173 1.1 jmcneill { 174 1.1 jmcneill ACPI_BUFFER buf; 175 1.1 jmcneill ACPI_STATUS rv; 176 1.1 jmcneill 177 1.1 jmcneill buf.Pointer = NULL; 178 1.1 jmcneill buf.Length = ACPI_ALLOCATE_BUFFER; 179 1.1 jmcneill rv = AcpiEvaluateObjectTyped(sc->sc_handle, "_CPC", NULL, &buf, 180 1.1 jmcneill ACPI_TYPE_PACKAGE); 181 1.1 jmcneill if (ACPI_FAILURE(rv)) { 182 1.1 jmcneill return rv; 183 1.1 jmcneill } 184 1.1 jmcneill 185 1.1 jmcneill sc->sc_cpc = (ACPI_OBJECT *)buf.Pointer; 186 1.1 jmcneill if (sc->sc_cpc->Package.Count == 0) { 187 1.1 jmcneill return AE_NOT_EXIST; 188 1.1 jmcneill } 189 1.1 jmcneill if (sc->sc_cpc->Package.Elements[CPCNumEntries].Type != 190 1.1 jmcneill ACPI_TYPE_INTEGER) { 191 1.1 jmcneill return AE_TYPE; 192 1.1 jmcneill } 193 1.1 jmcneill sc->sc_ncpc = 194 1.1 jmcneill sc->sc_cpc->Package.Elements[CPCNumEntries].Integer.Value; 195 1.1 jmcneill 196 1.1 jmcneill return AE_OK; 197 1.1 jmcneill } 198 1.1 jmcneill 199 1.1 jmcneill /* 200 1.3 jmcneill * cppc_perf_to_freq, cppc_freq_to_perf -- 201 1.3 jmcneill * 202 1.3 jmcneill * Convert between abstract performance values and CPU frequencies, 203 1.3 jmcneill * when possible. 204 1.3 jmcneill */ 205 1.3 jmcneill static ACPI_INTEGER 206 1.3 jmcneill cppc_perf_to_freq(struct cppc_softc *sc, ACPI_INTEGER perf) 207 1.3 jmcneill { 208 1.3 jmcneill return howmany(perf * sc->sc_freq_range, sc->sc_perf_range); 209 1.3 jmcneill } 210 1.3 jmcneill static ACPI_INTEGER 211 1.3 jmcneill cppc_freq_to_perf(struct cppc_softc *sc, ACPI_INTEGER freq) 212 1.3 jmcneill { 213 1.3 jmcneill return howmany(freq * sc->sc_perf_range, sc->sc_freq_range); 214 1.3 jmcneill } 215 1.3 jmcneill 216 1.3 jmcneill /* 217 1.1 jmcneill * cppc_cpufreq_sysctl -- 218 1.1 jmcneill * 219 1.1 jmcneill * sysctl helper function for machdep.cpu.cpuN.{target,current} 220 1.1 jmcneill * nodes. 221 1.1 jmcneill */ 222 1.1 jmcneill static int 223 1.1 jmcneill cppc_cpufreq_sysctl(SYSCTLFN_ARGS) 224 1.1 jmcneill { 225 1.1 jmcneill struct cppc_softc * const sc = rnode->sysctl_data; 226 1.1 jmcneill struct sysctlnode node; 227 1.1 jmcneill u_int fq, oldfq = 0; 228 1.1 jmcneill ACPI_INTEGER val; 229 1.1 jmcneill ACPI_STATUS rv; 230 1.1 jmcneill int error; 231 1.1 jmcneill 232 1.1 jmcneill node = *rnode; 233 1.1 jmcneill node.sysctl_data = &fq; 234 1.1 jmcneill 235 1.1 jmcneill if (rnode->sysctl_num == sc->sc_node_target) { 236 1.1 jmcneill rv = cppc_read(sc, CPCDesiredPerformanceReg, &val); 237 1.1 jmcneill } else { 238 1.1 jmcneill /* 239 1.1 jmcneill * XXX We should measure the delivered performance and 240 1.1 jmcneill * report it here. For now, just report the desired 241 1.1 jmcneill * performance level. 242 1.1 jmcneill */ 243 1.1 jmcneill rv = cppc_read(sc, CPCDesiredPerformanceReg, &val); 244 1.1 jmcneill } 245 1.1 jmcneill if (ACPI_FAILURE(rv)) { 246 1.1 jmcneill return EIO; 247 1.1 jmcneill } 248 1.1 jmcneill if (val > UINT32_MAX) { 249 1.1 jmcneill return ERANGE; 250 1.1 jmcneill } 251 1.3 jmcneill fq = (u_int)cppc_perf_to_freq(sc, val); 252 1.1 jmcneill 253 1.1 jmcneill if (rnode->sysctl_num == sc->sc_node_target) { 254 1.1 jmcneill oldfq = fq; 255 1.1 jmcneill } 256 1.1 jmcneill 257 1.1 jmcneill error = sysctl_lookup(SYSCTLFN_CALL(&node)); 258 1.1 jmcneill if (error != 0 || newp == NULL) { 259 1.1 jmcneill return error; 260 1.1 jmcneill } 261 1.1 jmcneill 262 1.1 jmcneill if (fq == oldfq || rnode->sysctl_num != sc->sc_node_target) { 263 1.1 jmcneill return 0; 264 1.1 jmcneill } 265 1.1 jmcneill 266 1.1 jmcneill if (fq < sc->sc_min_target || fq > sc->sc_max_target) { 267 1.1 jmcneill return EINVAL; 268 1.1 jmcneill } 269 1.1 jmcneill 270 1.3 jmcneill rv = cppc_write(sc, CPCDesiredPerformanceReg, 271 1.3 jmcneill cppc_freq_to_perf(sc, fq)); 272 1.1 jmcneill if (ACPI_FAILURE(rv)) { 273 1.1 jmcneill return EIO; 274 1.1 jmcneill } 275 1.1 jmcneill 276 1.1 jmcneill return 0; 277 1.1 jmcneill } 278 1.1 jmcneill 279 1.1 jmcneill /* 280 1.1 jmcneill * cppc_cpufreq_init -- 281 1.1 jmcneill * 282 1.1 jmcneill * Create sysctl machdep.cpu.cpuN.* sysctl tree. 283 1.1 jmcneill */ 284 1.1 jmcneill static ACPI_STATUS 285 1.1 jmcneill cppc_cpufreq_init(struct cppc_softc *sc) 286 1.1 jmcneill { 287 1.1 jmcneill static CPCPackageElement perf_regs[4] = { 288 1.1 jmcneill CPCHighestPerformance, 289 1.1 jmcneill CPCNominalPerformance, 290 1.1 jmcneill CPCLowestNonlinearPerformance, 291 1.1 jmcneill CPCLowestPerformance 292 1.1 jmcneill }; 293 1.3 jmcneill ACPI_INTEGER perf[4], min_freq = 0, nom_freq = 0, last; 294 1.1 jmcneill const struct sysctlnode *node, *cpunode; 295 1.1 jmcneill struct sysctllog *log = NULL; 296 1.1 jmcneill struct cpu_info *ci = sc->sc_cpuinfo; 297 1.1 jmcneill ACPI_STATUS rv; 298 1.1 jmcneill int error, i, n; 299 1.1 jmcneill 300 1.1 jmcneill /* 301 1.3 jmcneill * Read optional nominal and lowest frequencies. These are used, 302 1.3 jmcneill * when present, to scale units for display in the sysctl interface. 303 1.3 jmcneill */ 304 1.3 jmcneill cppc_read(sc, CPCLowestFrequency, &min_freq); 305 1.3 jmcneill cppc_read(sc, CPCNominalFrequency, &nom_freq); 306 1.3 jmcneill /* 307 1.1 jmcneill * Read highest, nominal, lowest nonlinear, and lowest performance 308 1.3 jmcneill * levels. 309 1.3 jmcneill */ 310 1.3 jmcneill for (i = 0, n = 0; i < __arraycount(perf_regs); i++) { 311 1.3 jmcneill rv = cppc_read(sc, perf_regs[i], &perf[i]); 312 1.3 jmcneill if (ACPI_FAILURE(rv)) { 313 1.3 jmcneill return rv; 314 1.3 jmcneill } 315 1.3 jmcneill } 316 1.3 jmcneill if (min_freq && nom_freq) { 317 1.3 jmcneill sc->sc_freq_range = nom_freq - min_freq; 318 1.3 jmcneill sc->sc_perf_range = perf[1] - perf[3]; 319 1.3 jmcneill } else { 320 1.3 jmcneill sc->sc_freq_range = 1; 321 1.3 jmcneill sc->sc_perf_range = 1; 322 1.3 jmcneill } 323 1.3 jmcneill 324 1.3 jmcneill /* 325 1.3 jmcneill * Build a list of performance levels for the 326 1.1 jmcneill * machdep.cpufreq.cpuN.available sysctl. 327 1.1 jmcneill */ 328 1.1 jmcneill sc->sc_available = kmem_zalloc( 329 1.1 jmcneill strlen("########## ") * __arraycount(perf_regs), KM_SLEEP); 330 1.1 jmcneill last = 0; 331 1.1 jmcneill for (i = 0, n = 0; i < __arraycount(perf_regs); i++) { 332 1.1 jmcneill rv = cppc_read(sc, perf_regs[i], &perf[i]); 333 1.1 jmcneill if (ACPI_FAILURE(rv)) { 334 1.1 jmcneill return rv; 335 1.1 jmcneill } 336 1.1 jmcneill if (perf[i] != last) { 337 1.1 jmcneill char buf[12]; 338 1.1 jmcneill snprintf(buf, sizeof(buf), n ? " %u" : "%u", 339 1.3 jmcneill (u_int)cppc_perf_to_freq(sc, perf[i])); 340 1.1 jmcneill strcat(sc->sc_available, buf); 341 1.1 jmcneill last = perf[i]; 342 1.1 jmcneill n++; 343 1.1 jmcneill } 344 1.1 jmcneill } 345 1.3 jmcneill sc->sc_max_target = cppc_perf_to_freq(sc, perf[0]); 346 1.3 jmcneill sc->sc_min_target = cppc_perf_to_freq(sc, perf[3]); 347 1.1 jmcneill 348 1.1 jmcneill error = sysctl_createv(&log, 0, NULL, &node, 349 1.1 jmcneill CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL, 350 1.1 jmcneill NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL); 351 1.1 jmcneill if (error != 0) { 352 1.1 jmcneill goto sysctl_failed; 353 1.1 jmcneill } 354 1.1 jmcneill 355 1.1 jmcneill error = sysctl_createv(&log, 0, &node, &node, 356 1.1 jmcneill 0, CTLTYPE_NODE, "cpufreq", NULL, 357 1.1 jmcneill NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 358 1.1 jmcneill if (error != 0) { 359 1.1 jmcneill goto sysctl_failed; 360 1.1 jmcneill } 361 1.1 jmcneill 362 1.1 jmcneill error = sysctl_createv(&log, 0, &node, &cpunode, 363 1.1 jmcneill 0, CTLTYPE_NODE, cpu_name(ci), NULL, 364 1.1 jmcneill NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 365 1.1 jmcneill if (error != 0) { 366 1.1 jmcneill goto sysctl_failed; 367 1.1 jmcneill } 368 1.1 jmcneill 369 1.1 jmcneill error = sysctl_createv(&log, 0, &cpunode, &node, 370 1.1 jmcneill CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL, 371 1.1 jmcneill cppc_cpufreq_sysctl, 0, (void *)sc, 0, 372 1.1 jmcneill CTL_CREATE, CTL_EOL); 373 1.1 jmcneill if (error != 0) { 374 1.1 jmcneill goto sysctl_failed; 375 1.1 jmcneill } 376 1.1 jmcneill sc->sc_node_target = node->sysctl_num; 377 1.1 jmcneill 378 1.1 jmcneill error = sysctl_createv(&log, 0, &cpunode, &node, 379 1.1 jmcneill CTLFLAG_READONLY, CTLTYPE_INT, "current", NULL, 380 1.1 jmcneill cppc_cpufreq_sysctl, 0, (void *)sc, 0, 381 1.1 jmcneill CTL_CREATE, CTL_EOL); 382 1.1 jmcneill if (error != 0) { 383 1.1 jmcneill goto sysctl_failed; 384 1.1 jmcneill } 385 1.1 jmcneill sc->sc_node_current = node->sysctl_num; 386 1.1 jmcneill 387 1.1 jmcneill error = sysctl_createv(&log, 0, &cpunode, &node, 388 1.1 jmcneill CTLFLAG_READONLY, CTLTYPE_STRING, "available", NULL, 389 1.1 jmcneill NULL, 0, sc->sc_available, 0, 390 1.1 jmcneill CTL_CREATE, CTL_EOL); 391 1.1 jmcneill if (error != 0) { 392 1.1 jmcneill goto sysctl_failed; 393 1.1 jmcneill } 394 1.1 jmcneill 395 1.1 jmcneill return AE_OK; 396 1.1 jmcneill 397 1.1 jmcneill sysctl_failed: 398 1.1 jmcneill aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes: %d\n", 399 1.1 jmcneill error); 400 1.1 jmcneill sysctl_teardown(&log); 401 1.1 jmcneill 402 1.1 jmcneill return AE_ERROR; 403 1.1 jmcneill } 404 1.1 jmcneill 405 1.1 jmcneill /* 406 1.1 jmcneill * cppc_read -- 407 1.1 jmcneill * 408 1.1 jmcneill * Read a value from the CPC package that contains either an integer 409 1.1 jmcneill * or indirect register reference. 410 1.1 jmcneill */ 411 1.1 jmcneill static ACPI_STATUS 412 1.1 jmcneill cppc_read(struct cppc_softc *sc, CPCPackageElement index, ACPI_INTEGER *val) 413 1.1 jmcneill { 414 1.1 jmcneill ACPI_OBJECT *obj; 415 1.1 jmcneill ACPI_GENERIC_ADDRESS addr; 416 1.1 jmcneill ACPI_STATUS rv; 417 1.1 jmcneill 418 1.1 jmcneill if (index >= sc->sc_ncpc) { 419 1.1 jmcneill return AE_NOT_EXIST; 420 1.1 jmcneill } 421 1.1 jmcneill 422 1.1 jmcneill obj = &sc->sc_cpc->Package.Elements[index]; 423 1.1 jmcneill switch (obj->Type) { 424 1.1 jmcneill case ACPI_TYPE_INTEGER: 425 1.1 jmcneill *val = obj->Integer.Value; 426 1.1 jmcneill return AE_OK; 427 1.1 jmcneill 428 1.1 jmcneill case ACPI_TYPE_BUFFER: 429 1.1 jmcneill if (obj->Buffer.Length < 430 1.1 jmcneill sizeof(AML_RESOURCE_GENERIC_REGISTER)) { 431 1.1 jmcneill return AE_TYPE; 432 1.1 jmcneill } 433 1.1 jmcneill memcpy(&addr, obj->Buffer.Pointer + 434 1.1 jmcneill sizeof(AML_RESOURCE_LARGE_HEADER), sizeof(addr)); 435 1.1 jmcneill if (addr.SpaceId == ACPI_ADR_SPACE_PLATFORM_COMM) { 436 1.1 jmcneill rv = pcc_message(&addr, CPPC_PCC_READ, PCC_READ, val); 437 1.1 jmcneill } else { 438 1.1 jmcneill rv = AcpiRead(val, &addr); 439 1.1 jmcneill } 440 1.1 jmcneill return rv; 441 1.1 jmcneill 442 1.1 jmcneill default: 443 1.1 jmcneill return AE_SUPPORT; 444 1.1 jmcneill } 445 1.1 jmcneill } 446 1.1 jmcneill 447 1.1 jmcneill /* 448 1.1 jmcneill * cppc_write -- 449 1.1 jmcneill * 450 1.1 jmcneill * Write a value based on the CPC package to the specified register. 451 1.1 jmcneill */ 452 1.1 jmcneill static ACPI_STATUS 453 1.1 jmcneill cppc_write(struct cppc_softc *sc, CPCPackageElement index, ACPI_INTEGER val) 454 1.1 jmcneill { 455 1.1 jmcneill ACPI_OBJECT *obj; 456 1.1 jmcneill ACPI_GENERIC_ADDRESS addr; 457 1.1 jmcneill ACPI_STATUS rv; 458 1.1 jmcneill 459 1.1 jmcneill if (index >= sc->sc_ncpc) { 460 1.1 jmcneill return AE_NOT_EXIST; 461 1.1 jmcneill } 462 1.1 jmcneill 463 1.1 jmcneill obj = &sc->sc_cpc->Package.Elements[index]; 464 1.1 jmcneill if (obj->Type != ACPI_TYPE_BUFFER || 465 1.1 jmcneill obj->Buffer.Length < sizeof(AML_RESOURCE_GENERIC_REGISTER)) { 466 1.1 jmcneill return AE_TYPE; 467 1.1 jmcneill } 468 1.1 jmcneill 469 1.1 jmcneill memcpy(&addr, obj->Buffer.Pointer + 470 1.1 jmcneill sizeof(AML_RESOURCE_LARGE_HEADER), sizeof(addr)); 471 1.1 jmcneill if (addr.SpaceId == ACPI_ADR_SPACE_PLATFORM_COMM) { 472 1.1 jmcneill rv = pcc_message(&addr, CPPC_PCC_WRITE, PCC_WRITE, &val); 473 1.1 jmcneill } else { 474 1.1 jmcneill rv = AcpiWrite(val, &addr); 475 1.1 jmcneill } 476 1.1 jmcneill 477 1.1 jmcneill return rv; 478 1.1 jmcneill } 479