mirror of https://gitee.com/openkylin/linux.git
Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal
Pull thermal SoC updates from Eduardo Valentin: "Several new things coming up. Specifics: - Rework of tsens and hisi thermal drivers - OF-thermal now allows sharing multiple cooling devices on maps - Added support for r8a7744 and R8A77970 on rcar thermal driver - Added support for r8a774a1 on rcar_gen3 thermal driver - New thermal driver stm32 - Fixes on multiple thermal drivers: of-thermal, imx, qoriq, armada, qcom-spmi, rcar, da9062/61" * 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal: (41 commits) thermal: da9062/61: Prevent hardware access during system suspend thermal: rcar_thermal: Prevent doing work after unbind thermal: rcar_thermal: Prevent hardware access during system suspend thermal: rcar_gen3_thermal: add R8A77980 support dt-bindings: thermal: rcar-gen3-thermal: document R8A77980 bindings thermal: add stm32 thermal driver dt-bindings: stm32-thermal: add binding documentation thermal: rcar_thermal: add R8A77970 support dt-bindings: thermal: rcar-thermal: document R8A77970 bindings thermal: rcar_thermal: fix duplicate IRQ request dt-bindings: thermal: rcar: Add device tree support for r8a7744 thermal/drivers/hisi: Add the dual clusters sensors for hi3660 thermal/drivers/hisi: Add more sensors channel thermal/drivers/hisi: Remove pointless irq field thermal/drivers/hisi: Use platform_get_irq_byname thermal/drivers/hisi: Replace macro name with relevant sensor location thermal/drivers/hisi: Add multiple sensors support thermal/drivers/hisi: Prepare to support multiple sensors thermal/drivers/hisi: Factor out the probe functions thermal/drivers/hisi: Set the thermal zone private data to the sensor pointer ...
This commit is contained in:
commit
0ef7791e2b
|
@ -6,8 +6,7 @@ interrupt signal and status register to identify high PMIC die temperature.
|
|||
|
||||
Required properties:
|
||||
- compatible: Should contain "qcom,spmi-temp-alarm".
|
||||
- reg: Specifies the SPMI address and length of the controller's
|
||||
registers.
|
||||
- reg: Specifies the SPMI address.
|
||||
- interrupts: PMIC temperature alarm interrupt.
|
||||
- #thermal-sensor-cells: Should be 0. See thermal.txt for a description.
|
||||
|
||||
|
@ -20,7 +19,7 @@ Example:
|
|||
|
||||
pm8941_temp: thermal-alarm@2400 {
|
||||
compatible = "qcom,spmi-temp-alarm";
|
||||
reg = <0x2400 0x100>;
|
||||
reg = <0x2400>;
|
||||
interrupts = <0 0x24 0 IRQ_TYPE_EDGE_RISING>;
|
||||
#thermal-sensor-cells = <0>;
|
||||
|
||||
|
@ -36,19 +35,14 @@ Example:
|
|||
thermal-sensors = <&pm8941_temp>;
|
||||
|
||||
trips {
|
||||
passive {
|
||||
temperature = <1050000>;
|
||||
stage1 {
|
||||
temperature = <105000>;
|
||||
hysteresis = <2000>;
|
||||
type = "passive";
|
||||
};
|
||||
alert {
|
||||
stage2 {
|
||||
temperature = <125000>;
|
||||
hysteresis = <2000>;
|
||||
type = "hot";
|
||||
};
|
||||
crit {
|
||||
temperature = <145000>;
|
||||
hysteresis = <2000>;
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
* Thermal Monitoring Unit (TMU) on Freescale QorIQ SoCs
|
||||
|
||||
Required properties:
|
||||
- compatible : Must include "fsl,qoriq-tmu". The version of the device is
|
||||
determined by the TMU IP Block Revision Register (IPBRR0) at
|
||||
offset 0x0BF8.
|
||||
- compatible : Must include "fsl,qoriq-tmu" or "fsl,imx8mq-tmu". The
|
||||
version of the device is determined by the TMU IP Block Revision
|
||||
Register (IPBRR0) at offset 0x0BF8.
|
||||
Table of correspondences between IPBRR0 values and example chips:
|
||||
Value Device
|
||||
---------- -----
|
||||
|
|
|
@ -7,9 +7,11 @@ inside the LSI.
|
|||
Required properties:
|
||||
- compatible : "renesas,<soctype>-thermal",
|
||||
Examples with soctypes are:
|
||||
- "renesas,r8a774a1-thermal" (RZ/G2M)
|
||||
- "renesas,r8a7795-thermal" (R-Car H3)
|
||||
- "renesas,r8a7796-thermal" (R-Car M3-W)
|
||||
- "renesas,r8a77965-thermal" (R-Car M3-N)
|
||||
- "renesas,r8a77980-thermal" (R-Car V3H)
|
||||
- reg : Address ranges of the thermal registers. Each sensor
|
||||
needs one address range. Sorting must be done in
|
||||
increasing order according to datasheet, i.e.
|
||||
|
@ -19,7 +21,8 @@ Required properties:
|
|||
|
||||
Optional properties:
|
||||
|
||||
- interrupts : interrupts routed to the TSC (3 for H3, M3-W and M3-N)
|
||||
- interrupts : interrupts routed to the TSC (3 for H3, M3-W, M3-N,
|
||||
and V3H)
|
||||
- power-domain : Must contain a reference to the power domain. This
|
||||
property is mandatory if the thermal sensor instance
|
||||
is part of a controllable power domain.
|
||||
|
|
|
@ -4,15 +4,17 @@ Required properties:
|
|||
- compatible : "renesas,thermal-<soctype>",
|
||||
"renesas,rcar-gen2-thermal" (with thermal-zone) or
|
||||
"renesas,rcar-thermal" (without thermal-zone) as
|
||||
fallback except R-Car D3.
|
||||
fallback except R-Car V3M/D3.
|
||||
Examples with soctypes are:
|
||||
- "renesas,thermal-r8a73a4" (R-Mobile APE6)
|
||||
- "renesas,thermal-r8a7743" (RZ/G1M)
|
||||
- "renesas,thermal-r8a7744" (RZ/G1N)
|
||||
- "renesas,thermal-r8a7779" (R-Car H1)
|
||||
- "renesas,thermal-r8a7790" (R-Car H2)
|
||||
- "renesas,thermal-r8a7791" (R-Car M2-W)
|
||||
- "renesas,thermal-r8a7792" (R-Car V2H)
|
||||
- "renesas,thermal-r8a7793" (R-Car M2-N)
|
||||
- "renesas,thermal-r8a77970" (R-Car V3M)
|
||||
- "renesas,thermal-r8a77995" (R-Car D3)
|
||||
- reg : Address range of the thermal registers.
|
||||
The 1st reg will be recognized as common register
|
||||
|
@ -21,7 +23,7 @@ Required properties:
|
|||
Option properties:
|
||||
|
||||
- interrupts : If present should contain 3 interrupts for
|
||||
R-Car D3 or 1 interrupt otherwise.
|
||||
R-Car V3M/D3 or 1 interrupt otherwise.
|
||||
|
||||
Example (non interrupt support):
|
||||
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
Binding for Thermal Sensor for STMicroelectronics STM32 series of SoCs.
|
||||
|
||||
On STM32 SoCs, the Digital Temperature Sensor (DTS) is in charge of managing an
|
||||
analog block which delivers a frequency depending on the internal SoC's
|
||||
temperature. By using a reference frequency, DTS is able to provide a sample
|
||||
number which can be translated into a temperature by the user.
|
||||
|
||||
DTS provides interrupt notification mechanism by threshold. This mechanism
|
||||
offers two temperature trip points: passive and critical. The first is intended
|
||||
for passive cooling notification while the second is used for over-temperature
|
||||
reset.
|
||||
|
||||
Required parameters:
|
||||
-------------------
|
||||
|
||||
compatible: Should be "st,stm32-thermal"
|
||||
reg: This should be the physical base address and length of the
|
||||
sensor's registers.
|
||||
clocks: Phandle of the clock used by the thermal sensor.
|
||||
See: Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
clock-names: Should be "pclk" for register access clock and reference clock.
|
||||
See: Documentation/devicetree/bindings/resource-names.txt
|
||||
#thermal-sensor-cells: Should be 0. See ./thermal.txt for a description.
|
||||
interrupts: Standard way to define interrupt number.
|
||||
|
||||
Example:
|
||||
|
||||
thermal-zones {
|
||||
cpu_thermal: cpu-thermal {
|
||||
polling-delay-passive = <0>;
|
||||
polling-delay = <0>;
|
||||
|
||||
thermal-sensors = <&thermal>;
|
||||
|
||||
trips {
|
||||
cpu_alert1: cpu-alert1 {
|
||||
temperature = <85000>;
|
||||
hysteresis = <0>;
|
||||
type = "passive";
|
||||
};
|
||||
|
||||
cpu-crit: cpu-crit {
|
||||
temperature = <120000>;
|
||||
hysteresis = <0>;
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
thermal: thermal@50028000 {
|
||||
compatible = "st,stm32-thermal";
|
||||
reg = <0x50028000 0x100>;
|
||||
clocks = <&rcc TMPSENS>;
|
||||
clock-names = "pclk";
|
||||
#thermal-sensor-cells = <0>;
|
||||
interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
|
@ -152,7 +152,7 @@ Optional property:
|
|||
Elem size: one cell the sensors listed in the thermal-sensors property.
|
||||
Elem type: signed Coefficients defaults to 1, in case this property
|
||||
is not specified. A simple linear polynomial is used:
|
||||
Z = c0 * x0 + c1 + x1 + ... + c(n-1) * x(n-1) + cn.
|
||||
Z = c0 * x0 + c1 * x1 + ... + c(n-1) * x(n-1) + cn.
|
||||
|
||||
The coefficients are ordered and they match with sensors
|
||||
by means of sensor ID. Additional coefficients are
|
||||
|
|
|
@ -432,7 +432,7 @@ source "drivers/thermal/samsung/Kconfig"
|
|||
endmenu
|
||||
|
||||
menu "STMicroelectronics thermal drivers"
|
||||
depends on ARCH_STI && OF
|
||||
depends on (ARCH_STI || ARCH_STM32) && OF
|
||||
source "drivers/thermal/st/Kconfig"
|
||||
endmenu
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
|
|||
obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
|
||||
obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o
|
||||
obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o
|
||||
obj-$(CONFIG_ST_THERMAL) += st/
|
||||
obj-y += st/
|
||||
obj-$(CONFIG_QCOM_TSENS) += qcom/
|
||||
obj-y += tegra/
|
||||
obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
|
||||
|
|
|
@ -526,8 +526,8 @@ static int armada_thermal_probe_legacy(struct platform_device *pdev,
|
|||
|
||||
/* First memory region points towards the status register */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (IS_ERR(res))
|
||||
return PTR_ERR(res);
|
||||
if (!res)
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Edit the resource start address and length to map over all the
|
||||
|
|
|
@ -106,7 +106,7 @@ static void da9062_thermal_poll_on(struct work_struct *work)
|
|||
THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
delay = msecs_to_jiffies(thermal->zone->passive_delay);
|
||||
schedule_delayed_work(&thermal->work, delay);
|
||||
queue_delayed_work(system_freezable_wq, &thermal->work, delay);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ static irqreturn_t da9062_thermal_irq_handler(int irq, void *data)
|
|||
struct da9062_thermal *thermal = data;
|
||||
|
||||
disable_irq_nosync(thermal->irq);
|
||||
schedule_delayed_work(&thermal->work, 0);
|
||||
queue_delayed_work(system_freezable_wq, &thermal->work, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
|
@ -55,25 +55,39 @@
|
|||
#define HI3660_TEMP_STEP (205)
|
||||
#define HI3660_TEMP_LAG (4000)
|
||||
|
||||
#define HI6220_DEFAULT_SENSOR 2
|
||||
#define HI3660_DEFAULT_SENSOR 1
|
||||
#define HI6220_CLUSTER0_SENSOR 2
|
||||
#define HI6220_CLUSTER1_SENSOR 1
|
||||
|
||||
#define HI3660_LITTLE_SENSOR 0
|
||||
#define HI3660_BIG_SENSOR 1
|
||||
#define HI3660_G3D_SENSOR 2
|
||||
#define HI3660_MODEM_SENSOR 3
|
||||
|
||||
struct hisi_thermal_data;
|
||||
|
||||
struct hisi_thermal_sensor {
|
||||
struct hisi_thermal_data *data;
|
||||
struct thermal_zone_device *tzd;
|
||||
const char *irq_name;
|
||||
uint32_t id;
|
||||
uint32_t thres_temp;
|
||||
};
|
||||
|
||||
struct hisi_thermal_ops {
|
||||
int (*get_temp)(struct hisi_thermal_sensor *sensor);
|
||||
int (*enable_sensor)(struct hisi_thermal_sensor *sensor);
|
||||
int (*disable_sensor)(struct hisi_thermal_sensor *sensor);
|
||||
int (*irq_handler)(struct hisi_thermal_sensor *sensor);
|
||||
int (*probe)(struct hisi_thermal_data *data);
|
||||
};
|
||||
|
||||
struct hisi_thermal_data {
|
||||
int (*get_temp)(struct hisi_thermal_data *data);
|
||||
int (*enable_sensor)(struct hisi_thermal_data *data);
|
||||
int (*disable_sensor)(struct hisi_thermal_data *data);
|
||||
int (*irq_handler)(struct hisi_thermal_data *data);
|
||||
const struct hisi_thermal_ops *ops;
|
||||
struct hisi_thermal_sensor *sensor;
|
||||
struct platform_device *pdev;
|
||||
struct clk *clk;
|
||||
struct hisi_thermal_sensor sensor;
|
||||
void __iomem *regs;
|
||||
int irq;
|
||||
int nr_sensors;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -266,30 +280,40 @@ static inline void hi6220_thermal_hdak_set(void __iomem *addr, int value)
|
|||
(value << 4), addr + HI6220_TEMP0_CFG);
|
||||
}
|
||||
|
||||
static int hi6220_thermal_irq_handler(struct hisi_thermal_data *data)
|
||||
static int hi6220_thermal_irq_handler(struct hisi_thermal_sensor *sensor)
|
||||
{
|
||||
struct hisi_thermal_data *data = sensor->data;
|
||||
|
||||
hi6220_thermal_alarm_clear(data->regs, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hi3660_thermal_irq_handler(struct hisi_thermal_data *data)
|
||||
static int hi3660_thermal_irq_handler(struct hisi_thermal_sensor *sensor)
|
||||
{
|
||||
hi3660_thermal_alarm_clear(data->regs, data->sensor.id, 1);
|
||||
struct hisi_thermal_data *data = sensor->data;
|
||||
|
||||
hi3660_thermal_alarm_clear(data->regs, sensor->id, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hi6220_thermal_get_temp(struct hisi_thermal_data *data)
|
||||
static int hi6220_thermal_get_temp(struct hisi_thermal_sensor *sensor)
|
||||
{
|
||||
struct hisi_thermal_data *data = sensor->data;
|
||||
|
||||
return hi6220_thermal_get_temperature(data->regs);
|
||||
}
|
||||
|
||||
static int hi3660_thermal_get_temp(struct hisi_thermal_data *data)
|
||||
static int hi3660_thermal_get_temp(struct hisi_thermal_sensor *sensor)
|
||||
{
|
||||
return hi3660_thermal_get_temperature(data->regs, data->sensor.id);
|
||||
struct hisi_thermal_data *data = sensor->data;
|
||||
|
||||
return hi3660_thermal_get_temperature(data->regs, sensor->id);
|
||||
}
|
||||
|
||||
static int hi6220_thermal_disable_sensor(struct hisi_thermal_data *data)
|
||||
static int hi6220_thermal_disable_sensor(struct hisi_thermal_sensor *sensor)
|
||||
{
|
||||
struct hisi_thermal_data *data = sensor->data;
|
||||
|
||||
/* disable sensor module */
|
||||
hi6220_thermal_enable(data->regs, 0);
|
||||
hi6220_thermal_alarm_enable(data->regs, 0);
|
||||
|
@ -300,16 +324,18 @@ static int hi6220_thermal_disable_sensor(struct hisi_thermal_data *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int hi3660_thermal_disable_sensor(struct hisi_thermal_data *data)
|
||||
static int hi3660_thermal_disable_sensor(struct hisi_thermal_sensor *sensor)
|
||||
{
|
||||
struct hisi_thermal_data *data = sensor->data;
|
||||
|
||||
/* disable sensor module */
|
||||
hi3660_thermal_alarm_enable(data->regs, data->sensor.id, 0);
|
||||
hi3660_thermal_alarm_enable(data->regs, sensor->id, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hi6220_thermal_enable_sensor(struct hisi_thermal_data *data)
|
||||
static int hi6220_thermal_enable_sensor(struct hisi_thermal_sensor *sensor)
|
||||
{
|
||||
struct hisi_thermal_sensor *sensor = &data->sensor;
|
||||
struct hisi_thermal_data *data = sensor->data;
|
||||
int ret;
|
||||
|
||||
/* enable clock for tsensor */
|
||||
|
@ -345,10 +371,10 @@ static int hi6220_thermal_enable_sensor(struct hisi_thermal_data *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int hi3660_thermal_enable_sensor(struct hisi_thermal_data *data)
|
||||
static int hi3660_thermal_enable_sensor(struct hisi_thermal_sensor *sensor)
|
||||
{
|
||||
unsigned int value;
|
||||
struct hisi_thermal_sensor *sensor = &data->sensor;
|
||||
struct hisi_thermal_data *data = sensor->data;
|
||||
|
||||
/* disable interrupt */
|
||||
hi3660_thermal_alarm_enable(data->regs, sensor->id, 0);
|
||||
|
@ -371,21 +397,8 @@ static int hi6220_thermal_probe(struct hisi_thermal_data *data)
|
|||
{
|
||||
struct platform_device *pdev = data->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
data->get_temp = hi6220_thermal_get_temp;
|
||||
data->enable_sensor = hi6220_thermal_enable_sensor;
|
||||
data->disable_sensor = hi6220_thermal_disable_sensor;
|
||||
data->irq_handler = hi6220_thermal_irq_handler;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
data->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(data->regs)) {
|
||||
dev_err(dev, "failed to get io address\n");
|
||||
return PTR_ERR(data->regs);
|
||||
}
|
||||
|
||||
data->clk = devm_clk_get(dev, "thermal_clk");
|
||||
if (IS_ERR(data->clk)) {
|
||||
ret = PTR_ERR(data->clk);
|
||||
|
@ -394,11 +407,14 @@ static int hi6220_thermal_probe(struct hisi_thermal_data *data)
|
|||
return ret;
|
||||
}
|
||||
|
||||
data->irq = platform_get_irq(pdev, 0);
|
||||
if (data->irq < 0)
|
||||
return data->irq;
|
||||
data->sensor = devm_kzalloc(dev, sizeof(*data->sensor), GFP_KERNEL);
|
||||
if (!data->sensor)
|
||||
return -ENOMEM;
|
||||
|
||||
data->sensor.id = HI6220_DEFAULT_SENSOR;
|
||||
data->sensor[0].id = HI6220_CLUSTER0_SENSOR;
|
||||
data->sensor[0].irq_name = "tsensor_intr";
|
||||
data->sensor[0].data = data;
|
||||
data->nr_sensors = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -407,38 +423,34 @@ static int hi3660_thermal_probe(struct hisi_thermal_data *data)
|
|||
{
|
||||
struct platform_device *pdev = data->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
|
||||
data->get_temp = hi3660_thermal_get_temp;
|
||||
data->enable_sensor = hi3660_thermal_enable_sensor;
|
||||
data->disable_sensor = hi3660_thermal_disable_sensor;
|
||||
data->irq_handler = hi3660_thermal_irq_handler;
|
||||
data->nr_sensors = 2;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
data->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(data->regs)) {
|
||||
dev_err(dev, "failed to get io address\n");
|
||||
return PTR_ERR(data->regs);
|
||||
}
|
||||
data->sensor = devm_kzalloc(dev, sizeof(*data->sensor) *
|
||||
data->nr_sensors, GFP_KERNEL);
|
||||
if (!data->sensor)
|
||||
return -ENOMEM;
|
||||
|
||||
data->irq = platform_get_irq(pdev, 0);
|
||||
if (data->irq < 0)
|
||||
return data->irq;
|
||||
data->sensor[0].id = HI3660_BIG_SENSOR;
|
||||
data->sensor[0].irq_name = "tsensor_a73";
|
||||
data->sensor[0].data = data;
|
||||
|
||||
data->sensor.id = HI3660_DEFAULT_SENSOR;
|
||||
data->sensor[1].id = HI3660_LITTLE_SENSOR;
|
||||
data->sensor[1].irq_name = "tsensor_a53";
|
||||
data->sensor[1].data = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hisi_thermal_get_temp(void *__data, int *temp)
|
||||
{
|
||||
struct hisi_thermal_data *data = __data;
|
||||
struct hisi_thermal_sensor *sensor = &data->sensor;
|
||||
struct hisi_thermal_sensor *sensor = __data;
|
||||
struct hisi_thermal_data *data = sensor->data;
|
||||
|
||||
*temp = data->get_temp(data);
|
||||
*temp = data->ops->get_temp(sensor);
|
||||
|
||||
dev_dbg(&data->pdev->dev, "id=%d, temp=%d, thres=%d\n",
|
||||
sensor->id, *temp, sensor->thres_temp);
|
||||
dev_dbg(&data->pdev->dev, "tzd=%p, id=%d, temp=%d, thres=%d\n",
|
||||
sensor->tzd, sensor->id, *temp, sensor->thres_temp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -449,38 +461,39 @@ static const struct thermal_zone_of_device_ops hisi_of_thermal_ops = {
|
|||
|
||||
static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
|
||||
{
|
||||
struct hisi_thermal_data *data = dev;
|
||||
struct hisi_thermal_sensor *sensor = &data->sensor;
|
||||
struct hisi_thermal_sensor *sensor = dev;
|
||||
struct hisi_thermal_data *data = sensor->data;
|
||||
int temp = 0;
|
||||
|
||||
data->irq_handler(data);
|
||||
data->ops->irq_handler(sensor);
|
||||
|
||||
hisi_thermal_get_temp(data, &temp);
|
||||
hisi_thermal_get_temp(sensor, &temp);
|
||||
|
||||
if (temp >= sensor->thres_temp) {
|
||||
dev_crit(&data->pdev->dev, "THERMAL ALARM: %d > %d\n",
|
||||
temp, sensor->thres_temp);
|
||||
dev_crit(&data->pdev->dev,
|
||||
"sensor <%d> THERMAL ALARM: %d > %d\n",
|
||||
sensor->id, temp, sensor->thres_temp);
|
||||
|
||||
thermal_zone_device_update(data->sensor.tzd,
|
||||
thermal_zone_device_update(sensor->tzd,
|
||||
THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
} else {
|
||||
dev_crit(&data->pdev->dev, "THERMAL ALARM stopped: %d < %d\n",
|
||||
temp, sensor->thres_temp);
|
||||
dev_crit(&data->pdev->dev,
|
||||
"sensor <%d> THERMAL ALARM stopped: %d < %d\n",
|
||||
sensor->id, temp, sensor->thres_temp);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int hisi_thermal_register_sensor(struct platform_device *pdev,
|
||||
struct hisi_thermal_data *data,
|
||||
struct hisi_thermal_sensor *sensor)
|
||||
{
|
||||
int ret, i;
|
||||
const struct thermal_trip *trip;
|
||||
|
||||
sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
|
||||
sensor->id, data,
|
||||
sensor->id, sensor,
|
||||
&hisi_of_thermal_ops);
|
||||
if (IS_ERR(sensor->tzd)) {
|
||||
ret = PTR_ERR(sensor->tzd);
|
||||
|
@ -502,14 +515,30 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct hisi_thermal_ops hi6220_ops = {
|
||||
.get_temp = hi6220_thermal_get_temp,
|
||||
.enable_sensor = hi6220_thermal_enable_sensor,
|
||||
.disable_sensor = hi6220_thermal_disable_sensor,
|
||||
.irq_handler = hi6220_thermal_irq_handler,
|
||||
.probe = hi6220_thermal_probe,
|
||||
};
|
||||
|
||||
static const struct hisi_thermal_ops hi3660_ops = {
|
||||
.get_temp = hi3660_thermal_get_temp,
|
||||
.enable_sensor = hi3660_thermal_enable_sensor,
|
||||
.disable_sensor = hi3660_thermal_disable_sensor,
|
||||
.irq_handler = hi3660_thermal_irq_handler,
|
||||
.probe = hi3660_thermal_probe,
|
||||
};
|
||||
|
||||
static const struct of_device_id of_hisi_thermal_match[] = {
|
||||
{
|
||||
.compatible = "hisilicon,tsensor",
|
||||
.data = hi6220_thermal_probe
|
||||
.data = &hi6220_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "hisilicon,hi3660-tsensor",
|
||||
.data = hi3660_thermal_probe
|
||||
.data = &hi3660_ops,
|
||||
},
|
||||
{ /* end */ }
|
||||
};
|
||||
|
@ -527,9 +556,9 @@ static void hisi_thermal_toggle_sensor(struct hisi_thermal_sensor *sensor,
|
|||
static int hisi_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hisi_thermal_data *data;
|
||||
int (*platform_probe)(struct hisi_thermal_data *);
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
struct resource *res;
|
||||
int i, ret;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
|
@ -537,41 +566,50 @@ static int hisi_thermal_probe(struct platform_device *pdev)
|
|||
|
||||
data->pdev = pdev;
|
||||
platform_set_drvdata(pdev, data);
|
||||
data->ops = of_device_get_match_data(dev);
|
||||
|
||||
platform_probe = of_device_get_match_data(dev);
|
||||
if (!platform_probe) {
|
||||
dev_err(dev, "failed to get probe func\n");
|
||||
return -EINVAL;
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
data->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(data->regs)) {
|
||||
dev_err(dev, "failed to get io address\n");
|
||||
return PTR_ERR(data->regs);
|
||||
}
|
||||
|
||||
ret = platform_probe(data);
|
||||
ret = data->ops->probe(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = hisi_thermal_register_sensor(pdev, data,
|
||||
&data->sensor);
|
||||
for (i = 0; i < data->nr_sensors; i++) {
|
||||
struct hisi_thermal_sensor *sensor = &data->sensor[i];
|
||||
|
||||
ret = hisi_thermal_register_sensor(pdev, sensor);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register thermal sensor: %d\n", ret);
|
||||
dev_err(dev, "failed to register thermal sensor: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = data->enable_sensor(data);
|
||||
ret = platform_get_irq_byname(pdev, sensor->irq_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, ret, NULL,
|
||||
hisi_thermal_alarm_irq_thread,
|
||||
IRQF_ONESHOT, sensor->irq_name,
|
||||
sensor);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to request alarm irq: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = data->ops->enable_sensor(sensor);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to setup the sensor: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (data->irq) {
|
||||
ret = devm_request_threaded_irq(dev, data->irq, NULL,
|
||||
hisi_thermal_alarm_irq_thread,
|
||||
IRQF_ONESHOT, "hisi_thermal", data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to request alarm irq: %d\n", ret);
|
||||
return ret;
|
||||
hisi_thermal_toggle_sensor(sensor, true);
|
||||
}
|
||||
}
|
||||
|
||||
hisi_thermal_toggle_sensor(&data->sensor, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -579,11 +617,14 @@ static int hisi_thermal_probe(struct platform_device *pdev)
|
|||
static int hisi_thermal_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hisi_thermal_data *data = platform_get_drvdata(pdev);
|
||||
struct hisi_thermal_sensor *sensor = &data->sensor;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < data->nr_sensors; i++) {
|
||||
struct hisi_thermal_sensor *sensor = &data->sensor[i];
|
||||
|
||||
hisi_thermal_toggle_sensor(sensor, false);
|
||||
|
||||
data->disable_sensor(data);
|
||||
data->ops->disable_sensor(sensor);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -592,8 +633,10 @@ static int hisi_thermal_remove(struct platform_device *pdev)
|
|||
static int hisi_thermal_suspend(struct device *dev)
|
||||
{
|
||||
struct hisi_thermal_data *data = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
data->disable_sensor(data);
|
||||
for (i = 0; i < data->nr_sensors; i++)
|
||||
data->ops->disable_sensor(&data->sensor[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -601,8 +644,12 @@ static int hisi_thermal_suspend(struct device *dev)
|
|||
static int hisi_thermal_resume(struct device *dev)
|
||||
{
|
||||
struct hisi_thermal_data *data = dev_get_drvdata(dev);
|
||||
int i, ret = 0;
|
||||
|
||||
return data->enable_sensor(data);
|
||||
for (i = 0; i < data->nr_sensors; i++)
|
||||
ret |= data->ops->enable_sensor(&data->sensor[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -725,7 +725,7 @@ static int imx_thermal_probe(struct platform_device *pdev)
|
|||
} else {
|
||||
ret = imx_init_from_tempmon_data(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to init from from fsl,tempmon-data\n");
|
||||
dev_err(&pdev->dev, "failed to init from fsl,tempmon-data\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -762,9 +762,7 @@ static int imx_thermal_probe(struct platform_device *pdev)
|
|||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get thermal clk: %d\n", ret);
|
||||
cpufreq_cooling_unregister(data->cdev);
|
||||
cpufreq_cpu_put(data->policy);
|
||||
return ret;
|
||||
goto cpufreq_put;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -777,9 +775,7 @@ static int imx_thermal_probe(struct platform_device *pdev)
|
|||
ret = clk_prepare_enable(data->thermal_clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret);
|
||||
cpufreq_cooling_unregister(data->cdev);
|
||||
cpufreq_cpu_put(data->policy);
|
||||
return ret;
|
||||
goto cpufreq_put;
|
||||
}
|
||||
|
||||
data->tz = thermal_zone_device_register("imx_thermal_zone",
|
||||
|
@ -792,10 +788,7 @@ static int imx_thermal_probe(struct platform_device *pdev)
|
|||
ret = PTR_ERR(data->tz);
|
||||
dev_err(&pdev->dev,
|
||||
"failed to register thermal zone device %d\n", ret);
|
||||
clk_disable_unprepare(data->thermal_clk);
|
||||
cpufreq_cooling_unregister(data->cdev);
|
||||
cpufreq_cpu_put(data->policy);
|
||||
return ret;
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "%s CPU temperature grade - max:%dC"
|
||||
|
@ -827,14 +820,20 @@ static int imx_thermal_probe(struct platform_device *pdev)
|
|||
0, "imx_thermal", data);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret);
|
||||
clk_disable_unprepare(data->thermal_clk);
|
||||
thermal_zone_device_unregister(data->tz);
|
||||
cpufreq_cooling_unregister(data->cdev);
|
||||
cpufreq_cpu_put(data->policy);
|
||||
return ret;
|
||||
goto thermal_zone_unregister;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
thermal_zone_unregister:
|
||||
thermal_zone_device_unregister(data->tz);
|
||||
clk_disable:
|
||||
clk_disable_unprepare(data->thermal_clk);
|
||||
cpufreq_put:
|
||||
cpufreq_cooling_unregister(data->cdev);
|
||||
cpufreq_cpu_put(data->policy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_thermal_remove(struct platform_device *pdev)
|
||||
|
|
|
@ -19,22 +19,33 @@
|
|||
/*** Private data structures to represent thermal device tree data ***/
|
||||
|
||||
/**
|
||||
* struct __thermal_bind_param - a match between trip and cooling device
|
||||
* struct __thermal_cooling_bind_param - a cooling device for a trip point
|
||||
* @cooling_device: a pointer to identify the referred cooling device
|
||||
* @trip_id: the trip point index
|
||||
* @usage: the percentage (from 0 to 100) of cooling contribution
|
||||
* @min: minimum cooling state used at this trip point
|
||||
* @max: maximum cooling state used at this trip point
|
||||
*/
|
||||
|
||||
struct __thermal_bind_params {
|
||||
struct __thermal_cooling_bind_param {
|
||||
struct device_node *cooling_device;
|
||||
unsigned int trip_id;
|
||||
unsigned int usage;
|
||||
unsigned long min;
|
||||
unsigned long max;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct __thermal_bind_param - a match between trip and cooling device
|
||||
* @tcbp: a pointer to an array of cooling devices
|
||||
* @count: number of elements in array
|
||||
* @trip_id: the trip point index
|
||||
* @usage: the percentage (from 0 to 100) of cooling contribution
|
||||
*/
|
||||
|
||||
struct __thermal_bind_params {
|
||||
struct __thermal_cooling_bind_param *tcbp;
|
||||
unsigned int count;
|
||||
unsigned int trip_id;
|
||||
unsigned int usage;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct __thermal_zone - internal representation of a thermal zone
|
||||
* @mode: current thermal zone device mode (enabled/disabled)
|
||||
|
@ -192,27 +203,33 @@ static int of_thermal_bind(struct thermal_zone_device *thermal,
|
|||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct __thermal_zone *data = thermal->devdata;
|
||||
int i;
|
||||
struct __thermal_bind_params *tbp;
|
||||
struct __thermal_cooling_bind_param *tcbp;
|
||||
int i, j;
|
||||
|
||||
if (!data || IS_ERR(data))
|
||||
return -ENODEV;
|
||||
|
||||
/* find where to bind */
|
||||
for (i = 0; i < data->num_tbps; i++) {
|
||||
struct __thermal_bind_params *tbp = data->tbps + i;
|
||||
tbp = data->tbps + i;
|
||||
|
||||
if (tbp->cooling_device == cdev->np) {
|
||||
for (j = 0; j < tbp->count; j++) {
|
||||
tcbp = tbp->tcbp + j;
|
||||
|
||||
if (tcbp->cooling_device == cdev->np) {
|
||||
int ret;
|
||||
|
||||
ret = thermal_zone_bind_cooling_device(thermal,
|
||||
tbp->trip_id, cdev,
|
||||
tbp->max,
|
||||
tbp->min,
|
||||
tcbp->max,
|
||||
tcbp->min,
|
||||
tbp->usage);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -221,16 +238,21 @@ static int of_thermal_unbind(struct thermal_zone_device *thermal,
|
|||
struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct __thermal_zone *data = thermal->devdata;
|
||||
int i;
|
||||
struct __thermal_bind_params *tbp;
|
||||
struct __thermal_cooling_bind_param *tcbp;
|
||||
int i, j;
|
||||
|
||||
if (!data || IS_ERR(data))
|
||||
return -ENODEV;
|
||||
|
||||
/* find where to unbind */
|
||||
for (i = 0; i < data->num_tbps; i++) {
|
||||
struct __thermal_bind_params *tbp = data->tbps + i;
|
||||
tbp = data->tbps + i;
|
||||
|
||||
if (tbp->cooling_device == cdev->np) {
|
||||
for (j = 0; j < tbp->count; j++) {
|
||||
tcbp = tbp->tcbp + j;
|
||||
|
||||
if (tcbp->cooling_device == cdev->np) {
|
||||
int ret;
|
||||
|
||||
ret = thermal_zone_unbind_cooling_device(thermal,
|
||||
|
@ -239,6 +261,7 @@ static int of_thermal_unbind(struct thermal_zone_device *thermal,
|
|||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -486,8 +509,8 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
|
|||
if (sensor_specs.args_count >= 1) {
|
||||
id = sensor_specs.args[0];
|
||||
WARN(sensor_specs.args_count > 1,
|
||||
"%s: too many cells in sensor specifier %d\n",
|
||||
sensor_specs.np->name, sensor_specs.args_count);
|
||||
"%pOFn: too many cells in sensor specifier %d\n",
|
||||
sensor_specs.np, sensor_specs.args_count);
|
||||
} else {
|
||||
id = 0;
|
||||
}
|
||||
|
@ -655,8 +678,9 @@ static int thermal_of_populate_bind_params(struct device_node *np,
|
|||
int ntrips)
|
||||
{
|
||||
struct of_phandle_args cooling_spec;
|
||||
struct __thermal_cooling_bind_param *__tcbp;
|
||||
struct device_node *trip;
|
||||
int ret, i;
|
||||
int ret, i, count;
|
||||
u32 prop;
|
||||
|
||||
/* Default weight. Usage is optional */
|
||||
|
@ -683,20 +707,44 @@ static int thermal_of_populate_bind_params(struct device_node *np,
|
|||
goto end;
|
||||
}
|
||||
|
||||
ret = of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells",
|
||||
0, &cooling_spec);
|
||||
if (ret < 0) {
|
||||
pr_err("missing cooling_device property\n");
|
||||
count = of_count_phandle_with_args(np, "cooling-device",
|
||||
"#cooling-cells");
|
||||
if (!count) {
|
||||
pr_err("Add a cooling_device property with at least one device\n");
|
||||
goto end;
|
||||
}
|
||||
__tbp->cooling_device = cooling_spec.np;
|
||||
|
||||
__tcbp = kcalloc(count, sizeof(*__tcbp), GFP_KERNEL);
|
||||
if (!__tcbp)
|
||||
goto end;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
ret = of_parse_phandle_with_args(np, "cooling-device",
|
||||
"#cooling-cells", i, &cooling_spec);
|
||||
if (ret < 0) {
|
||||
pr_err("Invalid cooling-device entry\n");
|
||||
goto free_tcbp;
|
||||
}
|
||||
|
||||
__tcbp[i].cooling_device = cooling_spec.np;
|
||||
|
||||
if (cooling_spec.args_count >= 2) { /* at least min and max */
|
||||
__tbp->min = cooling_spec.args[0];
|
||||
__tbp->max = cooling_spec.args[1];
|
||||
__tcbp[i].min = cooling_spec.args[0];
|
||||
__tcbp[i].max = cooling_spec.args[1];
|
||||
} else {
|
||||
pr_err("wrong reference to cooling device, missing limits\n");
|
||||
}
|
||||
}
|
||||
|
||||
__tbp->tcbp = __tcbp;
|
||||
__tbp->count = count;
|
||||
|
||||
goto end;
|
||||
|
||||
free_tcbp:
|
||||
for (i = i - 1; i >= 0; i--)
|
||||
of_node_put(__tcbp[i].cooling_device);
|
||||
kfree(__tcbp);
|
||||
end:
|
||||
of_node_put(trip);
|
||||
|
||||
|
@ -903,8 +951,16 @@ __init *thermal_of_build_thermal_zone(struct device_node *np)
|
|||
return tz;
|
||||
|
||||
free_tbps:
|
||||
for (i = i - 1; i >= 0; i--)
|
||||
of_node_put(tz->tbps[i].cooling_device);
|
||||
for (i = i - 1; i >= 0; i--) {
|
||||
struct __thermal_bind_params *tbp = tz->tbps + i;
|
||||
int j;
|
||||
|
||||
for (j = 0; j < tbp->count; j++)
|
||||
of_node_put(tbp->tcbp[j].cooling_device);
|
||||
|
||||
kfree(tbp->tcbp);
|
||||
}
|
||||
|
||||
kfree(tz->tbps);
|
||||
free_trips:
|
||||
for (i = 0; i < tz->ntrips; i++)
|
||||
|
@ -920,10 +976,18 @@ __init *thermal_of_build_thermal_zone(struct device_node *np)
|
|||
|
||||
static inline void of_thermal_free_zone(struct __thermal_zone *tz)
|
||||
{
|
||||
int i;
|
||||
struct __thermal_bind_params *tbp;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < tz->num_tbps; i++) {
|
||||
tbp = tz->tbps + i;
|
||||
|
||||
for (j = 0; j < tbp->count; j++)
|
||||
of_node_put(tbp->tcbp[j].cooling_device);
|
||||
|
||||
kfree(tbp->tcbp);
|
||||
}
|
||||
|
||||
for (i = 0; i < tz->num_tbps; i++)
|
||||
of_node_put(tz->tbps[i].cooling_device);
|
||||
kfree(tz->tbps);
|
||||
for (i = 0; i < tz->ntrips; i++)
|
||||
of_node_put(tz->trips[i].np);
|
||||
|
@ -963,8 +1027,8 @@ int __init of_parse_thermal_zones(void)
|
|||
|
||||
tz = thermal_of_build_thermal_zone(child);
|
||||
if (IS_ERR(tz)) {
|
||||
pr_err("failed to build thermal zone %s: %ld\n",
|
||||
child->name,
|
||||
pr_err("failed to build thermal zone %pOFn: %ld\n",
|
||||
child,
|
||||
PTR_ERR(tz));
|
||||
continue;
|
||||
}
|
||||
|
@ -998,7 +1062,7 @@ int __init of_parse_thermal_zones(void)
|
|||
tz->passive_delay,
|
||||
tz->polling_delay);
|
||||
if (IS_ERR(zone)) {
|
||||
pr_err("Failed to build %s zone %ld\n", child->name,
|
||||
pr_err("Failed to build %pOFn zone %ld\n", child,
|
||||
PTR_ERR(zone));
|
||||
kfree(tzp);
|
||||
kfree(ops);
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
#define QPNP_TM_REG_TYPE 0x04
|
||||
#define QPNP_TM_REG_SUBTYPE 0x05
|
||||
#define QPNP_TM_REG_STATUS 0x08
|
||||
|
@ -37,9 +39,11 @@
|
|||
#define STATUS_GEN2_STATE_MASK GENMASK(6, 4)
|
||||
#define STATUS_GEN2_STATE_SHIFT 4
|
||||
|
||||
#define SHUTDOWN_CTRL1_OVERRIDE_MASK GENMASK(7, 6)
|
||||
#define SHUTDOWN_CTRL1_OVERRIDE_S2 BIT(6)
|
||||
#define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0)
|
||||
|
||||
#define SHUTDOWN_CTRL1_RATE_25HZ BIT(3)
|
||||
|
||||
#define ALARM_CTRL_FORCE_ENABLE BIT(7)
|
||||
|
||||
/*
|
||||
|
@ -56,12 +60,19 @@
|
|||
#define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
|
||||
|
||||
#define THRESH_MIN 0
|
||||
#define THRESH_MAX 3
|
||||
|
||||
/* Stage 2 Threshold Min: 125 C */
|
||||
#define STAGE2_THRESHOLD_MIN 125000
|
||||
/* Stage 2 Threshold Max: 140 C */
|
||||
#define STAGE2_THRESHOLD_MAX 140000
|
||||
|
||||
/* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
|
||||
#define DEFAULT_TEMP 37000
|
||||
|
||||
struct qpnp_tm_chip {
|
||||
struct regmap *map;
|
||||
struct device *dev;
|
||||
struct thermal_zone_device *tz_dev;
|
||||
unsigned int subtype;
|
||||
long temp;
|
||||
|
@ -69,6 +80,10 @@ struct qpnp_tm_chip {
|
|||
unsigned int stage;
|
||||
unsigned int prev_stage;
|
||||
unsigned int base;
|
||||
/* protects .thresh, .stage and chip registers */
|
||||
struct mutex lock;
|
||||
bool initialized;
|
||||
|
||||
struct iio_channel *adc;
|
||||
};
|
||||
|
||||
|
@ -125,6 +140,8 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
|
|||
unsigned int stage, stage_new, stage_old;
|
||||
int ret;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&chip->lock));
|
||||
|
||||
ret = qpnp_tm_get_temp_stage(chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -163,8 +180,15 @@ static int qpnp_tm_get_temp(void *data, int *temp)
|
|||
if (!temp)
|
||||
return -EINVAL;
|
||||
|
||||
if (!chip->initialized) {
|
||||
*temp = DEFAULT_TEMP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!chip->adc) {
|
||||
mutex_lock(&chip->lock);
|
||||
ret = qpnp_tm_update_temp_no_adc(chip);
|
||||
mutex_unlock(&chip->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
|
@ -180,8 +204,72 @@ static int qpnp_tm_get_temp(void *data, int *temp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip,
|
||||
int temp)
|
||||
{
|
||||
u8 reg;
|
||||
bool disable_s2_shutdown = false;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&chip->lock));
|
||||
|
||||
/*
|
||||
* Default: S2 and S3 shutdown enabled, thresholds at
|
||||
* 105C/125C/145C, monitoring at 25Hz
|
||||
*/
|
||||
reg = SHUTDOWN_CTRL1_RATE_25HZ;
|
||||
|
||||
if (temp == THERMAL_TEMP_INVALID ||
|
||||
temp < STAGE2_THRESHOLD_MIN) {
|
||||
chip->thresh = THRESH_MIN;
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if (temp <= STAGE2_THRESHOLD_MAX) {
|
||||
chip->thresh = THRESH_MAX -
|
||||
((STAGE2_THRESHOLD_MAX - temp) /
|
||||
TEMP_THRESH_STEP);
|
||||
disable_s2_shutdown = true;
|
||||
} else {
|
||||
chip->thresh = THRESH_MAX;
|
||||
|
||||
if (chip->adc)
|
||||
disable_s2_shutdown = true;
|
||||
else
|
||||
dev_warn(chip->dev,
|
||||
"No ADC is configured and critical temperature is above the maximum stage 2 threshold of 140 C! Configuring stage 2 shutdown at 140 C.\n");
|
||||
}
|
||||
|
||||
skip:
|
||||
reg |= chip->thresh;
|
||||
if (disable_s2_shutdown)
|
||||
reg |= SHUTDOWN_CTRL1_OVERRIDE_S2;
|
||||
|
||||
return qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
|
||||
}
|
||||
|
||||
static int qpnp_tm_set_trip_temp(void *data, int trip, int temp)
|
||||
{
|
||||
struct qpnp_tm_chip *chip = data;
|
||||
const struct thermal_trip *trip_points;
|
||||
int ret;
|
||||
|
||||
trip_points = of_thermal_get_trip_points(chip->tz_dev);
|
||||
if (!trip_points)
|
||||
return -EINVAL;
|
||||
|
||||
if (trip_points[trip].type != THERMAL_TRIP_CRITICAL)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
ret = qpnp_tm_update_critical_trip_temp(chip, temp);
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_of_device_ops qpnp_tm_sensor_ops = {
|
||||
.get_temp = qpnp_tm_get_temp,
|
||||
.set_trip_temp = qpnp_tm_set_trip_temp,
|
||||
};
|
||||
|
||||
static irqreturn_t qpnp_tm_isr(int irq, void *data)
|
||||
|
@ -193,6 +281,29 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int qpnp_tm_get_critical_trip_temp(struct qpnp_tm_chip *chip)
|
||||
{
|
||||
int ntrips;
|
||||
const struct thermal_trip *trips;
|
||||
int i;
|
||||
|
||||
ntrips = of_thermal_get_ntrips(chip->tz_dev);
|
||||
if (ntrips <= 0)
|
||||
return THERMAL_TEMP_INVALID;
|
||||
|
||||
trips = of_thermal_get_trip_points(chip->tz_dev);
|
||||
if (!trips)
|
||||
return THERMAL_TEMP_INVALID;
|
||||
|
||||
for (i = 0; i < ntrips; i++) {
|
||||
if (of_thermal_is_trip_valid(chip->tz_dev, i) &&
|
||||
trips[i].type == THERMAL_TRIP_CRITICAL)
|
||||
return trips[i].temperature;
|
||||
}
|
||||
|
||||
return THERMAL_TEMP_INVALID;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function initializes the internal temp value based on only the
|
||||
* current thermal stage and threshold. Setup threshold control and
|
||||
|
@ -203,17 +314,20 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
|
|||
unsigned int stage;
|
||||
int ret;
|
||||
u8 reg = 0;
|
||||
int crit_temp;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
ret = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
chip->thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
|
||||
chip->temp = DEFAULT_TEMP;
|
||||
|
||||
ret = qpnp_tm_get_temp_stage(chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out;
|
||||
chip->stage = ret;
|
||||
|
||||
stage = chip->subtype == QPNP_TM_SUBTYPE_GEN1
|
||||
|
@ -224,21 +338,19 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip)
|
|||
(stage - 1) * TEMP_STAGE_STEP +
|
||||
TEMP_THRESH_MIN;
|
||||
|
||||
/*
|
||||
* Set threshold and disable software override of stage 2 and 3
|
||||
* shutdowns.
|
||||
*/
|
||||
chip->thresh = THRESH_MIN;
|
||||
reg &= ~(SHUTDOWN_CTRL1_OVERRIDE_MASK | SHUTDOWN_CTRL1_THRESHOLD_MASK);
|
||||
reg |= chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
|
||||
ret = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
|
||||
crit_temp = qpnp_tm_get_critical_trip_temp(chip);
|
||||
ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
/* Enable the thermal alarm PMIC module in always-on mode. */
|
||||
reg = ALARM_CTRL_FORCE_ENABLE;
|
||||
ret = qpnp_tm_write(chip, QPNP_TM_REG_ALARM_CTRL, reg);
|
||||
|
||||
chip->initialized = true;
|
||||
|
||||
out:
|
||||
mutex_unlock(&chip->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -257,6 +369,9 @@ static int qpnp_tm_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, chip);
|
||||
chip->dev = &pdev->dev;
|
||||
|
||||
mutex_init(&chip->lock);
|
||||
|
||||
chip->map = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!chip->map)
|
||||
|
@ -302,6 +417,18 @@ static int qpnp_tm_probe(struct platform_device *pdev)
|
|||
|
||||
chip->subtype = subtype;
|
||||
|
||||
/*
|
||||
* Register the sensor before initializing the hardware to be able to
|
||||
* read the trip points. get_temp() returns the default temperature
|
||||
* before the hardware initialization is completed.
|
||||
*/
|
||||
chip->tz_dev = devm_thermal_zone_of_sensor_register(
|
||||
&pdev->dev, 0, chip, &qpnp_tm_sensor_ops);
|
||||
if (IS_ERR(chip->tz_dev)) {
|
||||
dev_err(&pdev->dev, "failed to register sensor\n");
|
||||
return PTR_ERR(chip->tz_dev);
|
||||
}
|
||||
|
||||
ret = qpnp_tm_init(chip);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "init failed\n");
|
||||
|
@ -313,12 +440,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
chip->tz_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, chip,
|
||||
&qpnp_tm_sensor_ops);
|
||||
if (IS_ERR(chip->tz_dev)) {
|
||||
dev_err(&pdev->dev, "failed to register sensor\n");
|
||||
return PTR_ERR(chip->tz_dev);
|
||||
}
|
||||
thermal_zone_device_update(chip->tz_dev, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -109,5 +100,6 @@ static const struct tsens_ops ops_8916 = {
|
|||
const struct tsens_data data_8916 = {
|
||||
.num_sensors = 5,
|
||||
.ops = &ops_8916,
|
||||
.reg_offsets = { [SROT_CTRL_OFFSET] = 0x0 },
|
||||
.hw_ids = (unsigned int []){0, 1, 2, 4, 5 },
|
||||
};
|
||||
|
|
|
@ -1,15 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -69,7 +60,7 @@ static int suspend_8960(struct tsens_device *tmdev)
|
|||
{
|
||||
int ret;
|
||||
unsigned int mask;
|
||||
struct regmap *map = tmdev->map;
|
||||
struct regmap *map = tmdev->tm_map;
|
||||
|
||||
ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold);
|
||||
if (ret)
|
||||
|
@ -94,7 +85,7 @@ static int suspend_8960(struct tsens_device *tmdev)
|
|||
static int resume_8960(struct tsens_device *tmdev)
|
||||
{
|
||||
int ret;
|
||||
struct regmap *map = tmdev->map;
|
||||
struct regmap *map = tmdev->tm_map;
|
||||
|
||||
ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST);
|
||||
if (ret)
|
||||
|
@ -126,12 +117,12 @@ static int enable_8960(struct tsens_device *tmdev, int id)
|
|||
int ret;
|
||||
u32 reg, mask;
|
||||
|
||||
ret = regmap_read(tmdev->map, CNTL_ADDR, ®);
|
||||
ret = regmap_read(tmdev->tm_map, CNTL_ADDR, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mask = BIT(id + SENSOR0_SHIFT);
|
||||
ret = regmap_write(tmdev->map, CNTL_ADDR, reg | SW_RST);
|
||||
ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg | SW_RST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -140,7 +131,7 @@ static int enable_8960(struct tsens_device *tmdev, int id)
|
|||
else
|
||||
reg |= mask | SLP_CLK_ENA_8660 | EN;
|
||||
|
||||
ret = regmap_write(tmdev->map, CNTL_ADDR, reg);
|
||||
ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -157,7 +148,7 @@ static void disable_8960(struct tsens_device *tmdev)
|
|||
mask <<= SENSOR0_SHIFT;
|
||||
mask |= EN;
|
||||
|
||||
ret = regmap_read(tmdev->map, CNTL_ADDR, ®_cntl);
|
||||
ret = regmap_read(tmdev->tm_map, CNTL_ADDR, ®_cntl);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
|
@ -168,7 +159,7 @@ static void disable_8960(struct tsens_device *tmdev)
|
|||
else
|
||||
reg_cntl &= ~SLP_CLK_ENA_8660;
|
||||
|
||||
regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
|
||||
regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl);
|
||||
}
|
||||
|
||||
static int init_8960(struct tsens_device *tmdev)
|
||||
|
@ -176,8 +167,8 @@ static int init_8960(struct tsens_device *tmdev)
|
|||
int ret, i;
|
||||
u32 reg_cntl;
|
||||
|
||||
tmdev->map = dev_get_regmap(tmdev->dev, NULL);
|
||||
if (!tmdev->map)
|
||||
tmdev->tm_map = dev_get_regmap(tmdev->dev, NULL);
|
||||
if (!tmdev->tm_map)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
|
@ -193,14 +184,14 @@ static int init_8960(struct tsens_device *tmdev)
|
|||
}
|
||||
|
||||
reg_cntl = SW_RST;
|
||||
ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl);
|
||||
ret = regmap_update_bits(tmdev->tm_map, CNTL_ADDR, SW_RST, reg_cntl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (tmdev->num_sensors > 1) {
|
||||
reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
|
||||
reg_cntl &= ~SW_RST;
|
||||
ret = regmap_update_bits(tmdev->map, CONFIG_ADDR,
|
||||
ret = regmap_update_bits(tmdev->tm_map, CONFIG_ADDR,
|
||||
CONFIG_MASK, CONFIG);
|
||||
} else {
|
||||
reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16);
|
||||
|
@ -209,12 +200,12 @@ static int init_8960(struct tsens_device *tmdev)
|
|||
}
|
||||
|
||||
reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT;
|
||||
ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
|
||||
ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg_cntl |= EN;
|
||||
ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
|
||||
ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -261,12 +252,12 @@ static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp)
|
|||
|
||||
timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
|
||||
do {
|
||||
ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy);
|
||||
ret = regmap_read(tmdev->tm_map, INT_STATUS_ADDR, &trdy);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!(trdy & TRDY_MASK))
|
||||
continue;
|
||||
ret = regmap_read(tmdev->map, s->status, &code);
|
||||
ret = regmap_read(tmdev->tm_map, s->status, &code);
|
||||
if (ret)
|
||||
return ret;
|
||||
*temp = code_to_mdegC(code, s);
|
||||
|
|
|
@ -1,15 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -241,4 +232,5 @@ static const struct tsens_ops ops_8974 = {
|
|||
const struct tsens_data data_8974 = {
|
||||
.num_sensors = 11,
|
||||
.ops = &ops_8974,
|
||||
.reg_offsets = { [SROT_CTRL_OFFSET] = 0x0 },
|
||||
};
|
||||
|
|
|
@ -1,15 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
|
@ -21,7 +12,11 @@
|
|||
#include <linux/regmap.h>
|
||||
#include "tsens.h"
|
||||
|
||||
#define S0_ST_ADDR 0x1030
|
||||
/* SROT */
|
||||
#define TSENS_EN BIT(0)
|
||||
|
||||
/* TM */
|
||||
#define STATUS_OFFSET 0x30
|
||||
#define SN_ADDR_OFFSET 0x4
|
||||
#define SN_ST_TEMP_MASK 0x3ff
|
||||
#define CAL_DEGC_PT1 30
|
||||
|
@ -107,8 +102,8 @@ int get_temp_common(struct tsens_device *tmdev, int id, int *temp)
|
|||
unsigned int status_reg;
|
||||
int last_temp = 0, ret;
|
||||
|
||||
status_reg = S0_ST_ADDR + s->hw_id * SN_ADDR_OFFSET;
|
||||
ret = regmap_read(tmdev->map, status_reg, &code);
|
||||
status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * SN_ADDR_OFFSET;
|
||||
ret = regmap_read(tmdev->tm_map, status_reg, &code);
|
||||
if (ret)
|
||||
return ret;
|
||||
last_temp = code & SN_ST_TEMP_MASK;
|
||||
|
@ -126,29 +121,52 @@ static const struct regmap_config tsens_config = {
|
|||
|
||||
int __init init_common(struct tsens_device *tmdev)
|
||||
{
|
||||
void __iomem *base;
|
||||
void __iomem *tm_base, *srot_base;
|
||||
struct resource *res;
|
||||
u32 code;
|
||||
int ret;
|
||||
struct platform_device *op = of_find_device_by_node(tmdev->dev->of_node);
|
||||
u16 ctrl_offset = tmdev->reg_offsets[SROT_CTRL_OFFSET];
|
||||
|
||||
if (!op)
|
||||
return -EINVAL;
|
||||
|
||||
/* The driver only uses the TM register address space for now */
|
||||
if (op->num_resources > 1) {
|
||||
/* DT with separate SROT and TM address space */
|
||||
tmdev->tm_offset = 0;
|
||||
res = platform_get_resource(op, IORESOURCE_MEM, 1);
|
||||
srot_base = devm_ioremap_resource(&op->dev, res);
|
||||
if (IS_ERR(srot_base))
|
||||
return PTR_ERR(srot_base);
|
||||
|
||||
tmdev->srot_map = devm_regmap_init_mmio(tmdev->dev,
|
||||
srot_base, &tsens_config);
|
||||
if (IS_ERR(tmdev->srot_map))
|
||||
return PTR_ERR(tmdev->srot_map);
|
||||
|
||||
} else {
|
||||
/* old DTs where SROT and TM were in a contiguous 2K block */
|
||||
tmdev->tm_offset = 0x1000;
|
||||
}
|
||||
|
||||
res = platform_get_resource(op, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&op->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
tm_base = devm_ioremap_resource(&op->dev, res);
|
||||
if (IS_ERR(tm_base))
|
||||
return PTR_ERR(tm_base);
|
||||
|
||||
tmdev->map = devm_regmap_init_mmio(tmdev->dev, base, &tsens_config);
|
||||
if (IS_ERR(tmdev->map))
|
||||
return PTR_ERR(tmdev->map);
|
||||
tmdev->tm_map = devm_regmap_init_mmio(tmdev->dev, tm_base, &tsens_config);
|
||||
if (IS_ERR(tmdev->tm_map))
|
||||
return PTR_ERR(tmdev->tm_map);
|
||||
|
||||
if (tmdev->srot_map) {
|
||||
ret = regmap_read(tmdev->srot_map, ctrl_offset, &code);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!(code & TSENS_EN)) {
|
||||
dev_err(tmdev->dev, "tsens device is not enabled\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ static int get_temp_tsens_v2(struct tsens_device *tmdev, int id, int *temp)
|
|||
int ret;
|
||||
|
||||
status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * 4;
|
||||
ret = regmap_read(tmdev->map, status_reg, &code);
|
||||
ret = regmap_read(tmdev->tm_map, status_reg, &code);
|
||||
if (ret)
|
||||
return ret;
|
||||
last_temp = code & LAST_TEMP_MASK;
|
||||
|
@ -29,7 +29,7 @@ static int get_temp_tsens_v2(struct tsens_device *tmdev, int id, int *temp)
|
|||
goto done;
|
||||
|
||||
/* Try a second time */
|
||||
ret = regmap_read(tmdev->map, status_reg, &code);
|
||||
ret = regmap_read(tmdev->tm_map, status_reg, &code);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (code & STATUS_VALID_BIT) {
|
||||
|
@ -40,7 +40,7 @@ static int get_temp_tsens_v2(struct tsens_device *tmdev, int id, int *temp)
|
|||
}
|
||||
|
||||
/* Try a third/last time */
|
||||
ret = regmap_read(tmdev->map, status_reg, &code);
|
||||
ret = regmap_read(tmdev->tm_map, status_reg, &code);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (code & STATUS_VALID_BIT) {
|
||||
|
@ -68,10 +68,12 @@ static const struct tsens_ops ops_generic_v2 = {
|
|||
|
||||
const struct tsens_data data_tsens_v2 = {
|
||||
.ops = &ops_generic_v2,
|
||||
.reg_offsets = { [SROT_CTRL_OFFSET] = 0x4 },
|
||||
};
|
||||
|
||||
/* Kept around for backward compatibility with old msm8996.dtsi */
|
||||
const struct tsens_data data_8996 = {
|
||||
.num_sensors = 13,
|
||||
.ops = &ops_generic_v2,
|
||||
.reg_offsets = { [SROT_CTRL_OFFSET] = 0x4 },
|
||||
};
|
||||
|
|
|
@ -1,15 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
|
@ -89,11 +80,6 @@ static int tsens_register(struct tsens_device *tmdev)
|
|||
{
|
||||
int i;
|
||||
struct thermal_zone_device *tzd;
|
||||
u32 *hw_id, n = tmdev->num_sensors;
|
||||
|
||||
hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL);
|
||||
if (!hw_id)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < tmdev->num_sensors; i++) {
|
||||
tmdev->sensor[i].tmdev = tmdev;
|
||||
|
@ -158,6 +144,9 @@ static int tsens_probe(struct platform_device *pdev)
|
|||
else
|
||||
tmdev->sensor[i].hw_id = i;
|
||||
}
|
||||
for (i = 0; i < REG_ARRAY_SIZE; i++) {
|
||||
tmdev->reg_offsets[i] = data->reg_offsets[i];
|
||||
}
|
||||
|
||||
if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp)
|
||||
return -EINVAL;
|
||||
|
|
|
@ -1,15 +1,8 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __QCOM_TSENS_H__
|
||||
#define __QCOM_TSENS_H__
|
||||
|
||||
|
@ -55,15 +48,23 @@ struct tsens_ops {
|
|||
int (*get_trend)(struct tsens_device *, int, enum thermal_trend *);
|
||||
};
|
||||
|
||||
enum reg_list {
|
||||
SROT_CTRL_OFFSET,
|
||||
|
||||
REG_ARRAY_SIZE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tsens_data - tsens instance specific data
|
||||
* @num_sensors: Max number of sensors supported by platform
|
||||
* @ops: operations the tsens instance supports
|
||||
* @hw_ids: Subset of sensors ids supported by platform, if not the first n
|
||||
* @reg_offsets: Register offsets for commonly used registers
|
||||
*/
|
||||
struct tsens_data {
|
||||
const u32 num_sensors;
|
||||
const struct tsens_ops *ops;
|
||||
const u16 reg_offsets[REG_ARRAY_SIZE];
|
||||
unsigned int *hw_ids;
|
||||
};
|
||||
|
||||
|
@ -76,8 +77,10 @@ struct tsens_context {
|
|||
struct tsens_device {
|
||||
struct device *dev;
|
||||
u32 num_sensors;
|
||||
struct regmap *map;
|
||||
struct regmap *tm_map;
|
||||
struct regmap *srot_map;
|
||||
u32 tm_offset;
|
||||
u16 reg_offsets[REG_ARRAY_SIZE];
|
||||
struct tsens_context ctx;
|
||||
const struct tsens_ops *ops;
|
||||
struct tsens_sensor sensor[0];
|
||||
|
|
|
@ -119,8 +119,8 @@ static int qoriq_tmu_get_sensor_id(void)
|
|||
if (sensor_specs.args_count >= 1) {
|
||||
id = sensor_specs.args[0];
|
||||
WARN(sensor_specs.args_count > 1,
|
||||
"%s: too many cells in sensor specifier %d\n",
|
||||
sensor_specs.np->name, sensor_specs.args_count);
|
||||
"%pOFn: too many cells in sensor specifier %d\n",
|
||||
sensor_specs.np, sensor_specs.args_count);
|
||||
} else {
|
||||
id = 0;
|
||||
}
|
||||
|
@ -294,6 +294,7 @@ static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
|
|||
|
||||
static const struct of_device_id qoriq_tmu_match[] = {
|
||||
{ .compatible = "fsl,qoriq-tmu", },
|
||||
{ .compatible = "fsl,imx8mq-tmu", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
|
||||
|
|
|
@ -318,9 +318,11 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
|
|||
}
|
||||
|
||||
static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
|
||||
{ .compatible = "renesas,r8a774a1-thermal", },
|
||||
{ .compatible = "renesas,r8a7795-thermal", },
|
||||
{ .compatible = "renesas,r8a7796-thermal", },
|
||||
{ .compatible = "renesas,r8a77965-thermal", },
|
||||
{ .compatible = "renesas,r8a77980-thermal", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids);
|
||||
|
|
|
@ -112,6 +112,10 @@ static const struct of_device_id rcar_thermal_dt_ids[] = {
|
|||
.compatible = "renesas,rcar-gen2-thermal",
|
||||
.data = &rcar_gen2_thermal,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,thermal-r8a77970",
|
||||
.data = &rcar_gen3_thermal,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,thermal-r8a77995",
|
||||
.data = &rcar_gen3_thermal,
|
||||
|
@ -434,7 +438,7 @@ static irqreturn_t rcar_thermal_irq(int irq, void *data)
|
|||
rcar_thermal_for_each_priv(priv, common) {
|
||||
if (rcar_thermal_had_changed(priv, status)) {
|
||||
rcar_thermal_irq_disable(priv);
|
||||
schedule_delayed_work(&priv->work,
|
||||
queue_delayed_work(system_freezable_wq, &priv->work,
|
||||
msecs_to_jiffies(300));
|
||||
}
|
||||
}
|
||||
|
@ -453,6 +457,7 @@ static int rcar_thermal_remove(struct platform_device *pdev)
|
|||
|
||||
rcar_thermal_for_each_priv(priv, common) {
|
||||
rcar_thermal_irq_disable(priv);
|
||||
cancel_delayed_work_sync(&priv->work);
|
||||
if (priv->chip->use_of_thermal)
|
||||
thermal_remove_hwmon_sysfs(priv->zone);
|
||||
else
|
||||
|
@ -492,7 +497,7 @@ static int rcar_thermal_probe(struct platform_device *pdev)
|
|||
pm_runtime_get_sync(dev);
|
||||
|
||||
for (i = 0; i < chip->nirqs; i++) {
|
||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, i);
|
||||
if (!irq)
|
||||
continue;
|
||||
if (!common->base) {
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
#
|
||||
# STMicroelectronics thermal drivers configuration
|
||||
#
|
||||
|
||||
config ST_THERMAL
|
||||
tristate "Thermal sensors on STMicroelectronics STi series of SoCs"
|
||||
help
|
||||
|
@ -10,3 +14,13 @@ config ST_THERMAL_SYSCFG
|
|||
config ST_THERMAL_MEMMAP
|
||||
select ST_THERMAL
|
||||
tristate "STi series memory mapped access based thermal sensors"
|
||||
|
||||
config STM32_THERMAL
|
||||
tristate "Thermal framework support on STMicroelectronics STM32 series of SoCs"
|
||||
depends on MACH_STM32MP157
|
||||
default y
|
||||
help
|
||||
Support for thermal framework on STMicroelectronics STM32 series of
|
||||
SoCs. This thermal driver allows to access to general thermal framework
|
||||
functionalities and to acces to SoC sensor functionalities. This
|
||||
configuration is fully dependent of MACH_STM32MP157.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
obj-$(CONFIG_ST_THERMAL) := st_thermal.o
|
||||
obj-$(CONFIG_ST_THERMAL_SYSCFG) += st_thermal_syscfg.o
|
||||
obj-$(CONFIG_ST_THERMAL_MEMMAP) += st_thermal_memmap.o
|
||||
obj-$(CONFIG_STM32_THERMAL) := stm_thermal.o
|
|
@ -0,0 +1,760 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
||||
* Author: David Hernandez Sanchez <david.hernandezsanchez@st.com> for
|
||||
* STMicroelectronics.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "../thermal_core.h"
|
||||
#include "../thermal_hwmon.h"
|
||||
|
||||
/* DTS register offsets */
|
||||
#define DTS_CFGR1_OFFSET 0x0
|
||||
#define DTS_T0VALR1_OFFSET 0x8
|
||||
#define DTS_RAMPVALR_OFFSET 0X10
|
||||
#define DTS_ITR1_OFFSET 0x14
|
||||
#define DTS_DR_OFFSET 0x1C
|
||||
#define DTS_SR_OFFSET 0x20
|
||||
#define DTS_ITENR_OFFSET 0x24
|
||||
#define DTS_CIFR_OFFSET 0x28
|
||||
|
||||
/* DTS_CFGR1 register mask definitions */
|
||||
#define HSREF_CLK_DIV_MASK GENMASK(30, 24)
|
||||
#define TS1_SMP_TIME_MASK GENMASK(19, 16)
|
||||
#define TS1_INTRIG_SEL_MASK GENMASK(11, 8)
|
||||
|
||||
/* DTS_T0VALR1 register mask definitions */
|
||||
#define TS1_T0_MASK GENMASK(17, 16)
|
||||
#define TS1_FMT0_MASK GENMASK(15, 0)
|
||||
|
||||
/* DTS_RAMPVALR register mask definitions */
|
||||
#define TS1_RAMP_COEFF_MASK GENMASK(15, 0)
|
||||
|
||||
/* DTS_ITR1 register mask definitions */
|
||||
#define TS1_HITTHD_MASK GENMASK(31, 16)
|
||||
#define TS1_LITTHD_MASK GENMASK(15, 0)
|
||||
|
||||
/* DTS_DR register mask definitions */
|
||||
#define TS1_MFREQ_MASK GENMASK(15, 0)
|
||||
|
||||
/* Less significant bit position definitions */
|
||||
#define TS1_T0_POS 16
|
||||
#define TS1_SMP_TIME_POS 16
|
||||
#define TS1_HITTHD_POS 16
|
||||
#define HSREF_CLK_DIV_POS 24
|
||||
|
||||
/* DTS_CFGR1 bit definitions */
|
||||
#define TS1_EN BIT(0)
|
||||
#define TS1_START BIT(4)
|
||||
#define REFCLK_SEL BIT(20)
|
||||
#define REFCLK_LSE REFCLK_SEL
|
||||
#define Q_MEAS_OPT BIT(21)
|
||||
#define CALIBRATION_CONTROL Q_MEAS_OPT
|
||||
|
||||
/* DTS_SR bit definitions */
|
||||
#define TS_RDY BIT(15)
|
||||
/* Bit definitions below are common for DTS_SR, DTS_ITENR and DTS_CIFR */
|
||||
#define HIGH_THRESHOLD BIT(2)
|
||||
#define LOW_THRESHOLD BIT(1)
|
||||
|
||||
/* Constants */
|
||||
#define ADJUST 100
|
||||
#define ONE_MHZ 1000000
|
||||
#define POLL_TIMEOUT 5000
|
||||
#define STARTUP_TIME 40
|
||||
#define TS1_T0_VAL0 30
|
||||
#define TS1_T0_VAL1 130
|
||||
#define NO_HW_TRIG 0
|
||||
|
||||
/* The Thermal Framework expects millidegrees */
|
||||
#define mcelsius(temp) ((temp) * 1000)
|
||||
|
||||
/* The Sensor expects oC degrees */
|
||||
#define celsius(temp) ((temp) / 1000)
|
||||
|
||||
struct stm_thermal_sensor {
|
||||
struct device *dev;
|
||||
struct thermal_zone_device *th_dev;
|
||||
enum thermal_device_mode mode;
|
||||
struct clk *clk;
|
||||
int high_temp;
|
||||
int low_temp;
|
||||
int temp_critical;
|
||||
int temp_passive;
|
||||
unsigned int low_temp_enabled;
|
||||
int num_trips;
|
||||
int irq;
|
||||
unsigned int irq_enabled;
|
||||
void __iomem *base;
|
||||
int t0, fmt0, ramp_coeff;
|
||||
};
|
||||
|
||||
static irqreturn_t stm_thermal_alarm_irq(int irq, void *sdata)
|
||||
{
|
||||
struct stm_thermal_sensor *sensor = sdata;
|
||||
|
||||
disable_irq_nosync(irq);
|
||||
sensor->irq_enabled = false;
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t stm_thermal_alarm_irq_thread(int irq, void *sdata)
|
||||
{
|
||||
u32 value;
|
||||
struct stm_thermal_sensor *sensor = sdata;
|
||||
|
||||
/* read IT reason in SR and clear flags */
|
||||
value = readl_relaxed(sensor->base + DTS_SR_OFFSET);
|
||||
|
||||
if ((value & LOW_THRESHOLD) == LOW_THRESHOLD)
|
||||
writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
|
||||
|
||||
if ((value & HIGH_THRESHOLD) == HIGH_THRESHOLD)
|
||||
writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
|
||||
|
||||
thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int stm_sensor_power_on(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
int ret;
|
||||
u32 value;
|
||||
|
||||
/* Enable sensor */
|
||||
value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET);
|
||||
value |= TS1_EN;
|
||||
writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET);
|
||||
|
||||
/*
|
||||
* The DTS block can be enabled by setting TSx_EN bit in
|
||||
* DTS_CFGRx register. It requires a startup time of
|
||||
* 40μs. Use 5 ms as arbitrary timeout.
|
||||
*/
|
||||
ret = readl_poll_timeout(sensor->base + DTS_SR_OFFSET,
|
||||
value, (value & TS_RDY),
|
||||
STARTUP_TIME, POLL_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Start continuous measuring */
|
||||
value = readl_relaxed(sensor->base +
|
||||
DTS_CFGR1_OFFSET);
|
||||
value |= TS1_START;
|
||||
writel_relaxed(value, sensor->base +
|
||||
DTS_CFGR1_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm_sensor_power_off(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
/* Stop measuring */
|
||||
value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET);
|
||||
value &= ~TS1_START;
|
||||
writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET);
|
||||
|
||||
/* Ensure stop is taken into account */
|
||||
usleep_range(STARTUP_TIME, POLL_TIMEOUT);
|
||||
|
||||
/* Disable sensor */
|
||||
value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET);
|
||||
value &= ~TS1_EN;
|
||||
writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET);
|
||||
|
||||
/* Ensure disable is taken into account */
|
||||
return readl_poll_timeout(sensor->base + DTS_SR_OFFSET, value,
|
||||
!(value & TS_RDY),
|
||||
STARTUP_TIME, POLL_TIMEOUT);
|
||||
}
|
||||
|
||||
static int stm_thermal_calibration(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
u32 value, clk_freq;
|
||||
u32 prescaler;
|
||||
|
||||
/* Figure out prescaler value for PCLK during calibration */
|
||||
clk_freq = clk_get_rate(sensor->clk);
|
||||
if (!clk_freq)
|
||||
return -EINVAL;
|
||||
|
||||
prescaler = 0;
|
||||
clk_freq /= ONE_MHZ;
|
||||
if (clk_freq) {
|
||||
while (prescaler <= clk_freq)
|
||||
prescaler++;
|
||||
}
|
||||
|
||||
value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET);
|
||||
|
||||
/* Clear prescaler */
|
||||
value &= ~HSREF_CLK_DIV_MASK;
|
||||
|
||||
/* Set prescaler. pclk_freq/prescaler < 1MHz */
|
||||
value |= (prescaler << HSREF_CLK_DIV_POS);
|
||||
|
||||
/* Select PCLK as reference clock */
|
||||
value &= ~REFCLK_SEL;
|
||||
|
||||
/* Set maximal sampling time for better precision */
|
||||
value |= TS1_SMP_TIME_MASK;
|
||||
|
||||
/* Measure with calibration */
|
||||
value &= ~CALIBRATION_CONTROL;
|
||||
|
||||
/* select trigger */
|
||||
value &= ~TS1_INTRIG_SEL_MASK;
|
||||
value |= NO_HW_TRIG;
|
||||
|
||||
writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fill in DTS structure with factory sensor values */
|
||||
static int stm_thermal_read_factory_settings(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
/* Retrieve engineering calibration temperature */
|
||||
sensor->t0 = readl_relaxed(sensor->base + DTS_T0VALR1_OFFSET) &
|
||||
TS1_T0_MASK;
|
||||
if (!sensor->t0)
|
||||
sensor->t0 = TS1_T0_VAL0;
|
||||
else
|
||||
sensor->t0 = TS1_T0_VAL1;
|
||||
|
||||
/* Retrieve fmt0 and put it on Hz */
|
||||
sensor->fmt0 = ADJUST * readl_relaxed(sensor->base + DTS_T0VALR1_OFFSET)
|
||||
& TS1_FMT0_MASK;
|
||||
|
||||
/* Retrieve ramp coefficient */
|
||||
sensor->ramp_coeff = readl_relaxed(sensor->base + DTS_RAMPVALR_OFFSET) &
|
||||
TS1_RAMP_COEFF_MASK;
|
||||
|
||||
if (!sensor->fmt0 || !sensor->ramp_coeff) {
|
||||
dev_err(sensor->dev, "%s: wrong setting\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(sensor->dev, "%s: T0 = %doC, FMT0 = %dHz, RAMP_COEFF = %dHz/oC",
|
||||
__func__, sensor->t0, sensor->fmt0, sensor->ramp_coeff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm_thermal_calculate_threshold(struct stm_thermal_sensor *sensor,
|
||||
int temp, u32 *th)
|
||||
{
|
||||
int freqM;
|
||||
u32 sampling_time;
|
||||
|
||||
/* Retrieve the number of periods to sample */
|
||||
sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) &
|
||||
TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS;
|
||||
|
||||
/* Figure out the CLK_PTAT frequency for a given temperature */
|
||||
freqM = ((temp - sensor->t0) * sensor->ramp_coeff)
|
||||
+ sensor->fmt0;
|
||||
|
||||
dev_dbg(sensor->dev, "%s: freqM for threshold = %d Hz",
|
||||
__func__, freqM);
|
||||
|
||||
/* Figure out the threshold sample number */
|
||||
*th = clk_get_rate(sensor->clk);
|
||||
if (!*th)
|
||||
return -EINVAL;
|
||||
|
||||
*th = *th / freqM;
|
||||
|
||||
*th *= sampling_time;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm_thermal_set_threshold(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
u32 value, th;
|
||||
int ret;
|
||||
|
||||
value = readl_relaxed(sensor->base + DTS_ITR1_OFFSET);
|
||||
|
||||
/* Erase threshold content */
|
||||
value &= ~(TS1_LITTHD_MASK | TS1_HITTHD_MASK);
|
||||
|
||||
/* Retrieve the sample threshold number th for a given temperature */
|
||||
ret = stm_thermal_calculate_threshold(sensor, sensor->high_temp, &th);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
value |= th & TS1_LITTHD_MASK;
|
||||
|
||||
if (sensor->low_temp_enabled) {
|
||||
/* Retrieve the sample threshold */
|
||||
ret = stm_thermal_calculate_threshold(sensor, sensor->low_temp,
|
||||
&th);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
value |= (TS1_HITTHD_MASK & (th << TS1_HITTHD_POS));
|
||||
}
|
||||
|
||||
/* Write value on the Low interrupt threshold */
|
||||
writel_relaxed(value, sensor->base + DTS_ITR1_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Disable temperature interrupt */
|
||||
static int stm_disable_irq(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
/* Disable IT generation for low and high thresholds */
|
||||
value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET);
|
||||
writel_relaxed(value & ~(LOW_THRESHOLD | HIGH_THRESHOLD),
|
||||
sensor->base + DTS_ITENR_OFFSET);
|
||||
|
||||
dev_dbg(sensor->dev, "%s: IT disabled on sensor side", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Enable temperature interrupt */
|
||||
static int stm_enable_irq(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
/*
|
||||
* Code below enables High temperature threshold using a low threshold
|
||||
* sampling value
|
||||
*/
|
||||
|
||||
/* Make sure LOW_THRESHOLD IT is clear before enabling */
|
||||
writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
|
||||
|
||||
/* Enable IT generation for low threshold */
|
||||
value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET);
|
||||
value |= LOW_THRESHOLD;
|
||||
|
||||
/* Enable the low temperature threshold if needed */
|
||||
if (sensor->low_temp_enabled) {
|
||||
/* Make sure HIGH_THRESHOLD IT is clear before enabling */
|
||||
writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
|
||||
|
||||
/* Enable IT generation for high threshold */
|
||||
value |= HIGH_THRESHOLD;
|
||||
}
|
||||
|
||||
/* Enable thresholds */
|
||||
writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET);
|
||||
|
||||
dev_dbg(sensor->dev, "%s: IT enabled on sensor side", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm_thermal_update_threshold(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
int ret;
|
||||
|
||||
sensor->mode = THERMAL_DEVICE_DISABLED;
|
||||
|
||||
ret = stm_sensor_power_off(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = stm_disable_irq(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = stm_thermal_set_threshold(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = stm_enable_irq(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = stm_sensor_power_on(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sensor->mode = THERMAL_DEVICE_ENABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Callback to get temperature from HW */
|
||||
static int stm_thermal_get_temp(void *data, int *temp)
|
||||
{
|
||||
struct stm_thermal_sensor *sensor = data;
|
||||
u32 sampling_time;
|
||||
int freqM, ret;
|
||||
|
||||
if (sensor->mode != THERMAL_DEVICE_ENABLED)
|
||||
return -EAGAIN;
|
||||
|
||||
/* Retrieve the number of samples */
|
||||
ret = readl_poll_timeout(sensor->base + DTS_DR_OFFSET, freqM,
|
||||
(freqM & TS1_MFREQ_MASK), STARTUP_TIME,
|
||||
POLL_TIMEOUT);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!freqM)
|
||||
return -ENODATA;
|
||||
|
||||
/* Retrieve the number of periods sampled */
|
||||
sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) &
|
||||
TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS;
|
||||
|
||||
/* Figure out the number of samples per period */
|
||||
freqM /= sampling_time;
|
||||
|
||||
/* Figure out the CLK_PTAT frequency */
|
||||
freqM = clk_get_rate(sensor->clk) / freqM;
|
||||
if (!freqM)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(sensor->dev, "%s: freqM=%d\n", __func__, freqM);
|
||||
|
||||
/* Figure out the temperature in mili celsius */
|
||||
*temp = mcelsius(sensor->t0 + ((freqM - sensor->fmt0) /
|
||||
sensor->ramp_coeff));
|
||||
|
||||
dev_dbg(sensor->dev, "%s: temperature = %d millicelsius",
|
||||
__func__, *temp);
|
||||
|
||||
/* Update thresholds */
|
||||
if (sensor->num_trips > 1) {
|
||||
/* Update alarm threshold value to next higher trip point */
|
||||
if (sensor->high_temp == sensor->temp_passive &&
|
||||
celsius(*temp) >= sensor->temp_passive) {
|
||||
sensor->high_temp = sensor->temp_critical;
|
||||
sensor->low_temp = sensor->temp_passive;
|
||||
sensor->low_temp_enabled = true;
|
||||
ret = stm_thermal_update_threshold(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (sensor->high_temp == sensor->temp_critical &&
|
||||
celsius(*temp) < sensor->temp_passive) {
|
||||
sensor->high_temp = sensor->temp_passive;
|
||||
sensor->low_temp_enabled = false;
|
||||
ret = stm_thermal_update_threshold(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-enable alarm IRQ if temperature below critical
|
||||
* temperature
|
||||
*/
|
||||
if (!sensor->irq_enabled &&
|
||||
(celsius(*temp) < sensor->temp_critical)) {
|
||||
sensor->irq_enabled = true;
|
||||
enable_irq(sensor->irq);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Registers DTS irq to be visible by GIC */
|
||||
static int stm_register_irq(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
struct device *dev = sensor->dev;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int ret;
|
||||
|
||||
sensor->irq = platform_get_irq(pdev, 0);
|
||||
if (sensor->irq < 0) {
|
||||
dev_err(dev, "%s: Unable to find IRQ\n", __func__);
|
||||
return sensor->irq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, sensor->irq,
|
||||
stm_thermal_alarm_irq,
|
||||
stm_thermal_alarm_irq_thread,
|
||||
IRQF_ONESHOT,
|
||||
dev->driver->name, sensor);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: Failed to register IRQ %d\n", __func__,
|
||||
sensor->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sensor->irq_enabled = true;
|
||||
|
||||
dev_dbg(dev, "%s: thermal IRQ registered", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm_thermal_sensor_off(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = stm_sensor_power_off(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable_unprepare(sensor->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm_thermal_prepare(struct stm_thermal_sensor *sensor)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = sensor->dev;
|
||||
|
||||
ret = clk_prepare_enable(sensor->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = stm_thermal_calibration(sensor);
|
||||
if (ret)
|
||||
goto thermal_unprepare;
|
||||
|
||||
/* Set threshold(s) for IRQ */
|
||||
ret = stm_thermal_set_threshold(sensor);
|
||||
if (ret)
|
||||
goto thermal_unprepare;
|
||||
|
||||
ret = stm_enable_irq(sensor);
|
||||
if (ret)
|
||||
goto thermal_unprepare;
|
||||
|
||||
ret = stm_sensor_power_on(sensor);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: failed to power on sensor\n", __func__);
|
||||
goto irq_disable;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
irq_disable:
|
||||
stm_disable_irq(sensor);
|
||||
|
||||
thermal_unprepare:
|
||||
clk_disable_unprepare(sensor->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int stm_thermal_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev);
|
||||
|
||||
ret = stm_thermal_sensor_off(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sensor->mode = THERMAL_DEVICE_DISABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm_thermal_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev);
|
||||
|
||||
ret = stm_thermal_prepare(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sensor->mode = THERMAL_DEVICE_ENABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops, stm_thermal_suspend, stm_thermal_resume);
|
||||
|
||||
static const struct thermal_zone_of_device_ops stm_tz_ops = {
|
||||
.get_temp = stm_thermal_get_temp,
|
||||
};
|
||||
|
||||
static const struct of_device_id stm_thermal_of_match[] = {
|
||||
{ .compatible = "st,stm32-thermal"},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm_thermal_of_match);
|
||||
|
||||
static int stm_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct stm_thermal_sensor *sensor;
|
||||
struct resource *res;
|
||||
const struct thermal_trip *trip;
|
||||
void __iomem *base;
|
||||
int ret, i;
|
||||
|
||||
if (!pdev->dev.of_node) {
|
||||
dev_err(&pdev->dev, "%s: device tree node not found\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sensor = devm_kzalloc(&pdev->dev, sizeof(*sensor), GFP_KERNEL);
|
||||
if (!sensor)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, sensor);
|
||||
|
||||
sensor->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
/* Populate sensor */
|
||||
sensor->base = base;
|
||||
|
||||
ret = stm_thermal_read_factory_settings(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sensor->clk = devm_clk_get(&pdev->dev, "pclk");
|
||||
if (IS_ERR(sensor->clk)) {
|
||||
dev_err(&pdev->dev, "%s: failed to fetch PCLK clock\n",
|
||||
__func__);
|
||||
return PTR_ERR(sensor->clk);
|
||||
}
|
||||
|
||||
/* Register IRQ into GIC */
|
||||
ret = stm_register_irq(sensor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sensor->th_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0,
|
||||
sensor,
|
||||
&stm_tz_ops);
|
||||
|
||||
if (IS_ERR(sensor->th_dev)) {
|
||||
dev_err(&pdev->dev, "%s: thermal zone sensor registering KO\n",
|
||||
__func__);
|
||||
ret = PTR_ERR(sensor->th_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!sensor->th_dev->ops->get_crit_temp) {
|
||||
/* Critical point must be provided */
|
||||
ret = -EINVAL;
|
||||
goto err_tz;
|
||||
}
|
||||
|
||||
ret = sensor->th_dev->ops->get_crit_temp(sensor->th_dev,
|
||||
&sensor->temp_critical);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Not able to read critical_temp: %d\n", ret);
|
||||
goto err_tz;
|
||||
}
|
||||
|
||||
sensor->temp_critical = celsius(sensor->temp_critical);
|
||||
|
||||
/* Set thresholds for IRQ */
|
||||
sensor->high_temp = sensor->temp_critical;
|
||||
|
||||
trip = of_thermal_get_trip_points(sensor->th_dev);
|
||||
sensor->num_trips = of_thermal_get_ntrips(sensor->th_dev);
|
||||
|
||||
/* Find out passive temperature if it exists */
|
||||
for (i = (sensor->num_trips - 1); i >= 0; i--) {
|
||||
if (trip[i].type == THERMAL_TRIP_PASSIVE) {
|
||||
sensor->temp_passive = celsius(trip[i].temperature);
|
||||
/* Update high temperature threshold */
|
||||
sensor->high_temp = sensor->temp_passive;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure low_temp_enabled flag is disabled.
|
||||
* By disabling low_temp_enabled, low threshold IT will not be
|
||||
* configured neither enabled because it is not needed as high
|
||||
* threshold is set on the lowest temperature trip point after
|
||||
* probe.
|
||||
*/
|
||||
sensor->low_temp_enabled = false;
|
||||
|
||||
/* Configure and enable HW sensor */
|
||||
ret = stm_thermal_prepare(sensor);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Not able to enable sensor: %d\n", ret);
|
||||
goto err_tz;
|
||||
}
|
||||
|
||||
/*
|
||||
* Thermal_zone doesn't enable hwmon as default,
|
||||
* enable it here
|
||||
*/
|
||||
sensor->th_dev->tzp->no_hwmon = false;
|
||||
ret = thermal_add_hwmon_sysfs(sensor->th_dev);
|
||||
if (ret)
|
||||
goto err_tz;
|
||||
|
||||
sensor->mode = THERMAL_DEVICE_ENABLED;
|
||||
|
||||
dev_info(&pdev->dev, "%s: Driver initialized successfully\n",
|
||||
__func__);
|
||||
|
||||
return 0;
|
||||
|
||||
err_tz:
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev, sensor->th_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm_thermal_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev);
|
||||
|
||||
stm_thermal_sensor_off(sensor);
|
||||
thermal_remove_hwmon_sysfs(sensor->th_dev);
|
||||
thermal_zone_of_sensor_unregister(&pdev->dev, sensor->th_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver stm_thermal_driver = {
|
||||
.driver = {
|
||||
.name = "stm_thermal",
|
||||
.pm = &stm_thermal_pm_ops,
|
||||
.of_match_table = stm_thermal_of_match,
|
||||
},
|
||||
.probe = stm_thermal_probe,
|
||||
.remove = stm_thermal_remove,
|
||||
};
|
||||
module_platform_driver(stm_thermal_driver);
|
||||
|
||||
MODULE_DESCRIPTION("STMicroelectronics STM32 Thermal Sensor Driver");
|
||||
MODULE_AUTHOR("David Hernandez Sanchez <david.hernandezsanchez@st.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:stm_thermal");
|
Loading…
Reference in New Issue