165b6d57cSWei Ni /* 265b6d57cSWei Ni * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. 365b6d57cSWei Ni * 465b6d57cSWei Ni * Author: 565b6d57cSWei Ni * Mikko Perttunen <mperttunen@nvidia.com> 665b6d57cSWei Ni * 765b6d57cSWei Ni * This software is licensed under the terms of the GNU General Public 865b6d57cSWei Ni * License version 2, as published by the Free Software Foundation, and 965b6d57cSWei Ni * may be copied, distributed, and modified under those terms. 1065b6d57cSWei Ni * 1165b6d57cSWei Ni * This program is distributed in the hope that it will be useful, 1265b6d57cSWei Ni * but WITHOUT ANY WARRANTY; without even the implied warranty of 1365b6d57cSWei Ni * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1465b6d57cSWei Ni * GNU General Public License for more details. 1565b6d57cSWei Ni * 1665b6d57cSWei Ni */ 1765b6d57cSWei Ni 18d753b22dSWei Ni #include <linux/debugfs.h> 1965b6d57cSWei Ni #include <linux/bitops.h> 2065b6d57cSWei Ni #include <linux/clk.h> 2165b6d57cSWei Ni #include <linux/delay.h> 2265b6d57cSWei Ni #include <linux/err.h> 2365b6d57cSWei Ni #include <linux/interrupt.h> 2465b6d57cSWei Ni #include <linux/io.h> 2565b6d57cSWei Ni #include <linux/module.h> 2665b6d57cSWei Ni #include <linux/of.h> 2765b6d57cSWei Ni #include <linux/platform_device.h> 2865b6d57cSWei Ni #include <linux/reset.h> 2965b6d57cSWei Ni #include <linux/thermal.h> 3065b6d57cSWei Ni 3165b6d57cSWei Ni #include <dt-bindings/thermal/tegra124-soctherm.h> 3265b6d57cSWei Ni 3365b6d57cSWei Ni #include "soctherm.h" 3465b6d57cSWei Ni 3565b6d57cSWei Ni #define SENSOR_CONFIG0 0 3665b6d57cSWei Ni #define SENSOR_CONFIG0_STOP BIT(0) 3765b6d57cSWei Ni #define SENSOR_CONFIG0_CPTR_OVER BIT(2) 38d753b22dSWei Ni #define SENSOR_CONFIG0_OVER BIT(3) 39d753b22dSWei Ni #define SENSOR_CONFIG0_TCALC_OVER BIT(4) 40d753b22dSWei Ni #define SENSOR_CONFIG0_TALL_MASK (0xfffff << 8) 41d753b22dSWei Ni #define SENSOR_CONFIG0_TALL_SHIFT 8 4265b6d57cSWei Ni 4365b6d57cSWei Ni #define SENSOR_CONFIG1 4 44d753b22dSWei Ni #define SENSOR_CONFIG1_TSAMPLE_MASK 0x3ff 4565b6d57cSWei Ni #define SENSOR_CONFIG1_TSAMPLE_SHIFT 0 46d753b22dSWei Ni #define SENSOR_CONFIG1_TIDDQ_EN_MASK (0x3f << 15) 4765b6d57cSWei Ni #define SENSOR_CONFIG1_TIDDQ_EN_SHIFT 15 48d753b22dSWei Ni #define SENSOR_CONFIG1_TEN_COUNT_MASK (0x3f << 24) 4965b6d57cSWei Ni #define SENSOR_CONFIG1_TEN_COUNT_SHIFT 24 5065b6d57cSWei Ni #define SENSOR_CONFIG1_TEMP_ENABLE BIT(31) 5165b6d57cSWei Ni 5265b6d57cSWei Ni /* 5365b6d57cSWei Ni * SENSOR_CONFIG2 is defined in soctherm.h 5465b6d57cSWei Ni * because, it will be used by tegra_soctherm_fuse.c 5565b6d57cSWei Ni */ 5665b6d57cSWei Ni 57d753b22dSWei Ni #define SENSOR_STATUS0 0xc 58d753b22dSWei Ni #define SENSOR_STATUS0_VALID_MASK BIT(31) 59d753b22dSWei Ni #define SENSOR_STATUS0_CAPTURE_MASK 0xffff 60d753b22dSWei Ni 61d753b22dSWei Ni #define SENSOR_STATUS1 0x10 62d753b22dSWei Ni #define SENSOR_STATUS1_TEMP_VALID_MASK BIT(31) 63d753b22dSWei Ni #define SENSOR_STATUS1_TEMP_MASK 0xffff 64d753b22dSWei Ni 6565b6d57cSWei Ni #define READBACK_VALUE_MASK 0xff00 6665b6d57cSWei Ni #define READBACK_VALUE_SHIFT 8 6765b6d57cSWei Ni #define READBACK_ADD_HALF BIT(7) 6865b6d57cSWei Ni #define READBACK_NEGATE BIT(0) 6965b6d57cSWei Ni 7065b6d57cSWei Ni /* get val from register(r) mask bits(m) */ 7165b6d57cSWei Ni #define REG_GET_MASK(r, m) (((r) & (m)) >> (ffs(m) - 1)) 7265b6d57cSWei Ni /* set val(v) to mask bits(m) of register(r) */ 7365b6d57cSWei Ni #define REG_SET_MASK(r, m, v) (((r) & ~(m)) | \ 7465b6d57cSWei Ni (((v) & (m >> (ffs(m) - 1))) << (ffs(m) - 1))) 7565b6d57cSWei Ni 762a895871SWei Ni static const int min_low_temp = -127000; 772a895871SWei Ni static const int max_high_temp = 127000; 782a895871SWei Ni 7965b6d57cSWei Ni struct tegra_thermctl_zone { 8065b6d57cSWei Ni void __iomem *reg; 812a895871SWei Ni struct device *dev; 822a895871SWei Ni struct thermal_zone_device *tz; 832a895871SWei Ni const struct tegra_tsensor_group *sg; 8465b6d57cSWei Ni }; 8565b6d57cSWei Ni 8665b6d57cSWei Ni struct tegra_soctherm { 8765b6d57cSWei Ni struct reset_control *reset; 8865b6d57cSWei Ni struct clk *clock_tsensor; 8965b6d57cSWei Ni struct clk *clock_soctherm; 9065b6d57cSWei Ni void __iomem *regs; 91f09d6984SWei Ni struct thermal_zone_device **thermctl_tzs; 9265b6d57cSWei Ni 9365b6d57cSWei Ni u32 *calib; 9465b6d57cSWei Ni struct tegra_soctherm_soc *soc; 95d753b22dSWei Ni 96d753b22dSWei Ni struct dentry *debugfs_dir; 9765b6d57cSWei Ni }; 9865b6d57cSWei Ni 991ed895c2SWei Ni static void enable_tsensor(struct tegra_soctherm *tegra, unsigned int i) 10065b6d57cSWei Ni { 10165b6d57cSWei Ni const struct tegra_tsensor *sensor = &tegra->soc->tsensors[i]; 10265b6d57cSWei Ni void __iomem *base = tegra->regs + sensor->base; 10365b6d57cSWei Ni unsigned int val; 10465b6d57cSWei Ni 10565b6d57cSWei Ni val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT; 10665b6d57cSWei Ni writel(val, base + SENSOR_CONFIG0); 10765b6d57cSWei Ni 10865b6d57cSWei Ni val = (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT; 10965b6d57cSWei Ni val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT; 11065b6d57cSWei Ni val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT; 11165b6d57cSWei Ni val |= SENSOR_CONFIG1_TEMP_ENABLE; 11265b6d57cSWei Ni writel(val, base + SENSOR_CONFIG1); 11365b6d57cSWei Ni 1141ed895c2SWei Ni writel(tegra->calib[i], base + SENSOR_CONFIG2); 11565b6d57cSWei Ni } 11665b6d57cSWei Ni 11765b6d57cSWei Ni /* 11865b6d57cSWei Ni * Translate from soctherm readback format to millicelsius. 11965b6d57cSWei Ni * The soctherm readback format in bits is as follows: 12065b6d57cSWei Ni * TTTTTTTT H______N 12165b6d57cSWei Ni * where T's contain the temperature in Celsius, 12265b6d57cSWei Ni * H denotes an addition of 0.5 Celsius and N denotes negation 12365b6d57cSWei Ni * of the final value. 12465b6d57cSWei Ni */ 12565b6d57cSWei Ni static int translate_temp(u16 val) 12665b6d57cSWei Ni { 12765b6d57cSWei Ni int t; 12865b6d57cSWei Ni 12965b6d57cSWei Ni t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000; 13065b6d57cSWei Ni if (val & READBACK_ADD_HALF) 13165b6d57cSWei Ni t += 500; 13265b6d57cSWei Ni if (val & READBACK_NEGATE) 13365b6d57cSWei Ni t *= -1; 13465b6d57cSWei Ni 13565b6d57cSWei Ni return t; 13665b6d57cSWei Ni } 13765b6d57cSWei Ni 13865b6d57cSWei Ni static int tegra_thermctl_get_temp(void *data, int *out_temp) 13965b6d57cSWei Ni { 14065b6d57cSWei Ni struct tegra_thermctl_zone *zone = data; 14165b6d57cSWei Ni u32 val; 14265b6d57cSWei Ni 14365b6d57cSWei Ni val = readl(zone->reg); 1442a895871SWei Ni val = REG_GET_MASK(val, zone->sg->sensor_temp_mask); 14565b6d57cSWei Ni *out_temp = translate_temp(val); 14665b6d57cSWei Ni 14765b6d57cSWei Ni return 0; 14865b6d57cSWei Ni } 14965b6d57cSWei Ni 1502a895871SWei Ni static int 1512a895871SWei Ni thermtrip_program(struct device *dev, const struct tegra_tsensor_group *sg, 1522a895871SWei Ni int trip_temp); 1532a895871SWei Ni 1542a895871SWei Ni static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp) 1552a895871SWei Ni { 1562a895871SWei Ni struct tegra_thermctl_zone *zone = data; 1572a895871SWei Ni struct thermal_zone_device *tz = zone->tz; 1582a895871SWei Ni const struct tegra_tsensor_group *sg = zone->sg; 1592a895871SWei Ni struct device *dev = zone->dev; 1602a895871SWei Ni enum thermal_trip_type type; 1612a895871SWei Ni int ret; 1622a895871SWei Ni 1632a895871SWei Ni if (!tz) 1642a895871SWei Ni return -EINVAL; 1652a895871SWei Ni 1662a895871SWei Ni ret = tz->ops->get_trip_type(tz, trip, &type); 1672a895871SWei Ni if (ret) 1682a895871SWei Ni return ret; 1692a895871SWei Ni 1702a895871SWei Ni if (type != THERMAL_TRIP_CRITICAL) 1712a895871SWei Ni return 0; 1722a895871SWei Ni 1732a895871SWei Ni return thermtrip_program(dev, sg, temp); 1742a895871SWei Ni } 1752a895871SWei Ni 17665b6d57cSWei Ni static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = { 17765b6d57cSWei Ni .get_temp = tegra_thermctl_get_temp, 1782a895871SWei Ni .set_trip_temp = tegra_thermctl_set_trip_temp, 17965b6d57cSWei Ni }; 18065b6d57cSWei Ni 1812a895871SWei Ni /** 1822a895871SWei Ni * enforce_temp_range() - check and enforce temperature range [min, max] 1832a895871SWei Ni * @trip_temp: the trip temperature to check 1842a895871SWei Ni * 1852a895871SWei Ni * Checks and enforces the permitted temperature range that SOC_THERM 1862a895871SWei Ni * HW can support This is 1872a895871SWei Ni * done while taking care of precision. 1882a895871SWei Ni * 1892a895871SWei Ni * Return: The precision adjusted capped temperature in millicelsius. 1902a895871SWei Ni */ 1912a895871SWei Ni static int enforce_temp_range(struct device *dev, int trip_temp) 1922a895871SWei Ni { 1932a895871SWei Ni int temp; 1942a895871SWei Ni 1952a895871SWei Ni temp = clamp_val(trip_temp, min_low_temp, max_high_temp); 1962a895871SWei Ni if (temp != trip_temp) 1972a895871SWei Ni dev_info(dev, "soctherm: trip temperature %d forced to %d\n", 1982a895871SWei Ni trip_temp, temp); 1992a895871SWei Ni return temp; 2002a895871SWei Ni } 2012a895871SWei Ni 2022a895871SWei Ni /** 2032a895871SWei Ni * thermtrip_program() - Configures the hardware to shut down the 2042a895871SWei Ni * system if a given sensor group reaches a given temperature 2052a895871SWei Ni * @dev: ptr to the struct device for the SOC_THERM IP block 2062a895871SWei Ni * @sg: pointer to the sensor group to set the thermtrip temperature for 2072a895871SWei Ni * @trip_temp: the temperature in millicelsius to trigger the thermal trip at 2082a895871SWei Ni * 2092a895871SWei Ni * Sets the thermal trip threshold of the given sensor group to be the 2102a895871SWei Ni * @trip_temp. If this threshold is crossed, the hardware will shut 2112a895871SWei Ni * down. 2122a895871SWei Ni * 2132a895871SWei Ni * Note that, although @trip_temp is specified in millicelsius, the 2142a895871SWei Ni * hardware is programmed in degrees Celsius. 2152a895871SWei Ni * 2162a895871SWei Ni * Return: 0 upon success, or %-EINVAL upon failure. 2172a895871SWei Ni */ 2182a895871SWei Ni static int thermtrip_program(struct device *dev, 2192a895871SWei Ni const struct tegra_tsensor_group *sg, 2202a895871SWei Ni int trip_temp) 2212a895871SWei Ni { 2222a895871SWei Ni struct tegra_soctherm *ts = dev_get_drvdata(dev); 2232a895871SWei Ni int temp; 2242a895871SWei Ni u32 r; 2252a895871SWei Ni 2262a895871SWei Ni if (!dev || !sg) 2272a895871SWei Ni return -EINVAL; 2282a895871SWei Ni 2292a895871SWei Ni if (!sg->thermtrip_threshold_mask) 2302a895871SWei Ni return -EINVAL; 2312a895871SWei Ni 2322a895871SWei Ni temp = enforce_temp_range(dev, trip_temp) / ts->soc->thresh_grain; 2332a895871SWei Ni 2342a895871SWei Ni r = readl(ts->regs + THERMCTL_THERMTRIP_CTL); 2352a895871SWei Ni r = REG_SET_MASK(r, sg->thermtrip_threshold_mask, temp); 2362a895871SWei Ni r = REG_SET_MASK(r, sg->thermtrip_enable_mask, 1); 2372a895871SWei Ni r = REG_SET_MASK(r, sg->thermtrip_any_en_mask, 0); 2382a895871SWei Ni writel(r, ts->regs + THERMCTL_THERMTRIP_CTL); 2392a895871SWei Ni 2402a895871SWei Ni return 0; 2412a895871SWei Ni } 2422a895871SWei Ni 2432a895871SWei Ni /** 2442a895871SWei Ni * tegra_soctherm_set_hwtrips() - set HW trip point from DT data 2452a895871SWei Ni * @dev: struct device * of the SOC_THERM instance 2462a895871SWei Ni * 2472a895871SWei Ni * Configure the SOC_THERM HW trip points, setting "THERMTRIP" 2482a895871SWei Ni * trip points , using "critical" type trip_temp from thermal 2492a895871SWei Ni * zone. 2502a895871SWei Ni * After they have been configured, THERMTRIP will take action 2512a895871SWei Ni * when the configured SoC thermal sensor group reaches a 2522a895871SWei Ni * certain temperature. 2532a895871SWei Ni * 2542a895871SWei Ni * Return: 0 upon success, or a negative error code on failure. 2552a895871SWei Ni * "Success" does not mean that trips was enabled; it could also 2562a895871SWei Ni * mean that no node was found in DT. 2572a895871SWei Ni * THERMTRIP has been enabled successfully when a message similar to 2582a895871SWei Ni * this one appears on the serial console: 2592a895871SWei Ni * "thermtrip: will shut down when sensor group XXX reaches YYYYYY mC" 2602a895871SWei Ni */ 2612a895871SWei Ni static int tegra_soctherm_set_hwtrips(struct device *dev, 2622a895871SWei Ni const struct tegra_tsensor_group *sg, 2632a895871SWei Ni struct thermal_zone_device *tz) 2642a895871SWei Ni { 2652a895871SWei Ni int temperature; 2662a895871SWei Ni int ret; 2672a895871SWei Ni 2682a895871SWei Ni ret = tz->ops->get_crit_temp(tz, &temperature); 2692a895871SWei Ni if (ret) { 2702a895871SWei Ni dev_warn(dev, "thermtrip: %s: missing critical temperature\n", 2712a895871SWei Ni sg->name); 2722a895871SWei Ni return ret; 2732a895871SWei Ni } 2742a895871SWei Ni 2752a895871SWei Ni ret = thermtrip_program(dev, sg, temperature); 2762a895871SWei Ni if (ret) { 2772a895871SWei Ni dev_err(dev, "thermtrip: %s: error during enable\n", 2782a895871SWei Ni sg->name); 2792a895871SWei Ni return ret; 2802a895871SWei Ni } 2812a895871SWei Ni 2822a895871SWei Ni dev_info(dev, 2832a895871SWei Ni "thermtrip: will shut down when %s reaches %d mC\n", 2842a895871SWei Ni sg->name, temperature); 2852a895871SWei Ni 2862a895871SWei Ni return 0; 2872a895871SWei Ni } 2882a895871SWei Ni 289d753b22dSWei Ni #ifdef CONFIG_DEBUG_FS 290d753b22dSWei Ni static int regs_show(struct seq_file *s, void *data) 291d753b22dSWei Ni { 292d753b22dSWei Ni struct platform_device *pdev = s->private; 293d753b22dSWei Ni struct tegra_soctherm *ts = platform_get_drvdata(pdev); 294d753b22dSWei Ni const struct tegra_tsensor *tsensors = ts->soc->tsensors; 2952a895871SWei Ni const struct tegra_tsensor_group **ttgs = ts->soc->ttgs; 296d753b22dSWei Ni u32 r, state; 297d753b22dSWei Ni int i; 298d753b22dSWei Ni 299d753b22dSWei Ni seq_puts(s, "-----TSENSE (convert HW)-----\n"); 300d753b22dSWei Ni 301d753b22dSWei Ni for (i = 0; i < ts->soc->num_tsensors; i++) { 302d753b22dSWei Ni r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG1); 303d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG1_TEMP_ENABLE); 304d753b22dSWei Ni 305d753b22dSWei Ni seq_printf(s, "%s: ", tsensors[i].name); 306d753b22dSWei Ni seq_printf(s, "En(%d) ", state); 307d753b22dSWei Ni 308d753b22dSWei Ni if (!state) { 309d753b22dSWei Ni seq_puts(s, "\n"); 310d753b22dSWei Ni continue; 311d753b22dSWei Ni } 312d753b22dSWei Ni 313d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG1_TIDDQ_EN_MASK); 314d753b22dSWei Ni seq_printf(s, "tiddq(%d) ", state); 315d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG1_TEN_COUNT_MASK); 316d753b22dSWei Ni seq_printf(s, "ten_count(%d) ", state); 317d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG1_TSAMPLE_MASK); 318d753b22dSWei Ni seq_printf(s, "tsample(%d) ", state + 1); 319d753b22dSWei Ni 320d753b22dSWei Ni r = readl(ts->regs + tsensors[i].base + SENSOR_STATUS1); 321d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_STATUS1_TEMP_VALID_MASK); 322d753b22dSWei Ni seq_printf(s, "Temp(%d/", state); 323d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_STATUS1_TEMP_MASK); 324d753b22dSWei Ni seq_printf(s, "%d) ", translate_temp(state)); 325d753b22dSWei Ni 326d753b22dSWei Ni r = readl(ts->regs + tsensors[i].base + SENSOR_STATUS0); 327d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_STATUS0_VALID_MASK); 328d753b22dSWei Ni seq_printf(s, "Capture(%d/", state); 329d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_STATUS0_CAPTURE_MASK); 330d753b22dSWei Ni seq_printf(s, "%d) ", state); 331d753b22dSWei Ni 332d753b22dSWei Ni r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG0); 333d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG0_STOP); 334d753b22dSWei Ni seq_printf(s, "Stop(%d) ", state); 335d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG0_TALL_MASK); 336d753b22dSWei Ni seq_printf(s, "Tall(%d) ", state); 337d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG0_TCALC_OVER); 338d753b22dSWei Ni seq_printf(s, "Over(%d/", state); 339d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG0_OVER); 340d753b22dSWei Ni seq_printf(s, "%d/", state); 341d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG0_CPTR_OVER); 342d753b22dSWei Ni seq_printf(s, "%d) ", state); 343d753b22dSWei Ni 344d753b22dSWei Ni r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG2); 345d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG2_THERMA_MASK); 346d753b22dSWei Ni seq_printf(s, "Therm_A/B(%d/", state); 347d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG2_THERMB_MASK); 348d753b22dSWei Ni seq_printf(s, "%d)\n", (s16)state); 349d753b22dSWei Ni } 350d753b22dSWei Ni 351d753b22dSWei Ni r = readl(ts->regs + SENSOR_PDIV); 352d753b22dSWei Ni seq_printf(s, "PDIV: 0x%x\n", r); 353d753b22dSWei Ni 354d753b22dSWei Ni r = readl(ts->regs + SENSOR_HOTSPOT_OFF); 355d753b22dSWei Ni seq_printf(s, "HOTSPOT: 0x%x\n", r); 356d753b22dSWei Ni 357d753b22dSWei Ni seq_puts(s, "\n"); 358d753b22dSWei Ni seq_puts(s, "-----SOC_THERM-----\n"); 359d753b22dSWei Ni 360d753b22dSWei Ni r = readl(ts->regs + SENSOR_TEMP1); 361d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_TEMP1_CPU_TEMP_MASK); 362d753b22dSWei Ni seq_printf(s, "Temperatures: CPU(%d) ", translate_temp(state)); 363d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_TEMP1_GPU_TEMP_MASK); 364d753b22dSWei Ni seq_printf(s, " GPU(%d) ", translate_temp(state)); 365d753b22dSWei Ni r = readl(ts->regs + SENSOR_TEMP2); 366d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_TEMP2_PLLX_TEMP_MASK); 367d753b22dSWei Ni seq_printf(s, " PLLX(%d) ", translate_temp(state)); 368d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_TEMP2_MEM_TEMP_MASK); 369d753b22dSWei Ni seq_printf(s, " MEM(%d)\n", translate_temp(state)); 370d753b22dSWei Ni 3712a895871SWei Ni r = readl(ts->regs + THERMCTL_THERMTRIP_CTL); 3722a895871SWei Ni state = REG_GET_MASK(r, ttgs[0]->thermtrip_any_en_mask); 3732a895871SWei Ni seq_printf(s, "Thermtrip Any En(%d)\n", state); 3742a895871SWei Ni for (i = 0; i < ts->soc->num_ttgs; i++) { 3752a895871SWei Ni state = REG_GET_MASK(r, ttgs[i]->thermtrip_enable_mask); 3762a895871SWei Ni seq_printf(s, " %s En(%d) ", ttgs[i]->name, state); 3772a895871SWei Ni state = REG_GET_MASK(r, ttgs[i]->thermtrip_threshold_mask); 3782a895871SWei Ni state *= ts->soc->thresh_grain; 3792a895871SWei Ni seq_printf(s, "Thresh(%d)\n", state); 3802a895871SWei Ni } 3812a895871SWei Ni 382d753b22dSWei Ni return 0; 383d753b22dSWei Ni } 384d753b22dSWei Ni 385d753b22dSWei Ni static int regs_open(struct inode *inode, struct file *file) 386d753b22dSWei Ni { 387d753b22dSWei Ni return single_open(file, regs_show, inode->i_private); 388d753b22dSWei Ni } 389d753b22dSWei Ni 390d753b22dSWei Ni static const struct file_operations regs_fops = { 391d753b22dSWei Ni .open = regs_open, 392d753b22dSWei Ni .read = seq_read, 393d753b22dSWei Ni .llseek = seq_lseek, 394d753b22dSWei Ni .release = single_release, 395d753b22dSWei Ni }; 396d753b22dSWei Ni 397d753b22dSWei Ni static void soctherm_debug_init(struct platform_device *pdev) 398d753b22dSWei Ni { 399d753b22dSWei Ni struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 400d753b22dSWei Ni struct dentry *root, *file; 401d753b22dSWei Ni 402d753b22dSWei Ni root = debugfs_create_dir("soctherm", NULL); 403d753b22dSWei Ni if (!root) { 404d753b22dSWei Ni dev_err(&pdev->dev, "failed to create debugfs directory\n"); 405d753b22dSWei Ni return; 406d753b22dSWei Ni } 407d753b22dSWei Ni 408d753b22dSWei Ni tegra->debugfs_dir = root; 409d753b22dSWei Ni 410d753b22dSWei Ni file = debugfs_create_file("reg_contents", 0644, root, 411d753b22dSWei Ni pdev, ®s_fops); 412d753b22dSWei Ni if (!file) { 413d753b22dSWei Ni dev_err(&pdev->dev, "failed to create debugfs file\n"); 414d753b22dSWei Ni debugfs_remove_recursive(tegra->debugfs_dir); 415d753b22dSWei Ni tegra->debugfs_dir = NULL; 416d753b22dSWei Ni } 417d753b22dSWei Ni } 418d753b22dSWei Ni #else 419d753b22dSWei Ni static inline void soctherm_debug_init(struct platform_device *pdev) {} 420d753b22dSWei Ni #endif 421d753b22dSWei Ni 4228de2ab02SWei Ni static int soctherm_clk_enable(struct platform_device *pdev, bool enable) 4238de2ab02SWei Ni { 4248de2ab02SWei Ni struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 4258de2ab02SWei Ni int err; 4268de2ab02SWei Ni 4278de2ab02SWei Ni if (!tegra->clock_soctherm || !tegra->clock_tsensor) 4288de2ab02SWei Ni return -EINVAL; 4298de2ab02SWei Ni 4308de2ab02SWei Ni reset_control_assert(tegra->reset); 4318de2ab02SWei Ni 4328de2ab02SWei Ni if (enable) { 4338de2ab02SWei Ni err = clk_prepare_enable(tegra->clock_soctherm); 4348de2ab02SWei Ni if (err) { 4358de2ab02SWei Ni reset_control_deassert(tegra->reset); 4368de2ab02SWei Ni return err; 4378de2ab02SWei Ni } 4388de2ab02SWei Ni 4398de2ab02SWei Ni err = clk_prepare_enable(tegra->clock_tsensor); 4408de2ab02SWei Ni if (err) { 4418de2ab02SWei Ni clk_disable_unprepare(tegra->clock_soctherm); 4428de2ab02SWei Ni reset_control_deassert(tegra->reset); 4438de2ab02SWei Ni return err; 4448de2ab02SWei Ni } 4458de2ab02SWei Ni } else { 4468de2ab02SWei Ni clk_disable_unprepare(tegra->clock_tsensor); 4478de2ab02SWei Ni clk_disable_unprepare(tegra->clock_soctherm); 4488de2ab02SWei Ni } 4498de2ab02SWei Ni 4508de2ab02SWei Ni reset_control_deassert(tegra->reset); 4518de2ab02SWei Ni 4528de2ab02SWei Ni return 0; 4538de2ab02SWei Ni } 4548de2ab02SWei Ni 4551ed895c2SWei Ni static void soctherm_init(struct platform_device *pdev) 4561ed895c2SWei Ni { 4571ed895c2SWei Ni struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 4581ed895c2SWei Ni const struct tegra_tsensor_group **ttgs = tegra->soc->ttgs; 4591ed895c2SWei Ni int i; 4601ed895c2SWei Ni u32 pdiv, hotspot; 4611ed895c2SWei Ni 4621ed895c2SWei Ni /* Initialize raw sensors */ 4631ed895c2SWei Ni for (i = 0; i < tegra->soc->num_tsensors; ++i) 4641ed895c2SWei Ni enable_tsensor(tegra, i); 4651ed895c2SWei Ni 4661ed895c2SWei Ni /* program pdiv and hotspot offsets per THERM */ 4671ed895c2SWei Ni pdiv = readl(tegra->regs + SENSOR_PDIV); 4681ed895c2SWei Ni hotspot = readl(tegra->regs + SENSOR_HOTSPOT_OFF); 4691ed895c2SWei Ni for (i = 0; i < tegra->soc->num_ttgs; ++i) { 4701ed895c2SWei Ni pdiv = REG_SET_MASK(pdiv, ttgs[i]->pdiv_mask, 4711ed895c2SWei Ni ttgs[i]->pdiv); 4721ed895c2SWei Ni /* hotspot offset from PLLX, doesn't need to configure PLLX */ 4731ed895c2SWei Ni if (ttgs[i]->id == TEGRA124_SOCTHERM_SENSOR_PLLX) 4741ed895c2SWei Ni continue; 4751ed895c2SWei Ni hotspot = REG_SET_MASK(hotspot, 4761ed895c2SWei Ni ttgs[i]->pllx_hotspot_mask, 4771ed895c2SWei Ni ttgs[i]->pllx_hotspot_diff); 4781ed895c2SWei Ni } 4791ed895c2SWei Ni writel(pdiv, tegra->regs + SENSOR_PDIV); 4801ed895c2SWei Ni writel(hotspot, tegra->regs + SENSOR_HOTSPOT_OFF); 4811ed895c2SWei Ni } 4821ed895c2SWei Ni 48365b6d57cSWei Ni static const struct of_device_id tegra_soctherm_of_match[] = { 48465b6d57cSWei Ni #ifdef CONFIG_ARCH_TEGRA_124_SOC 48565b6d57cSWei Ni { 48665b6d57cSWei Ni .compatible = "nvidia,tegra124-soctherm", 48765b6d57cSWei Ni .data = &tegra124_soctherm, 48865b6d57cSWei Ni }, 48965b6d57cSWei Ni #endif 4908204104fSWei Ni #ifdef CONFIG_ARCH_TEGRA_210_SOC 4918204104fSWei Ni { 4928204104fSWei Ni .compatible = "nvidia,tegra210-soctherm", 4938204104fSWei Ni .data = &tegra210_soctherm, 4948204104fSWei Ni }, 4958204104fSWei Ni #endif 49665b6d57cSWei Ni { }, 49765b6d57cSWei Ni }; 49865b6d57cSWei Ni MODULE_DEVICE_TABLE(of, tegra_soctherm_of_match); 49965b6d57cSWei Ni 50065b6d57cSWei Ni static int tegra_soctherm_probe(struct platform_device *pdev) 50165b6d57cSWei Ni { 50265b6d57cSWei Ni const struct of_device_id *match; 50365b6d57cSWei Ni struct tegra_soctherm *tegra; 50465b6d57cSWei Ni struct thermal_zone_device *z; 50565b6d57cSWei Ni struct tsensor_shared_calib shared_calib; 50665b6d57cSWei Ni struct resource *res; 50765b6d57cSWei Ni struct tegra_soctherm_soc *soc; 50865b6d57cSWei Ni unsigned int i; 50965b6d57cSWei Ni int err; 51065b6d57cSWei Ni 51165b6d57cSWei Ni match = of_match_node(tegra_soctherm_of_match, pdev->dev.of_node); 51265b6d57cSWei Ni if (!match) 51365b6d57cSWei Ni return -ENODEV; 51465b6d57cSWei Ni 51565b6d57cSWei Ni soc = (struct tegra_soctherm_soc *)match->data; 51665b6d57cSWei Ni if (soc->num_ttgs > TEGRA124_SOCTHERM_SENSOR_NUM) 51765b6d57cSWei Ni return -EINVAL; 51865b6d57cSWei Ni 51965b6d57cSWei Ni tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); 52065b6d57cSWei Ni if (!tegra) 52165b6d57cSWei Ni return -ENOMEM; 52265b6d57cSWei Ni 52365b6d57cSWei Ni dev_set_drvdata(&pdev->dev, tegra); 52465b6d57cSWei Ni 52565b6d57cSWei Ni tegra->soc = soc; 52665b6d57cSWei Ni 52765b6d57cSWei Ni res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 52865b6d57cSWei Ni tegra->regs = devm_ioremap_resource(&pdev->dev, res); 52965b6d57cSWei Ni if (IS_ERR(tegra->regs)) 53065b6d57cSWei Ni return PTR_ERR(tegra->regs); 53165b6d57cSWei Ni 53265b6d57cSWei Ni tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm"); 53365b6d57cSWei Ni if (IS_ERR(tegra->reset)) { 53465b6d57cSWei Ni dev_err(&pdev->dev, "can't get soctherm reset\n"); 53565b6d57cSWei Ni return PTR_ERR(tegra->reset); 53665b6d57cSWei Ni } 53765b6d57cSWei Ni 53865b6d57cSWei Ni tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor"); 53965b6d57cSWei Ni if (IS_ERR(tegra->clock_tsensor)) { 54065b6d57cSWei Ni dev_err(&pdev->dev, "can't get tsensor clock\n"); 54165b6d57cSWei Ni return PTR_ERR(tegra->clock_tsensor); 54265b6d57cSWei Ni } 54365b6d57cSWei Ni 54465b6d57cSWei Ni tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soctherm"); 54565b6d57cSWei Ni if (IS_ERR(tegra->clock_soctherm)) { 54665b6d57cSWei Ni dev_err(&pdev->dev, "can't get soctherm clock\n"); 54765b6d57cSWei Ni return PTR_ERR(tegra->clock_soctherm); 54865b6d57cSWei Ni } 54965b6d57cSWei Ni 5501ed895c2SWei Ni tegra->calib = devm_kzalloc(&pdev->dev, 5511ed895c2SWei Ni sizeof(u32) * soc->num_tsensors, 5521ed895c2SWei Ni GFP_KERNEL); 5531ed895c2SWei Ni if (!tegra->calib) 5541ed895c2SWei Ni return -ENOMEM; 5551ed895c2SWei Ni 5561ed895c2SWei Ni /* calculate shared calibration data */ 5571ed895c2SWei Ni err = tegra_calc_shared_calib(soc->tfuse, &shared_calib); 5581ed895c2SWei Ni if (err) 5591ed895c2SWei Ni return err; 5601ed895c2SWei Ni 5611ed895c2SWei Ni /* calculate tsensor calibaration data */ 5621ed895c2SWei Ni for (i = 0; i < soc->num_tsensors; ++i) { 5631ed895c2SWei Ni err = tegra_calc_tsensor_calib(&soc->tsensors[i], 5641ed895c2SWei Ni &shared_calib, 5651ed895c2SWei Ni &tegra->calib[i]); 5661ed895c2SWei Ni if (err) 5671ed895c2SWei Ni return err; 5681ed895c2SWei Ni } 5691ed895c2SWei Ni 570f09d6984SWei Ni tegra->thermctl_tzs = devm_kzalloc(&pdev->dev, 571f09d6984SWei Ni sizeof(*z) * soc->num_ttgs, 572f09d6984SWei Ni GFP_KERNEL); 573f09d6984SWei Ni if (!tegra->thermctl_tzs) 574f09d6984SWei Ni return -ENOMEM; 575f09d6984SWei Ni 5768de2ab02SWei Ni err = soctherm_clk_enable(pdev, true); 57765b6d57cSWei Ni if (err) 57865b6d57cSWei Ni return err; 57965b6d57cSWei Ni 5801ed895c2SWei Ni soctherm_init(pdev); 58165b6d57cSWei Ni 58265b6d57cSWei Ni for (i = 0; i < soc->num_ttgs; ++i) { 58365b6d57cSWei Ni struct tegra_thermctl_zone *zone = 58465b6d57cSWei Ni devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL); 58565b6d57cSWei Ni if (!zone) { 58665b6d57cSWei Ni err = -ENOMEM; 58765b6d57cSWei Ni goto disable_clocks; 58865b6d57cSWei Ni } 58965b6d57cSWei Ni 59065b6d57cSWei Ni zone->reg = tegra->regs + soc->ttgs[i]->sensor_temp_offset; 5912a895871SWei Ni zone->dev = &pdev->dev; 5922a895871SWei Ni zone->sg = soc->ttgs[i]; 59365b6d57cSWei Ni 59465b6d57cSWei Ni z = devm_thermal_zone_of_sensor_register(&pdev->dev, 59565b6d57cSWei Ni soc->ttgs[i]->id, zone, 59665b6d57cSWei Ni &tegra_of_thermal_ops); 59765b6d57cSWei Ni if (IS_ERR(z)) { 59865b6d57cSWei Ni err = PTR_ERR(z); 59965b6d57cSWei Ni dev_err(&pdev->dev, "failed to register sensor: %d\n", 60065b6d57cSWei Ni err); 60165b6d57cSWei Ni goto disable_clocks; 60265b6d57cSWei Ni } 6032a895871SWei Ni 6042a895871SWei Ni zone->tz = z; 605f09d6984SWei Ni tegra->thermctl_tzs[soc->ttgs[i]->id] = z; 6062a895871SWei Ni 6072a895871SWei Ni /* Configure hw trip points */ 6082a895871SWei Ni tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z); 60965b6d57cSWei Ni } 61065b6d57cSWei Ni 611d753b22dSWei Ni soctherm_debug_init(pdev); 612d753b22dSWei Ni 61365b6d57cSWei Ni return 0; 61465b6d57cSWei Ni 61565b6d57cSWei Ni disable_clocks: 6168de2ab02SWei Ni soctherm_clk_enable(pdev, false); 61765b6d57cSWei Ni 61865b6d57cSWei Ni return err; 61965b6d57cSWei Ni } 62065b6d57cSWei Ni 62165b6d57cSWei Ni static int tegra_soctherm_remove(struct platform_device *pdev) 62265b6d57cSWei Ni { 62365b6d57cSWei Ni struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 62465b6d57cSWei Ni 625d753b22dSWei Ni debugfs_remove_recursive(tegra->debugfs_dir); 626d753b22dSWei Ni 6278de2ab02SWei Ni soctherm_clk_enable(pdev, false); 62865b6d57cSWei Ni 62965b6d57cSWei Ni return 0; 63065b6d57cSWei Ni } 63165b6d57cSWei Ni 632f09d6984SWei Ni static int soctherm_suspend(struct device *dev) 633f09d6984SWei Ni { 634f09d6984SWei Ni struct platform_device *pdev = to_platform_device(dev); 635f09d6984SWei Ni 636f09d6984SWei Ni soctherm_clk_enable(pdev, false); 637f09d6984SWei Ni 638f09d6984SWei Ni return 0; 639f09d6984SWei Ni } 640f09d6984SWei Ni 641f09d6984SWei Ni static int soctherm_resume(struct device *dev) 642f09d6984SWei Ni { 643f09d6984SWei Ni struct platform_device *pdev = to_platform_device(dev); 644f09d6984SWei Ni struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 645f09d6984SWei Ni struct tegra_soctherm_soc *soc = tegra->soc; 646f09d6984SWei Ni int err, i; 647f09d6984SWei Ni 648f09d6984SWei Ni err = soctherm_clk_enable(pdev, true); 649f09d6984SWei Ni if (err) { 650f09d6984SWei Ni dev_err(&pdev->dev, 651f09d6984SWei Ni "Resume failed: enable clocks failed\n"); 652f09d6984SWei Ni return err; 653f09d6984SWei Ni } 654f09d6984SWei Ni 655f09d6984SWei Ni soctherm_init(pdev); 656f09d6984SWei Ni 657f09d6984SWei Ni for (i = 0; i < soc->num_ttgs; ++i) { 658f09d6984SWei Ni struct thermal_zone_device *tz; 659f09d6984SWei Ni 660f09d6984SWei Ni tz = tegra->thermctl_tzs[soc->ttgs[i]->id]; 661f09d6984SWei Ni tegra_soctherm_set_hwtrips(dev, soc->ttgs[i], tz); 662f09d6984SWei Ni } 663f09d6984SWei Ni 664f09d6984SWei Ni return 0; 665f09d6984SWei Ni } 666f09d6984SWei Ni 667f09d6984SWei Ni static SIMPLE_DEV_PM_OPS(tegra_soctherm_pm, soctherm_suspend, soctherm_resume); 668f09d6984SWei Ni 66965b6d57cSWei Ni static struct platform_driver tegra_soctherm_driver = { 67065b6d57cSWei Ni .probe = tegra_soctherm_probe, 67165b6d57cSWei Ni .remove = tegra_soctherm_remove, 67265b6d57cSWei Ni .driver = { 67365b6d57cSWei Ni .name = "tegra_soctherm", 674f09d6984SWei Ni .pm = &tegra_soctherm_pm, 67565b6d57cSWei Ni .of_match_table = tegra_soctherm_of_match, 67665b6d57cSWei Ni }, 67765b6d57cSWei Ni }; 67865b6d57cSWei Ni module_platform_driver(tegra_soctherm_driver); 67965b6d57cSWei Ni 68065b6d57cSWei Ni MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>"); 68165b6d57cSWei Ni MODULE_DESCRIPTION("NVIDIA Tegra SOCTHERM thermal management driver"); 68265b6d57cSWei Ni MODULE_LICENSE("GPL v2"); 683