19d617949SNiklas Söderlund // SPDX-License-Identifier: GPL-2.0
29d617949SNiklas Söderlund /*
39d617949SNiklas Söderlund * R-Car Gen3 THS thermal sensor driver
49d617949SNiklas Söderlund * Based on rcar_thermal.c and work from Hien Dang and Khiem Nguyen.
59d617949SNiklas Söderlund *
69d617949SNiklas Söderlund * Copyright (C) 2016 Renesas Electronics Corporation.
79d617949SNiklas Söderlund * Copyright (C) 2016 Sang Engineering
89d617949SNiklas Söderlund */
99d617949SNiklas Söderlund #include <linux/delay.h>
109d617949SNiklas Söderlund #include <linux/err.h>
119d617949SNiklas Söderlund #include <linux/interrupt.h>
129d617949SNiklas Söderlund #include <linux/io.h>
139d617949SNiklas Söderlund #include <linux/module.h>
149d617949SNiklas Söderlund #include <linux/of.h>
159d617949SNiklas Söderlund #include <linux/platform_device.h>
169d617949SNiklas Söderlund #include <linux/pm_runtime.h>
179d617949SNiklas Söderlund #include <linux/thermal.h>
189d617949SNiklas Söderlund
199d617949SNiklas Söderlund #include "../thermal_hwmon.h"
209d617949SNiklas Söderlund
219d617949SNiklas Söderlund /* Register offsets */
229d617949SNiklas Söderlund #define REG_GEN3_IRQSTR 0x04
239d617949SNiklas Söderlund #define REG_GEN3_IRQMSK 0x08
249d617949SNiklas Söderlund #define REG_GEN3_IRQCTL 0x0C
259d617949SNiklas Söderlund #define REG_GEN3_IRQEN 0x10
269d617949SNiklas Söderlund #define REG_GEN3_IRQTEMP1 0x14
279d617949SNiklas Söderlund #define REG_GEN3_IRQTEMP2 0x18
289d617949SNiklas Söderlund #define REG_GEN3_IRQTEMP3 0x1C
299d617949SNiklas Söderlund #define REG_GEN3_THCTR 0x20
309d617949SNiklas Söderlund #define REG_GEN3_TEMP 0x28
319d617949SNiklas Söderlund #define REG_GEN3_THCODE1 0x50
329d617949SNiklas Söderlund #define REG_GEN3_THCODE2 0x54
339d617949SNiklas Söderlund #define REG_GEN3_THCODE3 0x58
349d617949SNiklas Söderlund #define REG_GEN3_PTAT1 0x5c
359d617949SNiklas Söderlund #define REG_GEN3_PTAT2 0x60
369d617949SNiklas Söderlund #define REG_GEN3_PTAT3 0x64
379d617949SNiklas Söderlund #define REG_GEN3_THSCP 0x68
389d617949SNiklas Söderlund #define REG_GEN4_THSFMON00 0x180
399d617949SNiklas Söderlund #define REG_GEN4_THSFMON01 0x184
409d617949SNiklas Söderlund #define REG_GEN4_THSFMON02 0x188
419d617949SNiklas Söderlund #define REG_GEN4_THSFMON15 0x1BC
429d617949SNiklas Söderlund #define REG_GEN4_THSFMON16 0x1C0
439d617949SNiklas Söderlund #define REG_GEN4_THSFMON17 0x1C4
449d617949SNiklas Söderlund
459d617949SNiklas Söderlund /* IRQ{STR,MSK,EN} bits */
469d617949SNiklas Söderlund #define IRQ_TEMP1 BIT(0)
479d617949SNiklas Söderlund #define IRQ_TEMP2 BIT(1)
489d617949SNiklas Söderlund #define IRQ_TEMP3 BIT(2)
499d617949SNiklas Söderlund #define IRQ_TEMPD1 BIT(3)
509d617949SNiklas Söderlund #define IRQ_TEMPD2 BIT(4)
519d617949SNiklas Söderlund #define IRQ_TEMPD3 BIT(5)
529d617949SNiklas Söderlund
539d617949SNiklas Söderlund /* THCTR bits */
549d617949SNiklas Söderlund #define THCTR_PONM BIT(6)
559d617949SNiklas Söderlund #define THCTR_THSST BIT(0)
569d617949SNiklas Söderlund
579d617949SNiklas Söderlund /* THSCP bits */
589d617949SNiklas Söderlund #define THSCP_COR_PARA_VLD (BIT(15) | BIT(14))
599d617949SNiklas Söderlund
609d617949SNiklas Söderlund #define CTEMP_MASK 0xFFF
619d617949SNiklas Söderlund
629d617949SNiklas Söderlund #define MCELSIUS(temp) ((temp) * 1000)
639d617949SNiklas Söderlund #define GEN3_FUSE_MASK 0xFFF
649d617949SNiklas Söderlund #define GEN4_FUSE_MASK 0xFFF
659d617949SNiklas Söderlund
669d617949SNiklas Söderlund #define TSC_MAX_NUM 5
679d617949SNiklas Söderlund
689d617949SNiklas Söderlund struct rcar_gen3_thermal_priv;
699d617949SNiklas Söderlund
709d617949SNiklas Söderlund struct rcar_thermal_info {
719d617949SNiklas Söderlund int scale;
729d617949SNiklas Söderlund int adj_below;
739d617949SNiklas Söderlund int adj_above;
749d617949SNiklas Söderlund void (*read_fuses)(struct rcar_gen3_thermal_priv *priv);
759d617949SNiklas Söderlund };
769d617949SNiklas Söderlund
779d617949SNiklas Söderlund struct equation_set_coef {
789d617949SNiklas Söderlund int a;
799d617949SNiklas Söderlund int b;
809d617949SNiklas Söderlund };
819d617949SNiklas Söderlund
829d617949SNiklas Söderlund struct rcar_gen3_thermal_tsc {
839d617949SNiklas Söderlund struct rcar_gen3_thermal_priv *priv;
849d617949SNiklas Söderlund void __iomem *base;
859d617949SNiklas Söderlund struct thermal_zone_device *zone;
869d617949SNiklas Söderlund /* Different coefficients are used depending on a threshold. */
879d617949SNiklas Söderlund struct {
889d617949SNiklas Söderlund struct equation_set_coef below;
899d617949SNiklas Söderlund struct equation_set_coef above;
909d617949SNiklas Söderlund } coef;
919d617949SNiklas Söderlund int thcode[3];
929d617949SNiklas Söderlund };
939d617949SNiklas Söderlund
949d617949SNiklas Söderlund struct rcar_gen3_thermal_priv {
959d617949SNiklas Söderlund struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM];
969d617949SNiklas Söderlund struct thermal_zone_device_ops ops;
979d617949SNiklas Söderlund unsigned int num_tscs;
989d617949SNiklas Söderlund int ptat[3];
999d617949SNiklas Söderlund int tj_t;
1009d617949SNiklas Söderlund const struct rcar_thermal_info *info;
1019d617949SNiklas Söderlund };
1029d617949SNiklas Söderlund
rcar_gen3_thermal_read(struct rcar_gen3_thermal_tsc * tsc,u32 reg)1039d617949SNiklas Söderlund static inline u32 rcar_gen3_thermal_read(struct rcar_gen3_thermal_tsc *tsc,
1049d617949SNiklas Söderlund u32 reg)
1059d617949SNiklas Söderlund {
1069d617949SNiklas Söderlund return ioread32(tsc->base + reg);
1079d617949SNiklas Söderlund }
1089d617949SNiklas Söderlund
rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc * tsc,u32 reg,u32 data)1099d617949SNiklas Söderlund static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc,
1109d617949SNiklas Söderlund u32 reg, u32 data)
1119d617949SNiklas Söderlund {
1129d617949SNiklas Söderlund iowrite32(data, tsc->base + reg);
1139d617949SNiklas Söderlund }
1149d617949SNiklas Söderlund
1159d617949SNiklas Söderlund /*
1169d617949SNiklas Söderlund * Linear approximation for temperature
1179d617949SNiklas Söderlund *
1189d617949SNiklas Söderlund * [temp] = ((thadj - [reg]) * a) / b + adj
1199d617949SNiklas Söderlund * [reg] = thadj - ([temp] - adj) * b / a
1209d617949SNiklas Söderlund *
1219d617949SNiklas Söderlund * The constants a and b are calculated using two triplets of int values PTAT
1229d617949SNiklas Söderlund * and THCODE. PTAT and THCODE can either be read from hardware or use hard
1239d617949SNiklas Söderlund * coded values from the driver. The formula to calculate a and b are taken from
1249d617949SNiklas Söderlund * the datasheet. Different calculations are needed for a and b depending on
1259d617949SNiklas Söderlund * if the input variables ([temp] or [reg]) are above or below a threshold. The
1269d617949SNiklas Söderlund * threshold is also calculated from PTAT and THCODE using formulas from the
1279d617949SNiklas Söderlund * datasheet.
1289d617949SNiklas Söderlund *
1299d617949SNiklas Söderlund * The constant thadj is one of the THCODE values, which one to use depends on
1309d617949SNiklas Söderlund * the threshold and input value.
1319d617949SNiklas Söderlund *
1329d617949SNiklas Söderlund * The constants adj is taken verbatim from the datasheet. Two values exists,
1339d617949SNiklas Söderlund * which one to use depends on the input value and the calculated threshold.
1349d617949SNiklas Söderlund * Furthermore different SoC models supported by the driver have different sets
1359d617949SNiklas Söderlund * of values. The values for each model are stored in the device match data.
1369d617949SNiklas Söderlund */
1379d617949SNiklas Söderlund
rcar_gen3_thermal_shared_coefs(struct rcar_gen3_thermal_priv * priv)1389d617949SNiklas Söderlund static void rcar_gen3_thermal_shared_coefs(struct rcar_gen3_thermal_priv *priv)
1399d617949SNiklas Söderlund {
1409d617949SNiklas Söderlund priv->tj_t =
1419d617949SNiklas Söderlund DIV_ROUND_CLOSEST((priv->ptat[1] - priv->ptat[2]) * priv->info->scale,
1429d617949SNiklas Söderlund priv->ptat[0] - priv->ptat[2])
1439d617949SNiklas Söderlund + priv->info->adj_below;
1449d617949SNiklas Söderlund }
rcar_gen3_thermal_tsc_coefs(struct rcar_gen3_thermal_priv * priv,struct rcar_gen3_thermal_tsc * tsc)1459d617949SNiklas Söderlund static void rcar_gen3_thermal_tsc_coefs(struct rcar_gen3_thermal_priv *priv,
1469d617949SNiklas Söderlund struct rcar_gen3_thermal_tsc *tsc)
1479d617949SNiklas Söderlund {
1489d617949SNiklas Söderlund tsc->coef.below.a = priv->info->scale * (priv->ptat[2] - priv->ptat[1]);
1499d617949SNiklas Söderlund tsc->coef.above.a = priv->info->scale * (priv->ptat[0] - priv->ptat[1]);
1509d617949SNiklas Söderlund
1519d617949SNiklas Söderlund tsc->coef.below.b = (priv->ptat[2] - priv->ptat[0]) * (tsc->thcode[2] - tsc->thcode[1]);
1529d617949SNiklas Söderlund tsc->coef.above.b = (priv->ptat[0] - priv->ptat[2]) * (tsc->thcode[1] - tsc->thcode[0]);
1539d617949SNiklas Söderlund }
1549d617949SNiklas Söderlund
rcar_gen3_thermal_get_temp(struct thermal_zone_device * tz,int * temp)1559d617949SNiklas Söderlund static int rcar_gen3_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
1569d617949SNiklas Söderlund {
1579d617949SNiklas Söderlund struct rcar_gen3_thermal_tsc *tsc = thermal_zone_device_priv(tz);
1589d617949SNiklas Söderlund struct rcar_gen3_thermal_priv *priv = tsc->priv;
1599d617949SNiklas Söderlund const struct equation_set_coef *coef;
1609d617949SNiklas Söderlund int adj, decicelsius, reg, thcode;
1619d617949SNiklas Söderlund
1629d617949SNiklas Söderlund /* Read register and convert to mili Celsius */
1639d617949SNiklas Söderlund reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK;
1649d617949SNiklas Söderlund
1659d617949SNiklas Söderlund if (reg < tsc->thcode[1]) {
1669d617949SNiklas Söderlund adj = priv->info->adj_below;
1679d617949SNiklas Söderlund coef = &tsc->coef.below;
1689d617949SNiklas Söderlund thcode = tsc->thcode[2];
1699d617949SNiklas Söderlund } else {
1709d617949SNiklas Söderlund adj = priv->info->adj_above;
1719d617949SNiklas Söderlund coef = &tsc->coef.above;
1729d617949SNiklas Söderlund thcode = tsc->thcode[0];
1739d617949SNiklas Söderlund }
1749d617949SNiklas Söderlund
1759d617949SNiklas Söderlund /*
1769d617949SNiklas Söderlund * The dividend can't be grown as it might overflow, instead shorten the
1779d617949SNiklas Söderlund * divisor to convert to decidegree Celsius. If we convert after the
1789d617949SNiklas Söderlund * division precision is lost as we will scale up from whole degrees
1799d617949SNiklas Söderlund * Celsius.
1809d617949SNiklas Söderlund */
1819d617949SNiklas Söderlund decicelsius = DIV_ROUND_CLOSEST(coef->a * (thcode - reg), coef->b / 10);
1829d617949SNiklas Söderlund
1839d617949SNiklas Söderlund /* Guaranteed operating range is -40C to 125C. */
1849d617949SNiklas Söderlund
1859d617949SNiklas Söderlund /* Reporting is done in millidegree Celsius */
1869d617949SNiklas Söderlund *temp = decicelsius * 100 + adj * 1000;
1879d617949SNiklas Söderlund
1889d617949SNiklas Söderlund return 0;
1899d617949SNiklas Söderlund }
1909d617949SNiklas Söderlund
rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc * tsc,int mcelsius)1919d617949SNiklas Söderlund static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc,
1929d617949SNiklas Söderlund int mcelsius)
1939d617949SNiklas Söderlund {
1949d617949SNiklas Söderlund struct rcar_gen3_thermal_priv *priv = tsc->priv;
1959d617949SNiklas Söderlund const struct equation_set_coef *coef;
1969d617949SNiklas Söderlund int adj, celsius, thcode;
1979d617949SNiklas Söderlund
1989d617949SNiklas Söderlund celsius = DIV_ROUND_CLOSEST(mcelsius, 1000);
1999d617949SNiklas Söderlund if (celsius < priv->tj_t) {
2009d617949SNiklas Söderlund coef = &tsc->coef.below;
2019d617949SNiklas Söderlund adj = priv->info->adj_below;
2029d617949SNiklas Söderlund thcode = tsc->thcode[2];
2039d617949SNiklas Söderlund } else {
2049d617949SNiklas Söderlund coef = &tsc->coef.above;
2059d617949SNiklas Söderlund adj = priv->info->adj_above;
2069d617949SNiklas Söderlund thcode = tsc->thcode[0];
2079d617949SNiklas Söderlund }
2089d617949SNiklas Söderlund
2099d617949SNiklas Söderlund return thcode - DIV_ROUND_CLOSEST((celsius - adj) * coef->b, coef->a);
2109d617949SNiklas Söderlund }
2119d617949SNiklas Söderlund
rcar_gen3_thermal_set_trips(struct thermal_zone_device * tz,int low,int high)2129d617949SNiklas Söderlund static int rcar_gen3_thermal_set_trips(struct thermal_zone_device *tz, int low, int high)
2139d617949SNiklas Söderlund {
2149d617949SNiklas Söderlund struct rcar_gen3_thermal_tsc *tsc = thermal_zone_device_priv(tz);
2159d617949SNiklas Söderlund u32 irqmsk = 0;
2169d617949SNiklas Söderlund
2179d617949SNiklas Söderlund if (low != -INT_MAX) {
2189d617949SNiklas Söderlund irqmsk |= IRQ_TEMPD1;
2199d617949SNiklas Söderlund rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1,
2209d617949SNiklas Söderlund rcar_gen3_thermal_mcelsius_to_temp(tsc, low));
2219d617949SNiklas Söderlund }
2229d617949SNiklas Söderlund
2239d617949SNiklas Söderlund if (high != INT_MAX) {
2249d617949SNiklas Söderlund irqmsk |= IRQ_TEMP2;
2259d617949SNiklas Söderlund rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2,
2269d617949SNiklas Söderlund rcar_gen3_thermal_mcelsius_to_temp(tsc, high));
2279d617949SNiklas Söderlund }
2289d617949SNiklas Söderlund
2299d617949SNiklas Söderlund rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, irqmsk);
2309d617949SNiklas Söderlund
2319d617949SNiklas Söderlund return 0;
2329d617949SNiklas Söderlund }
2339d617949SNiklas Söderlund
2349d617949SNiklas Söderlund static const struct thermal_zone_device_ops rcar_gen3_tz_of_ops = {
2359d617949SNiklas Söderlund .get_temp = rcar_gen3_thermal_get_temp,
2369d617949SNiklas Söderlund .set_trips = rcar_gen3_thermal_set_trips,
2379d617949SNiklas Söderlund };
2389d617949SNiklas Söderlund
rcar_gen3_thermal_irq(int irq,void * data)2399d617949SNiklas Söderlund static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data)
2409d617949SNiklas Söderlund {
2419d617949SNiklas Söderlund struct rcar_gen3_thermal_priv *priv = data;
2429d617949SNiklas Söderlund unsigned int i;
2439d617949SNiklas Söderlund u32 status;
2449d617949SNiklas Söderlund
2459d617949SNiklas Söderlund for (i = 0; i < priv->num_tscs; i++) {
2469d617949SNiklas Söderlund status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR);
2479d617949SNiklas Söderlund rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0);
2489d617949SNiklas Söderlund if (status && priv->tscs[i]->zone)
2499d617949SNiklas Söderlund thermal_zone_device_update(priv->tscs[i]->zone,
2509d617949SNiklas Söderlund THERMAL_EVENT_UNSPECIFIED);
2519d617949SNiklas Söderlund }
2529d617949SNiklas Söderlund
2539d617949SNiklas Söderlund return IRQ_HANDLED;
2549d617949SNiklas Söderlund }
2559d617949SNiklas Söderlund
rcar_gen3_thermal_read_fuses_gen3(struct rcar_gen3_thermal_priv * priv)2569d617949SNiklas Söderlund static void rcar_gen3_thermal_read_fuses_gen3(struct rcar_gen3_thermal_priv *priv)
2579d617949SNiklas Söderlund {
2589d617949SNiklas Söderlund unsigned int i;
2599d617949SNiklas Söderlund
2609d617949SNiklas Söderlund /*
2619d617949SNiklas Söderlund * Set the pseudo calibration points with fused values.
2629d617949SNiklas Söderlund * PTAT is shared between all TSCs but only fused for the first
2639d617949SNiklas Söderlund * TSC while THCODEs are fused for each TSC.
2649d617949SNiklas Söderlund */
2659d617949SNiklas Söderlund priv->ptat[0] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_PTAT1) &
2669d617949SNiklas Söderlund GEN3_FUSE_MASK;
2679d617949SNiklas Söderlund priv->ptat[1] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_PTAT2) &
2689d617949SNiklas Söderlund GEN3_FUSE_MASK;
2699d617949SNiklas Söderlund priv->ptat[2] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_PTAT3) &
2709d617949SNiklas Söderlund GEN3_FUSE_MASK;
2719d617949SNiklas Söderlund
2729d617949SNiklas Söderlund for (i = 0; i < priv->num_tscs; i++) {
2739d617949SNiklas Söderlund struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
2749d617949SNiklas Söderlund
2759d617949SNiklas Söderlund tsc->thcode[0] = rcar_gen3_thermal_read(tsc, REG_GEN3_THCODE1) &
2769d617949SNiklas Söderlund GEN3_FUSE_MASK;
2779d617949SNiklas Söderlund tsc->thcode[1] = rcar_gen3_thermal_read(tsc, REG_GEN3_THCODE2) &
2789d617949SNiklas Söderlund GEN3_FUSE_MASK;
2799d617949SNiklas Söderlund tsc->thcode[2] = rcar_gen3_thermal_read(tsc, REG_GEN3_THCODE3) &
2809d617949SNiklas Söderlund GEN3_FUSE_MASK;
2819d617949SNiklas Söderlund }
2829d617949SNiklas Söderlund }
2839d617949SNiklas Söderlund
rcar_gen3_thermal_read_fuses_gen4(struct rcar_gen3_thermal_priv * priv)2849d617949SNiklas Söderlund static void rcar_gen3_thermal_read_fuses_gen4(struct rcar_gen3_thermal_priv *priv)
2859d617949SNiklas Söderlund {
2869d617949SNiklas Söderlund unsigned int i;
2879d617949SNiklas Söderlund
2889d617949SNiklas Söderlund /*
2899d617949SNiklas Söderlund * Set the pseudo calibration points with fused values.
2909d617949SNiklas Söderlund * PTAT is shared between all TSCs but only fused for the first
2919d617949SNiklas Söderlund * TSC while THCODEs are fused for each TSC.
2929d617949SNiklas Söderlund */
2939d617949SNiklas Söderlund priv->ptat[0] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN4_THSFMON16) &
2949d617949SNiklas Söderlund GEN4_FUSE_MASK;
2959d617949SNiklas Söderlund priv->ptat[1] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN4_THSFMON17) &
2969d617949SNiklas Söderlund GEN4_FUSE_MASK;
2979d617949SNiklas Söderlund priv->ptat[2] = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN4_THSFMON15) &
2989d617949SNiklas Söderlund GEN4_FUSE_MASK;
2999d617949SNiklas Söderlund
3009d617949SNiklas Söderlund for (i = 0; i < priv->num_tscs; i++) {
3019d617949SNiklas Söderlund struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
3029d617949SNiklas Söderlund
3039d617949SNiklas Söderlund tsc->thcode[0] = rcar_gen3_thermal_read(tsc, REG_GEN4_THSFMON01) &
3049d617949SNiklas Söderlund GEN4_FUSE_MASK;
3059d617949SNiklas Söderlund tsc->thcode[1] = rcar_gen3_thermal_read(tsc, REG_GEN4_THSFMON02) &
3069d617949SNiklas Söderlund GEN4_FUSE_MASK;
3079d617949SNiklas Söderlund tsc->thcode[2] = rcar_gen3_thermal_read(tsc, REG_GEN4_THSFMON00) &
3089d617949SNiklas Söderlund GEN4_FUSE_MASK;
3099d617949SNiklas Söderlund }
3109d617949SNiklas Söderlund }
3119d617949SNiklas Söderlund
rcar_gen3_thermal_read_fuses(struct rcar_gen3_thermal_priv * priv)3129d617949SNiklas Söderlund static bool rcar_gen3_thermal_read_fuses(struct rcar_gen3_thermal_priv *priv)
3139d617949SNiklas Söderlund {
3149d617949SNiklas Söderlund unsigned int i;
3159d617949SNiklas Söderlund u32 thscp;
3169d617949SNiklas Söderlund
3179d617949SNiklas Söderlund /* If fuses are not set, fallback to pseudo values. */
3189d617949SNiklas Söderlund thscp = rcar_gen3_thermal_read(priv->tscs[0], REG_GEN3_THSCP);
3199d617949SNiklas Söderlund if (!priv->info->read_fuses ||
3209d617949SNiklas Söderlund (thscp & THSCP_COR_PARA_VLD) != THSCP_COR_PARA_VLD) {
3219d617949SNiklas Söderlund /* Default THCODE values in case FUSEs are not set. */
3229d617949SNiklas Söderlund static const int thcodes[TSC_MAX_NUM][3] = {
3239d617949SNiklas Söderlund { 3397, 2800, 2221 },
3249d617949SNiklas Söderlund { 3393, 2795, 2216 },
3259d617949SNiklas Söderlund { 3389, 2805, 2237 },
3269d617949SNiklas Söderlund { 3415, 2694, 2195 },
3279d617949SNiklas Söderlund { 3356, 2724, 2244 },
3289d617949SNiklas Söderlund };
3299d617949SNiklas Söderlund
3309d617949SNiklas Söderlund priv->ptat[0] = 2631;
3319d617949SNiklas Söderlund priv->ptat[1] = 1509;
3329d617949SNiklas Söderlund priv->ptat[2] = 435;
3339d617949SNiklas Söderlund
3349d617949SNiklas Söderlund for (i = 0; i < priv->num_tscs; i++) {
3359d617949SNiklas Söderlund struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
3369d617949SNiklas Söderlund
3379d617949SNiklas Söderlund tsc->thcode[0] = thcodes[i][0];
3389d617949SNiklas Söderlund tsc->thcode[1] = thcodes[i][1];
3399d617949SNiklas Söderlund tsc->thcode[2] = thcodes[i][2];
3409d617949SNiklas Söderlund }
3419d617949SNiklas Söderlund
3429d617949SNiklas Söderlund return false;
3439d617949SNiklas Söderlund }
3449d617949SNiklas Söderlund
3459d617949SNiklas Söderlund priv->info->read_fuses(priv);
3469d617949SNiklas Söderlund return true;
3479d617949SNiklas Söderlund }
3489d617949SNiklas Söderlund
rcar_gen3_thermal_init(struct rcar_gen3_thermal_priv * priv,struct rcar_gen3_thermal_tsc * tsc)3499d617949SNiklas Söderlund static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_priv *priv,
3509d617949SNiklas Söderlund struct rcar_gen3_thermal_tsc *tsc)
3519d617949SNiklas Söderlund {
3529d617949SNiklas Söderlund u32 reg_val;
3539d617949SNiklas Söderlund
3549d617949SNiklas Söderlund reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR);
3559d617949SNiklas Söderlund reg_val &= ~THCTR_PONM;
3569d617949SNiklas Söderlund rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val);
3579d617949SNiklas Söderlund
3589d617949SNiklas Söderlund usleep_range(1000, 2000);
3599d617949SNiklas Söderlund
3609d617949SNiklas Söderlund rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0);
3619d617949SNiklas Söderlund rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0);
3629d617949SNiklas Söderlund if (priv->ops.set_trips)
3639d617949SNiklas Söderlund rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN,
3649d617949SNiklas Söderlund IRQ_TEMPD1 | IRQ_TEMP2);
3659d617949SNiklas Söderlund
3669d617949SNiklas Söderlund reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR);
3679d617949SNiklas Söderlund reg_val |= THCTR_THSST;
3689d617949SNiklas Söderlund rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val);
3699d617949SNiklas Söderlund
3709d617949SNiklas Söderlund usleep_range(1000, 2000);
3719d617949SNiklas Söderlund }
3729d617949SNiklas Söderlund
3739d617949SNiklas Söderlund static const struct rcar_thermal_info rcar_m3w_thermal_info = {
3749d617949SNiklas Söderlund .scale = 157,
3759d617949SNiklas Söderlund .adj_below = -41,
3769d617949SNiklas Söderlund .adj_above = 116,
3779d617949SNiklas Söderlund .read_fuses = rcar_gen3_thermal_read_fuses_gen3,
3789d617949SNiklas Söderlund };
3799d617949SNiklas Söderlund
3809d617949SNiklas Söderlund static const struct rcar_thermal_info rcar_gen3_thermal_info = {
3819d617949SNiklas Söderlund .scale = 167,
3829d617949SNiklas Söderlund .adj_below = -41,
3839d617949SNiklas Söderlund .adj_above = 126,
3849d617949SNiklas Söderlund .read_fuses = rcar_gen3_thermal_read_fuses_gen3,
3859d617949SNiklas Söderlund };
3869d617949SNiklas Söderlund
3879d617949SNiklas Söderlund static const struct rcar_thermal_info rcar_gen4_thermal_info = {
3889d617949SNiklas Söderlund .scale = 167,
3899d617949SNiklas Söderlund .adj_below = -41,
3909d617949SNiklas Söderlund .adj_above = 126,
3919d617949SNiklas Söderlund .read_fuses = rcar_gen3_thermal_read_fuses_gen4,
3929d617949SNiklas Söderlund };
3939d617949SNiklas Söderlund
3949d617949SNiklas Söderlund static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
3959d617949SNiklas Söderlund {
3969d617949SNiklas Söderlund .compatible = "renesas,r8a774a1-thermal",
3979d617949SNiklas Söderlund .data = &rcar_m3w_thermal_info,
3989d617949SNiklas Söderlund },
3999d617949SNiklas Söderlund {
4009d617949SNiklas Söderlund .compatible = "renesas,r8a774b1-thermal",
4019d617949SNiklas Söderlund .data = &rcar_gen3_thermal_info,
4029d617949SNiklas Söderlund },
4039d617949SNiklas Söderlund {
4049d617949SNiklas Söderlund .compatible = "renesas,r8a774e1-thermal",
4059d617949SNiklas Söderlund .data = &rcar_gen3_thermal_info,
4069d617949SNiklas Söderlund },
4079d617949SNiklas Söderlund {
4089d617949SNiklas Söderlund .compatible = "renesas,r8a7795-thermal",
4099d617949SNiklas Söderlund .data = &rcar_gen3_thermal_info,
4109d617949SNiklas Söderlund },
4119d617949SNiklas Söderlund {
4129d617949SNiklas Söderlund .compatible = "renesas,r8a7796-thermal",
4139d617949SNiklas Söderlund .data = &rcar_m3w_thermal_info,
4149d617949SNiklas Söderlund },
4159d617949SNiklas Söderlund {
4169d617949SNiklas Söderlund .compatible = "renesas,r8a77961-thermal",
4179d617949SNiklas Söderlund .data = &rcar_m3w_thermal_info,
4189d617949SNiklas Söderlund },
4199d617949SNiklas Söderlund {
4209d617949SNiklas Söderlund .compatible = "renesas,r8a77965-thermal",
4219d617949SNiklas Söderlund .data = &rcar_gen3_thermal_info,
4229d617949SNiklas Söderlund },
4239d617949SNiklas Söderlund {
4249d617949SNiklas Söderlund .compatible = "renesas,r8a77980-thermal",
4259d617949SNiklas Söderlund .data = &rcar_gen3_thermal_info,
4269d617949SNiklas Söderlund },
4279d617949SNiklas Söderlund {
4289d617949SNiklas Söderlund .compatible = "renesas,r8a779a0-thermal",
4299d617949SNiklas Söderlund .data = &rcar_gen3_thermal_info,
4309d617949SNiklas Söderlund },
4319d617949SNiklas Söderlund {
4329d617949SNiklas Söderlund .compatible = "renesas,r8a779f0-thermal",
4339d617949SNiklas Söderlund .data = &rcar_gen4_thermal_info,
4349d617949SNiklas Söderlund },
4359d617949SNiklas Söderlund {
4369d617949SNiklas Söderlund .compatible = "renesas,r8a779g0-thermal",
4379d617949SNiklas Söderlund .data = &rcar_gen4_thermal_info,
4389d617949SNiklas Söderlund },
4399d617949SNiklas Söderlund {
4409d617949SNiklas Söderlund .compatible = "renesas,r8a779h0-thermal",
4419d617949SNiklas Söderlund .data = &rcar_gen4_thermal_info,
4429d617949SNiklas Söderlund },
4439d617949SNiklas Söderlund {},
4449d617949SNiklas Söderlund };
4459d617949SNiklas Söderlund MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids);
4469d617949SNiklas Söderlund
rcar_gen3_thermal_remove(struct platform_device * pdev)4479d617949SNiklas Söderlund static void rcar_gen3_thermal_remove(struct platform_device *pdev)
4489d617949SNiklas Söderlund {
4499d617949SNiklas Söderlund struct device *dev = &pdev->dev;
4509d617949SNiklas Söderlund
4519d617949SNiklas Söderlund pm_runtime_put(dev);
4529d617949SNiklas Söderlund pm_runtime_disable(dev);
4539d617949SNiklas Söderlund }
4549d617949SNiklas Söderlund
rcar_gen3_hwmon_action(void * data)4559d617949SNiklas Söderlund static void rcar_gen3_hwmon_action(void *data)
4569d617949SNiklas Söderlund {
4579d617949SNiklas Söderlund struct thermal_zone_device *zone = data;
4589d617949SNiklas Söderlund
4599d617949SNiklas Söderlund thermal_remove_hwmon_sysfs(zone);
4609d617949SNiklas Söderlund }
4619d617949SNiklas Söderlund
rcar_gen3_thermal_request_irqs(struct rcar_gen3_thermal_priv * priv,struct platform_device * pdev)4629d617949SNiklas Söderlund static int rcar_gen3_thermal_request_irqs(struct rcar_gen3_thermal_priv *priv,
4639d617949SNiklas Söderlund struct platform_device *pdev)
4649d617949SNiklas Söderlund {
4659d617949SNiklas Söderlund struct device *dev = &pdev->dev;
4669d617949SNiklas Söderlund unsigned int i;
4679d617949SNiklas Söderlund char *irqname;
4689d617949SNiklas Söderlund int ret, irq;
4699d617949SNiklas Söderlund
4709d617949SNiklas Söderlund for (i = 0; i < 2; i++) {
4719d617949SNiklas Söderlund irq = platform_get_irq_optional(pdev, i);
4729d617949SNiklas Söderlund if (irq < 0)
4739d617949SNiklas Söderlund return irq;
4749d617949SNiklas Söderlund
4759d617949SNiklas Söderlund irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:ch%d",
4769d617949SNiklas Söderlund dev_name(dev), i);
4779d617949SNiklas Söderlund if (!irqname)
4789d617949SNiklas Söderlund return -ENOMEM;
4799d617949SNiklas Söderlund
4809d617949SNiklas Söderlund ret = devm_request_threaded_irq(dev, irq, NULL,
4819d617949SNiklas Söderlund rcar_gen3_thermal_irq,
4829d617949SNiklas Söderlund IRQF_ONESHOT, irqname, priv);
4839d617949SNiklas Söderlund if (ret)
4849d617949SNiklas Söderlund return ret;
4859d617949SNiklas Söderlund }
4869d617949SNiklas Söderlund
4879d617949SNiklas Söderlund return 0;
4889d617949SNiklas Söderlund }
4899d617949SNiklas Söderlund
rcar_gen3_thermal_probe(struct platform_device * pdev)4909d617949SNiklas Söderlund static int rcar_gen3_thermal_probe(struct platform_device *pdev)
4919d617949SNiklas Söderlund {
4929d617949SNiklas Söderlund struct rcar_gen3_thermal_priv *priv;
4939d617949SNiklas Söderlund struct device *dev = &pdev->dev;
4949d617949SNiklas Söderlund struct resource *res;
4959d617949SNiklas Söderlund struct thermal_zone_device *zone;
4969d617949SNiklas Söderlund unsigned int i;
4979d617949SNiklas Söderlund int ret;
4989d617949SNiklas Söderlund
4999d617949SNiklas Söderlund priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
5009d617949SNiklas Söderlund if (!priv)
5019d617949SNiklas Söderlund return -ENOMEM;
5029d617949SNiklas Söderlund
5039d617949SNiklas Söderlund priv->ops = rcar_gen3_tz_of_ops;
5049d617949SNiklas Söderlund
5059d617949SNiklas Söderlund priv->info = of_device_get_match_data(dev);
5069d617949SNiklas Söderlund platform_set_drvdata(pdev, priv);
5079d617949SNiklas Söderlund
5089d617949SNiklas Söderlund if (rcar_gen3_thermal_request_irqs(priv, pdev))
5099d617949SNiklas Söderlund priv->ops.set_trips = NULL;
5109d617949SNiklas Söderlund
5119d617949SNiklas Söderlund pm_runtime_enable(dev);
5129d617949SNiklas Söderlund pm_runtime_get_sync(dev);
5139d617949SNiklas Söderlund
5149d617949SNiklas Söderlund for (i = 0; i < TSC_MAX_NUM; i++) {
5159d617949SNiklas Söderlund struct rcar_gen3_thermal_tsc *tsc;
5169d617949SNiklas Söderlund
5179d617949SNiklas Söderlund res = platform_get_resource(pdev, IORESOURCE_MEM, i);
5189d617949SNiklas Söderlund if (!res)
5199d617949SNiklas Söderlund break;
5209d617949SNiklas Söderlund
5219d617949SNiklas Söderlund tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL);
5229d617949SNiklas Söderlund if (!tsc) {
5239d617949SNiklas Söderlund ret = -ENOMEM;
5249d617949SNiklas Söderlund goto error_unregister;
5259d617949SNiklas Söderlund }
5269d617949SNiklas Söderlund
5279d617949SNiklas Söderlund tsc->priv = priv;
5289d617949SNiklas Söderlund tsc->base = devm_ioremap_resource(dev, res);
5299d617949SNiklas Söderlund if (IS_ERR(tsc->base)) {
5309d617949SNiklas Söderlund ret = PTR_ERR(tsc->base);
5319d617949SNiklas Söderlund goto error_unregister;
5329d617949SNiklas Söderlund }
5339d617949SNiklas Söderlund
5349d617949SNiklas Söderlund priv->tscs[i] = tsc;
5359d617949SNiklas Söderlund }
5369d617949SNiklas Söderlund
5379d617949SNiklas Söderlund priv->num_tscs = i;
5389d617949SNiklas Söderlund
5399d617949SNiklas Söderlund if (!rcar_gen3_thermal_read_fuses(priv))
5409d617949SNiklas Söderlund dev_info(dev, "No calibration values fused, fallback to driver values\n");
5419d617949SNiklas Söderlund
5429d617949SNiklas Söderlund rcar_gen3_thermal_shared_coefs(priv);
5439d617949SNiklas Söderlund
5449d617949SNiklas Söderlund for (i = 0; i < priv->num_tscs; i++) {
5459d617949SNiklas Söderlund struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
5469d617949SNiklas Söderlund
5479d617949SNiklas Söderlund rcar_gen3_thermal_init(priv, tsc);
5489d617949SNiklas Söderlund rcar_gen3_thermal_tsc_coefs(priv, tsc);
5499d617949SNiklas Söderlund
5509d617949SNiklas Söderlund zone = devm_thermal_of_zone_register(dev, i, tsc, &priv->ops);
5519d617949SNiklas Söderlund if (IS_ERR(zone)) {
5529d617949SNiklas Söderlund dev_err(dev, "Sensor %u: Can't register thermal zone\n", i);
5539d617949SNiklas Söderlund ret = PTR_ERR(zone);
5549d617949SNiklas Söderlund goto error_unregister;
5559d617949SNiklas Söderlund }
5569d617949SNiklas Söderlund tsc->zone = zone;
5579d617949SNiklas Söderlund
5589d617949SNiklas Söderlund ret = thermal_add_hwmon_sysfs(tsc->zone);
5599d617949SNiklas Söderlund if (ret)
5609d617949SNiklas Söderlund goto error_unregister;
5619d617949SNiklas Söderlund
5629d617949SNiklas Söderlund ret = devm_add_action_or_reset(dev, rcar_gen3_hwmon_action, zone);
5639d617949SNiklas Söderlund if (ret)
5649d617949SNiklas Söderlund goto error_unregister;
5659d617949SNiklas Söderlund
566*79f194ddSRafael J. Wysocki dev_info(dev, "Sensor %u: Loaded\n", i);
5679d617949SNiklas Söderlund }
5689d617949SNiklas Söderlund
5699d617949SNiklas Söderlund if (!priv->num_tscs) {
5709d617949SNiklas Söderlund ret = -ENODEV;
5719d617949SNiklas Söderlund goto error_unregister;
5729d617949SNiklas Söderlund }
5739d617949SNiklas Söderlund
5749d617949SNiklas Söderlund return 0;
5759d617949SNiklas Söderlund
5769d617949SNiklas Söderlund error_unregister:
5779d617949SNiklas Söderlund rcar_gen3_thermal_remove(pdev);
5789d617949SNiklas Söderlund
5799d617949SNiklas Söderlund return ret;
5809d617949SNiklas Söderlund }
5819d617949SNiklas Söderlund
rcar_gen3_thermal_resume(struct device * dev)5829d617949SNiklas Söderlund static int __maybe_unused rcar_gen3_thermal_resume(struct device *dev)
5839d617949SNiklas Söderlund {
5849d617949SNiklas Söderlund struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev);
5859d617949SNiklas Söderlund unsigned int i;
5869d617949SNiklas Söderlund
5879d617949SNiklas Söderlund for (i = 0; i < priv->num_tscs; i++) {
5889d617949SNiklas Söderlund struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i];
5899d617949SNiklas Söderlund
5909d617949SNiklas Söderlund rcar_gen3_thermal_init(priv, tsc);
5919d617949SNiklas Söderlund }
5929d617949SNiklas Söderlund
5939d617949SNiklas Söderlund return 0;
5949d617949SNiklas Söderlund }
5959d617949SNiklas Söderlund
5969d617949SNiklas Söderlund static SIMPLE_DEV_PM_OPS(rcar_gen3_thermal_pm_ops, NULL,
5979d617949SNiklas Söderlund rcar_gen3_thermal_resume);
5989d617949SNiklas Söderlund
5999d617949SNiklas Söderlund static struct platform_driver rcar_gen3_thermal_driver = {
6009d617949SNiklas Söderlund .driver = {
6019d617949SNiklas Söderlund .name = "rcar_gen3_thermal",
6029d617949SNiklas Söderlund .pm = &rcar_gen3_thermal_pm_ops,
6039d617949SNiklas Söderlund .of_match_table = rcar_gen3_thermal_dt_ids,
6049d617949SNiklas Söderlund },
6059d617949SNiklas Söderlund .probe = rcar_gen3_thermal_probe,
6069d617949SNiklas Söderlund .remove_new = rcar_gen3_thermal_remove,
6079d617949SNiklas Söderlund };
6089d617949SNiklas Söderlund module_platform_driver(rcar_gen3_thermal_driver);
6099d617949SNiklas Söderlund
6109d617949SNiklas Söderlund MODULE_LICENSE("GPL v2");
6119d617949SNiklas Söderlund MODULE_DESCRIPTION("R-Car Gen3 THS thermal sensor driver");
6129d617949SNiklas Söderlund MODULE_AUTHOR("Wolfram Sang <wsa+renesas@sang-engineering.com>");
613