Merge branch 'misc.namei' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux-2.6-microblaze.git] / drivers / thermal / rcar_gen3_thermal.c
index fdf16aa..85228d3 100644 (file)
@@ -84,7 +84,7 @@ struct rcar_gen3_thermal_tsc {
        struct thermal_zone_device *zone;
        struct equation_coefs coef;
        int tj_t;
-       int id; /* thermal channel id */
+       unsigned int id; /* thermal channel id */
 };
 
 struct rcar_gen3_thermal_priv {
@@ -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;
@@ -310,7 +398,8 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
        const int *ths_tj_1 = of_device_get_match_data(dev);
        struct resource *res;
        struct thermal_zone_device *zone;
-       int ret, i;
+       unsigned int i;
+       int ret;
 
        /* default values if FUSEs are missing */
        /* TODO: Read values from hardware on supported platforms */
@@ -326,6 +415,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 +443,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 +452,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)
@@ -376,7 +468,7 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
                if (ret < 0)
                        goto error_unregister;
 
-               dev_info(dev, "TSC%d: Loaded %d trip points\n", i, ret);
+               dev_info(dev, "TSC%u: Loaded %d trip points\n", i, ret);
        }
 
        priv->num_tscs = i;
@@ -401,8 +493,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;