ef2ee5d07a
The following pheripherals are supported: UART, MMC, AHCI, EHCI, PCIe, I2C, PMIC, GPIO, CPU temperature and clock. Note: The PCIe driver is pure mash at this moment. It will be reworked immediately when both D5237 and D2579 enter the current tree.
697 lines
19 KiB
C
697 lines
19 KiB
C
/*-
|
|
* 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);
|