1296936Smmel/*-
2296936Smmel * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3296936Smmel * All rights reserved.
4296936Smmel *
5296936Smmel * Redistribution and use in source and binary forms, with or without
6296936Smmel * modification, are permitted provided that the following conditions
7296936Smmel * are met:
8296936Smmel * 1. Redistributions of source code must retain the above copyright
9296936Smmel *    notice, this list of conditions and the following disclaimer.
10296936Smmel * 2. Redistributions in binary form must reproduce the above copyright
11296936Smmel *    notice, this list of conditions and the following disclaimer in the
12296936Smmel *    documentation and/or other materials provided with the distribution.
13296936Smmel *
14296936Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15296936Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16296936Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17296936Smmel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18296936Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19296936Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20296936Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21296936Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22296936Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23296936Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24296936Smmel * SUCH DAMAGE.
25296936Smmel */
26296936Smmel
27296936Smmel#include <sys/cdefs.h>
28296936Smmel__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_coretemp.c 308374 2016-11-06 15:25:46Z mmel $");
29296936Smmel
30296936Smmel#include <sys/param.h>
31296936Smmel#include <sys/systm.h>
32296936Smmel#include <sys/bus.h>
33296936Smmel#include <sys/cpu.h>
34296936Smmel#include <sys/kernel.h>
35296936Smmel#include <sys/lock.h>
36296936Smmel#include <sys/malloc.h>
37296936Smmel#include <sys/module.h>
38296936Smmel#include <sys/sysctl.h>
39296936Smmel
40296936Smmel#include <machine/bus.h>
41296936Smmel#include <machine/cpu.h>
42296936Smmel
43296936Smmel#include <dev/extres/clk/clk.h>
44296936Smmel#include <dev/ofw/ofw_bus_subr.h>
45296936Smmel
46296936Smmel#include "tegra_soctherm_if.h"
47296936Smmel
48296936Smmel
49296936Smmelenum therm_info {
50296936Smmel	CORETEMP_TEMP,
51296936Smmel	CORETEMP_DELTA,
52296936Smmel	CORETEMP_RESOLUTION,
53296936Smmel	CORETEMP_TJMAX,
54296936Smmel};
55296936Smmel
56296936Smmelstruct tegra124_coretemp_softc {
57296936Smmel	device_t		dev;
58296936Smmel	int			overheat_log;
59296936Smmel	int			core_max_temp;
60296936Smmel	int			cpu_id;
61296936Smmel	device_t		tsens_dev;
62296936Smmel	intptr_t		tsens_id;
63296936Smmel};
64296936Smmel
65296936Smmelstatic int
66296936Smmelcoretemp_get_val_sysctl(SYSCTL_HANDLER_ARGS)
67296936Smmel{
68296936Smmel	device_t dev;
69296936Smmel	int val, temp, rv;
70296936Smmel	struct tegra124_coretemp_softc *sc;
71296936Smmel	enum therm_info type;
72296936Smmel	char stemp[16];
73296936Smmel
74296936Smmel
75296936Smmel	dev = (device_t) arg1;
76296936Smmel	sc = device_get_softc(dev);
77296936Smmel	type = arg2;
78296936Smmel
79296936Smmel
80296936Smmel	rv = TEGRA_SOCTHERM_GET_TEMPERATURE(sc->tsens_dev, sc->dev,
81296936Smmel	     sc->tsens_id, &temp);
82296936Smmel	if (rv != 0) {
83296936Smmel		device_printf(sc->dev,
84296936Smmel		    "Cannot read temperature sensor %d:  %d\n",
85296936Smmel		    sc->tsens_id, rv);
86296936Smmel		return (rv);
87296936Smmel	}
88296936Smmel
89296936Smmel	switch (type) {
90296936Smmel	case CORETEMP_TEMP:
91296936Smmel		val = temp / 100;
92296936Smmel		val +=  2731;
93296936Smmel		break;
94296936Smmel	case CORETEMP_DELTA:
95296936Smmel		val = (sc->core_max_temp - temp) / 1000;
96296936Smmel		break;
97296936Smmel	case CORETEMP_RESOLUTION:
98296936Smmel		val = 1;
99296936Smmel		break;
100296936Smmel	case CORETEMP_TJMAX:
101296936Smmel		val = sc->core_max_temp / 100;
102296936Smmel		val +=  2731;
103296936Smmel		break;
104296936Smmel	}
105296936Smmel
106296936Smmel
107296936Smmel	if ((temp > sc->core_max_temp)  && !sc->overheat_log) {
108296936Smmel		sc->overheat_log = 1;
109296936Smmel
110296936Smmel		/*
111296936Smmel		 * Check for Critical Temperature Status and Critical
112296936Smmel		 * Temperature Log.  It doesn't really matter if the
113296936Smmel		 * current temperature is invalid because the "Critical
114296936Smmel		 * Temperature Log" bit will tell us if the Critical
115296936Smmel		 * Temperature has * been reached in past. It's not
116296936Smmel		 * directly related to the current temperature.
117296936Smmel		 *
118296936Smmel		 * If we reach a critical level, allow devctl(4)
119296936Smmel		 * to catch this and shutdown the system.
120296936Smmel		 */
121296936Smmel		device_printf(dev, "critical temperature detected, "
122296936Smmel		    "suggest system shutdown\n");
123296936Smmel		snprintf(stemp, sizeof(stemp), "%d", val);
124296936Smmel		devctl_notify("coretemp", "Thermal", stemp,
125296936Smmel		    "notify=0xcc");
126296936Smmel	} else {
127296936Smmel		sc->overheat_log = 0;
128296936Smmel	}
129296936Smmel
130296936Smmel	return (sysctl_handle_int(oidp, 0, val, req));
131296936Smmel}
132296936Smmel
133296936Smmelstatic int
134296936Smmeltegra124_coretemp_ofw_parse(struct tegra124_coretemp_softc *sc)
135296936Smmel{
136296936Smmel	int rv, ncells;
137296936Smmel	phandle_t node, xnode;
138296936Smmel	pcell_t *cells;
139296936Smmel
140296936Smmel	node = OF_peer(0);
141296936Smmel	node = ofw_bus_find_child(node, "thermal-zones");
142296936Smmel	if (node <= 0) {
143296936Smmel		device_printf(sc->dev, "Cannot find 'thermal-zones'.\n");
144296936Smmel		return (ENXIO);
145296936Smmel	}
146296936Smmel
147296936Smmel	node = ofw_bus_find_child(node, "cpu");
148296936Smmel	if (node <= 0) {
149296936Smmel		device_printf(sc->dev, "Cannot find 'cpu'\n");
150296936Smmel		return (ENXIO);
151296936Smmel	}
152296936Smmel	rv = ofw_bus_parse_xref_list_alloc(node, "thermal-sensors",
153296936Smmel	    "#thermal-sensor-cells", 0, &xnode, &ncells, &cells);
154296936Smmel	if (rv != 0) {
155296936Smmel		device_printf(sc->dev,
156296936Smmel		    "Cannot parse 'thermal-sensors' property.\n");
157296936Smmel		return (ENXIO);
158296936Smmel	}
159296936Smmel	if (ncells != 1) {
160296936Smmel		device_printf(sc->dev,
161296936Smmel		    "Invalid format of 'thermal-sensors' property(%d).\n",
162296936Smmel		    ncells);
163296936Smmel		return (ENXIO);
164296936Smmel	}
165296936Smmel
166296936Smmel	sc->tsens_id = 0x100 + sc->cpu_id; //cells[0];
167299715Sgonzo	OF_prop_free(cells);
168296936Smmel
169296936Smmel	sc->tsens_dev = OF_device_from_xref(xnode);
170296936Smmel	if (sc->tsens_dev == NULL) {
171296936Smmel		device_printf(sc->dev,
172296936Smmel		    "Cannot find thermal sensors device.");
173296936Smmel		return (ENXIO);
174296936Smmel	}
175296936Smmel	return (0);
176296936Smmel}
177296936Smmel
178296936Smmelstatic void
179296936Smmeltegra124_coretemp_identify(driver_t *driver, device_t parent)
180296936Smmel{
181308374Smmel	phandle_t root;
182296936Smmel
183308374Smmel	root = OF_finddevice("/");
184308374Smmel	if (!ofw_bus_node_is_compatible(root, "nvidia,tegra124"))
185308374Smmel		return;
186296936Smmel	if (device_find_child(parent, "tegra124_coretemp", -1) != NULL)
187296936Smmel		return;
188296936Smmel	if (BUS_ADD_CHILD(parent, 0, "tegra124_coretemp", -1) == NULL)
189296936Smmel		device_printf(parent, "add child failed\n");
190296936Smmel}
191296936Smmel
192296936Smmelstatic int
193296936Smmeltegra124_coretemp_probe(device_t dev)
194296936Smmel{
195296936Smmel
196308374Smmel	device_set_desc(dev, "CPU Thermal Sensor");
197296936Smmel	return (0);
198296936Smmel}
199296936Smmel
200296936Smmelstatic int
201296936Smmeltegra124_coretemp_attach(device_t dev)
202296936Smmel{
203296936Smmel	struct tegra124_coretemp_softc *sc;
204296936Smmel	device_t pdev;
205296936Smmel	struct sysctl_oid *oid;
206296936Smmel	struct sysctl_ctx_list *ctx;
207296936Smmel	int rv;
208296936Smmel
209296936Smmel	sc = device_get_softc(dev);
210296936Smmel	sc->dev = dev;
211296936Smmel	sc->cpu_id = device_get_unit(dev);
212296936Smmel	sc->core_max_temp = 102000;
213296936Smmel	pdev = device_get_parent(dev);
214296936Smmel
215296936Smmel	rv = tegra124_coretemp_ofw_parse(sc);
216296936Smmel	if (rv != 0)
217296936Smmel		return (rv);
218296936Smmel
219296936Smmel	ctx = device_get_sysctl_ctx(dev);
220296936Smmel
221296936Smmel	oid = SYSCTL_ADD_NODE(ctx,
222296936Smmel	    SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), OID_AUTO,
223296936Smmel	    "coretemp", CTLFLAG_RD, NULL, "Per-CPU thermal information");
224296936Smmel
225296936Smmel	/*
226296936Smmel	 * Add the MIBs to dev.cpu.N and dev.cpu.N.coretemp.
227296936Smmel	 */
228296936Smmel	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)),
229296936Smmel	    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
230296936Smmel	    dev, CORETEMP_TEMP, coretemp_get_val_sysctl, "IK",
231296936Smmel	    "Current temperature");
232296936Smmel	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "delta",
233296936Smmel	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_DELTA,
234296936Smmel	    coretemp_get_val_sysctl, "I",
235296936Smmel	    "Delta between TCC activation and current temperature");
236296936Smmel	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "resolution",
237296936Smmel	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_RESOLUTION,
238296936Smmel	    coretemp_get_val_sysctl, "I",
239296936Smmel	    "Resolution of CPU thermal sensor");
240296936Smmel	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "tjmax",
241296936Smmel	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_TJMAX,
242296936Smmel	    coretemp_get_val_sysctl, "IK",
243296936Smmel	    "TCC activation temperature");
244296936Smmel
245296936Smmel	return (0);
246296936Smmel}
247296936Smmel
248296936Smmelstatic int
249296936Smmeltegra124_coretemp_detach(device_t dev)
250296936Smmel{
251296936Smmel	struct tegra124_coretemp_softc *sc;
252296936Smmel
253296936Smmel	sc = device_get_softc(dev);
254296936Smmel	return (0);
255296936Smmel}
256296936Smmel
257296936Smmelstatic device_method_t tegra124_coretemp_methods[] = {
258296936Smmel	/* Device interface */
259296936Smmel	DEVMETHOD(device_identify,	tegra124_coretemp_identify),
260296936Smmel	DEVMETHOD(device_probe,		tegra124_coretemp_probe),
261296936Smmel	DEVMETHOD(device_attach,	tegra124_coretemp_attach),
262296936Smmel	DEVMETHOD(device_detach,	tegra124_coretemp_detach),
263296936Smmel
264296936Smmel
265296936Smmel	DEVMETHOD_END
266296936Smmel};
267296936Smmel
268296936Smmelstatic devclass_t tegra124_coretemp_devclass;
269308374Smmelstatic DEFINE_CLASS_0(tegra124_coretemp, tegra124_coretemp_driver,
270308335Smmel    tegra124_coretemp_methods, sizeof(struct tegra124_coretemp_softc));
271296936SmmelDRIVER_MODULE(tegra124_coretemp, cpu, tegra124_coretemp_driver,
272308335Smmel    tegra124_coretemp_devclass, NULL, NULL);
273