xref: /openbsd-src/sys/arch/arm64/dev/ampchwm.c (revision b66ad2f5276c3fa95c334072439a258553c77a1e)
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