summaryrefslogtreecommitdiffstats
path: root/sys/arm/nvidia/tegra_soctherm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm/nvidia/tegra_soctherm.c')
-rw-r--r--sys/arm/nvidia/tegra_soctherm.c696
1 files changed, 696 insertions, 0 deletions
diff --git a/sys/arm/nvidia/tegra_soctherm.c b/sys/arm/nvidia/tegra_soctherm.c
new file mode 100644
index 0000000..3102173
--- /dev/null
+++ b/sys/arm/nvidia/tegra_soctherm.c
@@ -0,0 +1,696 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Thermometer and thermal zones driver for Tegra SoCs.
+ * Calibration data and algo are taken from Linux, because this part of SoC
+ * is undocumented in TRM.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <arm/nvidia/tegra_efuse.h>
+#include <gnu/dts/include/dt-bindings/thermal/tegra124-soctherm.h>
+#include "tegra_soctherm_if.h"
+
+/* Per sensors registers - base is 0x0c0*/
+#define TSENSOR_CONFIG0 0x000
+#define TSENSOR_CONFIG0_TALL(x) (((x) & 0xFFFFF) << 8)
+#define TSENSOR_CONFIG0_STATUS_CLR (1 << 5)
+#define TSENSOR_CONFIG0_TCALC_OVERFLOW (1 << 4)
+#define TSENSOR_CONFIG0_OVERFLOW (1 << 3)
+#define TSENSOR_CONFIG0_CPTR_OVERFLOW (1 << 2)
+#define TSENSOR_CONFIG0_RO_SEL (1 << 1)
+#define TSENSOR_CONFIG0_STOP (1 << 0)
+
+#define TSENSOR_CONFIG1 0x004
+#define TSENSOR_CONFIG1_TEMP_ENABLE (1U << 31)
+#define TSENSOR_CONFIG1_TEN_COUNT(x) (((x) & 0x3F) << 24)
+#define TSENSOR_CONFIG1_TIDDQ_EN(x) (((x) & 0x3F) << 15)
+#define TSENSOR_CONFIG1_TSAMPLE(x) (((x) & 0x3FF) << 0)
+
+#define TSENSOR_CONFIG2 0x008
+#define TSENSOR_CONFIG2_THERMA(x) (((x) & 0xFFFF) << 16)
+#define TSENSOR_CONFIG2_THERMB(x) (((x) & 0xFFFF) << 0)
+
+#define TSENSOR_STATUS0 0x00c
+#define TSENSOR_STATUS0_CAPTURE_VALID (1U << 31)
+#define TSENSOR_STATUS0_CAPTURE(x) (((x) >> 0) & 0xffff)
+
+#define TSENSOR_STATUS1 0x010
+#define TSENSOR_STATUS1_TEMP_VALID (1U << 31)
+#define TSENSOR_STATUS1_TEMP(x) (((x) >> 0) & 0xffff)
+
+#define TSENSOR_STATUS2 0x014
+#define TSENSOR_STATUS2_TEMP_MAX(x) (((x) >> 16) & 0xffff)
+#define TSENSOR_STATUS2_TEMP_MIN(x) (((x) >> 0) & 0xffff)
+
+/* Global registers */
+#define TSENSOR_PDIV 0x1c0
+#define TSENSOR_PDIV_T124 0x8888
+#define TSENSOR_HOTSPOT_OFF 0x1c4
+#define TSENSOR_HOTSPOT_OFF_T124 0x00060600
+#define TSENSOR_TEMP1 0x1c8
+#define TSENSOR_TEMP2 0x1cc
+
+/* Readbacks */
+#define READBACK_VALUE_MASK 0xff00
+#define READBACK_VALUE_SHIFT 8
+#define READBACK_ADD_HALF (1 << 7)
+#define READBACK_NEGATE (1 << 0)
+
+
+/* Fuses */
+#define FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT 0
+#define FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS 13
+#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13
+#define FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS 13
+
+#define FUSE_TSENSOR8_CALIB 0x180
+#define FUSE_TSENSOR8_CALIB_CP_TS_BASE(x) (((x) >> 0) & 0x3ff)
+#define FUSE_TSENSOR8_CALIB_FT_TS_BASE(x) (((x) >> 10) & 0x7ff)
+
+#define FUSE_SPARE_REALIGNMENT_REG 0x1fc
+#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT 0
+#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS 6
+#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT 21
+#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_BITS 5
+#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP(x) (((x) >> 0) & 0x3f)
+#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT(x) (((x) >> 21) & 0x1f)
+
+
+#define NOMINAL_CALIB_FT_T124 105
+#define NOMINAL_CALIB_CP_T124 25
+
+#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v))
+#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
+
+static struct sysctl_ctx_list soctherm_sysctl_ctx;
+
+struct soctherm_shared_cal {
+ uint32_t base_cp;
+ uint32_t base_ft;
+ int32_t actual_temp_cp;
+ int32_t actual_temp_ft;
+};
+struct tsensor_cfg {
+ uint32_t tall;
+ uint32_t tsample;
+ uint32_t tiddq_en;
+ uint32_t ten_count;
+ uint32_t pdiv;
+ uint32_t tsample_ate;
+ uint32_t pdiv_ate;
+};
+
+struct tsensor {
+ char *name;
+ int id;
+ struct tsensor_cfg *cfg;
+ bus_addr_t sensor_base;
+ bus_addr_t calib_fuse;
+ int fuse_corr_alpha;
+ int fuse_corr_beta;
+
+ int16_t therm_a;
+ int16_t therm_b;
+};
+
+struct soctherm_softc {
+ device_t dev;
+ struct resource *mem_res;
+ struct resource *irq_res;
+ void *irq_ih;
+
+ clk_t tsensor_clk;
+ clk_t soctherm_clk;
+ hwreset_t reset;
+
+ int ntsensors;
+ struct tsensor *tsensors;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra124-soctherm", 1},
+ {NULL, 0},
+};
+
+static struct tsensor_cfg t124_tsensor_config = {
+ .tall = 16300,
+ .tsample = 120,
+ .tiddq_en = 1,
+ .ten_count = 1,
+ .pdiv = 8,
+ .tsample_ate = 480,
+ .pdiv_ate = 8
+};
+
+
+static struct tsensor t124_tsensors[] = {
+ {
+ .name = "cpu0",
+ .id = TEGRA124_SOCTHERM_SENSOR_CPU,
+ .cfg = &t124_tsensor_config,
+ .sensor_base = 0x0c0,
+ .calib_fuse = 0x098,
+ .fuse_corr_alpha = 1135400,
+ .fuse_corr_beta = -6266900,
+ },
+ {
+ .name = "cpu1",
+ .id = -1,
+ .cfg = &t124_tsensor_config,
+ .sensor_base = 0x0e0,
+ .calib_fuse = 0x084,
+ .fuse_corr_alpha = 1122220,
+ .fuse_corr_beta = -5700700,
+ },
+ {
+ .name = "cpu2",
+ .id = -1,
+ .cfg = &t124_tsensor_config,
+ .sensor_base = 0x100,
+ .calib_fuse = 0x088,
+ .fuse_corr_alpha = 1127000,
+ .fuse_corr_beta = -6768200,
+ },
+ {
+ .name = "cpu3",
+ .id = -1,
+ .cfg = &t124_tsensor_config,
+ .sensor_base = 0x120,
+ .calib_fuse = 0x12c,
+ .fuse_corr_alpha = 1110900,
+ .fuse_corr_beta = -6232000,
+ },
+ {
+ .name = "mem0",
+ .id = TEGRA124_SOCTHERM_SENSOR_MEM,
+ .cfg = &t124_tsensor_config,
+ .sensor_base = 0x140,
+ .calib_fuse = 0x158,
+ .fuse_corr_alpha = 1122300,
+ .fuse_corr_beta = -5936400,
+ },
+ {
+ .name = "mem1",
+ .id = -1,
+ .cfg = &t124_tsensor_config,
+ .sensor_base = 0x160,
+ .calib_fuse = 0x15c,
+ .fuse_corr_alpha = 1145700,
+ .fuse_corr_beta = -7124600,
+ },
+ {
+ .name = "gpu",
+ .id = TEGRA124_SOCTHERM_SENSOR_GPU,
+ .cfg = &t124_tsensor_config,
+ .sensor_base = 0x180,
+ .calib_fuse = 0x154,
+ .fuse_corr_alpha = 1120100,
+ .fuse_corr_beta = -6000500,
+ },
+ {
+ .name = "pllX",
+ .id = TEGRA124_SOCTHERM_SENSOR_PLLX,
+ .cfg = &t124_tsensor_config,
+ .sensor_base = 0x1a0,
+ .calib_fuse = 0x160,
+ .fuse_corr_alpha = 1106500,
+ .fuse_corr_beta = -6729300,
+ },
+};
+
+/* Extract signed integer bitfield from register */
+static int
+extract_signed(uint32_t reg, int shift, int bits)
+{
+ int32_t val;
+ uint32_t mask;
+
+ mask = (1 << bits) - 1;
+ val = ((reg >> shift) & mask) << (32 - bits);
+ val >>= 32 - bits;
+ return ((int32_t)val);
+}
+
+static inline int64_t div64_s64_precise(int64_t a, int64_t b)
+{
+ int64_t r, al;
+
+ al = a << 16;
+ r = (al * 2 + 1) / (2 * b);
+ return r >> 16;
+}
+
+static void
+get_shared_cal(struct soctherm_softc *sc, struct soctherm_shared_cal *cal)
+{
+ uint32_t val;
+ int calib_cp, calib_ft;
+
+ val = tegra_fuse_read_4(FUSE_TSENSOR8_CALIB);
+ cal->base_cp = FUSE_TSENSOR8_CALIB_CP_TS_BASE(val);
+ cal->base_ft = FUSE_TSENSOR8_CALIB_FT_TS_BASE(val);
+
+ val = tegra_fuse_read_4(FUSE_SPARE_REALIGNMENT_REG);
+ calib_ft = extract_signed(val,
+ FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT,
+ FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_BITS);
+ calib_cp = extract_signed(val,
+ FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT,
+ FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS);
+
+ cal->actual_temp_cp = 2 * NOMINAL_CALIB_CP_T124 + calib_cp;
+ cal->actual_temp_ft = 2 * NOMINAL_CALIB_FT_T124 + calib_ft;
+#ifdef DEBUG
+ printf("%s: base_cp: %u, base_ft: %d,"
+ " actual_temp_cp: %d, actual_temp_ft: %d\n",
+ __func__, cal->base_cp, cal->base_ft,
+ cal->actual_temp_cp, cal->actual_temp_ft);
+#endif
+}
+
+
+static void
+tsensor_calibration(struct tsensor *sensor, struct soctherm_shared_cal *shared)
+{
+ uint32_t val;
+ int mult, div, calib_cp, calib_ft;
+ int actual_tsensor_ft, actual_tsensor_cp, delta_sens, delta_temp;
+ int temp_a, temp_b;
+ int64_t tmp;
+
+ val = tegra_fuse_read_4(sensor->calib_fuse);
+ calib_cp = extract_signed(val,
+ FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT,
+ FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS);
+ actual_tsensor_cp = shared->base_cp * 64 + calib_cp;
+
+ calib_ft = extract_signed(val,
+ FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT,
+ FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS);
+ actual_tsensor_ft = shared->base_ft * 32 + calib_ft;
+
+ delta_sens = actual_tsensor_ft - actual_tsensor_cp;
+ delta_temp = shared->actual_temp_ft - shared->actual_temp_cp;
+ mult = sensor->cfg->pdiv * sensor->cfg->tsample_ate;
+ div = sensor->cfg->tsample * sensor->cfg->pdiv_ate;
+
+
+ temp_a = div64_s64_precise((int64_t) delta_temp * (1LL << 13) * mult,
+ (int64_t) delta_sens * div);
+
+ tmp = (int64_t)actual_tsensor_ft * shared->actual_temp_cp -
+ (int64_t)actual_tsensor_cp * shared->actual_temp_ft;
+ temp_b = div64_s64_precise(tmp, (int64_t)delta_sens);
+
+ temp_a = div64_s64_precise((int64_t)temp_a * sensor->fuse_corr_alpha,
+ 1000000);
+ temp_b = div64_s64_precise((int64_t)temp_b * sensor->fuse_corr_alpha +
+ sensor->fuse_corr_beta, 1000000);
+ sensor->therm_a = (int16_t)temp_a;
+ sensor->therm_b = (int16_t)temp_b;
+#ifdef DEBUG
+ printf("%s: sensor %s fuse: 0x%08X (0x%04X, 0x%04X)"
+ " calib_cp: %d(0x%04X), calib_ft: %d(0x%04X)\n",
+ __func__, sensor->name, val, val & 0x1FFF, (val >> 13) & 0x1FFF,
+ calib_cp, calib_cp, calib_ft, calib_ft);
+ printf("therma: 0x%04X(%d), thermb: 0x%04X(%d)\n",
+ (uint16_t)sensor->therm_a, temp_a,
+ (uint16_t)sensor->therm_b, sensor->therm_b);
+#endif
+}
+
+static void
+soctherm_init_tsensor(struct soctherm_softc *sc, struct tsensor *sensor,
+ struct soctherm_shared_cal *shared_cal)
+{
+ uint32_t val;
+
+ tsensor_calibration(sensor, shared_cal);
+
+ val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0);
+ val |= TSENSOR_CONFIG0_STOP;
+ val |= TSENSOR_CONFIG0_STATUS_CLR;
+ WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val);
+
+ val = TSENSOR_CONFIG0_TALL(sensor->cfg->tall);
+ val |= TSENSOR_CONFIG0_STOP;
+ WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val);
+
+ val = TSENSOR_CONFIG1_TSAMPLE(sensor->cfg->tsample - 1);
+ val |= TSENSOR_CONFIG1_TIDDQ_EN(sensor->cfg->tiddq_en);
+ val |= TSENSOR_CONFIG1_TEN_COUNT(sensor->cfg->ten_count);
+ val |= TSENSOR_CONFIG1_TEMP_ENABLE;
+ WR4(sc, sensor->sensor_base + TSENSOR_CONFIG1, val);
+
+ val = TSENSOR_CONFIG2_THERMA((uint16_t)sensor->therm_a) |
+ TSENSOR_CONFIG2_THERMB((uint16_t)sensor->therm_b);
+ WR4(sc, sensor->sensor_base + TSENSOR_CONFIG2, val);
+
+ val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0);
+ val &= ~TSENSOR_CONFIG0_STOP;
+ WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val);
+#ifdef DEBUG
+ printf(" Sensor: %s cfg:0x%08X, 0x%08X, 0x%08X,"
+ " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name,
+ RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0),
+ RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1),
+ RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2),
+ RD4(sc, sensor->sensor_base + TSENSOR_STATUS0),
+ RD4(sc, sensor->sensor_base + TSENSOR_STATUS1),
+ RD4(sc, sensor->sensor_base + TSENSOR_STATUS2)
+ );
+#endif
+}
+
+static int
+soctherm_convert_raw(uint32_t val)
+{
+ int32_t t;
+
+ t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000;
+ if (val & READBACK_ADD_HALF)
+ t += 500;
+ if (val & READBACK_NEGATE)
+ t *= -1;
+
+ return t;
+}
+
+static int
+soctherm_read_temp(struct soctherm_softc *sc, struct tsensor *sensor, int *temp)
+{
+ int timeout;
+ uint32_t val;
+
+
+ /* wait for valid sample */
+ for (timeout = 1000; timeout > 0; timeout--) {
+ val = RD4(sc, sensor->sensor_base + TSENSOR_STATUS1);
+ if ((val & TSENSOR_STATUS1_TEMP_VALID) != 0)
+ break;
+ DELAY(100);
+ }
+ if (timeout <= 0)
+ device_printf(sc->dev, "Sensor %s timeouted\n", sensor->name);
+ *temp = soctherm_convert_raw(val);
+#ifdef DEBUG
+ printf("%s: Raw: 0x%08X, temp: %d\n", __func__, val, *temp);
+ printf(" Sensor: %s cfg:0x%08X, 0x%08X, 0x%08X,"
+ " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name,
+ RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0),
+ RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1),
+ RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2),
+ RD4(sc, sensor->sensor_base + TSENSOR_STATUS0),
+ RD4(sc, sensor->sensor_base + TSENSOR_STATUS1),
+ RD4(sc, sensor->sensor_base + TSENSOR_STATUS2)
+ );
+#endif
+ return 0;
+}
+
+static int
+soctherm_get_temp(device_t dev, device_t cdev, uintptr_t id, int *val)
+{
+ struct soctherm_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ /* The direct sensor map starts at 0x100 */
+ if (id >= 0x100) {
+ id -= 0x100;
+ if (id >= sc->ntsensors)
+ return (ERANGE);
+ return(soctherm_read_temp(sc, sc->tsensors + id, val));
+ }
+ /* Linux (DT) compatible thermal zones */
+ for (i = 0; i < sc->ntsensors; i++) {
+ if (sc->tsensors->id == id)
+ return(soctherm_read_temp(sc, sc->tsensors + id, val));
+ }
+ return (ERANGE);
+}
+
+static int
+soctherm_sysctl_temperature(SYSCTL_HANDLER_ARGS)
+{
+ struct soctherm_softc *sc;
+ int val;
+ int rv;
+ int id;
+
+ /* Write request */
+ if (req->newptr != NULL)
+ return (EINVAL);
+
+ sc = arg1;
+ id = arg2;
+
+ if (id >= sc->ntsensors)
+ return (ERANGE);
+ rv = soctherm_read_temp(sc, sc->tsensors + id, &val);
+ if (rv != 0)
+ return (rv);
+
+ val = val / 100;
+ val += 2731;
+ rv = sysctl_handle_int(oidp, &val, 0, req);
+ return (rv);
+}
+
+static int
+soctherm_init_sysctl(struct soctherm_softc *sc)
+{
+ int i;
+ struct sysctl_oid *oid, *tmp;
+
+ sysctl_ctx_init(&soctherm_sysctl_ctx);
+ /* create node for hw.temp */
+ oid = SYSCTL_ADD_NODE(&soctherm_sysctl_ctx,
+ SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "temperature",
+ CTLFLAG_RD, NULL, "");
+ if (oid == NULL)
+ return (ENXIO);
+
+ /* Add sensors */
+ for (i = sc->ntsensors - 1; i >= 0; i--) {
+ tmp = SYSCTL_ADD_PROC(&soctherm_sysctl_ctx,
+ SYSCTL_CHILDREN(oid), OID_AUTO, sc->tsensors[i].name,
+ CTLTYPE_INT | CTLFLAG_RD, sc, i,
+ soctherm_sysctl_temperature, "IK", "SoC Temperature");
+ if (tmp == NULL)
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+soctherm_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Tegra temperature sensors");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+soctherm_attach(device_t dev)
+{
+ struct soctherm_softc *sc;
+ phandle_t node;
+ int i, rid, rv;
+ struct soctherm_shared_cal shared_calib;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(sc->dev);
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ goto fail;
+ }
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev, "Cannot allocate IRQ resources\n");
+ goto fail;
+ }
+
+/*
+ if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC,
+ soctherm_intr, NULL, sc, &sc->irq_ih))) {
+ device_printf(dev,
+ "WARNING: unable to register interrupt handler\n");
+ goto fail;
+ }
+*/
+
+ /* OWF resources */
+ rv = hwreset_get_by_ofw_name(dev, "soctherm", &sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get fuse reset\n");
+ goto fail;
+ }
+ rv = clk_get_by_ofw_name(dev, "tsensor", &sc->tsensor_clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get 'tsensor' clock: %d\n", rv);
+ goto fail;
+ }
+ rv = clk_get_by_ofw_name(dev, "soctherm", &sc->soctherm_clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get 'soctherm' clock: %d\n", rv);
+ goto fail;
+ }
+
+ rv = hwreset_assert(sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot assert reset\n");
+ goto fail;
+ }
+ rv = clk_enable(sc->tsensor_clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable 'tsensor' clock: %d\n", rv);
+ goto fail;
+ }
+ rv = clk_enable(sc->soctherm_clk);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable 'soctherm' clock: %d\n", rv);
+ goto fail;
+ }
+ rv = hwreset_deassert(sc->reset);
+ if (rv != 0) {
+ device_printf(dev, "Cannot clear reset\n");
+ goto fail;
+ }
+
+ /* Tegra 124 */
+ sc->tsensors = t124_tsensors;
+ sc->ntsensors = nitems(t124_tsensors);
+ get_shared_cal(sc, &shared_calib);
+
+ WR4(sc, TSENSOR_PDIV, TSENSOR_PDIV_T124);
+ WR4(sc, TSENSOR_HOTSPOT_OFF, TSENSOR_HOTSPOT_OFF_T124);
+
+ for (i = 0; i < sc->ntsensors; i++)
+ soctherm_init_tsensor(sc, sc->tsensors + i, &shared_calib);
+
+ rv = soctherm_init_sysctl(sc);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot initialize sysctls\n");
+ goto fail;
+ }
+
+ OF_device_register_xref(OF_xref_from_node(node), dev);
+ return (bus_generic_attach(dev));
+
+fail:
+ if (sc->irq_ih != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+ sysctl_ctx_free(&soctherm_sysctl_ctx);
+ if (sc->tsensor_clk != NULL)
+ clk_release(sc->tsensor_clk);
+ if (sc->soctherm_clk != NULL)
+ clk_release(sc->soctherm_clk);
+ if (sc->reset != NULL)
+ hwreset_release(sc->reset);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (ENXIO);
+}
+
+static int
+soctherm_detach(device_t dev)
+{
+ struct soctherm_softc *sc;
+ sc = device_get_softc(dev);
+
+ if (sc->irq_ih != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
+ sysctl_ctx_free(&soctherm_sysctl_ctx);
+ if (sc->tsensor_clk != NULL)
+ clk_release(sc->tsensor_clk);
+ if (sc->soctherm_clk != NULL)
+ clk_release(sc->soctherm_clk);
+ if (sc->reset != NULL)
+ hwreset_release(sc->reset);
+ if (sc->irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
+
+ return (ENXIO);
+}
+
+static device_method_t tegra_soctherm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, soctherm_probe),
+ DEVMETHOD(device_attach, soctherm_attach),
+ DEVMETHOD(device_detach, soctherm_detach),
+
+ /* SOCTHERM interface */
+ DEVMETHOD(tegra_soctherm_get_temperature, soctherm_get_temp),
+
+ DEVMETHOD_END
+};
+
+static devclass_t tegra_soctherm_devclass;
+DEFINE_CLASS_0(tegra_soctherm, tegra_soctherm_driver, tegra_soctherm_methods,
+ sizeof(struct soctherm_softc));
+EARLY_DRIVER_MODULE(tegra_soctherm, simplebus, tegra_soctherm_driver,
+tegra_soctherm_devclass, 0, 0, 79);
OpenPOWER on IntegriCloud