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:
Linus Torvalds 2018-10-26 12:04:29 -07:00
commit 0ef7791e2b
27 changed files with 1366 additions and 304 deletions

View File

@ -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";
};
};

View File

@ -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
---------- -----

View File

@ -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.

View File

@ -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):

View File

@ -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>;
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);
if (ret) {
dev_err(dev, "failed to register thermal sensor: %d\n", ret);
return ret;
}
for (i = 0; i < data->nr_sensors; i++) {
struct hisi_thermal_sensor *sensor = &data->sensor[i];
ret = data->enable_sensor(data);
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);
ret = hisi_thermal_register_sensor(pdev, sensor);
if (ret) {
dev_err(dev, "failed to register thermal sensor: %d\n",
ret);
return ret;
}
}
hisi_thermal_toggle_sensor(&data->sensor, true);
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;
}
hisi_thermal_toggle_sensor(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;
hisi_thermal_toggle_sensor(sensor, false);
for (i = 0; i < data->nr_sensors; i++) {
struct hisi_thermal_sensor *sensor = &data->sensor[i];
data->disable_sensor(data);
hisi_thermal_toggle_sensor(sensor, false);
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

View File

@ -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)

View File

@ -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,25 +203,31 @@ 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) {
int ret;
for (j = 0; j < tbp->count; j++) {
tcbp = tbp->tcbp + j;
ret = thermal_zone_bind_cooling_device(thermal,
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;
if (ret)
return ret;
}
}
}
@ -221,22 +238,28 @@ 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) {
int ret;
for (j = 0; j < tbp->count; j++) {
tcbp = tbp->tcbp + j;
ret = thermal_zone_unbind_cooling_device(thermal,
tbp->trip_id, cdev);
if (ret)
return ret;
if (tcbp->cooling_device == cdev->np) {
int ret;
ret = thermal_zone_unbind_cooling_device(thermal,
tbp->trip_id, cdev);
if (ret)
return ret;
}
}
}
@ -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;
if (cooling_spec.args_count >= 2) { /* at least min and max */
__tbp->min = cooling_spec.args[0];
__tbp->max = cooling_spec.args[1];
} else {
pr_err("wrong reference to cooling device, missing limits\n");
__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 */
__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);

View File

@ -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, &reg);
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;
}

View File

@ -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 },
};

View File

@ -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, &reg);
ret = regmap_read(tmdev->tm_map, CNTL_ADDR, &reg);
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, &reg_cntl);
ret = regmap_read(tmdev->tm_map, CNTL_ADDR, &reg_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);

View File

@ -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 },
};

View File

@ -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;
}

View File

@ -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 },
};

View File

@ -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;

View File

@ -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];

View File

@ -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);

View File

@ -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);

View File

@ -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,8 +438,8 @@ 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,
msecs_to_jiffies(300));
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) {

View File

@ -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.

View File

@ -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

View File

@ -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");