.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
---|
1 | 2 | /* |
---|
2 | | - * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. |
---|
| 3 | + * Copyright (c) 2014 - 2018, NVIDIA CORPORATION. All rights reserved. |
---|
3 | 4 | * |
---|
4 | 5 | * Author: |
---|
5 | 6 | * Mikko Perttunen <mperttunen@nvidia.com> |
---|
.. | .. |
---|
22 | 23 | #include <linux/err.h> |
---|
23 | 24 | #include <linux/interrupt.h> |
---|
24 | 25 | #include <linux/io.h> |
---|
| 26 | +#include <linux/irq.h> |
---|
| 27 | +#include <linux/irqdomain.h> |
---|
25 | 28 | #include <linux/module.h> |
---|
26 | 29 | #include <linux/of.h> |
---|
27 | 30 | #include <linux/platform_device.h> |
---|
.. | .. |
---|
85 | 88 | #define THERMCTL_LVL0_UP_STATS 0x10 |
---|
86 | 89 | #define THERMCTL_LVL0_DN_STATS 0x14 |
---|
87 | 90 | |
---|
| 91 | +#define THERMCTL_INTR_STATUS 0x84 |
---|
| 92 | + |
---|
| 93 | +#define TH_INTR_MD0_MASK BIT(25) |
---|
| 94 | +#define TH_INTR_MU0_MASK BIT(24) |
---|
| 95 | +#define TH_INTR_GD0_MASK BIT(17) |
---|
| 96 | +#define TH_INTR_GU0_MASK BIT(16) |
---|
| 97 | +#define TH_INTR_CD0_MASK BIT(9) |
---|
| 98 | +#define TH_INTR_CU0_MASK BIT(8) |
---|
| 99 | +#define TH_INTR_PD0_MASK BIT(1) |
---|
| 100 | +#define TH_INTR_PU0_MASK BIT(0) |
---|
| 101 | +#define TH_INTR_IGNORE_MASK 0xFCFCFCFC |
---|
| 102 | + |
---|
88 | 103 | #define THERMCTL_STATS_CTL 0x94 |
---|
89 | 104 | #define STATS_CTL_CLR_DN 0x8 |
---|
90 | 105 | #define STATS_CTL_EN_DN 0x4 |
---|
91 | 106 | #define STATS_CTL_CLR_UP 0x2 |
---|
92 | 107 | #define STATS_CTL_EN_UP 0x1 |
---|
| 108 | + |
---|
| 109 | +#define OC1_CFG 0x310 |
---|
| 110 | +#define OC1_CFG_LONG_LATENCY_MASK BIT(6) |
---|
| 111 | +#define OC1_CFG_HW_RESTORE_MASK BIT(5) |
---|
| 112 | +#define OC1_CFG_PWR_GOOD_MASK_MASK BIT(4) |
---|
| 113 | +#define OC1_CFG_THROTTLE_MODE_MASK (0x3 << 2) |
---|
| 114 | +#define OC1_CFG_ALARM_POLARITY_MASK BIT(1) |
---|
| 115 | +#define OC1_CFG_EN_THROTTLE_MASK BIT(0) |
---|
| 116 | + |
---|
| 117 | +#define OC1_CNT_THRESHOLD 0x314 |
---|
| 118 | +#define OC1_THROTTLE_PERIOD 0x318 |
---|
| 119 | +#define OC1_ALARM_COUNT 0x31c |
---|
| 120 | +#define OC1_FILTER 0x320 |
---|
| 121 | +#define OC1_STATS 0x3a8 |
---|
| 122 | + |
---|
| 123 | +#define OC_INTR_STATUS 0x39c |
---|
| 124 | +#define OC_INTR_ENABLE 0x3a0 |
---|
| 125 | +#define OC_INTR_DISABLE 0x3a4 |
---|
| 126 | +#define OC_STATS_CTL 0x3c4 |
---|
| 127 | +#define OC_STATS_CTL_CLR_ALL 0x2 |
---|
| 128 | +#define OC_STATS_CTL_EN_ALL 0x1 |
---|
| 129 | + |
---|
| 130 | +#define OC_INTR_OC1_MASK BIT(0) |
---|
| 131 | +#define OC_INTR_OC2_MASK BIT(1) |
---|
| 132 | +#define OC_INTR_OC3_MASK BIT(2) |
---|
| 133 | +#define OC_INTR_OC4_MASK BIT(3) |
---|
| 134 | +#define OC_INTR_OC5_MASK BIT(4) |
---|
93 | 135 | |
---|
94 | 136 | #define THROT_GLOBAL_CFG 0x400 |
---|
95 | 137 | #define THROT_GLOBAL_ENB_MASK BIT(0) |
---|
.. | .. |
---|
160 | 202 | /* get dividend from the depth */ |
---|
161 | 203 | #define THROT_DEPTH_DIVIDEND(depth) ((256 * (100 - (depth)) / 100) - 1) |
---|
162 | 204 | |
---|
| 205 | +/* gk20a nv_therm interface N:3 Mapping. Levels defined in tegra124-soctherm.h |
---|
| 206 | + * level vector |
---|
| 207 | + * NONE 3'b000 |
---|
| 208 | + * LOW 3'b001 |
---|
| 209 | + * MED 3'b011 |
---|
| 210 | + * HIGH 3'b111 |
---|
| 211 | + */ |
---|
| 212 | +#define THROT_LEVEL_TO_DEPTH(level) ((0x1 << (level)) - 1) |
---|
| 213 | + |
---|
163 | 214 | /* get THROT_PSKIP_xxx offset per LIGHT/HEAVY throt and CPU/GPU dev */ |
---|
164 | 215 | #define THROT_OFFSET 0x30 |
---|
165 | 216 | #define THROT_PSKIP_CTRL(throt, dev) (THROT_PSKIP_CTRL_LITE_CPU + \ |
---|
.. | .. |
---|
173 | 224 | #define THROT_DELAY_CTRL(throt) (THROT_DELAY_LITE + \ |
---|
174 | 225 | (THROT_OFFSET * throt)) |
---|
175 | 226 | |
---|
| 227 | +#define ALARM_OFFSET 0x14 |
---|
| 228 | +#define ALARM_CFG(throt) (OC1_CFG + \ |
---|
| 229 | + (ALARM_OFFSET * (throt - THROTTLE_OC1))) |
---|
| 230 | + |
---|
| 231 | +#define ALARM_CNT_THRESHOLD(throt) (OC1_CNT_THRESHOLD + \ |
---|
| 232 | + (ALARM_OFFSET * (throt - THROTTLE_OC1))) |
---|
| 233 | + |
---|
| 234 | +#define ALARM_THROTTLE_PERIOD(throt) (OC1_THROTTLE_PERIOD + \ |
---|
| 235 | + (ALARM_OFFSET * (throt - THROTTLE_OC1))) |
---|
| 236 | + |
---|
| 237 | +#define ALARM_ALARM_COUNT(throt) (OC1_ALARM_COUNT + \ |
---|
| 238 | + (ALARM_OFFSET * (throt - THROTTLE_OC1))) |
---|
| 239 | + |
---|
| 240 | +#define ALARM_FILTER(throt) (OC1_FILTER + \ |
---|
| 241 | + (ALARM_OFFSET * (throt - THROTTLE_OC1))) |
---|
| 242 | + |
---|
| 243 | +#define ALARM_STATS(throt) (OC1_STATS + \ |
---|
| 244 | + (4 * (throt - THROTTLE_OC1))) |
---|
| 245 | + |
---|
176 | 246 | /* get CCROC_THROT_PSKIP_xxx offset per HIGH/MED/LOW vect*/ |
---|
177 | 247 | #define CCROC_THROT_OFFSET 0x0c |
---|
178 | 248 | #define CCROC_THROT_PSKIP_CTRL_CPU_REG(vect) (CCROC_THROT_PSKIP_CTRL_CPU + \ |
---|
.. | .. |
---|
184 | 254 | #define THERMCTL_LVL_REGS_SIZE 0x20 |
---|
185 | 255 | #define THERMCTL_LVL_REG(rg, lv) ((rg) + ((lv) * THERMCTL_LVL_REGS_SIZE)) |
---|
186 | 256 | |
---|
| 257 | +#define OC_THROTTLE_MODE_DISABLED 0 |
---|
| 258 | +#define OC_THROTTLE_MODE_BRIEF 2 |
---|
| 259 | + |
---|
187 | 260 | static const int min_low_temp = -127000; |
---|
188 | 261 | static const int max_high_temp = 127000; |
---|
189 | 262 | |
---|
190 | 263 | enum soctherm_throttle_id { |
---|
191 | 264 | THROTTLE_LIGHT = 0, |
---|
192 | 265 | THROTTLE_HEAVY, |
---|
| 266 | + THROTTLE_OC1, |
---|
| 267 | + THROTTLE_OC2, |
---|
| 268 | + THROTTLE_OC3, |
---|
| 269 | + THROTTLE_OC4, |
---|
| 270 | + THROTTLE_OC5, /* OC5 is reserved */ |
---|
193 | 271 | THROTTLE_SIZE, |
---|
| 272 | +}; |
---|
| 273 | + |
---|
| 274 | +enum soctherm_oc_irq_id { |
---|
| 275 | + TEGRA_SOC_OC_IRQ_1, |
---|
| 276 | + TEGRA_SOC_OC_IRQ_2, |
---|
| 277 | + TEGRA_SOC_OC_IRQ_3, |
---|
| 278 | + TEGRA_SOC_OC_IRQ_4, |
---|
| 279 | + TEGRA_SOC_OC_IRQ_5, |
---|
| 280 | + TEGRA_SOC_OC_IRQ_MAX, |
---|
194 | 281 | }; |
---|
195 | 282 | |
---|
196 | 283 | enum soctherm_throttle_dev_id { |
---|
.. | .. |
---|
202 | 289 | static const char *const throt_names[] = { |
---|
203 | 290 | [THROTTLE_LIGHT] = "light", |
---|
204 | 291 | [THROTTLE_HEAVY] = "heavy", |
---|
| 292 | + [THROTTLE_OC1] = "oc1", |
---|
| 293 | + [THROTTLE_OC2] = "oc2", |
---|
| 294 | + [THROTTLE_OC3] = "oc3", |
---|
| 295 | + [THROTTLE_OC4] = "oc4", |
---|
| 296 | + [THROTTLE_OC5] = "oc5", |
---|
205 | 297 | }; |
---|
206 | 298 | |
---|
207 | 299 | struct tegra_soctherm; |
---|
.. | .. |
---|
213 | 305 | const struct tegra_tsensor_group *sg; |
---|
214 | 306 | }; |
---|
215 | 307 | |
---|
| 308 | +struct soctherm_oc_cfg { |
---|
| 309 | + u32 active_low; |
---|
| 310 | + u32 throt_period; |
---|
| 311 | + u32 alarm_cnt_thresh; |
---|
| 312 | + u32 alarm_filter; |
---|
| 313 | + u32 mode; |
---|
| 314 | + bool intr_en; |
---|
| 315 | +}; |
---|
| 316 | + |
---|
216 | 317 | struct soctherm_throt_cfg { |
---|
217 | 318 | const char *name; |
---|
218 | 319 | unsigned int id; |
---|
219 | 320 | u8 priority; |
---|
220 | 321 | u8 cpu_throt_level; |
---|
221 | 322 | u32 cpu_throt_depth; |
---|
| 323 | + u32 gpu_throt_level; |
---|
| 324 | + struct soctherm_oc_cfg oc_cfg; |
---|
222 | 325 | struct thermal_cooling_device *cdev; |
---|
223 | 326 | bool init; |
---|
224 | 327 | }; |
---|
.. | .. |
---|
231 | 334 | void __iomem *clk_regs; |
---|
232 | 335 | void __iomem *ccroc_regs; |
---|
233 | 336 | |
---|
| 337 | + int thermal_irq; |
---|
| 338 | + int edp_irq; |
---|
| 339 | + |
---|
234 | 340 | u32 *calib; |
---|
235 | 341 | struct thermal_zone_device **thermctl_tzs; |
---|
236 | 342 | struct tegra_soctherm_soc *soc; |
---|
.. | .. |
---|
238 | 344 | struct soctherm_throt_cfg throt_cfgs[THROTTLE_SIZE]; |
---|
239 | 345 | |
---|
240 | 346 | struct dentry *debugfs_dir; |
---|
| 347 | + |
---|
| 348 | + struct mutex thermctl_lock; |
---|
241 | 349 | }; |
---|
| 350 | + |
---|
| 351 | +struct soctherm_oc_irq_chip_data { |
---|
| 352 | + struct mutex irq_lock; /* serialize OC IRQs */ |
---|
| 353 | + struct irq_chip irq_chip; |
---|
| 354 | + struct irq_domain *domain; |
---|
| 355 | + int irq_enable; |
---|
| 356 | +}; |
---|
| 357 | + |
---|
| 358 | +static struct soctherm_oc_irq_chip_data soc_irq_cdata; |
---|
242 | 359 | |
---|
243 | 360 | /** |
---|
244 | 361 | * ccroc_writel() - writes a value to a CCROC register |
---|
245 | 362 | * @ts: pointer to a struct tegra_soctherm |
---|
246 | | - * @v: the value to write |
---|
| 363 | + * @value: the value to write |
---|
247 | 364 | * @reg: the register offset |
---|
248 | 365 | * |
---|
249 | 366 | * Writes @v to @reg. No return value. |
---|
.. | .. |
---|
318 | 435 | |
---|
319 | 436 | /** |
---|
320 | 437 | * enforce_temp_range() - check and enforce temperature range [min, max] |
---|
| 438 | + * @dev: struct device * of the SOC_THERM instance |
---|
321 | 439 | * @trip_temp: the trip temperature to check |
---|
322 | 440 | * |
---|
323 | 441 | * Checks and enforces the permitted temperature range that SOC_THERM |
---|
.. | .. |
---|
446 | 564 | return NULL; |
---|
447 | 565 | } |
---|
448 | 566 | |
---|
| 567 | +static int tsensor_group_thermtrip_get(struct tegra_soctherm *ts, int id) |
---|
| 568 | +{ |
---|
| 569 | + int i, temp = min_low_temp; |
---|
| 570 | + struct tsensor_group_thermtrips *tt = ts->soc->thermtrips; |
---|
| 571 | + |
---|
| 572 | + if (id >= TEGRA124_SOCTHERM_SENSOR_NUM) |
---|
| 573 | + return temp; |
---|
| 574 | + |
---|
| 575 | + if (tt) { |
---|
| 576 | + for (i = 0; i < ts->soc->num_ttgs; i++) { |
---|
| 577 | + if (tt[i].id == id) |
---|
| 578 | + return tt[i].temp; |
---|
| 579 | + } |
---|
| 580 | + } |
---|
| 581 | + |
---|
| 582 | + return temp; |
---|
| 583 | +} |
---|
| 584 | + |
---|
449 | 585 | static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp) |
---|
450 | 586 | { |
---|
451 | 587 | struct tegra_thermctl_zone *zone = data; |
---|
.. | .. |
---|
464 | 600 | return ret; |
---|
465 | 601 | |
---|
466 | 602 | if (type == THERMAL_TRIP_CRITICAL) { |
---|
467 | | - return thermtrip_program(dev, sg, temp); |
---|
| 603 | + /* |
---|
| 604 | + * If thermtrips property is set in DT, |
---|
| 605 | + * doesn't need to program critical type trip to HW, |
---|
| 606 | + * if not, program critical trip to HW. |
---|
| 607 | + */ |
---|
| 608 | + if (min_low_temp == tsensor_group_thermtrip_get(ts, sg->id)) |
---|
| 609 | + return thermtrip_program(dev, sg, temp); |
---|
| 610 | + else |
---|
| 611 | + return 0; |
---|
| 612 | + |
---|
468 | 613 | } else if (type == THERMAL_TRIP_HOT) { |
---|
469 | 614 | int i; |
---|
470 | 615 | |
---|
.. | .. |
---|
488 | 633 | return 0; |
---|
489 | 634 | } |
---|
490 | 635 | |
---|
| 636 | +static int tegra_thermctl_get_trend(void *data, int trip, |
---|
| 637 | + enum thermal_trend *trend) |
---|
| 638 | +{ |
---|
| 639 | + struct tegra_thermctl_zone *zone = data; |
---|
| 640 | + struct thermal_zone_device *tz = zone->tz; |
---|
| 641 | + int trip_temp, temp, last_temp, ret; |
---|
| 642 | + |
---|
| 643 | + if (!tz) |
---|
| 644 | + return -EINVAL; |
---|
| 645 | + |
---|
| 646 | + ret = tz->ops->get_trip_temp(zone->tz, trip, &trip_temp); |
---|
| 647 | + if (ret) |
---|
| 648 | + return ret; |
---|
| 649 | + |
---|
| 650 | + temp = READ_ONCE(tz->temperature); |
---|
| 651 | + last_temp = READ_ONCE(tz->last_temperature); |
---|
| 652 | + |
---|
| 653 | + if (temp > trip_temp) { |
---|
| 654 | + if (temp >= last_temp) |
---|
| 655 | + *trend = THERMAL_TREND_RAISING; |
---|
| 656 | + else |
---|
| 657 | + *trend = THERMAL_TREND_STABLE; |
---|
| 658 | + } else if (temp < trip_temp) { |
---|
| 659 | + *trend = THERMAL_TREND_DROPPING; |
---|
| 660 | + } else { |
---|
| 661 | + *trend = THERMAL_TREND_STABLE; |
---|
| 662 | + } |
---|
| 663 | + |
---|
| 664 | + return 0; |
---|
| 665 | +} |
---|
| 666 | + |
---|
| 667 | +static void thermal_irq_enable(struct tegra_thermctl_zone *zn) |
---|
| 668 | +{ |
---|
| 669 | + u32 r; |
---|
| 670 | + |
---|
| 671 | + /* multiple zones could be handling and setting trips at once */ |
---|
| 672 | + mutex_lock(&zn->ts->thermctl_lock); |
---|
| 673 | + r = readl(zn->ts->regs + THERMCTL_INTR_ENABLE); |
---|
| 674 | + r = REG_SET_MASK(r, zn->sg->thermctl_isr_mask, TH_INTR_UP_DN_EN); |
---|
| 675 | + writel(r, zn->ts->regs + THERMCTL_INTR_ENABLE); |
---|
| 676 | + mutex_unlock(&zn->ts->thermctl_lock); |
---|
| 677 | +} |
---|
| 678 | + |
---|
| 679 | +static void thermal_irq_disable(struct tegra_thermctl_zone *zn) |
---|
| 680 | +{ |
---|
| 681 | + u32 r; |
---|
| 682 | + |
---|
| 683 | + /* multiple zones could be handling and setting trips at once */ |
---|
| 684 | + mutex_lock(&zn->ts->thermctl_lock); |
---|
| 685 | + r = readl(zn->ts->regs + THERMCTL_INTR_DISABLE); |
---|
| 686 | + r = REG_SET_MASK(r, zn->sg->thermctl_isr_mask, 0); |
---|
| 687 | + writel(r, zn->ts->regs + THERMCTL_INTR_DISABLE); |
---|
| 688 | + mutex_unlock(&zn->ts->thermctl_lock); |
---|
| 689 | +} |
---|
| 690 | + |
---|
| 691 | +static int tegra_thermctl_set_trips(void *data, int lo, int hi) |
---|
| 692 | +{ |
---|
| 693 | + struct tegra_thermctl_zone *zone = data; |
---|
| 694 | + u32 r; |
---|
| 695 | + |
---|
| 696 | + thermal_irq_disable(zone); |
---|
| 697 | + |
---|
| 698 | + r = readl(zone->ts->regs + zone->sg->thermctl_lvl0_offset); |
---|
| 699 | + r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 0); |
---|
| 700 | + writel(r, zone->ts->regs + zone->sg->thermctl_lvl0_offset); |
---|
| 701 | + |
---|
| 702 | + lo = enforce_temp_range(zone->dev, lo) / zone->ts->soc->thresh_grain; |
---|
| 703 | + hi = enforce_temp_range(zone->dev, hi) / zone->ts->soc->thresh_grain; |
---|
| 704 | + dev_dbg(zone->dev, "%s hi:%d, lo:%d\n", __func__, hi, lo); |
---|
| 705 | + |
---|
| 706 | + r = REG_SET_MASK(r, zone->sg->thermctl_lvl0_up_thresh_mask, hi); |
---|
| 707 | + r = REG_SET_MASK(r, zone->sg->thermctl_lvl0_dn_thresh_mask, lo); |
---|
| 708 | + r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 1); |
---|
| 709 | + writel(r, zone->ts->regs + zone->sg->thermctl_lvl0_offset); |
---|
| 710 | + |
---|
| 711 | + thermal_irq_enable(zone); |
---|
| 712 | + |
---|
| 713 | + return 0; |
---|
| 714 | +} |
---|
| 715 | + |
---|
491 | 716 | static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = { |
---|
492 | 717 | .get_temp = tegra_thermctl_get_temp, |
---|
493 | 718 | .set_trip_temp = tegra_thermctl_set_trip_temp, |
---|
| 719 | + .get_trend = tegra_thermctl_get_trend, |
---|
| 720 | + .set_trips = tegra_thermctl_set_trips, |
---|
494 | 721 | }; |
---|
495 | 722 | |
---|
496 | 723 | static int get_hot_temp(struct thermal_zone_device *tz, int *trip, int *temp) |
---|
.. | .. |
---|
521 | 748 | /** |
---|
522 | 749 | * tegra_soctherm_set_hwtrips() - set HW trip point from DT data |
---|
523 | 750 | * @dev: struct device * of the SOC_THERM instance |
---|
| 751 | + * @sg: pointer to the sensor group to set the thermtrip temperature for |
---|
| 752 | + * @tz: struct thermal_zone_device * |
---|
524 | 753 | * |
---|
525 | 754 | * Configure the SOC_THERM HW trip points, setting "THERMTRIP" |
---|
526 | | - * "THROTTLE" trip points , using "critical" or "hot" type trip_temp |
---|
| 755 | + * "THROTTLE" trip points , using "thermtrips", "critical" or "hot" |
---|
| 756 | + * type trip_temp |
---|
527 | 757 | * from thermal zone. |
---|
528 | 758 | * After they have been configured, THERMTRIP or THROTTLE will take |
---|
529 | 759 | * action when the configured SoC thermal sensor group reaches a |
---|
.. | .. |
---|
545 | 775 | { |
---|
546 | 776 | struct tegra_soctherm *ts = dev_get_drvdata(dev); |
---|
547 | 777 | struct soctherm_throt_cfg *stc; |
---|
548 | | - int i, trip, temperature; |
---|
549 | | - int ret; |
---|
| 778 | + int i, trip, temperature, ret; |
---|
550 | 779 | |
---|
551 | | - ret = tz->ops->get_crit_temp(tz, &temperature); |
---|
552 | | - if (ret) { |
---|
553 | | - dev_warn(dev, "thermtrip: %s: missing critical temperature\n", |
---|
554 | | - sg->name); |
---|
555 | | - goto set_throttle; |
---|
556 | | - } |
---|
| 780 | + /* Get thermtrips. If missing, try to get critical trips. */ |
---|
| 781 | + temperature = tsensor_group_thermtrip_get(ts, sg->id); |
---|
| 782 | + if (min_low_temp == temperature) |
---|
| 783 | + if (tz->ops->get_crit_temp(tz, &temperature)) |
---|
| 784 | + temperature = max_high_temp; |
---|
557 | 785 | |
---|
558 | 786 | ret = thermtrip_program(dev, sg, temperature); |
---|
559 | 787 | if (ret) { |
---|
560 | | - dev_err(dev, "thermtrip: %s: error during enable\n", |
---|
561 | | - sg->name); |
---|
| 788 | + dev_err(dev, "thermtrip: %s: error during enable\n", sg->name); |
---|
562 | 789 | return ret; |
---|
563 | 790 | } |
---|
564 | 791 | |
---|
565 | | - dev_info(dev, |
---|
566 | | - "thermtrip: will shut down when %s reaches %d mC\n", |
---|
| 792 | + dev_info(dev, "thermtrip: will shut down when %s reaches %d mC\n", |
---|
567 | 793 | sg->name, temperature); |
---|
568 | 794 | |
---|
569 | | -set_throttle: |
---|
570 | 795 | ret = get_hot_temp(tz, &trip, &temperature); |
---|
571 | 796 | if (ret) { |
---|
572 | | - dev_warn(dev, "throttrip: %s: missing hot temperature\n", |
---|
| 797 | + dev_info(dev, "throttrip: %s: missing hot temperature\n", |
---|
573 | 798 | sg->name); |
---|
574 | 799 | return 0; |
---|
575 | 800 | } |
---|
576 | 801 | |
---|
577 | | - for (i = 0; i < THROTTLE_SIZE; i++) { |
---|
| 802 | + for (i = 0; i < THROTTLE_OC1; i++) { |
---|
578 | 803 | struct thermal_cooling_device *cdev; |
---|
579 | 804 | |
---|
580 | 805 | if (!ts->throt_cfgs[i].init) |
---|
.. | .. |
---|
600 | 825 | } |
---|
601 | 826 | |
---|
602 | 827 | if (i == THROTTLE_SIZE) |
---|
603 | | - dev_warn(dev, "throttrip: %s: missing throttle cdev\n", |
---|
| 828 | + dev_info(dev, "throttrip: %s: missing throttle cdev\n", |
---|
604 | 829 | sg->name); |
---|
605 | 830 | |
---|
| 831 | + return 0; |
---|
| 832 | +} |
---|
| 833 | + |
---|
| 834 | +static irqreturn_t soctherm_thermal_isr(int irq, void *dev_id) |
---|
| 835 | +{ |
---|
| 836 | + struct tegra_soctherm *ts = dev_id; |
---|
| 837 | + u32 r; |
---|
| 838 | + |
---|
| 839 | + /* Case for no lock: |
---|
| 840 | + * Although interrupts are enabled in set_trips, there is still no need |
---|
| 841 | + * to lock here because the interrupts are disabled before programming |
---|
| 842 | + * new trip points. Hence there cant be a interrupt on the same sensor. |
---|
| 843 | + * An interrupt can however occur on a sensor while trips are being |
---|
| 844 | + * programmed on a different one. This beign a LEVEL interrupt won't |
---|
| 845 | + * cause a new interrupt but this is taken care of by the re-reading of |
---|
| 846 | + * the STATUS register in the thread function. |
---|
| 847 | + */ |
---|
| 848 | + r = readl(ts->regs + THERMCTL_INTR_STATUS); |
---|
| 849 | + writel(r, ts->regs + THERMCTL_INTR_DISABLE); |
---|
| 850 | + |
---|
| 851 | + return IRQ_WAKE_THREAD; |
---|
| 852 | +} |
---|
| 853 | + |
---|
| 854 | +/** |
---|
| 855 | + * soctherm_thermal_isr_thread() - Handles a thermal interrupt request |
---|
| 856 | + * @irq: The interrupt number being requested; not used |
---|
| 857 | + * @dev_id: Opaque pointer to tegra_soctherm; |
---|
| 858 | + * |
---|
| 859 | + * Clears the interrupt status register if there are expected |
---|
| 860 | + * interrupt bits set. |
---|
| 861 | + * The interrupt(s) are then handled by updating the corresponding |
---|
| 862 | + * thermal zones. |
---|
| 863 | + * |
---|
| 864 | + * An error is logged if any unexpected interrupt bits are set. |
---|
| 865 | + * |
---|
| 866 | + * Disabled interrupts are re-enabled. |
---|
| 867 | + * |
---|
| 868 | + * Return: %IRQ_HANDLED. Interrupt was handled and no further processing |
---|
| 869 | + * is needed. |
---|
| 870 | + */ |
---|
| 871 | +static irqreturn_t soctherm_thermal_isr_thread(int irq, void *dev_id) |
---|
| 872 | +{ |
---|
| 873 | + struct tegra_soctherm *ts = dev_id; |
---|
| 874 | + struct thermal_zone_device *tz; |
---|
| 875 | + u32 st, ex = 0, cp = 0, gp = 0, pl = 0, me = 0; |
---|
| 876 | + |
---|
| 877 | + st = readl(ts->regs + THERMCTL_INTR_STATUS); |
---|
| 878 | + |
---|
| 879 | + /* deliberately clear expected interrupts handled in SW */ |
---|
| 880 | + cp |= st & TH_INTR_CD0_MASK; |
---|
| 881 | + cp |= st & TH_INTR_CU0_MASK; |
---|
| 882 | + |
---|
| 883 | + gp |= st & TH_INTR_GD0_MASK; |
---|
| 884 | + gp |= st & TH_INTR_GU0_MASK; |
---|
| 885 | + |
---|
| 886 | + pl |= st & TH_INTR_PD0_MASK; |
---|
| 887 | + pl |= st & TH_INTR_PU0_MASK; |
---|
| 888 | + |
---|
| 889 | + me |= st & TH_INTR_MD0_MASK; |
---|
| 890 | + me |= st & TH_INTR_MU0_MASK; |
---|
| 891 | + |
---|
| 892 | + ex |= cp | gp | pl | me; |
---|
| 893 | + if (ex) { |
---|
| 894 | + writel(ex, ts->regs + THERMCTL_INTR_STATUS); |
---|
| 895 | + st &= ~ex; |
---|
| 896 | + |
---|
| 897 | + if (cp) { |
---|
| 898 | + tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_CPU]; |
---|
| 899 | + thermal_zone_device_update(tz, |
---|
| 900 | + THERMAL_EVENT_UNSPECIFIED); |
---|
| 901 | + } |
---|
| 902 | + |
---|
| 903 | + if (gp) { |
---|
| 904 | + tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_GPU]; |
---|
| 905 | + thermal_zone_device_update(tz, |
---|
| 906 | + THERMAL_EVENT_UNSPECIFIED); |
---|
| 907 | + } |
---|
| 908 | + |
---|
| 909 | + if (pl) { |
---|
| 910 | + tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_PLLX]; |
---|
| 911 | + thermal_zone_device_update(tz, |
---|
| 912 | + THERMAL_EVENT_UNSPECIFIED); |
---|
| 913 | + } |
---|
| 914 | + |
---|
| 915 | + if (me) { |
---|
| 916 | + tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_MEM]; |
---|
| 917 | + thermal_zone_device_update(tz, |
---|
| 918 | + THERMAL_EVENT_UNSPECIFIED); |
---|
| 919 | + } |
---|
| 920 | + } |
---|
| 921 | + |
---|
| 922 | + /* deliberately ignore expected interrupts NOT handled in SW */ |
---|
| 923 | + ex |= TH_INTR_IGNORE_MASK; |
---|
| 924 | + st &= ~ex; |
---|
| 925 | + |
---|
| 926 | + if (st) { |
---|
| 927 | + /* Whine about any other unexpected INTR bits still set */ |
---|
| 928 | + pr_err("soctherm: Ignored unexpected INTRs 0x%08x\n", st); |
---|
| 929 | + writel(st, ts->regs + THERMCTL_INTR_STATUS); |
---|
| 930 | + } |
---|
| 931 | + |
---|
| 932 | + return IRQ_HANDLED; |
---|
| 933 | +} |
---|
| 934 | + |
---|
| 935 | +/** |
---|
| 936 | + * soctherm_oc_intr_enable() - Enables the soctherm over-current interrupt |
---|
| 937 | + * @ts: pointer to a struct tegra_soctherm |
---|
| 938 | + * @alarm: The soctherm throttle id |
---|
| 939 | + * @enable: Flag indicating enable the soctherm over-current |
---|
| 940 | + * interrupt or disable it |
---|
| 941 | + * |
---|
| 942 | + * Enables a specific over-current pins @alarm to raise an interrupt if the flag |
---|
| 943 | + * is set and the alarm corresponds to OC1, OC2, OC3, or OC4. |
---|
| 944 | + */ |
---|
| 945 | +static void soctherm_oc_intr_enable(struct tegra_soctherm *ts, |
---|
| 946 | + enum soctherm_throttle_id alarm, |
---|
| 947 | + bool enable) |
---|
| 948 | +{ |
---|
| 949 | + u32 r; |
---|
| 950 | + |
---|
| 951 | + if (!enable) |
---|
| 952 | + return; |
---|
| 953 | + |
---|
| 954 | + r = readl(ts->regs + OC_INTR_ENABLE); |
---|
| 955 | + switch (alarm) { |
---|
| 956 | + case THROTTLE_OC1: |
---|
| 957 | + r = REG_SET_MASK(r, OC_INTR_OC1_MASK, 1); |
---|
| 958 | + break; |
---|
| 959 | + case THROTTLE_OC2: |
---|
| 960 | + r = REG_SET_MASK(r, OC_INTR_OC2_MASK, 1); |
---|
| 961 | + break; |
---|
| 962 | + case THROTTLE_OC3: |
---|
| 963 | + r = REG_SET_MASK(r, OC_INTR_OC3_MASK, 1); |
---|
| 964 | + break; |
---|
| 965 | + case THROTTLE_OC4: |
---|
| 966 | + r = REG_SET_MASK(r, OC_INTR_OC4_MASK, 1); |
---|
| 967 | + break; |
---|
| 968 | + default: |
---|
| 969 | + r = 0; |
---|
| 970 | + break; |
---|
| 971 | + } |
---|
| 972 | + writel(r, ts->regs + OC_INTR_ENABLE); |
---|
| 973 | +} |
---|
| 974 | + |
---|
| 975 | +/** |
---|
| 976 | + * soctherm_handle_alarm() - Handles soctherm alarms |
---|
| 977 | + * @alarm: The soctherm throttle id |
---|
| 978 | + * |
---|
| 979 | + * "Handles" over-current alarms (OC1, OC2, OC3, and OC4) by printing |
---|
| 980 | + * a warning or informative message. |
---|
| 981 | + * |
---|
| 982 | + * Return: -EINVAL for @alarm = THROTTLE_OC3, otherwise 0 (success). |
---|
| 983 | + */ |
---|
| 984 | +static int soctherm_handle_alarm(enum soctherm_throttle_id alarm) |
---|
| 985 | +{ |
---|
| 986 | + int rv = -EINVAL; |
---|
| 987 | + |
---|
| 988 | + switch (alarm) { |
---|
| 989 | + case THROTTLE_OC1: |
---|
| 990 | + pr_debug("soctherm: Successfully handled OC1 alarm\n"); |
---|
| 991 | + rv = 0; |
---|
| 992 | + break; |
---|
| 993 | + |
---|
| 994 | + case THROTTLE_OC2: |
---|
| 995 | + pr_debug("soctherm: Successfully handled OC2 alarm\n"); |
---|
| 996 | + rv = 0; |
---|
| 997 | + break; |
---|
| 998 | + |
---|
| 999 | + case THROTTLE_OC3: |
---|
| 1000 | + pr_debug("soctherm: Successfully handled OC3 alarm\n"); |
---|
| 1001 | + rv = 0; |
---|
| 1002 | + break; |
---|
| 1003 | + |
---|
| 1004 | + case THROTTLE_OC4: |
---|
| 1005 | + pr_debug("soctherm: Successfully handled OC4 alarm\n"); |
---|
| 1006 | + rv = 0; |
---|
| 1007 | + break; |
---|
| 1008 | + |
---|
| 1009 | + default: |
---|
| 1010 | + break; |
---|
| 1011 | + } |
---|
| 1012 | + |
---|
| 1013 | + if (rv) |
---|
| 1014 | + pr_err("soctherm: ERROR in handling %s alarm\n", |
---|
| 1015 | + throt_names[alarm]); |
---|
| 1016 | + |
---|
| 1017 | + return rv; |
---|
| 1018 | +} |
---|
| 1019 | + |
---|
| 1020 | +/** |
---|
| 1021 | + * soctherm_edp_isr_thread() - log an over-current interrupt request |
---|
| 1022 | + * @irq: OC irq number. Currently not being used. See description |
---|
| 1023 | + * @arg: a void pointer for callback, currently not being used |
---|
| 1024 | + * |
---|
| 1025 | + * Over-current events are handled in hardware. This function is called to log |
---|
| 1026 | + * and handle any OC events that happened. Additionally, it checks every |
---|
| 1027 | + * over-current interrupt registers for registers are set but |
---|
| 1028 | + * was not expected (i.e. any discrepancy in interrupt status) by the function, |
---|
| 1029 | + * the discrepancy will logged. |
---|
| 1030 | + * |
---|
| 1031 | + * Return: %IRQ_HANDLED |
---|
| 1032 | + */ |
---|
| 1033 | +static irqreturn_t soctherm_edp_isr_thread(int irq, void *arg) |
---|
| 1034 | +{ |
---|
| 1035 | + struct tegra_soctherm *ts = arg; |
---|
| 1036 | + u32 st, ex, oc1, oc2, oc3, oc4; |
---|
| 1037 | + |
---|
| 1038 | + st = readl(ts->regs + OC_INTR_STATUS); |
---|
| 1039 | + |
---|
| 1040 | + /* deliberately clear expected interrupts handled in SW */ |
---|
| 1041 | + oc1 = st & OC_INTR_OC1_MASK; |
---|
| 1042 | + oc2 = st & OC_INTR_OC2_MASK; |
---|
| 1043 | + oc3 = st & OC_INTR_OC3_MASK; |
---|
| 1044 | + oc4 = st & OC_INTR_OC4_MASK; |
---|
| 1045 | + ex = oc1 | oc2 | oc3 | oc4; |
---|
| 1046 | + |
---|
| 1047 | + pr_err("soctherm: OC ALARM 0x%08x\n", ex); |
---|
| 1048 | + if (ex) { |
---|
| 1049 | + writel(st, ts->regs + OC_INTR_STATUS); |
---|
| 1050 | + st &= ~ex; |
---|
| 1051 | + |
---|
| 1052 | + if (oc1 && !soctherm_handle_alarm(THROTTLE_OC1)) |
---|
| 1053 | + soctherm_oc_intr_enable(ts, THROTTLE_OC1, true); |
---|
| 1054 | + |
---|
| 1055 | + if (oc2 && !soctherm_handle_alarm(THROTTLE_OC2)) |
---|
| 1056 | + soctherm_oc_intr_enable(ts, THROTTLE_OC2, true); |
---|
| 1057 | + |
---|
| 1058 | + if (oc3 && !soctherm_handle_alarm(THROTTLE_OC3)) |
---|
| 1059 | + soctherm_oc_intr_enable(ts, THROTTLE_OC3, true); |
---|
| 1060 | + |
---|
| 1061 | + if (oc4 && !soctherm_handle_alarm(THROTTLE_OC4)) |
---|
| 1062 | + soctherm_oc_intr_enable(ts, THROTTLE_OC4, true); |
---|
| 1063 | + |
---|
| 1064 | + if (oc1 && soc_irq_cdata.irq_enable & BIT(0)) |
---|
| 1065 | + handle_nested_irq( |
---|
| 1066 | + irq_find_mapping(soc_irq_cdata.domain, 0)); |
---|
| 1067 | + |
---|
| 1068 | + if (oc2 && soc_irq_cdata.irq_enable & BIT(1)) |
---|
| 1069 | + handle_nested_irq( |
---|
| 1070 | + irq_find_mapping(soc_irq_cdata.domain, 1)); |
---|
| 1071 | + |
---|
| 1072 | + if (oc3 && soc_irq_cdata.irq_enable & BIT(2)) |
---|
| 1073 | + handle_nested_irq( |
---|
| 1074 | + irq_find_mapping(soc_irq_cdata.domain, 2)); |
---|
| 1075 | + |
---|
| 1076 | + if (oc4 && soc_irq_cdata.irq_enable & BIT(3)) |
---|
| 1077 | + handle_nested_irq( |
---|
| 1078 | + irq_find_mapping(soc_irq_cdata.domain, 3)); |
---|
| 1079 | + } |
---|
| 1080 | + |
---|
| 1081 | + if (st) { |
---|
| 1082 | + pr_err("soctherm: Ignored unexpected OC ALARM 0x%08x\n", st); |
---|
| 1083 | + writel(st, ts->regs + OC_INTR_STATUS); |
---|
| 1084 | + } |
---|
| 1085 | + |
---|
| 1086 | + return IRQ_HANDLED; |
---|
| 1087 | +} |
---|
| 1088 | + |
---|
| 1089 | +/** |
---|
| 1090 | + * soctherm_edp_isr() - Disables any active interrupts |
---|
| 1091 | + * @irq: The interrupt request number |
---|
| 1092 | + * @arg: Opaque pointer to an argument |
---|
| 1093 | + * |
---|
| 1094 | + * Writes to the OC_INTR_DISABLE register the over current interrupt status, |
---|
| 1095 | + * masking any asserted interrupts. Doing this prevents the same interrupts |
---|
| 1096 | + * from triggering this isr repeatedly. The thread woken by this isr will |
---|
| 1097 | + * handle asserted interrupts and subsequently unmask/re-enable them. |
---|
| 1098 | + * |
---|
| 1099 | + * The OC_INTR_DISABLE register indicates which OC interrupts |
---|
| 1100 | + * have been disabled. |
---|
| 1101 | + * |
---|
| 1102 | + * Return: %IRQ_WAKE_THREAD, handler requests to wake the handler thread |
---|
| 1103 | + */ |
---|
| 1104 | +static irqreturn_t soctherm_edp_isr(int irq, void *arg) |
---|
| 1105 | +{ |
---|
| 1106 | + struct tegra_soctherm *ts = arg; |
---|
| 1107 | + u32 r; |
---|
| 1108 | + |
---|
| 1109 | + if (!ts) |
---|
| 1110 | + return IRQ_NONE; |
---|
| 1111 | + |
---|
| 1112 | + r = readl(ts->regs + OC_INTR_STATUS); |
---|
| 1113 | + writel(r, ts->regs + OC_INTR_DISABLE); |
---|
| 1114 | + |
---|
| 1115 | + return IRQ_WAKE_THREAD; |
---|
| 1116 | +} |
---|
| 1117 | + |
---|
| 1118 | +/** |
---|
| 1119 | + * soctherm_oc_irq_lock() - locks the over-current interrupt request |
---|
| 1120 | + * @data: Interrupt request data |
---|
| 1121 | + * |
---|
| 1122 | + * Looks up the chip data from @data and locks the mutex associated with |
---|
| 1123 | + * a particular over-current interrupt request. |
---|
| 1124 | + */ |
---|
| 1125 | +static void soctherm_oc_irq_lock(struct irq_data *data) |
---|
| 1126 | +{ |
---|
| 1127 | + struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); |
---|
| 1128 | + |
---|
| 1129 | + mutex_lock(&d->irq_lock); |
---|
| 1130 | +} |
---|
| 1131 | + |
---|
| 1132 | +/** |
---|
| 1133 | + * soctherm_oc_irq_sync_unlock() - Unlocks the OC interrupt request |
---|
| 1134 | + * @data: Interrupt request data |
---|
| 1135 | + * |
---|
| 1136 | + * Looks up the interrupt request data @data and unlocks the mutex associated |
---|
| 1137 | + * with a particular over-current interrupt request. |
---|
| 1138 | + */ |
---|
| 1139 | +static void soctherm_oc_irq_sync_unlock(struct irq_data *data) |
---|
| 1140 | +{ |
---|
| 1141 | + struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); |
---|
| 1142 | + |
---|
| 1143 | + mutex_unlock(&d->irq_lock); |
---|
| 1144 | +} |
---|
| 1145 | + |
---|
| 1146 | +/** |
---|
| 1147 | + * soctherm_oc_irq_enable() - Enables the SOC_THERM over-current interrupt queue |
---|
| 1148 | + * @data: irq_data structure of the chip |
---|
| 1149 | + * |
---|
| 1150 | + * Sets the irq_enable bit of SOC_THERM allowing SOC_THERM |
---|
| 1151 | + * to respond to over-current interrupts. |
---|
| 1152 | + * |
---|
| 1153 | + */ |
---|
| 1154 | +static void soctherm_oc_irq_enable(struct irq_data *data) |
---|
| 1155 | +{ |
---|
| 1156 | + struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); |
---|
| 1157 | + |
---|
| 1158 | + d->irq_enable |= BIT(data->hwirq); |
---|
| 1159 | +} |
---|
| 1160 | + |
---|
| 1161 | +/** |
---|
| 1162 | + * soctherm_oc_irq_disable() - Disables overcurrent interrupt requests |
---|
| 1163 | + * @data: The interrupt request information |
---|
| 1164 | + * |
---|
| 1165 | + * Clears the interrupt request enable bit of the overcurrent |
---|
| 1166 | + * interrupt request chip data. |
---|
| 1167 | + * |
---|
| 1168 | + * Return: Nothing is returned (void) |
---|
| 1169 | + */ |
---|
| 1170 | +static void soctherm_oc_irq_disable(struct irq_data *data) |
---|
| 1171 | +{ |
---|
| 1172 | + struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); |
---|
| 1173 | + |
---|
| 1174 | + d->irq_enable &= ~BIT(data->hwirq); |
---|
| 1175 | +} |
---|
| 1176 | + |
---|
| 1177 | +static int soctherm_oc_irq_set_type(struct irq_data *data, unsigned int type) |
---|
| 1178 | +{ |
---|
| 1179 | + return 0; |
---|
| 1180 | +} |
---|
| 1181 | + |
---|
| 1182 | +/** |
---|
| 1183 | + * soctherm_oc_irq_map() - SOC_THERM interrupt request domain mapper |
---|
| 1184 | + * @h: Interrupt request domain |
---|
| 1185 | + * @virq: Virtual interrupt request number |
---|
| 1186 | + * @hw: Hardware interrupt request number |
---|
| 1187 | + * |
---|
| 1188 | + * Mapping callback function for SOC_THERM's irq_domain. When a SOC_THERM |
---|
| 1189 | + * interrupt request is called, the irq_domain takes the request's virtual |
---|
| 1190 | + * request number (much like a virtual memory address) and maps it to a |
---|
| 1191 | + * physical hardware request number. |
---|
| 1192 | + * |
---|
| 1193 | + * When a mapping doesn't already exist for a virtual request number, the |
---|
| 1194 | + * irq_domain calls this function to associate the virtual request number with |
---|
| 1195 | + * a hardware request number. |
---|
| 1196 | + * |
---|
| 1197 | + * Return: 0 |
---|
| 1198 | + */ |
---|
| 1199 | +static int soctherm_oc_irq_map(struct irq_domain *h, unsigned int virq, |
---|
| 1200 | + irq_hw_number_t hw) |
---|
| 1201 | +{ |
---|
| 1202 | + struct soctherm_oc_irq_chip_data *data = h->host_data; |
---|
| 1203 | + |
---|
| 1204 | + irq_set_chip_data(virq, data); |
---|
| 1205 | + irq_set_chip(virq, &data->irq_chip); |
---|
| 1206 | + irq_set_nested_thread(virq, 1); |
---|
| 1207 | + return 0; |
---|
| 1208 | +} |
---|
| 1209 | + |
---|
| 1210 | +/** |
---|
| 1211 | + * soctherm_irq_domain_xlate_twocell() - xlate for soctherm interrupts |
---|
| 1212 | + * @d: Interrupt request domain |
---|
| 1213 | + * @ctrlr: Controller device tree node |
---|
| 1214 | + * @intspec: Array of u32s from DTs "interrupt" property |
---|
| 1215 | + * @intsize: Number of values inside the intspec array |
---|
| 1216 | + * @out_hwirq: HW IRQ value associated with this interrupt |
---|
| 1217 | + * @out_type: The IRQ SENSE type for this interrupt. |
---|
| 1218 | + * |
---|
| 1219 | + * This Device Tree IRQ specifier translation function will translate a |
---|
| 1220 | + * specific "interrupt" as defined by 2 DT values where the cell values map |
---|
| 1221 | + * the hwirq number + 1 and linux irq flags. Since the output is the hwirq |
---|
| 1222 | + * number, this function will subtract 1 from the value listed in DT. |
---|
| 1223 | + * |
---|
| 1224 | + * Return: 0 |
---|
| 1225 | + */ |
---|
| 1226 | +static int soctherm_irq_domain_xlate_twocell(struct irq_domain *d, |
---|
| 1227 | + struct device_node *ctrlr, const u32 *intspec, unsigned int intsize, |
---|
| 1228 | + irq_hw_number_t *out_hwirq, unsigned int *out_type) |
---|
| 1229 | +{ |
---|
| 1230 | + if (WARN_ON(intsize < 2)) |
---|
| 1231 | + return -EINVAL; |
---|
| 1232 | + |
---|
| 1233 | + /* |
---|
| 1234 | + * The HW value is 1 index less than the DT IRQ values. |
---|
| 1235 | + * i.e. OC4 goes to HW index 3. |
---|
| 1236 | + */ |
---|
| 1237 | + *out_hwirq = intspec[0] - 1; |
---|
| 1238 | + *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; |
---|
| 1239 | + return 0; |
---|
| 1240 | +} |
---|
| 1241 | + |
---|
| 1242 | +static const struct irq_domain_ops soctherm_oc_domain_ops = { |
---|
| 1243 | + .map = soctherm_oc_irq_map, |
---|
| 1244 | + .xlate = soctherm_irq_domain_xlate_twocell, |
---|
| 1245 | +}; |
---|
| 1246 | + |
---|
| 1247 | +/** |
---|
| 1248 | + * soctherm_oc_int_init() - Initial enabling of the over |
---|
| 1249 | + * current interrupts |
---|
| 1250 | + * @np: The devicetree node for soctherm |
---|
| 1251 | + * @num_irqs: The number of new interrupt requests |
---|
| 1252 | + * |
---|
| 1253 | + * Sets the over current interrupt request chip data |
---|
| 1254 | + * |
---|
| 1255 | + * Return: 0 on success or if overcurrent interrupts are not enabled, |
---|
| 1256 | + * -ENOMEM (out of memory), or irq_base if the function failed to |
---|
| 1257 | + * allocate the irqs |
---|
| 1258 | + */ |
---|
| 1259 | +static int soctherm_oc_int_init(struct device_node *np, int num_irqs) |
---|
| 1260 | +{ |
---|
| 1261 | + if (!num_irqs) { |
---|
| 1262 | + pr_info("%s(): OC interrupts are not enabled\n", __func__); |
---|
| 1263 | + return 0; |
---|
| 1264 | + } |
---|
| 1265 | + |
---|
| 1266 | + mutex_init(&soc_irq_cdata.irq_lock); |
---|
| 1267 | + soc_irq_cdata.irq_enable = 0; |
---|
| 1268 | + |
---|
| 1269 | + soc_irq_cdata.irq_chip.name = "soc_therm_oc"; |
---|
| 1270 | + soc_irq_cdata.irq_chip.irq_bus_lock = soctherm_oc_irq_lock; |
---|
| 1271 | + soc_irq_cdata.irq_chip.irq_bus_sync_unlock = |
---|
| 1272 | + soctherm_oc_irq_sync_unlock; |
---|
| 1273 | + soc_irq_cdata.irq_chip.irq_disable = soctherm_oc_irq_disable; |
---|
| 1274 | + soc_irq_cdata.irq_chip.irq_enable = soctherm_oc_irq_enable; |
---|
| 1275 | + soc_irq_cdata.irq_chip.irq_set_type = soctherm_oc_irq_set_type; |
---|
| 1276 | + soc_irq_cdata.irq_chip.irq_set_wake = NULL; |
---|
| 1277 | + |
---|
| 1278 | + soc_irq_cdata.domain = irq_domain_add_linear(np, num_irqs, |
---|
| 1279 | + &soctherm_oc_domain_ops, |
---|
| 1280 | + &soc_irq_cdata); |
---|
| 1281 | + |
---|
| 1282 | + if (!soc_irq_cdata.domain) { |
---|
| 1283 | + pr_err("%s: Failed to create IRQ domain\n", __func__); |
---|
| 1284 | + return -ENOMEM; |
---|
| 1285 | + } |
---|
| 1286 | + |
---|
| 1287 | + pr_debug("%s(): OC interrupts enabled successful\n", __func__); |
---|
606 | 1288 | return 0; |
---|
607 | 1289 | } |
---|
608 | 1290 | |
---|
.. | .. |
---|
803 | 1485 | return 0; |
---|
804 | 1486 | } |
---|
805 | 1487 | |
---|
806 | | -static int regs_open(struct inode *inode, struct file *file) |
---|
807 | | -{ |
---|
808 | | - return single_open(file, regs_show, inode->i_private); |
---|
809 | | -} |
---|
810 | | - |
---|
811 | | -static const struct file_operations regs_fops = { |
---|
812 | | - .open = regs_open, |
---|
813 | | - .read = seq_read, |
---|
814 | | - .llseek = seq_lseek, |
---|
815 | | - .release = single_release, |
---|
816 | | -}; |
---|
| 1488 | +DEFINE_SHOW_ATTRIBUTE(regs); |
---|
817 | 1489 | |
---|
818 | 1490 | static void soctherm_debug_init(struct platform_device *pdev) |
---|
819 | 1491 | { |
---|
820 | 1492 | struct tegra_soctherm *tegra = platform_get_drvdata(pdev); |
---|
821 | | - struct dentry *root, *file; |
---|
| 1493 | + struct dentry *root; |
---|
822 | 1494 | |
---|
823 | 1495 | root = debugfs_create_dir("soctherm", NULL); |
---|
824 | | - if (!root) { |
---|
825 | | - dev_err(&pdev->dev, "failed to create debugfs directory\n"); |
---|
826 | | - return; |
---|
827 | | - } |
---|
828 | 1496 | |
---|
829 | 1497 | tegra->debugfs_dir = root; |
---|
830 | 1498 | |
---|
831 | | - file = debugfs_create_file("reg_contents", 0644, root, |
---|
832 | | - pdev, ®s_fops); |
---|
833 | | - if (!file) { |
---|
834 | | - dev_err(&pdev->dev, "failed to create debugfs file\n"); |
---|
835 | | - debugfs_remove_recursive(tegra->debugfs_dir); |
---|
836 | | - tegra->debugfs_dir = NULL; |
---|
837 | | - } |
---|
| 1499 | + debugfs_create_file("reg_contents", 0644, root, pdev, ®s_fops); |
---|
838 | 1500 | } |
---|
839 | 1501 | #else |
---|
840 | 1502 | static inline void soctherm_debug_init(struct platform_device *pdev) {} |
---|
.. | .. |
---|
907 | 1569 | .set_cur_state = throt_set_cdev_state, |
---|
908 | 1570 | }; |
---|
909 | 1571 | |
---|
| 1572 | +static int soctherm_thermtrips_parse(struct platform_device *pdev) |
---|
| 1573 | +{ |
---|
| 1574 | + struct device *dev = &pdev->dev; |
---|
| 1575 | + struct tegra_soctherm *ts = dev_get_drvdata(dev); |
---|
| 1576 | + struct tsensor_group_thermtrips *tt = ts->soc->thermtrips; |
---|
| 1577 | + const int max_num_prop = ts->soc->num_ttgs * 2; |
---|
| 1578 | + u32 *tlb; |
---|
| 1579 | + int i, j, n, ret; |
---|
| 1580 | + |
---|
| 1581 | + if (!tt) |
---|
| 1582 | + return -ENOMEM; |
---|
| 1583 | + |
---|
| 1584 | + n = of_property_count_u32_elems(dev->of_node, "nvidia,thermtrips"); |
---|
| 1585 | + if (n <= 0) { |
---|
| 1586 | + dev_info(dev, |
---|
| 1587 | + "missing thermtrips, will use critical trips as shut down temp\n"); |
---|
| 1588 | + return n; |
---|
| 1589 | + } |
---|
| 1590 | + |
---|
| 1591 | + n = min(max_num_prop, n); |
---|
| 1592 | + |
---|
| 1593 | + tlb = devm_kcalloc(&pdev->dev, max_num_prop, sizeof(u32), GFP_KERNEL); |
---|
| 1594 | + if (!tlb) |
---|
| 1595 | + return -ENOMEM; |
---|
| 1596 | + ret = of_property_read_u32_array(dev->of_node, "nvidia,thermtrips", |
---|
| 1597 | + tlb, n); |
---|
| 1598 | + if (ret) { |
---|
| 1599 | + dev_err(dev, "invalid num ele: thermtrips:%d\n", ret); |
---|
| 1600 | + return ret; |
---|
| 1601 | + } |
---|
| 1602 | + |
---|
| 1603 | + i = 0; |
---|
| 1604 | + for (j = 0; j < n; j = j + 2) { |
---|
| 1605 | + if (tlb[j] >= TEGRA124_SOCTHERM_SENSOR_NUM) |
---|
| 1606 | + continue; |
---|
| 1607 | + |
---|
| 1608 | + tt[i].id = tlb[j]; |
---|
| 1609 | + tt[i].temp = tlb[j + 1]; |
---|
| 1610 | + i++; |
---|
| 1611 | + } |
---|
| 1612 | + |
---|
| 1613 | + return 0; |
---|
| 1614 | +} |
---|
| 1615 | + |
---|
| 1616 | +static void soctherm_oc_cfg_parse(struct device *dev, |
---|
| 1617 | + struct device_node *np_oc, |
---|
| 1618 | + struct soctherm_throt_cfg *stc) |
---|
| 1619 | +{ |
---|
| 1620 | + u32 val; |
---|
| 1621 | + |
---|
| 1622 | + if (of_property_read_bool(np_oc, "nvidia,polarity-active-low")) |
---|
| 1623 | + stc->oc_cfg.active_low = 1; |
---|
| 1624 | + else |
---|
| 1625 | + stc->oc_cfg.active_low = 0; |
---|
| 1626 | + |
---|
| 1627 | + if (!of_property_read_u32(np_oc, "nvidia,count-threshold", &val)) { |
---|
| 1628 | + stc->oc_cfg.intr_en = 1; |
---|
| 1629 | + stc->oc_cfg.alarm_cnt_thresh = val; |
---|
| 1630 | + } |
---|
| 1631 | + |
---|
| 1632 | + if (!of_property_read_u32(np_oc, "nvidia,throttle-period-us", &val)) |
---|
| 1633 | + stc->oc_cfg.throt_period = val; |
---|
| 1634 | + |
---|
| 1635 | + if (!of_property_read_u32(np_oc, "nvidia,alarm-filter", &val)) |
---|
| 1636 | + stc->oc_cfg.alarm_filter = val; |
---|
| 1637 | + |
---|
| 1638 | + /* BRIEF throttling by default, do not support STICKY */ |
---|
| 1639 | + stc->oc_cfg.mode = OC_THROTTLE_MODE_BRIEF; |
---|
| 1640 | +} |
---|
| 1641 | + |
---|
| 1642 | +static int soctherm_throt_cfg_parse(struct device *dev, |
---|
| 1643 | + struct device_node *np, |
---|
| 1644 | + struct soctherm_throt_cfg *stc) |
---|
| 1645 | +{ |
---|
| 1646 | + struct tegra_soctherm *ts = dev_get_drvdata(dev); |
---|
| 1647 | + int ret; |
---|
| 1648 | + u32 val; |
---|
| 1649 | + |
---|
| 1650 | + ret = of_property_read_u32(np, "nvidia,priority", &val); |
---|
| 1651 | + if (ret) { |
---|
| 1652 | + dev_err(dev, "throttle-cfg: %s: invalid priority\n", stc->name); |
---|
| 1653 | + return -EINVAL; |
---|
| 1654 | + } |
---|
| 1655 | + stc->priority = val; |
---|
| 1656 | + |
---|
| 1657 | + ret = of_property_read_u32(np, ts->soc->use_ccroc ? |
---|
| 1658 | + "nvidia,cpu-throt-level" : |
---|
| 1659 | + "nvidia,cpu-throt-percent", &val); |
---|
| 1660 | + if (!ret) { |
---|
| 1661 | + if (ts->soc->use_ccroc && |
---|
| 1662 | + val <= TEGRA_SOCTHERM_THROT_LEVEL_HIGH) |
---|
| 1663 | + stc->cpu_throt_level = val; |
---|
| 1664 | + else if (!ts->soc->use_ccroc && val <= 100) |
---|
| 1665 | + stc->cpu_throt_depth = val; |
---|
| 1666 | + else |
---|
| 1667 | + goto err; |
---|
| 1668 | + } else { |
---|
| 1669 | + goto err; |
---|
| 1670 | + } |
---|
| 1671 | + |
---|
| 1672 | + ret = of_property_read_u32(np, "nvidia,gpu-throt-level", &val); |
---|
| 1673 | + if (!ret && val <= TEGRA_SOCTHERM_THROT_LEVEL_HIGH) |
---|
| 1674 | + stc->gpu_throt_level = val; |
---|
| 1675 | + else |
---|
| 1676 | + goto err; |
---|
| 1677 | + |
---|
| 1678 | + return 0; |
---|
| 1679 | + |
---|
| 1680 | +err: |
---|
| 1681 | + dev_err(dev, "throttle-cfg: %s: no throt prop or invalid prop\n", |
---|
| 1682 | + stc->name); |
---|
| 1683 | + return -EINVAL; |
---|
| 1684 | +} |
---|
| 1685 | + |
---|
910 | 1686 | /** |
---|
911 | 1687 | * soctherm_init_hw_throt_cdev() - Parse the HW throttle configurations |
---|
912 | 1688 | * and register them as cooling devices. |
---|
| 1689 | + * @pdev: Pointer to platform_device struct |
---|
913 | 1690 | */ |
---|
914 | 1691 | static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) |
---|
915 | 1692 | { |
---|
.. | .. |
---|
917 | 1694 | struct tegra_soctherm *ts = dev_get_drvdata(dev); |
---|
918 | 1695 | struct device_node *np_stc, *np_stcc; |
---|
919 | 1696 | const char *name; |
---|
920 | | - u32 val; |
---|
921 | | - int i, r; |
---|
| 1697 | + int i; |
---|
922 | 1698 | |
---|
923 | 1699 | for (i = 0; i < THROTTLE_SIZE; i++) { |
---|
924 | 1700 | ts->throt_cfgs[i].name = throt_names[i]; |
---|
.. | .. |
---|
936 | 1712 | for_each_child_of_node(np_stc, np_stcc) { |
---|
937 | 1713 | struct soctherm_throt_cfg *stc; |
---|
938 | 1714 | struct thermal_cooling_device *tcd; |
---|
| 1715 | + int err; |
---|
939 | 1716 | |
---|
940 | 1717 | name = np_stcc->name; |
---|
941 | 1718 | stc = find_throttle_cfg_by_name(ts, name); |
---|
.. | .. |
---|
945 | 1722 | continue; |
---|
946 | 1723 | } |
---|
947 | 1724 | |
---|
948 | | - r = of_property_read_u32(np_stcc, "nvidia,priority", &val); |
---|
949 | | - if (r) { |
---|
950 | | - dev_info(dev, |
---|
951 | | - "throttle-cfg: %s: missing priority\n", name); |
---|
| 1725 | + if (stc->init) { |
---|
| 1726 | + dev_err(dev, "throttle-cfg: %s: redefined!\n", name); |
---|
| 1727 | + of_node_put(np_stcc); |
---|
| 1728 | + break; |
---|
| 1729 | + } |
---|
| 1730 | + |
---|
| 1731 | + err = soctherm_throt_cfg_parse(dev, np_stcc, stc); |
---|
| 1732 | + if (err) |
---|
952 | 1733 | continue; |
---|
953 | | - } |
---|
954 | | - stc->priority = val; |
---|
955 | 1734 | |
---|
956 | | - if (ts->soc->use_ccroc) { |
---|
957 | | - r = of_property_read_u32(np_stcc, |
---|
958 | | - "nvidia,cpu-throt-level", |
---|
959 | | - &val); |
---|
960 | | - if (r) { |
---|
961 | | - dev_info(dev, |
---|
962 | | - "throttle-cfg: %s: missing cpu-throt-level\n", |
---|
963 | | - name); |
---|
964 | | - continue; |
---|
965 | | - } |
---|
966 | | - stc->cpu_throt_level = val; |
---|
| 1735 | + if (stc->id >= THROTTLE_OC1) { |
---|
| 1736 | + soctherm_oc_cfg_parse(dev, np_stcc, stc); |
---|
| 1737 | + stc->init = true; |
---|
967 | 1738 | } else { |
---|
968 | | - r = of_property_read_u32(np_stcc, |
---|
969 | | - "nvidia,cpu-throt-percent", |
---|
970 | | - &val); |
---|
971 | | - if (r) { |
---|
972 | | - dev_info(dev, |
---|
973 | | - "throttle-cfg: %s: missing cpu-throt-percent\n", |
---|
974 | | - name); |
---|
975 | | - continue; |
---|
976 | | - } |
---|
977 | | - stc->cpu_throt_depth = val; |
---|
978 | | - } |
---|
979 | 1739 | |
---|
980 | | - tcd = thermal_of_cooling_device_register(np_stcc, |
---|
| 1740 | + tcd = thermal_of_cooling_device_register(np_stcc, |
---|
981 | 1741 | (char *)name, ts, |
---|
982 | 1742 | &throt_cooling_ops); |
---|
983 | | - of_node_put(np_stcc); |
---|
984 | | - if (IS_ERR_OR_NULL(tcd)) { |
---|
985 | | - dev_err(dev, |
---|
986 | | - "throttle-cfg: %s: failed to register cooling device\n", |
---|
987 | | - name); |
---|
988 | | - continue; |
---|
| 1743 | + if (IS_ERR_OR_NULL(tcd)) { |
---|
| 1744 | + dev_err(dev, |
---|
| 1745 | + "throttle-cfg: %s: failed to register cooling device\n", |
---|
| 1746 | + name); |
---|
| 1747 | + continue; |
---|
| 1748 | + } |
---|
| 1749 | + stc->cdev = tcd; |
---|
| 1750 | + stc->init = true; |
---|
989 | 1751 | } |
---|
990 | 1752 | |
---|
991 | | - stc->cdev = tcd; |
---|
992 | | - stc->init = true; |
---|
993 | 1753 | } |
---|
994 | 1754 | |
---|
995 | 1755 | of_node_put(np_stc); |
---|
.. | .. |
---|
997 | 1757 | |
---|
998 | 1758 | /** |
---|
999 | 1759 | * throttlectl_cpu_level_cfg() - programs CCROC NV_THERM level config |
---|
| 1760 | + * @ts: pointer to a struct tegra_soctherm |
---|
1000 | 1761 | * @level: describing the level LOW/MED/HIGH of throttling |
---|
1001 | 1762 | * |
---|
1002 | 1763 | * It's necessary to set up the CPU-local CCROC NV_THERM instance with |
---|
.. | .. |
---|
1044 | 1805 | |
---|
1045 | 1806 | /** |
---|
1046 | 1807 | * throttlectl_cpu_level_select() - program CPU pulse skipper config |
---|
| 1808 | + * @ts: pointer to a struct tegra_soctherm |
---|
1047 | 1809 | * @throt: the LIGHT/HEAVY of throttle event id |
---|
1048 | 1810 | * |
---|
1049 | 1811 | * Pulse skippers are used to throttle clock frequencies. This |
---|
.. | .. |
---|
1087 | 1849 | |
---|
1088 | 1850 | /** |
---|
1089 | 1851 | * throttlectl_cpu_mn() - program CPU pulse skipper configuration |
---|
| 1852 | + * @ts: pointer to a struct tegra_soctherm |
---|
1090 | 1853 | * @throt: the LIGHT/HEAVY of throttle event id |
---|
1091 | 1854 | * |
---|
1092 | 1855 | * Pulse skippers are used to throttle clock frequencies. This |
---|
.. | .. |
---|
1119 | 1882 | } |
---|
1120 | 1883 | |
---|
1121 | 1884 | /** |
---|
| 1885 | + * throttlectl_gpu_level_select() - selects throttling level for GPU |
---|
| 1886 | + * @ts: pointer to a struct tegra_soctherm |
---|
| 1887 | + * @throt: the LIGHT/HEAVY of throttle event id |
---|
| 1888 | + * |
---|
| 1889 | + * This function programs soctherm's interface to GK20a NV_THERM to select |
---|
| 1890 | + * pre-configured "Low", "Medium" or "Heavy" throttle levels. |
---|
| 1891 | + * |
---|
| 1892 | + * Return: boolean true if HW was programmed |
---|
| 1893 | + */ |
---|
| 1894 | +static void throttlectl_gpu_level_select(struct tegra_soctherm *ts, |
---|
| 1895 | + enum soctherm_throttle_id throt) |
---|
| 1896 | +{ |
---|
| 1897 | + u32 r, level, throt_vect; |
---|
| 1898 | + |
---|
| 1899 | + level = ts->throt_cfgs[throt].gpu_throt_level; |
---|
| 1900 | + throt_vect = THROT_LEVEL_TO_DEPTH(level); |
---|
| 1901 | + r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_GPU)); |
---|
| 1902 | + r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1); |
---|
| 1903 | + r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT_GPU_MASK, throt_vect); |
---|
| 1904 | + writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_GPU)); |
---|
| 1905 | +} |
---|
| 1906 | + |
---|
| 1907 | +static int soctherm_oc_cfg_program(struct tegra_soctherm *ts, |
---|
| 1908 | + enum soctherm_throttle_id throt) |
---|
| 1909 | +{ |
---|
| 1910 | + u32 r; |
---|
| 1911 | + struct soctherm_oc_cfg *oc = &ts->throt_cfgs[throt].oc_cfg; |
---|
| 1912 | + |
---|
| 1913 | + if (oc->mode == OC_THROTTLE_MODE_DISABLED) |
---|
| 1914 | + return -EINVAL; |
---|
| 1915 | + |
---|
| 1916 | + r = REG_SET_MASK(0, OC1_CFG_HW_RESTORE_MASK, 1); |
---|
| 1917 | + r = REG_SET_MASK(r, OC1_CFG_THROTTLE_MODE_MASK, oc->mode); |
---|
| 1918 | + r = REG_SET_MASK(r, OC1_CFG_ALARM_POLARITY_MASK, oc->active_low); |
---|
| 1919 | + r = REG_SET_MASK(r, OC1_CFG_EN_THROTTLE_MASK, 1); |
---|
| 1920 | + writel(r, ts->regs + ALARM_CFG(throt)); |
---|
| 1921 | + writel(oc->throt_period, ts->regs + ALARM_THROTTLE_PERIOD(throt)); |
---|
| 1922 | + writel(oc->alarm_cnt_thresh, ts->regs + ALARM_CNT_THRESHOLD(throt)); |
---|
| 1923 | + writel(oc->alarm_filter, ts->regs + ALARM_FILTER(throt)); |
---|
| 1924 | + soctherm_oc_intr_enable(ts, throt, oc->intr_en); |
---|
| 1925 | + |
---|
| 1926 | + return 0; |
---|
| 1927 | +} |
---|
| 1928 | + |
---|
| 1929 | +/** |
---|
1122 | 1930 | * soctherm_throttle_program() - programs pulse skippers' configuration |
---|
| 1931 | + * @ts: pointer to a struct tegra_soctherm |
---|
1123 | 1932 | * @throt: the LIGHT/HEAVY of the throttle event id. |
---|
1124 | 1933 | * |
---|
1125 | 1934 | * Pulse skippers are used to throttle clock frequencies. |
---|
.. | .. |
---|
1134 | 1943 | if (!stc.init) |
---|
1135 | 1944 | return; |
---|
1136 | 1945 | |
---|
| 1946 | + if ((throt >= THROTTLE_OC1) && (soctherm_oc_cfg_program(ts, throt))) |
---|
| 1947 | + return; |
---|
| 1948 | + |
---|
1137 | 1949 | /* Setup PSKIP parameters */ |
---|
1138 | 1950 | if (ts->soc->use_ccroc) |
---|
1139 | 1951 | throttlectl_cpu_level_select(ts, throt); |
---|
1140 | 1952 | else |
---|
1141 | 1953 | throttlectl_cpu_mn(ts, throt); |
---|
| 1954 | + |
---|
| 1955 | + throttlectl_gpu_level_select(ts, throt); |
---|
1142 | 1956 | |
---|
1143 | 1957 | r = REG_SET_MASK(0, THROT_PRIORITY_LITE_PRIO_MASK, stc.priority); |
---|
1144 | 1958 | writel(r, ts->regs + THROT_PRIORITY_CTRL(throt)); |
---|
.. | .. |
---|
1191 | 2005 | v = STATS_CTL_CLR_DN | STATS_CTL_EN_DN | |
---|
1192 | 2006 | STATS_CTL_CLR_UP | STATS_CTL_EN_UP; |
---|
1193 | 2007 | writel(v, ts->regs + THERMCTL_STATS_CTL); |
---|
| 2008 | +} |
---|
| 2009 | + |
---|
| 2010 | +static int soctherm_interrupts_init(struct platform_device *pdev, |
---|
| 2011 | + struct tegra_soctherm *tegra) |
---|
| 2012 | +{ |
---|
| 2013 | + struct device_node *np = pdev->dev.of_node; |
---|
| 2014 | + int ret; |
---|
| 2015 | + |
---|
| 2016 | + ret = soctherm_oc_int_init(np, TEGRA_SOC_OC_IRQ_MAX); |
---|
| 2017 | + if (ret < 0) { |
---|
| 2018 | + dev_err(&pdev->dev, "soctherm_oc_int_init failed\n"); |
---|
| 2019 | + return ret; |
---|
| 2020 | + } |
---|
| 2021 | + |
---|
| 2022 | + tegra->thermal_irq = platform_get_irq(pdev, 0); |
---|
| 2023 | + if (tegra->thermal_irq < 0) { |
---|
| 2024 | + dev_dbg(&pdev->dev, "get 'thermal_irq' failed.\n"); |
---|
| 2025 | + return 0; |
---|
| 2026 | + } |
---|
| 2027 | + |
---|
| 2028 | + tegra->edp_irq = platform_get_irq(pdev, 1); |
---|
| 2029 | + if (tegra->edp_irq < 0) { |
---|
| 2030 | + dev_dbg(&pdev->dev, "get 'edp_irq' failed.\n"); |
---|
| 2031 | + return 0; |
---|
| 2032 | + } |
---|
| 2033 | + |
---|
| 2034 | + ret = devm_request_threaded_irq(&pdev->dev, |
---|
| 2035 | + tegra->thermal_irq, |
---|
| 2036 | + soctherm_thermal_isr, |
---|
| 2037 | + soctherm_thermal_isr_thread, |
---|
| 2038 | + IRQF_ONESHOT, |
---|
| 2039 | + dev_name(&pdev->dev), |
---|
| 2040 | + tegra); |
---|
| 2041 | + if (ret < 0) { |
---|
| 2042 | + dev_err(&pdev->dev, "request_irq 'thermal_irq' failed.\n"); |
---|
| 2043 | + return ret; |
---|
| 2044 | + } |
---|
| 2045 | + |
---|
| 2046 | + ret = devm_request_threaded_irq(&pdev->dev, |
---|
| 2047 | + tegra->edp_irq, |
---|
| 2048 | + soctherm_edp_isr, |
---|
| 2049 | + soctherm_edp_isr_thread, |
---|
| 2050 | + IRQF_ONESHOT, |
---|
| 2051 | + "soctherm_edp", |
---|
| 2052 | + tegra); |
---|
| 2053 | + if (ret < 0) { |
---|
| 2054 | + dev_err(&pdev->dev, "request_irq 'edp_irq' failed.\n"); |
---|
| 2055 | + return ret; |
---|
| 2056 | + } |
---|
| 2057 | + |
---|
| 2058 | + return 0; |
---|
1194 | 2059 | } |
---|
1195 | 2060 | |
---|
1196 | 2061 | static void soctherm_init(struct platform_device *pdev) |
---|
.. | .. |
---|
1270 | 2135 | if (!tegra) |
---|
1271 | 2136 | return -ENOMEM; |
---|
1272 | 2137 | |
---|
| 2138 | + mutex_init(&tegra->thermctl_lock); |
---|
1273 | 2139 | dev_set_drvdata(&pdev->dev, tegra); |
---|
1274 | 2140 | |
---|
1275 | 2141 | tegra->soc = soc; |
---|
.. | .. |
---|
1339 | 2205 | } |
---|
1340 | 2206 | |
---|
1341 | 2207 | tegra->thermctl_tzs = devm_kcalloc(&pdev->dev, |
---|
1342 | | - soc->num_ttgs, sizeof(*z), |
---|
| 2208 | + soc->num_ttgs, sizeof(z), |
---|
1343 | 2209 | GFP_KERNEL); |
---|
1344 | 2210 | if (!tegra->thermctl_tzs) |
---|
1345 | 2211 | return -ENOMEM; |
---|
.. | .. |
---|
1347 | 2213 | err = soctherm_clk_enable(pdev, true); |
---|
1348 | 2214 | if (err) |
---|
1349 | 2215 | return err; |
---|
| 2216 | + |
---|
| 2217 | + soctherm_thermtrips_parse(pdev); |
---|
1350 | 2218 | |
---|
1351 | 2219 | soctherm_init_hw_throt_cdev(pdev); |
---|
1352 | 2220 | |
---|
.. | .. |
---|
1384 | 2252 | goto disable_clocks; |
---|
1385 | 2253 | } |
---|
1386 | 2254 | |
---|
| 2255 | + err = soctherm_interrupts_init(pdev, tegra); |
---|
| 2256 | + |
---|
1387 | 2257 | soctherm_debug_init(pdev); |
---|
1388 | 2258 | |
---|
1389 | 2259 | return 0; |
---|