/* $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; } }