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 static devclass_t armada_thermal_devclass; 127 128 DRIVER_MODULE(armada_thermal, simplebus, armada_thermal_driver, 129 armada_thermal_devclass, 0, 0); 130 DRIVER_MODULE(armada_thermal, ofwbus, armada_thermal_driver, 131 armada_thermal_devclass, 0, 0); 132 133 static int 134 armada_thermal_probe(device_t dev) 135 { 136 struct armada_thermal_softc *sc; 137 138 sc = device_get_softc(dev); 139 140 if (!ofw_bus_status_okay(dev)) 141 return (ENXIO); 142 143 if (ofw_bus_is_compatible(dev, "marvell,armada380-thermal")) { 144 device_set_desc(dev, "Armada380 Thermal Control"); 145 sc->tdata = &armada380_tdata; 146 147 return (BUS_PROBE_DEFAULT); 148 } 149 150 return (ENXIO); 151 } 152 153 static int 154 armada_thermal_attach(device_t dev) 155 { 156 struct armada_thermal_softc *sc; 157 const armada_tdata_t *tdata; 158 struct sysctl_ctx_list *sctx; 159 struct sysctl_oid_list *schildren; 160 int timeout; 161 int rid; 162 163 sc = device_get_softc(dev); 164 165 /* Allocate CTRL and STAT register spaces */ 166 rid = STAT_RID; 167 sc->stat_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 168 &rid, RF_ACTIVE); 169 if (sc->stat_res == NULL) { 170 device_printf(dev, 171 "Could not allocate memory for the status register\n"); 172 return (ENXIO); 173 } 174 175 rid = CTRL_RID; 176 sc->ctrl_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 177 &rid, RF_ACTIVE); 178 if (sc->ctrl_res == NULL) { 179 device_printf(dev, 180 "Could not allocate memory for the control register\n"); 181 bus_release_resource(dev, SYS_RES_MEMORY, 182 rman_get_rid(sc->stat_res), sc->stat_res); 183 sc->stat_res = NULL; 184 return (ENXIO); 185 } 186 187 /* Now initialize the sensor */ 188 tdata = sc->tdata; 189 tdata->tsen_init(sc); 190 /* Set initial temperature value */ 191 for (timeout = 1000; timeout > 0; timeout--) { 192 if (armada_tsen_get_temp(sc, &sc->chip_temperature) == 0) 193 break; 194 DELAY(10); 195 } 196 if (timeout <= 0) { 197 bus_release_resource(dev, SYS_RES_MEMORY, 198 rman_get_rid(sc->stat_res), sc->stat_res); 199 sc->stat_res = NULL; 200 bus_release_resource(dev, SYS_RES_MEMORY, 201 rman_get_rid(sc->ctrl_res), sc->ctrl_res); 202 sc->ctrl_res = NULL; 203 return (ENXIO); 204 } 205 /* Initialize mutex */ 206 mtx_init(&sc->temp_upd_mtx, "Armada Thermal", NULL, MTX_DEF); 207 /* Set up the temperature update callout */ 208 callout_init_mtx(&sc->temp_upd, &sc->temp_upd_mtx, 0); 209 /* Schedule callout */ 210 callout_reset(&sc->temp_upd, hz, armada_temp_update, sc); 211 212 sctx = device_get_sysctl_ctx(dev); 213 schildren = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); 214 SYSCTL_ADD_LONG(sctx, schildren, OID_AUTO, "temperature", 215 CTLFLAG_RD, &sc->chip_temperature, "SoC temperature"); 216 217 return (0); 218 } 219 220 static int 221 armada_thermal_detach(device_t dev) 222 { 223 struct armada_thermal_softc *sc; 224 225 sc = device_get_softc(dev); 226 227 if (!device_is_attached(dev)) 228 return (0); 229 230 callout_drain(&sc->temp_upd); 231 232 sc->chip_temperature = 0; 233 234 bus_release_resource(dev, SYS_RES_MEMORY, 235 rman_get_rid(sc->stat_res), sc->stat_res); 236 sc->stat_res = NULL; 237 238 bus_release_resource(dev, SYS_RES_MEMORY, 239 rman_get_rid(sc->ctrl_res), sc->ctrl_res); 240 sc->ctrl_res = NULL; 241 242 return (0); 243 } 244 245 static boolean_t 246 armada_tsen_readout_valid(struct armada_thermal_softc *sc) 247 { 248 const armada_tdata_t *tdata; 249 uint32_t tsen_stat; 250 boolean_t is_valid; 251 252 tdata = sc->tdata; 253 tsen_stat = bus_read_4(sc->stat_res, 0); 254 255 tsen_stat >>= tdata->is_valid_shift; 256 is_valid = ((tsen_stat & TSEN_STAT_READOUT_VALID) != 0); 257 258 return (is_valid); 259 } 260 261 static int 262 armada_tsen_get_temp(struct armada_thermal_softc *sc, u_long *temp) 263 { 264 const armada_tdata_t *tdata; 265 uint32_t reg; 266 u_long tmp; 267 u_long m, b, div; 268 269 tdata = sc->tdata; 270 /* Check if the readout is valid */ 271 if ((tdata->is_valid != NULL) && !tdata->is_valid(sc)) 272 return (EIO); 273 274 reg = bus_read_4(sc->stat_res, 0); 275 reg = (reg >> tdata->temp_shift) & tdata->temp_mask; 276 277 /* Get formula coefficients */ 278 b = tdata->coef_b; 279 m = tdata->coef_m; 280 div = tdata->coef_div; 281 282 if (tdata->inverted) 283 tmp = ((m * reg) - b) / div; 284 else 285 tmp = (b - (m * reg)) / div; 286 287 *temp = READOUT_TO_C(tmp); 288 289 return (0); 290 } 291 292 static void 293 armada380_tsen_init(struct armada_thermal_softc *sc) 294 { 295 uint32_t tsen_ctrl; 296 297 tsen_ctrl = bus_read_4(sc->ctrl_res, 0); 298 if ((tsen_ctrl & A380_TSEN_CTRL_RESET) == 0) { 299 tsen_ctrl |= A380_TSEN_CTRL_RESET; 300 bus_write_4(sc->ctrl_res, 0, tsen_ctrl); 301 DELAY(10000); 302 } 303 } 304 305 static void 306 armada_temp_update(void *arg) 307 { 308 struct armada_thermal_softc *sc; 309 310 sc = arg; 311 /* Update temperature value, keel old if the readout is not valid */ 312 (void)armada_tsen_get_temp(sc, &sc->chip_temperature); 313 314 callout_reset(&sc->temp_upd, hz, armada_temp_update, sc); 315 } 316