xref: /freebsd/sys/arm/allwinner/aw_thermal.c (revision 343044c4)
1343044c4SJared McNeill /*-
2343044c4SJared McNeill  * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
3343044c4SJared McNeill  * All rights reserved.
4343044c4SJared McNeill  *
5343044c4SJared McNeill  * Redistribution and use in source and binary forms, with or without
6343044c4SJared McNeill  * modification, are permitted provided that the following conditions
7343044c4SJared McNeill  * are met:
8343044c4SJared McNeill  * 1. Redistributions of source code must retain the above copyright
9343044c4SJared McNeill  *    notice, this list of conditions and the following disclaimer.
10343044c4SJared McNeill  * 2. Redistributions in binary form must reproduce the above copyright
11343044c4SJared McNeill  *    notice, this list of conditions and the following disclaimer in the
12343044c4SJared McNeill  *    documentation and/or other materials provided with the distribution.
13343044c4SJared McNeill  *
14343044c4SJared McNeill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15343044c4SJared McNeill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16343044c4SJared McNeill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17343044c4SJared McNeill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18343044c4SJared McNeill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19343044c4SJared McNeill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20343044c4SJared McNeill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21343044c4SJared McNeill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22343044c4SJared McNeill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23343044c4SJared McNeill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24343044c4SJared McNeill  * SUCH DAMAGE.
25343044c4SJared McNeill  *
26343044c4SJared McNeill  * $FreeBSD$
27343044c4SJared McNeill  */
28343044c4SJared McNeill 
29343044c4SJared McNeill /*
30343044c4SJared McNeill  * Allwinner thermal sensor controller
31343044c4SJared McNeill  */
32343044c4SJared McNeill 
33343044c4SJared McNeill #include <sys/cdefs.h>
34343044c4SJared McNeill __FBSDID("$FreeBSD$");
35343044c4SJared McNeill 
36343044c4SJared McNeill #include <sys/param.h>
37343044c4SJared McNeill #include <sys/systm.h>
38343044c4SJared McNeill #include <sys/bus.h>
39343044c4SJared McNeill #include <sys/rman.h>
40343044c4SJared McNeill #include <sys/kernel.h>
41343044c4SJared McNeill #include <sys/sysctl.h>
42343044c4SJared McNeill #include <sys/module.h>
43343044c4SJared McNeill #include <machine/bus.h>
44343044c4SJared McNeill 
45343044c4SJared McNeill #include <dev/ofw/ofw_bus.h>
46343044c4SJared McNeill #include <dev/ofw/ofw_bus_subr.h>
47343044c4SJared McNeill 
48343044c4SJared McNeill #include <arm/allwinner/aw_sid.h>
49343044c4SJared McNeill 
50343044c4SJared McNeill #define	THS_CTRL0		0x00
51343044c4SJared McNeill #define	THS_CTRL2		0x40
52343044c4SJared McNeill #define	 SENSOR_ACQ1_SHIFT	16
53343044c4SJared McNeill #define	 SENSOR2_EN		(1 << 2)
54343044c4SJared McNeill #define	 SENSOR1_EN		(1 << 1)
55343044c4SJared McNeill #define	 SENSOR0_EN		(1 << 0)
56343044c4SJared McNeill #define	THS_INTC		0x44
57343044c4SJared McNeill #define	THS_INTS		0x48
58343044c4SJared McNeill #define	THS_FILTER		0x70
59343044c4SJared McNeill #define	 FILTER_EN		(1 << 2)
60343044c4SJared McNeill #define	THS_CALIB0		0x74
61343044c4SJared McNeill #define	THS_CALIB1		0x78
62343044c4SJared McNeill #define	THS_DATA0		0x80
63343044c4SJared McNeill #define	THS_DATA1		0x84
64343044c4SJared McNeill #define	THS_DATA2		0x88
65343044c4SJared McNeill #define	 DATA_MASK		0xfff
66343044c4SJared McNeill 
67343044c4SJared McNeill #define	TEMP_BASE		2719
68343044c4SJared McNeill #define	TEMP_MUL		1000
69343044c4SJared McNeill #define	TEMP_DIV		14186
70343044c4SJared McNeill #define	TEMP_TO_K		273
71343044c4SJared McNeill #define	ADC_ACQUIRE_TIME	(24 - 1)
72343044c4SJared McNeill #define	SENSOR_ENABLE_ALL	(SENSOR0_EN|SENSOR1_EN|SENSOR2_EN)
73343044c4SJared McNeill 
74343044c4SJared McNeill enum aw_thermal_sensor {
75343044c4SJared McNeill 	THS_SENSOR_CPU_CLUSTER0,
76343044c4SJared McNeill 	THS_SENSOR_CPU_CLUSTER1,
77343044c4SJared McNeill 	THS_SENSOR_GPU,
78343044c4SJared McNeill 	THS_SENSOR_END = -1
79343044c4SJared McNeill };
80343044c4SJared McNeill 
81343044c4SJared McNeill struct aw_thermal_sensor_config {
82343044c4SJared McNeill 	enum aw_thermal_sensor	sensor;
83343044c4SJared McNeill 	const char		*name;
84343044c4SJared McNeill 	const char		*desc;
85343044c4SJared McNeill };
86343044c4SJared McNeill 
87343044c4SJared McNeill static const struct aw_thermal_sensor_config a83t_sensor_config[] = {
88343044c4SJared McNeill 	{ .sensor = THS_SENSOR_CPU_CLUSTER0,
89343044c4SJared McNeill 	  .name = "cluster0",	.desc = "CPU cluster 0 temperature" },
90343044c4SJared McNeill 	{ .sensor = THS_SENSOR_CPU_CLUSTER1,
91343044c4SJared McNeill 	  .name = "cluster1",	.desc = "CPU cluster 1 temperature" },
92343044c4SJared McNeill 	{ .sensor = THS_SENSOR_GPU,
93343044c4SJared McNeill 	  .name = "gpu",	.desc = "GPU temperature" },
94343044c4SJared McNeill 	{ .sensor = THS_SENSOR_END }
95343044c4SJared McNeill };
96343044c4SJared McNeill 
97343044c4SJared McNeill static struct ofw_compat_data compat_data[] = {
98343044c4SJared McNeill 	{ "allwinner,sun8i-a83t-ts",	(uintptr_t)&a83t_sensor_config },
99343044c4SJared McNeill 	{ NULL,				(uintptr_t)NULL }
100343044c4SJared McNeill };
101343044c4SJared McNeill 
102343044c4SJared McNeill #define	THS_CONF(d)		\
103343044c4SJared McNeill 	(void *)ofw_bus_search_compatible((d), compat_data)->ocd_data
104343044c4SJared McNeill 
105343044c4SJared McNeill struct aw_thermal_softc {
106343044c4SJared McNeill 	struct resource			*res;
107343044c4SJared McNeill 	struct aw_thermal_sensor_config	*conf;
108343044c4SJared McNeill };
109343044c4SJared McNeill 
110343044c4SJared McNeill static struct resource_spec aw_thermal_spec[] = {
111343044c4SJared McNeill 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
112343044c4SJared McNeill 	{ -1, 0 }
113343044c4SJared McNeill };
114343044c4SJared McNeill 
115343044c4SJared McNeill #define	RD4(sc, reg)		bus_read_4((sc)->res, (reg))
116343044c4SJared McNeill #define	WR4(sc, reg, val)	bus_write_4((sc)->res, (reg), (val))
117343044c4SJared McNeill 
118343044c4SJared McNeill static int
119343044c4SJared McNeill aw_thermal_init(struct aw_thermal_softc *sc)
120343044c4SJared McNeill {
121343044c4SJared McNeill 	uint32_t calib0, calib1;
122343044c4SJared McNeill 	int error;
123343044c4SJared McNeill 
124343044c4SJared McNeill 	/* Read calibration settings from SRAM */
125343044c4SJared McNeill 	error = aw_sid_read_tscalib(&calib0, &calib1);
126343044c4SJared McNeill 	if (error != 0)
127343044c4SJared McNeill 		return (error);
128343044c4SJared McNeill 
129343044c4SJared McNeill 	/* Write calibration settings to thermal controller */
130343044c4SJared McNeill 	WR4(sc, THS_CALIB0, calib0);
131343044c4SJared McNeill 	WR4(sc, THS_CALIB1, calib1);
132343044c4SJared McNeill 
133343044c4SJared McNeill 	/* Configure ADC acquire time (CLK_IN/(N+1)) and enable sensors */
134343044c4SJared McNeill 	WR4(sc, THS_CTRL0, ADC_ACQUIRE_TIME);
135343044c4SJared McNeill 	WR4(sc, THS_CTRL2, (ADC_ACQUIRE_TIME << SENSOR_ACQ1_SHIFT) |
136343044c4SJared McNeill 	    SENSOR_ENABLE_ALL);
137343044c4SJared McNeill 
138343044c4SJared McNeill 	/* Disable interrupts */
139343044c4SJared McNeill 	WR4(sc, THS_INTC, 0);
140343044c4SJared McNeill 	WR4(sc, THS_INTS, RD4(sc, THS_INTS));
141343044c4SJared McNeill 
142343044c4SJared McNeill 	/* Enable average filter */
143343044c4SJared McNeill 	WR4(sc, THS_FILTER, RD4(sc, THS_FILTER) | FILTER_EN);
144343044c4SJared McNeill 
145343044c4SJared McNeill 	return (0);
146343044c4SJared McNeill }
147343044c4SJared McNeill 
148343044c4SJared McNeill static int
149343044c4SJared McNeill aw_thermal_gettemp(uint32_t val)
150343044c4SJared McNeill {
151343044c4SJared McNeill 	int raw;
152343044c4SJared McNeill 
153343044c4SJared McNeill 	raw = val & DATA_MASK;
154343044c4SJared McNeill 	return (((TEMP_BASE - raw) * TEMP_MUL) / TEMP_DIV) + TEMP_TO_K;
155343044c4SJared McNeill }
156343044c4SJared McNeill 
157343044c4SJared McNeill static int
158343044c4SJared McNeill aw_thermal_sysctl(SYSCTL_HANDLER_ARGS)
159343044c4SJared McNeill {
160343044c4SJared McNeill 	struct aw_thermal_softc *sc;
161343044c4SJared McNeill 	enum aw_thermal_sensor sensor;
162343044c4SJared McNeill 	int val;
163343044c4SJared McNeill 
164343044c4SJared McNeill 	sc = arg1;
165343044c4SJared McNeill 	sensor = arg2;
166343044c4SJared McNeill 
167343044c4SJared McNeill 	val = aw_thermal_gettemp(RD4(sc, THS_DATA0 + (sensor * 4)));
168343044c4SJared McNeill 
169343044c4SJared McNeill 	return sysctl_handle_opaque(oidp, &val, sizeof(val), req);
170343044c4SJared McNeill }
171343044c4SJared McNeill 
172343044c4SJared McNeill static int
173343044c4SJared McNeill aw_thermal_probe(device_t dev)
174343044c4SJared McNeill {
175343044c4SJared McNeill 	if (!ofw_bus_status_okay(dev))
176343044c4SJared McNeill 		return (ENXIO);
177343044c4SJared McNeill 
178343044c4SJared McNeill 	if (THS_CONF(dev) == NULL)
179343044c4SJared McNeill 		return (ENXIO);
180343044c4SJared McNeill 
181343044c4SJared McNeill 	device_set_desc(dev, "Allwinner Thermal Sensor Controller");
182343044c4SJared McNeill 	return (BUS_PROBE_DEFAULT);
183343044c4SJared McNeill }
184343044c4SJared McNeill 
185343044c4SJared McNeill static int
186343044c4SJared McNeill aw_thermal_attach(device_t dev)
187343044c4SJared McNeill {
188343044c4SJared McNeill 	struct aw_thermal_softc *sc;
189343044c4SJared McNeill 	int i;
190343044c4SJared McNeill 
191343044c4SJared McNeill 	sc = device_get_softc(dev);
192343044c4SJared McNeill 
193343044c4SJared McNeill 	sc->conf = THS_CONF(dev);
194343044c4SJared McNeill 
195343044c4SJared McNeill 	if (bus_alloc_resources(dev, aw_thermal_spec, &sc->res) != 0) {
196343044c4SJared McNeill 		device_printf(dev, "cannot allocate resources for device\n");
197343044c4SJared McNeill 		return (ENXIO);
198343044c4SJared McNeill 	}
199343044c4SJared McNeill 
200343044c4SJared McNeill 	if (aw_thermal_init(sc) != 0)
201343044c4SJared McNeill 		return (ENXIO);
202343044c4SJared McNeill 
203343044c4SJared McNeill 	for (i = 0; sc->conf[i].sensor != THS_SENSOR_END; i++)
204343044c4SJared McNeill 		SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
205343044c4SJared McNeill 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
206343044c4SJared McNeill 		    OID_AUTO, sc->conf[i].name,
207343044c4SJared McNeill 		    CTLTYPE_INT | CTLFLAG_RD,
208343044c4SJared McNeill 		    sc, sc->conf[i].sensor, aw_thermal_sysctl, "IK0",
209343044c4SJared McNeill 		    sc->conf[i].desc);
210343044c4SJared McNeill 
211343044c4SJared McNeill 	return (0);
212343044c4SJared McNeill }
213343044c4SJared McNeill 
214343044c4SJared McNeill static device_method_t aw_thermal_methods[] = {
215343044c4SJared McNeill 	/* Device interface */
216343044c4SJared McNeill 	DEVMETHOD(device_probe,		aw_thermal_probe),
217343044c4SJared McNeill 	DEVMETHOD(device_attach,	aw_thermal_attach),
218343044c4SJared McNeill 
219343044c4SJared McNeill 	DEVMETHOD_END
220343044c4SJared McNeill };
221343044c4SJared McNeill 
222343044c4SJared McNeill static driver_t aw_thermal_driver = {
223343044c4SJared McNeill 	"aw_thermal",
224343044c4SJared McNeill 	aw_thermal_methods,
225343044c4SJared McNeill 	sizeof(struct aw_thermal_softc),
226343044c4SJared McNeill };
227343044c4SJared McNeill 
228343044c4SJared McNeill static devclass_t aw_thermal_devclass;
229343044c4SJared McNeill 
230343044c4SJared McNeill DRIVER_MODULE(aw_thermal, simplebus, aw_thermal_driver, aw_thermal_devclass,
231343044c4SJared McNeill     0, 0);
232343044c4SJared McNeill MODULE_VERSION(aw_thermal, 1);
233