1 /* $OpenBSD: sxitemp.c,v 1.9 2021/10/24 17:52:27 mpi Exp $ */ 2 /* 3 * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org> 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/device.h> 21 #include <sys/sensors.h> 22 23 #include <machine/intr.h> 24 #include <machine/bus.h> 25 #include <machine/fdt.h> 26 27 #include <dev/ofw/openfirm.h> 28 #include <dev/ofw/ofw_clock.h> 29 #include <dev/ofw/ofw_misc.h> 30 #include <dev/ofw/ofw_pinctrl.h> 31 #include <dev/ofw/ofw_thermal.h> 32 #include <dev/ofw/fdt.h> 33 34 /* Registers */ 35 #define THS_CTRL0 0x0000 36 #define THS_CTRL0_SENSOR_ACQ(x) ((x) & 0xffff) 37 #define THS_CTRL2 0x0040 38 #define THS_CTRL2_ADC_ACQ(x) (((x) & 0xffff) << 16) 39 #define THS_CTRL2_SENSE2_EN (1 << 2) 40 #define THS_CTRL2_SENSE1_EN (1 << 1) 41 #define THS_CTRL2_SENSE0_EN (1 << 0) 42 #define THS_INT_CTRL 0x0044 43 #define THS_INT_CTRL_THERMAL_PER(x) (((x) & 0xfffff) << 12) 44 #define THS_INT_CTRL_THS0_DATA_IRQ_EN (1 << 8) 45 #define THS_INT_CTRL_THS1_DATA_IRQ_EN (1 << 9) 46 #define THS_INT_CTRL_THS2_DATA_IRQ_EN (1 << 10) 47 #define THS_STAT 0x0048 48 #define THS_STAT_THS0_DATA_IRQ_STS (1 << 8) 49 #define THS_STAT_THS1_DATA_IRQ_STS (1 << 9) 50 #define THS_STAT_THS2_DATA_IRQ_STS (1 << 10) 51 #define THS_FILTER 0x0070 52 #define THS_FILTER_EN (1 << 2) 53 #define THS_FILTER_TYPE(x) ((x) & 0x3) 54 #define THS0_1_CDATA 0x0074 55 #define THS2_CDATA 0x0078 56 #define THS0_DATA 0x0080 57 #define THS1_DATA 0x0084 58 #define THS2_DATA 0x0088 59 60 #define HREAD4(sc, reg) \ 61 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 62 #define HWRITE4(sc, reg, val) \ 63 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 64 65 struct sxitemp_softc { 66 struct device sc_dev; 67 bus_space_tag_t sc_iot; 68 bus_space_handle_t sc_ioh; 69 70 void *sc_ih; 71 72 uint64_t (*sc_calc_temp0)(int64_t); 73 uint64_t (*sc_calc_temp1)(int64_t); 74 uint64_t (*sc_calc_temp2)(int64_t); 75 76 struct ksensor sc_sensors[3]; 77 struct ksensordev sc_sensordev; 78 79 struct thermal_sensor sc_ts; 80 }; 81 82 int sxitemp_match(struct device *, void *, void *); 83 void sxitemp_attach(struct device *, struct device *, void *); 84 85 const struct cfattach sxitemp_ca = { 86 sizeof (struct sxitemp_softc), sxitemp_match, sxitemp_attach 87 }; 88 89 struct cfdriver sxitemp_cd = { 90 NULL, "sxitemp", DV_DULL 91 }; 92 93 void sxitemp_setup_calib(struct sxitemp_softc *, int); 94 int sxitemp_intr(void *); 95 uint64_t sxitemp_h3_calc_temp(int64_t); 96 uint64_t sxitemp_r40_calc_temp(int64_t); 97 uint64_t sxitemp_a64_calc_temp(int64_t); 98 uint64_t sxitemp_h5_calc_temp0(int64_t); 99 uint64_t sxitemp_h5_calc_temp1(int64_t); 100 void sxitemp_refresh_sensors(void *); 101 int32_t sxitemp_get_temperature(void *, uint32_t *); 102 103 int 104 sxitemp_match(struct device *parent, void *match, void *aux) 105 { 106 struct fdt_attach_args *faa = aux; 107 108 return (OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-ths") || 109 OF_is_compatible(faa->fa_node, "allwinner,sun8i-r40-ths") || 110 OF_is_compatible(faa->fa_node, "allwinner,sun50i-a64-ths") || 111 OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-ths")); 112 } 113 114 void 115 sxitemp_attach(struct device *parent, struct device *self, void *aux) 116 { 117 struct sxitemp_softc *sc = (struct sxitemp_softc *)self; 118 struct fdt_attach_args *faa = aux; 119 int node = faa->fa_node; 120 uint32_t enable, irq; 121 122 if (faa->fa_nreg < 1) { 123 printf(": no registers\n"); 124 return; 125 } 126 127 sc->sc_iot = faa->fa_iot; 128 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 129 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 130 printf(": can't map registers\n"); 131 return; 132 } 133 134 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_SOFTCLOCK, 135 sxitemp_intr, sc, sc->sc_dev.dv_xname); 136 if (sc->sc_ih == NULL) { 137 printf(": can't establish interrupt\n"); 138 return; 139 } 140 141 printf("\n"); 142 143 pinctrl_byname(node, "default"); 144 145 clock_enable_all(node); 146 reset_deassert_all(node); 147 148 if (OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-ths")) { 149 sc->sc_calc_temp0 = sxitemp_h3_calc_temp; 150 } else if (OF_is_compatible(faa->fa_node, "allwinner,sun8i-r40-ths")) { 151 sc->sc_calc_temp0 = sxitemp_r40_calc_temp; 152 sc->sc_calc_temp1 = sxitemp_r40_calc_temp; 153 } else if (OF_is_compatible(faa->fa_node, "allwinner,sun50i-a64-ths")) { 154 sc->sc_calc_temp0 = sxitemp_a64_calc_temp; 155 sc->sc_calc_temp1 = sxitemp_a64_calc_temp; 156 sc->sc_calc_temp2 = sxitemp_a64_calc_temp; 157 } else { 158 sc->sc_calc_temp0 = sxitemp_h5_calc_temp0; 159 sc->sc_calc_temp1 = sxitemp_h5_calc_temp1; 160 } 161 162 enable = irq = 0; 163 if (sc->sc_calc_temp0) { 164 enable |= THS_CTRL2_SENSE0_EN; 165 irq |= THS_INT_CTRL_THS0_DATA_IRQ_EN; 166 } 167 if (sc->sc_calc_temp1) { 168 enable |= THS_CTRL2_SENSE1_EN; 169 irq |= THS_INT_CTRL_THS1_DATA_IRQ_EN; 170 } 171 if (sc->sc_calc_temp2) { 172 enable |= THS_CTRL2_SENSE2_EN; 173 irq |= THS_INT_CTRL_THS2_DATA_IRQ_EN; 174 } 175 176 sxitemp_setup_calib(sc, node); 177 178 /* Start data acquisition. */ 179 HWRITE4(sc, THS_FILTER, THS_FILTER_EN | THS_FILTER_TYPE(1)); 180 HWRITE4(sc, THS_INT_CTRL, THS_INT_CTRL_THERMAL_PER(800) | irq); 181 HWRITE4(sc, THS_CTRL0, THS_CTRL0_SENSOR_ACQ(31)); 182 HWRITE4(sc, THS_CTRL2, THS_CTRL2_ADC_ACQ(31) | enable); 183 184 /* Register sensors. */ 185 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 186 sizeof(sc->sc_sensordev.xname)); 187 if (sc->sc_calc_temp0) { 188 strlcpy(sc->sc_sensors[0].desc, "CPU", 189 sizeof(sc->sc_sensors[0].desc)); 190 sc->sc_sensors[0].type = SENSOR_TEMP; 191 sc->sc_sensors[0].flags = SENSOR_FINVALID; 192 sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[0]); 193 } 194 if (sc->sc_calc_temp1) { 195 strlcpy(sc->sc_sensors[1].desc, "GPU", 196 sizeof(sc->sc_sensors[1].desc)); 197 sc->sc_sensors[1].type = SENSOR_TEMP; 198 sc->sc_sensors[1].flags = SENSOR_FINVALID; 199 sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[1]); 200 } 201 if (sc->sc_calc_temp2) { 202 strlcpy(sc->sc_sensors[2].desc, "", 203 sizeof(sc->sc_sensors[2].desc)); 204 sc->sc_sensors[2].type = SENSOR_TEMP; 205 sc->sc_sensors[2].flags = SENSOR_FINVALID; 206 sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[2]); 207 } 208 sensordev_install(&sc->sc_sensordev); 209 sensor_task_register(sc, sxitemp_refresh_sensors, 5); 210 211 sc->sc_ts.ts_node = node; 212 sc->sc_ts.ts_cookie = sc; 213 sc->sc_ts.ts_get_temperature = sxitemp_get_temperature; 214 thermal_sensor_register(&sc->sc_ts); 215 } 216 217 void 218 sxitemp_setup_calib(struct sxitemp_softc *sc, int node) 219 { 220 uint32_t calib[2]; 221 bus_size_t size = sizeof(calib); 222 223 /* 224 * The size of the calibration data depends on the number of 225 * sensors. Instead of trying to be clever, just try the 226 * possible sizes. 227 */ 228 while (size > 0) { 229 if (nvmem_read_cell(node, "calibration", &calib, size) == 0) 230 break; 231 size -= sizeof(calib[0]); 232 } 233 234 if (size > 0) 235 HWRITE4(sc, THS0_1_CDATA, calib[0]); 236 if (size > 4) 237 HWRITE4(sc, THS2_CDATA, calib[1]); 238 } 239 240 int 241 sxitemp_intr(void *arg) 242 { 243 struct sxitemp_softc *sc = arg; 244 uint32_t cell, stat; 245 int rc = 0; 246 247 stat = HREAD4(sc, THS_STAT); 248 HWRITE4(sc, THS_STAT, stat); 249 250 if (stat & THS_STAT_THS0_DATA_IRQ_STS) { 251 cell = 0; 252 thermal_sensor_update(&sc->sc_ts, &cell); 253 rc = 1; 254 } 255 if (stat & THS_STAT_THS1_DATA_IRQ_STS) { 256 cell = 1; 257 thermal_sensor_update(&sc->sc_ts, &cell); 258 rc = 1; 259 } 260 if (stat & THS_STAT_THS2_DATA_IRQ_STS) { 261 cell = 2; 262 thermal_sensor_update(&sc->sc_ts, &cell); 263 rc = 1; 264 } 265 266 return rc; 267 } 268 269 uint64_t 270 sxitemp_h3_calc_temp(int64_t data) 271 { 272 /* From BSP since the H3 Data Sheet isn't accurate. */ 273 return 217000000 - data * 1000000000 / 8253; 274 } 275 276 uint64_t 277 sxitemp_r40_calc_temp(int64_t data) 278 { 279 /* From BSP as the R40 User Manual says T.B.D. */ 280 return -112500 * data + 250000000; 281 } 282 283 uint64_t 284 sxitemp_a64_calc_temp(int64_t data) 285 { 286 /* From BSP as the A64 User Manual isn't correct. */ 287 return (2170000000000 - data * 1000000000) / 8560; 288 } 289 290 uint64_t 291 sxitemp_h5_calc_temp0(int64_t data) 292 { 293 if (data > 0x500) 294 return -119100 * data + 223000000; 295 else 296 return -145200 * data + 259000000; 297 } 298 299 uint64_t 300 sxitemp_h5_calc_temp1(int64_t data) 301 { 302 if (data > 0x500) 303 return -119100 * data + 223000000; 304 else 305 return -159000 * data + 276000000; 306 } 307 308 void 309 sxitemp_refresh_sensors(void *arg) 310 { 311 struct sxitemp_softc *sc = arg; 312 uint32_t data; 313 314 if (sc->sc_calc_temp0) { 315 data = HREAD4(sc, THS0_DATA); 316 sc->sc_sensors[0].value = sc->sc_calc_temp0(data) + 273150000; 317 sc->sc_sensors[0].flags &= ~SENSOR_FINVALID; 318 } 319 320 if (sc->sc_calc_temp1) { 321 data = HREAD4(sc, THS1_DATA); 322 sc->sc_sensors[1].value = sc->sc_calc_temp1(data) + 273150000; 323 sc->sc_sensors[1].flags &= ~SENSOR_FINVALID; 324 } 325 326 if (sc->sc_calc_temp2) { 327 data = HREAD4(sc, THS2_DATA); 328 sc->sc_sensors[2].value = sc->sc_calc_temp2(data) + 273150000; 329 sc->sc_sensors[2].flags &= ~SENSOR_FINVALID; 330 } 331 } 332 333 int32_t 334 sxitemp_get_temperature(void *cookie, uint32_t *cells) 335 { 336 struct sxitemp_softc *sc = cookie; 337 uint32_t idx = cells[0]; 338 uint32_t data; 339 340 if (idx == 0 && sc->sc_calc_temp0) { 341 data = HREAD4(sc, THS0_DATA); 342 return sc->sc_calc_temp0(data) / 1000; 343 } else if (idx == 1 && sc->sc_calc_temp1) { 344 data = HREAD4(sc, THS1_DATA); 345 return sc->sc_calc_temp1(data) / 1000; 346 } else if (idx == 2 && sc->sc_calc_temp2) { 347 data = HREAD4(sc, THS2_DATA); 348 return sc->sc_calc_temp2(data) / 1000; 349 } 350 351 return THERMAL_SENSOR_MAX; 352 } 353