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