예제 #1
0
static int
acpi_perf_probe(device_t dev)
{
	ACPI_HANDLE handle;
	ACPI_OBJECT *pkg;
	struct resource *res;
	ACPI_BUFFER buf;
	int error, rid, type;

	if (resource_disabled("acpi_perf", 0))
		return (ENXIO);

	/*
	 * Check the performance state registers.  If they are of type
	 * "functional fixed hardware", we attach quietly since we will
	 * only be providing information on settings to other drivers.
	 */
	error = ENXIO;
	handle = acpi_get_handle(dev);
	buf.Pointer = NULL;
	buf.Length = ACPI_ALLOCATE_BUFFER;
	if (ACPI_FAILURE(AcpiEvaluateObject(handle, "_PCT", NULL, &buf)))
		return (error);
	pkg = (ACPI_OBJECT *)buf.Pointer;
	if (ACPI_PKG_VALID(pkg, 2)) {
		rid = 0;
		error = acpi_PkgGas(dev, pkg, 0, &type, &rid, &res, 0);
		switch (error) {
		case 0:
			bus_release_resource(dev, type, rid, res);
			bus_delete_resource(dev, type, rid);
			device_set_desc(dev, "ACPI CPU Frequency Control");
			break;
		case EOPNOTSUPP:
			device_quiet(dev);
			error = 0;
			break;
		}
	}
	AcpiOsFree(buf.Pointer);

	return (error);
}
예제 #2
0
/*
 * Parse a _CST package and set up its Cx states.  Since the _CST object
 * can change dynamically, our notify handler may call this function
 * to clean up and probe the new _CST package.
 */
static int
acpi_cpu_cx_cst(struct acpi_cpu_softc *sc)
{
    struct	 acpi_cx *cx_ptr;
    ACPI_STATUS	 status;
    ACPI_BUFFER	 buf;
    ACPI_OBJECT	*top;
    ACPI_OBJECT	*pkg;
    uint32_t	 count;
    int		 i;

    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);

    buf.Pointer = NULL;
    buf.Length = ACPI_ALLOCATE_BUFFER;
    status = AcpiEvaluateObject(sc->cpu_handle, "_CST", NULL, &buf);
    if (ACPI_FAILURE(status))
	return (ENXIO);

    /* _CST is a package with a count and at least one Cx package. */
    top = (ACPI_OBJECT *)buf.Pointer;
    if (!ACPI_PKG_VALID(top, 2) || acpi_PkgInt32(top, 0, &count) != 0) {
	device_printf(sc->cpu_dev, "invalid _CST package\n");
	AcpiOsFree(buf.Pointer);
	return (ENXIO);
    }
    if (count != top->Package.Count - 1) {
	device_printf(sc->cpu_dev, "invalid _CST state count (%d != %d)\n",
	       count, top->Package.Count - 1);
	count = top->Package.Count - 1;
    }
    if (count > MAX_CX_STATES) {
	device_printf(sc->cpu_dev, "_CST has too many states (%d)\n", count);
	count = MAX_CX_STATES;
    }

    /* Set up all valid states. */
    sc->cpu_cx_count = 0;
    cx_ptr = sc->cpu_cx_states;
    for (i = 0; i < count; i++) {
	pkg = &top->Package.Elements[i + 1];
	if (!ACPI_PKG_VALID(pkg, 4) ||
	    acpi_PkgInt32(pkg, 1, &cx_ptr->type) != 0 ||
	    acpi_PkgInt32(pkg, 2, &cx_ptr->trans_lat) != 0 ||
	    acpi_PkgInt32(pkg, 3, &cx_ptr->power) != 0) {

	    device_printf(sc->cpu_dev, "skipping invalid Cx state package\n");
	    continue;
	}

	/* Validate the state to see if we should use it. */
	switch (cx_ptr->type) {
	case ACPI_STATE_C1:
	    sc->cpu_non_c3 = i;
	    cx_ptr++;
	    sc->cpu_cx_count++;
	    continue;
	case ACPI_STATE_C2:
	    if (cx_ptr->trans_lat > 100) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				 "acpi_cpu%d: C2[%d] not available.\n",
				 device_get_unit(sc->cpu_dev), i));
		continue;
	    }
	    sc->cpu_non_c3 = i;
	    break;
	case ACPI_STATE_C3:
	default:
	    if (cx_ptr->trans_lat > 1000 ||
		(cpu_quirks & CPU_QUIRK_NO_C3) != 0) {

		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				 "acpi_cpu%d: C3[%d] not available.\n",
				 device_get_unit(sc->cpu_dev), i));
		continue;
	    }
	    break;
	}

#ifdef notyet
	/* Free up any previous register. */
	if (cx_ptr->p_lvlx != NULL) {
	    bus_release_resource(sc->cpu_dev, 0, 0, cx_ptr->p_lvlx);
	    cx_ptr->p_lvlx = NULL;
	}
