summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorcvs2svn <cvs2svn@FreeBSD.org>2005-02-25 10:38:44 +0000
committercvs2svn <cvs2svn@FreeBSD.org>2005-02-25 10:38:44 +0000
commitc4170850986b6d1be9c0bc370be680c7d3fb8fb5 (patch)
tree9dcc274c86477584c21016c9090b0fda5d1b2020 /sys
parentc1f4a77f7b0082c96a9f79a39a19f7870d2cfe7d (diff)
Notes
Diffstat (limited to 'sys')
-rw-r--r--sys/compat/ndis/subr_usbd.c156
-rw-r--r--sys/compat/ndis/usbd_var.h56
-rw-r--r--sys/dev/acpica/acpi_throttle.c426
-rw-r--r--sys/kern/kern_cpu.c852
-rw-r--r--sys/netinet/ip_carp.h163
-rw-r--r--sys/sys/cpu.h131
6 files changed, 1784 insertions, 0 deletions
diff --git a/sys/compat/ndis/subr_usbd.c b/sys/compat/ndis/subr_usbd.c
new file mode 100644
index 0000000000000..a9b30e4a40f66
--- /dev/null
+++ b/sys/compat/ndis/subr_usbd.c
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2005
+ * Bill Paul <wpaul@windriver.com>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/unistd.h>
+#include <sys/types.h>
+
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/mbuf.h>
+#include <sys/bus.h>
+
+#include <sys/queue.h>
+
+#include <compat/ndis/pe_var.h>
+#include <compat/ndis/cfg_var.h>
+#include <compat/ndis/resource_var.h>
+#include <compat/ndis/ntoskrnl_var.h>
+#include <compat/ndis/ndis_var.h>
+#include <compat/ndis/hal_var.h>
+#include <compat/ndis/usbd_var.h>
+
+static driver_object usbd_driver;
+
+__stdcall static uint32_t usbd_iodispatch(device_object *, irp *);
+
+__stdcall static void USBD_GetUSBDIVersion(usbd_version_info *);
+__stdcall static void dummy(void);
+
+int
+usbd_libinit(void)
+{
+ image_patch_table *patch;
+
+ patch = usbd_functbl;
+ while (patch->ipt_func != NULL) {
+ windrv_wrap((funcptr)patch->ipt_func,
+ (funcptr *)&patch->ipt_wrap);
+ patch++;
+ }
+
+ /* Create a fake USB driver instance. */
+
+ windrv_bus_attach(&usbd_driver, "USB Bus");
+
+ /* Set up our dipatch routine. */
+
+ usbd_driver.dro_dispatch[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
+ (driver_dispatch)usbd_iodispatch;
+
+ return(0);
+}
+
+int
+usbd_libfini(void)
+{
+ image_patch_table *patch;
+
+ patch = usbd_functbl;
+ while (patch->ipt_func != NULL) {
+ windrv_unwrap(patch->ipt_wrap);
+ patch++;
+ }
+
+ free(usbd_driver.dro_drivername.us_buf, M_DEVBUF);
+
+ return(0);
+}
+
+__stdcall static uint32_t
+usbd_iodispatch(dobj, ip)
+ device_object *dobj;
+ irp *ip;
+{
+ return(0);
+}
+
+__stdcall static void
+USBD_GetUSBDIVersion(ui)
+ usbd_version_info *ui;
+{
+ /* Pretend to be Windows XP. */
+
+ ui->uvi_usbdi_vers = USBDI_VERSION;
+ ui->uvi_supported_vers = USB_VER_2_0;
+
+ return;
+}
+
+__stdcall static void
+dummy(void)
+{
+ printf("USBD dummy called\n");
+ return;
+}
+
+image_patch_table usbd_functbl[] = {
+ IMPORT_FUNC(USBD_GetUSBDIVersion),
+#ifdef notyet
+ IMPORT_FUNC_MAP(_USBD_ParseConfigurationDescriptorEx@28,
+ USBD_ParseConfigurationDescriptorEx),
+ IMPORT_FUNC_MAP(_USBD_CreateConfigurationRequestEx@8,
+ USBD_CreateConfigurationRequestEx),
+#endif
+
+ /*
+ * This last entry is a catch-all for any function we haven't
+ * implemented yet. The PE import list patching routine will
+ * use it for any function that doesn't have an explicit match
+ * in this table.
+ */
+
+ { NULL, (FUNC)dummy, NULL },
+
+ /* End of list. */
+
+ { NULL, NULL, NULL }
+};
+
diff --git a/sys/compat/ndis/usbd_var.h b/sys/compat/ndis/usbd_var.h
new file mode 100644
index 0000000000000..8c9f2b3a5854c
--- /dev/null
+++ b/sys/compat/ndis/usbd_var.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 2003
+ * Bill Paul <wpaul@windriver.com>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _USBD_VAR_H_
+#define _USBD_VAR_H_
+
+#define USBDI_VERSION 0x00000500
+#define USB_VER_1_1 0x00000110
+#define USB_VER_2_0 0x00000200
+
+struct usbd_version_info {
+ uint32_t uvi_usbdi_vers;
+ uint32_t uvi_supported_vers;
+};
+
+typedef struct usbd_version_info usbd_version_info;
+
+extern image_patch_table usbd_functbl[];
+
+__BEGIN_DECLS
+extern int usbd_libinit(void);
+extern int usbd_libfini(void);
+__END_DECLS
+
+#endif /* _USBD_VAR_H_ */
diff --git a/sys/dev/acpica/acpi_throttle.c b/sys/dev/acpica/acpi_throttle.c
new file mode 100644
index 0000000000000..b736124db61e1
--- /dev/null
+++ b/sys/dev/acpica/acpi_throttle.c
@@ -0,0 +1,426 @@
+/*-
+ * Copyright (c) 2003-2005 Nate Lawson (SDG)
+ * Copyright (c) 2001 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_acpi.h"
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include "acpi.h"
+#include <dev/acpica/acpivar.h>
+#include <dev/pci/pcivar.h>
+
+#include "cpufreq_if.h"
+
+/*
+ * Throttling provides relative frequency control. It involves modulating
+ * the clock so that the CPU is active for only a fraction of the normal
+ * clock cycle. It does not change voltage and so is less efficient than
+ * other mechanisms. Since it is relative, it can be used in addition to
+ * absolute cpufreq drivers. We support the ACPI 2.0 specification.
+ */
+
+struct acpi_throttle_softc {
+ device_t cpu_dev;
+ ACPI_HANDLE cpu_handle;
+ uint32_t cpu_p_blk; /* ACPI P_BLK location */
+ uint32_t cpu_p_blk_len; /* P_BLK length (must be 6). */
+ struct resource *cpu_p_cnt; /* Throttling control register */
+ int cpu_p_type; /* Resource type for cpu_p_cnt. */
+ uint32_t cpu_thr_state; /* Current throttle setting. */
+};
+
+#define THR_GET_REG(reg) \
+ (bus_space_read_4(rman_get_bustag((reg)), \
+ rman_get_bushandle((reg)), 0))
+#define THR_SET_REG(reg, val) \
+ (bus_space_write_4(rman_get_bustag((reg)), \
+ rman_get_bushandle((reg)), 0, (val)))
+
+/*
+ * Speeds are stored in counts, from 1 to CPU_MAX_SPEED, and
+ * reported to the user in hundredths of a percent.
+ */
+#define CPU_MAX_SPEED (1 << cpu_duty_width)
+#define CPU_SPEED_PERCENT(x) ((10000 * (x)) / CPU_MAX_SPEED)
+#define CPU_SPEED_PRINTABLE(x) (CPU_SPEED_PERCENT(x) / 10), \
+ (CPU_SPEED_PERCENT(x) % 10)
+#define CPU_P_CNT_THT_EN (1<<4)
+#define CPU_QUIRK_NO_THROTTLE (1<<1) /* Throttling is not usable. */
+
+#define PCI_VENDOR_INTEL 0x8086
+#define PCI_DEVICE_82371AB_3 0x7113 /* PIIX4 chipset for quirks. */
+#define PCI_REVISION_A_STEP 0
+#define PCI_REVISION_B_STEP 1
+
+static uint32_t cpu_duty_offset; /* Offset in P_CNT of throttle val. */
+static uint32_t cpu_duty_width; /* Bit width of throttle value. */
+static int thr_rid; /* Driver-wide resource id. */
+static int thr_quirks; /* Indicate any hardware bugs. */
+
+static void acpi_throttle_identify(driver_t *driver, device_t parent);
+static int acpi_throttle_probe(device_t dev);
+static int acpi_throttle_attach(device_t dev);
+static int acpi_throttle_evaluate(struct acpi_throttle_softc *sc);
+static int acpi_throttle_quirks(struct acpi_throttle_softc *sc);
+static int acpi_thr_settings(device_t dev, struct cf_setting *sets,
+ int *count);
+static int acpi_thr_set(device_t dev, const struct cf_setting *set);
+static int acpi_thr_get(device_t dev, struct cf_setting *set);
+static int acpi_thr_type(device_t dev, int *type);
+
+static device_method_t acpi_throttle_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, acpi_throttle_identify),
+ DEVMETHOD(device_probe, acpi_throttle_probe),
+ DEVMETHOD(device_attach, acpi_throttle_attach),
+
+ /* cpufreq interface */
+ DEVMETHOD(cpufreq_drv_set, acpi_thr_set),
+ DEVMETHOD(cpufreq_drv_get, acpi_thr_get),
+ DEVMETHOD(cpufreq_drv_type, acpi_thr_type),
+ DEVMETHOD(cpufreq_drv_settings, acpi_thr_settings),
+ {0, 0}
+};
+
+static driver_t acpi_throttle_driver = {
+ "acpi_throttle",
+ acpi_throttle_methods,
+ sizeof(struct acpi_throttle_softc),
+};
+
+static devclass_t acpi_throttle_devclass;
+DRIVER_MODULE(acpi_throttle, cpu, acpi_throttle_driver, acpi_throttle_devclass,
+ 0, 0);
+
+static void
+acpi_throttle_identify(driver_t *driver, device_t parent)
+{
+ ACPI_BUFFER buf;
+ ACPI_HANDLE handle;
+ ACPI_OBJECT *obj;
+
+ /* Make sure we're not being doubly invoked. */
+ if (device_find_child(parent, "acpi_throttle", -1) != NULL)
+ return;
+
+ /* Check for a valid duty width and parent CPU type. */
+ handle = acpi_get_handle(parent);
+ if (handle == NULL)
+ return;
+ if (AcpiGbl_FADT->DutyWidth == 0 ||
+ acpi_get_type(parent) != ACPI_TYPE_PROCESSOR)
+ return;
+
+ /*
+ * Add a child if there's a non-NULL P_BLK and correct length, or
+ * if the _PTC method is present.
+ */
+ buf.Pointer = NULL;
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+ if (ACPI_FAILURE(AcpiEvaluateObject(handle, NULL, NULL, &buf)))
+ return;
+ obj = (ACPI_OBJECT *)buf.Pointer;
+ if ((obj->Processor.PblkAddress && obj->Processor.PblkLength >= 4) ||
+ ACPI_SUCCESS(AcpiEvaluateObject(handle, "_PTC", NULL, NULL))) {
+ if (BUS_ADD_CHILD(parent, 0, "acpi_throttle", -1) == NULL)
+ device_printf(parent, "add throttle child failed\n");
+ }
+ AcpiOsFree(obj);
+}
+
+static int
+acpi_throttle_probe(device_t dev)
+{
+
+ if (resource_disabled("acpi_throttle", 0))
+ return (ENXIO);
+
+ device_set_desc(dev, "ACPI CPU Throttling");
+ return (0);
+}
+
+static int
+acpi_throttle_attach(device_t dev)
+{
+ struct acpi_throttle_softc *sc;
+ ACPI_BUFFER buf;
+ ACPI_OBJECT *obj;
+ ACPI_STATUS status;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->cpu_dev = dev;
+ sc->cpu_handle = acpi_get_handle(dev);
+
+ buf.Pointer = NULL;
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+ status = AcpiEvaluateObject(sc->cpu_handle, NULL, NULL, &buf);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev, "attach failed to get Processor obj - %s\n",
+ AcpiFormatException(status));
+ return (ENXIO);
+ }
+ obj = (ACPI_OBJECT *)buf.Pointer;
+ sc->cpu_p_blk = obj->Processor.PblkAddress;
+ sc->cpu_p_blk_len = obj->Processor.PblkLength;
+ AcpiOsFree(obj);
+
+ /* If this is the first device probed, check for quirks. */
+ if (device_get_unit(dev) == 0)
+ acpi_throttle_quirks(sc);
+
+ /* Attempt to attach the actual throttling register. */
+ error = acpi_throttle_evaluate(sc);
+ if (error)
+ return (error);
+
+ /* Everything went ok, register with cpufreq(4). */
+ cpufreq_register(dev);
+ return (0);
+}
+
+static int
+acpi_throttle_evaluate(struct acpi_throttle_softc *sc)
+{
+ uint32_t duty_end;
+ ACPI_BUFFER buf;
+ ACPI_OBJECT obj;
+ ACPI_GENERIC_ADDRESS gas;
+ ACPI_STATUS status;
+
+ /* Get throttling parameters from the FADT. 0 means not supported. */
+ if (device_get_unit(sc->cpu_dev) == 0) {
+ cpu_duty_offset = AcpiGbl_FADT->DutyOffset;
+ cpu_duty_width = AcpiGbl_FADT->DutyWidth;
+ }
+ if (cpu_duty_width == 0 || (thr_quirks & CPU_QUIRK_NO_THROTTLE) != 0)
+ return (ENXIO);
+
+ /* Validate the duty offset/width. */
+ duty_end = cpu_duty_offset + cpu_duty_width - 1;
+ if (duty_end > 31) {
+ device_printf(sc->cpu_dev,
+ "CLK_VAL field overflows P_CNT register\n");
+ return (ENXIO);
+ }
+ if (cpu_duty_offset <= 4 && duty_end >= 4) {
+ device_printf(sc->cpu_dev,
+ "CLK_VAL field overlaps THT_EN bit\n");
+ return (ENXIO);
+ }
+
+ /*
+ * If not present, fall back to using the processor's P_BLK to find
+ * the P_CNT register.
+ *
+ * Note that some systems seem to duplicate the P_BLK pointer
+ * across multiple CPUs, so not getting the resource is not fatal.
+ */
+ buf.Pointer = &obj;
+ buf.Length = sizeof(obj);
+ status = AcpiEvaluateObject(sc->cpu_handle, "_PTC", NULL, &buf);
+ if (ACPI_SUCCESS(status)) {
+ if (obj.Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3) {
+ device_printf(sc->cpu_dev, "_PTC buffer too small\n");
+ return (ENXIO);
+ }
+ memcpy(&gas, obj.Buffer.Pointer + 3, sizeof(gas));
+ acpi_bus_alloc_gas(sc->cpu_dev, &sc->cpu_p_type, &thr_rid,
+ &gas, &sc->cpu_p_cnt);
+ if (sc->cpu_p_cnt != NULL && bootverbose) {
+ device_printf(sc->cpu_dev, "P_CNT from _PTC %#jx\n",
+ gas.Address);
+ }
+ }
+
+ /* If _PTC not present or other failure, try the P_BLK. */
+ if (sc->cpu_p_cnt == NULL) {
+ /*
+ * The spec says P_BLK must be 6 bytes long. However, some
+ * systems use it to indicate a fractional set of features
+ * present so we take anything >= 4.
+ */
+ if (sc->cpu_p_blk_len < 4)
+ return (ENXIO);
+ gas.Address = sc->cpu_p_blk;
+ gas.AddressSpaceId = ACPI_ADR_SPACE_SYSTEM_IO;
+ gas.RegisterBitWidth = 32;
+ acpi_bus_alloc_gas(sc->cpu_dev, &sc->cpu_p_type, &thr_rid,
+ &gas, &sc->cpu_p_cnt);
+ if (sc->cpu_p_cnt != NULL) {
+ if (bootverbose)
+ device_printf(sc->cpu_dev,
+ "P_CNT from P_BLK %#x\n", sc->cpu_p_blk);
+ } else {
+ device_printf(sc->cpu_dev, "failed to attach P_CNT\n");
+ return (ENXIO);
+ }
+ }
+ thr_rid++;
+
+ return (0);
+}
+
+static int
+acpi_throttle_quirks(struct acpi_throttle_softc *sc)
+{
+ device_t acpi_dev;
+
+ /* Look for various quirks of the PIIX4 part. */
+ acpi_dev = pci_find_device(PCI_VENDOR_INTEL, PCI_DEVICE_82371AB_3);
+ if (acpi_dev) {
+ switch (pci_get_revid(acpi_dev)) {
+ /*
+ * Disable throttling control on PIIX4 A and B-step.
+ * See specification changes #13 ("Manual Throttle Duty Cycle")
+ * and #14 ("Enabling and Disabling Manual Throttle"), plus
+ * erratum #5 ("STPCLK# Deassertion Time") from the January
+ * 2002 PIIX4 specification update. Note that few (if any)
+ * mobile systems ever used this part.
+ */
+ case PCI_REVISION_A_STEP:
+ case PCI_REVISION_B_STEP:
+ thr_quirks |= CPU_QUIRK_NO_THROTTLE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return (0);
+}
+
+static int
+acpi_thr_settings(device_t dev, struct cf_setting *sets, int *count)
+{
+ struct acpi_throttle_softc *sc;
+ int i, speed;
+
+ sc = device_get_softc(dev);
+ if (sets == NULL || count == NULL)
+ return (EINVAL);
+ if (*count < CPU_MAX_SPEED)
+ return (E2BIG);
+
+ /* Return a list of valid settings for this driver. */
+ memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * CPU_MAX_SPEED);
+ for (i = 0, speed = CPU_MAX_SPEED; speed != 0; i++, speed--) {
+ sets[i].freq = CPU_SPEED_PERCENT(speed);
+ sets[i].dev = dev;
+ }
+ *count = CPU_MAX_SPEED;
+
+ return (0);
+}
+
+static int
+acpi_thr_set(device_t dev, const struct cf_setting *set)
+{
+ struct acpi_throttle_softc *sc;
+ uint32_t clk_val, p_cnt, speed;
+
+ if (set == NULL)
+ return (EINVAL);
+ sc = device_get_softc(dev);
+
+ /*
+ * Validate requested state converts to a duty cycle that is an
+ * integer from [1 .. CPU_MAX_SPEED].
+ */
+ speed = set->freq * CPU_MAX_SPEED / 10000;
+ if (speed * 10000 != set->freq * CPU_MAX_SPEED ||
+ speed < 1 || speed > CPU_MAX_SPEED)
+ return (EINVAL);
+
+ /* If we're at this setting, don't bother applying it again. */
+ if (speed == sc->cpu_thr_state)
+ return (0);
+
+ /* Get the current P_CNT value and disable throttling */
+ p_cnt = THR_GET_REG(sc->cpu_p_cnt);
+ p_cnt &= ~CPU_P_CNT_THT_EN;
+ THR_SET_REG(sc->cpu_p_cnt, p_cnt);
+
+ /* If we're at maximum speed, that's all */
+ if (speed < CPU_MAX_SPEED) {
+ /* Mask the old CLK_VAL off and OR in the new value */
+ clk_val = (CPU_MAX_SPEED - 1) << cpu_duty_offset;
+ p_cnt &= ~clk_val;
+ p_cnt |= (speed << cpu_duty_offset);
+
+ /* Write the new P_CNT value and then enable throttling */
+ THR_SET_REG(sc->cpu_p_cnt, p_cnt);
+ p_cnt |= CPU_P_CNT_THT_EN;
+ THR_SET_REG(sc->cpu_p_cnt, p_cnt);
+ }
+ sc->cpu_thr_state = speed;
+
+ return (0);
+}
+
+static int
+acpi_thr_get(device_t dev, struct cf_setting *set)
+{
+ struct acpi_throttle_softc *sc;
+ uint32_t p_cnt, clk_val;
+
+ if (set == NULL)
+ return (EINVAL);
+ sc = device_get_softc(dev);
+
+ /* Get the current throttling setting from P_CNT. */
+ p_cnt = THR_GET_REG(sc->cpu_p_cnt);
+ clk_val = (p_cnt >> cpu_duty_offset) & (CPU_MAX_SPEED - 1);
+ sc->cpu_thr_state = clk_val;
+
+ memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
+ set->freq = CPU_SPEED_PERCENT(clk_val);
+ set->dev = dev;
+
+ return (0);
+}
+
+static int
+acpi_thr_type(device_t dev, int *type)
+{
+
+ if (type == NULL)
+ return (EINVAL);
+
+ *type = CPUFREQ_TYPE_RELATIVE;
+ return (0);
+}
diff --git a/sys/kern/kern_cpu.c b/sys/kern/kern_cpu.c
new file mode 100644
index 0000000000000..355b258ee9167
--- /dev/null
+++ b/sys/kern/kern_cpu.c
@@ -0,0 +1,852 @@
+/*-
+ * Copyright (c) 2004-2005 Nate Lawson (SDG)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sched.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/sbuf.h>
+#include <sys/timetc.h>
+
+#include "cpufreq_if.h"
+
+/*
+ * Common CPU frequency glue code. Drivers for specific hardware can
+ * attach this interface to allow users to get/set the CPU frequency.
+ */
+
+/*
+ * Number of levels we can handle. Levels are synthesized from settings
+ * so for M settings and N drivers, there may be M*N levels.
+ */
+#define CF_MAX_LEVELS 64
+
+struct cpufreq_softc {
+ struct cf_level curr_level;
+ int curr_priority;
+ struct cf_level saved_level;
+ int saved_priority;
+ struct cf_level_lst all_levels;
+ int all_count;
+ int max_mhz;
+ device_t dev;
+ struct sysctl_ctx_list sysctl_ctx;
+};
+
+struct cf_setting_array {
+ struct cf_setting sets[MAX_SETTINGS];
+ int count;
+ TAILQ_ENTRY(cf_setting_array) link;
+};
+
+TAILQ_HEAD(cf_setting_lst, cf_setting_array);
+
+static int cpufreq_attach(device_t dev);
+static int cpufreq_detach(device_t dev);
+static void cpufreq_evaluate(void *arg);
+static int cf_set_method(device_t dev, const struct cf_level *level,
+ int priority);
+static int cf_get_method(device_t dev, struct cf_level *level);
+static int cf_levels_method(device_t dev, struct cf_level *levels,
+ int *count);
+static int cpufreq_insert_abs(struct cpufreq_softc *sc,
+ struct cf_setting *sets, int count);
+static int cpufreq_expand_set(struct cpufreq_softc *sc,
+ struct cf_setting_array *set_arr);
+static struct cf_level *cpufreq_dup_set(struct cpufreq_softc *sc,
+ struct cf_level *dup, struct cf_setting *set);
+static int cpufreq_curr_sysctl(SYSCTL_HANDLER_ARGS);
+static int cpufreq_levels_sysctl(SYSCTL_HANDLER_ARGS);
+static int cpufreq_settings_sysctl(SYSCTL_HANDLER_ARGS);
+
+static device_method_t cpufreq_methods[] = {
+ DEVMETHOD(device_probe, bus_generic_probe),
+ DEVMETHOD(device_attach, cpufreq_attach),
+ DEVMETHOD(device_detach, cpufreq_detach),
+
+ DEVMETHOD(cpufreq_set, cf_set_method),
+ DEVMETHOD(cpufreq_get, cf_get_method),
+ DEVMETHOD(cpufreq_levels, cf_levels_method),
+ {0, 0}
+};
+static driver_t cpufreq_driver = {
+ "cpufreq", cpufreq_methods, sizeof(struct cpufreq_softc)
+};
+static devclass_t cpufreq_dc;
+DRIVER_MODULE(cpufreq, cpu, cpufreq_driver, cpufreq_dc, 0, 0);
+
+static eventhandler_tag cf_ev_tag;
+
+static int
+cpufreq_attach(device_t dev)
+{
+ struct cpufreq_softc *sc;
+ device_t parent;
+ int numdevs;
+
+ sc = device_get_softc(dev);
+ parent = device_get_parent(dev);
+ sc->dev = dev;
+ sysctl_ctx_init(&sc->sysctl_ctx);
+ TAILQ_INIT(&sc->all_levels);
+ sc->curr_level.total_set.freq = CPUFREQ_VAL_UNKNOWN;
+ sc->saved_level.total_set.freq = CPUFREQ_VAL_UNKNOWN;
+ sc->max_mhz = CPUFREQ_VAL_UNKNOWN;
+
+ /*
+ * Only initialize one set of sysctls for all CPUs. In the future,
+ * if multiple CPUs can have different settings, we can move these
+ * sysctls to be under every CPU instead of just the first one.
+ */
+ numdevs = devclass_get_count(cpufreq_dc);
+ if (numdevs > 1)
+ return (0);
+
+ SYSCTL_ADD_PROC(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(parent)),
+ OID_AUTO, "freq", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+ cpufreq_curr_sysctl, "I", "Current CPU frequency");
+ SYSCTL_ADD_PROC(&sc->sysctl_ctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(parent)),
+ OID_AUTO, "freq_levels", CTLTYPE_STRING | CTLFLAG_RD, sc, 0,
+ cpufreq_levels_sysctl, "A", "CPU frequency levels");
+ cf_ev_tag = EVENTHANDLER_REGISTER(cpufreq_changed, cpufreq_evaluate,
+ NULL, EVENTHANDLER_PRI_ANY);
+
+ return (0);
+}
+
+static int
+cpufreq_detach(device_t dev)
+{
+ struct cpufreq_softc *sc;
+ int numdevs;
+
+ sc = device_get_softc(dev);
+ sysctl_ctx_free(&sc->sysctl_ctx);
+
+ /* Only clean up these resources when the last device is detaching. */
+ numdevs = devclass_get_count(cpufreq_dc);
+ if (numdevs == 1)
+ EVENTHANDLER_DEREGISTER(cpufreq_changed, cf_ev_tag);
+
+ return (0);
+}
+
+static void
+cpufreq_evaluate(void *arg)
+{
+ /* TODO: Re-evaluate when notified of changes to drivers. */
+}
+
+static int
+cf_set_method(device_t dev, const struct cf_level *level, int priority)
+{
+ struct cpufreq_softc *sc;
+ const struct cf_setting *set;
+ struct pcpu *pc;
+ int cpu_id, error, i;
+
+ sc = device_get_softc(dev);
+
+ /*
+ * Check that the TSC isn't being used as a timecounter.
+ * If it is, then return EBUSY and refuse to change the
+ * clock speed.
+ */
+ if (strcmp(timecounter->tc_name, "TSC") == 0)
+ return (EBUSY);
+
+ /*
+ * If the caller didn't specify a level and one is saved, prepare to
+ * restore the saved level. If none has been saved, return an error.
+ * If they did specify one, but the requested level has a lower
+ * priority, don't allow the new level right now.
+ */
+ if (level == NULL) {
+ if (sc->saved_level.total_set.freq != CPUFREQ_VAL_UNKNOWN) {
+ level = &sc->saved_level;
+ priority = sc->saved_priority;
+ } else
+ return (ENXIO);
+ } else if (priority < sc->curr_priority)
+ return (EPERM);
+
+ /* If already at this level, just return. */
+ if (CPUFREQ_CMP(sc->curr_level.total_set.freq, level->total_set.freq))
+ return (0);
+
+ /* First, set the absolute frequency via its driver. */
+ set = &level->abs_set;
+ if (set->dev) {
+ if (!device_is_attached(set->dev)) {
+ error = ENXIO;
+ goto out;
+ }
+
+ /* Bind to the target CPU before switching, if necessary. */
+ cpu_id = PCPU_GET(cpuid);
+ pc = cpu_get_pcpu(set->dev);
+ if (cpu_id != pc->pc_cpuid) {
+ mtx_lock_spin(&sched_lock);
+ sched_bind(curthread, pc->pc_cpuid);
+ mtx_unlock_spin(&sched_lock);
+ }
+ error = CPUFREQ_DRV_SET(set->dev, set);
+ if (cpu_id != pc->pc_cpuid) {
+ mtx_lock_spin(&sched_lock);
+ sched_unbind(curthread);
+ mtx_unlock_spin(&sched_lock);
+ }
+ if (error) {
+ goto out;
+ }
+ }
+
+ /* Next, set any/all relative frequencies via their drivers. */
+ for (i = 0; i < level->rel_count; i++) {
+ set = &level->rel_set[i];
+ if (!device_is_attached(set->dev)) {
+ error = ENXIO;
+ goto out;
+ }
+
+ /* Bind to the target CPU before switching, if necessary. */
+ cpu_id = PCPU_GET(cpuid);
+ pc = cpu_get_pcpu(set->dev);
+ if (cpu_id != pc->pc_cpuid) {
+ mtx_lock_spin(&sched_lock);
+ sched_bind(curthread, pc->pc_cpuid);
+ mtx_unlock_spin(&sched_lock);
+ }
+ error = CPUFREQ_DRV_SET(set->dev, set);
+ if (cpu_id != pc->pc_cpuid) {
+ mtx_lock_spin(&sched_lock);
+ sched_unbind(curthread);
+ mtx_unlock_spin(&sched_lock);
+ }
+ if (error) {
+ /* XXX Back out any successful setting? */
+ goto out;
+ }
+ }
+
+ /* If we were restoring a saved state, reset it to "unused". */
+ if (level == &sc->saved_level) {
+ sc->saved_level.total_set.freq = CPUFREQ_VAL_UNKNOWN;
+ sc->saved_priority = 0;
+ }
+
+ /*
+ * Before recording the current level, check if we're going to a
+ * higher priority and have not saved a level yet. If so, save the
+ * previous level and priority.
+ */
+ if (sc->curr_level.total_set.freq != CPUFREQ_VAL_UNKNOWN &&
+ sc->saved_level.total_set.freq == CPUFREQ_VAL_UNKNOWN &&
+ priority > sc->curr_priority) {
+ sc->saved_level = sc->curr_level;
+ sc->saved_priority = sc->curr_priority;
+ }
+ sc->curr_level = *level;
+ sc->curr_priority = priority;
+ error = 0;
+
+out:
+ if (error)
+ device_printf(set->dev, "set freq failed, err %d\n", error);
+ return (error);
+}
+
+static int
+cf_get_method(device_t dev, struct cf_level *level)
+{
+ struct cpufreq_softc *sc;
+ struct cf_level *levels;
+ struct cf_setting *curr_set, set;
+ struct pcpu *pc;
+ device_t *devs;
+ int count, error, i, numdevs;
+ uint64_t rate;
+
+ sc = device_get_softc(dev);
+ curr_set = &sc->curr_level.total_set;
+ levels = NULL;
+
+ /* If we already know the current frequency, we're done. */
+ if (curr_set->freq != CPUFREQ_VAL_UNKNOWN)
+ goto out;
+
+ /*
+ * We need to figure out the current level. Loop through every
+ * driver, getting the current setting. Then, attempt to get a best
+ * match of settings against each level.
+ */
+ count = CF_MAX_LEVELS;
+ levels = malloc(count * sizeof(*levels), M_TEMP, M_NOWAIT);
+ if (levels == NULL)
+ return (ENOMEM);
+ error = CPUFREQ_LEVELS(sc->dev, levels, &count);
+ if (error) {
+ if (error == E2BIG)
+ printf("cpufreq: need to increase CF_MAX_LEVELS\n");
+ goto out;
+ }
+ error = device_get_children(device_get_parent(dev), &devs, &numdevs);
+ if (error)
+ goto out;
+ for (i = 0; i < numdevs && curr_set->freq == CPUFREQ_VAL_UNKNOWN; i++) {
+ if (!device_is_attached(devs[i]))
+ continue;
+ error = CPUFREQ_DRV_GET(devs[i], &set);
+ if (error)
+ continue;
+ for (i = 0; i < count; i++) {
+ if (CPUFREQ_CMP(set.freq, levels[i].total_set.freq)) {
+ sc->curr_level = levels[i];
+ break;
+ }
+ }
+ }
+ free(devs, M_TEMP);
+ if (curr_set->freq != CPUFREQ_VAL_UNKNOWN)
+ goto out;
+
+ /*
+ * We couldn't find an exact match, so attempt to estimate and then
+ * match against a level.
+ */
+ pc = cpu_get_pcpu(dev);
+ if (pc == NULL) {
+ error = ENXIO;
+ goto out;
+ }
+ cpu_est_clockrate(pc->pc_cpuid, &rate);
+ rate /= 1000000;
+ for (i = 0; i < count; i++) {
+ if (CPUFREQ_CMP(rate, levels[i].total_set.freq)) {
+ sc->curr_level = levels[i];
+ break;
+ }
+ }
+
+out:
+ if (levels)
+ free(levels, M_TEMP);
+ *level = sc->curr_level;
+ return (0);
+}
+
+static int
+cf_levels_method(device_t dev, struct cf_level *levels, int *count)
+{
+ struct cf_setting_array *set_arr;
+ struct cf_setting_lst rel_sets;
+ struct cpufreq_softc *sc;
+ struct cf_level *lev;
+ struct cf_setting *sets;
+ struct pcpu *pc;
+ device_t *devs;
+ int error, i, numdevs, set_count, type;
+ uint64_t rate;
+
+ if (levels == NULL || count == NULL)
+ return (EINVAL);
+
+ TAILQ_INIT(&rel_sets);
+ sc = device_get_softc(dev);
+ error = device_get_children(device_get_parent(dev), &devs, &numdevs);
+ if (error)
+ return (error);
+ sets = malloc(MAX_SETTINGS * sizeof(*sets), M_TEMP, M_NOWAIT);
+ if (sets == NULL) {
+ free(devs, M_TEMP);
+ return (ENOMEM);
+ }
+
+ /* Get settings from all cpufreq drivers. */
+ for (i = 0; i < numdevs; i++) {
+ /* Skip devices that aren't ready. */
+ if (!device_is_attached(devs[i]))
+ continue;
+
+ /*
+ * Get settings, skipping drivers that offer no settings or
+ * provide settings for informational purposes only.
+ */
+ error = CPUFREQ_DRV_TYPE(devs[i], &type);
+ if (error || (type & CPUFREQ_FLAG_INFO_ONLY))
+ continue;
+ set_count = MAX_SETTINGS;
+ error = CPUFREQ_DRV_SETTINGS(devs[i], sets, &set_count);
+ if (error || set_count == 0)
+ continue;
+
+ /* Add the settings to our absolute/relative lists. */
+ switch (type & CPUFREQ_TYPE_MASK) {
+ case CPUFREQ_TYPE_ABSOLUTE:
+ error = cpufreq_insert_abs(sc, sets, set_count);
+ break;
+ case CPUFREQ_TYPE_RELATIVE:
+ set_arr = malloc(sizeof(*set_arr), M_TEMP, M_NOWAIT);
+ if (set_arr == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ bcopy(sets, set_arr->sets, set_count * sizeof(*sets));
+ set_arr->count = set_count;
+ TAILQ_INSERT_TAIL(&rel_sets, set_arr, link);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ if (error)
+ goto out;
+ }
+
+ /*
+ * If there are no absolute levels, create a fake one at 100%. We
+ * then cache the clockrate for later use as our base frequency.
+ *
+ * XXX This assumes that the first time through, if we only have
+ * relative drivers, the CPU is currently running at 100%.
+ */
+ if (TAILQ_EMPTY(&sc->all_levels)) {
+ if (sc->max_mhz == CPUFREQ_VAL_UNKNOWN) {
+ pc = cpu_get_pcpu(dev);
+ cpu_est_clockrate(pc->pc_cpuid, &rate);
+ sc->max_mhz = rate / 1000000;
+ }
+ memset(&sets[0], CPUFREQ_VAL_UNKNOWN, sizeof(*sets));
+ sets[0].freq = sc->max_mhz;
+ sets[0].dev = NULL;
+ error = cpufreq_insert_abs(sc, sets, 1);
+ if (error)
+ goto out;
+ }
+
+ /* Create a combined list of absolute + relative levels. */
+ TAILQ_FOREACH(set_arr, &rel_sets, link)
+ cpufreq_expand_set(sc, set_arr);
+
+ /* If the caller doesn't have enough space, return the actual count. */
+ if (sc->all_count > *count) {
+ *count = sc->all_count;
+ error = E2BIG;
+ goto out;
+ }
+
+ /* Finally, output the list of levels. */
+ i = 0;
+ TAILQ_FOREACH(lev, &sc->all_levels, link) {
+ levels[i] = *lev;
+ i++;
+ }
+ *count = sc->all_count;
+ error = 0;
+
+out:
+ /* Clear all levels since we regenerate them each time. */
+ while ((lev = TAILQ_FIRST(&sc->all_levels)) != NULL) {
+ TAILQ_REMOVE(&sc->all_levels, lev, link);
+ free(lev, M_TEMP);
+ }
+ while ((set_arr = TAILQ_FIRST(&rel_sets)) != NULL) {
+ TAILQ_REMOVE(&rel_sets, set_arr, link);
+ free(set_arr, M_TEMP);
+ }
+ sc->all_count = 0;
+ free(devs, M_TEMP);
+ free(sets, M_TEMP);
+ return (error);
+}
+
+/*
+ * Create levels for an array of absolute settings and insert them in
+ * sorted order in the specified list.
+ */
+static int
+cpufreq_insert_abs(struct cpufreq_softc *sc, struct cf_setting *sets,
+ int count)
+{
+ struct cf_level_lst *list;
+ struct cf_level *level, *search;
+ int i;
+
+ list = &sc->all_levels;
+ for (i = 0; i < count; i++) {
+ level = malloc(sizeof(*level), M_TEMP, M_NOWAIT | M_ZERO);
+ if (level == NULL)
+ return (ENOMEM);
+ level->abs_set = sets[i];
+ level->total_set = sets[i];
+ level->total_set.dev = NULL;
+ sc->all_count++;
+
+ if (TAILQ_EMPTY(list)) {
+ TAILQ_INSERT_HEAD(list, level, link);
+ continue;
+ }
+
+ TAILQ_FOREACH_REVERSE(search, list, cf_level_lst, link) {
+ if (sets[i].freq <= search->total_set.freq) {
+ TAILQ_INSERT_AFTER(list, search, level, link);
+ break;
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * Expand a group of relative settings, creating derived levels from them.
+ */
+static int
+cpufreq_expand_set(struct cpufreq_softc *sc, struct cf_setting_array *set_arr)
+{
+ struct cf_level *fill, *search;
+ struct cf_setting *set;
+ int i;
+
+ TAILQ_FOREACH(search, &sc->all_levels, link) {
+ /* Skip this level if we've already modified it. */
+ for (i = 0; i < search->rel_count; i++) {
+ if (search->rel_set[i].dev == set_arr->sets[0].dev)
+ break;
+ }
+ if (i != search->rel_count)
+ continue;
+
+ /* Add each setting to the level, duplicating if necessary. */
+ for (i = 0; i < set_arr->count; i++) {
+ set = &set_arr->sets[i];
+
+ /*
+ * If this setting is less than 100%, split the level
+ * into two and add this setting to the new level.
+ */
+ fill = search;
+ if (set->freq < 10000)
+ fill = cpufreq_dup_set(sc, search, set);
+
+ /*
+ * The new level was a duplicate of an existing level
+ * so we freed it. Go to the next setting.
+ */
+ if (fill == NULL)
+ continue;
+
+ /* Add this setting to the existing or new level. */
+ KASSERT(fill->rel_count < MAX_SETTINGS,
+ ("cpufreq: too many relative drivers (%d)",
+ MAX_SETTINGS));
+ fill->rel_set[fill->rel_count] = *set;
+ fill->rel_count++;
+ }
+ }
+
+ return (0);
+}
+
+static struct cf_level *
+cpufreq_dup_set(struct cpufreq_softc *sc, struct cf_level *dup,
+ struct cf_setting *set)
+{
+ struct cf_level_lst *list;
+ struct cf_level *fill, *itr;
+ struct cf_setting *fill_set, *itr_set;
+ int i;
+
+ /*
+ * Create a new level, copy it from the old one, and update the
+ * total frequency and power by the percentage specified in the
+ * relative setting.
+ */
+ fill = malloc(sizeof(*fill), M_TEMP, M_NOWAIT);
+ if (fill == NULL)
+ return (NULL);
+ *fill = *dup;
+ fill_set = &fill->total_set;
+ fill_set->freq =
+ ((uint64_t)fill_set->freq * set->freq) / 10000;
+ if (fill_set->power != CPUFREQ_VAL_UNKNOWN) {
+ fill_set->power = ((uint64_t)fill_set->power * set->freq)
+ / 10000;
+ }
+ if (set->lat != CPUFREQ_VAL_UNKNOWN) {
+ if (fill_set->lat != CPUFREQ_VAL_UNKNOWN)
+ fill_set->lat += set->lat;
+ else
+ fill_set->lat = set->lat;
+ }
+
+ /*
+ * If we copied an old level that we already modified (say, at 100%),
+ * we need to remove that setting before adding this one. Since we
+ * process each setting array in order, we know any settings for this
+ * driver will be found at the end.
+ */
+ for (i = fill->rel_count; i != 0; i--) {
+ if (fill->rel_set[i - 1].dev != set->dev)
+ break;
+ fill->rel_count--;
+ }
+
+ /*
+ * Insert the new level in sorted order. If we find a duplicate,
+ * free the new level. We can do this since any existing level will
+ * be guaranteed to have the same or less settings and thus consume
+ * less power. For example, a level with one absolute setting of
+ * 800 Mhz uses less power than one composed of an absolute setting
+ * of 1600 Mhz and a relative setting at 50%.
+ */
+ list = &sc->all_levels;
+ if (TAILQ_EMPTY(list)) {
+ TAILQ_INSERT_HEAD(list, fill, link);
+ } else {
+ TAILQ_FOREACH_REVERSE(itr, list, cf_level_lst, link) {
+ itr_set = &itr->total_set;
+ if (CPUFREQ_CMP(fill_set->freq, itr_set->freq)) {
+ free(fill, M_TEMP);
+ fill = NULL;
+ break;
+ } else if (fill_set->freq < itr_set->freq) {
+ TAILQ_INSERT_AFTER(list, itr, fill, link);
+ sc->all_count++;
+ break;
+ }
+ }
+ }
+
+ return (fill);
+}
+
+static int
+cpufreq_curr_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct cpufreq_softc *sc;
+ struct cf_level *levels;
+ int count, devcount, error, freq, i, n;
+ device_t *devs;
+
+ devs = NULL;
+ sc = oidp->oid_arg1;
+ levels = malloc(CF_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT);
+ if (levels == NULL)
+ return (ENOMEM);
+
+ error = CPUFREQ_GET(sc->dev, &levels[0]);
+ if (error)
+ goto out;
+ freq = levels[0].total_set.freq;
+ error = sysctl_handle_int(oidp, &freq, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ goto out;
+
+ /*
+ * While we only call cpufreq_get() on one device (assuming all
+ * CPUs have equal levels), we call cpufreq_set() on all CPUs.
+ * This is needed for some MP systems.
+ */
+ error = devclass_get_devices(cpufreq_dc, &devs, &devcount);
+ if (error)
+ goto out;
+ for (n = 0; n < devcount; n++) {
+ count = CF_MAX_LEVELS;
+ error = CPUFREQ_LEVELS(devs[n], levels, &count);
+ if (error) {
+ if (error == E2BIG)
+ printf(
+ "cpufreq: need to increase CF_MAX_LEVELS\n");
+ break;
+ }
+ for (i = 0; i < count; i++) {
+ if (CPUFREQ_CMP(levels[i].total_set.freq, freq)) {
+ error = CPUFREQ_SET(devs[n], &levels[i],
+ CPUFREQ_PRIO_USER);
+ break;
+ }
+ }
+ if (i == count) {
+ error = EINVAL;
+ break;
+ }
+ }
+
+out:
+ if (devs)
+ free(devs, M_TEMP);
+ if (levels)
+ free(levels, M_TEMP);
+ return (error);
+}
+
+static int
+cpufreq_levels_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct cpufreq_softc *sc;
+ struct cf_level *levels;
+ struct cf_setting *set;
+ struct sbuf sb;
+ int count, error, i;
+
+ sc = oidp->oid_arg1;
+ sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
+
+ /* Get settings from the device and generate the output string. */
+ count = CF_MAX_LEVELS;
+ levels = malloc(count * sizeof(*levels), M_TEMP, M_NOWAIT);
+ if (levels == NULL)
+ return (ENOMEM);
+ error = CPUFREQ_LEVELS(sc->dev, levels, &count);
+ if (error) {
+ if (error == E2BIG)
+ printf("cpufreq: need to increase CF_MAX_LEVELS\n");
+ goto out;
+ }
+ if (count) {
+ for (i = 0; i < count; i++) {
+ set = &levels[i].total_set;
+ sbuf_printf(&sb, "%d/%d ", set->freq, set->power);
+ }
+ } else
+ sbuf_cpy(&sb, "0");
+ sbuf_trim(&sb);
+ sbuf_finish(&sb);
+ error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
+
+out:
+ free(levels, M_TEMP);
+ sbuf_delete(&sb);
+ return (error);
+}
+
+static int
+cpufreq_settings_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev;
+ struct cf_setting *sets;
+ struct sbuf sb;
+ int error, i, set_count;
+
+ dev = oidp->oid_arg1;
+ sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
+
+ /* Get settings from the device and generate the output string. */
+ set_count = MAX_SETTINGS;
+ sets = malloc(set_count * sizeof(*sets), M_TEMP, M_NOWAIT);
+ if (sets == NULL)
+ return (ENOMEM);
+ error = CPUFREQ_DRV_SETTINGS(dev, sets, &set_count);
+ if (error)
+ goto out;
+ if (set_count) {
+ for (i = 0; i < set_count; i++)
+ sbuf_printf(&sb, "%d/%d ", sets[i].freq, sets[i].power);
+ } else
+ sbuf_cpy(&sb, "0");
+ sbuf_trim(&sb);
+ sbuf_finish(&sb);
+ error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
+
+out:
+ free(sets, M_TEMP);
+ sbuf_delete(&sb);
+ return (error);
+}
+
+int
+cpufreq_register(device_t dev)
+{
+ struct cpufreq_softc *sc;
+ device_t cf_dev, cpu_dev;
+
+ /* Add a sysctl to get each driver's settings separately. */
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "freq_settings", CTLTYPE_STRING | CTLFLAG_RD, dev, 0,
+ cpufreq_settings_sysctl, "A", "CPU frequency driver settings");
+
+ /*
+ * Add only one cpufreq device to each CPU. Currently, all CPUs
+ * must offer the same levels and be switched at the same time.
+ */
+ cpu_dev = device_get_parent(dev);
+ if ((cf_dev = device_find_child(cpu_dev, "cpufreq", -1))) {
+ sc = device_get_softc(cf_dev);
+ sc->max_mhz = CPUFREQ_VAL_UNKNOWN;
+ return (0);
+ }
+
+ /* Add the child device and possibly sysctls. */
+ cf_dev = BUS_ADD_CHILD(cpu_dev, 0, "cpufreq", -1);
+ if (cf_dev == NULL)
+ return (ENOMEM);
+ device_quiet(cf_dev);
+
+ return (device_probe_and_attach(cf_dev));
+}
+
+int
+cpufreq_unregister(device_t dev)
+{
+ device_t cf_dev, *devs;
+ int cfcount, devcount, error, i, type;
+
+ /*
+ * If this is the last cpufreq child device, remove the control
+ * device as well. We identify cpufreq children by calling a method
+ * they support.
+ */
+ error = device_get_children(device_get_parent(dev), &devs, &devcount);
+ if (error)
+ return (error);
+ cf_dev = device_find_child(device_get_parent(dev), "cpufreq", -1);
+ cfcount = 0;
+ for (i = 0; i < devcount; i++) {
+ if (!device_is_attached(devs[i]))
+ continue;
+ if (CPUFREQ_DRV_TYPE(devs[i], &type) == 0)
+ cfcount++;
+ }
+ if (cfcount <= 1)
+ device_delete_child(device_get_parent(cf_dev), cf_dev);
+ free(devs, M_TEMP);
+
+ return (0);
+}
diff --git a/sys/netinet/ip_carp.h b/sys/netinet/ip_carp.h
new file mode 100644
index 0000000000000..a050a88e39915
--- /dev/null
+++ b/sys/netinet/ip_carp.h
@@ -0,0 +1,163 @@
+/* $FreeBSD$ */
+/* $OpenBSD: ip_carp.h,v 1.8 2004/07/29 22:12:15 mcbride Exp $ */
+
+/*
+ * Copyright (c) 2002 Michael Shalayeff. All rights reserved.
+ * Copyright (c) 2003 Ryan McBride. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _IP_CARP_H
+#define _IP_CARP_H
+
+/*
+ * The CARP header layout is as follows:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Version| Type | VirtualHostID | AdvSkew | Auth Len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reserved | AdvBase | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Counter (1) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Counter (2) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SHA-1 HMAC (1) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SHA-1 HMAC (2) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SHA-1 HMAC (3) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SHA-1 HMAC (4) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SHA-1 HMAC (5) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+struct carp_header {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u_int8_t carp_type:4,
+ carp_version:4;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ u_int8_t carp_version:4,
+ carp_type:4;
+#endif
+ u_int8_t carp_vhid; /* virtual host id */
+ u_int8_t carp_advskew; /* advertisement skew */
+ u_int8_t carp_authlen; /* size of counter+md, 32bit chunks */
+ u_int8_t carp_pad1; /* reserved */
+ u_int8_t carp_advbase; /* advertisement interval */
+ u_int16_t carp_cksum;
+ u_int32_t carp_counter[2];
+ unsigned char carp_md[20]; /* SHA1 HMAC */
+} __packed;
+
+#define CARP_DFLTTL 255
+
+/* carp_version */
+#define CARP_VERSION 2
+
+/* carp_type */
+#define CARP_ADVERTISEMENT 0x01
+
+#define CARP_KEY_LEN 20 /* a sha1 hash of a passphrase */
+
+/* carp_advbase */
+#define CARP_DFLTINTV 1
+
+/*
+ * Statistics.
+ */
+struct carpstats {
+ uint64_t carps_ipackets; /* total input packets, IPv4 */
+ uint64_t carps_ipackets6; /* total input packets, IPv6 */
+ uint64_t carps_badif; /* wrong interface */
+ uint64_t carps_badttl; /* TTL is not CARP_DFLTTL */
+ uint64_t carps_hdrops; /* packets shorter than hdr */
+ uint64_t carps_badsum; /* bad checksum */
+ uint64_t carps_badver; /* bad (incl unsupp) version */
+ uint64_t carps_badlen; /* data length does not match */
+ uint64_t carps_badauth; /* bad authentication */
+ uint64_t carps_badvhid; /* bad VHID */
+ uint64_t carps_badaddrs; /* bad address list */
+
+ uint64_t carps_opackets; /* total output packets, IPv4 */
+ uint64_t carps_opackets6; /* total output packets, IPv6 */
+ uint64_t carps_onomem; /* no memory for an mbuf */
+ uint64_t carps_ostates; /* total state updates sent */
+
+ uint64_t carps_preempt; /* if enabled, preemptions */
+};
+
+/*
+ * Configuration structure for SIOCSVH SIOCGVH
+ */
+struct carpreq {
+ int carpr_state;
+#define CARP_STATES "INIT", "BACKUP", "MASTER"
+#define CARP_MAXSTATE 2
+ int carpr_vhid;
+ int carpr_advskew;
+ int carpr_advbase;
+ unsigned char carpr_key[CARP_KEY_LEN];
+};
+#define SIOCSVH _IOWR('i', 245, struct ifreq)
+#define SIOCGVH _IOWR('i', 246, struct ifreq)
+
+/*
+ * Names for CARP sysctl objects
+ */
+#define CARPCTL_ALLOW 1 /* accept incoming CARP packets */
+#define CARPCTL_PREEMPT 2 /* high-pri backup preemption mode */
+#define CARPCTL_LOG 3 /* log bad packets */
+#define CARPCTL_STATS 4 /* statistics (read-only) */
+#define CARPCTL_ARPBALANCE 5 /* balance arp responses */
+#define CARPCTL_MAXID 6
+
+#define CARPCTL_NAMES { \
+ { 0, 0 }, \
+ { "allow", CTLTYPE_INT }, \
+ { "preempt", CTLTYPE_INT }, \
+ { "log", CTLTYPE_INT }, \
+ { "stats", CTLTYPE_STRUCT }, \
+ { "arpbalance", CTLTYPE_INT }, \
+}
+
+#ifdef _KERNEL
+void carp_ifdetach (struct ifnet *);
+void carp_carpdev_state(void *);
+void carp_input (struct mbuf *, int);
+int carp6_input (struct mbuf **, int *, int);
+int carp_output (struct ifnet *, struct mbuf *, struct sockaddr *,
+ struct rtentry *);
+int carp_iamatch (void *, struct in_ifaddr *, struct in_addr *,
+ u_int8_t **);
+struct ifaddr *carp_iamatch6(void *, struct in6_addr *);
+void *carp_macmatch6(void *, struct mbuf *, const struct in6_addr *);
+struct ifnet *carp_forus (void *, void *);
+#endif
+#endif /* _IP_CARP_H */
diff --git a/sys/sys/cpu.h b/sys/sys/cpu.h
new file mode 100644
index 0000000000000..e2337ab020f7b
--- /dev/null
+++ b/sys/sys/cpu.h
@@ -0,0 +1,131 @@
+/*-
+ * Copyright (c) 2005 Nate Lawson (SDG)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_CPU_H_
+#define _SYS_CPU_H_
+
+/*
+ * CPU device support.
+ */
+
+#define CPU_IVAR_PCPU 1
+
+static __inline struct pcpu *cpu_get_pcpu(device_t dev)
+{
+ uintptr_t v = 0;
+ BUS_READ_IVAR(device_get_parent(dev), dev, CPU_IVAR_PCPU, &v);
+ return ((struct pcpu *)v);
+}
+
+/*
+ * CPU frequency control interface.
+ */
+
+/* Each driver's CPU frequency setting is exported in this format. */
+struct cf_setting {
+ int freq; /* CPU clock in Mhz or 100ths of a percent. */
+ int volts; /* Voltage in mV. */
+ int power; /* Power consumed in mW. */
+ int lat; /* Transition latency in us. */
+ device_t dev; /* Driver providing this setting. */
+ int spec[4];/* Driver-specific storage for non-standard info. */
+};
+
+/* Maximum number of settings a given driver can have. */
+#define MAX_SETTINGS 24
+
+/* A combination of settings is a level. */
+struct cf_level {
+ struct cf_setting total_set;
+ struct cf_setting abs_set;
+ struct cf_setting rel_set[MAX_SETTINGS];
+ int rel_count;
+ TAILQ_ENTRY(cf_level) link;
+};
+
+TAILQ_HEAD(cf_level_lst, cf_level);
+
+/* Drivers should set all unknown values to this. */
+#define CPUFREQ_VAL_UNKNOWN (-1)
+
+/*
+ * Every driver offers a type of CPU control. Absolute levels are mutually
+ * exclusive while relative levels modify the current absolute level. There
+ * may be multiple absolute and relative drivers available on a given
+ * system.
+ *
+ * For example, consider a system with two absolute drivers that provide
+ * frequency settings of 100, 200 and 300, 400 and a relative driver that
+ * provides settings of 50%, 100%. The cpufreq core would export frequency
+ * levels of 50, 100, 150, 200, 300, 400.
+ *
+ * The "info only" flag signifies that settings returned by
+ * CPUFREQ_DRV_SETTINGS cannot be passed to the CPUFREQ_DRV_SET method and
+ * are only informational. This is for some drivers that can return
+ * information about settings but rely on another machine-dependent driver
+ * for actually performing the frequency transition (e.g., ACPI performance
+ * states of type "functional fixed hardware.")
+ */
+#define CPUFREQ_TYPE_MASK 0xffff
+#define CPUFREQ_TYPE_RELATIVE (1<<0)
+#define CPUFREQ_TYPE_ABSOLUTE (1<<1)
+#define CPUFREQ_FLAG_INFO_ONLY (1<<16)
+
+/*
+ * When setting a level, the caller indicates the priority of this request.
+ * Priorities determine, among other things, whether a level can be
+ * overridden by other callers. For example, if the user sets a level but
+ * the system thermal driver needs to override it for emergency cooling,
+ * the driver would use a higher priority. Once the event has passed, the
+ * driver would call cpufreq to resume any previous level.
+ */
+#define CPUFREQ_PRIO_HIGHEST 1000000
+#define CPUFREQ_PRIO_KERN 1000
+#define CPUFREQ_PRIO_USER 100
+#define CPUFREQ_PRIO_LOWEST 0
+
+/*
+ * Register and unregister a driver with the cpufreq core. Once a driver
+ * is registered, it must support calls to its CPUFREQ_GET, CPUFREQ_GET_LEVEL,
+ * and CPUFREQ_SET methods. It must also unregister before returning from
+ * its DEVICE_DETACH method.
+ */
+int cpufreq_register(device_t dev);
+int cpufreq_unregister(device_t dev);
+
+/* Allow values to be +/- a bit since sometimes we have to estimate. */
+#define CPUFREQ_CMP(x, y) (abs((x) - (y)) < 25)
+
+/*
+ * Machine-dependent functions.
+ */
+
+/* Estimate the current clock rate for the given CPU id. */
+int cpu_est_clockrate(int cpu_id, uint64_t *rate);
+
+#endif /* !_SYS_CPU_H_ */