Commit 47cf09e0 authored by Niklas Söderlund's avatar Niklas Söderlund Committed by Daniel Lezcano
Browse files

thermal/drivers/rcar_gen3_thermal: Add support for hardware trip points



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: default avatarNiklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20210804091818.2196806-2-niklas.soderlund+renesas@ragnatech.se
parent a414a08a
Loading
Loading
Loading
Loading
+99 −4
Original line number Diff line number Diff line
@@ -190,10 +190,64 @@ static int rcar_gen3_thermal_get_temp(void *devdata, int *temp)
	return 0;
}

static const struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = {
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 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 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 */ }
@@ -210,6 +264,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 (tsc->zone->ops->set_trips)
		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);
@@ -235,6 +292,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 (tsc->zone->ops->set_trips)
		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;
@@ -303,6 +363,34 @@ 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_optional(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)
{
	struct rcar_gen3_thermal_priv *priv;
@@ -326,6 +414,9 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)

	platform_set_drvdata(pdev, priv);

	if (rcar_gen3_thermal_request_irqs(priv, pdev))
		rcar_gen3_tz_of_ops.set_trips = NULL;

	pm_runtime_enable(dev);
	pm_runtime_get_sync(dev);

@@ -351,9 +442,6 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)

		priv->tscs[i] = tsc;

		priv->thermal_init(tsc);
		rcar_gen3_thermal_calc_coefs(tsc, ptat, thcodes[i], *ths_tj_1);

		zone = devm_thermal_zone_of_sensor_register(dev, i, tsc,
							    &rcar_gen3_tz_of_ops);
		if (IS_ERR(zone)) {
@@ -363,6 +451,9 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
		}
		tsc->zone = zone;

		priv->thermal_init(tsc);
		rcar_gen3_thermal_calc_coefs(tsc, ptat, thcodes[i], *ths_tj_1);

		tsc->zone->tzp->no_hwmon = false;
		ret = thermal_add_hwmon_sysfs(tsc->zone);
		if (ret)
@@ -401,8 +492,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);
		if (zone->ops->set_trips)
			rcar_gen3_thermal_set_trips(tsc, zone->prev_low_trip,
						    zone->prev_high_trip);
	}

	return 0;