Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 26 May 2016 16:23:43 +0000 (09:23 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 26 May 2016 16:23:43 +0000 (09:23 -0700)
Pull thermal management updates from Zhang Rui:

 - Introduce generic ADC thermal driver, based on OF thermal (Laxman
   Dewangan)

 - Introduce new thermal driver for Tango chips (Marc Gonzalez)

 - Rockchip driver support for RK3399, RK3366, and some fixes (Caesar
   Wang, Elaine Zhang and Shawn Lin)

 - Add CPU power cooling model to Mediatek thermal driver (Dawei Chien)

 - Wider usage of dev_thermal_zone_of_sensor_register (Eduardo Valentin)

 - TI thermal driver gained a new maintainer (Keerthy).

 - Enabled powerclamp driver by checking CPU feature and package cstate
   counter instead of CPU whitelist (Jacob Pan)

 - Various fixes on thermal governor, OF thermal, Tegra, and RCAR

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux: (50 commits)
  thermal: tango: initialize TEMPSI_CFG
  thermal: rockchip: use the usleep_range instead of udelay
  thermal: rockchip: add the notes for better reading
  thermal: rockchip: Support RK3366 SoCs in the thermal driver
  thermal: rockchip: handle the power sequence for tsadc controller
  thermal: rockchip: update the tsadc table for rk3399
  thermal: rockchip: fixes the code_to_temp for tsadc driver
  thermal: rockchip: disable thermal->clk in err case
  thermal: tegra: add Tegra132 specific SOC_THERM driver
  thermal: fix ptr_ret.cocci warnings
  thermal: mediatek: Add cpu dynamic power cooling model.
  thermal: generic-adc: Add ADC based thermal sensor driver
  thermal: generic-adc: Add DT binding for ADC based thermal sensor
  thermal: tegra: fix static checker warning
  thermal: tegra: mark PM functions __maybe_unused
  thermal: add temperature sensor support for tango SoC
  thermal: hisilicon: fix IRQ imbalance enabling
  thermal: hisilicon: support to use any sensor
  thermal: rcar: Remove binding docs for r8a7794
  thermal: tegra: add PM support
  ...

38 files changed:
Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.txt
Documentation/devicetree/bindings/thermal/rcar-thermal.txt
Documentation/devicetree/bindings/thermal/tango-thermal.txt [new file with mode: 0644]
Documentation/devicetree/bindings/thermal/thermal-generic-adc.txt [new file with mode: 0644]
Documentation/thermal/sysfs-api.txt
MAINTAINERS
drivers/cpufreq/mt8173-cpufreq.c
drivers/hwmon/lm75.c
drivers/hwmon/ntc_thermistor.c
drivers/hwmon/scpi-hwmon.c
drivers/hwmon/tmp102.c
drivers/input/touchscreen/sun4i-ts.c
drivers/thermal/Kconfig
drivers/thermal/Makefile
drivers/thermal/gov_bang_bang.c
drivers/thermal/hisi_thermal.c
drivers/thermal/int340x_thermal/processor_thermal_device.c
drivers/thermal/intel_powerclamp.c
drivers/thermal/mtk_thermal.c
drivers/thermal/of-thermal.c
drivers/thermal/qcom-spmi-temp-alarm.c
drivers/thermal/rcar_thermal.c
drivers/thermal/rockchip_thermal.c
drivers/thermal/tango_thermal.c [new file with mode: 0644]
drivers/thermal/tegra/Kconfig [new file with mode: 0644]
drivers/thermal/tegra/Makefile [new file with mode: 0644]
drivers/thermal/tegra/soctherm-fuse.c [new file with mode: 0644]
drivers/thermal/tegra/soctherm.c [new file with mode: 0644]
drivers/thermal/tegra/soctherm.h [new file with mode: 0644]
drivers/thermal/tegra/tegra124-soctherm.c [new file with mode: 0644]
drivers/thermal/tegra/tegra132-soctherm.c [new file with mode: 0644]
drivers/thermal/tegra/tegra210-soctherm.c [new file with mode: 0644]
drivers/thermal/tegra_soctherm.c [deleted file]
drivers/thermal/thermal-generic-adc.c [new file with mode: 0644]
drivers/thermal/ti-soc-thermal/ti-thermal-common.c
drivers/thermal/x86_pkg_temp_thermal.c
include/dt-bindings/thermal/tegra124-soctherm.h
include/linux/thermal.h

index 6908d3a..edebfa0 100644 (file)
@@ -26,6 +26,10 @@ Required properties :
     of this property. See <dt-bindings/thermal/tegra124-soctherm.h> for a
     list of valid values when referring to thermal sensors.
 
+Note:
+- the "critical" type trip points will be set to SOC_THERM hardware as the
+shut down temperature. Once the temperature of this thermal zone is higher
+than it, the system will be shutdown or reset by hardware.
 
 Example :
 
@@ -51,5 +55,13 @@ Example: referring to thermal sensors :
 
                         thermal-sensors =
                                 <&soctherm TEGRA124_SOCTHERM_SENSOR_CPU>;
+
+                       trips {
+                               cpu_shutdown_trip: shutdown-trip {
+                                       temperature = <102500>;
+                                       hysteresis = <1000>;
+                                       type = "critical";
+                               };
+                       };
                 };
        };
index e5ee3f1..a8e52c8 100644 (file)
@@ -11,7 +11,6 @@ Required properties:
                            - "renesas,thermal-r8a7791" (R-Car M2-W)
                            - "renesas,thermal-r8a7792" (R-Car V2H)
                            - "renesas,thermal-r8a7793" (R-Car M2-N)
-                           - "renesas,thermal-r8a7794" (R-Car E2)
 - reg                  : Address range of the thermal registers.
                          The 1st reg will be recognized as common register
                          if it has "interrupts".
diff --git a/Documentation/devicetree/bindings/thermal/tango-thermal.txt b/Documentation/devicetree/bindings/thermal/tango-thermal.txt
new file mode 100644 (file)
index 0000000..212198d
--- /dev/null
@@ -0,0 +1,17 @@
+* Tango Thermal
+
+The SMP8758 SoC includes 3 instances of this temperature sensor
+(in the CPU, video decoder, and PCIe controller).
+
+Required properties:
+- #thermal-sensor-cells: Should be 0 (see thermal.txt)
+- compatible: "sigma,smp8758-thermal"
+- reg: Address range of the thermal registers
+
+Example:
+
+       cpu_temp: thermal@920100 {
+               #thermal-sensor-cells = <0>;
+               compatible = "sigma,smp8758-thermal";
+               reg = <0x920100 12>;
+       };
diff --git a/Documentation/devicetree/bindings/thermal/thermal-generic-adc.txt b/Documentation/devicetree/bindings/thermal/thermal-generic-adc.txt
new file mode 100644 (file)
index 0000000..d723555
--- /dev/null
@@ -0,0 +1,89 @@
+General Purpose Analog To Digital Converter (ADC) based thermal sensor.
+
+On some of platforms, thermal sensor like thermistors are connected to
+one of ADC channel and sensor resistance is read via voltage across the
+sensor resistor. The voltage read across the sensor is mapped to
+temperature using voltage-temperature lookup table.
+
+Required properties:
+===================
+- compatible:               Must be "generic-adc-thermal".
+- temperature-lookup-table:  Two dimensional array of Integer; lookup table
+                            to map the relation between ADC value and
+                            temperature. When ADC is read, the value is
+                            looked up on the table to get the equivalent
+                            temperature.
+                            The first value of the each row of array is the
+                            temperature in milliCelsius and second value of
+                            the each row of array is the ADC read value.
+- #thermal-sensor-cells:     Should be 1. See ./thermal.txt for a description
+                            of this property.
+
+Example :
+#include <dt-bindings/thermal/thermal.h>
+
+i2c@7000c400 {
+       ads1015: ads1015@4a {
+               reg = <0x4a>;
+               compatible = "ads1015";
+               sampling-frequency = <3300>;
+               #io-channel-cells = <1>;
+       };
+};
+
+tboard_thermistor: thermal-sensor {
+       compatible = "generic-adc-thermal";
+       #thermal-sensor-cells = <0>;
+       io-channels = <&ads1015 1>;
+       io-channel-names = "sensor-channel";
+       temperature-lookup-table = <    (-40000) 2578
+                                       (-39000) 2577
+                                       (-38000) 2576
+                                       (-37000) 2575
+                                       (-36000) 2574
+                                       (-35000) 2573
+                                       (-34000) 2572
+                                       (-33000) 2571
+                                       (-32000) 2569
+                                       (-31000) 2568
+                                       (-30000) 2567
+                                       ::::::::::
+                                       118000 254
+                                       119000 247
+                                       120000 240
+                                       121000 233
+                                       122000 226
+                                       123000 220
+                                       124000 214
+                                       125000 208>;
+};
+
+dummy_cool_dev: dummy-cool-dev {
+       compatible = "dummy-cooling-dev";
+       #cooling-cells = <2>; /* min followed by max */
+};
+
+thermal-zones {
+       Tboard {
+               polling-delay = <15000>; /* milliseconds */
+               polling-delay-passive = <0>; /* milliseconds */
+               thermal-sensors = <&tboard_thermistor>;
+
+               trips {
+                       therm_est_trip: therm_est_trip {
+                               temperature = <40000>;
+                               type = "active";
+                               hysteresis = <1000>;
+                       };
+               };
+
+               cooling-maps {
+                       map0 {
+                               trip = <&therm_est_trip>;
+                               cooling-device = <&dummy_cool_dev THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+                               contribution = <100>;
+                       };
+
+               };
+       };
+};
index ed419d6..efc3f3d 100644 (file)
@@ -69,8 +69,8 @@ temperature) and throttle appropriate devices.
 1.1.2 void thermal_zone_device_unregister(struct thermal_zone_device *tz)
 
     This interface function removes the thermal zone device.
-    It deletes the corresponding entry form /sys/class/thermal folder and
-    unbind all the thermal cooling devices it uses.
+    It deletes the corresponding entry from /sys/class/thermal folder and
+    unbinds all the thermal cooling devices it uses.
 
 1.1.3 struct thermal_zone_device *thermal_zone_of_sensor_register(
                struct device *dev, int sensor_id, void *data,
@@ -146,32 +146,32 @@ temperature) and throttle appropriate devices.
 
     This interface function adds a new thermal cooling device (fan/processor/...)
     to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
-    to all the thermal zone devices register at the same time.
+    to all the thermal zone devices registered at the same time.
     name: the cooling device name.
     devdata: device private data.
     ops: thermal cooling devices call-backs.
        .get_max_state: get the Maximum throttle state of the cooling device.
-       .get_cur_state: get the Current throttle state of the cooling device.
+       .get_cur_state: get the Currently requested throttle state of the cooling device.
        .set_cur_state: set the Current throttle state of the cooling device.
 
 1.2.2 void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
 
-    This interface function remove the thermal cooling device.
-    It deletes the corresponding entry form /sys/class/thermal folder and
-    unbind itself from all the thermal zone devices using it.
+    This interface function removes the thermal cooling device.
+    It deletes the corresponding entry from /sys/class/thermal folder and
+    unbinds itself from all the thermal zone devices using it.
 
 1.3 interface for binding a thermal zone device with a thermal cooling device
 1.3.1 int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
        int trip, struct thermal_cooling_device *cdev,
        unsigned long upper, unsigned long lower, unsigned int weight);
 
-    This interface function bind a thermal cooling device to the certain trip
+    This interface function binds a thermal cooling device to a particular trip
     point of a thermal zone device.
     This function is usually called in the thermal zone device .bind callback.
     tz: the thermal zone device
     cdev: thermal cooling device
-    trip: indicates which trip point the cooling devices is associated with
-         in this thermal zone.
+    trip: indicates which trip point in this thermal zone the cooling device
+          is associated with.
     upper:the Maximum cooling state for this trip point.
           THERMAL_NO_LIMIT means no upper limit,
          and the cooling device can be in max_state.
@@ -184,13 +184,13 @@ temperature) and throttle appropriate devices.
 1.3.2 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
                int trip, struct thermal_cooling_device *cdev);
 
-    This interface function unbind a thermal cooling device from the certain
+    This interface function unbinds a thermal cooling device from a particular
     trip point of a thermal zone device. This function is usually called in
     the thermal zone device .unbind callback.
     tz: the thermal zone device
     cdev: thermal cooling device
-    trip: indicates which trip point the cooling devices is associated with
-         in this thermal zone.
+    trip: indicates which trip point in this thermal zone the cooling device
+          is associated with.
 
 1.4 Thermal Zone Parameters
 1.4.1 struct thermal_bind_params
@@ -210,13 +210,13 @@ temperature) and throttle appropriate devices.
                this thermal zone and cdev, for a particular trip point.
                If nth bit is set, then the cdev and thermal zone are bound
                for trip point n.
-    .limits: This is an array of cooling state limits. Must have exactly
-         2 * thermal_zone.number_of_trip_points. It is an array consisting
-         of tuples <lower-state upper-state> of state limits. Each trip
-         will be associated with one state limit tuple when binding.
-         A NULL pointer means <THERMAL_NO_LIMITS THERMAL_NO_LIMITS>
-         on all trips. These limits are used when binding a cdev to a
-         trip point.
+    .binding_limits: This is an array of cooling state limits. Must have
+                     exactly 2 * thermal_zone.number_of_trip_points. It is an
+                     array consisting of tuples <lower-state upper-state> of
+                     state limits. Each trip will be associated with one state
+                     limit tuple when binding. A NULL pointer means
+                     <THERMAL_NO_LIMITS THERMAL_NO_LIMITS> on all trips.
+                     These limits are used when binding a cdev to a trip point.
     .match: This call back returns success(0) if the 'tz and cdev' need to
            be bound, as per platform data.
 1.4.2 struct thermal_zone_params
@@ -351,8 +351,8 @@ cdev[0-*]
        RO, Optional
 
 cdev[0-*]_trip_point
-       The trip point with which cdev[0-*] is associated in this thermal
-       zone; -1 means the cooling device is not associated with any trip
+       The trip point in this thermal zone which cdev[0-*] is associated
+       with; -1 means the cooling device is not associated with any trip
        point.
        RO, Optional
 
index ab7e05d..81e9c98 100644 (file)
@@ -11296,6 +11296,7 @@ F:      drivers/platform/x86/thinkpad_acpi.c
 
 TI BANDGAP AND THERMAL DRIVER
 M:     Eduardo Valentin <edubezval@gmail.com>
+M:     Keerthy <j-keerthy@ti.com>
 L:     linux-pm@vger.kernel.org
 L:     linux-omap@vger.kernel.org
 S:     Maintained
index 6f602c7..643f431 100644 (file)
@@ -307,17 +307,24 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
        return 0;
 }
 
