1*9fdf0c62Smpi /* $OpenBSD: imxtmu.c,v 1.3 2021/10/24 17:52:26 mpi Exp $ */
2a315094dSpatrick /*
3a315094dSpatrick * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se>
4a315094dSpatrick *
5a315094dSpatrick * Permission to use, copy, modify, and distribute this software for any
6a315094dSpatrick * purpose with or without fee is hereby granted, provided that the above
7a315094dSpatrick * copyright notice and this permission notice appear in all copies.
8a315094dSpatrick *
9a315094dSpatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10a315094dSpatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11a315094dSpatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12a315094dSpatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13a315094dSpatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14a315094dSpatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15a315094dSpatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16a315094dSpatrick */
17a315094dSpatrick
18a315094dSpatrick #include <sys/param.h>
19a315094dSpatrick #include <sys/systm.h>
20a315094dSpatrick #include <sys/malloc.h>
21a315094dSpatrick #include <sys/device.h>
22a315094dSpatrick #include <sys/sensors.h>
23a315094dSpatrick #include <sys/timeout.h>
24a315094dSpatrick
25a315094dSpatrick #include <machine/bus.h>
26a315094dSpatrick #include <machine/fdt.h>
27a315094dSpatrick
28a315094dSpatrick #include <dev/ofw/openfirm.h>
29a315094dSpatrick #include <dev/ofw/fdt.h>
302ee9f557Spatrick #include <dev/ofw/ofw_clock.h>
31a315094dSpatrick
322ee9f557Spatrick /* i.MX8MQ registers */
332ee9f557Spatrick #define TMU_MQ_TMR 0x000
342ee9f557Spatrick #define TMU_MQ_TMR_ME (1U << 31)
352ee9f557Spatrick #define TMU_MQ_TMR_ALPF (0x3 << 26)
362ee9f557Spatrick #define TMU_MQ_TMR_SENSOR(x) (1 << (15 - (x)))
372ee9f557Spatrick #define TMU_MQ_TMTMIR 0x008
382ee9f557Spatrick #define TMU_MQ_TMTMIR_DEFAULT 0xf
392ee9f557Spatrick #define TMU_MQ_TIER 0x020
402ee9f557Spatrick #define TMU_MQ_TTCFGR 0x080
412ee9f557Spatrick #define TMU_MQ_TSCFGR 0x084
422ee9f557Spatrick #define TMU_MQ_TRITSR(x) (0x100 + ((x) * 0x10))
432ee9f557Spatrick #define TMU_MQ_TTR0CR 0xf10
442ee9f557Spatrick #define TMU_MQ_TTR1CR 0xf14
452ee9f557Spatrick #define TMU_MQ_TTR2CR 0xf18
462ee9f557Spatrick #define TMU_MQ_TTR3CR 0xf1c
472ee9f557Spatrick
482ee9f557Spatrick /* i.MX8MM registers */
492ee9f557Spatrick #define TMU_MM_TER 0x000
502ee9f557Spatrick #define TMU_MM_TER_EN (1U << 31)
512ee9f557Spatrick #define TMU_MM_TRITSR 0x020
522ee9f557Spatrick #define TMU_MM_TRITSR_LOW_LIMIT 10
53a315094dSpatrick
54a315094dSpatrick #define HREAD4(sc, reg) \
55a315094dSpatrick (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
56a315094dSpatrick #define HWRITE4(sc, reg, val) \
57a315094dSpatrick bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
58a315094dSpatrick #define HSET4(sc, reg, bits) \
59a315094dSpatrick HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
60a315094dSpatrick #define HCLR4(sc, reg, bits) \
61a315094dSpatrick HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
62a315094dSpatrick
63a315094dSpatrick struct imxtmu_softc {
64a315094dSpatrick struct device sc_dev;
65a315094dSpatrick bus_space_tag_t sc_iot;
66a315094dSpatrick bus_space_handle_t sc_ioh;
67a315094dSpatrick int sc_sensorid;
68a315094dSpatrick
69a315094dSpatrick struct ksensor sc_sensor;
70a315094dSpatrick struct ksensordev sc_sensordev;
71a315094dSpatrick struct timeout sc_sensorto;
72a315094dSpatrick };
73a315094dSpatrick
74a315094dSpatrick int imxtmu_match(struct device *, void *, void *);
75a315094dSpatrick void imxtmu_attach(struct device *, struct device *, void *);
762ee9f557Spatrick
772ee9f557Spatrick void imxtmu_mm_refresh_sensors(void *);
782ee9f557Spatrick void imxtmu_mq_refresh_sensors(void *);
79a315094dSpatrick
80*9fdf0c62Smpi const struct cfattach imxtmu_ca = {
81a315094dSpatrick sizeof(struct imxtmu_softc), imxtmu_match, imxtmu_attach
82a315094dSpatrick };
83a315094dSpatrick
84a315094dSpatrick struct cfdriver imxtmu_cd = {
85a315094dSpatrick NULL, "imxtmu", DV_DULL
86a315094dSpatrick };
87a315094dSpatrick
88a315094dSpatrick int
imxtmu_match(struct device * parent,void * match,void * aux)89a315094dSpatrick imxtmu_match(struct device *parent, void *match, void *aux)
90a315094dSpatrick {
91a315094dSpatrick struct fdt_attach_args *faa = aux;
92a315094dSpatrick
932ee9f557Spatrick return OF_is_compatible(faa->fa_node, "fsl,imx8mm-tmu") ||
942ee9f557Spatrick OF_is_compatible(faa->fa_node, "fsl,imx8mq-tmu");
95a315094dSpatrick }
96a315094dSpatrick
97a315094dSpatrick void
imxtmu_attach(struct device * parent,struct device * self,void * aux)98a315094dSpatrick imxtmu_attach(struct device *parent, struct device *self, void *aux)
99a315094dSpatrick {
100a315094dSpatrick struct imxtmu_softc *sc = (struct imxtmu_softc *)self;
101a315094dSpatrick struct fdt_attach_args *faa = aux;
102a315094dSpatrick uint32_t range[4], *calibration;
103a315094dSpatrick int i, len;
104a315094dSpatrick
105a315094dSpatrick if (faa->fa_nreg < 1)
106a315094dSpatrick return;
107a315094dSpatrick
108a315094dSpatrick sc->sc_iot = faa->fa_iot;
109a315094dSpatrick if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
110a315094dSpatrick faa->fa_reg[0].size, 0, &sc->sc_ioh))
111a315094dSpatrick panic("%s: bus_space_map failed!", __func__);
112a315094dSpatrick
113a315094dSpatrick printf("\n");
114a315094dSpatrick
1152ee9f557Spatrick clock_enable_all(faa->fa_node);
116a315094dSpatrick
117a315094dSpatrick strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
118a315094dSpatrick sizeof(sc->sc_sensordev.xname));
119a315094dSpatrick strlcpy(sc->sc_sensor.desc, "core",
120a315094dSpatrick sizeof(sc->sc_sensor.desc));
121a315094dSpatrick sc->sc_sensor.type = SENSOR_TEMP;
122a315094dSpatrick sc->sc_sensor.flags = SENSOR_FINVALID;
123a315094dSpatrick sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
124a315094dSpatrick sensordev_install(&sc->sc_sensordev);
1252ee9f557Spatrick
1262ee9f557Spatrick if (OF_is_compatible(faa->fa_node, "fsl,imx8mm-tmu")) {
1272ee9f557Spatrick HSET4(sc, TMU_MM_TER, TMU_MM_TER_EN);
1282ee9f557Spatrick sensor_task_register(sc, imxtmu_mm_refresh_sensors, 5);
1292ee9f557Spatrick }
1302ee9f557Spatrick
1312ee9f557Spatrick if (OF_is_compatible(faa->fa_node, "fsl,imx8mq-tmu")) {
1322ee9f557Spatrick /*
1332ee9f557Spatrick * XXX: This thermal unit can show temperatures per node, and
1342ee9f557Spatrick * XXX: the thermal-zones can reference this. But since we do
1352ee9f557Spatrick * XXX: not register ourselves with such a infrastructure we can
1362ee9f557Spatrick * XXX: live with just extracting sensor 0: the CPU.
1372ee9f557Spatrick */
1382ee9f557Spatrick sc->sc_sensorid = 0;
1392ee9f557Spatrick
1402ee9f557Spatrick HWRITE4(sc, TMU_MQ_TIER, 0);
1412ee9f557Spatrick HWRITE4(sc, TMU_MQ_TMTMIR, TMU_MQ_TMTMIR_DEFAULT);
1422ee9f557Spatrick HWRITE4(sc, TMU_MQ_TMR, 0);
1432ee9f557Spatrick
1442ee9f557Spatrick if (OF_getpropintarray(faa->fa_node, "fsl,tmu-range", range,
1452ee9f557Spatrick sizeof(range)) != sizeof(range))
1462ee9f557Spatrick return;
1472ee9f557Spatrick
1482ee9f557Spatrick HWRITE4(sc, TMU_MQ_TTR0CR, range[0]);
1492ee9f557Spatrick HWRITE4(sc, TMU_MQ_TTR1CR, range[1]);
1502ee9f557Spatrick HWRITE4(sc, TMU_MQ_TTR2CR, range[2]);
1512ee9f557Spatrick HWRITE4(sc, TMU_MQ_TTR3CR, range[3]);
1522ee9f557Spatrick
1532ee9f557Spatrick len = OF_getproplen(faa->fa_node, "fsl,tmu-calibration");
1542ee9f557Spatrick if (len <= 0 || (len % 8) != 0)
1552ee9f557Spatrick return;
1562ee9f557Spatrick
1572ee9f557Spatrick calibration = malloc(len, M_TEMP, M_WAITOK);
1582ee9f557Spatrick OF_getpropintarray(faa->fa_node, "fsl,tmu-calibration", calibration,
1592ee9f557Spatrick len);
1602ee9f557Spatrick for (i = 0; i < (len / 4); i += 2) {
1612ee9f557Spatrick HWRITE4(sc, TMU_MQ_TTCFGR, calibration[i + 0]);
1622ee9f557Spatrick HWRITE4(sc, TMU_MQ_TSCFGR, calibration[i + 1]);
1632ee9f557Spatrick }
1642ee9f557Spatrick free(calibration, M_TEMP, len);
1652ee9f557Spatrick
1662ee9f557Spatrick HWRITE4(sc, TMU_MQ_TMR, TMU_MQ_TMR_SENSOR(sc->sc_sensorid) |
1672ee9f557Spatrick TMU_MQ_TMR_ME | TMU_MQ_TMR_ALPF);
1682ee9f557Spatrick
1692ee9f557Spatrick sensor_task_register(sc, imxtmu_mq_refresh_sensors, 5);
1702ee9f557Spatrick }
171a315094dSpatrick }
172a315094dSpatrick
173a315094dSpatrick void
imxtmu_mm_refresh_sensors(void * arg)1742ee9f557Spatrick imxtmu_mm_refresh_sensors(void *arg)
175a315094dSpatrick {
176a315094dSpatrick struct imxtmu_softc *sc = (struct imxtmu_softc *)arg;
177a315094dSpatrick uint32_t value;
178a315094dSpatrick
1792ee9f557Spatrick value = HREAD4(sc, TMU_MM_TRITSR);
1802ee9f557Spatrick if (value < TMU_MM_TRITSR_LOW_LIMIT)
1812ee9f557Spatrick return;
1822ee9f557Spatrick value = (value & 0xff) * 1000000;
1832ee9f557Spatrick
1842ee9f557Spatrick sc->sc_sensor.value = value + 273150000;
1852ee9f557Spatrick sc->sc_sensor.flags &= ~SENSOR_FINVALID;
1862ee9f557Spatrick }
1872ee9f557Spatrick
1882ee9f557Spatrick void
imxtmu_mq_refresh_sensors(void * arg)1892ee9f557Spatrick imxtmu_mq_refresh_sensors(void *arg)
1902ee9f557Spatrick {
1912ee9f557Spatrick struct imxtmu_softc *sc = (struct imxtmu_softc *)arg;
1922ee9f557Spatrick uint32_t value;
1932ee9f557Spatrick
1942ee9f557Spatrick value = HREAD4(sc, TMU_MQ_TRITSR(sc->sc_sensorid));
195a315094dSpatrick value = (value & 0xff) * 1000000;
196a315094dSpatrick
197a315094dSpatrick sc->sc_sensor.value = value + 273150000;
198a315094dSpatrick sc->sc_sensor.flags &= ~SENSOR_FINVALID;
199a315094dSpatrick }
200