From b66ad2f5276c3fa95c334072439a258553c77a1e Mon Sep 17 00:00:00 2001 From: claudio Date: Mon, 11 Dec 2023 11:15:44 +0000 Subject: [PATCH] Add ampchwm(4) a driver to access the Ampere Altra Fine-Grained Power Telemetry This adds per core energy sensors (in Joules) and one per SoC temparature sensor. OK kettenis@ deraadt@ --- sys/arch/arm64/conf/GENERIC | 3 +- sys/arch/arm64/conf/files.arm64 | 6 +- sys/arch/arm64/dev/ampchwm.c | 272 ++++++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 sys/arch/arm64/dev/ampchwm.c diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC index 3b6bc19cf0a..0b518c26f78 100644 --- a/sys/arch/arm64/conf/GENERIC +++ b/sys/arch/arm64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.277 2023/07/22 22:43:53 patrick Exp $ +# $OpenBSD: GENERIC,v 1.278 2023/12/11 11:15:44 claudio Exp $ # # GENERIC machine description file # @@ -49,6 +49,7 @@ acpiiort* at acpi? smmu* at acpiiort? acpipci* at acpi? pci* at acpipci? +ampchwm* at acpi? acpipwrres* at acpi? acpitz* at acpi? ahci* at acpi? diff --git a/sys/arch/arm64/conf/files.arm64 b/sys/arch/arm64/conf/files.arm64 index 9ec032f2445..4414dd8d981 100644 --- a/sys/arch/arm64/conf/files.arm64 +++ b/sys/arch/arm64/conf/files.arm64 @@ -1,4 +1,4 @@ -# $OpenBSD: files.arm64,v 1.66 2023/01/14 12:11:11 kettenis Exp $ +# $OpenBSD: files.arm64,v 1.67 2023/12/11 11:15:44 claudio Exp $ maxpartitions 16 maxusers 2 8 128 @@ -279,6 +279,10 @@ file arch/arm64/dev/acpipci.c acpipci attach smmu at acpiiort with smmu_acpi file arch/arm64/dev/smmu_acpi.c smmu_acpi +device ampchwm +attach ampchwm at acpi +file arch/arm64/dev/ampchwm.c ampchwm + device apm attach apm at mainbus file arch/arm64/dev/apm.c apm needs-flag diff --git a/sys/arch/arm64/dev/ampchwm.c b/sys/arch/arm64/dev/ampchwm.c new file mode 100644 index 00000000000..2115e0d4060 --- /dev/null +++ b/sys/arch/arm64/dev/ampchwm.c @@ -0,0 +1,272 @@ +/* $OpenBSD: ampchwm.c,v 1.1 2023/12/11 11:15:44 claudio Exp $ */ +/* + * Copyright (c) 2023 Claudio Jeker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +int ampchwm_match(struct device *, void *, void *); +void ampchwm_attach(struct device *, struct device *, void *); + +#define HWMON_ID 0x304d5748 +#define HWMON_UNIT_CELSIUS 0x01 +#define HWMON_UNIT_JOULES 0x10 +#define HWMON_UNIT_MILIJOULES 0x11 +#define HWMON_UNIT_MICROJOULES 0x12 +#define HWMON_MAX_METRIC_COUNT 2 + +union metrics_hdr { + uint64_t data; + struct { + uint32_t id; + uint16_t version; + uint16_t count; + }; +}; + +union metric_hdr { + uint64_t data[3]; + struct { + char label[16]; + uint8_t unit; + uint8_t data_size; + uint16_t data_count; + uint32_t pad; + }; +}; + + +struct ampchwm_softc { + struct device sc_dev; + struct acpi_softc *sc_acpi; + struct aml_node *sc_node; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + size_t sc_size; + + uint16_t sc_count; + struct { + struct ksensor *sc_sens; + uint16_t sc_sens_offset; + uint16_t sc_sens_count; + uint16_t sc_sens_size; + uint16_t sc_sens_unit; + } sc_metrics[HWMON_MAX_METRIC_COUNT]; + + struct ksensordev sc_sensdev; + struct sensor_task *sc_sens_task; +}; + +const struct cfattach ampchwm_ca = { + sizeof(struct ampchwm_softc), ampchwm_match, ampchwm_attach +}; + +struct cfdriver ampchwm_cd = { + NULL, "ampchwm", DV_DULL +}; + +const char *ampchwm_hids[] = { + "AMPC0005", + NULL +}; + +int ampchwm_attach_sensors(struct ampchwm_softc *, int, + union metric_hdr *, uint16_t *); +void ampchwm_refresh_sensors(void *); +void ampchwm_update_sensor(struct ampchwm_softc *, int, int); + + +int +ampchwm_match(struct device *parent, void *match, void *aux) +{ + struct acpi_attach_args *aaa = aux; + struct cfdata *cf = match; + + if (aaa->aaa_naddr < 1) + return (0); + return (acpi_matchhids(aaa, ampchwm_hids, cf->cf_driver->cd_name)); +} + +void +ampchwm_attach(struct device *parent, struct device *self, void *aux) +{ + struct ampchwm_softc *sc = (struct ampchwm_softc *)self; + struct acpi_attach_args *aaa = aux; + union metrics_hdr hdr; + union metric_hdr metric; + uint16_t offset = 0; + int i; + + sc->sc_acpi = (struct acpi_softc *)parent; + sc->sc_node = aaa->aaa_node; + + printf(" %s", sc->sc_node->name); + printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]); + + sc->sc_iot = aaa->aaa_bst[0]; + sc->sc_size = aaa->aaa_size[0]; + + if (bus_space_map(sc->sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0], + 0, &sc->sc_ioh)) { + printf(": can't map registers\n"); + return; + } + + bus_space_read_region_8(sc->sc_iot, sc->sc_ioh, offset, &hdr.data, 1); + + if (hdr.id != HWMON_ID) { + printf(": bad id %x\n", hdr.id); + goto unmap; + } + + printf(": ver %d", hdr.version); + + strlcpy(sc->sc_sensdev.xname, sc->sc_dev.dv_xname, + sizeof(sc->sc_sensdev.xname)); + + offset += sizeof(hdr); + for (i = 0; i < hdr.count; i++) { + bus_space_read_region_8(sc->sc_iot, sc->sc_ioh, offset, + metric.data, 3); + if (ampchwm_attach_sensors(sc, i, &metric, &offset)) + goto unmap; + } + sc->sc_count = MIN(hdr.count, HWMON_MAX_METRIC_COUNT); + + sensordev_install(&sc->sc_sensdev); + sc->sc_sens_task = sensor_task_register(sc, ampchwm_refresh_sensors, 1); + printf("\n"); + + return; +unmap: + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); + return; +} + +int +ampchwm_attach_sensors(struct ampchwm_softc *sc, int num, + union metric_hdr *metric, uint16_t *offsetp) +{ + uint16_t off = *offsetp; + int i, count = 0; + + if (num >= HWMON_MAX_METRIC_COUNT) { + if (num == HWMON_MAX_METRIC_COUNT) + printf(" ignoring extra metrics"); + return 0; + } + + off += sizeof(*metric); + /* skip 0 values since those are disabled cores */ + for (i = 0; i < metric->data_count; i++) { + if (bus_space_read_8(sc->sc_iot, sc->sc_ioh, + off + i * 8) == 0) + continue; + count++; + } + + sc->sc_metrics[num].sc_sens = mallocarray(count, + sizeof(struct ksensor), M_DEVBUF, M_NOWAIT); + if (sc->sc_metrics[num].sc_sens == NULL) { + printf(" out of memory\n"); + return -1; + } + + sc->sc_metrics[num].sc_sens_offset = off; + sc->sc_metrics[num].sc_sens_count = count; + sc->sc_metrics[num].sc_sens_unit = metric->unit; + if (metric->data_size == 0) + sc->sc_metrics[num].sc_sens_size = 8; + else + sc->sc_metrics[num].sc_sens_size = 4; + + for (i = 0; i < count; i++) { + struct ksensor *s = &sc->sc_metrics[num].sc_sens[i]; + + strlcpy(s->desc, metric->label, sizeof(s->desc)); + if (metric->unit == HWMON_UNIT_CELSIUS) + s->type = SENSOR_TEMP; + else + s->type = SENSOR_ENERGY; + sensor_attach(&sc->sc_sensdev, s); + + ampchwm_update_sensor(sc, num, i); + } + + off += metric->data_count * 8; + + printf(", %d \"%s\"", count, metric->label); + *offsetp = off; + return 0; +} + +void +ampchwm_refresh_sensors(void *arg) +{ + struct ampchwm_softc *sc = arg; + int num, i; + + for (num = 0; num < sc->sc_count; num++) + for (i = 0; i < sc->sc_metrics[num].sc_sens_count; i++) + ampchwm_update_sensor(sc, num, i); +} + +void +ampchwm_update_sensor(struct ampchwm_softc *sc, int num, int i) +{ + struct ksensor *s; + uint64_t v; + + KASSERT(i < sc->sc_metrics[num].sc_sens_count); + + s = &sc->sc_metrics[num].sc_sens[i]; + if (sc->sc_metrics[num].sc_sens_size == 8) { + v = bus_space_read_8(sc->sc_iot, sc->sc_ioh, + sc->sc_metrics[num].sc_sens_offset + i * sizeof(v)); + } else { + v = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + sc->sc_metrics[num].sc_sens_offset + i * sizeof(v)); + } + + if (v == 0) { + s->flags = SENSOR_FUNKNOWN; + s->status = SENSOR_S_UNKNOWN; + } else { + s->flags = 0; + s->status = SENSOR_S_OK; + } + + switch (sc->sc_metrics[num].sc_sens_unit) { + case HWMON_UNIT_CELSIUS: + s->value = v * 1000 * 1000 + 273150000; + break; + case HWMON_UNIT_JOULES: + v *= 1000; + case HWMON_UNIT_MILIJOULES: + v *= 1000; + case HWMON_UNIT_MICROJOULES: + s->value = v; + break; + } +} -- 2.20.1