Page MenuHomeFreeBSD

D55474.id172752.diff
No OneTemporary

D55474.id172752.diff

diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -760,6 +760,7 @@
dev/acpica/acpi_battery.c optional acpi
dev/acpica/acpi_button.c optional acpi
dev/acpica/acpi_cmbat.c optional acpi
+dev/acpica/acpi_cppc_lib.c optional acpi
dev/acpica/acpi_cpu.c optional acpi
dev/acpica/acpi_ec.c optional acpi
dev/acpica/acpi_ged.c optional acpi_ged acpi
diff --git a/sys/dev/acpica/acpi_cppc_lib.h b/sys/dev/acpica/acpi_cppc_lib.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/acpica/acpi_cppc_lib.h
@@ -0,0 +1,39 @@
+#include <sys/cdefs.h>
+#include <sys/bus.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+
+struct acpi_cppc_ctx;
+
+enum acpi_cppc_regs {
+ ACPI_CPPC_HIGHTEST_PERF,
+ ACPI_CPPC_NOMINAL_PERF,
+ ACPI_CPPC_NOLINEAR_PERF,
+ ACPI_CPPC_LOWEST_PERF,
+ ACPI_CPPC_MIN_REG,
+ ACPI_CPPC_MAX_REG,
+ ACPI_CPPC_EPP_REG,
+ ACPI_CPPC_ENABLE_REG,
+ ACPI_CPPC_AUTONOMOUS_REG,
+ ACPI_CPPC_LOWEST_FREQ,
+ ACPI_CPPC_NOMINAL_FREQ,
+};
+
+enum acpi_cppc_feature {
+ ACPI_CPPC_HAS_LOW_FREQ = (1 << 0),
+ ACPI_CPPC_HAS_NOMINAL_FREQ = (1 << 1),
+ ACPI_CPPC_HAS_MIN = (1 << 2),
+ ACPI_CPPC_HAS_MAX = (1 << 3),
+ ACPI_CPPC_HAS_EPP = (1 << 4),
+ ACPI_CPPC_HAS_DESIRED = (1 << 5),
+ ACPI_CPPC_HAS_ENABLE = (1 << 6),
+ ACPI_CPPC_HAS_AUTONOMOUS = (1 << 7),
+};
+
+struct acpi_cppc_ctx *acpi_cppc_ctx_init(device_t dev);
+void acpi_cppc_ctx_free(device_t dev, struct acpi_cppc_ctx *ctx);
+bool acpi_cppc_optional_object_valid(ACPI_OBJECT *pkg, int idx);
+uint64_t acpi_cppc_read_reg(struct acpi_cppc_ctx *ctx, enum acpi_cppc_regs reg);
+void acpi_cppc_write_reg(struct acpi_cppc_ctx *ctx, enum acpi_cppc_regs reg,
+ uint64_t val);
+uint32_t acpi_cppc_get_features(struct acpi_cppc_ctx *ctx);
diff --git a/sys/dev/acpica/acpi_cppc_lib.c b/sys/dev/acpica/acpi_cppc_lib.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/acpica/acpi_cppc_lib.c
@@ -0,0 +1,346 @@
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+
+#include <machine/acpica_machdep.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+
+#include <dev/acpica/acpivar.h>
+
+#include "acpi_cppc_lib.h"
+
+#define BITS_VALUE(bits, val) \
+ (((val) & (bits)) >> (ffsll((bits)) - 1))
+#define BITS_WITH_VALUE(bits, val) \
+ (((uintmax_t)(val) << (ffsll((bits)) - 1)) & (bits))
+#define SET_BITS_VALUE(var, bits, val) \
+ ((var) = ((var) & ~(bits)) | BITS_WITH_VALUE((bits), (val)))
+
+register_t acpi_ffh_read(device_t dev, struct resource *res);
+void acpi_ffh_write(device_t dev, struct resource *res, register_t val);
+
+struct acpi_cppc_reg {
+ int type;
+ int mask;
+ struct resource *res;
+};
+
+struct acpi_cppc_item {
+ int type;
+ union {
+ struct acpi_cppc_reg reg;
+ uint64_t int_value;
+ };
+};
+
+struct acpi_cppc_ctx {
+ struct acpi_cppc_item hightest_perf;
+ struct acpi_cppc_item nominal_perf;
+ struct acpi_cppc_item nolinear_perf;
+ struct acpi_cppc_item lowest_perf;
+ struct acpi_cppc_item min_reg;
+ struct acpi_cppc_item max_reg;
+ struct acpi_cppc_item epp_reg;
+ struct acpi_cppc_item enable_reg;
+ struct acpi_cppc_item autonomous_reg;
+ struct acpi_cppc_item lowest_freq;
+ struct acpi_cppc_item nominal_freq;
+ device_t dev;
+ uint32_t features;
+};
+
+static int
+acpi_cppc_alloc_gas(device_t dev, ACPI_OBJECT *obj, int rid,
+ struct acpi_cppc_reg *reg)
+{
+ ACPI_GENERIC_ADDRESS *gas;
+
+ if (obj->Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3)
+ return (EINVAL);
+ gas = (ACPI_GENERIC_ADDRESS *)(obj->Buffer.Pointer + 3);
+ reg->type = gas->SpaceId;
+ reg->mask = (1ULL << gas->BitWidth) - 1;
+ reg->mask <<= gas->BitOffset;
+ return (acpi_bus_alloc_gas(dev, &reg->type, rid, gas, &reg->res,
+ RF_SHAREABLE));
+}
+static int
+acpi_cppc_parse_item(device_t dev, ACPI_OBJECT *obj,
+ struct acpi_cppc_item *item, int rid)
+{
+ item->type = obj->Type;
+ if (obj->Type == ACPI_TYPE_INTEGER) {
+ item->int_value = obj->Integer.Value;
+ return (0);
+ } else if (obj->Type == ACPI_TYPE_BUFFER) {
+ return (acpi_cppc_alloc_gas(dev, obj, rid, &item->reg));
+ }
+ return (ENXIO);
+}
+
+static uint64_t
+acpi_cppc_read_gas(device_t dev, struct acpi_cppc_item *item)
+{
+ uint64_t val;
+
+ switch (item->reg.type) {
+#ifdef SYS_RES_FFH
+ case SYS_RES_FFH:
+ val = acpi_ffh_read(dev, item->reg.res);
+ break;
+#endif
+ case SYS_RES_IOPORT:
+ case SYS_RES_MEMORY:
+ val = bus_read_8(item->reg.res, 0);
+ break;
+ default:
+ val = 0;
+ KASSERT(0, ("Impossible to read other type in CPPC"));
+ }
+ return (val);
+}
+
+static uint64_t
+acpi_cppc_read_item(device_t dev, struct acpi_cppc_item *item)
+{
+ uint64_t val;
+
+ if (item->type == ACPI_TYPE_INTEGER)
+ return (item->int_value);
+ else if (item->type == ACPI_TYPE_BUFFER) {
+ val = acpi_cppc_read_gas(dev, item);
+ val = BITS_VALUE(val, item->reg.mask);
+ return (val);
+ }
+ KASSERT(0, ("Impossible to read other type in CPPC"));
+
+ return (0);
+}
+
+static void
+acpi_cppc_write_item(device_t dev, struct acpi_cppc_item *item, uint64_t val)
+{
+ uint64_t cur;
+
+ if (item->type == ACPI_TYPE_BUFFER) {
+ cur = acpi_cppc_read_gas(dev, item);
+ SET_BITS_VALUE(cur, item->reg.mask, val);
+ switch (item->reg.type) {
+#ifdef SYS_RES_FFH
+ case SYS_RES_FFH:
+ return (acpi_ffh_write(dev, item->reg.res, cur));
+#endif
+ case SYS_RES_IOPORT:
+ case SYS_RES_MEMORY:
+ return (bus_write_8(item->reg.res, 0, cur));
+ }
+ }
+ KASSERT(0, ("Impossible to write other type in CPPC"));
+}
+
+static void
+acpi_cppc_free_item(device_t dev, struct acpi_cppc_item *item, int rid)
+{
+ if (item->type == ACPI_TYPE_BUFFER) {
+ bus_release_resource(dev, item->reg.type, rid, item->reg.res);
+ bus_delete_resource(dev, item->reg.type, rid);
+ }
+}
+
+bool
+acpi_cppc_optional_object_valid(ACPI_OBJECT *pkg, int idx)
+{
+ ACPI_OBJECT *obj;
+ int i;
+
+ if (!ACPI_PKG_VALID(pkg, idx))
+ return (false);
+ obj = &pkg->Package.Elements[idx];
+ if (!obj)
+ return (false);
+ if (obj->Type == ACPI_TYPE_INTEGER) {
+ return (obj->Integer.Value != 0);
+ } else if (obj->Type == ACPI_TYPE_BUFFER) {
+ if (obj->Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3)
+ return (false);
+ for (i = 0; i < sizeof(ACPI_GENERIC_ADDRESS); ++i)
+ if (obj->Buffer.Pointer[3 + i] != 0)
+ return (true);
+ }
+
+ return (false);
+}
+
+void
+acpi_cppc_ctx_free(device_t dev, struct acpi_cppc_ctx *ctx)
+{
+ acpi_cppc_free_item(dev, &ctx->nominal_freq, 11);
+ acpi_cppc_free_item(dev, &ctx->lowest_freq, 10);
+ acpi_cppc_free_item(dev, &ctx->epp_reg, 9);
+ acpi_cppc_free_item(dev, &ctx->autonomous_reg, 8);
+ acpi_cppc_free_item(dev, &ctx->enable_reg, 7);
+ acpi_cppc_free_item(dev, &ctx->max_reg, 6);
+ acpi_cppc_free_item(dev, &ctx->min_reg, 5);
+ acpi_cppc_free_item(dev, &ctx->lowest_perf, 4);
+ acpi_cppc_free_item(dev, &ctx->nolinear_perf, 3);
+ acpi_cppc_free_item(dev, &ctx->nominal_perf, 2);
+ acpi_cppc_free_item(dev, &ctx->hightest_perf, 1);
+ free(ctx, M_DEVBUF);
+}
+
+struct acpi_cppc_ctx *
+acpi_cppc_ctx_init(device_t dev)
+{
+ ACPI_HANDLE handle;
+ ACPI_BUFFER buf;
+ ACPI_OBJECT *pkg;
+ struct acpi_cppc_ctx *ctx;
+ int err;
+
+ handle = acpi_get_handle(dev);
+ buf.Pointer = NULL;
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+ if (ACPI_FAILURE(AcpiEvaluateObjectTyped(handle, "_CPC", NULL, &buf,
+ ACPI_TYPE_PACKAGE)))
+ return (NULL);
+ pkg = buf.Pointer;
+ ctx = malloc(sizeof(struct acpi_cppc_ctx), M_DEVBUF, M_WAITOK);
+
+ KASSERT(acpi_cppc_optional_object_valid(pkg, 2),
+ ("Highest Performance is mandatory in CPPC"));
+ if ((err = acpi_cppc_parse_item(dev, &pkg->Package.Elements[2],
+ &ctx->hightest_perf, 1)) != 0)
+ goto end;
+ KASSERT(acpi_cppc_optional_object_valid(pkg, 3),
+ ("Nominal Performance is mandatory in CPPC"));
+ if ((err = acpi_cppc_parse_item(dev, &pkg->Package.Elements[3],
+ &ctx->nominal_perf, 2)) != 0)
+ goto end;
+ KASSERT(acpi_cppc_optional_object_valid(pkg, 4),
+ ("Lowest Nonlinear Performance is mandatory in CPPC"));
+ if ((err = acpi_cppc_parse_item(dev, &pkg->Package.Elements[4],
+ &ctx->nolinear_perf, 3)) != 0)
+ goto end;
+ KASSERT(acpi_cppc_optional_object_valid(pkg, 5),
+ ("Lowest Performance is mandatory in CPPC"));
+ if ((err = acpi_cppc_parse_item(dev, &pkg->Package.Elements[5],
+ &ctx->lowest_perf, 4)) != 0)
+ goto end;
+ if (acpi_cppc_optional_object_valid(pkg, 8)) {
+ if ((err = acpi_cppc_parse_item(dev, &pkg->Package.Elements[8],
+ &ctx->min_reg, 5)) != 0)
+ goto end;
+ ctx->features |= ACPI_CPPC_HAS_MIN;
+ }
+ if (acpi_cppc_optional_object_valid(pkg, 9)) {
+ if ((err = acpi_cppc_parse_item(dev, &pkg->Package.Elements[9],
+ &ctx->max_reg, 6)) != 0)
+ goto end;
+ ctx->features |= ACPI_CPPC_HAS_MAX;
+ }
+ if (acpi_cppc_optional_object_valid(pkg, 16)) {
+ if ((err = acpi_cppc_parse_item(dev, &pkg->Package.Elements[16],
+ &ctx->enable_reg, 7)) != 0)
+ goto end;
+ ctx->features |= ACPI_CPPC_HAS_ENABLE;
+ }
+ if (acpi_cppc_optional_object_valid(pkg, 17)) {
+ if ((err = acpi_cppc_parse_item(dev, &pkg->Package.Elements[17],
+ &ctx->autonomous_reg, 8)) != 0)
+ goto end;
+ ctx->features |= ACPI_CPPC_HAS_AUTONOMOUS;
+ }
+ if (acpi_cppc_optional_object_valid(pkg, 19)) {
+ if ((err = acpi_cppc_parse_item(dev, &pkg->Package.Elements[19],
+ &ctx->epp_reg, 9)) != 0)
+ goto end;
+ ctx->features |= ACPI_CPPC_HAS_EPP;
+ }
+ if (acpi_cppc_optional_object_valid(pkg, 21)) {
+ if ((err = acpi_cppc_parse_item(dev, &pkg->Package.Elements[21],
+ &ctx->lowest_freq, 10)) != 0)
+ goto end;
+ ctx->features |= ACPI_CPPC_HAS_LOW_FREQ;
+ }
+ if (acpi_cppc_optional_object_valid(pkg, 22)) {
+ if ((err = acpi_cppc_parse_item(dev, &pkg->Package.Elements[22],
+ &ctx->nominal_freq, 11)) != 0)
+ goto end;
+ ctx->features |= ACPI_CPPC_HAS_NOMINAL_FREQ;
+ }
+ ctx->dev = dev;
+end:
+ if (err)
+ acpi_cppc_ctx_free(dev, ctx);
+
+ AcpiOsFree(buf.Pointer);
+ return (ctx);
+}
+
+static struct acpi_cppc_item *
+acpi_cppc_map_regs(struct acpi_cppc_ctx *ctx, enum acpi_cppc_regs reg)
+{
+ struct acpi_cppc_item *item;
+
+ switch (reg) {
+ case ACPI_CPPC_HIGHTEST_PERF:
+ item = &ctx->hightest_perf;
+ break;
+ case ACPI_CPPC_NOMINAL_PERF:
+ item = &ctx->nominal_perf;
+ break;
+ case ACPI_CPPC_NOLINEAR_PERF:
+ item = &ctx->nolinear_perf;
+ break;
+ case ACPI_CPPC_LOWEST_PERF:
+ item = &ctx->lowest_perf;
+ break;
+ case ACPI_CPPC_MIN_REG:
+ item = &ctx->min_reg;
+ break;
+ case ACPI_CPPC_MAX_REG:
+ item = &ctx->max_reg;
+ break;
+ case ACPI_CPPC_EPP_REG:
+ item = &ctx->epp_reg;
+ break;
+ case ACPI_CPPC_ENABLE_REG:
+ item = &ctx->enable_reg;
+ break;
+ case ACPI_CPPC_AUTONOMOUS_REG:
+ item = &ctx->autonomous_reg;
+ break;
+ case ACPI_CPPC_LOWEST_FREQ:
+ item = &ctx->lowest_freq;
+ break;
+ case ACPI_CPPC_NOMINAL_FREQ:
+ item = &ctx->nominal_freq;
+ break;
+ default:
+ KASSERT(0, ("Unsupported register"));
+ }
+ return (item);
+}
+
+uint64_t
+acpi_cppc_read_reg(struct acpi_cppc_ctx *ctx, enum acpi_cppc_regs reg)
+{
+ return (acpi_cppc_read_item(ctx->dev, acpi_cppc_map_regs(ctx, reg)));
+}
+
+void
+acpi_cppc_write_reg(struct acpi_cppc_ctx *ctx, enum acpi_cppc_regs reg,
+ uint64_t val)
+{
+ return (
+ acpi_cppc_write_item(ctx->dev, acpi_cppc_map_regs(ctx, reg), val));
+}
+
+uint32_t
+acpi_cppc_get_features(struct acpi_cppc_ctx *ctx)
+{
+ return (ctx->features);
+}

File Metadata

Mime Type
text/plain
Expires
Sat, May 16, 3:10 PM (12 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33134281
Default Alt Text
D55474.id172752.diff (11 KB)

Event Timeline