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