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