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 226f3500980SWei Ni if (!sg || !sg->thermtrip_threshold_mask) 2272a895871SWei Ni return -EINVAL; 2282a895871SWei Ni 2292a895871SWei Ni temp = enforce_temp_range(dev, trip_temp) / ts->soc->thresh_grain; 2302a895871SWei Ni 2312a895871SWei Ni r = readl(ts->regs + THERMCTL_THERMTRIP_CTL); 2322a895871SWei Ni r = REG_SET_MASK(r, sg->thermtrip_threshold_mask, temp); 2332a895871SWei Ni r = REG_SET_MASK(r, sg->thermtrip_enable_mask, 1); 2342a895871SWei Ni r = REG_SET_MASK(r, sg->thermtrip_any_en_mask, 0); 2352a895871SWei Ni writel(r, ts->regs + THERMCTL_THERMTRIP_CTL); 2362a895871SWei Ni 2372a895871SWei Ni return 0; 2382a895871SWei Ni } 2392a895871SWei Ni 2402a895871SWei Ni /** 2412a895871SWei Ni * tegra_soctherm_set_hwtrips() - set HW trip point from DT data 2422a895871SWei Ni * @dev: struct device * of the SOC_THERM instance 2432a895871SWei Ni * 2442a895871SWei Ni * Configure the SOC_THERM HW trip points, setting "THERMTRIP" 2452a895871SWei Ni * trip points , using "critical" type trip_temp from thermal 2462a895871SWei Ni * zone. 2472a895871SWei Ni * After they have been configured, THERMTRIP will take action 2482a895871SWei Ni * when the configured SoC thermal sensor group reaches a 2492a895871SWei Ni * certain temperature. 2502a895871SWei Ni * 2512a895871SWei Ni * Return: 0 upon success, or a negative error code on failure. 2522a895871SWei Ni * "Success" does not mean that trips was enabled; it could also 2532a895871SWei Ni * mean that no node was found in DT. 2542a895871SWei Ni * THERMTRIP has been enabled successfully when a message similar to 2552a895871SWei Ni * this one appears on the serial console: 2562a895871SWei Ni * "thermtrip: will shut down when sensor group XXX reaches YYYYYY mC" 2572a895871SWei Ni */ 2582a895871SWei Ni static int tegra_soctherm_set_hwtrips(struct device *dev, 2592a895871SWei Ni const struct tegra_tsensor_group *sg, 2602a895871SWei Ni struct thermal_zone_device *tz) 2612a895871SWei Ni { 2622a895871SWei Ni int temperature; 2632a895871SWei Ni int ret; 2642a895871SWei Ni 2652a895871SWei Ni ret = tz->ops->get_crit_temp(tz, &temperature); 2662a895871SWei Ni if (ret) { 2672a895871SWei Ni dev_warn(dev, "thermtrip: %s: missing critical temperature\n", 2682a895871SWei Ni sg->name); 2692a895871SWei Ni return ret; 2702a895871SWei Ni } 2712a895871SWei Ni 2722a895871SWei Ni ret = thermtrip_program(dev, sg, temperature); 2732a895871SWei Ni if (ret) { 2742a895871SWei Ni dev_err(dev, "thermtrip: %s: error during enable\n", 2752a895871SWei Ni sg->name); 2762a895871SWei Ni return ret; 2772a895871SWei Ni } 2782a895871SWei Ni 2792a895871SWei Ni dev_info(dev, 2802a895871SWei Ni "thermtrip: will shut down when %s reaches %d mC\n", 2812a895871SWei Ni sg->name, temperature); 2822a895871SWei Ni 2832a895871SWei Ni return 0; 2842a895871SWei Ni } 2852a895871SWei Ni 286d753b22dSWei Ni #ifdef CONFIG_DEBUG_FS 287d753b22dSWei Ni static int regs_show(struct seq_file *s, void *data) 288d753b22dSWei Ni { 289d753b22dSWei Ni struct platform_device *pdev = s->private; 290d753b22dSWei Ni struct tegra_soctherm *ts = platform_get_drvdata(pdev); 291d753b22dSWei Ni const struct tegra_tsensor *tsensors = ts->soc->tsensors; 2922a895871SWei Ni const struct tegra_tsensor_group **ttgs = ts->soc->ttgs; 293d753b22dSWei Ni u32 r, state; 294d753b22dSWei Ni int i; 295d753b22dSWei Ni 296d753b22dSWei Ni seq_puts(s, "-----TSENSE (convert HW)-----\n"); 297d753b22dSWei Ni 298d753b22dSWei Ni for (i = 0; i < ts->soc->num_tsensors; i++) { 299d753b22dSWei Ni r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG1); 300d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG1_TEMP_ENABLE); 301d753b22dSWei Ni 302d753b22dSWei Ni seq_printf(s, "%s: ", tsensors[i].name); 303d753b22dSWei Ni seq_printf(s, "En(%d) ", state); 304d753b22dSWei Ni 305d753b22dSWei Ni if (!state) { 306d753b22dSWei Ni seq_puts(s, "\n"); 307d753b22dSWei Ni continue; 308d753b22dSWei Ni } 309d753b22dSWei Ni 310d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG1_TIDDQ_EN_MASK); 311d753b22dSWei Ni seq_printf(s, "tiddq(%d) ", state); 312d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG1_TEN_COUNT_MASK); 313d753b22dSWei Ni seq_printf(s, "ten_count(%d) ", state); 314d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG1_TSAMPLE_MASK); 315d753b22dSWei Ni seq_printf(s, "tsample(%d) ", state + 1); 316d753b22dSWei Ni 317d753b22dSWei Ni r = readl(ts->regs + tsensors[i].base + SENSOR_STATUS1); 318d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_STATUS1_TEMP_VALID_MASK); 319d753b22dSWei Ni seq_printf(s, "Temp(%d/", state); 320d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_STATUS1_TEMP_MASK); 321d753b22dSWei Ni seq_printf(s, "%d) ", translate_temp(state)); 322d753b22dSWei Ni 323d753b22dSWei Ni r = readl(ts->regs + tsensors[i].base + SENSOR_STATUS0); 324d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_STATUS0_VALID_MASK); 325d753b22dSWei Ni seq_printf(s, "Capture(%d/", state); 326d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_STATUS0_CAPTURE_MASK); 327d753b22dSWei Ni seq_printf(s, "%d) ", state); 328d753b22dSWei Ni 329d753b22dSWei Ni r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG0); 330d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG0_STOP); 331d753b22dSWei Ni seq_printf(s, "Stop(%d) ", state); 332d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG0_TALL_MASK); 333d753b22dSWei Ni seq_printf(s, "Tall(%d) ", state); 334d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG0_TCALC_OVER); 335d753b22dSWei Ni seq_printf(s, "Over(%d/", state); 336d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG0_OVER); 337d753b22dSWei Ni seq_printf(s, "%d/", state); 338d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG0_CPTR_OVER); 339d753b22dSWei Ni seq_printf(s, "%d) ", state); 340d753b22dSWei Ni 341d753b22dSWei Ni r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG2); 342d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG2_THERMA_MASK); 343d753b22dSWei Ni seq_printf(s, "Therm_A/B(%d/", state); 344d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_CONFIG2_THERMB_MASK); 345d753b22dSWei Ni seq_printf(s, "%d)\n", (s16)state); 346d753b22dSWei Ni } 347d753b22dSWei Ni 348d753b22dSWei Ni r = readl(ts->regs + SENSOR_PDIV); 349d753b22dSWei Ni seq_printf(s, "PDIV: 0x%x\n", r); 350d753b22dSWei Ni 351d753b22dSWei Ni r = readl(ts->regs + SENSOR_HOTSPOT_OFF); 352d753b22dSWei Ni seq_printf(s, "HOTSPOT: 0x%x\n", r); 353d753b22dSWei Ni 354d753b22dSWei Ni seq_puts(s, "\n"); 355d753b22dSWei Ni seq_puts(s, "-----SOC_THERM-----\n"); 356d753b22dSWei Ni 357d753b22dSWei Ni r = readl(ts->regs + SENSOR_TEMP1); 358d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_TEMP1_CPU_TEMP_MASK); 359d753b22dSWei Ni seq_printf(s, "Temperatures: CPU(%d) ", translate_temp(state)); 360d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_TEMP1_GPU_TEMP_MASK); 361d753b22dSWei Ni seq_printf(s, " GPU(%d) ", translate_temp(state)); 362d753b22dSWei Ni r = readl(ts->regs + SENSOR_TEMP2); 363d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_TEMP2_PLLX_TEMP_MASK); 364d753b22dSWei Ni seq_printf(s, " PLLX(%d) ", translate_temp(state)); 365d753b22dSWei Ni state = REG_GET_MASK(r, SENSOR_TEMP2_MEM_TEMP_MASK); 366d753b22dSWei Ni seq_printf(s, " MEM(%d)\n", translate_temp(state)); 367d753b22dSWei Ni 3682a895871SWei Ni r = readl(ts->regs + THERMCTL_THERMTRIP_CTL); 3692a895871SWei Ni state = REG_GET_MASK(r, ttgs[0]->thermtrip_any_en_mask); 3702a895871SWei Ni seq_printf(s, "Thermtrip Any En(%d)\n", state); 3712a895871SWei Ni for (i = 0; i < ts->soc->num_ttgs; i++) { 3722a895871SWei Ni state = REG_GET_MASK(r, ttgs[i]->thermtrip_enable_mask); 3732a895871SWei Ni seq_printf(s, " %s En(%d) ", ttgs[i]->name, state); 3742a895871SWei Ni state = REG_GET_MASK(r, ttgs[i]->thermtrip_threshold_mask); 3752a895871SWei Ni state *= ts->soc->thresh_grain; 3762a895871SWei Ni seq_printf(s, "Thresh(%d)\n", state); 3772a895871SWei Ni } 3782a895871SWei Ni 379d753b22dSWei Ni return 0; 380d753b22dSWei Ni } 381d753b22dSWei Ni 382d753b22dSWei Ni static int regs_open(struct inode *inode, struct file *file) 383d753b22dSWei Ni { 384d753b22dSWei Ni return single_open(file, regs_show, inode->i_private); 385d753b22dSWei Ni } 386d753b22dSWei Ni 387d753b22dSWei Ni static const struct file_operations regs_fops = { 388d753b22dSWei Ni .open = regs_open, 389d753b22dSWei Ni .read = seq_read, 390d753b22dSWei Ni .llseek = seq_lseek, 391d753b22dSWei Ni .release = single_release, 392d753b22dSWei Ni }; 393d753b22dSWei Ni 394d753b22dSWei Ni static void soctherm_debug_init(struct platform_device *pdev) 395d753b22dSWei Ni { 396d753b22dSWei Ni struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 397d753b22dSWei Ni struct dentry *root, *file; 398d753b22dSWei Ni 399d753b22dSWei Ni root = debugfs_create_dir("soctherm", NULL); 400d753b22dSWei Ni if (!root) { 401d753b22dSWei Ni dev_err(&pdev->dev, "failed to create debugfs directory\n"); 402d753b22dSWei Ni return; 403d753b22dSWei Ni } 404d753b22dSWei Ni 405d753b22dSWei Ni tegra->debugfs_dir = root; 406d753b22dSWei Ni 407d753b22dSWei Ni file = debugfs_create_file("reg_contents", 0644, root, 408d753b22dSWei Ni pdev, ®s_fops); 409d753b22dSWei Ni if (!file) { 410d753b22dSWei Ni dev_err(&pdev->dev, "failed to create debugfs file\n"); 411d753b22dSWei Ni debugfs_remove_recursive(tegra->debugfs_dir); 412d753b22dSWei Ni tegra->debugfs_dir = NULL; 413d753b22dSWei Ni } 414d753b22dSWei Ni } 415d753b22dSWei Ni #else 416d753b22dSWei Ni static inline void soctherm_debug_init(struct platform_device *pdev) {} 417d753b22dSWei Ni #endif 418d753b22dSWei Ni 4198de2ab02SWei Ni static int soctherm_clk_enable(struct platform_device *pdev, bool enable) 4208de2ab02SWei Ni { 4218de2ab02SWei Ni struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 4228de2ab02SWei Ni int err; 4238de2ab02SWei Ni 4248de2ab02SWei Ni if (!tegra->clock_soctherm || !tegra->clock_tsensor) 4258de2ab02SWei Ni return -EINVAL; 4268de2ab02SWei Ni 4278de2ab02SWei Ni reset_control_assert(tegra->reset); 4288de2ab02SWei Ni 4298de2ab02SWei Ni if (enable) { 4308de2ab02SWei Ni err = clk_prepare_enable(tegra->clock_soctherm); 4318de2ab02SWei Ni if (err) { 4328de2ab02SWei Ni reset_control_deassert(tegra->reset); 4338de2ab02SWei Ni return err; 4348de2ab02SWei Ni } 4358de2ab02SWei Ni 4368de2ab02SWei Ni err = clk_prepare_enable(tegra->clock_tsensor); 4378de2ab02SWei Ni if (err) { 4388de2ab02SWei Ni clk_disable_unprepare(tegra->clock_soctherm); 4398de2ab02SWei Ni reset_control_deassert(tegra->reset); 4408de2ab02SWei Ni return err; 4418de2ab02SWei Ni } 4428de2ab02SWei Ni } else { 4438de2ab02SWei Ni clk_disable_unprepare(tegra->clock_tsensor); 4448de2ab02SWei Ni clk_disable_unprepare(tegra->clock_soctherm); 4458de2ab02SWei Ni } 4468de2ab02SWei Ni 4478de2ab02SWei Ni reset_control_deassert(tegra->reset); 4488de2ab02SWei Ni 4498de2ab02SWei Ni return 0; 4508de2ab02SWei Ni } 4518de2ab02SWei Ni 4521ed895c2SWei Ni static void soctherm_init(struct platform_device *pdev) 4531ed895c2SWei Ni { 4541ed895c2SWei Ni struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 4551ed895c2SWei Ni const struct tegra_tsensor_group **ttgs = tegra->soc->ttgs; 4561ed895c2SWei Ni int i; 4571ed895c2SWei Ni u32 pdiv, hotspot; 4581ed895c2SWei Ni 4591ed895c2SWei Ni /* Initialize raw sensors */ 4601ed895c2SWei Ni for (i = 0; i < tegra->soc->num_tsensors; ++i) 4611ed895c2SWei Ni enable_tsensor(tegra, i); 4621ed895c2SWei Ni 4631ed895c2SWei Ni /* program pdiv and hotspot offsets per THERM */ 4641ed895c2SWei Ni pdiv = readl(tegra->regs + SENSOR_PDIV); 4651ed895c2SWei Ni hotspot = readl(tegra->regs + SENSOR_HOTSPOT_OFF); 4661ed895c2SWei Ni for (i = 0; i < tegra->soc->num_ttgs; ++i) { 4671ed895c2SWei Ni pdiv = REG_SET_MASK(pdiv, ttgs[i]->pdiv_mask, 4681ed895c2SWei Ni ttgs[i]->pdiv); 4691ed895c2SWei Ni /* hotspot offset from PLLX, doesn't need to configure PLLX */ 4701ed895c2SWei Ni if (ttgs[i]->id == TEGRA124_SOCTHERM_SENSOR_PLLX) 4711ed895c2SWei Ni continue; 4721ed895c2SWei Ni hotspot = REG_SET_MASK(hotspot, 4731ed895c2SWei Ni ttgs[i]->pllx_hotspot_mask, 4741ed895c2SWei Ni ttgs[i]->pllx_hotspot_diff); 4751ed895c2SWei Ni } 4761ed895c2SWei Ni writel(pdiv, tegra->regs + SENSOR_PDIV); 4771ed895c2SWei Ni writel(hotspot, tegra->regs + SENSOR_HOTSPOT_OFF); 4781ed895c2SWei Ni } 4791ed895c2SWei Ni 48065b6d57cSWei Ni static const struct of_device_id tegra_soctherm_of_match[] = { 48165b6d57cSWei Ni #ifdef CONFIG_ARCH_TEGRA_124_SOC 48265b6d57cSWei Ni { 48365b6d57cSWei Ni .compatible = "nvidia,tegra124-soctherm", 48465b6d57cSWei Ni .data = &tegra124_soctherm, 48565b6d57cSWei Ni }, 48665b6d57cSWei Ni #endif 4878204104fSWei Ni #ifdef CONFIG_ARCH_TEGRA_210_SOC 4888204104fSWei Ni { 4898204104fSWei Ni .compatible = "nvidia,tegra210-soctherm", 4908204104fSWei Ni .data = &tegra210_soctherm, 4918204104fSWei Ni }, 4928204104fSWei Ni #endif 49365b6d57cSWei Ni { }, 49465b6d57cSWei Ni }; 49565b6d57cSWei Ni MODULE_DEVICE_TABLE(of, tegra_soctherm_of_match); 49665b6d57cSWei Ni 49765b6d57cSWei Ni static int tegra_soctherm_probe(struct platform_device *pdev) 49865b6d57cSWei Ni { 49965b6d57cSWei Ni const struct of_device_id *match; 50065b6d57cSWei Ni struct tegra_soctherm *tegra; 50165b6d57cSWei Ni struct thermal_zone_device *z; 50265b6d57cSWei Ni struct tsensor_shared_calib shared_calib; 50365b6d57cSWei Ni struct resource *res; 50465b6d57cSWei Ni struct tegra_soctherm_soc *soc; 50565b6d57cSWei Ni unsigned int i; 50665b6d57cSWei Ni int err; 50765b6d57cSWei Ni 50865b6d57cSWei Ni match = of_match_node(tegra_soctherm_of_match, pdev->dev.of_node); 50965b6d57cSWei Ni if (!match) 51065b6d57cSWei Ni return -ENODEV; 51165b6d57cSWei Ni 51265b6d57cSWei Ni soc = (struct tegra_soctherm_soc *)match->data; 51365b6d57cSWei Ni if (soc->num_ttgs > TEGRA124_SOCTHERM_SENSOR_NUM) 51465b6d57cSWei Ni return -EINVAL; 51565b6d57cSWei Ni 51665b6d57cSWei Ni tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); 51765b6d57cSWei Ni if (!tegra) 51865b6d57cSWei Ni return -ENOMEM; 51965b6d57cSWei Ni 52065b6d57cSWei Ni dev_set_drvdata(&pdev->dev, tegra); 52165b6d57cSWei Ni 52265b6d57cSWei Ni tegra->soc = soc; 52365b6d57cSWei Ni 52465b6d57cSWei Ni res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 52565b6d57cSWei Ni tegra->regs = devm_ioremap_resource(&pdev->dev, res); 52665b6d57cSWei Ni if (IS_ERR(tegra->regs)) 52765b6d57cSWei Ni return PTR_ERR(tegra->regs); 52865b6d57cSWei Ni 52965b6d57cSWei Ni tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm"); 53065b6d57cSWei Ni if (IS_ERR(tegra->reset)) { 53165b6d57cSWei Ni dev_err(&pdev->dev, "can't get soctherm reset\n"); 53265b6d57cSWei Ni return PTR_ERR(tegra->reset); 53365b6d57cSWei Ni } 53465b6d57cSWei Ni 53565b6d57cSWei Ni tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor"); 53665b6d57cSWei Ni if (IS_ERR(tegra->clock_tsensor)) { 53765b6d57cSWei Ni dev_err(&pdev->dev, "can't get tsensor clock\n"); 53865b6d57cSWei Ni return PTR_ERR(tegra->clock_tsensor); 53965b6d57cSWei Ni } 54065b6d57cSWei Ni 54165b6d57cSWei Ni tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soctherm"); 54265b6d57cSWei Ni if (IS_ERR(tegra->clock_soctherm)) { 54365b6d57cSWei Ni dev_err(&pdev->dev, "can't get soctherm clock\n"); 54465b6d57cSWei Ni return PTR_ERR(tegra->clock_soctherm); 54565b6d57cSWei Ni } 54665b6d57cSWei Ni 5471ed895c2SWei Ni tegra->calib = devm_kzalloc(&pdev->dev, 5481ed895c2SWei Ni sizeof(u32) * soc->num_tsensors, 5491ed895c2SWei Ni GFP_KERNEL); 5501ed895c2SWei Ni if (!tegra->calib) 5511ed895c2SWei Ni return -ENOMEM; 5521ed895c2SWei Ni 5531ed895c2SWei Ni /* calculate shared calibration data */ 5541ed895c2SWei Ni err = tegra_calc_shared_calib(soc->tfuse, &shared_calib); 5551ed895c2SWei Ni if (err) 5561ed895c2SWei Ni return err; 5571ed895c2SWei Ni 5581ed895c2SWei Ni /* calculate tsensor calibaration data */ 5591ed895c2SWei Ni for (i = 0; i < soc->num_tsensors; ++i) { 5601ed895c2SWei Ni err = tegra_calc_tsensor_calib(&soc->tsensors[i], 5611ed895c2SWei Ni &shared_calib, 5621ed895c2SWei Ni &tegra->calib[i]); 5631ed895c2SWei Ni if (err) 5641ed895c2SWei Ni return err; 5651ed895c2SWei Ni } 5661ed895c2SWei Ni 567f09d6984SWei Ni tegra->thermctl_tzs = devm_kzalloc(&pdev->dev, 568f09d6984SWei Ni sizeof(*z) * soc->num_ttgs, 569f09d6984SWei Ni GFP_KERNEL); 570f09d6984SWei Ni if (!tegra->thermctl_tzs) 571f09d6984SWei Ni return -ENOMEM; 572f09d6984SWei Ni 5738de2ab02SWei Ni err = soctherm_clk_enable(pdev, true); 57465b6d57cSWei Ni if (err) 57565b6d57cSWei Ni return err; 57665b6d57cSWei Ni 5771ed895c2SWei Ni soctherm_init(pdev); 57865b6d57cSWei Ni 57965b6d57cSWei Ni for (i = 0; i < soc->num_ttgs; ++i) { 58065b6d57cSWei Ni struct tegra_thermctl_zone *zone = 58165b6d57cSWei Ni devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL); 58265b6d57cSWei Ni if (!zone) { 58365b6d57cSWei Ni err = -ENOMEM; 58465b6d57cSWei Ni goto disable_clocks; 58565b6d57cSWei Ni } 58665b6d57cSWei Ni 58765b6d57cSWei Ni zone->reg = tegra->regs + soc->ttgs[i]->sensor_temp_offset; 5882a895871SWei Ni zone->dev = &pdev->dev; 5892a895871SWei Ni zone->sg = soc->ttgs[i]; 59065b6d57cSWei Ni 59165b6d57cSWei Ni z = devm_thermal_zone_of_sensor_register(&pdev->dev, 59265b6d57cSWei Ni soc->ttgs[i]->id, zone, 59365b6d57cSWei Ni &tegra_of_thermal_ops); 59465b6d57cSWei Ni if (IS_ERR(z)) { 59565b6d57cSWei Ni err = PTR_ERR(z); 59665b6d57cSWei Ni dev_err(&pdev->dev, "failed to register sensor: %d\n", 59765b6d57cSWei Ni err); 59865b6d57cSWei Ni goto disable_clocks; 59965b6d57cSWei Ni } 6002a895871SWei Ni 6012a895871SWei Ni zone->tz = z; 602f09d6984SWei Ni tegra->thermctl_tzs[soc->ttgs[i]->id] = z; 6032a895871SWei Ni 6042a895871SWei Ni /* Configure hw trip points */ 6052a895871SWei Ni tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z); 60665b6d57cSWei Ni } 60765b6d57cSWei Ni 608d753b22dSWei Ni soctherm_debug_init(pdev); 609d753b22dSWei Ni 61065b6d57cSWei Ni return 0; 61165b6d57cSWei Ni 61265b6d57cSWei Ni disable_clocks: 6138de2ab02SWei Ni soctherm_clk_enable(pdev, false); 61465b6d57cSWei Ni 61565b6d57cSWei Ni return err; 61665b6d57cSWei Ni } 61765b6d57cSWei Ni 61865b6d57cSWei Ni static int tegra_soctherm_remove(struct platform_device *pdev) 61965b6d57cSWei Ni { 62065b6d57cSWei Ni struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 62165b6d57cSWei Ni 622d753b22dSWei Ni debugfs_remove_recursive(tegra->debugfs_dir); 623d753b22dSWei Ni 6248de2ab02SWei Ni soctherm_clk_enable(pdev, false); 62565b6d57cSWei Ni 62665b6d57cSWei Ni return 0; 62765b6d57cSWei Ni } 62865b6d57cSWei Ni 629a977c41eSArnd Bergmann static int __maybe_unused soctherm_suspend(struct device *dev) 630f09d6984SWei Ni { 631f09d6984SWei Ni struct platform_device *pdev = to_platform_device(dev); 632f09d6984SWei Ni 633f09d6984SWei Ni soctherm_clk_enable(pdev, false); 634f09d6984SWei Ni 635f09d6984SWei Ni return 0; 636f09d6984SWei Ni } 637f09d6984SWei Ni 638a977c41eSArnd Bergmann static int __maybe_unused soctherm_resume(struct device *dev) 639f09d6984SWei Ni { 640f09d6984SWei Ni struct platform_device *pdev = to_platform_device(dev); 641f09d6984SWei Ni struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 642f09d6984SWei Ni struct tegra_soctherm_soc *soc = tegra->soc; 643f09d6984SWei Ni int err, i; 644f09d6984SWei Ni 645f09d6984SWei Ni err = soctherm_clk_enable(pdev, true); 646f09d6984SWei Ni if (err) { 647f09d6984SWei Ni dev_err(&pdev->dev, 648f09d6984SWei Ni "Resume failed: enable clocks failed\n"); 649f09d6984SWei Ni return err; 650f09d6984SWei Ni } 651f09d6984SWei Ni 652f09d6984SWei Ni soctherm_init(pdev); 653f09d6984SWei Ni 654f09d6984SWei Ni for (i = 0; i < soc->num_ttgs; ++i) { 655f09d6984SWei Ni struct thermal_zone_device *tz; 656f09d6984SWei Ni 657f09d6984SWei Ni tz = tegra->thermctl_tzs[soc->ttgs[i]->id]; 658f09d6984SWei Ni tegra_soctherm_set_hwtrips(dev, soc->ttgs[i], tz); 659f09d6984SWei Ni } 660f09d6984SWei Ni 661f09d6984SWei Ni return 0; 662f09d6984SWei Ni } 663f09d6984SWei Ni 664f09d6984SWei Ni static SIMPLE_DEV_PM_OPS(tegra_soctherm_pm, soctherm_suspend, soctherm_resume); 665f09d6984SWei Ni 66665b6d57cSWei Ni static struct platform_driver tegra_soctherm_driver = { 66765b6d57cSWei Ni .probe = tegra_soctherm_probe, 66865b6d57cSWei Ni .remove = tegra_soctherm_remove, 66965b6d57cSWei Ni .driver = { 67065b6d57cSWei Ni .name = "tegra_soctherm", 671f09d6984SWei Ni .pm = &tegra_soctherm_pm, 67265b6d57cSWei Ni .of_match_table = tegra_soctherm_of_match, 67365b6d57cSWei Ni }, 67465b6d57cSWei Ni }; 67565b6d57cSWei Ni module_platform_driver(tegra_soctherm_driver); 67665b6d57cSWei Ni 67765b6d57cSWei Ni MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>"); 67865b6d57cSWei Ni MODULE_DESCRIPTION("NVIDIA Tegra SOCTHERM thermal management driver"); 67965b6d57cSWei Ni MODULE_LICENSE("GPL v2"); 680