123e46357SWojciech Macek /*-
223e46357SWojciech Macek * Copyright (c) 2017 Semihalf.
323e46357SWojciech Macek * Copyright (c) 2017 Stormshield.
423e46357SWojciech Macek * All rights reserved.
523e46357SWojciech Macek *
623e46357SWojciech Macek * Redistribution and use in source and binary forms, with or without
723e46357SWojciech Macek * modification, are permitted provided that the following conditions
823e46357SWojciech Macek * are met:
923e46357SWojciech Macek * 1. Redistributions of source code must retain the above copyright
1023e46357SWojciech Macek * notice, this list of conditions and the following disclaimer.
1123e46357SWojciech Macek * 2. Redistributions in binary form must reproduce the above copyright
1223e46357SWojciech Macek * notice, this list of conditions and the following disclaimer in the
1323e46357SWojciech Macek * documentation and/or other materials provided with the distribution.
1423e46357SWojciech Macek *
1523e46357SWojciech Macek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1623e46357SWojciech Macek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1723e46357SWojciech Macek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1823e46357SWojciech Macek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1923e46357SWojciech Macek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2023e46357SWojciech Macek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2123e46357SWojciech Macek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2223e46357SWojciech Macek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2323e46357SWojciech Macek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2423e46357SWojciech Macek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2523e46357SWojciech Macek * SUCH DAMAGE.
2623e46357SWojciech Macek */
27*fdafd315SWarner Losh
2823e46357SWojciech Macek #include <sys/param.h>
2923e46357SWojciech Macek #include <sys/sysctl.h>
3023e46357SWojciech Macek #include <sys/systm.h>
3123e46357SWojciech Macek #include <sys/bus.h>
3223e46357SWojciech Macek #include <sys/conf.h>
3323e46357SWojciech Macek #include <sys/rman.h>
3423e46357SWojciech Macek #include <sys/kernel.h>
35e2e050c8SConrad Meyer #include <sys/lock.h>
3623e46357SWojciech Macek #include <sys/module.h>
37e2e050c8SConrad Meyer #include <sys/mutex.h>
3823e46357SWojciech Macek #include <sys/resource.h>
3923e46357SWojciech Macek
4023e46357SWojciech Macek #include <machine/fdt.h>
4123e46357SWojciech Macek
4223e46357SWojciech Macek #include <dev/ofw/ofw_bus_subr.h>
4323e46357SWojciech Macek
4423e46357SWojciech Macek #define READOUT_TO_C(temp) ((temp) / 1000)
4523e46357SWojciech Macek
4623e46357SWojciech Macek #define STAT_RID 0
4723e46357SWojciech Macek #define CTRL_RID 1
4823e46357SWojciech Macek
4923e46357SWojciech Macek #define TSEN_STAT_READOUT_VALID 0x1
5023e46357SWojciech Macek
5123e46357SWojciech Macek #define A380_TSEN_CTRL_RESET (1 << 8)
5223e46357SWojciech Macek
5323e46357SWojciech Macek struct armada_thermal_softc;
5423e46357SWojciech Macek
5523e46357SWojciech Macek typedef struct armada_thermal_data {
5623e46357SWojciech Macek /* Initialize the sensor */
5723e46357SWojciech Macek void (*tsen_init)(struct armada_thermal_softc *);
5823e46357SWojciech Macek
5923e46357SWojciech Macek /* Test for a valid sensor value */
6023e46357SWojciech Macek boolean_t (*is_valid)(struct armada_thermal_softc *);
6123e46357SWojciech Macek
6223e46357SWojciech Macek /* Formula coefficients: temp = (b + m * reg) / div */
6323e46357SWojciech Macek u_long coef_b;
6423e46357SWojciech Macek u_long coef_m;
6523e46357SWojciech Macek u_long coef_div;
6623e46357SWojciech Macek
6723e46357SWojciech Macek boolean_t inverted;
6823e46357SWojciech Macek
6923e46357SWojciech Macek /* Shift and mask to access the sensor temperature */
7023e46357SWojciech Macek u_int temp_shift;
7123e46357SWojciech Macek u_int temp_mask;
7223e46357SWojciech Macek u_int is_valid_shift;
7323e46357SWojciech Macek } armada_tdata_t;
7423e46357SWojciech Macek
7523e46357SWojciech Macek static boolean_t armada_tsen_readout_valid(struct armada_thermal_softc *);
7623e46357SWojciech Macek static int armada_tsen_get_temp(struct armada_thermal_softc *, u_long *);
7723e46357SWojciech Macek static void armada380_tsen_init(struct armada_thermal_softc *);
7823e46357SWojciech Macek static void armada_temp_update(void *);
7923e46357SWojciech Macek
8023e46357SWojciech Macek static const armada_tdata_t armada380_tdata = {
8123e46357SWojciech Macek .tsen_init = armada380_tsen_init,
8223e46357SWojciech Macek .is_valid = armada_tsen_readout_valid,
8323e46357SWojciech Macek .is_valid_shift = 10,
8423e46357SWojciech Macek .temp_shift = 0,
8523e46357SWojciech Macek .temp_mask = 0x3ff,
8623e46357SWojciech Macek .coef_b = 1172499100UL,
8723e46357SWojciech Macek .coef_m = 2000096UL,
8823e46357SWojciech Macek .coef_div = 4201,
8923e46357SWojciech Macek .inverted = TRUE,
9023e46357SWojciech Macek };
9123e46357SWojciech Macek
9223e46357SWojciech Macek static int armada_thermal_probe(device_t);
9323e46357SWojciech Macek static int armada_thermal_attach(device_t);
9423e46357SWojciech Macek static int armada_thermal_detach(device_t);
9523e46357SWojciech Macek
9623e46357SWojciech Macek static device_method_t armada_thermal_methods[] = {
9723e46357SWojciech Macek DEVMETHOD(device_probe, armada_thermal_probe),
9823e46357SWojciech Macek DEVMETHOD(device_attach, armada_thermal_attach),
9923e46357SWojciech Macek DEVMETHOD(device_detach, armada_thermal_detach),
10023e46357SWojciech Macek
10123e46357SWojciech Macek DEVMETHOD_END
10223e46357SWojciech Macek };
10323e46357SWojciech Macek
10423e46357SWojciech Macek struct armada_thermal_softc {
10523e46357SWojciech Macek device_t dev;
10623e46357SWojciech Macek
10723e46357SWojciech Macek struct resource *stat_res;
10823e46357SWojciech Macek struct resource *ctrl_res;
10923e46357SWojciech Macek
11023e46357SWojciech Macek struct callout temp_upd;
11123e46357SWojciech Macek struct mtx temp_upd_mtx;
11223e46357SWojciech Macek
11323e46357SWojciech Macek const armada_tdata_t *tdata;
11423e46357SWojciech Macek
11523e46357SWojciech Macek u_long chip_temperature;
11623e46357SWojciech Macek };
11723e46357SWojciech Macek
11823e46357SWojciech Macek static driver_t armada_thermal_driver = {
11923e46357SWojciech Macek "armada_thermal",
12023e46357SWojciech Macek armada_thermal_methods,
12123e46357SWojciech Macek sizeof(struct armada_thermal_softc)
12223e46357SWojciech Macek };
12323e46357SWojciech Macek
124a3b866cbSJohn Baldwin DRIVER_MODULE(armada_thermal, simplebus, armada_thermal_driver, 0, 0);
125a3b866cbSJohn Baldwin DRIVER_MODULE(armada_thermal, ofwbus, armada_thermal_driver, 0, 0);
12623e46357SWojciech Macek
12723e46357SWojciech Macek static int
armada_thermal_probe(device_t dev)12823e46357SWojciech Macek armada_thermal_probe(device_t dev)
12923e46357SWojciech Macek {
13023e46357SWojciech Macek struct armada_thermal_softc *sc;
13123e46357SWojciech Macek
13223e46357SWojciech Macek sc = device_get_softc(dev);
13323e46357SWojciech Macek
13423e46357SWojciech Macek if (!ofw_bus_status_okay(dev))
13523e46357SWojciech Macek return (ENXIO);
13623e46357SWojciech Macek
13723e46357SWojciech Macek if (ofw_bus_is_compatible(dev, "marvell,armada380-thermal")) {
13823e46357SWojciech Macek device_set_desc(dev, "Armada380 Thermal Control");
13923e46357SWojciech Macek sc->tdata = &armada380_tdata;
14023e46357SWojciech Macek
14123e46357SWojciech Macek return (BUS_PROBE_DEFAULT);
14223e46357SWojciech Macek }
14323e46357SWojciech Macek
14423e46357SWojciech Macek return (ENXIO);
14523e46357SWojciech Macek }
14623e46357SWojciech Macek
14723e46357SWojciech Macek static int
armada_thermal_attach(device_t dev)14823e46357SWojciech Macek armada_thermal_attach(device_t dev)
14923e46357SWojciech Macek {
15023e46357SWojciech Macek struct armada_thermal_softc *sc;
15123e46357SWojciech Macek const armada_tdata_t *tdata;
15223e46357SWojciech Macek struct sysctl_ctx_list *sctx;
15323e46357SWojciech Macek struct sysctl_oid_list *schildren;
15423e46357SWojciech Macek int timeout;
15523e46357SWojciech Macek int rid;
15623e46357SWojciech Macek
15723e46357SWojciech Macek sc = device_get_softc(dev);
15823e46357SWojciech Macek
15923e46357SWojciech Macek /* Allocate CTRL and STAT register spaces */
16023e46357SWojciech Macek rid = STAT_RID;
16123e46357SWojciech Macek sc->stat_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
16223e46357SWojciech Macek &rid, RF_ACTIVE);
16323e46357SWojciech Macek if (sc->stat_res == NULL) {
16423e46357SWojciech Macek device_printf(dev,
16523e46357SWojciech Macek "Could not allocate memory for the status register\n");
16623e46357SWojciech Macek return (ENXIO);
16723e46357SWojciech Macek }
16823e46357SWojciech Macek
16923e46357SWojciech Macek rid = CTRL_RID;
17023e46357SWojciech Macek sc->ctrl_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
17123e46357SWojciech Macek &rid, RF_ACTIVE);
17223e46357SWojciech Macek if (sc->ctrl_res == NULL) {
17323e46357SWojciech Macek device_printf(dev,
17423e46357SWojciech Macek "Could not allocate memory for the control register\n");
17523e46357SWojciech Macek bus_release_resource(dev, SYS_RES_MEMORY,
17623e46357SWojciech Macek rman_get_rid(sc->stat_res), sc->stat_res);
17723e46357SWojciech Macek sc->stat_res = NULL;
17823e46357SWojciech Macek return (ENXIO);
17923e46357SWojciech Macek }
18023e46357SWojciech Macek
18123e46357SWojciech Macek /* Now initialize the sensor */
18223e46357SWojciech Macek tdata = sc->tdata;
18323e46357SWojciech Macek tdata->tsen_init(sc);
18423e46357SWojciech Macek /* Set initial temperature value */
18523e46357SWojciech Macek for (timeout = 1000; timeout > 0; timeout--) {
18623e46357SWojciech Macek if (armada_tsen_get_temp(sc, &sc->chip_temperature) == 0)
18723e46357SWojciech Macek break;
18823e46357SWojciech Macek DELAY(10);
18923e46357SWojciech Macek }
19023e46357SWojciech Macek if (timeout <= 0) {
19123e46357SWojciech Macek bus_release_resource(dev, SYS_RES_MEMORY,
19223e46357SWojciech Macek rman_get_rid(sc->stat_res), sc->stat_res);
19323e46357SWojciech Macek sc->stat_res = NULL;
19423e46357SWojciech Macek bus_release_resource(dev, SYS_RES_MEMORY,
19523e46357SWojciech Macek rman_get_rid(sc->ctrl_res), sc->ctrl_res);
19623e46357SWojciech Macek sc->ctrl_res = NULL;
19723e46357SWojciech Macek return (ENXIO);
19823e46357SWojciech Macek }
19923e46357SWojciech Macek /* Initialize mutex */
20023e46357SWojciech Macek mtx_init(&sc->temp_upd_mtx, "Armada Thermal", NULL, MTX_DEF);
20123e46357SWojciech Macek /* Set up the temperature update callout */
20223e46357SWojciech Macek callout_init_mtx(&sc->temp_upd, &sc->temp_upd_mtx, 0);
20323e46357SWojciech Macek /* Schedule callout */
20423e46357SWojciech Macek callout_reset(&sc->temp_upd, hz, armada_temp_update, sc);
20523e46357SWojciech Macek
20623e46357SWojciech Macek sctx = device_get_sysctl_ctx(dev);
20723e46357SWojciech Macek schildren = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
20823e46357SWojciech Macek SYSCTL_ADD_LONG(sctx, schildren, OID_AUTO, "temperature",
20923e46357SWojciech Macek CTLFLAG_RD, &sc->chip_temperature, "SoC temperature");
21023e46357SWojciech Macek
21123e46357SWojciech Macek return (0);
21223e46357SWojciech Macek }
21323e46357SWojciech Macek
21423e46357SWojciech Macek static int
armada_thermal_detach(device_t dev)21523e46357SWojciech Macek armada_thermal_detach(device_t dev)
21623e46357SWojciech Macek {
21723e46357SWojciech Macek struct armada_thermal_softc *sc;
21823e46357SWojciech Macek
21923e46357SWojciech Macek sc = device_get_softc(dev);
22023e46357SWojciech Macek
22123e46357SWojciech Macek if (!device_is_attached(dev))
22223e46357SWojciech Macek return (0);
22323e46357SWojciech Macek
22423e46357SWojciech Macek callout_drain(&sc->temp_upd);
22523e46357SWojciech Macek
22623e46357SWojciech Macek sc->chip_temperature = 0;
22723e46357SWojciech Macek
22823e46357SWojciech Macek bus_release_resource(dev, SYS_RES_MEMORY,
22923e46357SWojciech Macek rman_get_rid(sc->stat_res), sc->stat_res);
23023e46357SWojciech Macek sc->stat_res = NULL;
23123e46357SWojciech Macek
23223e46357SWojciech Macek bus_release_resource(dev, SYS_RES_MEMORY,
23323e46357SWojciech Macek rman_get_rid(sc->ctrl_res), sc->ctrl_res);
23423e46357SWojciech Macek sc->ctrl_res = NULL;
23523e46357SWojciech Macek
23623e46357SWojciech Macek return (0);
23723e46357SWojciech Macek }
23823e46357SWojciech Macek
23923e46357SWojciech Macek static boolean_t
armada_tsen_readout_valid(struct armada_thermal_softc * sc)24023e46357SWojciech Macek armada_tsen_readout_valid(struct armada_thermal_softc *sc)
24123e46357SWojciech Macek {
24223e46357SWojciech Macek const armada_tdata_t *tdata;
24323e46357SWojciech Macek uint32_t tsen_stat;
24423e46357SWojciech Macek boolean_t is_valid;
24523e46357SWojciech Macek
24623e46357SWojciech Macek tdata = sc->tdata;
24723e46357SWojciech Macek tsen_stat = bus_read_4(sc->stat_res, 0);
24823e46357SWojciech Macek
24923e46357SWojciech Macek tsen_stat >>= tdata->is_valid_shift;
25023e46357SWojciech Macek is_valid = ((tsen_stat & TSEN_STAT_READOUT_VALID) != 0);
25123e46357SWojciech Macek
25223e46357SWojciech Macek return (is_valid);
25323e46357SWojciech Macek }
25423e46357SWojciech Macek
25523e46357SWojciech Macek static int
armada_tsen_get_temp(struct armada_thermal_softc * sc,u_long * temp)25623e46357SWojciech Macek armada_tsen_get_temp(struct armada_thermal_softc *sc, u_long *temp)
25723e46357SWojciech Macek {
25823e46357SWojciech Macek const armada_tdata_t *tdata;
25923e46357SWojciech Macek uint32_t reg;
26023e46357SWojciech Macek u_long tmp;
26123e46357SWojciech Macek u_long m, b, div;
26223e46357SWojciech Macek
26323e46357SWojciech Macek tdata = sc->tdata;
26423e46357SWojciech Macek /* Check if the readout is valid */
26523e46357SWojciech Macek if ((tdata->is_valid != NULL) && !tdata->is_valid(sc))
26623e46357SWojciech Macek return (EIO);
26723e46357SWojciech Macek
26823e46357SWojciech Macek reg = bus_read_4(sc->stat_res, 0);
26923e46357SWojciech Macek reg = (reg >> tdata->temp_shift) & tdata->temp_mask;
27023e46357SWojciech Macek
27123e46357SWojciech Macek /* Get formula coefficients */
27223e46357SWojciech Macek b = tdata->coef_b;
27323e46357SWojciech Macek m = tdata->coef_m;
27423e46357SWojciech Macek div = tdata->coef_div;
27523e46357SWojciech Macek
27623e46357SWojciech Macek if (tdata->inverted)
27723e46357SWojciech Macek tmp = ((m * reg) - b) / div;
27823e46357SWojciech Macek else
27923e46357SWojciech Macek tmp = (b - (m * reg)) / div;
28023e46357SWojciech Macek
28123e46357SWojciech Macek *temp = READOUT_TO_C(tmp);
28223e46357SWojciech Macek
28323e46357SWojciech Macek return (0);
28423e46357SWojciech Macek }
28523e46357SWojciech Macek
28623e46357SWojciech Macek static void
armada380_tsen_init(struct armada_thermal_softc * sc)28723e46357SWojciech Macek armada380_tsen_init(struct armada_thermal_softc *sc)
28823e46357SWojciech Macek {
28923e46357SWojciech Macek uint32_t tsen_ctrl;
29023e46357SWojciech Macek
29123e46357SWojciech Macek tsen_ctrl = bus_read_4(sc->ctrl_res, 0);
29223e46357SWojciech Macek if ((tsen_ctrl & A380_TSEN_CTRL_RESET) == 0) {
29323e46357SWojciech Macek tsen_ctrl |= A380_TSEN_CTRL_RESET;
29423e46357SWojciech Macek bus_write_4(sc->ctrl_res, 0, tsen_ctrl);
29523e46357SWojciech Macek DELAY(10000);
29623e46357SWojciech Macek }
29723e46357SWojciech Macek }
29823e46357SWojciech Macek
29923e46357SWojciech Macek static void
armada_temp_update(void * arg)30023e46357SWojciech Macek armada_temp_update(void *arg)
30123e46357SWojciech Macek {
30223e46357SWojciech Macek struct armada_thermal_softc *sc;
30323e46357SWojciech Macek
30423e46357SWojciech Macek sc = arg;
30523e46357SWojciech Macek /* Update temperature value, keel old if the readout is not valid */
30623e46357SWojciech Macek (void)armada_tsen_get_temp(sc, &sc->chip_temperature);
30723e46357SWojciech Macek
30823e46357SWojciech Macek callout_reset(&sc->temp_upd, hz, armada_temp_update, sc);
30923e46357SWojciech Macek }
310