]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/allwinner/aw_thermal.c
Add support for the Allwinner H3 Thermal Sensor Controller. The H3 embeds
[FreeBSD/FreeBSD.git] / sys / arm / allwinner / aw_thermal.c
1 /*-
2  * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 /*
30  * Allwinner thermal sensor controller
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/bus.h>
39 #include <sys/rman.h>
40 #include <sys/kernel.h>
41 #include <sys/sysctl.h>
42 #include <sys/reboot.h>
43 #include <sys/module.h>
44 #include <machine/bus.h>
45
46 #include <dev/ofw/ofw_bus.h>
47 #include <dev/ofw/ofw_bus_subr.h>
48
49 #include <dev/extres/clk/clk.h>
50 #include <dev/extres/hwreset/hwreset.h>
51
52 #include <arm/allwinner/aw_sid.h>
53
54 #define THS_CTRL0               0x00
55 #define THS_CTRL1               0x04
56 #define  ADC_CALI_EN            (1 << 17)
57 #define THS_CTRL2               0x40
58 #define  SENSOR_ACQ1_SHIFT      16
59 #define  SENSOR2_EN             (1 << 2)
60 #define  SENSOR1_EN             (1 << 1)
61 #define  SENSOR0_EN             (1 << 0)
62 #define THS_INTC                0x44
63 #define THS_INTS                0x48
64 #define  THS2_DATA_IRQ_STS      (1 << 10)
65 #define  THS1_DATA_IRQ_STS      (1 << 9)
66 #define  THS0_DATA_IRQ_STS      (1 << 8)
67 #define  SHUT_INT2_STS          (1 << 6)
68 #define  SHUT_INT1_STS          (1 << 5)
69 #define  SHUT_INT0_STS          (1 << 4)
70 #define  ALARM_INT2_STS         (1 << 2)
71 #define  ALARM_INT1_STS         (1 << 1)
72 #define  ALARM_INT0_STS         (1 << 0)
73 #define THS_FILTER              0x70
74 #define THS_CALIB0              0x74
75 #define THS_CALIB1              0x78
76 #define THS_DATA0               0x80
77 #define THS_DATA1               0x84
78 #define THS_DATA2               0x88
79 #define  DATA_MASK              0xfff
80
81 #define A83T_ADC_ACQUIRE_TIME   0x17
82 #define A83T_FILTER             0x4
83 #define A83T_INTC               0x1000
84 #define A83T_TEMP_BASE          2719000
85 #define A83T_TEMP_MUL           1000
86 #define A83T_TEMP_DIV           14186
87 #define A83T_CLK_RATE           24000000
88
89 #define A64_ADC_ACQUIRE_TIME    0x190
90 #define A64_FILTER              0x6
91 #define A64_INTC                0x18000
92 #define A64_TEMP_BASE           2170000
93 #define A64_TEMP_MUL            1000
94 #define A64_TEMP_DIV            8560
95 #define A64_CLK_RATE            4000000
96
97 #define H3_ADC_ACQUIRE_TIME     0x3f
98 #define H3_FILTER               0x6
99 #define H3_INTC                 0x191000
100 #define H3_TEMP_BASE            217000000
101 #define H3_TEMP_MUL             121168
102 #define H3_TEMP_DIV             1000000
103 #define H3_CLK_RATE             4000000
104
105 #define TEMP_C_TO_K             273
106 #define SENSOR_ENABLE_ALL       (SENSOR0_EN|SENSOR1_EN|SENSOR2_EN)
107 #define SHUT_INT_ALL            (SHUT_INT0_STS|SHUT_INT1_STS|SHUT_INT2_STS)
108
109 #define MAX_SENSORS     3
110
111 struct aw_thermal_sensor {
112         const char              *name;
113         const char              *desc;
114 };
115
116 struct aw_thermal_config {
117         struct aw_thermal_sensor        sensors[MAX_SENSORS];
118         int                             nsensors;
119         uint64_t                        clk_rate;
120         uint32_t                        adc_acquire_time;
121         uint32_t                        filter;
122         uint32_t                        intc;
123         int                             temp_base;
124         int                             temp_mul;
125         int                             temp_div;
126         int                             calib;
127 };
128
129 static const struct aw_thermal_config a83t_config = {
130         .nsensors = 3,
131         .sensors = {
132                 [0] = {
133                         .name = "cluster0",
134                         .desc = "CPU cluster 0 temperature",
135                 },
136                 [1] = {
137                         .name = "cluster1",
138                         .desc = "CPU cluster 1 temperature",
139                 },
140                 [2] = {
141                         .name = "gpu",
142                         .desc = "GPU temperature",
143                 },
144         },
145         .clk_rate = A83T_CLK_RATE,
146         .adc_acquire_time = A83T_ADC_ACQUIRE_TIME,
147         .filter = A83T_FILTER,
148         .intc = A83T_INTC,
149         .temp_base = A83T_TEMP_BASE,
150         .temp_mul = A83T_TEMP_MUL,
151         .temp_div = A83T_TEMP_DIV,
152         .calib = 1,
153 };
154
155 static const struct aw_thermal_config a64_config = {
156         .nsensors = 3,
157         .sensors = {
158                 [0] = {
159                         .name = "cpu",
160                         .desc = "CPU temperature",
161                 },
162                 [1] = {
163                         .name = "gpu1",
164                         .desc = "GPU temperature 1",
165                 },
166                 [2] = {
167                         .name = "gpu2",
168                         .desc = "GPU temperature 2",
169                 },
170         },
171         .clk_rate = A64_CLK_RATE,
172         .adc_acquire_time = A64_ADC_ACQUIRE_TIME,
173         .filter = A64_FILTER,
174         .intc = A64_INTC,
175         .temp_base = A64_TEMP_BASE,
176         .temp_mul = A64_TEMP_MUL,
177         .temp_div = A64_TEMP_DIV,
178 };
179
180 static const struct aw_thermal_config h3_config = {
181         .nsensors = 1,
182         .sensors = {
183                 [0] = {
184                         .name = "cpu",
185                         .desc = "CPU temperature",
186                 },
187         },
188         .clk_rate = H3_CLK_RATE,
189         .adc_acquire_time = H3_ADC_ACQUIRE_TIME,
190         .filter = H3_FILTER,
191         .intc = H3_INTC,
192         .temp_base = H3_TEMP_BASE,
193         .temp_mul = H3_TEMP_MUL,
194         .temp_div = H3_TEMP_DIV,
195 };
196
197 static struct ofw_compat_data compat_data[] = {
198         { "allwinner,sun8i-a83t-ts",    (uintptr_t)&a83t_config },
199         { "allwinner,sun8i-h3-ts",      (uintptr_t)&h3_config },
200         { "allwinner,sun50i-a64-ts",    (uintptr_t)&a64_config },
201         { NULL,                         (uintptr_t)NULL }
202 };
203
204 #define THS_CONF(d)             \
205         (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data
206
207 struct aw_thermal_softc {
208         struct resource                 *res[2];
209         struct aw_thermal_config        *conf;
210 };
211
212 static struct resource_spec aw_thermal_spec[] = {
213         { SYS_RES_MEMORY,       0,      RF_ACTIVE },
214         { SYS_RES_IRQ,          0,      RF_ACTIVE },
215         { -1, 0 }
216 };
217
218 #define RD4(sc, reg)            bus_read_4((sc)->res[0], (reg))
219 #define WR4(sc, reg, val)       bus_write_4((sc)->res[0], (reg), (val))
220
221 static int
222 aw_thermal_init(struct aw_thermal_softc *sc)
223 {
224         uint32_t calib0, calib1;
225         int error;
226
227         if (sc->conf->calib) {
228                 /* Read calibration settings from SRAM */
229                 error = aw_sid_read_tscalib(&calib0, &calib1);
230                 if (error != 0)
231                         return (error);
232
233                 /* Write calibration settings to thermal controller */
234                 WR4(sc, THS_CALIB0, calib0);
235                 WR4(sc, THS_CALIB1, calib1);
236         }
237
238         /* Configure ADC acquire time (CLK_IN/(N+1)) and enable sensors */
239         WR4(sc, THS_CTRL1, ADC_CALI_EN);
240         WR4(sc, THS_CTRL0, sc->conf->adc_acquire_time);
241         WR4(sc, THS_CTRL2, sc->conf->adc_acquire_time << SENSOR_ACQ1_SHIFT);
242
243         /* Enable average filter */
244         WR4(sc, THS_FILTER, sc->conf->filter);
245
246         /* Enable interrupts */
247         WR4(sc, THS_INTS, RD4(sc, THS_INTS));
248         WR4(sc, THS_INTC, sc->conf->intc | SHUT_INT_ALL);
249
250         /* Enable sensors */
251         WR4(sc, THS_CTRL2, RD4(sc, THS_CTRL2) | SENSOR_ENABLE_ALL);
252
253         return (0);
254 }
255
256 static int
257 aw_thermal_reg_to_temp(struct aw_thermal_softc *sc, uint32_t val)
258 {
259         return ((sc->conf->temp_base - (val * sc->conf->temp_mul)) /
260             sc->conf->temp_div);
261 }
262
263 static int
264 aw_thermal_gettemp(struct aw_thermal_softc *sc, int sensor)
265 {
266         uint32_t val;
267
268         val = RD4(sc, THS_DATA0 + (sensor * 4));
269
270         return (aw_thermal_reg_to_temp(sc, val) + TEMP_C_TO_K);
271 }
272
273 static int
274 aw_thermal_sysctl(SYSCTL_HANDLER_ARGS)
275 {
276         struct aw_thermal_softc *sc;
277         int sensor, val;
278
279         sc = arg1;
280         sensor = arg2;
281
282         val = aw_thermal_gettemp(sc, sensor);
283
284         return sysctl_handle_opaque(oidp, &val, sizeof(val), req);
285 }
286
287 static void
288 aw_thermal_intr(void *arg)
289 {
290         struct aw_thermal_softc *sc;
291         device_t dev;
292         uint32_t ints;
293
294         dev = arg;
295         sc = device_get_softc(dev);
296
297         ints = RD4(sc, THS_INTS);
298         WR4(sc, THS_INTS, ints);
299
300         if ((ints & SHUT_INT_ALL) != 0) {
301                 device_printf(dev,
302                    "WARNING - current temperature exceeds safe limits\n");
303                 shutdown_nice(RB_POWEROFF);
304         }
305 }
306
307 static int
308 aw_thermal_probe(device_t dev)
309 {
310         if (!ofw_bus_status_okay(dev))
311                 return (ENXIO);
312
313         if (THS_CONF(dev) == NULL)
314                 return (ENXIO);
315
316         device_set_desc(dev, "Allwinner Thermal Sensor Controller");
317         return (BUS_PROBE_DEFAULT);
318 }
319
320 static int
321 aw_thermal_attach(device_t dev)
322 {
323         struct aw_thermal_softc *sc;
324         clk_t clk_ahb, clk_ths;
325         hwreset_t rst;
326         int i, error;
327         void *ih;
328
329         sc = device_get_softc(dev);
330         clk_ahb = clk_ths = NULL;
331         rst = NULL;
332         ih = NULL;
333
334         sc->conf = THS_CONF(dev);
335
336         if (bus_alloc_resources(dev, aw_thermal_spec, sc->res) != 0) {
337                 device_printf(dev, "cannot allocate resources for device\n");
338                 return (ENXIO);
339         }
340
341         if (clk_get_by_ofw_name(dev, 0, "ahb", &clk_ahb) == 0) {
342                 error = clk_enable(clk_ahb);
343                 if (error != 0) {
344                         device_printf(dev, "cannot enable ahb clock\n");
345                         goto fail;
346                 }
347         }
348         if (clk_get_by_ofw_name(dev, 0, "ths", &clk_ths) == 0) {
349                 error = clk_set_freq(clk_ths, sc->conf->clk_rate, 0);
350                 if (error != 0) {
351                         device_printf(dev, "cannot set ths clock rate\n");
352                         goto fail;
353                 }
354                 error = clk_enable(clk_ths);
355                 if (error != 0) {
356                         device_printf(dev, "cannot enable ths clock\n");
357                         goto fail;
358                 }
359         }
360         if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) {
361                 error = hwreset_deassert(rst);
362                 if (error != 0) {
363                         device_printf(dev, "cannot de-assert reset\n");
364                         goto fail;
365                 }
366         }
367
368         error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
369             NULL, aw_thermal_intr, dev, &ih);
370         if (error != 0) {
371                 device_printf(dev, "cannot setup interrupt handler\n");
372                 goto fail;
373         }
374
375         if (aw_thermal_init(sc) != 0)
376                 goto fail;
377
378         for (i = 0; i < sc->conf->nsensors; i++)
379                 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
380                     SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
381                     OID_AUTO, sc->conf->sensors[i].name,
382                     CTLTYPE_INT | CTLFLAG_RD,
383                     sc, i, aw_thermal_sysctl, "IK0",
384                     sc->conf->sensors[i].desc);
385
386         return (0);
387
388 fail:
389         if (ih != NULL)
390                 bus_teardown_intr(dev, sc->res[1], ih);
391         if (rst != NULL)
392                 hwreset_release(rst);
393         if (clk_ahb != NULL)
394                 clk_release(clk_ahb);
395         if (clk_ths != NULL)
396                 clk_release(clk_ths);
397         bus_release_resources(dev, aw_thermal_spec, sc->res);
398
399         return (ENXIO);
400 }
401
402 static device_method_t aw_thermal_methods[] = {
403         /* Device interface */
404         DEVMETHOD(device_probe,         aw_thermal_probe),
405         DEVMETHOD(device_attach,        aw_thermal_attach),
406
407         DEVMETHOD_END
408 };
409
410 static driver_t aw_thermal_driver = {
411         "aw_thermal",
412         aw_thermal_methods,
413         sizeof(struct aw_thermal_softc),
414 };
415
416 static devclass_t aw_thermal_devclass;
417
418 DRIVER_MODULE(aw_thermal, simplebus, aw_thermal_driver, aw_thermal_devclass,
419     0, 0);
420 MODULE_VERSION(aw_thermal, 1);