xref: /freebsd/sys/arm/mv/armada/thermal.c (revision f126890a)
1 /*-
2  * Copyright (c) 2017 Semihalf.
3  * Copyright (c) 2017 Stormshield.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/sysctl.h>
30 #include <sys/systm.h>
31 #include <sys/bus.h>
32 #include <sys/conf.h>
33 #include <sys/rman.h>
34 #include <sys/kernel.h>
35 #include <sys/lock.h>
36 #include <sys/module.h>
37 #include <sys/mutex.h>
38 #include <sys/resource.h>
39 
40 #include <machine/fdt.h>
41 
42 #include <dev/ofw/ofw_bus_subr.h>
43 
44 #define	READOUT_TO_C(temp)	((temp) / 1000)
45 
46 #define	STAT_RID		0
47 #define	CTRL_RID		1
48 
49 #define	TSEN_STAT_READOUT_VALID	0x1
50 
51 #define	A380_TSEN_CTRL_RESET	(1 << 8)
52 
53 struct armada_thermal_softc;
54 
55 typedef struct armada_thermal_data {
56 	/* Initialize the sensor */
57 	void (*tsen_init)(struct armada_thermal_softc *);
58 
59 	/* Test for a valid sensor value */
60 	boolean_t (*is_valid)(struct armada_thermal_softc *);
61 
62 	/* Formula coefficients: temp = (b + m * reg) / div */
63 	u_long coef_b;
64 	u_long coef_m;
65 	u_long coef_div;
66 
67 	boolean_t inverted;
68 
69 	/* Shift and mask to access the sensor temperature */
70 	u_int temp_shift;
71 	u_int temp_mask;
72 	u_int is_valid_shift;
73 } armada_tdata_t;
74 
75 static boolean_t armada_tsen_readout_valid(struct armada_thermal_softc *);
76 static int armada_tsen_get_temp(struct armada_thermal_softc *, u_long *);
77 static void armada380_tsen_init(struct armada_thermal_softc *);
78 static void armada_temp_update(void *);
79 
80 static const armada_tdata_t armada380_tdata = {
81 	.tsen_init = armada380_tsen_init,
82 	.is_valid = armada_tsen_readout_valid,
83 	.is_valid_shift = 10,
84 	.temp_shift = 0,
85 	.temp_mask = 0x3ff,
86 	.coef_b = 1172499100UL,
87 	.coef_m = 2000096UL,
88 	.coef_div = 4201,
89 	.inverted = TRUE,
90 };
91 
92 static int armada_thermal_probe(device_t);
93 static int armada_thermal_attach(device_t);
94 static int armada_thermal_detach(device_t);
95 
96 static device_method_t armada_thermal_methods[] = {
97 	DEVMETHOD(device_probe,		armada_thermal_probe),
98 	DEVMETHOD(device_attach,	armada_thermal_attach),
99 	DEVMETHOD(device_detach,	armada_thermal_detach),
100 
101 	DEVMETHOD_END
102 };
103 
104 struct armada_thermal_softc {
105 	device_t		dev;
106 
107 	struct resource		*stat_res;
108 	struct resource		*ctrl_res;
109 
110 	struct callout		temp_upd;
111 	struct mtx		temp_upd_mtx;
112 
113 	const armada_tdata_t	*tdata;
114 
115 	u_long			chip_temperature;
116 };
117 
118 static driver_t	armada_thermal_driver = {
119 	"armada_thermal",
120 	armada_thermal_methods,
121 	sizeof(struct armada_thermal_softc)
122 };
123 
124 DRIVER_MODULE(armada_thermal, simplebus, armada_thermal_driver, 0, 0);
125 DRIVER_MODULE(armada_thermal, ofwbus, armada_thermal_driver, 0, 0);
126 
127 static int
128 armada_thermal_probe(device_t dev)
129 {
130 	struct armada_thermal_softc *sc;
131 
132 	sc = device_get_softc(dev);
133 
134 	if (!ofw_bus_status_okay(dev))
135 		return (ENXIO);
136 
137 	if (ofw_bus_is_compatible(dev, "marvell,armada380-thermal")) {
138 		device_set_desc(dev, "Armada380 Thermal Control");
139 		sc->tdata = &armada380_tdata;
140 
141 		return (BUS_PROBE_DEFAULT);
142 	}
143 
144 	return (ENXIO);
145 }
146 
147 static int
148 armada_thermal_attach(device_t dev)
149 {
150 	struct armada_thermal_softc *sc;
151 	const armada_tdata_t *tdata;
152 	struct sysctl_ctx_list *sctx;
153 	struct sysctl_oid_list *schildren;
154 	int timeout;
155 	int rid;
156 
157 	sc = device_get_softc(dev);
158 
159 	/* Allocate CTRL and STAT register spaces */
160 	rid = STAT_RID;
161 	sc->stat_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
162 	    &rid, RF_ACTIVE);
163 	if (sc->stat_res == NULL) {
164 		device_printf(dev,
165 		    "Could not allocate memory for the status register\n");
166 		return (ENXIO);
167 	}
168 
169 	rid = CTRL_RID;
170 	sc->ctrl_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
171 	    &rid, RF_ACTIVE);
172 	if (sc->ctrl_res == NULL) {
173 		device_printf(dev,
174 		    "Could not allocate memory for the control register\n");
175 		bus_release_resource(dev, SYS_RES_MEMORY,
176 		    rman_get_rid(sc->stat_res), sc->stat_res);
177 		sc->stat_res = NULL;
178 		return (ENXIO);
179 	}
180 
181 	/* Now initialize the sensor */
182 	tdata = sc->tdata;
183 	tdata->tsen_init(sc);
184 	/* Set initial temperature value */
185 	for (timeout = 1000; timeout > 0; timeout--) {
186 		if (armada_tsen_get_temp(sc, &sc->chip_temperature) == 0)
187 			break;
188 		DELAY(10);
189 	}
190 	if (timeout <= 0) {
191 		bus_release_resource(dev, SYS_RES_MEMORY,
192 		    rman_get_rid(sc->stat_res), sc->stat_res);
193 		sc->stat_res = NULL;
194 		bus_release_resource(dev, SYS_RES_MEMORY,
195 		    rman_get_rid(sc->ctrl_res), sc->ctrl_res);
196 		sc->ctrl_res = NULL;
197 		return (ENXIO);
198 	}
199 	/* Initialize mutex */
200 	mtx_init(&sc->temp_upd_mtx, "Armada Thermal", NULL, MTX_DEF);
201 	/* Set up the temperature update callout */
202 	callout_init_mtx(&sc->temp_upd, &sc->temp_upd_mtx, 0);
203 	/* Schedule callout */
204 	callout_reset(&sc->temp_upd, hz, armada_temp_update, sc);
205 
206 	sctx = device_get_sysctl_ctx(dev);
207 	schildren = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
208 	SYSCTL_ADD_LONG(sctx, schildren, OID_AUTO, "temperature",
209 	    CTLFLAG_RD, &sc->chip_temperature, "SoC temperature");
210 
211 	return (0);
212 }
213 
214 static int
215 armada_thermal_detach(device_t dev)
216 {
217 	struct armada_thermal_softc *sc;
218 
219 	sc = device_get_softc(dev);
220 
221 	if (!device_is_attached(dev))
222 		return (0);
223 
224 	callout_drain(&sc->temp_upd);
225 
226 	sc->chip_temperature = 0;
227 
228 	bus_release_resource(dev, SYS_RES_MEMORY,
229 	    rman_get_rid(sc->stat_res), sc->stat_res);
230 	sc->stat_res = NULL;
231 
232 	bus_release_resource(dev, SYS_RES_MEMORY,
233 	    rman_get_rid(sc->ctrl_res), sc->ctrl_res);
234 	sc->ctrl_res = NULL;
235 
236 	return (0);
237 }
238 
239 static boolean_t
240 armada_tsen_readout_valid(struct armada_thermal_softc *sc)
241 {
242 	const armada_tdata_t *tdata;
243 	uint32_t tsen_stat;
244 	boolean_t is_valid;
245 
246 	tdata = sc->tdata;
247 	tsen_stat = bus_read_4(sc->stat_res, 0);
248 
249 	tsen_stat >>= tdata->is_valid_shift;
250 	is_valid = ((tsen_stat & TSEN_STAT_READOUT_VALID) != 0);
251 
252 	return (is_valid);
253 }
254 
255 static int
256 armada_tsen_get_temp(struct armada_thermal_softc *sc, u_long *temp)
257 {
258 	const armada_tdata_t *tdata;
259 	uint32_t reg;
260 	u_long tmp;
261 	u_long m, b, div;
262 
263 	tdata = sc->tdata;
264 	/* Check if the readout is valid */
265 	if ((tdata->is_valid != NULL) && !tdata->is_valid(sc))
266 		return (EIO);
267 
268 	reg = bus_read_4(sc->stat_res, 0);
269 	reg = (reg >> tdata->temp_shift) & tdata->temp_mask;
270 
271 	/* Get formula coefficients */
272 	b = tdata->coef_b;
273 	m = tdata->coef_m;
274 	div = tdata->coef_div;
275 
276 	if (tdata->inverted)
277 		tmp = ((m * reg) - b) / div;
278 	else
279 		tmp = (b - (m * reg)) / div;
280 
281 	*temp = READOUT_TO_C(tmp);
282 
283 	return (0);
284 }
285 
286 static void
287 armada380_tsen_init(struct armada_thermal_softc *sc)
288 {
289 	uint32_t tsen_ctrl;
290 
291 	tsen_ctrl = bus_read_4(sc->ctrl_res, 0);
292 	if ((tsen_ctrl & A380_TSEN_CTRL_RESET) == 0) {
293 		tsen_ctrl |= A380_TSEN_CTRL_RESET;
294 		bus_write_4(sc->ctrl_res, 0, tsen_ctrl);
295 		DELAY(10000);
296 	}
297 }
298 
299 static void
300 armada_temp_update(void *arg)
301 {
302 	struct armada_thermal_softc *sc;
303 
304 	sc = arg;
305 	/* Update temperature value, keel old if the readout is not valid */
306 	(void)armada_tsen_get_temp(sc, &sc->chip_temperature);
307 
308 	callout_reset(&sc->temp_upd, hz, armada_temp_update, sc);
309 }
310