1 /* $OpenBSD: mvtemp.c,v 1.5 2023/04/18 08:35:02 patrick Exp $ */ 2 /* 3 * Copyright (c) 2018 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/bus.h> 24 #include <machine/fdt.h> 25 26 #include <dev/ofw/openfirm.h> 27 #include <dev/ofw/ofw_misc.h> 28 #include <dev/ofw/fdt.h> 29 30 /* Registers */ 31 #define TEMP_STAT 0x0000 32 #define TEMP_CTRL0 0x0000 33 #define TEMP_CTRL0_TSEN_TC_TRIM_MASK 0x7 34 #define TEMP_CTRL0_TSEN_TC_TRIM_VAL 0x3 35 #define TEMP_CTRL0_TSEN_START (1 << 0) 36 #define TEMP_CTRL0_TSEN_RESET (1 << 1) 37 #define TEMP_CTRL0_TSEN_ENABLE (1 << 2) 38 #define TEMP_CTRL0_TSEN_AVG_BYPASS (1 << 6) 39 #define TEMP_CTRL0_TSEN_OSR_MAX (0x3 << 24) 40 #define TEMP_CTRL1 0x0004 41 #define TMEP_CTRL1_TSEN_START (1 << 0) 42 #define TEMP_CTRL1_TSEN_SW_RESET (1 << 7) 43 #define TEMP_CTRL1_TSEN_HW_RESETN (1 << 8) 44 #define TEMP_CTRL1_TSEN_AVG_MASK 0x7 45 46 #define REG_CTRL0 0 47 #define REG_CTRL1 1 48 #define REG_STAT 2 49 #define REG_MAX 3 50 51 struct mvtemp_softc { 52 struct device sc_dev; 53 bus_space_tag_t sc_iot; 54 bus_space_handle_t sc_stat_ioh; 55 bus_space_handle_t sc_ctrl_ioh; 56 struct regmap *sc_rm; 57 58 uint32_t sc_stat_valid; 59 int32_t (*sc_calc_temp)(uint32_t); 60 bus_size_t sc_offs[REG_MAX]; 61 62 struct ksensor sc_sensor; 63 struct ksensordev sc_sensordev; 64 }; 65 66 int mvtemp_match(struct device *, void *, void *); 67 void mvtemp_attach(struct device *, struct device *, void *); 68 69 const struct cfattach mvtemp_ca = { 70 sizeof (struct mvtemp_softc), mvtemp_match, mvtemp_attach 71 }; 72 73 struct cfdriver mvtemp_cd = { 74 NULL, "mvtemp", DV_DULL 75 }; 76 77 struct mvtemp_compat { 78 const char *compat; 79 uint32_t stat_valid; 80 void (*init)(struct mvtemp_softc *); 81 int32_t (*calc_temp)(uint32_t); 82 bus_size_t offs[REG_MAX]; 83 }; 84 85 void mvtemp_380_init(struct mvtemp_softc *); 86 void mvtemp_ap806_init(struct mvtemp_softc *); 87 int32_t mvtemp_ap806_calc_temp(uint32_t); 88 void mvtemp_cp110_init(struct mvtemp_softc *); 89 int32_t mvtemp_cp110_calc_temp(uint32_t); 90 91 const struct mvtemp_compat mvtemp_compat[] = { 92 { 93 "marvell,armada-ap806-thermal", (1 << 16), 94 mvtemp_ap806_init, mvtemp_ap806_calc_temp, 95 { 0x84, 0x88, 0x8c }, 96 }, 97 { 98 "marvell,armada-cp110-thermal", (1 << 10), 99 mvtemp_cp110_init, mvtemp_cp110_calc_temp, 100 { 0x70, 0x74, 0x78 }, 101 }, 102 { 103 "marvell,armada380-thermal", (1 << 10), 104 mvtemp_380_init, mvtemp_cp110_calc_temp, 105 { 0x70, 0x74, 0x78 }, 106 }, 107 }; 108 109 void mvtemp_refresh_sensors(void *); 110 111 int 112 mvtemp_match(struct device *parent, void *match, void *aux) 113 { 114 struct fdt_attach_args *faa = aux; 115 int i; 116 117 for (i = 0; i < nitems(mvtemp_compat); i++) { 118 if (OF_is_compatible(faa->fa_node, mvtemp_compat[i].compat)) 119 return 1; 120 } 121 122 return 0; 123 } 124 125 void 126 mvtemp_attach(struct device *parent, struct device *self, void *aux) 127 { 128 struct mvtemp_softc *sc = (struct mvtemp_softc *)self; 129 struct fdt_attach_args *faa = aux; 130 int i; 131 132 if (faa->fa_nreg >= 2) { 133 sc->sc_iot = faa->fa_iot; 134 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 135 faa->fa_reg[0].size, 0, &sc->sc_stat_ioh)) { 136 printf(": can't map registers\n"); 137 return; 138 } 139 if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, 140 faa->fa_reg[1].size, 0, &sc->sc_ctrl_ioh)) { 141 bus_space_unmap(sc->sc_iot, sc->sc_stat_ioh, 142 faa->fa_reg[0].size); 143 printf(": can't map registers\n"); 144 return; 145 } 146 } else { 147 sc->sc_rm = regmap_bynode(OF_parent(faa->fa_node)); 148 if (sc->sc_rm == NULL) { 149 printf(": no registers\n"); 150 return; 151 } 152 } 153 154 printf("\n"); 155 156 for (i = 0; i < nitems(mvtemp_compat); i++) { 157 if (OF_is_compatible(faa->fa_node, mvtemp_compat[i].compat)) { 158 break; 159 } 160 } 161 KASSERT(i < nitems(mvtemp_compat)); 162 163 /* Legacy offsets */ 164 sc->sc_offs[REG_CTRL0] = TEMP_CTRL0; 165 sc->sc_offs[REG_CTRL1] = TEMP_CTRL1; 166 sc->sc_offs[REG_STAT] = TEMP_STAT; 167 168 /* Syscon offsets */ 169 if (sc->sc_rm != NULL) { 170 sc->sc_offs[REG_CTRL0] = mvtemp_compat[i].offs[REG_CTRL0]; 171 sc->sc_offs[REG_CTRL1] = mvtemp_compat[i].offs[REG_CTRL1]; 172 sc->sc_offs[REG_STAT] = mvtemp_compat[i].offs[REG_STAT]; 173 } 174 175 mvtemp_compat[i].init(sc); 176 sc->sc_stat_valid = mvtemp_compat[i].stat_valid; 177 sc->sc_calc_temp = mvtemp_compat[i].calc_temp; 178 179 /* Register sensors. */ 180 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 181 sizeof(sc->sc_sensordev.xname)); 182 sc->sc_sensor.type = SENSOR_TEMP; 183 sc->sc_sensor.flags = SENSOR_FINVALID; 184 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); 185 sensordev_install(&sc->sc_sensordev); 186 sensor_task_register(sc, mvtemp_refresh_sensors, 5); 187 } 188 189 uint32_t 190 mvtemp_read(struct mvtemp_softc *sc, int reg) 191 { 192 if (sc->sc_rm != NULL) 193 return regmap_read_4(sc->sc_rm, sc->sc_offs[reg]); 194 else if (reg == REG_STAT) 195 return bus_space_read_4(sc->sc_iot, sc->sc_stat_ioh, sc->sc_offs[reg]); 196 else 197 return bus_space_read_4(sc->sc_iot, sc->sc_ctrl_ioh, sc->sc_offs[reg]); 198 } 199 200 void 201 mvtemp_write(struct mvtemp_softc *sc, int reg, uint32_t val) 202 { 203 if (sc->sc_rm != NULL) 204 regmap_write_4(sc->sc_rm, sc->sc_offs[reg], val); 205 else if (reg == REG_STAT) 206 bus_space_write_4(sc->sc_iot, sc->sc_stat_ioh, sc->sc_offs[reg], val); 207 else 208 bus_space_write_4(sc->sc_iot, sc->sc_ctrl_ioh, sc->sc_offs[reg], val); 209 } 210 211 /* 380 */ 212 213 void 214 mvtemp_380_init(struct mvtemp_softc *sc) 215 { 216 uint32_t ctrl; 217 218 ctrl = mvtemp_read(sc, REG_CTRL1); 219 ctrl |= TEMP_CTRL1_TSEN_HW_RESETN; 220 ctrl &= ~TEMP_CTRL1_TSEN_SW_RESET; 221 mvtemp_write(sc, REG_CTRL1, ctrl); 222 223 ctrl = mvtemp_read(sc, REG_CTRL0); 224 ctrl &= ~TEMP_CTRL0_TSEN_TC_TRIM_MASK; 225 ctrl |= TEMP_CTRL0_TSEN_TC_TRIM_VAL; 226 mvtemp_write(sc, REG_CTRL0, ctrl); 227 } 228 229 /* AP806 */ 230 231 void 232 mvtemp_ap806_init(struct mvtemp_softc *sc) 233 { 234 uint32_t ctrl; 235 236 ctrl = mvtemp_read(sc, REG_CTRL0); 237 ctrl &= ~TEMP_CTRL0_TSEN_RESET; 238 ctrl |= TEMP_CTRL0_TSEN_START | TEMP_CTRL0_TSEN_ENABLE; 239 ctrl |= TEMP_CTRL0_TSEN_OSR_MAX; 240 ctrl &= ~TEMP_CTRL0_TSEN_AVG_BYPASS; 241 mvtemp_write(sc, REG_CTRL0, ctrl); 242 } 243 244 int32_t 245 mvtemp_ap806_calc_temp(uint32_t stat) 246 { 247 stat = ((stat & 0x3ff) ^ 0x200) - 0x200; 248 return (stat * 423000) + 150000000 + 273150000; 249 } 250 251 /* CP110 */ 252 253 void 254 mvtemp_cp110_init(struct mvtemp_softc *sc) 255 { 256 uint32_t ctrl; 257 258 mvtemp_380_init(sc); 259 260 ctrl = mvtemp_read(sc, REG_CTRL0); 261 ctrl |= TEMP_CTRL0_TSEN_OSR_MAX; 262 mvtemp_write(sc, REG_CTRL0, ctrl); 263 264 ctrl = mvtemp_read(sc, REG_CTRL1); 265 ctrl |= TMEP_CTRL1_TSEN_START; 266 ctrl &= ~TEMP_CTRL1_TSEN_AVG_MASK; 267 mvtemp_write(sc, REG_CTRL1, ctrl); 268 } 269 270 int32_t 271 mvtemp_cp110_calc_temp(uint32_t stat) 272 { 273 return ((stat & 0x3ff) * 476100) - 279100000 + 273150000; 274 } 275 276 void 277 mvtemp_refresh_sensors(void *arg) 278 { 279 struct mvtemp_softc *sc = arg; 280 int32_t stat, temp; 281 282 stat = mvtemp_read(sc, REG_STAT); 283 temp = sc->sc_calc_temp(stat); 284 sc->sc_sensor.value = temp; 285 if ((stat & sc->sc_stat_valid) && temp >= 0) 286 sc->sc_sensor.flags &= ~SENSOR_FINVALID; 287 else 288 sc->sc_sensor.flags |= SENSOR_FINVALID; 289 } 290