#endif

	/* Allocate the control register for C2 or C3. */
	acpi_PkgGas(sc->cpu_dev, pkg, 0, &cx_ptr->res_type, &sc->cpu_rid,
	    &cx_ptr->p_lvlx, RF_SHAREABLE);
	if (cx_ptr->p_lvlx) {
	    sc->cpu_rid++;
	    ACPI_DEBUG_PRINT((ACPI_DB_INFO,
			     "acpi_cpu%d: Got C%d - %d latency\n",
			     device_get_unit(sc->cpu_dev), cx_ptr->type,
			     cx_ptr->trans_lat));
	    cx_ptr++;
	    sc->cpu_cx_count++;
	}
    }
    AcpiOsFree(buf.Pointer);

    return (0);
}
예제 #3
0
/* Probe and setup any valid performance states (Px). */
static int
acpi_perf_evaluate(device_t dev)
{
	struct acpi_perf_softc *sc;
	ACPI_BUFFER buf;
	ACPI_OBJECT *pkg, *res;
	ACPI_STATUS status;
	int count, error, i, j;
	static int once = 1;
	uint32_t *p;

	/* Get the control values and parameters for each state. */
	error = ENXIO;
	sc = device_get_softc(dev);
	buf.Pointer = NULL;
	buf.Length = ACPI_ALLOCATE_BUFFER;
	status = AcpiEvaluateObject(sc->handle, "_PSS", NULL, &buf);
	if (ACPI_FAILURE(status))
		return (ENXIO);

	pkg = (ACPI_OBJECT *)buf.Pointer;
	if (!ACPI_PKG_VALID(pkg, 1)) {
		device_printf(dev, "invalid top level _PSS package\n");
		goto out;
	}
	sc->px_count = pkg->Package.Count;

	sc->px_states = malloc(sc->px_count * sizeof(struct acpi_px),
	    M_ACPIPERF, M_WAITOK | M_ZERO);
	if (sc->px_states == NULL)
		goto out;

	/*
	 * Each state is a package of {CoreFreq, Power, TransitionLatency,
	 * BusMasterLatency, ControlVal, StatusVal}, sorted from highest
	 * performance to lowest.
	 */
	count = 0;
	for (i = 0; i < sc->px_count; i++) {
		res = &pkg->Package.Elements[i];
		if (!ACPI_PKG_VALID(res, 6)) {
			if (once) {
				once = 0;
				device_printf(dev, "invalid _PSS package\n");
			}
			continue;
		}

		/* Parse the rest of the package into the struct. */
		p = &sc->px_states[count].core_freq;
		for (j = 0; j < 6; j++, p++)
			acpi_PkgInt32(res, j, p);

		/*
		 * Check for some impossible frequencies that some systems
		 * use to indicate they don't actually support this Px state.
		 */
		if (sc->px_states[count].core_freq == 0 ||
		    sc->px_states[count].core_freq == 9999 ||
		    sc->px_states[count].core_freq == 0x9999 ||
		    sc->px_states[count].core_freq >= 0xffff)
			continue;

		/* Check for duplicate entries */
		if (count > 0 &&
		    sc->px_states[count - 1].core_freq ==
			sc->px_states[count].core_freq)
			continue;

		count++;
	}
	sc->px_count = count;

	/* No valid Px state found so give up. */
	if (count == 0)
		goto out;
	AcpiOsFree(buf.Pointer);

	/* Get the control and status registers (one of each). */
	buf.Pointer = NULL;
	buf.Length = ACPI_ALLOCATE_BUFFER;
	status = AcpiEvaluateObject(sc->handle, "_PCT", NULL, &buf);
	if (ACPI_FAILURE(status))
		goto out;

	/* Check the package of two registers, each a Buffer in GAS format. */
	pkg = (ACPI_OBJECT *)buf.Pointer;
	if (!ACPI_PKG_VALID(pkg, 2)) {
		device_printf(dev, "invalid perf register package\n");
		goto out;
	}

	error = acpi_PkgGas(sc->dev, pkg, 0, &sc->perf_ctrl_type, &sc->px_rid,
	    &sc->perf_ctrl, 0);
	if (error) {
		/*
		 * If the register is of type FFixedHW, we can only return
		 * info, we can't get or set new settings.
		 */
		if (error == EOPNOTSUPP) {
			sc->info_only = TRUE;
			error = 0;
		} else
			device_printf(dev, "failed in PERF_CTL attach\n");
		goto out;
	}
	sc->px_rid++;

	error = acpi_PkgGas(sc->dev, pkg, 1, &sc->perf_sts_type, &sc->px_rid,
	    &sc->perf_status, 0);
	if (error) {
		if (error == EOPNOTSUPP) {
			sc->info_only = TRUE;
			error = 0;
		} else
			device_printf(dev, "failed in PERF_STATUS attach\n");
		goto out;
	}
	sc->px_rid++;

	/* Get our current limit and register for notifies. */
	acpi_px_available(sc);
	AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
	    acpi_px_notify, sc);
	error = 0;

out:
	if (error) {
		if (sc->px_states) {
			free(sc->px_states, M_ACPIPERF);
			sc->px_states = NULL;
		}
		if (sc->perf_ctrl) {
			bus_release_resource(sc->dev, sc->perf_ctrl_type, 0,
			    sc->perf_ctrl);
			bus_delete_resource(sc->dev, sc->perf_ctrl_type, 0);
			sc->perf_ctrl = NULL;
		}
		if (sc->perf_status) {
			bus_release_resource(sc->dev, sc->perf_sts_type, 1,
			    sc->perf_status);
			bus_delete_resource(sc->dev, sc->perf_sts_type, 1);
			sc->perf_status = NULL;
		}
		sc->px_rid = 0;
		sc->px_count = 0;
	}
	if (buf.Pointer)
		AcpiOsFree(buf.Pointer);
	return (error);
}