sxitemp.c revision 1.2
1/*	$OpenBSD: sxitemp.c,v 1.2 2017/12/31 15:41:25 kettenis Exp $	*/
2/*
3 * Copyright (c) 2017 Mark Kettenis <kettenis@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
18#include <sys/param.h>
19#include <sys/systm.h>
20#include <sys/device.h>
21#include <sys/sensors.h>
22
23#include <machine/intr.h>
24#include <machine/bus.h>
25#include <machine/fdt.h>
26
27#include <dev/ofw/openfirm.h>
28#include <dev/ofw/ofw_clock.h>
29#include <dev/ofw/ofw_misc.h>
30#include <dev/ofw/ofw_pinctrl.h>
31#include <dev/ofw/fdt.h>
32
33/* Registers */
34#define THS_CTRL0			0x0000
35#define  THS_CTRL0_SENSOR_ACQ(x)	((x) & 0xffff)
36#define THS_CTRL2			0x0040
37#define  THS_CTRL2_ADC_ACQ(x)		(((x) & 0xffff) << 16)
38#define  THS_CTRL2_SENSE1_EN		(1 << 1)
39#define  THS_CTRL2_SENSE0_EN		(1 << 0)
40#define THS_INT_CTRL			0x0044
41#define  THS_INT_CTRL_THERMAL_PER(x)	(((x) & 0xfffff) << 12)
42#define THS_FILTER			0x0070
43#define  THS_FILTER_EN			(1 << 2)
44#define  THS_FILTER_TYPE(x)		((x) & 0x3)
45#define THS0_DATA			0x0080
46#define THS1_DATA			0x0084
47
48#define HREAD4(sc, reg)							\
49	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
50#define HWRITE4(sc, reg, val)						\
51	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
52
53struct sxitemp_softc {
54	struct device		sc_dev;
55	bus_space_tag_t		sc_iot;
56	bus_space_handle_t	sc_ioh;
57
58	uint64_t		(*sc_calc_temp0)(int64_t);
59	uint64_t		(*sc_calc_temp1)(int64_t);
60
61	struct ksensor		sc_sensors[2];
62	struct ksensordev	sc_sensordev;
63};
64
65int	sxitemp_match(struct device *, void *, void *);
66void	sxitemp_attach(struct device *, struct device *, void *);
67
68struct cfattach	sxitemp_ca = {
69	sizeof (struct sxitemp_softc), sxitemp_match, sxitemp_attach
70};
71
72struct cfdriver sxitemp_cd = {
73	NULL, "sxitemp", DV_DULL
74};
75
76uint64_t sxitemp_r40_calc_temp(int64_t);
77uint64_t sxitemp_h5_calc_temp0(int64_t);
78uint64_t sxitemp_h5_calc_temp1(int64_t);
79void	sxitemp_refresh_sensors(void *);
80
81int
82sxitemp_match(struct device *parent, void *match, void *aux)
83{
84	struct fdt_attach_args *faa = aux;
85
86	return (OF_is_compatible(faa->fa_node, "allwinner,sun8i-r40-ths") ||
87	    OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-ths"));
88}
89
90void
91sxitemp_attach(struct device *parent, struct device *self, void *aux)
92{
93	struct sxitemp_softc *sc = (struct sxitemp_softc *)self;
94	struct fdt_attach_args *faa = aux;
95	int node = faa->fa_node;
96
97	if (faa->fa_nreg < 1) {
98		printf(": no registers\n");
99		return;
100	}
101
102	sc->sc_iot = faa->fa_iot;
103	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
104	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
105		printf(": can't map registers\n");
106		return;
107	}
108
109	printf("\n");
110
111	pinctrl_byname(node, "default");
112
113	clock_enable_all(node);
114	reset_deassert_all(node);
115
116	if (OF_is_compatible(faa->fa_node, "allwinner,sun8i-r40-ths")) {
117		sc->sc_calc_temp0 = sxitemp_r40_calc_temp;
118		sc->sc_calc_temp1 = sxitemp_r40_calc_temp;
119	} else {
120		sc->sc_calc_temp0 = sxitemp_h5_calc_temp0;
121		sc->sc_calc_temp1 = sxitemp_h5_calc_temp1;
122	}
123
124	/* Start data acquisition. */
125	HWRITE4(sc, THS_FILTER, THS_FILTER_EN | THS_FILTER_TYPE(1));
126	HWRITE4(sc, THS_INT_CTRL, THS_INT_CTRL_THERMAL_PER(800));
127	HWRITE4(sc, THS_CTRL0, THS_CTRL0_SENSOR_ACQ(31));
128	HWRITE4(sc, THS_CTRL2, THS_CTRL2_ADC_ACQ(31) |
129	    THS_CTRL2_SENSE0_EN | THS_CTRL2_SENSE1_EN);
130
131	/* Register sensors. */
132	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
133	    sizeof(sc->sc_sensordev.xname));
134	strlcpy(sc->sc_sensors[0].desc, "CPU", sizeof(sc->sc_sensors[0].desc));
135	sc->sc_sensors[0].type = SENSOR_TEMP;
136	sc->sc_sensors[0].flags = SENSOR_FINVALID;
137	sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[0]);
138	strlcpy(sc->sc_sensors[1].desc, "GPU", sizeof(sc->sc_sensors[1].desc));
139	sc->sc_sensors[1].type = SENSOR_TEMP;
140	sc->sc_sensors[1].flags = SENSOR_FINVALID;
141	sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[1]);
142	sensordev_install(&sc->sc_sensordev);
143	sensor_task_register(sc, sxitemp_refresh_sensors, 5);
144}
145
146uint64_t
147sxitemp_r40_calc_temp(int64_t data)
148{
149	/* From BSP as the R40 User Manual says T.B.D. */
150	return -112500 * data + 250000000;
151}
152
153uint64_t
154sxitemp_h5_calc_temp0(int64_t data)
155{
156	if (data > 0x500)
157		return -119100 * data + 223000000;
158	else
159		return -145200 * data + 259000000;
160}
161
162uint64_t
163sxitemp_h5_calc_temp1(int64_t data)
164{
165	if (data > 0x500)
166		return -119100 * data + 223000000;
167	else
168		return -159000 * data + 276000000;
169}
170
171void
172sxitemp_refresh_sensors(void *arg)
173{
174	struct sxitemp_softc *sc = arg;
175	uint32_t data;
176
177	data = HREAD4(sc, THS0_DATA);
178	sc->sc_sensors[0].value = sc->sc_calc_temp0(data) + 273150000;
179	sc->sc_sensors[0].flags &= ~SENSOR_FINVALID;
180
181	data = HREAD4(sc, THS1_DATA);
182	sc->sc_sensors[1].value = sc->sc_calc_temp1(data) + 273150000;
183	sc->sc_sensors[1].flags &= ~SENSOR_FINVALID;
184}
185