/*
- * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
- *
- * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
+ * Copyright (c) 2014-2016, Fuzhou Rockchip Electronics Co., Ltd
* Caesar Wang <wxt@rock-chips.com>
*
* This program is free software; you can redistribute it and/or modify it
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/thermal.h>
+#include <linux/mfd/syscon.h>
#include <linux/pinctrl/consumer.h>
/**
#define SOC_MAX_SENSORS 2
/**
- * struct chip_tsadc_table: hold information about chip-specific differences
+ * struct chip_tsadc_table - hold information about chip-specific differences
* @id: conversion table
* @length: size of conversion table
* @data_mask: mask to apply on data inputs
enum adc_sort_mode mode;
};
+/**
+ * struct rockchip_tsadc_chip - hold the private data of tsadc chip
+ * @chn_id[SOC_MAX_SENSORS]: the sensor id of chip correspond to the channel
+ * @chn_num: the channel number of tsadc chip
+ * @tshut_temp: the hardware-controlled shutdown temperature value
+ * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
+ * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
+ * @initialize: SoC special initialize tsadc controller method
+ * @irq_ack: clear the interrupt
+ * @get_temp: get the temperature
+ * @set_alarm_temp: set the high temperature interrupt
+ * @set_tshut_temp: set the hardware-controlled shutdown temperature
+ * @set_tshut_mode: set the hardware-controlled shutdown mode
+ * @table: the chip-specific conversion table
+ */
struct rockchip_tsadc_chip {
/* The sensor id of chip correspond to the ADC channel */
int chn_id[SOC_MAX_SENSORS];
enum tshut_polarity tshut_polarity;
/* Chip-wide methods */
- void (*initialize)(void __iomem *reg, enum tshut_polarity p);
+ void (*initialize)(struct regmap *grf,
+ void __iomem *reg, enum tshut_polarity p);
void (*irq_ack)(void __iomem *reg);
void (*control)(void __iomem *reg, bool on);
/* Per-sensor methods */
int (*get_temp)(struct chip_tsadc_table table,
int chn, void __iomem *reg, int *temp);
+ void (*set_alarm_temp)(struct chip_tsadc_table table,
+ int chn, void __iomem *reg, int temp);
void (*set_tshut_temp)(struct chip_tsadc_table table,
int chn, void __iomem *reg, int temp);
void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m);
struct chip_tsadc_table table;
};
+/**
+ * struct rockchip_thermal_sensor - hold the information of thermal sensor
+ * @thermal: pointer to the platform/configuration data
+ * @tzd: pointer to a thermal zone
+ * @id: identifier of the thermal sensor
+ */
struct rockchip_thermal_sensor {
struct rockchip_thermal_data *thermal;
struct thermal_zone_device *tzd;
int id;
};
+/**
+ * struct rockchip_thermal_data - hold the private data of thermal driver
+ * @chip: pointer to the platform/configuration data
+ * @pdev: platform device of thermal
+ * @reset: the reset controller of tsadc
+ * @sensors[SOC_MAX_SENSORS]: the thermal sensor
+ * @clk: the controller clock is divided by the exteral 24MHz
+ * @pclk: the advanced peripherals bus clock
+ * @grf: the general register file will be used to do static set by software
+ * @regs: the base address of tsadc controller
+ * @tshut_temp: the hardware-controlled shutdown temperature value
+ * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
+ * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
+ */
struct rockchip_thermal_data {
const struct rockchip_tsadc_chip *chip;
struct platform_device *pdev;
struct clk *clk;
struct clk *pclk;
+ struct regmap *grf;
void __iomem *regs;
int tshut_temp;
* TSADCV3_* are used for newer SoCs than RK3288. (e.g: RK3228, RK3399)
*
*/
+#define TSADCV2_USER_CON 0x00
#define TSADCV2_AUTO_CON 0x04
#define TSADCV2_INT_EN 0x08
#define TSADCV2_INT_PD 0x0c
#define TSADCV2_DATA(chn) (0x20 + (chn) * 0x04)
+#define TSADCV2_COMP_INT(chn) (0x30 + (chn) * 0x04)
#define TSADCV2_COMP_SHUT(chn) (0x40 + (chn) * 0x04)
#define TSADCV2_HIGHT_INT_DEBOUNCE 0x60
#define TSADCV2_HIGHT_TSHUT_DEBOUNCE 0x64
#define TSADCV2_AUTO_EN BIT(0)
#define TSADCV2_AUTO_SRC_EN(chn) BIT(4 + (chn))
#define TSADCV2_AUTO_TSHUT_POLARITY_HIGH BIT(8)
-/**
- * TSADCV1_AUTO_Q_SEL_EN:
- * whether select (1024 - tsadc_q) as output
- * 1'b0:use tsadc_q as output(temperature-code is rising sequence)
- * 1'b1:use(1024 - tsadc_q) as output (temperature-code is falling sequence)
- */
+
#define TSADCV3_AUTO_Q_SEL_EN BIT(1)
#define TSADCV2_INT_SRC_EN(chn) BIT(chn)
#define TSADCV2_HIGHT_INT_DEBOUNCE_COUNT 4
#define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT 4
-#define TSADCV2_AUTO_PERIOD_TIME 250 /* msec */
-#define TSADCV2_AUTO_PERIOD_HT_TIME 50 /* msec */
+#define TSADCV2_AUTO_PERIOD_TIME 250 /* 250ms */
+#define TSADCV2_AUTO_PERIOD_HT_TIME 50 /* 50ms */
+#define TSADCV3_AUTO_PERIOD_TIME 1875 /* 2.5ms */
+#define TSADCV3_AUTO_PERIOD_HT_TIME 1875 /* 2.5ms */
-struct tsadc_table {
- u32 code;
- int temp;
-};
+#define TSADCV2_USER_INTER_PD_SOC 0x340 /* 13 clocks */
+
+#define GRF_SARADC_TESTBIT 0x0e644
+#define GRF_TSADC_TESTBIT_L 0x0e648
+#define GRF_TSADC_TESTBIT_H 0x0e64c
+
+#define GRF_SARADC_TESTBIT_ON (0x10001 << 2)
+#define GRF_TSADC_TESTBIT_H_ON (0x10001 << 2)
+#define GRF_TSADC_VCM_EN_L (0x10001 << 7)
+#define GRF_TSADC_VCM_EN_H (0x10001 << 7)
/**
+ * struct tsadc_table - code to temperature conversion table
+ * @code: the value of adc channel
+ * @temp: the temperature
* Note:
- * Code to Temperature mapping of the Temperature sensor is a piece wise linear
+ * code to temperature mapping of the temperature sensor is a piece wise linear
* curve.Any temperature, code faling between to 2 give temperatures can be
* linearly interpolated.
- * Code to Temperature mapping should be updated based on sillcon results.
+ * Code to Temperature mapping should be updated based on manufacturer results.
*/
+struct tsadc_table {
+ u32 code;
+ int temp;
+};
+
static const struct tsadc_table rk3228_code_table[] = {
{0, -40000},
{588, -40000},
{3421, 125000},
};
+static const struct tsadc_table rk3328_code_table[] = {
+ {0, -40000},
+ {296, -40000},
+ {304, -35000},
+ {313, -30000},
+ {331, -20000},
+ {340, -15000},
+ {349, -10000},
+ {359, -5000},
+ {368, 0},
+ {378, 5000},
+ {388, 10000},
+ {398, 15000},
+ {408, 20000},
+ {418, 25000},
+ {429, 30000},
+ {440, 35000},
+ {451, 40000},
+ {462, 45000},
+ {473, 50000},
+ {485, 55000},
+ {496, 60000},
+ {508, 65000},
+ {521, 70000},
+ {533, 75000},
+ {546, 80000},
+ {559, 85000},
+ {572, 90000},
+ {586, 95000},
+ {600, 100000},
+ {614, 105000},
+ {629, 110000},
+ {644, 115000},
+ {659, 120000},
+ {675, 125000},
+ {TSADCV2_DATA_MASK, 125000},
+};
+
static const struct tsadc_table rk3368_code_table[] = {
{0, -40000},
{106, -40000},
static const struct tsadc_table rk3399_code_table[] = {
{0, -40000},
- {593, -40000},
- {598, -35000},
- {603, -30000},
- {609, -25000},
- {614, -20000},
- {619, -15000},
- {625, -10000},
- {630, -5000},
- {635, 0},
- {641, 5000},
- {646, 10000},
- {651, 15000},
- {657, 20000},
- {662, 25000},
- {667, 30000},
- {673, 35000},
- {678, 40000},
- {684, 45000},
- {689, 50000},
- {694, 55000},
- {700, 60000},
- {705, 65000},
- {711, 70000},
- {716, 75000},
- {722, 80000},
- {727, 85000},
- {733, 90000},
- {738, 95000},
- {743, 100000},
- {749, 105000},
- {754, 110000},
- {760, 115000},
- {765, 120000},
- {771, 125000},
+ {402, -40000},
+ {410, -35000},
+ {419, -30000},
+ {427, -25000},
+ {436, -20000},
+ {444, -15000},
+ {453, -10000},
+ {461, -5000},
+ {470, 0},
+ {478, 5000},
+ {487, 10000},
+ {496, 15000},
+ {504, 20000},
+ {513, 25000},
+ {521, 30000},
+ {530, 35000},
+ {538, 40000},
+ {547, 45000},
+ {555, 50000},
+ {564, 55000},
+ {573, 60000},
+ {581, 65000},
+ {590, 70000},
+ {599, 75000},
+ {607, 80000},
+ {616, 85000},
+ {624, 90000},
+ {633, 95000},
+ {642, 100000},
+ {650, 105000},
+ {659, 110000},
+ {668, 115000},
+ {677, 120000},
+ {685, 125000},
{TSADCV3_DATA_MASK, 125000},
};
int temp)
{
int high, low, mid;
+ u32 error = table.data_mask;
low = 0;
high = table.length - 1;
mid = (high + low) / 2;
+ /* Return mask code data when the temp is over table range */
if (temp < table.id[low].temp || temp > table.id[high].temp)
- return 0;
+ goto exit;
while (low <= high) {
if (temp == table.id[mid].temp)
mid = (low + high) / 2;
}
- return 0;
+exit:
+ pr_err("%s: Invalid conversion table: code=%d, temperature=%d\n",
+ __func__, error, temp);
+
+ return error;
}
static int rk_tsadcv2_code_to_temp(struct chip_tsadc_table table, u32 code,
return -EAGAIN; /* Incorrect reading */
while (low <= high) {
- if (code >= table.id[mid - 1].code &&
- code < table.id[mid].code)
+ if (code <= table.id[mid].code &&
+ code > table.id[mid - 1].code)
break;
else if (code > table.id[mid].code)
low = mid + 1;
}
break;
default:
- pr_err("Invalid the conversion table\n");
+ pr_err("%s: Invalid the conversion table mode=%d\n",
+ __func__, table.mode);
}
/*
* If the temperature is higher than COMP_INT or COMP_SHUT for
* "debounce" times, TSADC controller will generate interrupt or TSHUT.
*/
-static void rk_tsadcv2_initialize(void __iomem *regs,
+static void rk_tsadcv2_initialize(struct regmap *grf, void __iomem *regs,
enum tshut_polarity tshut_polarity)
{
if (tshut_polarity == TSHUT_HIGH_ACTIVE)
regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
}
+/**
+ * rk_tsadcv3_initialize - initialize TASDC Controller.
+ *
+ * (1) The tsadc control power sequence.
+ *
+ * (2) Set TSADC_V2_AUTO_PERIOD:
+ * Configure the interleave between every two accessing of
+ * TSADC in normal operation.
+ *
+ * (2) Set TSADCV2_AUTO_PERIOD_HT:
+ * Configure the interleave between every two accessing of
+ * TSADC after the temperature is higher than COM_SHUT or COM_INT.
+ *
+ * (3) Set TSADCV2_HIGH_INT_DEBOUNCE and TSADC_HIGHT_TSHUT_DEBOUNCE:
+ * If the temperature is higher than COMP_INT or COMP_SHUT for
+ * "debounce" times, TSADC controller will generate interrupt or TSHUT.
+ */
+static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs,
+ enum tshut_polarity tshut_polarity)
+{
+ /* The tsadc control power sequence */
+ if (IS_ERR(grf)) {
+ /* Set interleave value to workround ic time sync issue */
+ writel_relaxed(TSADCV2_USER_INTER_PD_SOC, regs +
+ TSADCV2_USER_CON);
+
+ writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD);
+ writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT,
+ regs + TSADCV2_HIGHT_INT_DEBOUNCE);
+ writel_relaxed(TSADCV2_AUTO_PERIOD_HT_TIME,
+ regs + TSADCV2_AUTO_PERIOD_HT);
+ writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
+ regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
+
+ } else {
+ /* Enable the voltage common mode feature */
+ regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_VCM_EN_L);
+ regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_VCM_EN_H);
+
+ udelay(100); /* The spec note says at least 15 us */
+ regmap_write(grf, GRF_SARADC_TESTBIT, GRF_SARADC_TESTBIT_ON);
+ regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_TESTBIT_H_ON);
+ udelay(200); /* The spec note says at least 90 us */
+
+ writel_relaxed(TSADCV3_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD);
+ writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT,
+ regs + TSADCV2_HIGHT_INT_DEBOUNCE);
+ writel_relaxed(TSADCV3_AUTO_PERIOD_HT_TIME,
+ regs + TSADCV2_AUTO_PERIOD_HT);
+ writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
+ regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
+ }
+
+ if (tshut_polarity == TSHUT_HIGH_ACTIVE)
+ writel_relaxed(0U | TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
+ regs + TSADCV2_AUTO_CON);
+ else
+ writel_relaxed(0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
+ regs + TSADCV2_AUTO_CON);
+}
+
static void rk_tsadcv2_irq_ack(void __iomem *regs)
{
u32 val;
}
/**
- * @rk_tsadcv3_control:
- * TSADC controller works at auto mode, and some SoCs need set the tsadc_q_sel
- * bit on TSADCV2_AUTO_CON[1]. The (1024 - tsadc_q) as output adc value if
- * setting this bit to enable.
+ * rk_tsadcv3_control - the tsadc controller is enabled or disabled.
+ *
+ * NOTE: TSADC controller works at auto mode, and some SoCs need set the
+ * tsadc_q_sel bit on TSADCV2_AUTO_CON[1]. The (1024 - tsadc_q) as output
+ * adc value if setting this bit to enable.
*/
static void rk_tsadcv3_control(void __iomem *regs, bool enable)
{
return rk_tsadcv2_code_to_temp(table, val, temp);
}
+static void rk_tsadcv2_alarm_temp(struct chip_tsadc_table table,
+ int chn, void __iomem *regs, int temp)
+{
+ u32 alarm_value, int_en;
+
+ /* Make sure the value is valid */
+ alarm_value = rk_tsadcv2_temp_to_code(table, temp);
+ if (alarm_value == table.data_mask)
+ return;
+
+ writel_relaxed(alarm_value & table.data_mask,
+ regs + TSADCV2_COMP_INT(chn));
+
+ int_en = readl_relaxed(regs + TSADCV2_INT_EN);
+ int_en |= TSADCV2_INT_SRC_EN(chn);
+ writel_relaxed(int_en, regs + TSADCV2_INT_EN);
+}
+
static void rk_tsadcv2_tshut_temp(struct chip_tsadc_table table,
int chn, void __iomem *regs, int temp)
{
u32 tshut_value, val;
+ /* Make sure the value is valid */
tshut_value = rk_tsadcv2_temp_to_code(table, temp);
+ if (tshut_value == table.data_mask)
+ return;
+
writel_relaxed(tshut_value, regs + TSADCV2_COMP_SHUT(chn));
/* TSHUT will be valid */
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
.get_temp = rk_tsadcv2_get_temp,
+ .set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
.irq_ack = rk_tsadcv2_irq_ack,
.control = rk_tsadcv2_control,
.get_temp = rk_tsadcv2_get_temp,
+ .set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
},
};
+static const struct rockchip_tsadc_chip rk3328_tsadc_data = {
+ .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
+ .chn_num = 1, /* one channels for tsadc */
+
+ .tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */
+ .tshut_temp = 95000,
+
+ .initialize = rk_tsadcv2_initialize,
+ .irq_ack = rk_tsadcv3_irq_ack,
+ .control = rk_tsadcv3_control,
+ .get_temp = rk_tsadcv2_get_temp,
+ .set_alarm_temp = rk_tsadcv2_alarm_temp,
+ .set_tshut_temp = rk_tsadcv2_tshut_temp,
+ .set_tshut_mode = rk_tsadcv2_tshut_mode,
+
+ .table = {
+ .id = rk3328_code_table,
+ .length = ARRAY_SIZE(rk3328_code_table),
+ .data_mask = TSADCV2_DATA_MASK,
+ .mode = ADC_INCREMENT,
+ },
+};
+
+static const struct rockchip_tsadc_chip rk3366_tsadc_data = {
+ .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
+ .chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */
+ .chn_num = 2, /* two channels for tsadc */
+
+ .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
+ .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
+ .tshut_temp = 95000,
+
+ .initialize = rk_tsadcv3_initialize,
+ .irq_ack = rk_tsadcv3_irq_ack,
+ .control = rk_tsadcv3_control,
+ .get_temp = rk_tsadcv2_get_temp,
+ .set_alarm_temp = rk_tsadcv2_alarm_temp,
+ .set_tshut_temp = rk_tsadcv2_tshut_temp,
+ .set_tshut_mode = rk_tsadcv2_tshut_mode,
+
+ .table = {
+ .id = rk3228_code_table,
+ .length = ARRAY_SIZE(rk3228_code_table),
+ .data_mask = TSADCV3_DATA_MASK,
+ .mode = ADC_INCREMENT,
+ },
+};
+
static const struct rockchip_tsadc_chip rk3368_tsadc_data = {
.chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
.chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */
.irq_ack = rk_tsadcv2_irq_ack,
.control = rk_tsadcv2_control,
.get_temp = rk_tsadcv2_get_temp,
+ .set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
- .initialize = rk_tsadcv2_initialize,
+ .initialize = rk_tsadcv3_initialize,
.irq_ack = rk_tsadcv3_irq_ack,
.control = rk_tsadcv3_control,
.get_temp = rk_tsadcv2_get_temp,
+ .set_alarm_temp = rk_tsadcv2_alarm_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
.compatible = "rockchip,rk3288-tsadc",
.data = (void *)&rk3288_tsadc_data,
},
+ {
+ .compatible = "rockchip,rk3328-tsadc",
+ .data = (void *)&rk3328_tsadc_data,
+ },
+ {
+ .compatible = "rockchip,rk3366-tsadc",
+ .data = (void *)&rk3366_tsadc_data,
+ },
{
.compatible = "rockchip,rk3368-tsadc",
.data = (void *)&rk3368_tsadc_data,
return IRQ_HANDLED;
}
+static int rockchip_thermal_set_trips(void *_sensor, int low, int high)
+{
+ struct rockchip_thermal_sensor *sensor = _sensor;
+ struct rockchip_thermal_data *thermal = sensor->thermal;
+ const struct rockchip_tsadc_chip *tsadc = thermal->chip;
+
+ dev_dbg(&thermal->pdev->dev, "%s: sensor %d: low: %d, high %d\n",
+ __func__, sensor->id, low, high);
+
+ tsadc->set_alarm_temp(tsadc->table,
+ sensor->id, thermal->regs, high);
+
+ return 0;
+}
+
static int rockchip_thermal_get_temp(void *_sensor, int *out_temp)
{
struct rockchip_thermal_sensor *sensor = _sensor;
static const struct thermal_zone_of_device_ops rockchip_of_thermal_ops = {
.get_temp = rockchip_thermal_get_temp,
+ .set_trips = rockchip_thermal_set_trips,
};
static int rockchip_configure_from_dt(struct device *dev,
return -EINVAL;
}
+ /* The tsadc wont to handle the error in here since some SoCs didn't
+ * need this property.
+ */
+ thermal->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(thermal->grf))
+ dev_warn(dev, "Missing rockchip,grf property\n");
+
return 0;
}
sensor->thermal = thermal;
sensor->id = id;
- sensor->tzd = thermal_zone_of_sensor_register(&pdev->dev, id, sensor,
- &rockchip_of_thermal_ops);
+ sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, id,
+ sensor, &rockchip_of_thermal_ops);
if (IS_ERR(sensor->tzd)) {
error = PTR_ERR(sensor->tzd);
dev_err(&pdev->dev, "failed to register sensor %d: %d\n",
reset_control_deassert(reset);
}
+static struct platform_device *thermal_device;
+
+void rockchip_dump_temperature(void)
+{
+ struct rockchip_thermal_data *thermal;
+ struct platform_device *pdev;
+ int i;
+
+ if (!thermal_device)
+ return;
+
+ pdev = thermal_device;
+ thermal = platform_get_drvdata(pdev);
+
+ for (i = 0; i < thermal->chip->chn_num; i++) {
+ struct rockchip_thermal_sensor *sensor = &thermal->sensors[i];
+ struct thermal_zone_device *tz = sensor->tzd;
+
+ if (tz->temperature != THERMAL_TEMP_INVALID)
+ dev_warn(&pdev->dev, "channal %d: temperature(%d C)\n",
+ i, tz->temperature / 1000);
+ }
+
+ if (thermal->regs) {
+ pr_warn("THERMAL REGS:\n");
+ print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET,
+ 32, 4, thermal->regs, 0x88, false);
+ }
+}
+EXPORT_SYMBOL_GPL(rockchip_dump_temperature);
+
+static int rockchip_thermal_panic(struct notifier_block *this,
+ unsigned long ev, void *ptr)
+{
+ rockchip_dump_temperature();
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block rockchip_thermal_panic_block = {
+ .notifier_call = rockchip_thermal_panic,
+};
+
static int rockchip_thermal_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct resource *res;
int irq;
- int i, j;
+ int i;
int error;
match = of_match_node(of_rockchip_thermal_match, np);
goto err_disable_pclk;
}
- thermal->chip->initialize(thermal->regs, thermal->tshut_polarity);
+ thermal->chip->initialize(thermal->grf, thermal->regs,
+ thermal->tshut_polarity);
for (i = 0; i < thermal->chip->chn_num; i++) {
error = rockchip_thermal_register_sensor(pdev, thermal,
dev_err(&pdev->dev,
"failed to register sensor[%d] : error = %d\n",
i, error);
- for (j = 0; j < i; j++)
- thermal_zone_of_sensor_unregister(&pdev->dev,
- thermal->sensors[j].tzd);
goto err_disable_pclk;
}
}
if (error) {
dev_err(&pdev->dev,
"failed to request tsadc irq: %d\n", error);
- goto err_unregister_sensor;
+ goto err_disable_pclk;
}
thermal->chip->control(thermal->regs, true);
platform_set_drvdata(pdev, thermal);
- return 0;
+ thermal_device = pdev;
-err_unregister_sensor:
- while (i--)
- thermal_zone_of_sensor_unregister(&pdev->dev,
- thermal->sensors[i].tzd);
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &rockchip_thermal_panic_block);
+
+ return 0;
err_disable_pclk:
clk_disable_unprepare(thermal->pclk);
struct rockchip_thermal_sensor *sensor = &thermal->sensors[i];
rockchip_thermal_toggle_sensor(sensor, false);
- thermal_zone_of_sensor_unregister(&pdev->dev, sensor->tzd);
}
thermal->chip->control(thermal->regs, false);
return error;
error = clk_enable(thermal->pclk);
- if (error)
+ if (error) {
+ clk_disable(thermal->clk);
return error;
+ }
rockchip_thermal_reset_controller(thermal->reset);
- thermal->chip->initialize(thermal->regs, thermal->tshut_polarity);
+ thermal->chip->initialize(thermal->grf, thermal->regs,
+ thermal->tshut_polarity);
for (i = 0; i < thermal->chip->chn_num; i++) {
int id = thermal->sensors[i].id;