Home | History | Annotate | Line # | Download | only in acpi
acpi_cppc.c revision 1.1
      1  1.1  jmcneill /* $NetBSD: acpi_cppc.c,v 1.1 2020/12/13 20:39:20 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.1  jmcneill __KERNEL_RCSID(0, "$NetBSD: acpi_cppc.c,v 1.1 2020/12/13 20:39:20 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.1  jmcneill };
     93  1.1  jmcneill 
     94  1.1  jmcneill static int		cppc_match(device_t, cfdata_t, void *);
     95  1.1  jmcneill static void		cppc_attach(device_t, device_t, void *);
     96  1.1  jmcneill 
     97  1.1  jmcneill static ACPI_STATUS 	cppc_parse_cpc(struct cppc_softc *);
     98  1.1  jmcneill static ACPI_STATUS 	cppc_cpufreq_init(struct cppc_softc *);
     99  1.1  jmcneill static ACPI_STATUS	cppc_read(struct cppc_softc *, CPCPackageElement,
    100  1.1  jmcneill 				  ACPI_INTEGER *);
    101  1.1  jmcneill static ACPI_STATUS	cppc_write(struct cppc_softc *, CPCPackageElement,
    102  1.1  jmcneill 				   ACPI_INTEGER);
    103  1.1  jmcneill 
    104  1.1  jmcneill CFATTACH_DECL_NEW(acpicppc, sizeof(struct cppc_softc),
    105  1.1  jmcneill     cppc_match, cppc_attach, NULL, NULL);
    106  1.1  jmcneill 
    107  1.1  jmcneill static const char * const compatible[] = {
    108  1.1  jmcneill 	"ACPI0007",	/* ACPI Processor Device */
    109  1.1  jmcneill 	NULL
    110  1.1  jmcneill };
    111  1.1  jmcneill 
    112  1.1  jmcneill static int
    113  1.1  jmcneill cppc_match(device_t parent, cfdata_t cf, void *aux)
    114  1.1  jmcneill {
    115  1.1  jmcneill 	struct acpi_attach_args * const aa = aux;
    116  1.1  jmcneill 	ACPI_HANDLE handle;
    117  1.1  jmcneill 	ACPI_STATUS rv;
    118  1.1  jmcneill 
    119  1.1  jmcneill 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) {
    120  1.1  jmcneill 		return 0;
    121  1.1  jmcneill 	}
    122  1.1  jmcneill 
    123  1.1  jmcneill 	if (acpi_match_hid(aa->aa_node->ad_devinfo, compatible) == 0) {
    124  1.1  jmcneill 		return 0;
    125  1.1  jmcneill 	}
    126  1.1  jmcneill 
    127  1.1  jmcneill 	rv = AcpiGetHandle(aa->aa_node->ad_handle, "_CPC", &handle);
    128  1.1  jmcneill 	if (ACPI_FAILURE(rv)) {
    129  1.1  jmcneill 		return 0;
    130  1.1  jmcneill 	}
    131  1.1  jmcneill 
    132  1.1  jmcneill 	if (acpi_match_cpu_handle(aa->aa_node->ad_handle) == NULL) {
    133  1.1  jmcneill 		return 0;
    134  1.1  jmcneill 	}
    135  1.1  jmcneill 
    136  1.1  jmcneill 	/* When CPPC and P-states/T-states are both available, prefer CPPC */
    137  1.1  jmcneill 	return 20;
    138  1.1  jmcneill }
    139  1.1  jmcneill 
    140  1.1  jmcneill static void
    141  1.1  jmcneill cppc_attach(device_t parent, device_t self, void *aux)
    142  1.1  jmcneill {
    143  1.1  jmcneill 	struct cppc_softc * const sc = device_private(self);
    144  1.1  jmcneill 	struct acpi_attach_args * const aa = aux;
    145  1.1  jmcneill 	ACPI_HANDLE handle = aa->aa_node->ad_handle;
    146  1.1  jmcneill 	struct cpu_info *ci;
    147  1.1  jmcneill 	ACPI_STATUS rv;
    148  1.1  jmcneill 
    149  1.1  jmcneill 	ci = acpi_match_cpu_handle(handle);
    150  1.1  jmcneill 	KASSERT(ci != NULL);
    151  1.1  jmcneill 
    152  1.1  jmcneill 	aprint_naive("\n");
    153  1.1  jmcneill 	aprint_normal(": Processor Performance Control (%s)\n", cpu_name(ci));
    154  1.1  jmcneill 
    155  1.1  jmcneill 	sc->sc_dev = self;
    156  1.1  jmcneill 	sc->sc_cpuinfo = ci;
    157  1.1  jmcneill 	sc->sc_handle = handle;
    158  1.1  jmcneill 
    159  1.1  jmcneill 	rv = cppc_parse_cpc(sc);
    160  1.1  jmcneill 	if (ACPI_FAILURE(rv)) {
    161  1.1  jmcneill 		aprint_error_dev(self, "failed to parse CPC package: %s\n",
    162  1.1  jmcneill 		    AcpiFormatException(rv));
    163  1.1  jmcneill 		return;
    164  1.1  jmcneill 	}
    165  1.1  jmcneill 
    166  1.1  jmcneill 	cppc_cpufreq_init(sc);
    167  1.1  jmcneill }
    168  1.1  jmcneill 
    169  1.1  jmcneill /*
    170  1.1  jmcneill  * cppc_parse_cpc --
    171  1.1  jmcneill  *
    172  1.1  jmcneill  *	Read and verify the contents of the _CPC package.
    173  1.1  jmcneill  */
    174  1.1  jmcneill static ACPI_STATUS
    175  1.1  jmcneill cppc_parse_cpc(struct cppc_softc *sc)
    176  1.1  jmcneill {
    177  1.1  jmcneill 	ACPI_BUFFER buf;
    178  1.1  jmcneill 	ACPI_STATUS rv;
    179  1.1  jmcneill 
    180  1.1  jmcneill 	buf.Pointer = NULL;
    181  1.1  jmcneill 	buf.Length = ACPI_ALLOCATE_BUFFER;
    182  1.1  jmcneill 	rv = AcpiEvaluateObjectTyped(sc->sc_handle, "_CPC", NULL, &buf,
    183  1.1  jmcneill 	    ACPI_TYPE_PACKAGE);
    184  1.1  jmcneill 	if (ACPI_FAILURE(rv)) {
    185  1.1  jmcneill 		return rv;
    186  1.1  jmcneill 	}
    187  1.1  jmcneill 
    188  1.1  jmcneill 	sc->sc_cpc = (ACPI_OBJECT *)buf.Pointer;
    189  1.1  jmcneill 	if (sc->sc_cpc->Package.Count == 0) {
    190  1.1  jmcneill 		return AE_NOT_EXIST;
    191  1.1  jmcneill 	}
    192  1.1  jmcneill 	if (sc->sc_cpc->Package.Elements[CPCNumEntries].Type !=
    193  1.1  jmcneill 	    ACPI_TYPE_INTEGER) {
    194  1.1  jmcneill 		return AE_TYPE;
    195  1.1  jmcneill 	}
    196  1.1  jmcneill 	sc->sc_ncpc =
    197  1.1  jmcneill 	    sc->sc_cpc->Package.Elements[CPCNumEntries].Integer.Value;
    198  1.1  jmcneill 
    199  1.1  jmcneill 	return AE_OK;
    200  1.1  jmcneill }
    201  1.1  jmcneill 
    202  1.1  jmcneill /*
    203  1.1  jmcneill  * cppc_cpufreq_sysctl --
    204  1.1  jmcneill  *
    205  1.1  jmcneill  *	sysctl helper function for machdep.cpu.cpuN.{target,current}
    206  1.1  jmcneill  *	nodes.
    207  1.1  jmcneill  */
    208  1.1  jmcneill static int
    209  1.1  jmcneill cppc_cpufreq_sysctl(SYSCTLFN_ARGS)
    210  1.1  jmcneill {
    211  1.1  jmcneill 	struct cppc_softc * const sc = rnode->sysctl_data;
    212  1.1  jmcneill 	struct sysctlnode node;
    213  1.1  jmcneill 	u_int fq, oldfq = 0;
    214  1.1  jmcneill 	ACPI_INTEGER val;
    215  1.1  jmcneill 	ACPI_STATUS rv;
    216  1.1  jmcneill 	int error;
    217  1.1  jmcneill 
    218  1.1  jmcneill 	node = *rnode;
    219  1.1  jmcneill 	node.sysctl_data = &fq;
    220  1.1  jmcneill 
    221  1.1  jmcneill 	if (rnode->sysctl_num == sc->sc_node_target) {
    222  1.1  jmcneill 		rv = cppc_read(sc, CPCDesiredPerformanceReg, &val);
    223  1.1  jmcneill 	} else {
    224  1.1  jmcneill 		/*
    225  1.1  jmcneill 		 * XXX We should measure the delivered performance and
    226  1.1  jmcneill 		 *     report it here. For now, just report the desired
    227  1.1  jmcneill 		 *     performance level.
    228  1.1  jmcneill 		 */
    229  1.1  jmcneill 		rv = cppc_read(sc, CPCDesiredPerformanceReg, &val);
    230  1.1  jmcneill 	}
    231  1.1  jmcneill 	if (ACPI_FAILURE(rv)) {
    232  1.1  jmcneill 		return EIO;
    233  1.1  jmcneill 	}
    234  1.1  jmcneill 	if (val > UINT32_MAX) {
    235  1.1  jmcneill 		return ERANGE;
    236  1.1  jmcneill 	}
    237  1.1  jmcneill 	fq = (u_int)val;
    238  1.1  jmcneill 
    239  1.1  jmcneill 	if (rnode->sysctl_num == sc->sc_node_target) {
    240  1.1  jmcneill 		oldfq = fq;
    241  1.1  jmcneill 	}
    242  1.1  jmcneill 
    243  1.1  jmcneill 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    244  1.1  jmcneill 	if (error != 0 || newp == NULL) {
    245  1.1  jmcneill 		return error;
    246  1.1  jmcneill 	}
    247  1.1  jmcneill 
    248  1.1  jmcneill 	if (fq == oldfq || rnode->sysctl_num != sc->sc_node_target) {
    249  1.1  jmcneill 		return 0;
    250  1.1  jmcneill 	}
    251  1.1  jmcneill 
    252  1.1  jmcneill 	if (fq < sc->sc_min_target || fq > sc->sc_max_target) {
    253  1.1  jmcneill 		return EINVAL;
    254  1.1  jmcneill 	}
    255  1.1  jmcneill 
    256  1.1  jmcneill 	rv = cppc_write(sc, CPCDesiredPerformanceReg, fq);
    257  1.1  jmcneill 	if (ACPI_FAILURE(rv)) {
    258  1.1  jmcneill 		return EIO;
    259  1.1  jmcneill 	}
    260  1.1  jmcneill 
    261  1.1  jmcneill 	return 0;
    262  1.1  jmcneill }
    263  1.1  jmcneill 
    264  1.1  jmcneill /*
    265  1.1  jmcneill  * cppc_cpufreq_init --
    266  1.1  jmcneill  *
    267  1.1  jmcneill  *	Create sysctl machdep.cpu.cpuN.* sysctl tree.
    268  1.1  jmcneill  */
    269  1.1  jmcneill static ACPI_STATUS
    270  1.1  jmcneill cppc_cpufreq_init(struct cppc_softc *sc)
    271  1.1  jmcneill {
    272  1.1  jmcneill 	static CPCPackageElement perf_regs[4] = {
    273  1.1  jmcneill 		CPCHighestPerformance,
    274  1.1  jmcneill 		CPCNominalPerformance,
    275  1.1  jmcneill 		CPCLowestNonlinearPerformance,
    276  1.1  jmcneill 		CPCLowestPerformance
    277  1.1  jmcneill 	};
    278  1.1  jmcneill 	ACPI_INTEGER perf[4], last;
    279  1.1  jmcneill 	const struct sysctlnode *node, *cpunode;
    280  1.1  jmcneill 	struct sysctllog *log = NULL;
    281  1.1  jmcneill 	struct cpu_info *ci = sc->sc_cpuinfo;
    282  1.1  jmcneill 	ACPI_STATUS rv;
    283  1.1  jmcneill 	int error, i, n;
    284  1.1  jmcneill 
    285  1.1  jmcneill 	/*
    286  1.1  jmcneill 	 * Read highest, nominal, lowest nonlinear, and lowest performance
    287  1.1  jmcneill 	 * levels and advertise this list of performance levels in the
    288  1.1  jmcneill 	 * machdep.cpufreq.cpuN.available sysctl.
    289  1.1  jmcneill 	 */
    290  1.1  jmcneill 	sc->sc_available = kmem_zalloc(
    291  1.1  jmcneill 	    strlen("########## ") * __arraycount(perf_regs), KM_SLEEP);
    292  1.1  jmcneill 	last = 0;
    293  1.1  jmcneill 	for (i = 0, n = 0; i < __arraycount(perf_regs); i++) {
    294  1.1  jmcneill 		rv = cppc_read(sc, perf_regs[i], &perf[i]);
    295  1.1  jmcneill 		if (ACPI_FAILURE(rv)) {
    296  1.1  jmcneill 			return rv;
    297  1.1  jmcneill 		}
    298  1.1  jmcneill 		if (perf[i] != last) {
    299  1.1  jmcneill 			char buf[12];
    300  1.1  jmcneill 			snprintf(buf, sizeof(buf), n ? " %u" : "%u",
    301  1.1  jmcneill 			    (u_int)perf[i]);
    302  1.1  jmcneill 			strcat(sc->sc_available, buf);
    303  1.1  jmcneill 			last = perf[i];
    304  1.1  jmcneill 			n++;
    305  1.1  jmcneill 		}
    306  1.1  jmcneill 	}
    307  1.1  jmcneill 	sc->sc_max_target = perf[0];
    308  1.1  jmcneill 	sc->sc_min_target = perf[3];
    309  1.1  jmcneill 
    310  1.1  jmcneill 	error = sysctl_createv(&log, 0, NULL, &node,
    311  1.1  jmcneill 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
    312  1.1  jmcneill 	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
    313  1.1  jmcneill 	if (error != 0) {
    314  1.1  jmcneill 		goto sysctl_failed;
    315  1.1  jmcneill 	}
    316  1.1  jmcneill 
    317  1.1  jmcneill 	error = sysctl_createv(&log, 0, &node, &node,
    318  1.1  jmcneill 	    0, CTLTYPE_NODE, "cpufreq", NULL,
    319  1.1  jmcneill 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
    320  1.1  jmcneill 	if (error != 0) {
    321  1.1  jmcneill 		goto sysctl_failed;
    322  1.1  jmcneill 	}
    323  1.1  jmcneill 
    324  1.1  jmcneill 	error = sysctl_createv(&log, 0, &node, &cpunode,
    325  1.1  jmcneill 	    0, CTLTYPE_NODE, cpu_name(ci), NULL,
    326  1.1  jmcneill 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
    327  1.1  jmcneill 	if (error != 0) {
    328  1.1  jmcneill 		goto sysctl_failed;
    329  1.1  jmcneill 	}
    330  1.1  jmcneill 
    331  1.1  jmcneill 	error = sysctl_createv(&log, 0, &cpunode, &node,
    332  1.1  jmcneill 	    CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
    333  1.1  jmcneill 	    cppc_cpufreq_sysctl, 0, (void *)sc, 0,
    334  1.1  jmcneill 	    CTL_CREATE, CTL_EOL);
    335  1.1  jmcneill 	if (error != 0) {
    336  1.1  jmcneill 		goto sysctl_failed;
    337  1.1  jmcneill 	}
    338  1.1  jmcneill 	sc->sc_node_target = node->sysctl_num;
    339  1.1  jmcneill 
    340  1.1  jmcneill 	error = sysctl_createv(&log, 0, &cpunode, &node,
    341  1.1  jmcneill 	    CTLFLAG_READONLY, CTLTYPE_INT, "current", NULL,
    342  1.1  jmcneill 	    cppc_cpufreq_sysctl, 0, (void *)sc, 0,
    343  1.1  jmcneill 	    CTL_CREATE, CTL_EOL);
    344  1.1  jmcneill 	if (error != 0) {
    345  1.1  jmcneill 		goto sysctl_failed;
    346  1.1  jmcneill 	}
    347  1.1  jmcneill 	sc->sc_node_current = node->sysctl_num;
    348  1.1  jmcneill 
    349  1.1  jmcneill 	error = sysctl_createv(&log, 0, &cpunode, &node,
    350  1.1  jmcneill 	    CTLFLAG_READONLY, CTLTYPE_STRING, "available", NULL,
    351  1.1  jmcneill 	    NULL, 0, sc->sc_available, 0,
    352  1.1  jmcneill 	    CTL_CREATE, CTL_EOL);
    353  1.1  jmcneill 	if (error != 0) {
    354  1.1  jmcneill 		goto sysctl_failed;
    355  1.1  jmcneill 	}
    356  1.1  jmcneill 
    357  1.1  jmcneill 	return AE_OK;
    358  1.1  jmcneill 
    359  1.1  jmcneill sysctl_failed:
    360  1.1  jmcneill 	aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes: %d\n",
    361  1.1  jmcneill 	    error);
    362  1.1  jmcneill 	sysctl_teardown(&log);
    363  1.1  jmcneill 
    364  1.1  jmcneill 	return AE_ERROR;
    365  1.1  jmcneill }
    366  1.1  jmcneill 
    367  1.1  jmcneill /*
    368  1.1  jmcneill  * cppc_read --
    369  1.1  jmcneill  *
    370  1.1  jmcneill  *	Read a value from the CPC package that contains either an integer
    371  1.1  jmcneill  *	or indirect register reference.
    372  1.1  jmcneill  */
    373  1.1  jmcneill static ACPI_STATUS
    374  1.1  jmcneill cppc_read(struct cppc_softc *sc, CPCPackageElement index, ACPI_INTEGER *val)
    375  1.1  jmcneill {
    376  1.1  jmcneill 	ACPI_OBJECT *obj;
    377  1.1  jmcneill 	ACPI_GENERIC_ADDRESS addr;
    378  1.1  jmcneill 	ACPI_STATUS rv;
    379  1.1  jmcneill 
    380  1.1  jmcneill 	if (index >= sc->sc_ncpc) {
    381  1.1  jmcneill 		return AE_NOT_EXIST;
    382  1.1  jmcneill 	}
    383  1.1  jmcneill 
    384  1.1  jmcneill 	obj = &sc->sc_cpc->Package.Elements[index];
    385  1.1  jmcneill 	switch (obj->Type) {
    386  1.1  jmcneill 	case ACPI_TYPE_INTEGER:
    387  1.1  jmcneill 		*val = obj->Integer.Value;
    388  1.1  jmcneill 		return AE_OK;
    389  1.1  jmcneill 
    390  1.1  jmcneill 	case ACPI_TYPE_BUFFER:
    391  1.1  jmcneill 		if (obj->Buffer.Length <
    392  1.1  jmcneill 		    sizeof(AML_RESOURCE_GENERIC_REGISTER)) {
    393  1.1  jmcneill 			return AE_TYPE;
    394  1.1  jmcneill 		}
    395  1.1  jmcneill 		memcpy(&addr, obj->Buffer.Pointer +
    396  1.1  jmcneill 		    sizeof(AML_RESOURCE_LARGE_HEADER), sizeof(addr));
    397  1.1  jmcneill 		if (addr.SpaceId == ACPI_ADR_SPACE_PLATFORM_COMM) {
    398  1.1  jmcneill 			rv = pcc_message(&addr, CPPC_PCC_READ, PCC_READ, val);
    399  1.1  jmcneill 		} else {
    400  1.1  jmcneill 			rv = AcpiRead(val, &addr);
    401  1.1  jmcneill 		}
    402  1.1  jmcneill 		return rv;
    403  1.1  jmcneill 
    404  1.1  jmcneill 	default:
    405  1.1  jmcneill 		return AE_SUPPORT;
    406  1.1  jmcneill 	}
    407  1.1  jmcneill }
    408  1.1  jmcneill 
    409  1.1  jmcneill /*
    410  1.1  jmcneill  * cppc_write --
    411  1.1  jmcneill  *
    412  1.1  jmcneill  *	Write a value based on the CPC package to the specified register.
    413  1.1  jmcneill  */
    414  1.1  jmcneill static ACPI_STATUS
    415  1.1  jmcneill cppc_write(struct cppc_softc *sc, CPCPackageElement index, ACPI_INTEGER val)
    416  1.1  jmcneill {
    417  1.1  jmcneill 	ACPI_OBJECT *obj;
    418  1.1  jmcneill 	ACPI_GENERIC_ADDRESS addr;
    419  1.1  jmcneill 	ACPI_STATUS rv;
    420  1.1  jmcneill 
    421  1.1  jmcneill 	if (index >= sc->sc_ncpc) {
    422  1.1  jmcneill 		return AE_NOT_EXIST;
    423  1.1  jmcneill 	}
    424  1.1  jmcneill 
    425  1.1  jmcneill 	obj = &sc->sc_cpc->Package.Elements[index];
    426  1.1  jmcneill 	if (obj->Type != ACPI_TYPE_BUFFER ||
    427  1.1  jmcneill 	    obj->Buffer.Length < sizeof(AML_RESOURCE_GENERIC_REGISTER)) {
    428  1.1  jmcneill 		return AE_TYPE;
    429  1.1  jmcneill 	}
    430  1.1  jmcneill 
    431  1.1  jmcneill 	memcpy(&addr, obj->Buffer.Pointer +
    432  1.1  jmcneill 	    sizeof(AML_RESOURCE_LARGE_HEADER), sizeof(addr));
    433  1.1  jmcneill 	if (addr.SpaceId == ACPI_ADR_SPACE_PLATFORM_COMM) {
    434  1.1  jmcneill 		rv = pcc_message(&addr, CPPC_PCC_WRITE, PCC_WRITE, &val);
    435  1.1  jmcneill 	} else {
    436  1.1  jmcneill 		rv = AcpiWrite(val, &addr);
    437  1.1  jmcneill 	}
    438  1.1  jmcneill 
    439  1.1  jmcneill 	return rv;
    440  1.1  jmcneill }
    441