+#define DYNAMIC_POWER "dynamic-power-coefficient"
+
 static void mtk_cpufreq_ready(struct cpufreq_policy *policy)
 {
        struct mtk_cpu_dvfs_info *info = policy->driver_data;
        struct device_node *np = of_node_get(info->cpu_dev->of_node);
+       u32 capacitance = 0;
 
        if (WARN_ON(!np))
                return;
 
        if (of_find_property(np, "#cooling-cells", NULL)) {
-               info->cdev = of_cpufreq_cooling_register(np,
-                                                        policy->related_cpus);
+               of_property_read_u32(np, DYNAMIC_POWER, &capacitance);
+
+               info->cdev = of_cpufreq_power_cooling_register(np,
+                                               policy->related_cpus,
+                                               capacitance,
+                                               NULL);
 
                if (IS_ERR(info->cdev)) {
                        dev_err(info->cpu_dev,
index 0addc84..69166ab 100644 (file)
@@ -77,7 +77,6 @@ static const u8 LM75_REG_TEMP[3] = {
 struct lm75_data {
        struct i2c_client       *client;
        struct device           *hwmon_dev;
-       struct thermal_zone_device      *tz;
        struct mutex            update_lock;
        u8                      orig_conf;
        u8                      resolution;     /* In bits, between 9 and 12 */
@@ -306,11 +305,9 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
        if (IS_ERR(data->hwmon_dev))
                return PTR_ERR(data->hwmon_dev);
 
-       data->tz = thermal_zone_of_sensor_register(data->hwmon_dev, 0,
-                                                  data->hwmon_dev,
-                                                  &lm75_of_thermal_ops);
-       if (IS_ERR(data->tz))
-               data->tz = NULL;
+       devm_thermal_zone_of_sensor_register(data->hwmon_dev, 0,
+                                            data->hwmon_dev,
+                                            &lm75_of_thermal_ops);
 
        dev_info(dev, "%s: sensor '%s'\n",
                 dev_name(data->hwmon_dev), client->name);
@@ -322,7 +319,6 @@ static int lm75_remove(struct i2c_client *client)
 {
        struct lm75_data *data = i2c_get_clientdata(client);
 
-       thermal_zone_of_sensor_unregister(data->hwmon_dev, data->tz);
        hwmon_device_unregister(data->hwmon_dev);
        lm75_write_value(client, LM75_REG_CONF, data->orig_conf);
        return 0;
index faa6e8d..8ef7b71 100644 (file)
@@ -259,7 +259,6 @@ struct ntc_data {
        struct device *dev;
        int n_comp;
        char name[PLATFORM_NAME_SIZE];
-       struct thermal_zone_device *tz;
 };
 
 #if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO)
@@ -579,6 +578,7 @@ static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = {
 
 static int ntc_thermistor_probe(struct platform_device *pdev)
 {
+       struct thermal_zone_device *tz;
        const struct of_device_id *of_id =
                        of_match_device(of_match_ptr(ntc_match), &pdev->dev);
        const struct platform_device_id *pdev_id;
@@ -677,12 +677,10 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
        dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n",
                                                                pdev_id->name);
 
-       data->tz = thermal_zone_of_sensor_register(data->dev, 0, data->dev,
-                                                  &ntc_of_thermal_ops);
-       if (IS_ERR(data->tz)) {
+       tz = devm_thermal_zone_of_sensor_register(data->dev, 0, data->dev,
+                                                 &ntc_of_thermal_ops);
+       if (IS_ERR(tz))
                dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n");
-               data->tz = NULL;
-       }
 
        return 0;
 err_after_sysfs:
@@ -700,8 +698,6 @@ static int ntc_thermistor_remove(struct platform_device *pdev)
        sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
        ntc_iio_channel_release(pdata);
 
-       thermal_zone_of_sensor_unregister(data->dev, data->tz);
-
        return 0;
 }
 
index 912b449..25b44e6 100644 (file)
@@ -31,10 +31,8 @@ struct sensor_data {
 };
 
 struct scpi_thermal_zone {
-       struct list_head list;
        int sensor_id;
        struct scpi_sensors *scpi_sensors;
-       struct thermal_zone_device *tzd;
 };
 
 struct scpi_sensors {
@@ -92,20 +90,6 @@ scpi_show_label(struct device *dev, struct device_attribute *attr, char *buf)
        return sprintf(buf, "%s\n", sensor->info.name);
 }
 
-static void
-unregister_thermal_zones(struct platform_device *pdev,
-                        struct scpi_sensors *scpi_sensors)
-{
-       struct list_head *pos;
-
-       list_for_each(pos, &scpi_sensors->thermal_zones) {
-               struct scpi_thermal_zone *zone;
-
-               zone = list_entry(pos, struct scpi_thermal_zone, list);
-               thermal_zone_of_sensor_unregister(&pdev->dev, zone->tzd);
-       }
-}
-
 static struct thermal_zone_of_device_ops scpi_sensor_ops = {
        .get_temp = scpi_read_temp,
 };
@@ -118,7 +102,7 @@ static int scpi_hwmon_probe(struct platform_device *pdev)
        struct scpi_ops *scpi_ops;
        struct device *hwdev, *dev = &pdev->dev;
        struct scpi_sensors *scpi_sensors;
-       int ret, idx;
+       int idx, ret;
 
        scpi_ops = get_scpi_ops();
        if (!scpi_ops)
@@ -232,47 +216,34 @@ static int scpi_hwmon_probe(struct platform_device *pdev)
        INIT_LIST_HEAD(&scpi_sensors->thermal_zones);
        for (i = 0; i < nr_sensors; i++) {
                struct sensor_data *sensor = &scpi_sensors->data[i];
+               struct thermal_zone_device *z;
                struct scpi_thermal_zone *zone;
 
                if (sensor->info.class != TEMPERATURE)
                        continue;
 
                zone = devm_kzalloc(dev, sizeof(*zone), GFP_KERNEL);
-               if (!zone) {
-                       ret = -ENOMEM;
-                       goto unregister_tzd;
-               }
+               if (!zone)
+                       return -ENOMEM;
 
                zone->sensor_id = i;
                zone->scpi_sensors = scpi_sensors;
-               zone->tzd = thermal_zone_of_sensor_register(dev,
-                               sensor->info.sensor_id, zone, &scpi_sensor_ops);
+               z = devm_thermal_zone_of_sensor_register(dev,
+                                                        sensor->info.sensor_id,
+                                                        zone,
+                                                        &scpi_sensor_ops);
                /*
                 * The call to thermal_zone_of_sensor_register returns
                 * an error for sensors that are not associated with
                 * any thermal zones or if the thermal subsystem is
                 * not configured.
                 */
-               if (IS_ERR(zone->tzd)) {
+               if (IS_ERR(z)) {
                        devm_kfree(dev, zone);
                        continue;
                }
-               list_add(&zone->list, &scpi_sensors->thermal_zones);
        }
 
-       return 0;
-
-unregister_tzd:
-       unregister_thermal_zones(pdev, scpi_sensors);
-       return ret;
-}
-
-static int scpi_hwmon_remove(struct platform_device *pdev)
-{
-       struct scpi_sensors *scpi_sensors = platform_get_drvdata(pdev);
-
-       unregister_thermal_zones(pdev, scpi_sensors);
-
        return 0;
 }
 
@@ -288,7 +259,6 @@ static struct platform_driver scpi_hwmon_platdrv = {
                .of_match_table = scpi_of_match,
        },
        .probe          = scpi_hwmon_probe,
-       .remove         = scpi_hwmon_remove,
 };
 module_platform_driver(scpi_hwmon_platdrv);
 
index 5289aa0..f1e96fd 100644 (file)
@@ -53,7 +53,6 @@
 struct tmp102 {
        struct i2c_client *client;
        struct device *hwmon_dev;
-       struct thermal_zone_device *tz;
        struct mutex lock;
        u16 config_orig;
        unsigned long last_update;
@@ -232,10 +231,8 @@ static int tmp102_probe(struct i2c_client *client,
                goto fail_restore_config;
        }
        tmp102->hwmon_dev = hwmon_dev;
-       tmp102->tz = thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev,
-                                                    &tmp102_of_thermal_ops);
-       if (IS_ERR(tmp102->tz))
-               tmp102->tz = NULL;
+       devm_thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev,
+                                            &tmp102_of_thermal_ops);
 
        dev_info(dev, "initialized\n");
 
@@ -251,7 +248,6 @@ static int tmp102_remove(struct i2c_client *client)
 {
        struct tmp102 *tmp102 = i2c_get_clientdata(client);
 
-       thermal_zone_of_sensor_unregister(tmp102->hwmon_dev, tmp102->tz);
        hwmon_device_unregister(tmp102->hwmon_dev);
 
        /* Stop monitoring if device was stopped originally */
index 4857943..d07dd29 100644 (file)
 struct sun4i_ts_data {
        struct device *dev;
        struct input_dev *input;
-       struct thermal_zone_device *tz;
        void __iomem *base;
        unsigned int irq;
        bool ignore_fifo_data;
@@ -366,10 +365,7 @@ static int sun4i_ts_probe(struct platform_device *pdev)
        if (IS_ERR(hwmon))
                return PTR_ERR(hwmon);
 
-       ts->tz = thermal_zone_of_sensor_register(ts->dev, 0, ts,
-                                                &sun4i_ts_tz_ops);
-       if (IS_ERR(ts->tz))
-               ts->tz = NULL;
+       devm_thermal_zone_of_sensor_register(ts->dev, 0, ts, &sun4i_ts_tz_ops);
 
        writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
 
@@ -377,7 +373,6 @@ static int sun4i_ts_probe(struct platform_device *pdev)
                error = input_register_device(ts->input);
                if (error) {
                        writel(0, ts->base + TP_INT_FIFOC);
-                       thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
                        return error;
                }
        }
@@ -394,8 +389,6 @@ static int sun4i_ts_remove(struct platform_device *pdev)
        if (ts->input)
                input_unregister_device(ts->input);
 
-       thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
-
        /* Deactivate all IRQs */
        writel(0, ts->base + TP_INT_FIFOC);
 
index d89d60c..2d702ca 100644 (file)
@@ -260,16 +260,6 @@ config ARMADA_THERMAL
          Enable this option if you want to have support for thermal management
          controller present in Armada 370 and Armada XP SoC.
 
-config TEGRA_SOCTHERM
-       tristate "Tegra SOCTHERM thermal management"
-       depends on ARCH_TEGRA
-       help
-         Enable this option for integrated thermal management support on NVIDIA
-         Tegra124 systems-on-chip. The driver supports four thermal zones
-         (CPU, GPU, MEM, PLLX). Cooling devices can be bound to the thermal
-         zones to manage temperatures. This option is also required for the
-         emergency thermal reset (thermtrip) feature to function.
-
 config DB8500_CPUFREQ_COOLING
        tristate "DB8500 cpufreq cooling"
        depends on ARCH_U8500 || COMPILE_TEST
@@ -377,6 +367,17 @@ depends on ARCH_STI && OF
 source "drivers/thermal/st/Kconfig"
 endmenu
 
+config TANGO_THERMAL
+       tristate "Tango thermal management"
+       depends on ARCH_TANGO || COMPILE_TEST
+       help
+         Enable the Tango thermal driver, which supports the primitive
+         temperature sensor embedded in Tango chips since the SMP8758.
+         This sensor only generates a 1-bit signal to indicate whether
+         the die temperature exceeds a programmable threshold.
+
+source "drivers/thermal/tegra/Kconfig"
+
 config QCOM_SPMI_TEMP_ALARM
        tristate "Qualcomm SPMI PMIC Temperature Alarm"
        depends on OF && SPMI && IIO
@@ -388,4 +389,14 @@ config QCOM_SPMI_TEMP_ALARM
          real time die temperature if an ADC is present or an estimate of the
          temperature based upon the over temperature stage value.
 
+config GENERIC_ADC_THERMAL
+       tristate "Generic ADC based thermal sensor"
+       depends on IIO
+       help
+         This enabled a thermal sysfs driver for the temperature sensor
+         which is connected to the General Purpose ADC. The ADC channel
+         is read via IIO framework and the channel information is provided
+         to this driver. This driver reports the temperature by reading ADC
+         channel and converts it to temperature based on lookup table.
+
 endif
index 8e9cbc3..10b07c1 100644 (file)
@@ -35,6 +35,7 @@ obj-y                         += samsung/
 obj-$(CONFIG_DOVE_THERMAL)     += dove_thermal.o
 obj-$(CONFIG_DB8500_THERMAL)   += db8500_thermal.o
 obj-$(CONFIG_ARMADA_THERMAL)   += armada_thermal.o
+obj-$(CONFIG_TANGO_THERMAL)    += tango_thermal.o
 obj-$(CONFIG_IMX_THERMAL)      += imx_thermal.o
 obj-$(CONFIG_DB8500_CPUFREQ_COOLING)   += db8500_cpufreq_cooling.o
 obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
@@ -46,6 +47,7 @@ obj-$(CONFIG_TI_SOC_THERMAL)  += ti-soc-thermal/
 obj-$(CONFIG_INT340X_THERMAL)  += int340x_thermal/
 obj-$(CONFIG_INTEL_PCH_THERMAL)        += intel_pch_thermal.o
 obj-$(CONFIG_ST_THERMAL)       += st/
-obj-$(CONFIG_TEGRA_SOCTHERM)   += tegra_soctherm.o
+obj-$(CONFIG_TEGRA_SOCTHERM)   += tegra/
 obj-$(CONFIG_HISI_THERMAL)     += hisi_thermal.o
 obj-$(CONFIG_MTK_THERMAL)      += mtk_thermal.o
+obj-$(CONFIG_GENERIC_ADC_THERMAL)      += thermal-generic-adc.o
index 70836c5..fc52016 100644 (file)
@@ -29,7 +29,13 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
        struct thermal_instance *instance;
 
        tz->ops->get_trip_temp(tz, trip, &trip_temp);
-       tz->ops->get_trip_hyst(tz, trip, &trip_hyst);
+
+       if (!tz->ops->get_trip_hyst) {
+               pr_warn_once("Undefined get_trip_hyst for thermal zone %s - "
+                               "running with default hysteresis zero\n", tz->type);
+               trip_hyst = 0;
+       } else
+               tz->ops->get_trip_hyst(tz, trip, &trip_hyst);
 
        dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n",
                                trip, trip_temp, tz->temperature,
index 5e820b5..97fad8f 100644 (file)
@@ -160,7 +160,7 @@ static int hisi_thermal_get_temp(void *_sensor, int *temp)
        struct hisi_thermal_sensor *sensor = _sensor;
        struct hisi_thermal_data *data = sensor->thermal;
 
-       int sensor_id = 0, i;
+       int sensor_id = -1, i;
        long max_temp = 0;
 
        *temp = hisi_thermal_get_sensor_temp(data, sensor);
@@ -168,12 +168,19 @@ static int hisi_thermal_get_temp(void *_sensor, int *temp)
        sensor->sensor_temp = *temp;
 
        for (i = 0; i < HISI_MAX_SENSORS; i++) {
+               if (!data->sensors[i].tzd)
+                       continue;
+
                if (data->sensors[i].sensor_temp >= max_temp) {
                        max_temp = data->sensors[i].sensor_temp;
                        sensor_id = i;
                }
        }
 
+       /* If no sensor has been enabled, then skip to enable irq */
+       if (sensor_id == -1)
+               return 0;
+
        mutex_lock(&data->thermal_lock);
        data->irq_bind_sensor = sensor_id;
        mutex_unlock(&data->thermal_lock);
@@ -226,8 +233,12 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
                 sensor->thres_temp / 1000);
        mutex_unlock(&data->thermal_lock);
 
-       for (i = 0; i < HISI_MAX_SENSORS; i++)
+       for (i = 0; i < HISI_MAX_SENSORS; i++) {
+               if (!data->sensors[i].tzd)
+                       continue;
+
                thermal_zone_device_update(data->sensors[i].tzd);
+       }
 
        return IRQ_HANDLED;
 }
@@ -243,10 +254,11 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev,
        sensor->id = index;
        sensor->thermal = data;
 
-       sensor->tzd = thermal_zone_of_sensor_register(&pdev->dev, sensor->id,
-                               sensor, &hisi_of_thermal_ops);
+       sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
+                               sensor->id, sensor, &hisi_of_thermal_ops);
        if (IS_ERR(sensor->tzd)) {
                ret = PTR_ERR(sensor->tzd);
+               sensor->tzd = NULL;
                dev_err(&pdev->dev, "failed to register sensor id %d: %d\n",
                        sensor->id, ret);
                return ret;
@@ -331,28 +343,21 @@ static int hisi_thermal_probe(struct platform_device *pdev)
                return ret;
        }
 
+       hisi_thermal_enable_bind_irq_sensor(data);
+       irq_get_irqchip_state(data->irq, IRQCHIP_STATE_MASKED,
+                             &data->irq_enabled);
+
        for (i = 0; i < HISI_MAX_SENSORS; ++i) {
                ret = hisi_thermal_register_sensor(pdev, data,
                                                   &data->sensors[i], i);
-               if (ret) {
+               if (ret)
                        dev_err(&pdev->dev,
                                "failed to register thermal sensor: %d\n", ret);
-                       goto err_get_sensor_data;
-               }
+               else
+                       hisi_thermal_toggle_sensor(&data->sensors[i], true);
        }
 
-       hisi_thermal_enable_bind_irq_sensor(data);
-       data->irq_enabled = true;
-
-       for (i = 0; i < HISI_MAX_SENSORS; i++)
-               hisi_thermal_toggle_sensor(&data->sensors[i], true);
-
        return 0;
-
-err_get_sensor_data:
-       clk_disable_unprepare(data->clk);
-
-       return ret;
 }
 
 static int hisi_thermal_remove(struct platform_device *pdev)
@@ -363,8 +368,10 @@ static int hisi_thermal_remove(struct platform_device *pdev)
        for (i = 0; i < HISI_MAX_SENSORS; i++) {
                struct hisi_thermal_sensor *sensor = &data->sensors[i];
 
+               if (!sensor->tzd)
+                       continue;
+
                hisi_thermal_toggle_sensor(sensor, false);
-               thermal_zone_of_sensor_unregister(&pdev->dev, sensor->tzd);
        }
 
        hisi_thermal_disable_sensor(data);
index 36fa724..42c1ac0 100644 (file)
@@ -198,49 +198,33 @@ static struct thermal_zone_device_ops proc_thermal_local_ops = {
        .get_temp       = proc_thermal_get_zone_temp,
 };
 
-static int proc_thermal_add(struct device *dev,
-                           struct proc_thermal_device **priv)
+static int proc_thermal_read_ppcc(struct proc_thermal_device *proc_priv)
 {
-       struct proc_thermal_device *proc_priv;
-       struct acpi_device *adev;
+       int i;
        acpi_status status;
        struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
        union acpi_object *elements, *ppcc;
        union acpi_object *p;
-       unsigned long long tmp;
-       struct thermal_zone_device_ops *ops = NULL;
-       int i;
-       int ret;
-
-       adev = ACPI_COMPANION(dev);
-       if (!adev)
-               return -ENODEV;
+       int ret = 0;
 
-       status = acpi_evaluate_object(adev->handle, "PPCC", NULL, &buf);
+       status = acpi_evaluate_object(proc_priv->adev->handle, "PPCC",
+                                     NULL, &buf);
        if (ACPI_FAILURE(status))
                return -ENODEV;
 
        p = buf.pointer;
        if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
-               dev_err(dev, "Invalid PPCC data\n");
+               dev_err(proc_priv->dev, "Invalid PPCC data\n");
                ret = -EFAULT;
                goto free_buffer;
        }
+
        if (!p->package.count) {
-               dev_err(dev, "Invalid PPCC package size\n");
+               dev_err(proc_priv->dev, "Invalid PPCC package size\n");
                ret = -EFAULT;
                goto free_buffer;
        }
 
-       proc_priv = devm_kzalloc(dev, sizeof(*proc_priv), GFP_KERNEL);
-       if (!proc_priv) {
-               ret = -ENOMEM;
-               goto free_buffer;
-       }
-
-       proc_priv->dev = dev;
-       proc_priv->adev = adev;
-
        for (i = 0; i < min((int)p->package.count - 1, 2); ++i) {
                elements = &(p->package.elements[i+1]);
                if (elements->type != ACPI_TYPE_PACKAGE ||
@@ -257,12 +241,62 @@ static int proc_thermal_add(struct device *dev,
                proc_priv->power_limits[i].step_uw = ppcc[5].integer.value;
        }
 
+free_buffer:
+       kfree(buf.pointer);
+
+       return ret;
+}
+
+#define PROC_POWER_CAPABILITY_CHANGED  0x83
+static void proc_thermal_notify(acpi_handle handle, u32 event, void *data)
+{
+       struct proc_thermal_device *proc_priv = data;
+
+       if (!proc_priv)
+               return;
+
+       switch (event) {
+       case PROC_POWER_CAPABILITY_CHANGED:
+               proc_thermal_read_ppcc(proc_priv);
+               int340x_thermal_zone_device_update(proc_priv->int340x_zone);
+               break;
+       default:
+               dev_err(proc_priv->dev, "Unsupported event [0x%x]\n", event);
+               break;
+       }
+}
+
+
+static int proc_thermal_add(struct device *dev,
+                           struct proc_thermal_device **priv)
+{
+       struct proc_thermal_device *proc_priv;
+       struct acpi_device *adev;
+       acpi_status status;
+       unsigned long long tmp;
+       struct thermal_zone_device_ops *ops = NULL;
+       int ret;
+
+       adev = ACPI_COMPANION(dev);
+       if (!adev)
+               return -ENODEV;
+
+       proc_priv = devm_kzalloc(dev, sizeof(*proc_priv), GFP_KERNEL);
+       if (!proc_priv)
+               return -ENOMEM;
+
+       proc_priv->dev = dev;
+       proc_priv->adev = adev;
        *priv = proc_priv;
 
-       ret = sysfs_create_group(&dev->kobj,
-                                &power_limit_attribute_group);
+       ret = proc_thermal_read_ppcc(proc_priv);
+       if (!ret) {
+               ret = sysfs_create_group(&dev->kobj,
+                                        &power_limit_attribute_group);
+
+       }
        if (ret)
-               goto free_buffer;
+               return ret;
 
        status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp);
        if (ACPI_FAILURE(status)) {
@@ -274,20 +308,32 @@ static int proc_thermal_add(struct device *dev,
 
        proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops);
        if (IS_ERR(proc_priv->int340x_zone)) {
-               sysfs_remove_group(&proc_priv->dev->kobj,
-                          &power_limit_attribute_group);
                ret = PTR_ERR(proc_priv->int340x_zone);
+               goto remove_group;
        } else
                ret = 0;
 
-free_buffer:
-       kfree(buf.pointer);
+       ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
+                                         proc_thermal_notify,
+                                         (void *)proc_priv);
+       if (ret)
+               goto remove_zone;
+
+       return 0;
+
+remove_zone:
+       int340x_thermal_zone_remove(proc_priv->int340x_zone);
+remove_group:
+       sysfs_remove_group(&proc_priv->dev->kobj,
+                          &power_limit_attribute_group);
 
        return ret;
 }
 
 static void proc_thermal_remove(struct proc_thermal_device *proc_priv)
 {
+       acpi_remove_notify_handler(proc_priv->adev->handle,
+                                  ACPI_DEVICE_NOTIFY, proc_thermal_notify);
        int340x_thermal_zone_remove(proc_priv->int340x_zone);
        sysfs_remove_group(&proc_priv->dev->kobj,
                           &power_limit_attribute_group);
index 6c79588..015ce2e 100644 (file)
@@ -510,12 +510,6 @@ static int start_power_clamp(void)
        unsigned long cpu;
        struct task_struct *thread;
 
-       /* check if pkg cstate counter is completely 0, abort in this case */
-       if (!has_pkg_state_counter()) {
-               pr_err("pkg cstate counter not functional, abort\n");
-               return -EINVAL;
-       }
-
        set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO - 1);
        /* prevent cpu hotplug */
        get_online_cpus();
@@ -672,35 +666,11 @@ static struct thermal_cooling_device_ops powerclamp_cooling_ops = {
        .set_cur_state = powerclamp_set_cur_state,
 };
 
-/* runs on Nehalem and later */
 static const struct x86_cpu_id intel_powerclamp_ids[] __initconst = {
-       { X86_VENDOR_INTEL, 6, 0x1a},
-       { X86_VENDOR_INTEL, 6, 0x1c},
-       { X86_VENDOR_INTEL, 6, 0x1e},
-       { X86_VENDOR_INTEL, 6, 0x1f},
-       { X86_VENDOR_INTEL, 6, 0x25},
-       { X86_VENDOR_INTEL, 6, 0x26},
-       { X86_VENDOR_INTEL, 6, 0x2a},
-       { X86_VENDOR_INTEL, 6, 0x2c},
-       { X86_VENDOR_INTEL, 6, 0x2d},
-       { X86_VENDOR_INTEL, 6, 0x2e},
-       { X86_VENDOR_INTEL, 6, 0x2f},
-       { X86_VENDOR_INTEL, 6, 0x37},
-       { X86_VENDOR_INTEL, 6, 0x3a},
-       { X86_VENDOR_INTEL, 6, 0x3c},
-       { X86_VENDOR_INTEL, 6, 0x3d},
-       { X86_VENDOR_INTEL, 6, 0x3e},
-       { X86_VENDOR_INTEL, 6, 0x3f},
-       { X86_VENDOR_INTEL, 6, 0x45},
-       { X86_VENDOR_INTEL, 6, 0x46},
-       { X86_VENDOR_INTEL, 6, 0x47},
-       { X86_VENDOR_INTEL, 6, 0x4c},
-       { X86_VENDOR_INTEL, 6, 0x4d},
-       { X86_VENDOR_INTEL, 6, 0x4e},
-       { X86_VENDOR_INTEL, 6, 0x4f},
-       { X86_VENDOR_INTEL, 6, 0x56},
-       { X86_VENDOR_INTEL, 6, 0x57},
-       { X86_VENDOR_INTEL, 6, 0x5e},
+       { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_MWAIT },
+       { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_ARAT },
+       { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_NONSTOP_TSC },
+       { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_CONSTANT_TSC},
        {}
 };
 MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids);
@@ -712,11 +682,12 @@ static int __init powerclamp_probe(void)
                                boot_cpu_data.x86, boot_cpu_data.x86_model);
                return -ENODEV;
        }
