1/* $NetBSD: rpi_vcmbox.c,v 1.8 2021/03/08 13:53:08 mlelstv Exp $ */
2
3/*-
4 * Copyright (c) 2013 Jared D. McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * Raspberry Pi VC Mailbox Interface
31 */
32
33#include <sys/cdefs.h>
34__KERNEL_RCSID(0, "$NetBSD: rpi_vcmbox.c,v 1.8 2021/03/08 13:53:08 mlelstv Exp $");
35
36#include <sys/param.h>
37#include <sys/types.h>
38#include <sys/bus.h>
39#include <sys/conf.h>
40#include <sys/device.h>
41#include <sys/endian.h>
42#include <sys/kmem.h>
43#include <sys/systm.h>
44#include <sys/sysctl.h>
45
46#include <dev/sysmon/sysmonvar.h>
47
48#include <arm/broadcom/bcm2835_mbox.h>
49
50#include <evbarm/rpi/vcio.h>
51#include <evbarm/rpi/vcprop.h>
52
53struct vcmbox_temp_request {
54	struct vcprop_buffer_hdr	vb_hdr;
55	struct vcprop_tag_temperature	vbt_temp;
56	struct vcprop_tag end;
57} __packed;
58
59struct vcmbox_clockrate_request {
60	struct vcprop_buffer_hdr	vb_hdr;
61	struct vcprop_tag_clockrate	vbt_clockrate;
62	struct vcprop_tag end;
63} __packed;
64
65#define RATE2MHZ(rate)	((rate) / 1000000)
66#define MHZ2RATE(mhz)	((mhz) * 1000000)
67
68#define VCMBOX_INIT_REQUEST(r)		VCPROP_INIT_REQUEST(r)
69#define VCMBOX_INIT_TAG(s, t)		VCPROP_INIT_TAG(s, t)
70
71struct vcmbox_softc {
72	device_t		sc_dev;
73
74	/* temperature sensor */
75	struct sysmon_envsys	*sc_sme;
76#define VCMBOX_SENSOR_TEMP	0
77#define VCMBOX_NSENSORS		1
78	envsys_data_t		sc_sensor[VCMBOX_NSENSORS];
79
80	/* cpu frequency scaling */
81	struct sysctllog	*sc_log;
82	uint32_t		sc_cpu_minrate;
83	uint32_t		sc_cpu_maxrate;
84	int			sc_node_target;
85	int			sc_node_current;
86	int			sc_node_min;
87	int			sc_node_max;
88};
89
90static const char *vcmbox_sensor_name[VCMBOX_NSENSORS] = {
91	"temperature",
92};
93
94static int vcmbox_sensor_id[VCMBOX_NSENSORS] = {
95	VCPROP_TEMP_SOC,
96};
97
98static int	vcmbox_match(device_t, cfdata_t, void *);
99static void	vcmbox_attach(device_t, device_t, void *);
100
101static int	vcmbox_read_temp(struct vcmbox_softc *, uint32_t, int,
102				 uint32_t *);
103static int	vcmbox_read_clockrate(struct vcmbox_softc *, uint32_t, int,
104				 uint32_t *);
105static int	vcmbox_write_clockrate(struct vcmbox_softc *, uint32_t, int,
106				 uint32_t);
107
108static int	vcmbox_cpufreq_init(struct vcmbox_softc *);
109static int	vcmbox_cpufreq_sysctl_helper(SYSCTLFN_PROTO);
110
111static void	vcmbox_create_sensors(struct vcmbox_softc *);
112static void	vcmbox_sensor_get_limits(struct sysmon_envsys *,
113					 envsys_data_t *,
114					 sysmon_envsys_lim_t *, uint32_t *);
115static void	vcmbox_sensor_refresh(struct sysmon_envsys *,
116				      envsys_data_t *);
117
118CFATTACH_DECL_NEW(vcmbox, sizeof(struct vcmbox_softc),
119    vcmbox_match, vcmbox_attach, NULL, NULL);
120
121static int
122vcmbox_match(device_t parent, cfdata_t match, void *aux)
123{
124	return 1;
125}
126
127static void
128vcmbox_attach(device_t parent, device_t self, void *aux)
129{
130	struct vcmbox_softc *sc = device_private(self);
131
132	sc->sc_dev = self;
133
134	aprint_naive("\n");
135	aprint_normal("\n");
136
137	vcmbox_cpufreq_init(sc);
138
139	sc->sc_sme = sysmon_envsys_create();
140	sc->sc_sme->sme_cookie = sc;
141	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
142	sc->sc_sme->sme_refresh = vcmbox_sensor_refresh;
143	sc->sc_sme->sme_get_limits = vcmbox_sensor_get_limits;
144	vcmbox_create_sensors(sc);
145	if (sysmon_envsys_register(sc->sc_sme) == 0)
146		return;
147
148	aprint_error_dev(self, "unable to register with sysmon\n");
149	sysmon_envsys_destroy(sc->sc_sme);
150}
151
152static int
153vcmbox_read_temp(struct vcmbox_softc *sc, uint32_t tag, int id, uint32_t *val)
154{
155	struct vcmbox_temp_request vb;
156	uint32_t res;
157	int error;
158
159	VCMBOX_INIT_REQUEST(vb);
160	VCMBOX_INIT_TAG(vb.vbt_temp, tag);
161	vb.vbt_temp.id = htole32(id);
162	error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res);
163	if (error)
164		return error;
165	if (!vcprop_buffer_success_p(&vb.vb_hdr) ||
166	    !vcprop_tag_success_p(&vb.vbt_temp.tag)) {
167		return EIO;
168	}
169	*val = le32toh(vb.vbt_temp.value);
170
171	return 0;
172}
173
174static int
175vcmbox_read_clockrate(struct vcmbox_softc *sc, uint32_t tag, int id,
176    uint32_t *val)
177{
178	struct vcmbox_clockrate_request vb;
179	uint32_t res;
180	int error;
181
182	VCMBOX_INIT_REQUEST(vb);
183	VCMBOX_INIT_TAG(vb.vbt_clockrate, tag);
184	vb.vbt_clockrate.id = htole32(id);
185	error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res);
186	if (error)
187		return error;
188	if (!vcprop_buffer_success_p(&vb.vb_hdr) ||
189	    !vcprop_tag_success_p(&vb.vbt_clockrate.tag)) {
190		return EIO;
191	}
192	*val = le32toh(vb.vbt_clockrate.rate);
193
194	return 0;
195}
196
197static int
198vcmbox_write_clockrate(struct vcmbox_softc *sc, uint32_t tag, int id,
199    uint32_t val)
200{
201	struct vcmbox_clockrate_request vb;
202	uint32_t res;
203	int error;
204
205	VCMBOX_INIT_REQUEST(vb);
206	VCMBOX_INIT_TAG(vb.vbt_clockrate, tag);
207	vb.vbt_clockrate.id = htole32(id);
208	vb.vbt_clockrate.rate = htole32(val);
209	error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res);
210	if (error)
211		return error;
212	if (!vcprop_buffer_success_p(&vb.vb_hdr) ||
213	    !vcprop_tag_success_p(&vb.vbt_clockrate.tag)) {
214		return EIO;
215	}
216
217	return 0;
218}
219
220
221static int
222vcmbox_cpufreq_init(struct vcmbox_softc *sc)
223{
224	const struct sysctlnode *node, *cpunode, *freqnode;
225	int error;
226	static char available[20];
227
228	error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_MIN_CLOCKRATE,
229	    VCPROP_CLK_ARM, &sc->sc_cpu_minrate);
230	if (error) {
231		aprint_error_dev(sc->sc_dev, "couldn't read min clkrate (%d)\n",
232		    error);
233		return error;
234	}
235	error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_MAX_CLOCKRATE,
236	    VCPROP_CLK_ARM, &sc->sc_cpu_maxrate);
237	if (error) {
238		aprint_error_dev(sc->sc_dev, "couldn't read max clkrate (%d)\n",
239		    error);
240		return error;
241	}
242
243	error = sysctl_createv(&sc->sc_log, 0, NULL, &node,
244	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
245	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
246	if (error)
247		goto sysctl_failed;
248	error = sysctl_createv(&sc->sc_log, 0, &node, &cpunode,
249	    0, CTLTYPE_NODE, "cpu", NULL,
250	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
251	if (error)
252		goto sysctl_failed;
253	error = sysctl_createv(&sc->sc_log, 0, &cpunode, &freqnode,
254	    0, CTLTYPE_NODE, "frequency", NULL,
255	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
256	if (error)
257		goto sysctl_failed;
258
259	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
260	    CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
261	    vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
262	    CTL_CREATE, CTL_EOL);
263	if (error)
264		goto sysctl_failed;
265	sc->sc_node_target = node->sysctl_num;
266
267	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
268	    0, CTLTYPE_INT, "current", NULL,
269	    vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
270	    CTL_CREATE, CTL_EOL);
271	if (error)
272		goto sysctl_failed;
273	sc->sc_node_current = node->sysctl_num;
274
275	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
276	    0, CTLTYPE_INT, "min", NULL,
277	    vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
278	    CTL_CREATE, CTL_EOL);
279	if (error)
280		goto sysctl_failed;
281	sc->sc_node_min = node->sysctl_num;
282
283	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
284	    0, CTLTYPE_INT, "max", NULL,
285	    vcmbox_cpufreq_sysctl_helper, 0, (void *)sc, 0,
286	    CTL_CREATE, CTL_EOL);
287	if (error)
288		goto sysctl_failed;
289	sc->sc_node_max = node->sysctl_num;
290
291	snprintf(available, sizeof(available), "%" PRIu32 " %" PRIu32,
292	    RATE2MHZ(sc->sc_cpu_minrate), RATE2MHZ(sc->sc_cpu_maxrate));
293
294	error = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
295	    CTLFLAG_PERMANENT, CTLTYPE_STRING, "available", NULL,
296	    NULL, 0, available, strlen(available),
297	    CTL_CREATE, CTL_EOL);
298	if (error)
299		goto sysctl_failed;
300
301	return 0;
302
303sysctl_failed:
304	aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes (%d)\n",
305	    error);
306	sysctl_teardown(&sc->sc_log);
307	return error;
308}
309
310static int
311vcmbox_cpufreq_sysctl_helper(SYSCTLFN_ARGS)
312{
313	struct sysctlnode node;
314	struct vcmbox_softc *sc;
315	int fq, oldfq = 0, error;
316	uint32_t rate;
317
318	node = *rnode;
319	sc = node.sysctl_data;
320
321	node.sysctl_data = &fq;
322
323	if (rnode->sysctl_num == sc->sc_node_target ||
324	    rnode->sysctl_num == sc->sc_node_current) {
325		error = vcmbox_read_clockrate(sc, VCPROPTAG_GET_CLOCKRATE,
326		    VCPROP_CLK_ARM, &rate);
327		if (error)
328			return error;
329		fq = RATE2MHZ(rate);
330		if (rnode->sysctl_num == sc->sc_node_target)
331			oldfq = fq;
332	} else if (rnode->sysctl_num == sc->sc_node_min) {
333		fq = RATE2MHZ(sc->sc_cpu_minrate);
334	} else if (rnode->sysctl_num == sc->sc_node_max) {
335		fq = RATE2MHZ(sc->sc_cpu_maxrate);
336	} else
337		return EOPNOTSUPP;
338
339	error = sysctl_lookup(SYSCTLFN_CALL(&node));
340	if (error || newp == NULL)
341		return error;
342
343	if (fq == oldfq || rnode->sysctl_num != sc->sc_node_target)
344		return 0;
345
346	if (fq < RATE2MHZ(sc->sc_cpu_minrate))
347		fq = RATE2MHZ(sc->sc_cpu_minrate);
348	if (fq > RATE2MHZ(sc->sc_cpu_maxrate))
349		fq = RATE2MHZ(sc->sc_cpu_maxrate);
350
351	return vcmbox_write_clockrate(sc, VCPROPTAG_SET_CLOCKRATE,
352	    VCPROP_CLK_ARM, MHZ2RATE(fq));
353}
354
355static void
356vcmbox_create_sensors(struct vcmbox_softc *sc)
357{
358	uint32_t val;
359
360	sc->sc_sensor[VCMBOX_SENSOR_TEMP].sensor = VCMBOX_SENSOR_TEMP;
361	sc->sc_sensor[VCMBOX_SENSOR_TEMP].units = ENVSYS_STEMP;
362	sc->sc_sensor[VCMBOX_SENSOR_TEMP].state = ENVSYS_SINVALID;
363	sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags = ENVSYS_FMONLIMITS |
364						  ENVSYS_FHAS_ENTROPY;
365	strlcpy(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc,
366	    vcmbox_sensor_name[VCMBOX_SENSOR_TEMP],
367	    sizeof(sc->sc_sensor[VCMBOX_SENSOR_TEMP].desc));
368	if (vcmbox_read_temp(sc, VCPROPTAG_GET_MAX_TEMPERATURE,
369			     vcmbox_sensor_id[VCMBOX_SENSOR_TEMP], &val) == 0) {
370		sc->sc_sensor[VCMBOX_SENSOR_TEMP].value_max =
371		    val * 1000 + 273150000;
372		sc->sc_sensor[VCMBOX_SENSOR_TEMP].flags |= ENVSYS_FVALID_MAX;
373	}
374	sysmon_envsys_sensor_attach(sc->sc_sme,
375	    &sc->sc_sensor[VCMBOX_SENSOR_TEMP]);
376}
377
378static void
379vcmbox_sensor_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
380    sysmon_envsys_lim_t *limits, uint32_t *props)
381{
382	struct vcmbox_softc *sc = sme->sme_cookie;
383	uint32_t val;
384
385	*props = 0;
386
387	if (edata->units == ENVSYS_STEMP) {
388		if (vcmbox_read_temp(sc, VCPROPTAG_GET_MAX_TEMPERATURE,
389				     vcmbox_sensor_id[edata->sensor], &val))
390			return;
391		*props = PROP_CRITMAX;
392		limits->sel_critmax = val * 1000 + 273150000;
393	}
394}
395
396static void
397vcmbox_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
398{
399	struct vcmbox_softc *sc = sme->sme_cookie;
400	uint32_t val;
401
402	edata->state = ENVSYS_SINVALID;
403
404	if (edata->units == ENVSYS_STEMP) {
405		if (vcmbox_read_temp(sc, VCPROPTAG_GET_TEMPERATURE,
406				     vcmbox_sensor_id[edata->sensor], &val))
407			return;
408
409		edata->value_cur = val * 1000 + 273150000;
410		edata->state = ENVSYS_SVALID;
411	}
412}
413