1 /* $OpenBSD: ampchwm.c,v 1.1 2023/12/11 11:15:44 claudio Exp $ */ 2 /* 3 * Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include <sys/param.h> 18 #include <sys/systm.h> 19 #include <sys/types.h> 20 #include <sys/device.h> 21 #include <sys/malloc.h> 22 #include <sys/sensors.h> 23 24 #include <dev/acpi/acpireg.h> 25 #include <dev/acpi/acpivar.h> 26 #include <dev/acpi/amltypes.h> 27 28 int ampchwm_match(struct device *, void *, void *); 29 void ampchwm_attach(struct device *, struct device *, void *); 30 31 #define HWMON_ID 0x304d5748 32 #define HWMON_UNIT_CELSIUS 0x01 33 #define HWMON_UNIT_JOULES 0x10 34 #define HWMON_UNIT_MILIJOULES 0x11 35 #define HWMON_UNIT_MICROJOULES 0x12 36 #define HWMON_MAX_METRIC_COUNT 2 37 38 union metrics_hdr { 39 uint64_t data; 40 struct { 41 uint32_t id; 42 uint16_t version; 43 uint16_t count; 44 }; 45 }; 46 47 union metric_hdr { 48 uint64_t data[3]; 49 struct { 50 char label[16]; 51 uint8_t unit; 52 uint8_t data_size; 53 uint16_t data_count; 54 uint32_t pad; 55 }; 56 }; 57 58 59 struct ampchwm_softc { 60 struct device sc_dev; 61 struct acpi_softc *sc_acpi; 62 struct aml_node *sc_node; 63 64 bus_space_tag_t sc_iot; 65 bus_space_handle_t sc_ioh; 66 size_t sc_size; 67 68 uint16_t sc_count; 69 struct { 70 struct ksensor *sc_sens; 71 uint16_t sc_sens_offset; 72 uint16_t sc_sens_count; 73 uint16_t sc_sens_size; 74 uint16_t sc_sens_unit; 75 } sc_metrics[HWMON_MAX_METRIC_COUNT]; 76 77 struct ksensordev sc_sensdev; 78 struct sensor_task *sc_sens_task; 79 }; 80 81 const struct cfattach ampchwm_ca = { 82 sizeof(struct ampchwm_softc), ampchwm_match, ampchwm_attach 83 }; 84 85 struct cfdriver ampchwm_cd = { 86 NULL, "ampchwm", DV_DULL 87 }; 88 89 const char *ampchwm_hids[] = { 90 "AMPC0005", 91 NULL 92 }; 93 94 int ampchwm_attach_sensors(struct ampchwm_softc *, int, 95 union metric_hdr *, uint16_t *); 96 void ampchwm_refresh_sensors(void *); 97 void ampchwm_update_sensor(struct ampchwm_softc *, int, int); 98 99 100 int 101 ampchwm_match(struct device *parent, void *match, void *aux) 102 { 103 struct acpi_attach_args *aaa = aux; 104 struct cfdata *cf = match; 105 106 if (aaa->aaa_naddr < 1) 107 return (0); 108 return (acpi_matchhids(aaa, ampchwm_hids, cf->cf_driver->cd_name)); 109 } 110 111 void 112 ampchwm_attach(struct device *parent, struct device *self, void *aux) 113 { 114 struct ampchwm_softc *sc = (struct ampchwm_softc *)self; 115 struct acpi_attach_args *aaa = aux; 116 union metrics_hdr hdr; 117 union metric_hdr metric; 118 uint16_t offset = 0; 119 int i; 120 121 sc->sc_acpi = (struct acpi_softc *)parent; 122 sc->sc_node = aaa->aaa_node; 123 124 printf(" %s", sc->sc_node->name); 125 printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]); 126 127 sc->sc_iot = aaa->aaa_bst[0]; 128 sc->sc_size = aaa->aaa_size[0]; 129 130 if (bus_space_map(sc->sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0], 131 0, &sc->sc_ioh)) { 132 printf(": can't map registers\n"); 133 return; 134 } 135 136 bus_space_read_region_8(sc->sc_iot, sc->sc_ioh, offset, &hdr.data, 1); 137 138 if (hdr.id != HWMON_ID) { 139 printf(": bad id %x\n", hdr.id); 140 goto unmap; 141 } 142 143 printf(": ver %d", hdr.version); 144 145 strlcpy(sc->sc_sensdev.xname, sc->sc_dev.dv_xname, 146 sizeof(sc->sc_sensdev.xname)); 147 148 offset += sizeof(hdr); 149 for (i = 0; i < hdr.count; i++) { 150 bus_space_read_region_8(sc->sc_iot, sc->sc_ioh, offset, 151 metric.data, 3); 152 if (ampchwm_attach_sensors(sc, i, &metric, &offset)) 153 goto unmap; 154 } 155 sc->sc_count = MIN(hdr.count, HWMON_MAX_METRIC_COUNT); 156 157 sensordev_install(&sc->sc_sensdev); 158 sc->sc_sens_task = sensor_task_register(sc, ampchwm_refresh_sensors, 1); 159 printf("\n"); 160 161 return; 162 unmap: 163 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); 164 return; 165 } 166 167 int 168 ampchwm_attach_sensors(struct ampchwm_softc *sc, int num, 169 union metric_hdr *metric, uint16_t *offsetp) 170 { 171 uint16_t off = *offsetp; 172 int i, count = 0; 173 174 if (num >= HWMON_MAX_METRIC_COUNT) { 175 if (num == HWMON_MAX_METRIC_COUNT) 176 printf(" ignoring extra metrics"); 177 return 0; 178 } 179 180 off += sizeof(*metric); 181 /* skip 0 values since those are disabled cores */ 182 for (i = 0; i < metric->data_count; i++) { 183 if (bus_space_read_8(sc->sc_iot, sc->sc_ioh, 184 off + i * 8) == 0) 185 continue; 186 count++; 187 } 188 189 sc->sc_metrics[num].sc_sens = mallocarray(count, 190 sizeof(struct ksensor), M_DEVBUF, M_NOWAIT); 191 if (sc->sc_metrics[num].sc_sens == NULL) { 192 printf(" out of memory\n"); 193 return -1; 194 } 195 196 sc->sc_metrics[num].sc_sens_offset = off; 197 sc->sc_metrics[num].sc_sens_count = count; 198 sc->sc_metrics[num].sc_sens_unit = metric->unit; 199 if (metric->data_size == 0) 200 sc->sc_metrics[num].sc_sens_size = 8; 201 else 202 sc->sc_metrics[num].sc_sens_size = 4; 203 204 for (i = 0; i < count; i++) { 205 struct ksensor *s = &sc->sc_metrics[num].sc_sens[i]; 206 207 strlcpy(s->desc, metric->label, sizeof(s->desc)); 208 if (metric->unit == HWMON_UNIT_CELSIUS) 209 s->type = SENSOR_TEMP; 210 else 211 s->type = SENSOR_ENERGY; 212 sensor_attach(&sc->sc_sensdev, s); 213 214 ampchwm_update_sensor(sc, num, i); 215 } 216 217 off += metric->data_count * 8; 218 219 printf(", %d \"%s\"", count, metric->label); 220 *offsetp = off; 221 return 0; 222 } 223 224 void 225 ampchwm_refresh_sensors(void *arg) 226 { 227 struct ampchwm_softc *sc = arg; 228 int num, i; 229 230 for (num = 0; num < sc->sc_count; num++) 231 for (i = 0; i < sc->sc_metrics[num].sc_sens_count; i++) 232 ampchwm_update_sensor(sc, num, i); 233 } 234 235 void 236 ampchwm_update_sensor(struct ampchwm_softc *sc, int num, int i) 237 { 238 struct ksensor *s; 239 uint64_t v; 240 241 KASSERT(i < sc->sc_metrics[num].sc_sens_count); 242 243 s = &sc->sc_metrics[num].sc_sens[i]; 244 if (sc->sc_metrics[num].sc_sens_size == 8) { 245 v = bus_space_read_8(sc->sc_iot, sc->sc_ioh, 246 sc->sc_metrics[num].sc_sens_offset + i * sizeof(v)); 247 } else { 248 v = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 249 sc->sc_metrics[num].sc_sens_offset + i * sizeof(v)); 250 } 251 252 if (v == 0) { 253 s->flags = SENSOR_FUNKNOWN; 254 s->status = SENSOR_S_UNKNOWN; 255 } else { 256 s->flags = 0; 257 s->status = SENSOR_S_OK; 258 } 259 260 switch (sc->sc_metrics[num].sc_sens_unit) { 261 case HWMON_UNIT_CELSIUS: 262 s->value = v * 1000 * 1000 + 273150000; 263 break; 264 case HWMON_UNIT_JOULES: 265 v *= 1000; 266 case HWMON_UNIT_MILIJOULES: 267 v *= 1000; 268 case HWMON_UNIT_MICROJOULES: 269 s->value = v; 270 break; 271 } 272 } 273