1 /* $OpenBSD: imxtmu.c,v 1.3 2021/10/24 17:52:26 mpi Exp $ */ 2 /* 3 * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se> 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/malloc.h> 21 #include <sys/device.h> 22 #include <sys/sensors.h> 23 #include <sys/timeout.h> 24 25 #include <machine/bus.h> 26 #include <machine/fdt.h> 27 28 #include <dev/ofw/openfirm.h> 29 #include <dev/ofw/fdt.h> 30 #include <dev/ofw/ofw_clock.h> 31 32 /* i.MX8MQ registers */ 33 #define TMU_MQ_TMR 0x000 34 #define TMU_MQ_TMR_ME (1U << 31) 35 #define TMU_MQ_TMR_ALPF (0x3 << 26) 36 #define TMU_MQ_TMR_SENSOR(x) (1 << (15 - (x))) 37 #define TMU_MQ_TMTMIR 0x008 38 #define TMU_MQ_TMTMIR_DEFAULT 0xf 39 #define TMU_MQ_TIER 0x020 40 #define TMU_MQ_TTCFGR 0x080 41 #define TMU_MQ_TSCFGR 0x084 42 #define TMU_MQ_TRITSR(x) (0x100 + ((x) * 0x10)) 43 #define TMU_MQ_TTR0CR 0xf10 44 #define TMU_MQ_TTR1CR 0xf14 45 #define TMU_MQ_TTR2CR 0xf18 46 #define TMU_MQ_TTR3CR 0xf1c 47 48 /* i.MX8MM registers */ 49 #define TMU_MM_TER 0x000 50 #define TMU_MM_TER_EN (1U << 31) 51 #define TMU_MM_TRITSR 0x020 52 #define TMU_MM_TRITSR_LOW_LIMIT 10 53 54 #define HREAD4(sc, reg) \ 55 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 56 #define HWRITE4(sc, reg, val) \ 57 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 58 #define HSET4(sc, reg, bits) \ 59 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 60 #define HCLR4(sc, reg, bits) \ 61 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 62 63 struct imxtmu_softc { 64 struct device sc_dev; 65 bus_space_tag_t sc_iot; 66 bus_space_handle_t sc_ioh; 67 int sc_sensorid; 68 69 struct ksensor sc_sensor; 70 struct ksensordev sc_sensordev; 71 struct timeout sc_sensorto; 72 }; 73 74 int imxtmu_match(struct device *, void *, void *); 75 void imxtmu_attach(struct device *, struct device *, void *); 76 77 void imxtmu_mm_refresh_sensors(void *); 78 void imxtmu_mq_refresh_sensors(void *); 79 80 const struct cfattach imxtmu_ca = { 81 sizeof(struct imxtmu_softc), imxtmu_match, imxtmu_attach 82 }; 83 84 struct cfdriver imxtmu_cd = { 85 NULL, "imxtmu", DV_DULL 86 }; 87 88 int 89 imxtmu_match(struct device *parent, void *match, void *aux) 90 { 91 struct fdt_attach_args *faa = aux; 92 93 return OF_is_compatible(faa->fa_node, "fsl,imx8mm-tmu") || 94 OF_is_compatible(faa->fa_node, "fsl,imx8mq-tmu"); 95 } 96 97 void 98 imxtmu_attach(struct device *parent, struct device *self, void *aux) 99 { 100 struct imxtmu_softc *sc = (struct imxtmu_softc *)self; 101 struct fdt_attach_args *faa = aux; 102 uint32_t range[4], *calibration; 103 int i, len; 104 105 if (faa->fa_nreg < 1) 106 return; 107 108 sc->sc_iot = faa->fa_iot; 109 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 110 faa->fa_reg[0].size, 0, &sc->sc_ioh)) 111 panic("%s: bus_space_map failed!", __func__); 112 113 printf("\n"); 114 115 clock_enable_all(faa->fa_node); 116 117 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 118 sizeof(sc->sc_sensordev.xname)); 119 strlcpy(sc->sc_sensor.desc, "core", 120 sizeof(sc->sc_sensor.desc)); 121 sc->sc_sensor.type = SENSOR_TEMP; 122 sc->sc_sensor.flags = SENSOR_FINVALID; 123 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); 124 sensordev_install(&sc->sc_sensordev); 125 126 if (OF_is_compatible(faa->fa_node, "fsl,imx8mm-tmu")) { 127 HSET4(sc, TMU_MM_TER, TMU_MM_TER_EN); 128 sensor_task_register(sc, imxtmu_mm_refresh_sensors, 5); 129 } 130 131 if (OF_is_compatible(faa->fa_node, "fsl,imx8mq-tmu")) { 132 /* 133 * XXX: This thermal unit can show temperatures per node, and 134 * XXX: the thermal-zones can reference this. But since we do 135 * XXX: not register ourselves with such a infrastructure we can 136 * XXX: live with just extracting sensor 0: the CPU. 137 */ 138 sc->sc_sensorid = 0; 139 140 HWRITE4(sc, TMU_MQ_TIER, 0); 141 HWRITE4(sc, TMU_MQ_TMTMIR, TMU_MQ_TMTMIR_DEFAULT); 142 HWRITE4(sc, TMU_MQ_TMR, 0); 143 144 if (OF_getpropintarray(faa->fa_node, "fsl,tmu-range", range, 145 sizeof(range)) != sizeof(range)) 146 return; 147 148 HWRITE4(sc, TMU_MQ_TTR0CR, range[0]); 149 HWRITE4(sc, TMU_MQ_TTR1CR, range[1]); 150 HWRITE4(sc, TMU_MQ_TTR2CR, range[2]); 151 HWRITE4(sc, TMU_MQ_TTR3CR, range[3]); 152 153 len = OF_getproplen(faa->fa_node, "fsl,tmu-calibration"); 154 if (len <= 0 || (len % 8) != 0) 155 return; 156 157 calibration = malloc(len, M_TEMP, M_WAITOK); 158 OF_getpropintarray(faa->fa_node, "fsl,tmu-calibration", calibration, 159 len); 160 for (i = 0; i < (len / 4); i += 2) { 161 HWRITE4(sc, TMU_MQ_TTCFGR, calibration[i + 0]); 162 HWRITE4(sc, TMU_MQ_TSCFGR, calibration[i + 1]); 163 } 164 free(calibration, M_TEMP, len); 165 166 HWRITE4(sc, TMU_MQ_TMR, TMU_MQ_TMR_SENSOR(sc->sc_sensorid) | 167 TMU_MQ_TMR_ME | TMU_MQ_TMR_ALPF); 168 169 sensor_task_register(sc, imxtmu_mq_refresh_sensors, 5); 170 } 171 } 172 173 void 174 imxtmu_mm_refresh_sensors(void *arg) 175 { 176 struct imxtmu_softc *sc = (struct imxtmu_softc *)arg; 177 uint32_t value; 178 179 value = HREAD4(sc, TMU_MM_TRITSR); 180 if (value < TMU_MM_TRITSR_LOW_LIMIT) 181 return; 182 value = (value & 0xff) * 1000000; 183 184 sc->sc_sensor.value = value + 273150000; 185 sc->sc_sensor.flags &= ~SENSOR_FINVALID; 186 } 187 188 void 189 imxtmu_mq_refresh_sensors(void *arg) 190 { 191 struct imxtmu_softc *sc = (struct imxtmu_softc *)arg; 192 uint32_t value; 193 194 value = HREAD4(sc, TMU_MQ_TRITSR(sc->sc_sensorid)); 195 value = (value & 0xff) * 1000000; 196 197 sc->sc_sensor.value = value + 273150000; 198 sc->sc_sensor.flags &= ~SENSOR_FINVALID; 199 } 200