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 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/sysctl.h> 32 #include <sys/systm.h> 33 #include <sys/bus.h> 34 #include <sys/conf.h> 35 #include <sys/rman.h> 36 #include <sys/kernel.h> 37 #include <sys/lock.h> 38 #include <sys/module.h> 39 #include <sys/mutex.h> 40 #include <sys/resource.h> 41 42 #include <machine/fdt.h> 43 44 #include <dev/ofw/ofw_bus_subr.h> 45 46 #define READOUT_TO_C(temp) ((temp) / 1000) 47 48 #define STAT_RID 0 49 #define CTRL_RID 1 50 51 #define TSEN_STAT_READOUT_VALID 0x1 52 53 #define A380_TSEN_CTRL_RESET (1 << 8) 54 55 struct armada_thermal_softc; 56 57 typedef struct armada_thermal_data { 58 /* Initialize the sensor */ 59 void (*tsen_init)(struct armada_thermal_softc *); 60 61 /* Test for a valid sensor value */ 62 boolean_t (*is_valid)(struct armada_thermal_softc *); 63 64 /* Formula coefficients: temp = (b + m * reg) / div */ 65 u_long coef_b; 66 u_long coef_m; 67 u_long coef_div; 68 69 boolean_t inverted; 70 71 /* Shift and mask to access the sensor temperature */ 72 u_int temp_shift; 73 u_int temp_mask; 74 u_int is_valid_shift; 75 } armada_tdata_t; 76 77 static boolean_t armada_tsen_readout_valid(struct armada_thermal_softc *); 78 static int armada_tsen_get_temp(struct armada_thermal_softc *, u_long *); 79 static void armada380_tsen_init(struct armada_thermal_softc *); 80 static void armada_temp_update(void *); 81 82 static const armada_tdata_t armada380_tdata = { 83 .tsen_init = armada380_tsen_init, 84 .is_valid = armada_tsen_readout_valid, 85 .is_valid_shift = 10, 86 .temp_shift = 0, 87 .temp_mask = 0x3ff, 88 .coef_b = 1172499100UL, 89 .coef_m = 2000096UL, 90 .coef_div = 4201, 91 .inverted = TRUE, 92 }; 93 94 static int armada_thermal_probe(device_t); 95 static int armada_thermal_attach(device_t); 96 static int armada_thermal_detach(device_t); 97 98 static device_method_t armada_thermal_methods[] = { 99 DEVMETHOD(device_probe, armada_thermal_probe), 100 DEVMETHOD(device_attach, armada_thermal_attach), 101 DEVMETHOD(device_detach, armada_thermal_detach), 102 103 DEVMETHOD_END 104 }; 105 106 struct armada_thermal_softc { 107 device_t dev; 108 109 struct resource *stat_res; 110 struct resource *ctrl_res; 111 112 struct callout temp_upd; 113 struct mtx temp_upd_mtx; 114 115 const armada_tdata_t *tdata; 116 117 u_long chip_temperature; 118 }; 119 120 static driver_t armada_thermal_driver = { 121 "armada_thermal", 122 armada_thermal_methods, 123 sizeof(struct armada_thermal_softc) 124 }; 125 126 DRIVER_MODULE(armada_thermal, simplebus, armada_thermal_driver, 0, 0); 127 DRIVER_MODULE(armada_thermal, ofwbus, armada_thermal_driver, 0, 0); 128 129 static int 130 armada_thermal_probe(device_t dev) 131 { 132 struct armada_thermal_softc *sc; 133 134 sc = device_get_softc(dev); 135 136 if (!ofw_bus_status_okay(dev)) 137 return (ENXIO); 138 139 if (ofw_bus_is_compatible(dev, "marvell,armada380-thermal")) { 140 device_set_desc(dev, "Armada380 Thermal Control"); 141 sc->tdata = &armada380_tdata; 142 143 return (BUS_PROBE_DEFAULT); 144 } 145 146 return (ENXIO); 147 } 148 149 static int 150 armada_thermal_attach(device_t dev) 151 { 152 struct armada_thermal_softc *sc; 153 const armada_tdata_t *tdata; 154 struct sysctl_ctx_list *sctx; 155 struct sysctl_oid_list *schildren; 156 int timeout; 157 int rid; 158 159 sc = device_get_softc(dev); 160 161 /* Allocate CTRL and STAT register spaces */ 162 rid = STAT_RID; 163 sc->stat_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 164 &rid, RF_ACTIVE); 165 if (sc->stat_res == NULL) { 166 device_printf(dev, 167 "Could not allocate memory for the status register\n"); 168 return (ENXIO); 169 } 170 171 rid = CTRL_RID; 172 sc->ctrl_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 173 &rid, RF_ACTIVE); 174 if (sc->ctrl_res == NULL) { 175 device_printf(dev, 176 "Could not allocate memory for the control register\n"); 177 bus_release_resource(dev, SYS_RES_MEMORY, 178 rman_get_rid(sc->stat_res), sc->stat_res); 179 sc->stat_res = NULL; 180 return (ENXIO); 181 } 182 183 /* Now initialize the sensor */ 184 tdata = sc->tdata; 185 tdata->tsen_init(sc); 186 /* Set initial temperature value */ 187 for (timeout = 1000; timeout > 0; timeout--) { 188 if (armada_tsen_get_temp(sc, &sc->chip_temperature) == 0) 189 break; 190 DELAY(10); 191 } 192 if (timeout <= 0) { 193 bus_release_resource(dev, SYS_RES_MEMORY, 194 rman_get_rid(sc->stat_res), sc->stat_res); 195 sc->stat_res = NULL; 196 bus_release_resource(dev, SYS_RES_MEMORY, 197 rman_get_rid(sc->ctrl_res), sc->ctrl_res); 198 sc->ctrl_res = NULL; 199 return (ENXIO); 200 } 201 /* Initialize mutex */ 202 mtx_init(&sc->temp_upd_mtx, "Armada Thermal", NULL, MTX_DEF); 203 /* Set up the temperature update callout */ 204 callout_init_mtx(&sc->temp_upd, &sc->temp_upd_mtx, 0); 205 /* Schedule callout */ 206 callout_reset(&sc->temp_upd, hz, armada_temp_update, sc); 207 208 sctx = device_get_sysctl_ctx(dev); 209 schildren = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); 210 SYSCTL_ADD_LONG(sctx, schildren, OID_AUTO, "temperature", 211 CTLFLAG_RD, &sc->chip_temperature, "SoC temperature"); 212 213 return (0); 214 } 215 216 static int 217 armada_thermal_detach(device_t dev) 218 { 219 struct armada_thermal_softc *sc; 220 221 sc = device_get_softc(dev); 222 223 if (!device_is_attached(dev)) 224 return (0); 225 226 callout_drain(&sc->temp_upd); 227 228 sc->chip_temperature = 0; 229 230 bus_release_resource(dev, SYS_RES_MEMORY, 231 rman_get_rid(sc->stat_res), sc->stat_res); 232 sc->stat_res = NULL; 233 234 bus_release_resource(dev, SYS_RES_MEMORY, 235 rman_get_rid(sc->ctrl_res), sc->ctrl_res); 236 sc->ctrl_res = NULL; 237 238 return (0); 239 } 240 241 static boolean_t 242 armada_tsen_readout_valid(struct armada_thermal_softc *sc) 243 { 244 const armada_tdata_t *tdata; 245 uint32_t tsen_stat; 246 boolean_t is_valid; 247 248 tdata = sc->tdata; 249 tsen_stat = bus_read_4(sc->stat_res, 0); 250 251 tsen_stat >>= tdata->is_valid_shift; 252 is_valid = ((tsen_stat & TSEN_STAT_READOUT_VALID) != 0); 253 254 return (is_valid); 255 } 256 257 static int 258 armada_tsen_get_temp(struct armada_thermal_softc *sc, u_long *temp) 259 { 260 const armada_tdata_t *tdata; 261 uint32_t reg; 262 u_long tmp; 263 u_long m, b, div; 264 265 tdata = sc->tdata; 266 /* Check if the readout is valid */ 267 if ((tdata->is_valid != NULL) && !tdata->is_valid(sc)) 268 return (EIO); 269 270 reg = bus_read_4(sc->stat_res, 0); 271 reg = (reg >> tdata->temp_shift) & tdata->temp_mask; 272 273 /* Get formula coefficients */ 274 b = tdata->coef_b; 275 m = tdata->coef_m; 276 div = tdata->coef_div; 277 278 if (tdata->inverted) 279 tmp = ((m * reg) - b) / div; 280 else 281 tmp = (b - (m * reg)) / div; 282 283 *temp = READOUT_TO_C(tmp); 284 285 return (0); 286 } 287 288 static void 289 armada380_tsen_init(struct armada_thermal_softc *sc) 290 { 291 uint32_t tsen_ctrl; 292 293 tsen_ctrl = bus_read_4(sc->ctrl_res, 0); 294 if ((tsen_ctrl & A380_TSEN_CTRL_RESET) == 0) { 295 tsen_ctrl |= A380_TSEN_CTRL_RESET; 296 bus_write_4(sc->ctrl_res, 0, tsen_ctrl); 297 DELAY(10000); 298 } 299 } 300 301 static void 302 armada_temp_update(void *arg) 303 { 304 struct armada_thermal_softc *sc; 305 306 sc = arg; 307 /* Update temperature value, keel old if the readout is not valid */ 308 (void)armada_tsen_get_temp(sc, &sc->chip_temperature); 309 310 callout_reset(&sc->temp_upd, hz, armada_temp_update, sc); 311 } 312