1 /*- 2 * Copyright (c) 2017 Semihalf. 3 * Copyright (c) 2017 Stormshield. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/sysctl.h> 30 #include <sys/systm.h> 31 #include <sys/bus.h> 32 #include <sys/conf.h> 33 #include <sys/rman.h> 34 #include <sys/kernel.h> 35 #include <sys/lock.h> 36 #include <sys/module.h> 37 #include <sys/mutex.h> 38 #include <sys/resource.h> 39 40 #include <machine/fdt.h> 41 42 #include <dev/ofw/ofw_bus_subr.h> 43 44 #define READOUT_TO_C(temp) ((temp) / 1000) 45 46 #define STAT_RID 0 47 #define CTRL_RID 1 48 49 #define TSEN_STAT_READOUT_VALID 0x1 50 51 #define A380_TSEN_CTRL_RESET (1 << 8) 52 53 struct armada_thermal_softc; 54 55 typedef struct armada_thermal_data { 56 /* Initialize the sensor */ 57 void (*tsen_init)(struct armada_thermal_softc *); 58 59 /* Test for a valid sensor value */ 60 boolean_t (*is_valid)(struct armada_thermal_softc *); 61 62 /* Formula coefficients: temp = (b + m * reg) / div */ 63 u_long coef_b; 64 u_long coef_m; 65 u_long coef_div; 66 67 boolean_t inverted; 68 69 /* Shift and mask to access the sensor temperature */ 70 u_int temp_shift; 71 u_int temp_mask; 72 u_int is_valid_shift; 73 } armada_tdata_t; 74 75 static boolean_t armada_tsen_readout_valid(struct armada_thermal_softc *); 76 static int armada_tsen_get_temp(struct armada_thermal_softc *, u_long *); 77 static void armada380_tsen_init(struct armada_thermal_softc *); 78 static void armada_temp_update(void *); 79 80 static const armada_tdata_t armada380_tdata = { 81 .tsen_init = armada380_tsen_init, 82 .is_valid = armada_tsen_readout_valid, 83 .is_valid_shift = 10, 84 .temp_shift = 0, 85 .temp_mask = 0x3ff, 86 .coef_b = 1172499100UL, 87 .coef_m = 2000096UL, 88 .coef_div = 4201, 89 .inverted = TRUE, 90 }; 91 92 static int armada_thermal_probe(device_t); 93 static int armada_thermal_attach(device_t); 94 static int armada_thermal_detach(device_t); 95 96 static device_method_t armada_thermal_methods[] = { 97 DEVMETHOD(device_probe, armada_thermal_probe), 98 DEVMETHOD(device_attach, armada_thermal_attach), 99 DEVMETHOD(device_detach, armada_thermal_detach), 100 101 DEVMETHOD_END 102 }; 103 104 struct armada_thermal_softc { 105 device_t dev; 106 107 struct resource *stat_res; 108 struct resource *ctrl_res; 109 110 struct callout temp_upd; 111 struct mtx temp_upd_mtx; 112 113 const armada_tdata_t *tdata; 114 115 u_long chip_temperature; 116 }; 117 118 static driver_t armada_thermal_driver = { 119 "armada_thermal", 120 armada_thermal_methods, 121 sizeof(struct armada_thermal_softc) 122 }; 123 124 DRIVER_MODULE(armada_thermal, simplebus, armada_thermal_driver, 0, 0); 125 DRIVER_MODULE(armada_thermal, ofwbus, armada_thermal_driver, 0, 0); 126 127 static int 128 armada_thermal_probe(device_t dev) 129 { 130 struct armada_thermal_softc *sc; 131 132 sc = device_get_softc(dev); 133 134 if (!ofw_bus_status_okay(dev)) 135 return (ENXIO); 136 137 if (ofw_bus_is_compatible(dev, "marvell,armada380-thermal")) { 138 device_set_desc(dev, "Armada380 Thermal Control"); 139 sc->tdata = &armada380_tdata; 140 141 return (BUS_PROBE_DEFAULT); 142 } 143 144 return (ENXIO); 145 } 146 147 static int 148 armada_thermal_attach(device_t dev) 149 { 150 struct armada_thermal_softc *sc; 151 const armada_tdata_t *tdata; 152 struct sysctl_ctx_list *sctx; 153 struct sysctl_oid_list *schildren; 154 int timeout; 155 int rid; 156 157 sc = device_get_softc(dev); 158 159 /* Allocate CTRL and STAT register spaces */ 160 rid = STAT_RID; 161 sc->stat_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 162 &rid, RF_ACTIVE); 163 if (sc->stat_res == NULL) { 164 device_printf(dev, 165 "Could not allocate memory for the status register\n"); 166 return (ENXIO); 167 } 168 169 rid = CTRL_RID; 170 sc->ctrl_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 171 &rid, RF_ACTIVE); 172 if (sc->ctrl_res == NULL) { 173 device_printf(dev, 174 "Could not allocate memory for the control register\n"); 175 bus_release_resource(dev, SYS_RES_MEMORY, 176 rman_get_rid(sc->stat_res), sc->stat_res); 177 sc->stat_res = NULL; 178 return (ENXIO); 179 } 180 181 /* Now initialize the sensor */ 182 tdata = sc->tdata; 183 tdata->tsen_init(sc); 184 /* Set initial temperature value */ 185 for (timeout = 1000; timeout > 0; timeout--) { 186 if (armada_tsen_get_temp(sc, &sc->chip_temperature) == 0) 187 break; 188 DELAY(10); 189 } 190 if (timeout <= 0) { 191 bus_release_resource(dev, SYS_RES_MEMORY, 192 rman_get_rid(sc->stat_res), sc->stat_res); 193 sc->stat_res = NULL; 194 bus_release_resource(dev, SYS_RES_MEMORY, 195 rman_get_rid(sc->ctrl_res), sc->ctrl_res); 196 sc->ctrl_res = NULL; 197 return (ENXIO); 198 } 199 /* Initialize mutex */ 200 mtx_init(&sc->temp_upd_mtx, "Armada Thermal", NULL, MTX_DEF); 201 /* Set up the temperature update callout */ 202 callout_init_mtx(&sc->temp_upd, &sc->temp_upd_mtx, 0); 203 /* Schedule callout */ 204 callout_reset(&sc->temp_upd, hz, armada_temp_update, sc); 205 206 sctx = device_get_sysctl_ctx(dev); 207 schildren = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); 208 SYSCTL_ADD_LONG(sctx, schildren, OID_AUTO, "temperature", 209 CTLFLAG_RD, &sc->chip_temperature, "SoC temperature"); 210 211 return (0); 212 } 213 214 static int 215 armada_thermal_detach(device_t dev) 216 { 217 struct armada_thermal_softc *sc; 218 219 sc = device_get_softc(dev); 220 221 if (!device_is_attached(dev)) 222 return (0); 223 224 callout_drain(&sc->temp_upd); 225 226 sc->chip_temperature = 0; 227 228 bus_release_resource(dev, SYS_RES_MEMORY, 229 rman_get_rid(sc->stat_res), sc->stat_res); 230 sc->stat_res = NULL; 231 232 bus_release_resource(dev, SYS_RES_MEMORY, 233 rman_get_rid(sc->ctrl_res), sc->ctrl_res); 234 sc->ctrl_res = NULL; 235 236 return (0); 237 } 238 239 static boolean_t 240 armada_tsen_readout_valid(struct armada_thermal_softc *sc) 241 { 242 const armada_tdata_t *tdata; 243 uint32_t tsen_stat; 244 boolean_t is_valid; 245 246 tdata = sc->tdata; 247 tsen_stat = bus_read_4(sc->stat_res, 0); 248 249 tsen_stat >>= tdata->is_valid_shift; 250 is_valid = ((tsen_stat & TSEN_STAT_READOUT_VALID) != 0); 251 252 return (is_valid); 253 } 254 255 static int 256 armada_tsen_get_temp(struct armada_thermal_softc *sc, u_long *temp) 257 { 258 const armada_tdata_t *tdata; 259 uint32_t reg; 260 u_long tmp; 261 u_long m, b, div; 262 263 tdata = sc->tdata; 264 /* Check if the readout is valid */ 265 if ((tdata->is_valid != NULL) && !tdata->is_valid(sc)) 266 return (EIO); 267 268 reg = bus_read_4(sc->stat_res, 0); 269 reg = (reg >> tdata->temp_shift) & tdata->temp_mask; 270 271 /* Get formula coefficients */ 272 b = tdata->coef_b; 273 m = tdata->coef_m; 274 div = tdata->coef_div; 275 276 if (tdata->inverted) 277 tmp = ((m * reg) - b) / div; 278 else 279 tmp = (b - (m * reg)) / div; 280 281 *temp = READOUT_TO_C(tmp); 282 283 return (0); 284 } 285 286 static void 287 armada380_tsen_init(struct armada_thermal_softc *sc) 288 { 289 uint32_t tsen_ctrl; 290 291 tsen_ctrl = bus_read_4(sc->ctrl_res, 0); 292 if ((tsen_ctrl & A380_TSEN_CTRL_RESET) == 0) { 293 tsen_ctrl |= A380_TSEN_CTRL_RESET; 294 bus_write_4(sc->ctrl_res, 0, tsen_ctrl); 295 DELAY(10000); 296 } 297 } 298 299 static void 300 armada_temp_update(void *arg) 301 { 302 struct armada_thermal_softc *sc; 303 304 sc = arg; 305 /* Update temperature value, keel old if the readout is not valid */ 306 (void)armada_tsen_get_temp(sc, &sc->chip_temperature); 307 308 callout_reset(&sc->temp_upd, hz, armada_temp_update, sc); 309 } 310