xref: /freebsd/sys/arm/mv/armada/thermal.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
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