-       if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC) ||
-               !boot_cpu_has(X86_FEATURE_CONSTANT_TSC) ||
-               !boot_cpu_has(X86_FEATURE_MWAIT) ||
-               !boot_cpu_has(X86_FEATURE_ARAT))
+
+       /* The goal for idle time alignment is to achieve package cstate. */
+       if (!has_pkg_state_counter()) {
+               pr_info("No package C-state available");
                return -ENODEV;
+       }
 
        /* find the deepest mwait value */
        find_target_mwait();
index 507632b..262ab0a 100644 (file)
@@ -144,7 +144,6 @@ struct mtk_thermal {
        s32 o_slope;
        s32 vts[MT8173_NUM_SENSORS];
 
-       struct thermal_zone_device *tzd;
 };
 
 struct mtk_thermal_bank_cfg {
@@ -572,16 +571,11 @@ static int mtk_thermal_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, mt);
 
-       mt->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, mt,
-                               &mtk_thermal_ops);
-       if (IS_ERR(mt->tzd))
-               goto err_register;
+       devm_thermal_zone_of_sensor_register(&pdev->dev, 0, mt,
+                                            &mtk_thermal_ops);
 
        return 0;
 
-err_register:
-       clk_disable_unprepare(mt->clk_peri_therm);
-
 err_disable_clk_auxadc:
        clk_disable_unprepare(mt->clk_auxadc);
 
