1 /* $OpenBSD: amltemp.c,v 1.2 2021/10/24 17:52:26 mpi Exp $ */ 2 /* 3 * Copyright (c) 2020 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_thermal.h> 31 #include <dev/ofw/fdt.h> 32 33 /* Calibration */ 34 #define TS_CALIB_VALID_MASK 0x8c000000 35 #define TS_CALIB_SIGN_MASK 0x00008000 36 #define TS_CALIB_VALUE_MASK 0x000003ff 37 38 /* Registers */ 39 #define TS_CFG_REG1 0x01 40 #define TS_CFG_REG1_ANA_EN_VCM (1 << 10) 41 #define TS_CFG_REG1_ANA_EN_VBG (1 << 9) 42 #define TS_CFG_REG1_FILTER_EN (1 << 5) 43 #define TS_CFG_REG1_DEM_EN (1 << 3) 44 #define TS_CFG_REG1_ANA_CH_SEL 3 45 #define TS_STAT0 0x10 46 #define TS_STAT0_CODE_MASK 0xffff 47 48 #define HREAD4(sc, reg) \ 49 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg) << 2)) 50 #define HWRITE4(sc, reg, val) \ 51 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg) << 2, (val)) 52 #define HSET4(sc, reg, bits) \ 53 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 54 #define HCLR4(sc, reg, bits) \ 55 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 56 57 struct amltemp_softc { 58 struct device sc_dev; 59 bus_space_tag_t sc_iot; 60 bus_space_handle_t sc_ioh; 61 62 int sc_node; 63 int32_t sc_calib; 64 65 struct ksensor sc_sensor; 66 struct ksensordev sc_sensordev; 67 68 struct thermal_sensor sc_ts; 69 }; 70 71 int amltemp_match(struct device *, void *, void *); 72 void amltemp_attach(struct device *, struct device *, void *); 73 74 const struct cfattach amltemp_ca = { 75 sizeof (struct amltemp_softc), amltemp_match, amltemp_attach 76 }; 77 78 struct cfdriver amltemp_cd = { 79 NULL, "amltemp", DV_DULL 80 }; 81 82 void amltemp_attachhook(struct device *); 83 int32_t amltemp_calc_temp(struct amltemp_softc *, int32_t); 84 void amltemp_refresh_sensors(void *); 85 int32_t amltemp_get_temperature(void *, uint32_t *); 86 87 int 88 amltemp_match(struct device *parent, void *match, void *aux) 89 { 90 struct fdt_attach_args *faa = aux; 91 92 return OF_is_compatible(faa->fa_node, "amlogic,g12a-thermal"); 93 } 94 95 void 96 amltemp_attach(struct device *parent, struct device *self, void *aux) 97 { 98 struct amltemp_softc *sc = (struct amltemp_softc *)self; 99 struct fdt_attach_args *faa = aux; 100 101 if (faa->fa_nreg < 1) { 102 printf(": no registers\n"); 103 return; 104 } 105 106 sc->sc_iot = faa->fa_iot; 107 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 108 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 109 printf(": can't map registers\n"); 110 return; 111 } 112 113 sc->sc_node = faa->fa_node; 114 printf("\n"); 115 116 config_mountroot(self, amltemp_attachhook); 117 } 118 119 void 120 amltemp_attachhook(struct device *self) 121 { 122 struct amltemp_softc *sc = (struct amltemp_softc *)self; 123 struct regmap *rm; 124 bus_addr_t offset; 125 const char *name; 126 uint32_t ao_secure; 127 128 if (OF_is_compatible(sc->sc_node, "amlogic,g12a-cpu-thermal")) { 129 offset = 0x128; 130 name = "CPU"; 131 } else if (OF_is_compatible(sc->sc_node, "amlogic,g12a-ddr-thermal")) { 132 offset = 0x0f0; 133 name = "DDR"; 134 } else { 135 printf("%s: unknown sensor\n", sc->sc_dev.dv_xname); 136 return; 137 } 138 139 /* Extract calibration value. */ 140 ao_secure = OF_getpropint(sc->sc_node, "amlogic,ao-secure", 0); 141 rm = regmap_byphandle(ao_secure); 142 if (rm == NULL) { 143 printf("%s: no calibration info\n", sc->sc_dev.dv_xname); 144 return; 145 } 146 sc->sc_calib = regmap_read_4(rm, offset); 147 if ((sc->sc_calib & TS_CALIB_VALID_MASK) == 0) { 148 printf("%s: invalid calibration\n", sc->sc_dev.dv_xname); 149 return; 150 } 151 if (sc->sc_calib & TS_CALIB_SIGN_MASK) 152 sc->sc_calib = ~(sc->sc_calib & TS_CALIB_VALUE_MASK) + 1; 153 else 154 sc->sc_calib = (sc->sc_calib & TS_CALIB_VALUE_MASK); 155 156 /* Enable hardware. */ 157 clock_enable_all(sc->sc_node); 158 HSET4(sc, TS_CFG_REG1, TS_CFG_REG1_ANA_EN_VCM | 159 TS_CFG_REG1_ANA_EN_VBG | TS_CFG_REG1_FILTER_EN | 160 TS_CFG_REG1_DEM_EN | TS_CFG_REG1_ANA_CH_SEL); 161 162 /* Register sensor. */ 163 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 164 sizeof(sc->sc_sensordev.xname)); 165 strlcpy(sc->sc_sensor.desc, name, sizeof(sc->sc_sensor.desc)); 166 sc->sc_sensor.type = SENSOR_TEMP; 167 sc->sc_sensor.flags = SENSOR_FINVALID; 168 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); 169 sensordev_install(&sc->sc_sensordev); 170 sensor_task_register(sc, amltemp_refresh_sensors, 5); 171 172 sc->sc_ts.ts_node = sc->sc_node; 173 sc->sc_ts.ts_cookie = sc; 174 sc->sc_ts.ts_get_temperature = amltemp_get_temperature; 175 thermal_sensor_register(&sc->sc_ts); 176 } 177 178 int32_t 179 amltemp_calc_temp(struct amltemp_softc *sc, int32_t code) 180 { 181 const uint32_t A = 9411; 182 const uint32_t B = 3159; 183 const uint32_t m = 424; 184 const uint32_t n = 324; 185 int64_t tmp1, tmp2; 186 187 tmp1 = (code * m) / 100; 188 tmp2 = (code * n) / 100; 189 tmp1 = (tmp1 * (1 << 16)) / ((1 << 16) + tmp2); 190 tmp1 = ((tmp1 + sc->sc_calib) * A) / (1 << 16); 191 return (tmp1 - B) * 100; 192 } 193 194 void 195 amltemp_refresh_sensors(void *arg) 196 { 197 struct amltemp_softc *sc = arg; 198 int32_t code, temp; 199 200 code = HREAD4(sc, TS_STAT0); 201 temp = amltemp_calc_temp(sc, code & TS_STAT0_CODE_MASK); 202 203 sc->sc_sensor.value = 273150000 + 1000 * temp; 204 sc->sc_sensor.flags &= ~SENSOR_FINVALID; 205 } 206 207 int32_t 208 amltemp_get_temperature(void *cookie, uint32_t *cells) 209 { 210 struct amltemp_softc *sc = cookie; 211 int32_t code; 212 213 code = HREAD4(sc, TS_STAT0); 214 return amltemp_calc_temp(sc, code & TS_STAT0_CODE_MASK); 215 } 216