Home | History | Annotate | Line # | Download | only in acpi
      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