16059Samurai/*- 26059Samurai * Copyright (c) 2017 Semihalf. 36059Samurai * Copyright (c) 2017 Stormshield. 46059Samurai * All rights reserved. 56059Samurai * 66059Samurai * Redistribution and use in source and binary forms, with or without 76059Samurai * modification, are permitted provided that the following conditions 86059Samurai * are met: 96059Samurai * 1. Redistributions of source code must retain the above copyright 106059Samurai * notice, this list of conditions and the following disclaimer. 116059Samurai * 2. Redistributions in binary form must reproduce the above copyright 126059Samurai * notice, this list of conditions and the following disclaimer in the 136059Samurai * documentation and/or other materials provided with the distribution. 146059Samurai * 156059Samurai * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 166059Samurai * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 176059Samurai * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 186059Samurai * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 198857Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2029696Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 218857Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 226059Samurai * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2313379Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2427089Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 256059Samurai * SUCH DAMAGE. 266735Samurai */ 2713385Speter 2813379Sphk#include <sys/param.h> 2926031Sbrian#include <sys/sysctl.h> 3026031Sbrian#include <sys/systm.h> 3126031Sbrian#include <sys/bus.h> 3226031Sbrian#include <sys/conf.h> 3326031Sbrian#include <sys/rman.h> 3426031Sbrian#include <sys/kernel.h> 3526031Sbrian#include <sys/lock.h> 3626516Sbrian#include <sys/module.h> 3726516Sbrian#include <sys/mutex.h> 386059Samurai#include <sys/resource.h> 396059Samurai 406059Samurai#include <machine/fdt.h> 416059Samurai 426059Samurai#include <dev/ofw/ofw_bus_subr.h> 4313389Sphk 446059Samurai#define READOUT_TO_C(temp) ((temp) / 1000) 4526031Sbrian 466059Samurai#define STAT_RID 0 4726142Sbrian#define CTRL_RID 1 486059Samurai 4925630Sbrian#define TSEN_STAT_READOUT_VALID 0x1 5025630Sbrian 516059Samurai#define A380_TSEN_CTRL_RESET (1 << 8) 5226516Sbrian 5326940Sbrianstruct armada_thermal_softc; 546059Samurai 556059Samuraitypedef struct armada_thermal_data { 5628679Sbrian /* Initialize the sensor */ 5728679Sbrian void (*tsen_init)(struct armada_thermal_softc *); 5828679Sbrian 5928679Sbrian /* Test for a valid sensor value */ 6028679Sbrian boolean_t (*is_valid)(struct armada_thermal_softc *); 6128679Sbrian 6228679Sbrian /* Formula coefficients: temp = (b + m * reg) / div */ 6328679Sbrian u_long coef_b; 6410528Samurai u_long coef_m; 656735Samurai u_long coef_div; 6614418Sache 676059Samurai boolean_t inverted; 6822973Sphk 6922973Sphk /* Shift and mask to access the sensor temperature */ 706059Samurai u_int temp_shift; 7123603Sache u_int temp_mask; 726059Samurai u_int is_valid_shift; 7328679Sbrian} armada_tdata_t; 7428679Sbrian 7528679Sbrianstatic boolean_t armada_tsen_readout_valid(struct armada_thermal_softc *); 7628679Sbrianstatic int armada_tsen_get_temp(struct armada_thermal_softc *, u_long *); 7728679Sbrianstatic void armada380_tsen_init(struct armada_thermal_softc *); 7828679Sbrianstatic void armada_temp_update(void *); 7928679Sbrian 8028679Sbrianstatic const armada_tdata_t armada380_tdata = { 8128679Sbrian .tsen_init = armada380_tsen_init, 8228679Sbrian .is_valid = armada_tsen_readout_valid, 8328679Sbrian .is_valid_shift = 10, 8428679Sbrian .temp_shift = 0, 856059Samurai .temp_mask = 0x3ff, 866059Samurai .coef_b = 1172499100UL, 8728679Sbrian .coef_m = 2000096UL, 8828679Sbrian .coef_div = 4201, 8928679Sbrian .inverted = TRUE, 9028679Sbrian}; 916059Samurai 9228679Sbrianstatic int armada_thermal_probe(device_t); 936059Samuraistatic int armada_thermal_attach(device_t); 946059Samuraistatic int armada_thermal_detach(device_t); 9526516Sbrian 9626516Sbrianstatic device_method_t armada_thermal_methods[] = { 9726516Sbrian DEVMETHOD(device_probe, armada_thermal_probe), 986059Samurai DEVMETHOD(device_attach, armada_thermal_attach), 9926516Sbrian DEVMETHOD(device_detach, armada_thermal_detach), 10025566Sbrian 10128679Sbrian DEVMETHOD_END 10228679Sbrian}; 1036059Samurai 10426516Sbrianstruct armada_thermal_softc { 1056059Samurai device_t dev; 1066059Samurai 10726516Sbrian struct resource *stat_res; 1086764Samurai struct resource *ctrl_res; 10926587Sbrian 1106059Samurai struct callout temp_upd; 1116059Samurai struct mtx temp_upd_mtx; 1126059Samurai 11326516Sbrian const armada_tdata_t *tdata; 11426516Sbrian 11526516Sbrian u_long chip_temperature; 1166059Samurai}; 1176059Samurai 1186059Samuraistatic driver_t armada_thermal_driver = { 1196059Samurai "armada_thermal", 1206059Samurai armada_thermal_methods, 1216059Samurai sizeof(struct armada_thermal_softc) 1226059Samurai}; 12320120Snate 12420120SnateDRIVER_MODULE(armada_thermal, simplebus, armada_thermal_driver, 0, 0); 12525908SbrianDRIVER_MODULE(armada_thermal, ofwbus, armada_thermal_driver, 0, 0); 12625908Sbrian 12720120Snatestatic int 12810528Samuraiarmada_thermal_probe(device_t dev) 1296059Samurai{ 13010528Samurai struct armada_thermal_softc *sc; 1316059Samurai 13210528Samurai sc = device_get_softc(dev); 1336059Samurai 13426516Sbrian if (!ofw_bus_status_okay(dev)) 13526516Sbrian return (ENXIO); 13626516Sbrian 1376059Samurai if (ofw_bus_is_compatible(dev, "marvell,armada380-thermal")) { 13826516Sbrian device_set_desc(dev, "Armada380 Thermal Control"); 1396059Samurai sc->tdata = &armada380_tdata; 1406059Samurai 1416059Samurai return (BUS_PROBE_DEFAULT); 14228679Sbrian } 1436059Samurai 14411336Samurai return (ENXIO); 14526858Sbrian} 14611336Samurai 1476059Samuraistatic int 14826516Sbrianarmada_thermal_attach(device_t dev) 14926516Sbrian{ 15026516Sbrian struct armada_thermal_softc *sc; 1516059Samurai const armada_tdata_t *tdata; 15226516Sbrian struct sysctl_ctx_list *sctx; 1536735Samurai struct sysctl_oid_list *schildren; 1546735Samurai int timeout; 15526516Sbrian int rid; 15628679Sbrian 15726516Sbrian sc = device_get_softc(dev); 1586735Samurai 1596735Samurai /* Allocate CTRL and STAT register spaces */ 16011336Samurai rid = STAT_RID; 16111336Samurai sc->stat_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 16226516Sbrian &rid, RF_ACTIVE); 16326516Sbrian if (sc->stat_res == NULL) { 16429521Sbrian device_printf(dev, 16526516Sbrian "Could not allocate memory for the status register\n"); 16628679Sbrian return (ENXIO); 16711336Samurai } 16811336Samurai 16926858Sbrian rid = CTRL_RID; 17011336Samurai sc->ctrl_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 17111336Samurai &rid, RF_ACTIVE); 17211336Samurai if (sc->ctrl_res == NULL) { 17311336Samurai device_printf(dev, 17426858Sbrian "Could not allocate memory for the control register\n"); 17526858Sbrian bus_release_resource(dev, SYS_RES_MEMORY, 17611336Samurai rman_get_rid(sc->stat_res), sc->stat_res); 17726516Sbrian sc->stat_res = NULL; 17826516Sbrian return (ENXIO); 1796059Samurai } 1806059Samurai 18110528Samurai /* Now initialize the sensor */ 18228679Sbrian tdata = sc->tdata; 18328536Sbrian tdata->tsen_init(sc); 18428536Sbrian /* Set initial temperature value */ 18528536Sbrian for (timeout = 1000; timeout > 0; timeout--) { 18628536Sbrian if (armada_tsen_get_temp(sc, &sc->chip_temperature) == 0) 18728536Sbrian break; 18828536Sbrian DELAY(10); 18928536Sbrian } 19028536Sbrian if (timeout <= 0) { 19128536Sbrian bus_release_resource(dev, SYS_RES_MEMORY, 19228536Sbrian rman_get_rid(sc->stat_res), sc->stat_res); 19328679Sbrian sc->stat_res = NULL; 19410528Samurai bus_release_resource(dev, SYS_RES_MEMORY, 19528381Sbrian rman_get_rid(sc->ctrl_res), sc->ctrl_res); 19628381Sbrian sc->ctrl_res = NULL; 19728381Sbrian return (ENXIO); 19828381Sbrian } 19928381Sbrian /* Initialize mutex */ 20028381Sbrian mtx_init(&sc->temp_upd_mtx, "Armada Thermal", NULL, MTX_DEF); 20128679Sbrian /* Set up the temperature update callout */ 20228381Sbrian callout_init_mtx(&sc->temp_upd, &sc->temp_upd_mtx, 0); 20328381Sbrian /* Schedule callout */ 20428381Sbrian callout_reset(&sc->temp_upd, hz, armada_temp_update, sc); 20528381Sbrian 20628381Sbrian sctx = device_get_sysctl_ctx(dev); 20728679Sbrian schildren = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); 20828381Sbrian SYSCTL_ADD_LONG(sctx, schildren, OID_AUTO, "temperature", 20910528Samurai CTLFLAG_RD, &sc->chip_temperature, "SoC temperature"); 21010528Samurai 21126516Sbrian return (0); 21220813Sjkh} 21318856Ssos 21426911Sbrianstatic int 21526516Sbrianarmada_thermal_detach(device_t dev) 21626516Sbrian{ 21726516Sbrian struct armada_thermal_softc *sc; 21810528Samurai 21926911Sbrian sc = device_get_softc(dev); 22026911Sbrian 22128679Sbrian if (!device_is_attached(dev)) 22226911Sbrian return (0); 22328679Sbrian 22428679Sbrian callout_drain(&sc->temp_upd); 22526911Sbrian 22628679Sbrian sc->chip_temperature = 0; 22728679Sbrian 22826516Sbrian bus_release_resource(dev, SYS_RES_MEMORY, 22926516Sbrian rman_get_rid(sc->stat_res), sc->stat_res); 23026516Sbrian sc->stat_res = NULL; 23126516Sbrian 23228679Sbrian bus_release_resource(dev, SYS_RES_MEMORY, 23328381Sbrian rman_get_rid(sc->ctrl_res), sc->ctrl_res); 23428381Sbrian sc->ctrl_res = NULL; 23528679Sbrian 23628381Sbrian return (0); 23728381Sbrian} 23828381Sbrian 23928679Sbrianstatic boolean_t 24028381Sbrianarmada_tsen_readout_valid(struct armada_thermal_softc *sc) 24129255Sbrian{ 24229255Sbrian const armada_tdata_t *tdata; 24329255Sbrian uint32_t tsen_stat; 24429255Sbrian boolean_t is_valid; 24528381Sbrian 24628679Sbrian tdata = sc->tdata; 24726516Sbrian tsen_stat = bus_read_4(sc->stat_res, 0); 24826516Sbrian 24928679Sbrian tsen_stat >>= tdata->is_valid_shift; 25028679Sbrian is_valid = ((tsen_stat & TSEN_STAT_READOUT_VALID) != 0); 25118531Sbde 25228679Sbrian return (is_valid); 25328679Sbrian} 25428679Sbrian 25528679Sbrianstatic int 25628679Sbrianarmada_tsen_get_temp(struct armada_thermal_softc *sc, u_long *temp) 25728679Sbrian{ 25828679Sbrian const armada_tdata_t *tdata; 25928679Sbrian uint32_t reg; 26026516Sbrian u_long tmp; 26128679Sbrian u_long m, b, div; 26228679Sbrian 26328679Sbrian tdata = sc->tdata; 26428679Sbrian /* Check if the readout is valid */ 26528679Sbrian if ((tdata->is_valid != NULL) && !tdata->is_valid(sc)) 26628679Sbrian return (EIO); 26728679Sbrian 26828679Sbrian reg = bus_read_4(sc->stat_res, 0); 26926516Sbrian reg = (reg >> tdata->temp_shift) & tdata->temp_mask; 27028679Sbrian 27128679Sbrian /* Get formula coefficients */ 27226516Sbrian b = tdata->coef_b; 27328679Sbrian m = tdata->coef_m; 27428679Sbrian div = tdata->coef_div; 27528679Sbrian 27628679Sbrian if (tdata->inverted) 27728679Sbrian tmp = ((m * reg) - b) / div; 27828679Sbrian else 27928679Sbrian tmp = (b - (m * reg)) / div; 28028679Sbrian 28128679Sbrian *temp = READOUT_TO_C(tmp); 28228679Sbrian 28328679Sbrian return (0); 28428679Sbrian} 28528679Sbrian 28628679Sbrianstatic void 28728679Sbrianarmada380_tsen_init(struct armada_thermal_softc *sc) 28828679Sbrian{ 28928679Sbrian uint32_t tsen_ctrl; 29028679Sbrian 29128679Sbrian tsen_ctrl = bus_read_4(sc->ctrl_res, 0); 29228679Sbrian if ((tsen_ctrl & A380_TSEN_CTRL_RESET) == 0) { 29328679Sbrian tsen_ctrl |= A380_TSEN_CTRL_RESET; 29428679Sbrian bus_write_4(sc->ctrl_res, 0, tsen_ctrl); 29528679Sbrian DELAY(10000); 29628679Sbrian } 29710528Samurai} 29828679Sbrian 29928679Sbrianstatic void 30028974Sbrianarmada_temp_update(void *arg) 30128679Sbrian{ 30228679Sbrian struct armada_thermal_softc *sc; 30328679Sbrian 30428679Sbrian sc = arg; 30528679Sbrian /* Update temperature value, keel old if the readout is not valid */ 30628679Sbrian (void)armada_tsen_get_temp(sc, &sc->chip_temperature); 30720813Sjkh 30828679Sbrian callout_reset(&sc->temp_upd, hz, armada_temp_update, sc); 30928679Sbrian} 31010528Samurai