@@ -592,8 +586,6 @@ static int mtk_thermal_remove(struct platform_device *pdev)
 {
        struct mtk_thermal *mt = platform_get_drvdata(pdev);
 
-       thermal_zone_of_sensor_unregister(&pdev->dev, mt->tzd);
-
        clk_disable_unprepare(mt->clk_peri_therm);
        clk_disable_unprepare(mt->clk_auxadc);
 
index d8ec44b..b8e509c 100644 (file)
@@ -331,6 +331,14 @@ static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
        if (trip >= data->ntrips || trip < 0)
                return -EDOM;
 
+       if (data->ops->set_trip_temp) {
+               int ret;
+
+               ret = data->ops->set_trip_temp(data->sensor_data, trip, temp);
+               if (ret)
+                       return ret;
+       }
+
        /* thermal framework should take care of data->mask & (1 << trip) */
        data->trips[trip].temperature = temp;
 
@@ -906,7 +914,7 @@ finish:
        return tz;
 
 free_tbps:
-       for (i = 0; i < tz->num_tbps; i++)
+       for (i = i - 1; i >= 0; i--)
                of_node_put(tz->tbps[i].cooling_device);
        kfree(tz->tbps);
 free_trips:
index b677aad..f8a3c60 100644 (file)
@@ -260,7 +260,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
        if (ret < 0)
                goto fail;
 
-       chip->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, 0, chip,
+       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");
@@ -281,7 +281,6 @@ static int qpnp_tm_remove(struct platform_device *pdev)
 {
        struct qpnp_tm_chip *chip = dev_get_drvdata(&pdev->dev);
 
-       thermal_zone_of_sensor_unregister(&pdev->dev, chip->tz_dev);
        if (!IS_ERR(chip->adc))
                iio_channel_release(chip->adc);
 
index 82daba0..71a3392 100644 (file)
@@ -492,7 +492,7 @@ static int rcar_thermal_probe(struct platform_device *pdev)
                        goto error_unregister;
 
                if (of_data == USE_OF_THERMAL)
-                       priv->zone = thermal_zone_of_sensor_register(
+                       priv->zone = devm_thermal_zone_of_sensor_register(
                                                dev, i, priv,
                                                &rcar_thermal_zone_of_ops);
                else
index 233a564..5d491f1 100644 (file)
@@ -1,7 +1,5 @@
 /*
- * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
- *
- * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
+ * Copyright (c) 2014-2016, Fuzhou Rockchip Electronics Co., Ltd
  * Caesar Wang <wxt@rock-chips.com>
  *
  * This program is free software; you can redistribute it and/or modify it
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/platform_device.h>
+#include <linux/regmap.h>
 #include <linux/reset.h>
 #include <linux/thermal.h>
+#include <linux/mfd/syscon.h>
 #include <linux/pinctrl/consumer.h>
 
 /**
@@ -73,7 +73,7 @@ enum adc_sort_mode {
 #define SOC_MAX_SENSORS        2
 
 /**
- * struct chip_tsadc_table: hold information about chip-specific differences
+ * struct chip_tsadc_table - hold information about chip-specific differences
  * @id: conversion table
  * @length: size of conversion table
  * @data_mask: mask to apply on data inputs
@@ -86,6 +86,20 @@ struct chip_tsadc_table {
        enum adc_sort_mode mode;
 };
 
+/**
+ * struct rockchip_tsadc_chip - hold the private data of tsadc chip
+ * @chn_id[SOC_MAX_SENSORS]: the sensor id of chip correspond to the channel
+ * @chn_num: the channel number of tsadc chip
+ * @tshut_temp: the hardware-controlled shutdown temperature value
+ * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
+ * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
+ * @initialize: SoC special initialize tsadc controller method
+ * @irq_ack: clear the interrupt
+ * @get_temp: get the temperature
+ * @set_tshut_temp: set the hardware-controlled shutdown temperature
+ * @set_tshut_mode: set the hardware-controlled shutdown mode
+ * @table: the chip-specific conversion table
+ */
 struct rockchip_tsadc_chip {
        /* The sensor id of chip correspond to the ADC channel */
        int chn_id[SOC_MAX_SENSORS];
@@ -97,7 +111,8 @@ struct rockchip_tsadc_chip {
        enum tshut_polarity tshut_polarity;
 
        /* Chip-wide methods */
-       void (*initialize)(void __iomem *reg, enum tshut_polarity p);
+       void (*initialize)(struct regmap *grf,
+                          void __iomem *reg, enum tshut_polarity p);
        void (*irq_ack)(void __iomem *reg);
        void (*control)(void __iomem *reg, bool on);
 
@@ -112,12 +127,32 @@ struct rockchip_tsadc_chip {
        struct chip_tsadc_table table;
 };
 
+/**
+ * struct rockchip_thermal_sensor - hold the information of thermal sensor
+ * @thermal:  pointer to the platform/configuration data
+ * @tzd: pointer to a thermal zone
+ * @id: identifier of the thermal sensor
+ */
 struct rockchip_thermal_sensor {
        struct rockchip_thermal_data *thermal;
        struct thermal_zone_device *tzd;
        int id;
 };
 
+/**
+ * struct rockchip_thermal_data - hold the private data of thermal driver
+ * @chip: pointer to the platform/configuration data
+ * @pdev: platform device of thermal
+ * @reset: the reset controller of tsadc
+ * @sensors[SOC_MAX_SENSORS]: the thermal sensor
+ * @clk: the controller clock is divided by the exteral 24MHz
+ * @pclk: the advanced peripherals bus clock
+ * @grf: the general register file will be used to do static set by software
+ * @regs: the base address of tsadc controller
+ * @tshut_temp: the hardware-controlled shutdown temperature value
+ * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
+ * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
+ */
 struct rockchip_thermal_data {
        const struct rockchip_tsadc_chip *chip;
        struct platform_device *pdev;
@@ -128,6 +163,7 @@ struct rockchip_thermal_data {
        struct clk *clk;
        struct clk *pclk;
 
+       struct regmap *grf;
        void __iomem *regs;
 
        int tshut_temp;
@@ -142,6 +178,7 @@ struct rockchip_thermal_data {
  * TSADCV3_* are used for newer SoCs than RK3288. (e.g: RK3228, RK3399)
  *
  */
+#define TSADCV2_USER_CON                       0x00
 #define TSADCV2_AUTO_CON                       0x04
 #define TSADCV2_INT_EN                         0x08
 #define TSADCV2_INT_PD                         0x0c
@@ -155,12 +192,7 @@ struct rockchip_thermal_data {
 #define TSADCV2_AUTO_EN                                BIT(0)
 #define TSADCV2_AUTO_SRC_EN(chn)               BIT(4 + (chn))
 #define TSADCV2_AUTO_TSHUT_POLARITY_HIGH       BIT(8)
-/**
- * TSADCV1_AUTO_Q_SEL_EN:
- * whether select (1024 - tsadc_q) as output
- * 1'b0:use tsadc_q as output(temperature-code is rising sequence)
- * 1'b1:use(1024 - tsadc_q) as output (temperature-code is falling sequence)
- */
+
 #define TSADCV3_AUTO_Q_SEL_EN                  BIT(1)
 
 #define TSADCV2_INT_SRC_EN(chn)                        BIT(chn)
@@ -177,19 +209,32 @@ struct rockchip_thermal_data {
 #define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT     4
 #define TSADCV2_AUTO_PERIOD_TIME               250 /* msec */
 #define TSADCV2_AUTO_PERIOD_HT_TIME            50  /* msec */
+#define TSADCV2_USER_INTER_PD_SOC              0x340 /* 13 clocks */
 
-struct tsadc_table {
-       u32 code;
-       int temp;
-};
+#define GRF_SARADC_TESTBIT                     0x0e644
+#define GRF_TSADC_TESTBIT_L                    0x0e648
+#define GRF_TSADC_TESTBIT_H                    0x0e64c
+
+#define GRF_TSADC_TSEN_PD_ON                   (0x30003 << 0)
+#define GRF_TSADC_TSEN_PD_OFF                  (0x30000 << 0)
+#define GRF_SARADC_TESTBIT_ON                  (0x10001 << 2)
+#define GRF_TSADC_TESTBIT_H_ON                 (0x10001 << 2)
 
 /**
+ * struct tsadc_table - code to temperature conversion table
+ * @code: the value of adc channel
+ * @temp: the temperature
  * Note:
- * Code to Temperature mapping of the Temperature sensor is a piece wise linear
+ * code to temperature mapping of the temperature sensor is a piece wise linear
  * curve.Any temperature, code faling between to 2 give temperatures can be
  * linearly interpolated.
- * Code to Temperature mapping should be updated based on sillcon results.
+ * Code to Temperature mapping should be updated based on manufacturer results.
  */
+struct tsadc_table {
+       u32 code;
+       int temp;
+};
+
 static const struct tsadc_table rk3228_code_table[] = {
        {0, -40000},
        {588, -40000},
@@ -308,40 +353,40 @@ static const struct tsadc_table rk3368_code_table[] = {
 
 static const struct tsadc_table rk3399_code_table[] = {
        {0, -40000},
-       {593, -40000},
-       {598, -35000},
-       {603, -30000},
-       {609, -25000},
-       {614, -20000},
-       {619, -15000},
-       {625, -10000},
-       {630, -5000},
-       {635, 0},
-       {641, 5000},
-       {646, 10000},
-       {651, 15000},
-       {657, 20000},
-       {662, 25000},
-       {667, 30000},
-       {673, 35000},
-       {678, 40000},
-       {684, 45000},
-       {689, 50000},
-       {694, 55000},
-       {700, 60000},
-       {705, 65000},
-       {711, 70000},
-       {716, 75000},
-       {722, 80000},
-       {727, 85000},
-       {733, 90000},
-       {738, 95000},
-       {743, 100000},
-       {749, 105000},
-       {754, 110000},
-       {760, 115000},
-       {765, 120000},
-       {771, 125000},
+       {402, -40000},
+       {410, -35000},
+       {419, -30000},
+       {427, -25000},
+       {436, -20000},
+       {444, -15000},
+       {453, -10000},
+       {461, -5000},
+       {470, 0},
+       {478, 5000},
+       {487, 10000},
+       {496, 15000},
+       {504, 20000},
+       {513, 25000},
+       {521, 30000},
+       {530, 35000},
+       {538, 40000},
+       {547, 45000},
+       {555, 50000},
+       {564, 55000},
+       {573, 60000},
+       {581, 65000},
+       {590, 70000},
+       {599, 75000},
+       {607, 80000},
+       {616, 85000},
+       {624, 90000},
+       {633, 95000},
+       {642, 100000},
+       {650, 105000},
+       {659, 110000},
+       {668, 115000},
+       {677, 120000},
+       {685, 125000},
        {TSADCV3_DATA_MASK, 125000},
 };
 
@@ -405,8 +450,8 @@ static int rk_tsadcv2_code_to_temp(struct chip_tsadc_table table, u32 code,
                        return -EAGAIN;         /* Incorrect reading */
 
                while (low <= high) {
-                       if (code >= table.id[mid - 1].code &&
-                           code < table.id[mid].code)
+                       if (code <= table.id[mid].code &&
+                           code > table.id[mid - 1].code)
                                break;
                        else if (code > table.id[mid].code)
                                low = mid + 1;
@@ -449,7 +494,7 @@ static int rk_tsadcv2_code_to_temp(struct chip_tsadc_table table, u32 code,
  *     If the temperature is higher than COMP_INT or COMP_SHUT for
  *     "debounce" times, TSADC controller will generate interrupt or TSHUT.
  */
-static void rk_tsadcv2_initialize(void __iomem *regs,
+static void rk_tsadcv2_initialize(struct regmap *grf, void __iomem *regs,
                                  enum tshut_polarity tshut_polarity)
 {
        if (tshut_polarity == TSHUT_HIGH_ACTIVE)
@@ -466,6 +511,62 @@ static void rk_tsadcv2_initialize(void __iomem *regs,
                       regs + TSADCV2_AUTO_PERIOD_HT);
        writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
                       regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
+
+       if (IS_ERR(grf)) {
+               pr_warn("%s: Missing rockchip,grf property\n", __func__);
+               return;
+       }
+}
+
+/**
+ * rk_tsadcv3_initialize - initialize TASDC Controller.
+ *
+ * (1) The tsadc control power sequence.
+ *
+ * (2) Set TSADC_V2_AUTO_PERIOD:
+ *     Configure the interleave between every two accessing of
+ *     TSADC in normal operation.
+ *
+ * (2) Set TSADCV2_AUTO_PERIOD_HT:
+ *     Configure the interleave between every two accessing of
+ *     TSADC after the temperature is higher than COM_SHUT or COM_INT.
+ *
+ * (3) Set TSADCV2_HIGH_INT_DEBOUNCE and TSADC_HIGHT_TSHUT_DEBOUNCE:
+ *     If the temperature is higher than COMP_INT or COMP_SHUT for
+ *     "debounce" times, TSADC controller will generate interrupt or TSHUT.
+ */
+static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs,
+                                 enum tshut_polarity tshut_polarity)
+{
+       /* The tsadc control power sequence */
+       if (IS_ERR(grf)) {
+               /* Set interleave value to workround ic time sync issue */
+               writel_relaxed(TSADCV2_USER_INTER_PD_SOC, regs +
+                              TSADCV2_USER_CON);
+       } else {
+               regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_ON);
+               mdelay(10);
+               regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_OFF);
+               usleep_range(15, 100); /* The spec note says at least 15 us */
+               regmap_write(grf, GRF_SARADC_TESTBIT, GRF_SARADC_TESTBIT_ON);
+               regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_TESTBIT_H_ON);
+               usleep_range(90, 200); /* The spec note says at least 90 us */
+       }
+
+       if (tshut_polarity == TSHUT_HIGH_ACTIVE)
+               writel_relaxed(0U | TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
+                              regs + TSADCV2_AUTO_CON);
+       else
+               writel_relaxed(0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
+                              regs + TSADCV2_AUTO_CON);
+
+       writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD);
+       writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT,
+                      regs + TSADCV2_HIGHT_INT_DEBOUNCE);
+       writel_relaxed(TSADCV2_AUTO_PERIOD_HT_TIME,
+                      regs + TSADCV2_AUTO_PERIOD_HT);
+       writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
+                      regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
 }
 
 static void rk_tsadcv2_irq_ack(void __iomem *regs)
@@ -498,10 +599,11 @@ static void rk_tsadcv2_control(void __iomem *regs, bool enable)
 }
 
 /**
- * @rk_tsadcv3_control:
- * TSADC controller works at auto mode, and some SoCs need set the tsadc_q_sel
- * bit on TSADCV2_AUTO_CON[1]. The (1024 - tsadc_q) as output adc value if
- * setting this bit to enable.
+ * rk_tsadcv3_control - the tsadc controller is enabled or disabled.
+ *
+ * NOTE: TSADC controller works at auto mode, and some SoCs need set the
+ * tsadc_q_sel bit on TSADCV2_AUTO_CON[1]. The (1024 - tsadc_q) as output
+ * adc value if setting this bit to enable.
  */
 static void rk_tsadcv3_control(void __iomem *regs, bool enable)
 {
@@ -603,6 +705,30 @@ static const struct rockchip_tsadc_chip rk3288_tsadc_data = {
        },
 };
 
+static const struct rockchip_tsadc_chip rk3366_tsadc_data = {
+       .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
+       .chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */
+       .chn_num = 2, /* two channels for tsadc */
+
+       .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
+       .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
+       .tshut_temp = 95000,
+
+       .initialize = rk_tsadcv3_initialize,
+       .irq_ack = rk_tsadcv3_irq_ack,
+       .control = rk_tsadcv3_control,
+       .get_temp = rk_tsadcv2_get_temp,
+       .set_tshut_temp = rk_tsadcv2_tshut_temp,
+       .set_tshut_mode = rk_tsadcv2_tshut_mode,
+
+       .table = {
+               .id = rk3228_code_table,
+               .length = ARRAY_SIZE(rk3228_code_table),
+               .data_mask = TSADCV3_DATA_MASK,
+               .mode = ADC_INCREMENT,
+       },
+};
+
 static const struct rockchip_tsadc_chip rk3368_tsadc_data = {
        .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
        .chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */
@@ -636,7 +762,7 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = {
        .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
        .tshut_temp = 95000,
 
-       .initialize = rk_tsadcv2_initialize,
+       .initialize = rk_tsadcv3_initialize,
        .irq_ack = rk_tsadcv3_irq_ack,
        .control = rk_tsadcv3_control,
        .get_temp = rk_tsadcv2_get_temp,
@@ -660,6 +786,10 @@ static const struct of_device_id of_rockchip_thermal_match[] = {
                .compatible = "rockchip,rk3288-tsadc",
                .data = (void *)&rk3288_tsadc_data,
        },
+       {
+               .compatible = "rockchip,rk3366-tsadc",
+               .data = (void *)&rk3366_tsadc_data,
+       },
        {
                .compatible = "rockchip,rk3368-tsadc",
                .data = (void *)&rk3368_tsadc_data,
@@ -768,6 +898,11 @@ static int rockchip_configure_from_dt(struct device *dev,
                return -EINVAL;
        }
 
+       /* The tsadc wont to handle the error in here since some SoCs didn't
+        * need this property.
+        */
+       thermal->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+
        return 0;
 }
 
@@ -786,8 +921,8 @@ rockchip_thermal_register_sensor(struct platform_device *pdev,
 
        sensor->thermal = thermal;
        sensor->id = id;
-       sensor->tzd = thermal_zone_of_sensor_register(&pdev->dev, id, sensor,
-                                                     &rockchip_of_thermal_ops);
+       sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, id,
+                                       sensor, &rockchip_of_thermal_ops);
        if (IS_ERR(sensor->tzd)) {
                error = PTR_ERR(sensor->tzd);
                dev_err(&pdev->dev, "failed to register sensor %d: %d\n",
@@ -815,7 +950,7 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
        const struct of_device_id *match;
        struct resource *res;
        int irq;
-       int i, j;
+       int i;
        int error;
 
        match = of_match_node(of_rockchip_thermal_match, np);
@@ -888,7 +1023,8 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
                goto err_disable_pclk;
        }
 
-       thermal->chip->initialize(thermal->regs, thermal->tshut_polarity);
+       thermal->chip->initialize(thermal->grf, thermal->regs,
+                                 thermal->tshut_polarity);
 
        for (i = 0; i < thermal->chip->chn_num; i++) {
                error = rockchip_thermal_register_sensor(pdev, thermal,
@@ -898,9 +1034,6 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev,
                                "failed to register sensor[%d] : error = %d\n",
                                i, error);
-                       for (j = 0; j < i; j++)
-                               thermal_zone_of_sensor_unregister(&pdev->dev,
-                                               thermal->sensors[j].tzd);
                        goto err_disable_pclk;
                }
        }
@@ -912,7 +1045,7 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
        if (error) {
                dev_err(&pdev->dev,
                        "failed to request tsadc irq: %d\n", error);
-               goto err_unregister_sensor;
+               goto err_disable_pclk;
        }
 
        thermal->chip->control(thermal->regs, true);
@@ -924,11 +1057,6 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
 
        return 0;
 
-err_unregister_sensor:
-       while (i--)
-               thermal_zone_of_sensor_unregister(&pdev->dev,
-                                                 thermal->sensors[i].tzd);
-
 err_disable_pclk:
        clk_disable_unprepare(thermal->pclk);
 err_disable_clk:
@@ -946,7 +1074,6 @@ static int rockchip_thermal_remove(struct platform_device *pdev)
                struct rockchip_thermal_sensor *sensor = &thermal->sensors[i];
 
                rockchip_thermal_toggle_sensor(sensor, false);
-               thermal_zone_of_sensor_unregister(&pdev->dev, sensor->tzd);
        }
 
        thermal->chip->control(thermal->regs, false);
@@ -988,12 +1115,15 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev)
                return error;
 
        error = clk_enable(thermal->pclk);
-       if (error)
+       if (error) {
+               clk_disable(thermal->clk);
                return error;
+       }
 
        rockchip_thermal_reset_controller(thermal->reset);
 
-       thermal->chip->initialize(thermal->regs, thermal->tshut_polarity);
+       thermal->chip->initialize(thermal->grf, thermal->regs,
+                                 thermal->tshut_polarity);
 
        for (i = 0; i < thermal->chip->chn_num; i++) {
                int id = thermal->sensors[i].id;
diff --git a/drivers/thermal/tango_thermal.c b/drivers/thermal/tango_thermal.c
new file mode 100644 (file)
index 0000000..70e0d9f
--- /dev/null
@@ -0,0 +1,109 @@
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+
+/*
+ * According to a data sheet draft, "this temperature sensor uses a bandgap
+ * type of circuit to compare a voltage which has a negative temperature
+ * coefficient with a voltage that is proportional to absolute temperature.
+ * A resistor bank allows 41 different temperature thresholds to be selected
+ * and the logic output will then indicate whether the actual die temperature
+ * lies above or below the selected threshold."
+ */
+
+#define TEMPSI_CMD     0
+#define TEMPSI_RES     4
+#define TEMPSI_CFG     8
+
+#define CMD_OFF                0
+#define CMD_ON         1
+#define CMD_READ       2
+
+#define IDX_MIN                15
+#define IDX_MAX                40
+
+struct tango_thermal_priv {
+       void __iomem *base;
+       int thresh_idx;
+};
+
+static bool temp_above_thresh(void __iomem *base, int thresh_idx)
+{
+       writel(CMD_READ | thresh_idx << 8, base + TEMPSI_CMD);
+       usleep_range(10, 20);
+       writel(CMD_READ | thresh_idx << 8, base + TEMPSI_CMD);
+
+       return readl(base + TEMPSI_RES);
+}
+
+static int tango_get_temp(void *arg, int *res)
+{
+       struct tango_thermal_priv *priv = arg;
+       int idx = priv->thresh_idx;
+
+       if (temp_above_thresh(priv->base, idx)) {
+               /* Search upward by incrementing thresh_idx */
+               while (idx < IDX_MAX && temp_above_thresh(priv->base, ++idx))
+                       cpu_relax();
+               idx = idx - 1; /* always return lower bound */
+       } else {
+               /* Search downward by decrementing thresh_idx */
+               while (idx > IDX_MIN && !temp_above_thresh(priv->base, --idx))
+                       cpu_relax();
+       }
+
+       *res = (idx * 9 / 2 - 38) * 1000; /* millidegrees Celsius */
+       priv->thresh_idx = idx;
+
+       return 0;
+}
+
+static const struct thermal_zone_of_device_ops ops = {
+       .get_temp       = tango_get_temp,
+};
+
+static int tango_thermal_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       struct tango_thermal_priv *priv;
+       struct thermal_zone_device *tzdev;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(priv->base))
+               return PTR_ERR(priv->base);
+
+       priv->thresh_idx = IDX_MIN;
+       writel(0, priv->base + TEMPSI_CFG);
+       writel(CMD_ON, priv->base + TEMPSI_CMD);
+
+       tzdev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv, &ops);
+       return PTR_ERR_OR_ZERO(tzdev);
+}
+
+static const struct of_device_id tango_sensor_ids[] = {
+       {
+               .compatible = "sigma,smp8758-thermal",
+       },
+       { /* sentinel */ }
+};
+
+static struct platform_driver tango_thermal_driver = {
+       .probe  = tango_thermal_probe,
+       .driver = {
+               .name           = "tango-thermal",
+               .of_match_table = tango_sensor_ids,
+       },
+};
+
+module_platform_driver(tango_thermal_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sigma Designs");
+MODULE_DESCRIPTION("Tango temperature sensor");
diff --git a/drivers/thermal/tegra/Kconfig b/drivers/thermal/tegra/Kconfig
new file mode 100644 (file)
index 0000000..cec586e
--- /dev/null
@@ -0,0 +1,13 @@
+menu "NVIDIA Tegra thermal drivers"
+depends on ARCH_TEGRA
+
+config TEGRA_SOCTHERM
+       tristate "Tegra SOCTHERM thermal management"
+       help
+         Enable this option for integrated thermal management support on NVIDIA
+         Tegra systems-on-chip. The driver supports four thermal zones
+         (CPU, GPU, MEM, PLLX). Cooling devices can be bound to the thermal
+         zones to manage temperatures. This option is also required for the
+         emergency thermal reset (thermtrip) feature to function.
+
+endmenu
diff --git a/drivers/thermal/tegra/Makefile b/drivers/thermal/tegra/Makefile
new file mode 100644 (file)
index 0000000..1ce1af2
--- /dev/null
@@ -0,0 +1,6 @@
+obj-$(CONFIG_TEGRA_SOCTHERM)   += tegra-soctherm.o
+
+tegra-soctherm-y                               := soctherm.o soctherm-fuse.o
+tegra-soctherm-$(CONFIG_ARCH_TEGRA_124_SOC)    += tegra124-soctherm.o
+tegra-soctherm-$(CONFIG_ARCH_TEGRA_132_SOC)    += tegra132-soctherm.o
+tegra-soctherm-$(CONFIG_ARCH_TEGRA_210_SOC)    += tegra210-soctherm.o
diff --git a/drivers/thermal/tegra/soctherm-fuse.c b/drivers/thermal/tegra/soctherm-fuse.c
new file mode 100644 (file)
index 0000000..2996318
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <soc/tegra/fuse.h>
+
+#include "soctherm.h"
+
+#define NOMINAL_CALIB_FT                       105
+#define NOMINAL_CALIB_CP                       25
+
+#define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK     0x1fff
+#define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK     (0x1fff << 13)
+#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT    13
+
+#define FUSE_TSENSOR_COMMON                    0x180
+
+/*
+ * Tegra210: Layout of bits in FUSE_TSENSOR_COMMON:
+ *    3                   2                   1                   0
+ *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |       BASE_FT       |      BASE_CP      | SHFT_FT | SHIFT_CP  |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Tegra12x, etc:
+ * In chips prior to Tegra210, this fuse was incorrectly sized as 26 bits,
+ * and didn't hold SHIFT_CP in [31:26]. Therefore these missing six bits
+ * were obtained via the FUSE_SPARE_REALIGNMENT_REG register [5:0].
+ *
+ * FUSE_TSENSOR_COMMON:
+ *    3                   2                   1                   0
+ *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |-----------| SHFT_FT |       BASE_FT       |      BASE_CP      |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * FUSE_SPARE_REALIGNMENT_REG:
+ *    3                   2                   1                   0
+ *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |---------------------------------------------------| SHIFT_CP  |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+#define CALIB_COEFFICIENT 1000000LL
+
+/**
+ * div64_s64_precise() - wrapper for div64_s64()
+ * @a:  the dividend
+ * @b:  the divisor
+ *
+ * Implements division with fairly accurate rounding instead of truncation by
+ * shifting the dividend to the left by 16 so that the quotient has a
+ * much higher precision.
+ *
+ * Return: the quotient of a / b.
+ */
+static s64 div64_s64_precise(s64 a, s32 b)
+{
+       s64 r, al;
+
+       /* Scale up for increased precision division */
+       al = a << 16;
+
+       r = div64_s64(al * 2 + 1, 2 * b);
+       return r >> 16;
+}
+
+int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
+                           struct tsensor_shared_calib *shared)
+{
+       u32 val;
+       s32 shifted_cp, shifted_ft;
+       int err;
+
+       err = tegra_fuse_readl(FUSE_TSENSOR_COMMON, &val);
+       if (err)
+               return err;
+
+       shared->base_cp = (val & tfuse->fuse_base_cp_mask) >>
+                         tfuse->fuse_base_cp_shift;
+       shared->base_ft = (val & tfuse->fuse_base_ft_mask) >>
+                         tfuse->fuse_base_ft_shift;
+
+       shifted_ft = (val & tfuse->fuse_shift_ft_mask) >>
+                    tfuse->fuse_shift_ft_shift;
+       shifted_ft = sign_extend32(shifted_ft, 4);
+
+       if (tfuse->fuse_spare_realignment) {
+               err = tegra_fuse_readl(tfuse->fuse_spare_realignment, &val);
+               if (err)
+                       return err;
+       }
+
+       shifted_cp = sign_extend32(val, 5);
+
+       shared->actual_temp_cp = 2 * NOMINAL_CALIB_CP + shifted_cp;
+       shared->actual_temp_ft = 2 * NOMINAL_CALIB_FT + shifted_ft;
+
+       return 0;
+}
+
+int tegra_calc_tsensor_calib(const struct tegra_tsensor *sensor,
+                            const struct tsensor_shared_calib *shared,
+                            u32 *calibration)
+{
+       const struct tegra_tsensor_group *sensor_group;
+       u32 val, calib;
+       s32 actual_tsensor_ft, actual_tsensor_cp;
+       s32 delta_sens, delta_temp;
+       s32 mult, div;
+       s16 therma, thermb;
+       s64 temp;
+       int err;
+
+       sensor_group = sensor->group;
+
+       err = tegra_fuse_readl(sensor->calib_fuse_offset, &val);
+       if (err)
+               return err;
+
+       actual_tsensor_cp = (shared->base_cp * 64) + sign_extend32(val, 12);
+       val = (val & FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK) >>
+             FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT;
+       actual_tsensor_ft = (shared->base_ft * 32) + sign_extend32(val, 12);
+
+       delta_sens = actual_tsensor_ft - actual_tsensor_cp;
+       delta_temp = shared->actual_temp_ft - shared->actual_temp_cp;
+
+       mult = sensor_group->pdiv * sensor->config->tsample_ate;
+       div = sensor->config->tsample * sensor_group->pdiv_ate;
+
+       temp = (s64)delta_temp * (1LL << 13) * mult;
+       therma = div64_s64_precise(temp, (s64)delta_sens * div);
+
+       temp = ((s64)actual_tsensor_ft * shared->actual_temp_cp) -
+               ((s64)actual_tsensor_cp * shared->actual_temp_ft);
+       thermb = div64_s64_precise(temp, delta_sens);
+
+       temp = (s64)therma * sensor->fuse_corr_alpha;
+       therma = div64_s64_precise(temp, CALIB_COEFFICIENT);
+
+       temp = (s64)thermb * sensor->fuse_corr_alpha + sensor->fuse_corr_beta;
+       thermb = div64_s64_precise(temp, CALIB_COEFFICIENT);
+
+       calib = ((u16)therma << SENSOR_CONFIG2_THERMA_SHIFT) |
+               ((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT);
+
+       *calibration = calib;
+
+       return 0;
+}
+
+MODULE_AUTHOR("Wei Ni <wni@nvidia.com>");
+MODULE_DESCRIPTION("Tegra SOCTHERM fuse management");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c
new file mode 100644 (file)
index 0000000..b865172
--- /dev/null
@@ -0,0 +1,685 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Author:
+ *     Mikko Perttunen <mperttunen@nvidia.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/thermal.h>
+
+#include <dt-bindings/thermal/tegra124-soctherm.h>
+
+#include "soctherm.h"
+
+#define SENSOR_CONFIG0                         0
+#define SENSOR_CONFIG0_STOP                    BIT(0)
+#define SENSOR_CONFIG0_CPTR_OVER               BIT(2)
+#define SENSOR_CONFIG0_OVER                    BIT(3)
+#define SENSOR_CONFIG0_TCALC_OVER              BIT(4)
+#define SENSOR_CONFIG0_TALL_MASK               (0xfffff << 8)
+#define SENSOR_CONFIG0_TALL_SHIFT              8
+
+#define SENSOR_CONFIG1                         4
+#define SENSOR_CONFIG1_TSAMPLE_MASK            0x3ff
+#define SENSOR_CONFIG1_TSAMPLE_SHIFT           0
+#define SENSOR_CONFIG1_TIDDQ_EN_MASK           (0x3f << 15)
+#define SENSOR_CONFIG1_TIDDQ_EN_SHIFT          15
+#define SENSOR_CONFIG1_TEN_COUNT_MASK          (0x3f << 24)
+#define SENSOR_CONFIG1_TEN_COUNT_SHIFT         24
+#define SENSOR_CONFIG1_TEMP_ENABLE             BIT(31)
+
+/*
+ * SENSOR_CONFIG2 is defined in soctherm.h
+ * because, it will be used by tegra_soctherm_fuse.c
+ */
+
+#define SENSOR_STATUS0                         0xc
+#define SENSOR_STATUS0_VALID_MASK              BIT(31)
+#define SENSOR_STATUS0_CAPTURE_MASK            0xffff
+
+#define SENSOR_STATUS1                         0x10
+#define SENSOR_STATUS1_TEMP_VALID_MASK         BIT(31)
+#define SENSOR_STATUS1_TEMP_MASK               0xffff
+
+#define READBACK_VALUE_MASK                    0xff00
+#define READBACK_VALUE_SHIFT                   8
+#define READBACK_ADD_HALF                      BIT(7)
+#define READBACK_NEGATE                                BIT(0)
+
+/* get val from register(r) mask bits(m) */
+#define REG_GET_MASK(r, m)     (((r) & (m)) >> (ffs(m) - 1))
+/* set val(v) to mask bits(m) of register(r) */
+#define REG_SET_MASK(r, m, v)  (((r) & ~(m)) | \
+                                (((v) & (m >> (ffs(m) - 1))) << (ffs(m) - 1)))
+
+static const int min_low_temp = -127000;
+static const int max_high_temp = 127000;
+
+struct tegra_thermctl_zone {
+       void __iomem *reg;
+       struct device *dev;
+       struct thermal_zone_device *tz;
+       const struct tegra_tsensor_group *sg;
+};
+
+struct tegra_soctherm {
+       struct reset_control *reset;
+       struct clk *clock_tsensor;
+       struct clk *clock_soctherm;
+       void __iomem *regs;
+       struct thermal_zone_device **thermctl_tzs;
+
+       u32 *calib;
+       struct tegra_soctherm_soc *soc;
+
+       struct dentry *debugfs_dir;
+};
+
+static void enable_tsensor(struct tegra_soctherm *tegra, unsigned int i)
+{
+       const struct tegra_tsensor *sensor = &tegra->soc->tsensors[i];
+       void __iomem *base = tegra->regs + sensor->base;
+       unsigned int val;
+
+       val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT;
+       writel(val, base + SENSOR_CONFIG0);
+
+       val  = (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT;
+       val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT;
+       val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT;
+       val |= SENSOR_CONFIG1_TEMP_ENABLE;
+       writel(val, base + SENSOR_CONFIG1);
+
+       writel(tegra->calib[i], base + SENSOR_CONFIG2);
+}
+
+/*
+ * Translate from soctherm readback format to millicelsius.
+ * The soctherm readback format in bits is as follows:
+ *   TTTTTTTT H______N
+ * where T's contain the temperature in Celsius,
+ * H denotes an addition of 0.5 Celsius and N denotes negation
+ * of the final value.
+ */
+static int translate_temp(u16 val)
+{
+       int t;
+
+       t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000;
+       if (val & READBACK_ADD_HALF)
+               t += 500;
+       if (val & READBACK_NEGATE)
+               t *= -1;
+
+       return t;
+}
+
+static int tegra_thermctl_get_temp(void *data, int *out_temp)
+{
+       struct tegra_thermctl_zone *zone = data;
+       u32 val;
+
+       val = readl(zone->reg);
+       val = REG_GET_MASK(val, zone->sg->sensor_temp_mask);
+       *out_temp = translate_temp(val);
+
+       return 0;
+}
+
+static int
+thermtrip_program(struct device *dev, const struct tegra_tsensor_group *sg,
+                 int trip_temp);
+
+static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp)
+{
+       struct tegra_thermctl_zone *zone = data;
+       struct thermal_zone_device *tz = zone->tz;
+       const struct tegra_tsensor_group *sg = zone->sg;
+       struct device *dev = zone->dev;
+       enum thermal_trip_type type;
+       int ret;
+
+       if (!tz)
+               return -EINVAL;
+
+       ret = tz->ops->get_trip_type(tz, trip, &type);
+       if (ret)
+               return ret;
+
+       if (type != THERMAL_TRIP_CRITICAL)
+               return 0;
+
+       return thermtrip_program(dev, sg, temp);
+}
+
+static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = {
+       .get_temp = tegra_thermctl_get_temp,
+       .set_trip_temp = tegra_thermctl_set_trip_temp,
+};
+
+/**
+ * enforce_temp_range() - check and enforce temperature range [min, max]
+ * @trip_temp: the trip temperature to check
+ *
+ * Checks and enforces the permitted temperature range that SOC_THERM
+ * HW can support This is
+ * done while taking care of precision.
+ *
+ * Return: The precision adjusted capped temperature in millicelsius.
+ */
+static int enforce_temp_range(struct device *dev, int trip_temp)
+{
+       int temp;
+
+       temp = clamp_val(trip_temp, min_low_temp, max_high_temp);
+       if (temp != trip_temp)
+               dev_info(dev, "soctherm: trip temperature %d forced to %d\n",
+                        trip_temp, temp);
+       return temp;
+}
+
+/**
+ * thermtrip_program() - Configures the hardware to shut down the
+ * system if a given sensor group reaches a given temperature
+ * @dev: ptr to the struct device for the SOC_THERM IP block
+ * @sg: pointer to the sensor group to set the thermtrip temperature for
+ * @trip_temp: the temperature in millicelsius to trigger the thermal trip at
+ *
+ * Sets the thermal trip threshold of the given sensor group to be the
+ * @trip_temp.  If this threshold is crossed, the hardware will shut
+ * down.
+ *
+ * Note that, although @trip_temp is specified in millicelsius, the
+ * hardware is programmed in degrees Celsius.
+ *
+ * Return: 0 upon success, or %-EINVAL upon failure.
+ */
+static int thermtrip_program(struct device *dev,
+                            const struct tegra_tsensor_group *sg,
+                            int trip_temp)
+{
+       struct tegra_soctherm *ts = dev_get_drvdata(dev);
+       int temp;
+       u32 r;
+
+       if (!sg || !sg->thermtrip_threshold_mask)
+               return -EINVAL;
+
+       temp = enforce_temp_range(dev, trip_temp) / ts->soc->thresh_grain;
+
+       r = readl(ts->regs + THERMCTL_THERMTRIP_CTL);
+       r = REG_SET_MASK(r, sg->thermtrip_threshold_mask, temp);
+       r = REG_SET_MASK(r, sg->thermtrip_enable_mask, 1);
+       r = REG_SET_MASK(r, sg->thermtrip_any_en_mask, 0);
+       writel(r, ts->regs + THERMCTL_THERMTRIP_CTL);
+
+       return 0;
+}
+
+/**
+ * tegra_soctherm_set_hwtrips() - set HW trip point from DT data
+ * @dev: struct device * of the SOC_THERM instance
+ *
+ * Configure the SOC_THERM HW trip points, setting "THERMTRIP"
+ * trip points , using "critical" type trip_temp from thermal
+ * zone.
+ * After they have been configured, THERMTRIP will take action
+ * when the configured SoC thermal sensor group reaches a
+ * certain temperature.
+ *
+ * Return: 0 upon success, or a negative error code on failure.
+ * "Success" does not mean that trips was enabled; it could also
+ * mean that no node was found in DT.
+ * THERMTRIP has been enabled successfully when a message similar to
+ * this one appears on the serial console:
+ * "thermtrip: will shut down when sensor group XXX reaches YYYYYY mC"
+ */
+static int tegra_soctherm_set_hwtrips(struct device *dev,
+                                     const struct tegra_tsensor_group *sg,
+                                     struct thermal_zone_device *tz)
+{
+       int temperature;
+       int ret;
+
+       ret = tz->ops->get_crit_temp(tz, &temperature);
+       if (ret) {
+               dev_warn(dev, "thermtrip: %s: missing critical temperature\n",
+                        sg->name);
+               return ret;
+       }
+
+       ret = thermtrip_program(dev, sg, temperature);
+       if (ret) {
+               dev_err(dev, "thermtrip: %s: error during enable\n",
+                       sg->name);
+               return ret;
+       }
+
+       dev_info(dev,
+                "thermtrip: will shut down when %s reaches %d mC\n",
+                sg->name, temperature);
+
+       return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int regs_show(struct seq_file *s, void *data)
+{
+       struct platform_device *pdev = s->private;
+       struct tegra_soctherm *ts = platform_get_drvdata(pdev);
+       const struct tegra_tsensor *tsensors = ts->soc->tsensors;
+       const struct tegra_tsensor_group **ttgs = ts->soc->ttgs;
+       u32 r, state;
+       int i;
+
+       seq_puts(s, "-----TSENSE (convert HW)-----\n");
+
+       for (i = 0; i < ts->soc->num_tsensors; i++) {
+               r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG1);
+               state = REG_GET_MASK(r, SENSOR_CONFIG1_TEMP_ENABLE);
+
+               seq_printf(s, "%s: ", tsensors[i].name);
+               seq_printf(s, "En(%d) ", state);
+
+               if (!state) {
+                       seq_puts(s, "\n");
+                       continue;
+               }
+
+               state = REG_GET_MASK(r, SENSOR_CONFIG1_TIDDQ_EN_MASK);
+               seq_printf(s, "tiddq(%d) ", state);
+               state = REG_GET_MASK(r, SENSOR_CONFIG1_TEN_COUNT_MASK);
+               seq_printf(s, "ten_count(%d) ", state);
+               state = REG_GET_MASK(r, SENSOR_CONFIG1_TSAMPLE_MASK);
+               seq_printf(s, "tsample(%d) ", state + 1);
+
+               r = readl(ts->regs + tsensors[i].base + SENSOR_STATUS1);
+               state = REG_GET_MASK(r, SENSOR_STATUS1_TEMP_VALID_MASK);
+               seq_printf(s, "Temp(%d/", state);
+               state = REG_GET_MASK(r, SENSOR_STATUS1_TEMP_MASK);
+               seq_printf(s, "%d) ", translate_temp(state));
+
+               r = readl(ts->regs + tsensors[i].base + SENSOR_STATUS0);
+               state = REG_GET_MASK(r, SENSOR_STATUS0_VALID_MASK);
+               seq_printf(s, "Capture(%d/", state);
+               state = REG_GET_MASK(r, SENSOR_STATUS0_CAPTURE_MASK);
+               seq_printf(s, "%d) ", state);
+
+               r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG0);
+               state = REG_GET_MASK(r, SENSOR_CONFIG0_STOP);
+               seq_printf(s, "Stop(%d) ", state);
+               state = REG_GET_MASK(r, SENSOR_CONFIG0_TALL_MASK);
+               seq_printf(s, "Tall(%d) ", state);
+               state = REG_GET_MASK(r, SENSOR_CONFIG0_TCALC_OVER);
+               seq_printf(s, "Over(%d/", state);
+               state = REG_GET_MASK(r, SENSOR_CONFIG0_OVER);
+               seq_printf(s, "%d/", state);
+               state = REG_GET_MASK(r, SENSOR_CONFIG0_CPTR_OVER);
+               seq_printf(s, "%d) ", state);
+
+               r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG2);
+               state = REG_GET_MASK(r, SENSOR_CONFIG2_THERMA_MASK);
+               seq_printf(s, "Therm_A/B(%d/", state);
+               state = REG_GET_MASK(r, SENSOR_CONFIG2_THERMB_MASK);
+               seq_printf(s, "%d)\n", (s16)state);
+       }
+
+       r = readl(ts->regs + SENSOR_PDIV);
+       seq_printf(s, "PDIV: 0x%x\n", r);
+
+       r = readl(ts->regs + SENSOR_HOTSPOT_OFF);
+       seq_printf(s, "HOTSPOT: 0x%x\n", r);
+
+       seq_puts(s, "\n");
+       seq_puts(s, "-----SOC_THERM-----\n");
+
+       r = readl(ts->regs + SENSOR_TEMP1);
+       state = REG_GET_MASK(r, SENSOR_TEMP1_CPU_TEMP_MASK);
+       seq_printf(s, "Temperatures: CPU(%d) ", translate_temp(state));
+       state = REG_GET_MASK(r, SENSOR_TEMP1_GPU_TEMP_MASK);
+       seq_printf(s, " GPU(%d) ", translate_temp(state));
+       r = readl(ts->regs + SENSOR_TEMP2);
+       state = REG_GET_MASK(r, SENSOR_TEMP2_PLLX_TEMP_MASK);
+       seq_printf(s, " PLLX(%d) ", translate_temp(state));
+       state = REG_GET_MASK(r, SENSOR_TEMP2_MEM_TEMP_MASK);
+       seq_printf(s, " MEM(%d)\n", translate_temp(state));
+
+       r = readl(ts->regs + THERMCTL_THERMTRIP_CTL);
+       state = REG_GET_MASK(r, ttgs[0]->thermtrip_any_en_mask);
+       seq_printf(s, "Thermtrip Any En(%d)\n", state);
+       for (i = 0; i < ts->soc->num_ttgs; i++) {
+               state = REG_GET_MASK(r, ttgs[i]->thermtrip_enable_mask);
+               seq_printf(s, "     %s En(%d) ", ttgs[i]->name, state);
+               state = REG_GET_MASK(r, ttgs[i]->thermtrip_threshold_mask);
+               state *= ts->soc->thresh_grain;
+               seq_printf(s, "Thresh(%d)\n", state);
+       }
+
+       return 0;
+}
+
+static int regs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, regs_show, inode->i_private);
+}
+
+static const struct file_operations regs_fops = {
+       .open           = regs_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void soctherm_debug_init(struct platform_device *pdev)
+{
+       struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
+       struct dentry *root, *file;
+
+       root = debugfs_create_dir("soctherm", NULL);
+       if (!root) {
+               dev_err(&pdev->dev, "failed to create debugfs directory\n");
+               return;
+       }
+
+       tegra->debugfs_dir = root;
+
+       file = debugfs_create_file("reg_contents", 0644, root,
+                                  pdev, &regs_fops);
+       if (!file) {
+               dev_err(&pdev->dev, "failed to create debugfs file\n");
+               debugfs_remove_recursive(tegra->debugfs_dir);
+               tegra->debugfs_dir = NULL;
+       }
+}
+#else
+static inline void soctherm_debug_init(struct platform_device *pdev) {}
+#endif
+
+static int soctherm_clk_enable(struct platform_device *pdev, bool enable)
+{
+       struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
+       int err;
+
+       if (!tegra->clock_soctherm || !tegra->clock_tsensor)
+               return -EINVAL;
+
+       reset_control_assert(tegra->reset);
+
+       if (enable) {
+               err = clk_prepare_enable(tegra->clock_soctherm);
+               if (err) {
+                       reset_control_deassert(tegra->reset);
+                       return err;
+               }
+
+               err = clk_prepare_enable(tegra->clock_tsensor);
+               if (err) {
+                       clk_disable_unprepare(tegra->clock_soctherm);
+                       reset_control_deassert(tegra->reset);
+                       return err;
+               }
+       } else {
+               clk_disable_unprepare(tegra->clock_tsensor);
+               clk_disable_unprepare(tegra->clock_soctherm);
+       }
+
+       reset_control_deassert(tegra->reset);
+
+       return 0;
+}
+
+static void soctherm_init(struct platform_device *pdev)
+{
+       struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
+       const struct tegra_tsensor_group **ttgs = tegra->soc->ttgs;
+       int i;
+       u32 pdiv, hotspot;
+
+       /* Initialize raw sensors */
+       for (i = 0; i < tegra->soc->num_tsensors; ++i)
+               enable_tsensor(tegra, i);
+
+       /* program pdiv and hotspot offsets per THERM */
+       pdiv = readl(tegra->regs + SENSOR_PDIV);
+       hotspot = readl(tegra->regs + SENSOR_HOTSPOT_OFF);
+       for (i = 0; i < tegra->soc->num_ttgs; ++i) {
+               pdiv = REG_SET_MASK(pdiv, ttgs[i]->pdiv_mask,
+                                   ttgs[i]->pdiv);
+               /* hotspot offset from PLLX, doesn't need to configure PLLX */
+               if (ttgs[i]->id == TEGRA124_SOCTHERM_SENSOR_PLLX)
+                       continue;
+               hotspot =  REG_SET_MASK(hotspot,
+                                       ttgs[i]->pllx_hotspot_mask,
+                                       ttgs[i]->pllx_hotspot_diff);
+       }
+       writel(pdiv, tegra->regs + SENSOR_PDIV);
+       writel(hotspot, tegra->regs + SENSOR_HOTSPOT_OFF);
+}
+
+static const struct of_device_id tegra_soctherm_of_match[] = {
+#ifdef CONFIG_ARCH_TEGRA_124_SOC
+       {
+               .compatible = "nvidia,tegra124-soctherm",
+               .data = &tegra124_soctherm,
+       },
+#endif
+#ifdef CONFIG_ARCH_TEGRA_132_SOC
+       {
+               .compatible = "nvidia,tegra132-soctherm",
+               .data = &tegra132_soctherm,
+       },
+#endif
+#ifdef CONFIG_ARCH_TEGRA_210_SOC
+       {
+               .compatible = "nvidia,tegra210-soctherm",
+               .data = &tegra210_soctherm,
+       },
+#endif
+       { },
+};
+MODULE_DEVICE_TABLE(of, tegra_soctherm_of_match);
+
+static int tegra_soctherm_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *match;
+       struct tegra_soctherm *tegra;
+       struct thermal_zone_device *z;
+       struct tsensor_shared_calib shared_calib;
+       struct resource *res;
+       struct tegra_soctherm_soc *soc;
+       unsigned int i;
+       int err;
+
+       match = of_match_node(tegra_soctherm_of_match, pdev->dev.of_node);
+       if (!match)
+               return -ENODEV;
+
+       soc = (struct tegra_soctherm_soc *)match->data;
+       if (soc->num_ttgs > TEGRA124_SOCTHERM_SENSOR_NUM)
+               return -EINVAL;
+
+       tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
+       if (!tegra)
+               return -ENOMEM;
+
+       dev_set_drvdata(&pdev->dev, tegra);
+
+       tegra->soc = soc;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       tegra->regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(tegra->regs))
+               return PTR_ERR(tegra->regs);
+
+       tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm");
+       if (IS_ERR(tegra->reset)) {
+               dev_err(&pdev->dev, "can't get soctherm reset\n");
+               return PTR_ERR(tegra->reset);
+       }
+
+       tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor");
+       if (IS_ERR(tegra->clock_tsensor)) {
+               dev_err(&pdev->dev, "can't get tsensor clock\n");
+               return PTR_ERR(tegra->clock_tsensor);
+       }
+
+       tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soctherm");
+       if (IS_ERR(tegra->clock_soctherm)) {
+               dev_err(&pdev->dev, "can't get soctherm clock\n");
+               return PTR_ERR(tegra->clock_soctherm);
+       }
+
+       tegra->calib = devm_kzalloc(&pdev->dev,
+                                   sizeof(u32) * soc->num_tsensors,
+                                   GFP_KERNEL);
+       if (!tegra->calib)
+               return -ENOMEM;
+
+       /* calculate shared calibration data */
+       err = tegra_calc_shared_calib(soc->tfuse, &shared_calib);
+       if (err)
+               return err;
+
+       /* calculate tsensor calibaration data */
+       for (i = 0; i < soc->num_tsensors; ++i) {
+               err = tegra_calc_tsensor_calib(&soc->tsensors[i],
+                                              &shared_calib,
+                                              &tegra->calib[i]);
+               if (err)
+                       return err;
+       }
+
+       tegra->thermctl_tzs = devm_kzalloc(&pdev->dev,
+                                          sizeof(*z) * soc->num_ttgs,
+                                          GFP_KERNEL);
+       if (!tegra->thermctl_tzs)
+               return -ENOMEM;
+
+       err = soctherm_clk_enable(pdev, true);
+       if (err)
+               return err;
+
+       soctherm_init(pdev);
+
+       for (i = 0; i < soc->num_ttgs; ++i) {
+               struct tegra_thermctl_zone *zone =
+                       devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL);
+               if (!zone) {
+                       err = -ENOMEM;
+                       goto disable_clocks;
+               }
+
+               zone->reg = tegra->regs + soc->ttgs[i]->sensor_temp_offset;
+               zone->dev = &pdev->dev;
+               zone->sg = soc->ttgs[i];
+
+               z = devm_thermal_zone_of_sensor_register(&pdev->dev,
+                                                        soc->ttgs[i]->id, zone,
+                                                        &tegra_of_thermal_ops);
+               if (IS_ERR(z)) {
+                       err = PTR_ERR(z);
+                       dev_err(&pdev->dev, "failed to register sensor: %d\n",
+                               err);
+                       goto disable_clocks;
+               }
+
+               zone->tz = z;
+               tegra->thermctl_tzs[soc->ttgs[i]->id] = z;
+
+               /* Configure hw trip points */
+               tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z);
+       }
+
+       soctherm_debug_init(pdev);
+
+       return 0;
+
+disable_clocks:
+       soctherm_clk_enable(pdev, false);
+
+       return err;
+}
+
+static int tegra_soctherm_remove(struct platform_device *pdev)
+{
+       struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
+
+       debugfs_remove_recursive(tegra->debugfs_dir);
+
+       soctherm_clk_enable(pdev, false);
+
+       return 0;
+}
+
+static int __maybe_unused soctherm_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+
+       soctherm_clk_enable(pdev, false);
+
+       return 0;
+}
+
+static int __maybe_unused soctherm_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
+       struct tegra_soctherm_soc *soc = tegra->soc;
+       int err, i;
+
+       err = soctherm_clk_enable(pdev, true);
+       if (err) {
+               dev_err(&pdev->dev,
+                       "Resume failed: enable clocks failed\n");
+               return err;
+       }
+
+       soctherm_init(pdev);
+
+       for (i = 0; i < soc->num_ttgs; ++i) {
+               struct thermal_zone_device *tz;
+
+               tz = tegra->thermctl_tzs[soc->ttgs[i]->id];
+               tegra_soctherm_set_hwtrips(dev, soc->ttgs[i], tz);
+       }
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(tegra_soctherm_pm, soctherm_suspend, soctherm_resume);
+
+static struct platform_driver tegra_soctherm_driver = {
+       .probe = tegra_soctherm_probe,
+       .remove = tegra_soctherm_remove,
+       .driver = {
+               .name = "tegra_soctherm",
+               .pm = &tegra_soctherm_pm,
+               .of_match_table = tegra_soctherm_of_match,
+       },
+};
+module_platform_driver(tegra_soctherm_driver);
+
+MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
+MODULE_DESCRIPTION("NVIDIA Tegra SOCTHERM thermal management driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/tegra/soctherm.h b/drivers/thermal/tegra/soctherm.h
new file mode 100644 (file)
index 0000000..28e18ec
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  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 __DRIVERS_THERMAL_TEGRA_SOCTHERM_H
+#define __DRIVERS_THERMAL_TEGRA_SOCTHERM_H
+
+#define SENSOR_CONFIG2                          8
+#define SENSOR_CONFIG2_THERMA_MASK             (0xffff << 16)
+#define SENSOR_CONFIG2_THERMA_SHIFT            16
+#define SENSOR_CONFIG2_THERMB_MASK             0xffff
+#define SENSOR_CONFIG2_THERMB_SHIFT            0
+
+#define THERMCTL_THERMTRIP_CTL                 0x80
+/* BITs are defined in device file */
+
+#define SENSOR_PDIV                            0x1c0
+#define SENSOR_PDIV_CPU_MASK                   (0xf << 12)
+#define SENSOR_PDIV_GPU_MASK                   (0xf << 8)
+#define SENSOR_PDIV_MEM_MASK                   (0xf << 4)
+#define SENSOR_PDIV_PLLX_MASK                  (0xf << 0)
+
+#define SENSOR_HOTSPOT_OFF                     0x1c4
+#define SENSOR_HOTSPOT_CPU_MASK                        (0xff << 16)
+#define SENSOR_HOTSPOT_GPU_MASK                        (0xff << 8)
+#define SENSOR_HOTSPOT_MEM_MASK                        (0xff << 0)
+
+#define SENSOR_TEMP1                           0x1c8
+#define SENSOR_TEMP1_CPU_TEMP_MASK             (0xffff << 16)
+#define SENSOR_TEMP1_GPU_TEMP_MASK             0xffff
+#define SENSOR_TEMP2                           0x1cc
+#define SENSOR_TEMP2_MEM_TEMP_MASK             (0xffff << 16)
+#define SENSOR_TEMP2_PLLX_TEMP_MASK            0xffff
+
+/**
+ * struct tegra_tsensor_group - SOC_THERM sensor group data
+ * @name: short name of the temperature sensor group
+ * @id: numeric ID of the temperature sensor group
+ * @sensor_temp_offset: offset of the SENSOR_TEMP* register
+ * @sensor_temp_mask: bit mask for this sensor group in SENSOR_TEMP* register
+ * @pdiv: the sensor count post-divider to use during runtime
+ * @pdiv_ate: the sensor count post-divider used during automated test
+ * @pdiv_mask: register bitfield mask for the PDIV field for this sensor
+ * @pllx_hotspot_diff: hotspot offset from the PLLX sensor, must be 0 for
+    PLLX sensor group
+ * @pllx_hotspot_mask: register bitfield mask for the HOTSPOT field
+ */
+struct tegra_tsensor_group {
+       const char *name;
+       u8 id;
+       u16 sensor_temp_offset;
+       u32 sensor_temp_mask;
+       u32 pdiv, pdiv_ate, pdiv_mask;
+       u32 pllx_hotspot_diff, pllx_hotspot_mask;
+       u32 thermtrip_enable_mask;
+       u32 thermtrip_any_en_mask;
+       u32 thermtrip_threshold_mask;
+};
+
+struct tegra_tsensor_configuration {
+       u32 tall, tiddq_en, ten_count, pdiv, pdiv_ate, tsample, tsample_ate;
+};
+
+struct tegra_tsensor {
+       const char *name;
+       const u32 base;
+       const struct tegra_tsensor_configuration *config;
+       const u32 calib_fuse_offset;
+       /*
+        * Correction values used to modify values read from
+        * calibration fuses
+        */
+       const s32 fuse_corr_alpha, fuse_corr_beta;
+       const struct tegra_tsensor_group *group;
+};
+
+struct tegra_soctherm_fuse {
+       u32 fuse_base_cp_mask, fuse_base_cp_shift;
+       u32 fuse_base_ft_mask, fuse_base_ft_shift;
+       u32 fuse_shift_ft_mask, fuse_shift_ft_shift;
+       u32 fuse_spare_realignment;
+};
+
+struct tsensor_shared_calib {
+       u32 base_cp, base_ft;
+       u32 actual_temp_cp, actual_temp_ft;
+};
+
+struct tegra_soctherm_soc {
+       const struct tegra_tsensor *tsensors;
+       const unsigned int num_tsensors;
+       const struct tegra_tsensor_group **ttgs;
+       const unsigned int num_ttgs;
+       const struct tegra_soctherm_fuse *tfuse;
+       const int thresh_grain;
+};
+
+int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
+                           struct tsensor_shared_calib *shared);
+int tegra_calc_tsensor_calib(const struct tegra_tsensor *sensor,
+                            const struct tsensor_shared_calib *shared,
+                            u32 *calib);
+
+#ifdef CONFIG_ARCH_TEGRA_124_SOC
+extern const struct tegra_soctherm_soc tegra124_soctherm;
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_132_SOC
+extern const struct tegra_soctherm_soc tegra132_soctherm;
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_210_SOC
+extern const struct tegra_soctherm_soc tegra210_soctherm;
+#endif
+
+#endif
+
diff --git a/drivers/thermal/tegra/tegra124-soctherm.c b/drivers/thermal/tegra/tegra124-soctherm.c
new file mode 100644 (file)
index 0000000..beb9d36
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/thermal/tegra124-soctherm.h>
+
+#include "soctherm.h"
+
+#define TEGRA124_THERMTRIP_ANY_EN_MASK         (0x1 << 28)
+#define TEGRA124_THERMTRIP_MEM_EN_MASK         (0x1 << 27)
+#define TEGRA124_THERMTRIP_GPU_EN_MASK         (0x1 << 26)
+#define TEGRA124_THERMTRIP_CPU_EN_MASK         (0x1 << 25)
+#define TEGRA124_THERMTRIP_TSENSE_EN_MASK      (0x1 << 24)
+#define TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK  (0xff << 16)
+#define TEGRA124_THERMTRIP_CPU_THRESH_MASK     (0xff << 8)
+#define TEGRA124_THERMTRIP_TSENSE_THRESH_MASK  0xff
+
+#define TEGRA124_THRESH_GRAIN                  1000
+
+static const struct tegra_tsensor_configuration tegra124_tsensor_config = {
+       .tall = 16300,
+       .tiddq_en = 1,
+       .ten_count = 1,
+       .tsample = 120,
+       .tsample_ate = 480,
+};
+
+static const struct tegra_tsensor_group tegra124_tsensor_group_cpu = {
+       .id = TEGRA124_SOCTHERM_SENSOR_CPU,
+       .name   = "cpu",
+       .sensor_temp_offset     = SENSOR_TEMP1,
+       .sensor_temp_mask       = SENSOR_TEMP1_CPU_TEMP_MASK,
+       .pdiv = 8,
+       .pdiv_ate = 8,
+       .pdiv_mask = SENSOR_PDIV_CPU_MASK,
+       .pllx_hotspot_diff = 10,
+       .pllx_hotspot_mask = SENSOR_HOTSPOT_CPU_MASK,
+       .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
+       .thermtrip_enable_mask = TEGRA124_THERMTRIP_CPU_EN_MASK,
+       .thermtrip_threshold_mask = TEGRA124_THERMTRIP_CPU_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group tegra124_tsensor_group_gpu = {
+       .id = TEGRA124_SOCTHERM_SENSOR_GPU,
+       .name = "gpu",
+       .sensor_temp_offset = SENSOR_TEMP1,
+       .sensor_temp_mask = SENSOR_TEMP1_GPU_TEMP_MASK,
+       .pdiv = 8,
+       .pdiv_ate = 8,
+       .pdiv_mask = SENSOR_PDIV_GPU_MASK,
+       .pllx_hotspot_diff = 5,
+       .pllx_hotspot_mask = SENSOR_HOTSPOT_GPU_MASK,
+       .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
+       .thermtrip_enable_mask = TEGRA124_THERMTRIP_GPU_EN_MASK,
+       .thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group tegra124_tsensor_group_pll = {
+       .id = TEGRA124_SOCTHERM_SENSOR_PLLX,
+       .name = "pll",
+       .sensor_temp_offset = SENSOR_TEMP2,
+       .sensor_temp_mask = SENSOR_TEMP2_PLLX_TEMP_MASK,
+       .pdiv = 8,
+       .pdiv_ate = 8,
+       .pdiv_mask = SENSOR_PDIV_PLLX_MASK,
+       .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
+       .thermtrip_enable_mask = TEGRA124_THERMTRIP_TSENSE_EN_MASK,
+       .thermtrip_threshold_mask = TEGRA124_THERMTRIP_TSENSE_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group tegra124_tsensor_group_mem = {
+       .id = TEGRA124_SOCTHERM_SENSOR_MEM,
+       .name = "mem",
+       .sensor_temp_offset = SENSOR_TEMP2,
+       .sensor_temp_mask = SENSOR_TEMP2_MEM_TEMP_MASK,
+       .pdiv = 8,
+       .pdiv_ate = 8,
+       .pdiv_mask = SENSOR_PDIV_MEM_MASK,
+       .pllx_hotspot_diff = 0,
+       .pllx_hotspot_mask = SENSOR_HOTSPOT_MEM_MASK,
+       .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
+       .thermtrip_enable_mask = TEGRA124_THERMTRIP_MEM_EN_MASK,
+       .thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group *tegra124_tsensor_groups[] = {
+       &tegra124_tsensor_group_cpu,
+       &tegra124_tsensor_group_gpu,
+       &tegra124_tsensor_group_pll,
+       &tegra124_tsensor_group_mem,
+};
+
+static const struct tegra_tsensor tegra124_tsensors[] = {
+       {
+               .name = "cpu0",
+               .base = 0xc0,
+               .config = &tegra124_tsensor_config,
+               .calib_fuse_offset = 0x098,
+               .fuse_corr_alpha = 1135400,
+               .fuse_corr_beta = -6266900,
+               .group = &tegra124_tsensor_group_cpu,
+       }, {
+               .name = "cpu1",
+               .base = 0xe0,
+               .config = &tegra124_tsensor_config,
+               .calib_fuse_offset = 0x084,
+               .fuse_corr_alpha = 1122220,
+               .fuse_corr_beta = -5700700,
+               .group = &tegra124_tsensor_group_cpu,
+       }, {
+               .name = "cpu2",
+               .base = 0x100,
+               .config = &tegra124_tsensor_config,
+               .calib_fuse_offset = 0x088,
+               .fuse_corr_alpha = 1127000,
+               .fuse_corr_beta = -6768200,
+               .group = &tegra124_tsensor_group_cpu,
+       }, {
+               .name = "cpu3",
+               .base = 0x120,
+               .config = &tegra124_tsensor_config,
+               .calib_fuse_offset = 0x12c,
+               .fuse_corr_alpha = 1110900,
+               .fuse_corr_beta = -6232000,
+               .group = &tegra124_tsensor_group_cpu,
+       }, {
+               .name = "mem0",
+               .base = 0x140,
+               .config = &tegra124_tsensor_config,
+               .calib_fuse_offset = 0x158,
+               .fuse_corr_alpha = 1122300,
+               .fuse_corr_beta = -5936400,
+               .group = &tegra124_tsensor_group_mem,
+       }, {
+               .name = "mem1",
+               .base = 0x160,
+               .config = &tegra124_tsensor_config,
+               .calib_fuse_offset = 0x15c,
+               .fuse_corr_alpha = 1145700,
+               .fuse_corr_beta = -7124600,
+               .group = &tegra124_tsensor_group_mem,
+       }, {
+               .name = "gpu",
+               .base = 0x180,
+               .config = &tegra124_tsensor_config,
+               .calib_fuse_offset = 0x154,
+               .fuse_corr_alpha = 1120100,
+               .fuse_corr_beta = -6000500,
+               .group = &tegra124_tsensor_group_gpu,
+       }, {
+               .name = "pllx",
+               .base = 0x1a0,
+               .config = &tegra124_tsensor_config,
+               .calib_fuse_offset = 0x160,
+               .fuse_corr_alpha = 1106500,
+               .fuse_corr_beta = -6729300,
+               .group = &tegra124_tsensor_group_pll,
+       },
+};
+
+/*
+ * Mask/shift bits in FUSE_TSENSOR_COMMON and
+ * FUSE_TSENSOR_COMMON, which are described in
+ * tegra_soctherm_fuse.c
+ */
+static const struct tegra_soctherm_fuse tegra124_soctherm_fuse = {
+       .fuse_base_cp_mask = 0x3ff,
+       .fuse_base_cp_shift = 0,
+       .fuse_base_ft_mask = 0x7ff << 10,
+       .fuse_base_ft_shift = 10,
+       .fuse_shift_ft_mask = 0x1f << 21,
+       .fuse_shift_ft_shift = 21,
+       .fuse_spare_realignment = 0x1fc,
+};
+
+const struct tegra_soctherm_soc tegra124_soctherm = {
+       .tsensors = tegra124_tsensors,
+       .num_tsensors = ARRAY_SIZE(tegra124_tsensors),
+       .ttgs = tegra124_tsensor_groups,
+       .num_ttgs = ARRAY_SIZE(tegra124_tsensor_groups),
+       .tfuse = &tegra124_soctherm_fuse,
+       .thresh_grain = TEGRA124_THRESH_GRAIN,
+};
diff --git a/drivers/thermal/tegra/tegra132-soctherm.c b/drivers/thermal/tegra/tegra132-soctherm.c
new file mode 100644 (file)
index 0000000..e2aa84e
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/thermal/tegra124-soctherm.h>
+
+#include "soctherm.h"
+
+#define TEGRA132_THERMTRIP_ANY_EN_MASK         (0x1 << 28)
+#define TEGRA132_THERMTRIP_MEM_EN_MASK         (0x1 << 27)
+#define TEGRA132_THERMTRIP_GPU_EN_MASK         (0x1 << 26)
+#define TEGRA132_THERMTRIP_CPU_EN_MASK         (0x1 << 25)
+#define TEGRA132_THERMTRIP_TSENSE_EN_MASK      (0x1 << 24)
+#define TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK  (0xff << 16)
+#define TEGRA132_THERMTRIP_CPU_THRESH_MASK     (0xff << 8)
+#define TEGRA132_THERMTRIP_TSENSE_THRESH_MASK  0xff
+
+#define TEGRA132_THRESH_GRAIN                  1000
+
+static const struct tegra_tsensor_configuration tegra132_tsensor_config = {
+       .tall = 16300,
+       .tiddq_en = 1,
+       .ten_count = 1,
+       .tsample = 120,
+       .tsample_ate = 480,
+};
+
+static const struct tegra_tsensor_group tegra132_tsensor_group_cpu = {
+       .id = TEGRA124_SOCTHERM_SENSOR_CPU,
+       .name = "cpu",
+       .sensor_temp_offset = SENSOR_TEMP1,
+       .sensor_temp_mask = SENSOR_TEMP1_CPU_TEMP_MASK,
+       .pdiv = 8,
+       .pdiv_ate = 8,
+       .pdiv_mask = SENSOR_PDIV_CPU_MASK,
+       .pllx_hotspot_diff = 10,
+       .pllx_hotspot_mask = SENSOR_HOTSPOT_CPU_MASK,
+       .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
+       .thermtrip_enable_mask = TEGRA132_THERMTRIP_CPU_EN_MASK,
+       .thermtrip_threshold_mask = TEGRA132_THERMTRIP_CPU_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group tegra132_tsensor_group_gpu = {
+       .id = TEGRA124_SOCTHERM_SENSOR_GPU,
+       .name = "gpu",
+       .sensor_temp_offset = SENSOR_TEMP1,
+       .sensor_temp_mask = SENSOR_TEMP1_GPU_TEMP_MASK,
+       .pdiv = 8,
+       .pdiv_ate = 8,
+       .pdiv_mask = SENSOR_PDIV_GPU_MASK,
+       .pllx_hotspot_diff = 5,
+       .pllx_hotspot_mask = SENSOR_HOTSPOT_GPU_MASK,
+       .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
+       .thermtrip_enable_mask = TEGRA132_THERMTRIP_GPU_EN_MASK,
+       .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group tegra132_tsensor_group_pll = {
+       .id = TEGRA124_SOCTHERM_SENSOR_PLLX,
+       .name = "pll",
+       .sensor_temp_offset = SENSOR_TEMP2,
+       .sensor_temp_mask = SENSOR_TEMP2_PLLX_TEMP_MASK,
+       .pdiv = 8,
+       .pdiv_ate = 8,
+       .pdiv_mask = SENSOR_PDIV_PLLX_MASK,
+       .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
+       .thermtrip_enable_mask = TEGRA132_THERMTRIP_TSENSE_EN_MASK,
+       .thermtrip_threshold_mask = TEGRA132_THERMTRIP_TSENSE_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group tegra132_tsensor_group_mem = {
+       .id = TEGRA124_SOCTHERM_SENSOR_MEM,
+       .name = "mem",
+       .sensor_temp_offset = SENSOR_TEMP2,
+       .sensor_temp_mask = SENSOR_TEMP2_MEM_TEMP_MASK,
+       .pdiv = 8,
+       .pdiv_ate = 8,
+       .pdiv_mask = SENSOR_PDIV_MEM_MASK,
+       .pllx_hotspot_diff = 0,
+       .pllx_hotspot_mask = SENSOR_HOTSPOT_MEM_MASK,
+       .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
+       .thermtrip_enable_mask = TEGRA132_THERMTRIP_MEM_EN_MASK,
+       .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group *tegra132_tsensor_groups[] = {
+       &tegra132_tsensor_group_cpu,
+       &tegra132_tsensor_group_gpu,
+       &tegra132_tsensor_group_pll,
+       &tegra132_tsensor_group_mem,
+};
+
+static struct tegra_tsensor tegra132_tsensors[] = {
+       {
+               .name = "cpu0",
+               .base = 0xc0,
+               .config = &tegra132_tsensor_config,
+               .calib_fuse_offset = 0x098,
+               .fuse_corr_alpha = 1126600,
+               .fuse_corr_beta = -9433500,
+               .group = &tegra132_tsensor_group_cpu,
+       }, {
+               .name = "cpu1",
+               .base = 0xe0,
+               .config = &tegra132_tsensor_config,
+               .calib_fuse_offset = 0x084,
+               .fuse_corr_alpha = 1110800,
+               .fuse_corr_beta = -7383000,
+               .group = &tegra132_tsensor_group_cpu,
+       }, {
+               .name = "cpu2",
+               .base = 0x100,
+               .config = &tegra132_tsensor_config,
+               .calib_fuse_offset = 0x088,
+               .fuse_corr_alpha = 1113800,
+               .fuse_corr_beta = -6215200,
+               .group = &tegra132_tsensor_group_cpu,
+       }, {
+               .name = "cpu3",
+               .base = 0x120,
+               .config = &tegra132_tsensor_config,
+               .calib_fuse_offset = 0x12c,
+               .fuse_corr_alpha = 1129600,
+               .fuse_corr_beta = -8196100,
+               .group = &tegra132_tsensor_group_cpu,
+       }, {
+               .name = "mem0",
+               .base = 0x140,
+               .config = &tegra132_tsensor_config,
+               .calib_fuse_offset = 0x158,
+               .fuse_corr_alpha = 1132900,
+               .fuse_corr_beta = -6755300,
+               .group = &tegra132_tsensor_group_mem,
+       }, {
+               .name = "mem1",
+               .base = 0x160,
+               .config = &tegra132_tsensor_config,
+               .calib_fuse_offset = 0x15c,
+               .fuse_corr_alpha = 1142300,
+               .fuse_corr_beta = -7374200,
+               .group = &tegra132_tsensor_group_mem,
+       }, {
+               .name = "gpu",
+               .base = 0x180,
+               .config = &tegra132_tsensor_config,
+               .calib_fuse_offset = 0x154,
+               .fuse_corr_alpha = 1125100,
+               .fuse_corr_beta = -6350400,
+               .group = &tegra132_tsensor_group_gpu,
+       }, {
+               .name = "pllx",
+               .base = 0x1a0,
+               .config = &tegra132_tsensor_config,
+               .calib_fuse_offset = 0x160,
+               .fuse_corr_alpha = 1118100,
+               .fuse_corr_beta = -8208800,
+               .group = &tegra132_tsensor_group_pll,
+       },
+};
+
+/*
+ * Mask/shift bits in FUSE_TSENSOR_COMMON and
+ * FUSE_TSENSOR_COMMON, which are described in
+ * tegra_soctherm_fuse.c
+ */
+static const struct tegra_soctherm_fuse tegra132_soctherm_fuse = {
+       .fuse_base_cp_mask = 0x3ff,
+       .fuse_base_cp_shift = 0,
+       .fuse_base_ft_mask = 0x7ff << 10,
+       .fuse_base_ft_shift = 10,
+       .fuse_shift_ft_mask = 0x1f << 21,
+       .fuse_shift_ft_shift = 21,
+       .fuse_spare_realignment = 0x1fc,
+};
+
+const struct tegra_soctherm_soc tegra132_soctherm = {
+       .tsensors = tegra132_tsensors,
+       .num_tsensors = ARRAY_SIZE(tegra132_tsensors),
+       .ttgs = tegra132_tsensor_groups,
+       .num_ttgs = ARRAY_SIZE(tegra132_tsensor_groups),
+       .tfuse = &tegra132_soctherm_fuse,
+       .thresh_grain = TEGRA132_THRESH_GRAIN,
+};
diff --git a/drivers/thermal/tegra/tegra210-soctherm.c b/drivers/thermal/tegra/tegra210-soctherm.c
new file mode 100644 (file)
index 0000000..19cc0ab
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <soc/tegra/fuse.h>
+
+#include <dt-bindings/thermal/tegra124-soctherm.h>
+
+#include "soctherm.h"
+
+#define TEGRA210_THERMTRIP_ANY_EN_MASK         (0x1 << 31)
+#define TEGRA210_THERMTRIP_MEM_EN_MASK         (0x1 << 30)
+#define TEGRA210_THERMTRIP_GPU_EN_MASK         (0x1 << 29)
+#define TEGRA210_THERMTRIP_CPU_EN_MASK         (0x1 << 28)
+#define TEGRA210_THERMTRIP_TSENSE_EN_MASK      (0x1 << 27)
+#define TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK  (0x1ff << 18)
+#define TEGRA210_THERMTRIP_CPU_THRESH_MASK     (0x1ff << 9)
+#define TEGRA210_THERMTRIP_TSENSE_THRESH_MASK  0x1ff
+
+#define TEGRA210_THRESH_GRAIN                  500
+
+static const struct tegra_tsensor_configuration tegra210_tsensor_config = {
+       .tall = 16300,
+       .tiddq_en = 1,
+       .ten_count = 1,
+       .tsample = 120,
+       .tsample_ate = 480,
+};
+
+static const struct tegra_tsensor_group tegra210_tsensor_group_cpu = {
+       .id = TEGRA124_SOCTHERM_SENSOR_CPU,
+       .name = "cpu",
+       .sensor_temp_offset = SENSOR_TEMP1,
+       .sensor_temp_mask = SENSOR_TEMP1_CPU_TEMP_MASK,
+       .pdiv = 8,
+       .pdiv_ate = 8,
+       .pdiv_mask = SENSOR_PDIV_CPU_MASK,
+       .pllx_hotspot_diff = 10,
+       .pllx_hotspot_mask = SENSOR_HOTSPOT_CPU_MASK,
+       .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
+       .thermtrip_enable_mask = TEGRA210_THERMTRIP_CPU_EN_MASK,
+       .thermtrip_threshold_mask = TEGRA210_THERMTRIP_CPU_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group tegra210_tsensor_group_gpu = {
+       .id = TEGRA124_SOCTHERM_SENSOR_GPU,
+       .name = "gpu",
+       .sensor_temp_offset = SENSOR_TEMP1,
+       .sensor_temp_mask = SENSOR_TEMP1_GPU_TEMP_MASK,
+       .pdiv = 8,
+       .pdiv_ate = 8,
+       .pdiv_mask = SENSOR_PDIV_GPU_MASK,
+       .pllx_hotspot_diff = 5,
+       .pllx_hotspot_mask = SENSOR_HOTSPOT_GPU_MASK,
+       .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
+       .thermtrip_enable_mask = TEGRA210_THERMTRIP_GPU_EN_MASK,
+       .thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group tegra210_tsensor_group_pll = {
+       .id = TEGRA124_SOCTHERM_SENSOR_PLLX,
+       .name = "pll",
+       .sensor_temp_offset = SENSOR_TEMP2,
+       .sensor_temp_mask = SENSOR_TEMP2_PLLX_TEMP_MASK,
+       .pdiv = 8,
+       .pdiv_ate = 8,
+       .pdiv_mask = SENSOR_PDIV_PLLX_MASK,
+       .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
+       .thermtrip_enable_mask = TEGRA210_THERMTRIP_TSENSE_EN_MASK,
+       .thermtrip_threshold_mask = TEGRA210_THERMTRIP_TSENSE_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group tegra210_tsensor_group_mem = {
+       .id = TEGRA124_SOCTHERM_SENSOR_MEM,
+       .name = "mem",
+       .sensor_temp_offset = SENSOR_TEMP2,
+       .sensor_temp_mask = SENSOR_TEMP2_MEM_TEMP_MASK,
+       .pdiv = 8,
+       .pdiv_ate = 8,
+       .pdiv_mask = SENSOR_PDIV_MEM_MASK,
+       .pllx_hotspot_diff = 0,
+       .pllx_hotspot_mask = SENSOR_HOTSPOT_MEM_MASK,
+       .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
+       .thermtrip_enable_mask = TEGRA210_THERMTRIP_MEM_EN_MASK,
+       .thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK,
+};
+
+static const struct tegra_tsensor_group *tegra210_tsensor_groups[] = {
+       &tegra210_tsensor_group_cpu,
+       &tegra210_tsensor_group_gpu,
+       &tegra210_tsensor_group_pll,
+       &tegra210_tsensor_group_mem,
+};
+
+static const struct tegra_tsensor tegra210_tsensors[] = {
+       {
+               .name = "cpu0",
+               .base = 0xc0,
+               .config = &tegra210_tsensor_config,
+               .calib_fuse_offset = 0x098,
+               .fuse_corr_alpha = 1085000,
+               .fuse_corr_beta = 3244200,
+               .group = &tegra210_tsensor_group_cpu,
+       }, {
+               .name = "cpu1",
+               .base = 0xe0,
+               .config = &tegra210_tsensor_config,
+               .calib_fuse_offset = 0x084,
+               .fuse_corr_alpha = 1126200,
+               .fuse_corr_beta = -67500,
+               .group = &tegra210_tsensor_group_cpu,
+       }, {
+               .name = "cpu2",
+               .base = 0x100,
+               .config = &tegra210_tsensor_config,
+               .calib_fuse_offset = 0x088,
+               .fuse_corr_alpha = 1098400,
+               .fuse_corr_beta = 2251100,
+               .group = &tegra210_tsensor_group_cpu,
+       }, {
+               .name = "cpu3",
+               .base = 0x120,
+               .config = &tegra210_tsensor_config,
+               .calib_fuse_offset = 0x12c,
+               .fuse_corr_alpha = 1108000,
+               .fuse_corr_beta = 602700,
+               .group = &tegra210_tsensor_group_cpu,
+       }, {
+               .name = "mem0",
+               .base = 0x140,
+               .config = &tegra210_tsensor_config,
+               .calib_fuse_offset = 0x158,
+               .fuse_corr_alpha = 1069200,
+               .fuse_corr_beta = 3549900,
+               .group = &tegra210_tsensor_group_mem,
+       }, {
+               .name = "mem1",
+               .base = 0x160,
+               .config = &tegra210_tsensor_config,
+               .calib_fuse_offset = 0x15c,
+               .fuse_corr_alpha = 1173700,
+               .fuse_corr_beta = -6263600,
+               .group = &tegra210_tsensor_group_mem,
+       }, {
+               .name = "gpu",
+               .base = 0x180,
+               .config = &tegra210_tsensor_config,
+               .calib_fuse_offset = 0x154,
+               .fuse_corr_alpha = 1074300,
+               .fuse_corr_beta = 2734900,
+               .group = &tegra210_tsensor_group_gpu,
+       }, {
+               .name = "pllx",
+               .base = 0x1a0,
+               .config = &tegra210_tsensor_config,
+               .calib_fuse_offset = 0x160,
+               .fuse_corr_alpha = 1039700,
+               .fuse_corr_beta = 6829100,
+               .group = &tegra210_tsensor_group_pll,
+       },
+};
+
+/*
+ * Mask/shift bits in FUSE_TSENSOR_COMMON and
+ * FUSE_TSENSOR_COMMON, which are described in
+ * tegra_soctherm_fuse.c
+ */
+static const struct tegra_soctherm_fuse tegra210_soctherm_fuse = {
+       .fuse_base_cp_mask = 0x3ff << 11,
+       .fuse_base_cp_shift = 11,
+       .fuse_base_ft_mask = 0x7ff << 21,
+       .fuse_base_ft_shift = 21,
+       .fuse_shift_ft_mask = 0x1f << 6,
+       .fuse_shift_ft_shift = 6,
+       .fuse_spare_realignment = 0,
+};
+
+const struct tegra_soctherm_soc tegra210_soctherm = {
+       .tsensors = tegra210_tsensors,
+       .num_tsensors = ARRAY_SIZE(tegra210_tsensors),
+       .ttgs = tegra210_tsensor_groups,
+       .num_ttgs = ARRAY_SIZE(tegra210_tsensor_groups),
+       .tfuse = &tegra210_soctherm_fuse,
+       .thresh_grain = TEGRA210_THRESH_GRAIN,
+};
diff --git a/drivers/thermal/tegra_soctherm.c b/drivers/thermal/tegra_soctherm.c
deleted file mode 100644 (file)
index 1369752..0000000
+++ /dev/null
@@ -1,476 +0,0 @@
-/*
- * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
- *
- * Author:
- *     Mikko Perttunen <mperttunen@nvidia.com>
- *
- * 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.
- *
- */
-
-#include <linux/bitops.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/reset.h>
-#include <linux/thermal.h>
-
-#include <soc/tegra/fuse.h>
-
-#define SENSOR_CONFIG0                         0
-#define SENSOR_CONFIG0_STOP                    BIT(0)
-#define SENSOR_CONFIG0_TALL_SHIFT              8
-#define SENSOR_CONFIG0_TCALC_OVER              BIT(4)
-#define SENSOR_CONFIG0_OVER                    BIT(3)
-#define SENSOR_CONFIG0_CPTR_OVER               BIT(2)
-
-#define SENSOR_CONFIG1                         4
-#define SENSOR_CONFIG1_TSAMPLE_SHIFT           0
-#define SENSOR_CONFIG1_TIDDQ_EN_SHIFT          15
-#define SENSOR_CONFIG1_TEN_COUNT_SHIFT         24
-#define SENSOR_CONFIG1_TEMP_ENABLE             BIT(31)
-
-#define SENSOR_CONFIG2                         8
-#define SENSOR_CONFIG2_THERMA_SHIFT            16
-#define SENSOR_CONFIG2_THERMB_SHIFT            0
-
-#define SENSOR_PDIV                            0x1c0
-#define SENSOR_PDIV_T124                       0x8888
-#define SENSOR_HOTSPOT_OFF                     0x1c4
-#define SENSOR_HOTSPOT_OFF_T124                        0x00060600
-#define SENSOR_TEMP1                           0x1c8
-#define SENSOR_TEMP2                           0x1cc
-
-#define SENSOR_TEMP_MASK                       0xffff
-#define READBACK_VALUE_MASK                    0xff00
-#define READBACK_VALUE_SHIFT                   8
-#define READBACK_ADD_HALF                      BIT(7)
-#define READBACK_NEGATE                                BIT(0)
-
-#define FUSE_TSENSOR8_CALIB                    0x180
-#define FUSE_SPARE_REALIGNMENT_REG_0           0x1fc
-
-#define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK     0x1fff
-#define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK     (0x1fff << 13)
-#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT    13
-
-#define FUSE_TSENSOR8_CALIB_CP_TS_BASE_MASK    0x3ff
-#define FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK    (0x7ff << 10)
-#define FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT   10
-
-#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_MASK 0x3f
-#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK (0x1f << 21)
-#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT 21
-
-#define NOMINAL_CALIB_FT_T124                  105
-#define NOMINAL_CALIB_CP_T124                  25
-
-struct tegra_tsensor_configuration {
-       u32 tall, tsample, tiddq_en, ten_count, pdiv, tsample_ate, pdiv_ate;
-};
-
-struct tegra_tsensor {
-       const struct tegra_tsensor_configuration *config;
-       u32 base, calib_fuse_offset;
-       /* Correction values used to modify values read from calibration fuses */
-       s32 fuse_corr_alpha, fuse_corr_beta;
-};
-
-struct tegra_thermctl_zone {
-       void __iomem *reg;
-       unsigned int shift;
-};
-
-static const struct tegra_tsensor_configuration t124_tsensor_config = {
-       .tall = 16300,
-       .tsample = 120,
-       .tiddq_en = 1,
-       .ten_count = 1,
-       .pdiv = 8,
-       .tsample_ate = 480,
-       .pdiv_ate = 8
-};
-
-static const struct tegra_tsensor t124_tsensors[] = {
-       {
-               .config = &t124_tsensor_config,
-               .base = 0xc0,
-               .calib_fuse_offset = 0x098,
-               .fuse_corr_alpha = 1135400,
-               .fuse_corr_beta = -6266900,
-       },
-       {
-               .config = &t124_tsensor_config,
-               .base = 0xe0,
-               .calib_fuse_offset = 0x084,
-               .fuse_corr_alpha = 1122220,
-               .fuse_corr_beta = -5700700,
-       },
-       {
-               .config = &t124_tsensor_config,
-               .base = 0x100,
-               .calib_fuse_offset = 0x088,
-               .fuse_corr_alpha = 1127000,
-               .fuse_corr_beta = -6768200,
-       },
-       {
-               .config = &t124_tsensor_config,
-               .base = 0x120,
-               .calib_fuse_offset = 0x12c,
-               .fuse_corr_alpha = 1110900,
-               .fuse_corr_beta = -6232000,
-       },
-       {
-               .config = &t124_tsensor_config,
-               .base = 0x140,
-               .calib_fuse_offset = 0x158,
-               .fuse_corr_alpha = 1122300,
-               .fuse_corr_beta = -5936400,
-       },
-       {
-               .config = &t124_tsensor_config,
-               .base = 0x160,
-               .calib_fuse_offset = 0x15c,
-               .fuse_corr_alpha = 1145700,
-               .fuse_corr_beta = -7124600,
-       },
-       {
-               .config = &t124_tsensor_config,
-               .base = 0x180,
-               .calib_fuse_offset = 0x154,
-               .fuse_corr_alpha = 1120100,
-               .fuse_corr_beta = -6000500,
-       },
-       {
-               .config = &t124_tsensor_config,
-               .base = 0x1a0,
-               .calib_fuse_offset = 0x160,
-               .fuse_corr_alpha = 1106500,
-               .fuse_corr_beta = -6729300,
-       },
-};
-
-struct tegra_soctherm {
-       struct reset_control *reset;
-       struct clk *clock_tsensor;
-       struct clk *clock_soctherm;
-       void __iomem *regs;
-
-       struct thermal_zone_device *thermctl_tzs[4];
-};
-
-struct tsensor_shared_calibration {
-       u32 base_cp, base_ft;
-       u32 actual_temp_cp, actual_temp_ft;
-};
-
-static int calculate_shared_calibration(struct tsensor_shared_calibration *r)
-{
-       u32 val, shifted_cp, shifted_ft;
-       int err;
-
-       err = tegra_fuse_readl(FUSE_TSENSOR8_CALIB, &val);
-       if (err)
-               return err;
-       r->base_cp = val & FUSE_TSENSOR8_CALIB_CP_TS_BASE_MASK;
-       r->base_ft = (val & FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK)
-               >> FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT;
-       val = ((val & FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK)
-               >> FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT);
-       shifted_ft = sign_extend32(val, 4);
-
-       err = tegra_fuse_readl(FUSE_SPARE_REALIGNMENT_REG_0, &val);
-       if (err)
-               return err;
-       shifted_cp = sign_extend32(val, 5);
-
-       r->actual_temp_cp = 2 * NOMINAL_CALIB_CP_T124 + shifted_cp;
-       r->actual_temp_ft = 2 * NOMINAL_CALIB_FT_T124 + shifted_ft;
-
-       return 0;
-}
-
-static s64 div64_s64_precise(s64 a, s64 b)
-{
-       s64 r, al;
-
-       /* Scale up for increased precision division */
-       al = a << 16;
-
-       r = div64_s64(al * 2 + 1, 2 * b);
-       return r >> 16;
-}
-
-static int
-calculate_tsensor_calibration(const struct tegra_tsensor *sensor,
-                             const struct tsensor_shared_calibration *shared,
-                             u32 *calib)
-{
-       u32 val;
-       s32 actual_tsensor_ft, actual_tsensor_cp, delta_sens, delta_temp,
-           mult, div;
-       s16 therma, thermb;
-       s64 tmp;
-       int err;
-
-       err = tegra_fuse_readl(sensor->calib_fuse_offset, &val);
-       if (err)
-               return err;
-
-       actual_tsensor_cp = (shared->base_cp * 64) + sign_extend32(val, 12);
-       val = (val & FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK)
-               >> FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT;
-       actual_tsensor_ft = (shared->base_ft * 32) + sign_extend32(val, 12);
-
-       delta_sens = actual_tsensor_ft - actual_tsensor_cp;
-       delta_temp = shared->actual_temp_ft - shared->actual_temp_cp;
-
-       mult = sensor->config->pdiv * sensor->config->tsample_ate;
-       div = sensor->config->tsample * sensor->config->pdiv_ate;
-
-       therma = div64_s64_precise((s64) delta_temp * (1LL << 13) * mult,
-                                  (s64) delta_sens * div);
-
-       tmp = (s64)actual_tsensor_ft * shared->actual_temp_cp -
-             (s64)actual_tsensor_cp * shared->actual_temp_ft;
-       thermb = div64_s64_precise(tmp, (s64)delta_sens);
-
-       therma = div64_s64_precise((s64)therma * sensor->fuse_corr_alpha,
-                                  (s64)1000000LL);
-       thermb = div64_s64_precise((s64)thermb * sensor->fuse_corr_alpha +
-                                  sensor->fuse_corr_beta, (s64)1000000LL);
-
-       *calib = ((u16)therma << SENSOR_CONFIG2_THERMA_SHIFT) |
-                ((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT);
-
-       return 0;
-}
-
-static int enable_tsensor(struct tegra_soctherm *tegra,
-                         const struct tegra_tsensor *sensor,
-                         const struct tsensor_shared_calibration *shared)
-{
-       void __iomem *base = tegra->regs + sensor->base;
-       unsigned int val;
-       u32 calib;
-       int err;
-
-       err = calculate_tsensor_calibration(sensor, shared, &calib);
-       if (err)
-               return err;
-
-       val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT;
-       writel(val, base + SENSOR_CONFIG0);
-
-       val  = (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT;
-       val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT;
-       val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT;
-       val |= SENSOR_CONFIG1_TEMP_ENABLE;
-       writel(val, base + SENSOR_CONFIG1);
-
-       writel(calib, base + SENSOR_CONFIG2);
-
-       return 0;
-}
-
-/*
- * Translate from soctherm readback format to millicelsius.
- * The soctherm readback format in bits is as follows:
- *   TTTTTTTT H______N
- * where T's contain the temperature in Celsius,
- * H denotes an addition of 0.5 Celsius and N denotes negation
- * of the final value.
- */
-static int translate_temp(u16 val)
-{
-       long t;
-
-       t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000;
-       if (val & READBACK_ADD_HALF)
-               t += 500;
-       if (val & READBACK_NEGATE)
-               t *= -1;
-
-       return t;
-}
-
-static int tegra_thermctl_get_temp(void *data, int *out_temp)
-{
-       struct tegra_thermctl_zone *zone = data;
-       u32 val;
-
-       val = (readl(zone->reg) >> zone->shift) & SENSOR_TEMP_MASK;
-       *out_temp = translate_temp(val);
-
-       return 0;
-}
-
-static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = {
-       .get_temp = tegra_thermctl_get_temp,
-};
-
-static const struct of_device_id tegra_soctherm_of_match[] = {
-       { .compatible = "nvidia,tegra124-soctherm" },
-       { },
-};
-MODULE_DEVICE_TABLE(of, tegra_soctherm_of_match);
-
-struct thermctl_zone_desc {
-       unsigned int offset;
-       unsigned int shift;
-};
-
-static const struct thermctl_zone_desc t124_thermctl_temp_zones[] = {
-       { SENSOR_TEMP1, 16 },
-       { SENSOR_TEMP2, 16 },
-       { SENSOR_TEMP1, 0 },
-       { SENSOR_TEMP2, 0 }
-};
-
-static int tegra_soctherm_probe(struct platform_device *pdev)
-{
-       struct tegra_soctherm *tegra;
-       struct thermal_zone_device *tz;
-       struct tsensor_shared_calibration shared_calib;
-       struct resource *res;
-       unsigned int i;
-       int err;
-
-       const struct tegra_tsensor *tsensors = t124_tsensors;
-
-       tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
-       if (!tegra)
-               return -ENOMEM;
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       tegra->regs = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(tegra->regs))
-               return PTR_ERR(tegra->regs);
-
-       tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm");
-       if (IS_ERR(tegra->reset)) {
-               dev_err(&pdev->dev, "can't get soctherm reset\n");
-               return PTR_ERR(tegra->reset);
-       }
-
-       tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor");
-       if (IS_ERR(tegra->clock_tsensor)) {
-               dev_err(&pdev->dev, "can't get tsensor clock\n");
-               return PTR_ERR(tegra->clock_tsensor);
-       }
-
-       tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soctherm");
-       if (IS_ERR(tegra->clock_soctherm)) {
-               dev_err(&pdev->dev, "can't get soctherm clock\n");
-               return PTR_ERR(tegra->clock_soctherm);
-       }
-
-       reset_control_assert(tegra->reset);
-
-       err = clk_prepare_enable(tegra->clock_soctherm);
-       if (err)
-               return err;
-
-       err = clk_prepare_enable(tegra->clock_tsensor);
-       if (err) {
-               clk_disable_unprepare(tegra->clock_soctherm);
-               return err;
-       }
-
-       reset_control_deassert(tegra->reset);
-
-       /* Initialize raw sensors */
-
-       err = calculate_shared_calibration(&shared_calib);
-       if (err)
-               goto disable_clocks;
-
-       for (i = 0; i < ARRAY_SIZE(t124_tsensors); ++i) {
-               err = enable_tsensor(tegra, tsensors + i, &shared_calib);
-               if (err)
-                       goto disable_clocks;
-       }
-
-       writel(SENSOR_PDIV_T124, tegra->regs + SENSOR_PDIV);
-       writel(SENSOR_HOTSPOT_OFF_T124, tegra->regs + SENSOR_HOTSPOT_OFF);
-
-       /* Initialize thermctl sensors */
-
-       for (i = 0; i < ARRAY_SIZE(tegra->thermctl_tzs); ++i) {
-               struct tegra_thermctl_zone *zone =
-                       devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL);
-               if (!zone) {
-                       err = -ENOMEM;
-                       goto unregister_tzs;
-               }
-
-               zone->reg = tegra->regs + t124_thermctl_temp_zones[i].offset;
-               zone->shift = t124_thermctl_temp_zones[i].shift;
-
-               tz = thermal_zone_of_sensor_register(&pdev->dev, i, zone,
-                                                    &tegra_of_thermal_ops);
-               if (IS_ERR(tz)) {
-                       err = PTR_ERR(tz);
-                       dev_err(&pdev->dev, "failed to register sensor: %d\n",
-                               err);
-                       goto unregister_tzs;
-               }
-
-               tegra->thermctl_tzs[i] = tz;
-       }
-
-       return 0;
-
-unregister_tzs:
-       while (i--)
-               thermal_zone_of_sensor_unregister(&pdev->dev,
-                                                 tegra->thermctl_tzs[i]);
-
-disable_clocks:
-       clk_disable_unprepare(tegra->clock_tsensor);
-       clk_disable_unprepare(tegra->clock_soctherm);
-
-       return err;
-}
-
-static int tegra_soctherm_remove(struct platform_device *pdev)
-{
-       struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
-       unsigned int i;
-
-       for (i = 0; i < ARRAY_SIZE(tegra->thermctl_tzs); ++i) {
-               thermal_zone_of_sensor_unregister(&pdev->dev,
-                                                 tegra->thermctl_tzs[i]);
-       }
-
-       clk_disable_unprepare(tegra->clock_tsensor);
-       clk_disable_unprepare(tegra->clock_soctherm);
-
-       return 0;
-}
-
-static struct platform_driver tegra_soctherm_driver = {
-       .probe = tegra_soctherm_probe,
-       .remove = tegra_soctherm_remove,
-       .driver = {
-               .name = "tegra-soctherm",
-               .of_match_table = tegra_soctherm_of_match,
-       },
-};
-module_platform_driver(tegra_soctherm_driver);
-
-MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
-MODULE_DESCRIPTION("NVIDIA Tegra SOCTHERM thermal management driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c
new file mode 100644 (file)
index 0000000..73f55d6
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Generic ADC thermal driver
+ *
+ * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/iio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+struct gadc_thermal_info {
+       struct device *dev;
+       struct thermal_zone_device *tz_dev;
+       struct iio_channel *channel;
+       s32 *lookup_table;
+       int nlookup_table;
+};
+
+static int gadc_thermal_adc_to_temp(struct gadc_thermal_info *gti, int val)
+{
+       int temp, adc_hi, adc_lo;
+       int i;
+
+       for (i = 0; i < gti->nlookup_table; i++) {
+               if (val >= gti->lookup_table[2 * i + 1])
+                       break;
+       }
+
+       if (i == 0) {
+               temp = gti->lookup_table[0];
+       } else if (i >= (gti->nlookup_table - 1)) {
+               temp = gti->lookup_table[2 * (gti->nlookup_table - 1)];
+       } else {
+               adc_hi = gti->lookup_table[2 * i - 1];
+               adc_lo = gti->lookup_table[2 * i + 1];
+               temp = gti->lookup_table[2 * i];
+               temp -= ((val - adc_lo) * 1000) / (adc_hi - adc_lo);
+       }
+
+       return temp;
+}
+
+static int gadc_thermal_get_temp(void *data, int *temp)
+{
+       struct gadc_thermal_info *gti = data;
+       int val;
+       int ret;
+
+       ret = iio_read_channel_processed(gti->channel, &val);
+       if (ret < 0) {
+               dev_err(gti->dev, "IIO channel read failed %d\n", ret);
+               return ret;
+       }
+       *temp = gadc_thermal_adc_to_temp(gti, val);
+
+       return 0;
+}
+
+static const struct thermal_zone_of_device_ops gadc_thermal_ops = {
+       .get_temp = gadc_thermal_get_temp,
+};
+
+static int gadc_thermal_read_linear_lookup_table(struct device *dev,
+                                                struct gadc_thermal_info *gti)
+{
+       struct device_node *np = dev->of_node;
+       int ntable;
+       int ret;
+
+       ntable = of_property_count_elems_of_size(np, "temperature-lookup-table",
+                                                sizeof(u32));
+       if (ntable < 0) {
+               dev_err(dev, "Lookup table is not provided\n");
+               return ntable;
+       }
+
+       if (ntable % 2) {
+               dev_err(dev, "Pair of temperature vs ADC read value missing\n");
+               return -EINVAL;
+       }
+
+       gti->lookup_table = devm_kzalloc(dev, sizeof(*gti->lookup_table) *
+                                        ntable, GFP_KERNEL);
+       if (!gti->lookup_table)
+               return -ENOMEM;
+
+       ret = of_property_read_u32_array(np, "temperature-lookup-table",
+                                        (u32 *)gti->lookup_table, ntable);
+       if (ret < 0) {
+               dev_err(dev, "Failed to read temperature lookup table: %d\n",
+                       ret);
+               return ret;
+       }
+
+       gti->nlookup_table = ntable / 2;
+
+       return 0;
+}
+
+static int gadc_thermal_probe(struct platform_device *pdev)
+{
+       struct gadc_thermal_info *gti;
+       int ret;
+
+       if (!pdev->dev.of_node) {
+               dev_err(&pdev->dev, "Only DT based supported\n");
+               return -ENODEV;
+       }
+
+       gti = devm_kzalloc(&pdev->dev, sizeof(*gti), GFP_KERNEL);
+       if (!gti)
+               return -ENOMEM;
+
+       ret = gadc_thermal_read_linear_lookup_table(&pdev->dev, gti);
+       if (ret < 0)
+               return ret;
+
+       gti->dev = &pdev->dev;
+       platform_set_drvdata(pdev, gti);
+
+       gti->channel = iio_channel_get(&pdev->dev, "sensor-channel");
+       if (IS_ERR(gti->channel)) {
+               ret = PTR_ERR(gti->channel);
+               dev_err(&pdev->dev, "IIO channel not found: %d\n", ret);
+               return ret;
+       }
+
+       gti->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, 0,
+                                                     gti, &gadc_thermal_ops);
+       if (IS_ERR(gti->tz_dev)) {
+               ret = PTR_ERR(gti->tz_dev);
+               dev_err(&pdev->dev, "Thermal zone sensor register failed: %d\n",
+                       ret);
+               goto sensor_fail;
+       }
+
+       return 0;
+
+sensor_fail:
+       iio_channel_release(gti->channel);
+
+       return ret;
+}
+
+static int gadc_thermal_remove(struct platform_device *pdev)
+{
+       struct gadc_thermal_info *gti = platform_get_drvdata(pdev);
+
+       thermal_zone_of_sensor_unregister(&pdev->dev, gti->tz_dev);
+       iio_channel_release(gti->channel);
+
+       return 0;
+}
+
+static const struct of_device_id of_adc_thermal_match[] = {
+       { .compatible = "generic-adc-thermal", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, of_adc_thermal_match);
+
+static struct platform_driver gadc_thermal_driver = {
+       .driver = {
+               .name = "generic-adc-thermal",
+               .of_match_table = of_adc_thermal_match,
+       },
+       .probe = gadc_thermal_probe,
+       .remove = gadc_thermal_remove,
+};
+
+module_platform_driver(gadc_thermal_driver);
+
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("Generic ADC thermal driver using IIO framework with DT");
+MODULE_LICENSE("GPL v2");
index b213a12..15c0a9a 100644 (file)
@@ -337,7 +337,7 @@ int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
                return -EINVAL;
 
        /* in case this is specified by DT */
-       data->ti_thermal = thermal_zone_of_sensor_register(bgp->dev, id,
+       data->ti_thermal = devm_thermal_zone_of_sensor_register(bgp->dev, id,
                                        data, &ti_of_thermal_ops);
        if (IS_ERR(data->ti_thermal)) {
                /* Create thermal zone */
@@ -368,9 +368,6 @@ int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
        if (data && data->ti_thermal) {
                if (data->our_zone)
                        thermal_zone_device_unregister(data->ti_thermal);
-               else
-                       thermal_zone_of_sensor_unregister(bgp->dev,
-                                                         data->ti_thermal);
        }
 
        return 0;
index 7fc919f..97f0a2b 100644 (file)
@@ -555,7 +555,7 @@ static int pkg_temp_thermal_cpu_callback(struct notifier_block *nfb,
 {
        unsigned int cpu = (unsigned long) hcpu;
 
-       switch (action) {
+       switch (action & ~CPU_TASKS_FROZEN) {
        case CPU_ONLINE:
        case CPU_DOWN_FAILED:
                get_core_online(cpu);
index 85aaf66..729ab9f 100644 (file)
@@ -9,5 +9,6 @@
 #define TEGRA124_SOCTHERM_SENSOR_MEM 1
 #define TEGRA124_SOCTHERM_SENSOR_GPU 2
 #define TEGRA124_SOCTHERM_SENSOR_PLLX 3
+#define TEGRA124_SOCTHERM_SENSOR_NUM 4
 
 #endif
index 1b8a5a7..e45abe7 100644 (file)
@@ -340,6 +340,7 @@ struct thermal_zone_of_device_ops {
        int (*get_temp)(void *, int *);
        int (*get_trend)(void *, long *);
        int (*set_emul_temp)(void *, int);
+       int (*set_trip_temp)(void *, int, int);
 };
 
 /**