@@ -81,6 +81,7 @@ struct equation_coefs {
struct rcar_gen3_thermal_info {
int ths_tj_1;
+ bool have_irq;
};
struct rcar_gen3_thermal_tsc {
@@ -95,7 +96,8 @@ struct rcar_gen3_thermal_priv {
const struct rcar_gen3_thermal_info *info;
struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM];
unsigned int num_tscs;
- void (*thermal_init)(struct rcar_gen3_thermal_tsc *tsc);
+ void (*thermal_init)(struct rcar_gen3_thermal_priv *priv,
+ struct rcar_gen3_thermal_tsc *tsc);
};
static inline u32 rcar_gen3_thermal_read(struct rcar_gen3_thermal_tsc *tsc,
@@ -195,16 +197,75 @@ static int rcar_gen3_thermal_get_temp(void *devdata, int *temp)
return 0;
}
+static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc,
+ int mcelsius)
+{
+ int celsius, val;
+
+ celsius = DIV_ROUND_CLOSEST(mcelsius, 1000);
+ if (celsius <= INT_FIXPT(tsc->tj_t))
+ val = celsius * tsc->coef.a1 + tsc->coef.b1;
+ else
+ val = celsius * tsc->coef.a2 + tsc->coef.b2;
+
+ return INT_FIXPT(val);
+}
+
+static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high)
+{
+ struct rcar_gen3_thermal_tsc *tsc = devdata;
+ u32 irqmsk = 0;
+
+ if (low != -INT_MAX) {
+ irqmsk |= IRQ_TEMPD1;
+ rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1,
+ rcar_gen3_thermal_mcelsius_to_temp(tsc, low));
+ }
+
+ if (high != INT_MAX) {
+ irqmsk |= IRQ_TEMP2;
+ rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2,
+ rcar_gen3_thermal_mcelsius_to_temp(tsc, high));
+ }
+
+ rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, irqmsk);
+
+ return 0;
+}
+
static const struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = {
.get_temp = rcar_gen3_thermal_get_temp,
+ .set_trips = rcar_gen3_thermal_set_trips,
};
+static const struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops_no_irq = {
+ .get_temp = rcar_gen3_thermal_get_temp,
+};
+
+static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data)
+{
+ struct rcar_gen3_thermal_priv *priv = data;
+ unsigned int i;
+ u32 status;
+
+ for (i = 0; i < priv->num_tscs; i++) {
+ status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR);
+ rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0);
+ if (status)
+ thermal_zone_device_update(priv->tscs[i]->zone,
+ THERMAL_EVENT_UNSPECIFIED);
+ }
+
+ return IRQ_HANDLED;
+}
+
static const struct soc_device_attribute r8a7795es1[] = {
{ .soc_id = "r8a7795", .revision = "ES1.*" },
{ /* sentinel */ }
};
-static void rcar_gen3_thermal_init_r8a7795es1(struct rcar_gen3_thermal_tsc *tsc)
+static void rcar_gen3_thermal_init_r8a7795es1(struct rcar_gen3_thermal_priv *priv,
+ struct rcar_gen3_thermal_tsc *tsc)
{
rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_THBGR);
rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, 0x0);
@@ -215,6 +276,9 @@ static void rcar_gen3_thermal_init_r8a7795es1(struct rcar_gen3_thermal_tsc *tsc)
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F);
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0);
+ if (priv->info->have_irq)
+ rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN,
+ IRQ_TEMPD1 | IRQ_TEMP2);
rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR,
CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN);
@@ -228,7 +292,8 @@ static void rcar_gen3_thermal_init_r8a7795es1(struct rcar_gen3_thermal_tsc *tsc)
usleep_range(1000, 2000);
}
-static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
+static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_priv *priv,
+ struct rcar_gen3_thermal_tsc *tsc)
{
u32 reg_val;
@@ -240,6 +305,9 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0);
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0);
+ if (priv->info->have_irq)
+ rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN,
+ IRQ_TEMPD1 | IRQ_TEMP2);
reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR);
reg_val |= THCTR_THSST;
@@ -250,10 +318,16 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
static const struct rcar_gen3_thermal_info rcar_gen3_thermal_gen3 = {
.ths_tj_1 = 126,
+ .have_irq = true,
};
static const struct rcar_gen3_thermal_info rcar_gen3_thermal_m3w = {
.ths_tj_1 = 116,
+ .have_irq = true,
+};
+
+static const struct rcar_gen3_thermal_info rcar_gen3_thermal_v3u = {
+ .ths_tj_1 = 126,
};
static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
@@ -291,7 +365,7 @@ static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
},
{
.compatible = "renesas,r8a779a0-thermal",
- .data = &rcar_gen3_thermal_gen3,
+ .data = &rcar_gen3_thermal_v3u,
},
{},
};
@@ -314,8 +388,37 @@ static void rcar_gen3_hwmon_action(void *data)
thermal_remove_hwmon_sysfs(zone);
}
+static int rcar_gen3_thermal_request_irqs(struct rcar_gen3_thermal_priv *priv,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ unsigned int i;
+ char *irqname;
+ int ret, irq;
+
+ for (i = 0; i < 2; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ return irq;
+
+ irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:ch%d",
+ dev_name(dev), i);
+ if (!irqname)
+ return -ENOMEM;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ rcar_gen3_thermal_irq,
+ IRQF_ONESHOT, irqname, priv);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int rcar_gen3_thermal_probe(struct platform_device *pdev)
{
+ const struct thermal_zone_of_device_ops *tz_of_ops;
struct rcar_gen3_thermal_priv *priv;
struct device *dev = &pdev->dev;
struct resource *res;
@@ -338,6 +441,15 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
+ tz_of_ops = &rcar_gen3_tz_of_ops_no_irq;
+ if (priv->info->have_irq) {
+ ret = rcar_gen3_thermal_request_irqs(priv, pdev);
+ if (ret)
+ return ret;
+
+ tz_of_ops = &rcar_gen3_tz_of_ops;
+ }
+
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
@@ -363,12 +475,12 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
priv->tscs[i] = tsc;
- priv->thermal_init(tsc);
+ priv->thermal_init(priv, tsc);
rcar_gen3_thermal_calc_coefs(tsc, ptat, thcodes[i],
priv->info->ths_tj_1);
zone = devm_thermal_zone_of_sensor_register(dev, i, tsc,
- &rcar_gen3_tz_of_ops);
+ tz_of_ops);
if (IS_ERR(zone)) {
dev_err(dev, "Can't register thermal zone\n");
ret = PTR_ERR(zone);
@@ -414,8 +526,12 @@ static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)
for (i = 0; i < priv->num_tscs; i++) {
struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
+ struct thermal_zone_device *zone = tsc->zone;
- priv->thermal_init(tsc);
+ priv->thermal_init(priv, tsc);
+ if (priv->info->have_irq)
+ rcar_gen3_thermal_set_trips(tsc, zone->prev_low_trip,
+ zone->prev_high_trip);
}
return 0;
All supported hardware except V3U is capable of generating interrupts to the CPU when the temperature go below or above a set value. Use this to implement support for the set_trip() feature of the thermal core on supported hardware. The V3U have its interrupts routed to the ECM module and therefore can not be used to implement set_trip() as the driver can't be made aware of when the interrupt triggers. Each TSC is capable of tracking up-to three different temperatures while only two are needed to implement the tracking of the thermal window. Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> --- drivers/thermal/rcar_gen3_thermal.c | 130 ++++++++++++++++++++++++++-- 1 file changed, 123 insertions(+), 7 deletions(-)