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