.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Marvell EBU Armada SoCs thermal sensor driver |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2013 Marvell |
---|
5 | | - * |
---|
6 | | - * This software is licensed under the terms of the GNU General Public |
---|
7 | | - * License version 2, as published by the Free Software Foundation, and |
---|
8 | | - * may be copied, distributed, and modified under those terms. |
---|
9 | | - * |
---|
10 | | - * This program is distributed in the hope that it will be useful, |
---|
11 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
12 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
13 | | - * GNU General Public License for more details. |
---|
14 | | - * |
---|
15 | 6 | */ |
---|
16 | 7 | #include <linux/device.h> |
---|
17 | 8 | #include <linux/err.h> |
---|
.. | .. |
---|
26 | 17 | #include <linux/iopoll.h> |
---|
27 | 18 | #include <linux/mfd/syscon.h> |
---|
28 | 19 | #include <linux/regmap.h> |
---|
| 20 | +#include <linux/interrupt.h> |
---|
| 21 | + |
---|
| 22 | +#include "thermal_core.h" |
---|
29 | 23 | |
---|
30 | 24 | /* Thermal Manager Control and Status Register */ |
---|
31 | 25 | #define PMU_TDC0_SW_RST_MASK (0x1 << 1) |
---|
.. | .. |
---|
57 | 51 | #define CONTROL0_TSEN_MODE_EXTERNAL 0x2 |
---|
58 | 52 | #define CONTROL0_TSEN_MODE_MASK 0x3 |
---|
59 | 53 | |
---|
60 | | -#define CONTROL1_TSEN_AVG_SHIFT 0 |
---|
61 | 54 | #define CONTROL1_TSEN_AVG_MASK 0x7 |
---|
62 | 55 | #define CONTROL1_EXT_TSEN_SW_RESET BIT(7) |
---|
63 | 56 | #define CONTROL1_EXT_TSEN_HW_RESETn BIT(8) |
---|
| 57 | +#define CONTROL1_TSEN_INT_EN BIT(25) |
---|
| 58 | +#define CONTROL1_TSEN_SELECT_OFF 21 |
---|
| 59 | +#define CONTROL1_TSEN_SELECT_MASK 0x3 |
---|
64 | 60 | |
---|
65 | 61 | #define STATUS_POLL_PERIOD_US 1000 |
---|
66 | 62 | #define STATUS_POLL_TIMEOUT_US 100000 |
---|
| 63 | +#define OVERHEAT_INT_POLL_DELAY_MS 1000 |
---|
67 | 64 | |
---|
68 | 65 | struct armada_thermal_data; |
---|
69 | 66 | |
---|
.. | .. |
---|
75 | 72 | /* serialize temperature reads/updates */ |
---|
76 | 73 | struct mutex update_lock; |
---|
77 | 74 | struct armada_thermal_data *data; |
---|
| 75 | + struct thermal_zone_device *overheat_sensor; |
---|
| 76 | + int interrupt_source; |
---|
78 | 77 | int current_channel; |
---|
| 78 | + long current_threshold; |
---|
| 79 | + long current_hysteresis; |
---|
79 | 80 | }; |
---|
80 | 81 | |
---|
81 | 82 | struct armada_thermal_data { |
---|
.. | .. |
---|
93 | 94 | /* Register shift and mask to access the sensor temperature */ |
---|
94 | 95 | unsigned int temp_shift; |
---|
95 | 96 | unsigned int temp_mask; |
---|
| 97 | + unsigned int thresh_shift; |
---|
| 98 | + unsigned int hyst_shift; |
---|
| 99 | + unsigned int hyst_mask; |
---|
96 | 100 | u32 is_valid_bit; |
---|
97 | 101 | |
---|
98 | 102 | /* Syscon access */ |
---|
99 | 103 | unsigned int syscon_control0_off; |
---|
100 | 104 | unsigned int syscon_control1_off; |
---|
101 | 105 | unsigned int syscon_status_off; |
---|
| 106 | + unsigned int dfx_irq_cause_off; |
---|
| 107 | + unsigned int dfx_irq_mask_off; |
---|
| 108 | + unsigned int dfx_overheat_irq; |
---|
| 109 | + unsigned int dfx_server_irq_mask_off; |
---|
| 110 | + unsigned int dfx_server_irq_en; |
---|
102 | 111 | |
---|
103 | 112 | /* One sensor is in the thermal IC, the others are in the CPUs if any */ |
---|
104 | 113 | unsigned int cpu_nr; |
---|
.. | .. |
---|
142 | 151 | /* Reset the sensor */ |
---|
143 | 152 | reg |= PMU_TDC0_SW_RST_MASK; |
---|
144 | 153 | |
---|
| 154 | + regmap_write(priv->syscon, data->syscon_control1_off, reg); |
---|
| 155 | + |
---|
| 156 | + reg &= ~PMU_TDC0_SW_RST_MASK; |
---|
145 | 157 | regmap_write(priv->syscon, data->syscon_control1_off, reg); |
---|
146 | 158 | |
---|
147 | 159 | /* Enable the sensor */ |
---|
.. | .. |
---|
255 | 267 | |
---|
256 | 268 | /* Average the output value over 2^1 = 2 samples */ |
---|
257 | 269 | regmap_read(priv->syscon, data->syscon_control1_off, ®); |
---|
258 | | - reg &= ~CONTROL1_TSEN_AVG_MASK << CONTROL1_TSEN_AVG_SHIFT; |
---|
259 | | - reg |= 1 << CONTROL1_TSEN_AVG_SHIFT; |
---|
| 270 | + reg &= ~CONTROL1_TSEN_AVG_MASK; |
---|
| 271 | + reg |= 1; |
---|
260 | 272 | regmap_write(priv->syscon, data->syscon_control1_off, reg); |
---|
261 | 273 | } |
---|
262 | 274 | |
---|
.. | .. |
---|
270 | 282 | regmap_read(priv->syscon, priv->data->syscon_status_off, ®); |
---|
271 | 283 | |
---|
272 | 284 | return reg & priv->data->is_valid_bit; |
---|
| 285 | +} |
---|
| 286 | + |
---|
| 287 | +static void armada_enable_overheat_interrupt(struct armada_thermal_priv *priv) |
---|
| 288 | +{ |
---|
| 289 | + struct armada_thermal_data *data = priv->data; |
---|
| 290 | + u32 reg; |
---|
| 291 | + |
---|
| 292 | + /* Clear DFX temperature IRQ cause */ |
---|
| 293 | + regmap_read(priv->syscon, data->dfx_irq_cause_off, ®); |
---|
| 294 | + |
---|
| 295 | + /* Enable DFX Temperature IRQ */ |
---|
| 296 | + regmap_read(priv->syscon, data->dfx_irq_mask_off, ®); |
---|
| 297 | + reg |= data->dfx_overheat_irq; |
---|
| 298 | + regmap_write(priv->syscon, data->dfx_irq_mask_off, reg); |
---|
| 299 | + |
---|
| 300 | + /* Enable DFX server IRQ */ |
---|
| 301 | + regmap_read(priv->syscon, data->dfx_server_irq_mask_off, ®); |
---|
| 302 | + reg |= data->dfx_server_irq_en; |
---|
| 303 | + regmap_write(priv->syscon, data->dfx_server_irq_mask_off, reg); |
---|
| 304 | + |
---|
| 305 | + /* Enable overheat interrupt */ |
---|
| 306 | + regmap_read(priv->syscon, data->syscon_control1_off, ®); |
---|
| 307 | + reg |= CONTROL1_TSEN_INT_EN; |
---|
| 308 | + regmap_write(priv->syscon, data->syscon_control1_off, reg); |
---|
| 309 | +} |
---|
| 310 | + |
---|
| 311 | +static void __maybe_unused |
---|
| 312 | +armada_disable_overheat_interrupt(struct armada_thermal_priv *priv) |
---|
| 313 | +{ |
---|
| 314 | + struct armada_thermal_data *data = priv->data; |
---|
| 315 | + u32 reg; |
---|
| 316 | + |
---|
| 317 | + regmap_read(priv->syscon, data->syscon_control1_off, ®); |
---|
| 318 | + reg &= ~CONTROL1_TSEN_INT_EN; |
---|
| 319 | + regmap_write(priv->syscon, data->syscon_control1_off, reg); |
---|
273 | 320 | } |
---|
274 | 321 | |
---|
275 | 322 | /* There is currently no board with more than one sensor per channel */ |
---|
.. | .. |
---|
388 | 435 | |
---|
389 | 436 | /* Do the actual reading */ |
---|
390 | 437 | ret = armada_read_sensor(priv, temp); |
---|
| 438 | + if (ret) |
---|
| 439 | + goto unlock_mutex; |
---|
| 440 | + |
---|
| 441 | + /* |
---|
| 442 | + * Select back the interrupt source channel from which a potential |
---|
| 443 | + * critical trip point has been set. |
---|
| 444 | + */ |
---|
| 445 | + ret = armada_select_channel(priv, priv->interrupt_source); |
---|
391 | 446 | |
---|
392 | 447 | unlock_mutex: |
---|
393 | 448 | mutex_unlock(&priv->update_lock); |
---|
.. | .. |
---|
395 | 450 | return ret; |
---|
396 | 451 | } |
---|
397 | 452 | |
---|
398 | | -static struct thermal_zone_of_device_ops of_ops = { |
---|
| 453 | +static const struct thermal_zone_of_device_ops of_ops = { |
---|
399 | 454 | .get_temp = armada_get_temp, |
---|
400 | 455 | }; |
---|
| 456 | + |
---|
| 457 | +static unsigned int armada_mc_to_reg_temp(struct armada_thermal_data *data, |
---|
| 458 | + unsigned int temp_mc) |
---|
| 459 | +{ |
---|
| 460 | + s64 b = data->coef_b; |
---|
| 461 | + s64 m = data->coef_m; |
---|
| 462 | + s64 div = data->coef_div; |
---|
| 463 | + unsigned int sample; |
---|
| 464 | + |
---|
| 465 | + if (data->inverted) |
---|
| 466 | + sample = div_s64(((temp_mc * div) + b), m); |
---|
| 467 | + else |
---|
| 468 | + sample = div_s64((b - (temp_mc * div)), m); |
---|
| 469 | + |
---|
| 470 | + return sample & data->temp_mask; |
---|
| 471 | +} |
---|
| 472 | + |
---|
| 473 | +/* |
---|
| 474 | + * The documentation states: |
---|
| 475 | + * high/low watermark = threshold +/- 0.4761 * 2^(hysteresis + 2) |
---|
| 476 | + * which is the mathematical derivation for: |
---|
| 477 | + * 0x0 <=> 1.9°C, 0x1 <=> 3.8°C, 0x2 <=> 7.6°C, 0x3 <=> 15.2°C |
---|
| 478 | + */ |
---|
| 479 | +static unsigned int hyst_levels_mc[] = {1900, 3800, 7600, 15200}; |
---|
| 480 | + |
---|
| 481 | +static unsigned int armada_mc_to_reg_hyst(struct armada_thermal_data *data, |
---|
| 482 | + unsigned int hyst_mc) |
---|
| 483 | +{ |
---|
| 484 | + int i; |
---|
| 485 | + |
---|
| 486 | + /* |
---|
| 487 | + * We will always take the smallest possible hysteresis to avoid risking |
---|
| 488 | + * the hardware integrity by enlarging the threshold by +8°C in the |
---|
| 489 | + * worst case. |
---|
| 490 | + */ |
---|
| 491 | + for (i = ARRAY_SIZE(hyst_levels_mc) - 1; i > 0; i--) |
---|
| 492 | + if (hyst_mc >= hyst_levels_mc[i]) |
---|
| 493 | + break; |
---|
| 494 | + |
---|
| 495 | + return i & data->hyst_mask; |
---|
| 496 | +} |
---|
| 497 | + |
---|
| 498 | +static void armada_set_overheat_thresholds(struct armada_thermal_priv *priv, |
---|
| 499 | + int thresh_mc, int hyst_mc) |
---|
| 500 | +{ |
---|
| 501 | + struct armada_thermal_data *data = priv->data; |
---|
| 502 | + unsigned int threshold = armada_mc_to_reg_temp(data, thresh_mc); |
---|
| 503 | + unsigned int hysteresis = armada_mc_to_reg_hyst(data, hyst_mc); |
---|
| 504 | + u32 ctrl1; |
---|
| 505 | + |
---|
| 506 | + regmap_read(priv->syscon, data->syscon_control1_off, &ctrl1); |
---|
| 507 | + |
---|
| 508 | + /* Set Threshold */ |
---|
| 509 | + if (thresh_mc >= 0) { |
---|
| 510 | + ctrl1 &= ~(data->temp_mask << data->thresh_shift); |
---|
| 511 | + ctrl1 |= threshold << data->thresh_shift; |
---|
| 512 | + priv->current_threshold = thresh_mc; |
---|
| 513 | + } |
---|
| 514 | + |
---|
| 515 | + /* Set Hysteresis */ |
---|
| 516 | + if (hyst_mc >= 0) { |
---|
| 517 | + ctrl1 &= ~(data->hyst_mask << data->hyst_shift); |
---|
| 518 | + ctrl1 |= hysteresis << data->hyst_shift; |
---|
| 519 | + priv->current_hysteresis = hyst_mc; |
---|
| 520 | + } |
---|
| 521 | + |
---|
| 522 | + regmap_write(priv->syscon, data->syscon_control1_off, ctrl1); |
---|
| 523 | +} |
---|
| 524 | + |
---|
| 525 | +static irqreturn_t armada_overheat_isr(int irq, void *blob) |
---|
| 526 | +{ |
---|
| 527 | + /* |
---|
| 528 | + * Disable the IRQ and continue in thread context (thermal core |
---|
| 529 | + * notification and temperature monitoring). |
---|
| 530 | + */ |
---|
| 531 | + disable_irq_nosync(irq); |
---|
| 532 | + |
---|
| 533 | + return IRQ_WAKE_THREAD; |
---|
| 534 | +} |
---|
| 535 | + |
---|
| 536 | +static irqreturn_t armada_overheat_isr_thread(int irq, void *blob) |
---|
| 537 | +{ |
---|
| 538 | + struct armada_thermal_priv *priv = blob; |
---|
| 539 | + int low_threshold = priv->current_threshold - priv->current_hysteresis; |
---|
| 540 | + int temperature; |
---|
| 541 | + u32 dummy; |
---|
| 542 | + int ret; |
---|
| 543 | + |
---|
| 544 | + /* Notify the core in thread context */ |
---|
| 545 | + thermal_zone_device_update(priv->overheat_sensor, |
---|
| 546 | + THERMAL_EVENT_UNSPECIFIED); |
---|
| 547 | + |
---|
| 548 | + /* |
---|
| 549 | + * The overheat interrupt must be cleared by reading the DFX interrupt |
---|
| 550 | + * cause _after_ the temperature has fallen down to the low threshold. |
---|
| 551 | + * Otherwise future interrupts might not be served. |
---|
| 552 | + */ |
---|
| 553 | + do { |
---|
| 554 | + msleep(OVERHEAT_INT_POLL_DELAY_MS); |
---|
| 555 | + mutex_lock(&priv->update_lock); |
---|
| 556 | + ret = armada_read_sensor(priv, &temperature); |
---|
| 557 | + mutex_unlock(&priv->update_lock); |
---|
| 558 | + if (ret) |
---|
| 559 | + goto enable_irq; |
---|
| 560 | + } while (temperature >= low_threshold); |
---|
| 561 | + |
---|
| 562 | + regmap_read(priv->syscon, priv->data->dfx_irq_cause_off, &dummy); |
---|
| 563 | + |
---|
| 564 | + /* Notify the thermal core that the temperature is acceptable again */ |
---|
| 565 | + thermal_zone_device_update(priv->overheat_sensor, |
---|
| 566 | + THERMAL_EVENT_UNSPECIFIED); |
---|
| 567 | + |
---|
| 568 | +enable_irq: |
---|
| 569 | + enable_irq(irq); |
---|
| 570 | + |
---|
| 571 | + return IRQ_HANDLED; |
---|
| 572 | +} |
---|
401 | 573 | |
---|
402 | 574 | static const struct armada_thermal_data armadaxp_data = { |
---|
403 | 575 | .init = armadaxp_init, |
---|
.. | .. |
---|
407 | 579 | .coef_m = 10000000ULL, |
---|
408 | 580 | .coef_div = 13825, |
---|
409 | 581 | .syscon_status_off = 0xb0, |
---|
410 | | - .syscon_control1_off = 0xd0, |
---|
| 582 | + .syscon_control1_off = 0x2d0, |
---|
411 | 583 | }; |
---|
412 | 584 | |
---|
413 | 585 | static const struct armada_thermal_data armada370_data = { |
---|
.. | .. |
---|
454 | 626 | .is_valid_bit = BIT(16), |
---|
455 | 627 | .temp_shift = 0, |
---|
456 | 628 | .temp_mask = 0x3ff, |
---|
| 629 | + .thresh_shift = 3, |
---|
| 630 | + .hyst_shift = 19, |
---|
| 631 | + .hyst_mask = 0x3, |
---|
457 | 632 | .coef_b = -150000LL, |
---|
458 | 633 | .coef_m = 423ULL, |
---|
459 | 634 | .coef_div = 1, |
---|
.. | .. |
---|
462 | 637 | .syscon_control0_off = 0x84, |
---|
463 | 638 | .syscon_control1_off = 0x88, |
---|
464 | 639 | .syscon_status_off = 0x8C, |
---|
| 640 | + .dfx_irq_cause_off = 0x108, |
---|
| 641 | + .dfx_irq_mask_off = 0x10C, |
---|
| 642 | + .dfx_overheat_irq = BIT(22), |
---|
| 643 | + .dfx_server_irq_mask_off = 0x104, |
---|
| 644 | + .dfx_server_irq_en = BIT(1), |
---|
465 | 645 | .cpu_nr = 4, |
---|
466 | 646 | }; |
---|
467 | 647 | |
---|
.. | .. |
---|
470 | 650 | .is_valid_bit = BIT(10), |
---|
471 | 651 | .temp_shift = 0, |
---|
472 | 652 | .temp_mask = 0x3ff, |
---|
| 653 | + .thresh_shift = 16, |
---|
| 654 | + .hyst_shift = 26, |
---|
| 655 | + .hyst_mask = 0x3, |
---|
473 | 656 | .coef_b = 1172499100ULL, |
---|
474 | 657 | .coef_m = 2000096ULL, |
---|
475 | 658 | .coef_div = 4201, |
---|
.. | .. |
---|
477 | 660 | .syscon_control0_off = 0x70, |
---|
478 | 661 | .syscon_control1_off = 0x74, |
---|
479 | 662 | .syscon_status_off = 0x78, |
---|
| 663 | + .dfx_irq_cause_off = 0x108, |
---|
| 664 | + .dfx_irq_mask_off = 0x10C, |
---|
| 665 | + .dfx_overheat_irq = BIT(20), |
---|
| 666 | + .dfx_server_irq_mask_off = 0x104, |
---|
| 667 | + .dfx_server_irq_en = BIT(1), |
---|
480 | 668 | }; |
---|
481 | 669 | |
---|
482 | 670 | static const struct of_device_id armada_thermal_id_table[] = { |
---|
.. | .. |
---|
526 | 714 | |
---|
527 | 715 | /* First memory region points towards the status register */ |
---|
528 | 716 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
529 | | - if (!res) |
---|
530 | | - return -EIO; |
---|
531 | | - |
---|
532 | | - /* |
---|
533 | | - * Edit the resource start address and length to map over all the |
---|
534 | | - * registers, instead of pointing at them one by one. |
---|
535 | | - */ |
---|
536 | | - res->start -= data->syscon_status_off; |
---|
537 | | - res->end = res->start + max(data->syscon_status_off, |
---|
538 | | - max(data->syscon_control0_off, |
---|
539 | | - data->syscon_control1_off)) + |
---|
540 | | - sizeof(unsigned int) - 1; |
---|
541 | | - |
---|
542 | 717 | base = devm_ioremap_resource(&pdev->dev, res); |
---|
543 | 718 | if (IS_ERR(base)) |
---|
544 | 719 | return PTR_ERR(base); |
---|
545 | 720 | |
---|
| 721 | + /* |
---|
| 722 | + * Fix up from the old individual DT register specification to |
---|
| 723 | + * cover all the registers. We do this by adjusting the ioremap() |
---|
| 724 | + * result, which should be fine as ioremap() deals with pages. |
---|
| 725 | + * However, validate that we do not cross a page boundary while |
---|
| 726 | + * making this adjustment. |
---|
| 727 | + */ |
---|
| 728 | + if (((unsigned long)base & ~PAGE_MASK) < data->syscon_status_off) |
---|
| 729 | + return -EINVAL; |
---|
| 730 | + base -= data->syscon_status_off; |
---|
| 731 | + |
---|
546 | 732 | priv->syscon = devm_regmap_init_mmio(&pdev->dev, base, |
---|
547 | 733 | &armada_thermal_regmap_config); |
---|
548 | | - if (IS_ERR(priv->syscon)) |
---|
549 | | - return PTR_ERR(priv->syscon); |
---|
550 | | - |
---|
551 | | - return 0; |
---|
| 734 | + return PTR_ERR_OR_ZERO(priv->syscon); |
---|
552 | 735 | } |
---|
553 | 736 | |
---|
554 | 737 | static int armada_thermal_probe_syscon(struct platform_device *pdev, |
---|
555 | 738 | struct armada_thermal_priv *priv) |
---|
556 | 739 | { |
---|
557 | 740 | priv->syscon = syscon_node_to_regmap(pdev->dev.parent->of_node); |
---|
558 | | - if (IS_ERR(priv->syscon)) |
---|
559 | | - return PTR_ERR(priv->syscon); |
---|
560 | | - |
---|
561 | | - return 0; |
---|
| 741 | + return PTR_ERR_OR_ZERO(priv->syscon); |
---|
562 | 742 | } |
---|
563 | 743 | |
---|
564 | 744 | static void armada_set_sane_name(struct platform_device *pdev, |
---|
.. | .. |
---|
592 | 772 | } while (insane_char); |
---|
593 | 773 | } |
---|
594 | 774 | |
---|
| 775 | +/* |
---|
| 776 | + * The IP can manage to trigger interrupts on overheat situation from all the |
---|
| 777 | + * sensors. However, the interrupt source changes along with the last selected |
---|
| 778 | + * source (ie. the last read sensor), which is an inconsistent behavior. Avoid |
---|
| 779 | + * possible glitches by always selecting back only one channel (arbitrarily: the |
---|
| 780 | + * first in the DT which has a critical trip point). We also disable sensor |
---|
| 781 | + * switch during overheat situations. |
---|
| 782 | + */ |
---|
| 783 | +static int armada_configure_overheat_int(struct armada_thermal_priv *priv, |
---|
| 784 | + struct thermal_zone_device *tz, |
---|
| 785 | + int sensor_id) |
---|
| 786 | +{ |
---|
| 787 | + /* Retrieve the critical trip point to enable the overheat interrupt */ |
---|
| 788 | + const struct thermal_trip *trips = of_thermal_get_trip_points(tz); |
---|
| 789 | + int ret; |
---|
| 790 | + int i; |
---|
| 791 | + |
---|
| 792 | + if (!trips) |
---|
| 793 | + return -EINVAL; |
---|
| 794 | + |
---|
| 795 | + for (i = 0; i < of_thermal_get_ntrips(tz); i++) |
---|
| 796 | + if (trips[i].type == THERMAL_TRIP_CRITICAL) |
---|
| 797 | + break; |
---|
| 798 | + |
---|
| 799 | + if (i == of_thermal_get_ntrips(tz)) |
---|
| 800 | + return -EINVAL; |
---|
| 801 | + |
---|
| 802 | + ret = armada_select_channel(priv, sensor_id); |
---|
| 803 | + if (ret) |
---|
| 804 | + return ret; |
---|
| 805 | + |
---|
| 806 | + armada_set_overheat_thresholds(priv, |
---|
| 807 | + trips[i].temperature, |
---|
| 808 | + trips[i].hysteresis); |
---|
| 809 | + priv->overheat_sensor = tz; |
---|
| 810 | + priv->interrupt_source = sensor_id; |
---|
| 811 | + |
---|
| 812 | + armada_enable_overheat_interrupt(priv); |
---|
| 813 | + |
---|
| 814 | + return 0; |
---|
| 815 | +} |
---|
| 816 | + |
---|
595 | 817 | static int armada_thermal_probe(struct platform_device *pdev) |
---|
596 | 818 | { |
---|
597 | 819 | struct thermal_zone_device *tz; |
---|
.. | .. |
---|
599 | 821 | struct armada_drvdata *drvdata; |
---|
600 | 822 | const struct of_device_id *match; |
---|
601 | 823 | struct armada_thermal_priv *priv; |
---|
602 | | - int sensor_id; |
---|
| 824 | + int sensor_id, irq; |
---|
603 | 825 | int ret; |
---|
604 | 826 | |
---|
605 | 827 | match = of_match_device(armada_thermal_id_table, &pdev->dev); |
---|
.. | .. |
---|
652 | 874 | return PTR_ERR(tz); |
---|
653 | 875 | } |
---|
654 | 876 | |
---|
| 877 | + ret = thermal_zone_device_enable(tz); |
---|
| 878 | + if (ret) { |
---|
| 879 | + thermal_zone_device_unregister(tz); |
---|
| 880 | + return ret; |
---|
| 881 | + } |
---|
| 882 | + |
---|
655 | 883 | drvdata->type = LEGACY; |
---|
656 | 884 | drvdata->data.tz = tz; |
---|
657 | 885 | platform_set_drvdata(pdev, drvdata); |
---|
.. | .. |
---|
668 | 896 | drvdata->type = SYSCON; |
---|
669 | 897 | drvdata->data.priv = priv; |
---|
670 | 898 | platform_set_drvdata(pdev, drvdata); |
---|
| 899 | + |
---|
| 900 | + irq = platform_get_irq(pdev, 0); |
---|
| 901 | + if (irq == -EPROBE_DEFER) |
---|
| 902 | + return irq; |
---|
| 903 | + |
---|
| 904 | + /* The overheat interrupt feature is not mandatory */ |
---|
| 905 | + if (irq > 0) { |
---|
| 906 | + ret = devm_request_threaded_irq(&pdev->dev, irq, |
---|
| 907 | + armada_overheat_isr, |
---|
| 908 | + armada_overheat_isr_thread, |
---|
| 909 | + 0, NULL, priv); |
---|
| 910 | + if (ret) { |
---|
| 911 | + dev_err(&pdev->dev, "Cannot request threaded IRQ %d\n", |
---|
| 912 | + irq); |
---|
| 913 | + return ret; |
---|
| 914 | + } |
---|
| 915 | + } |
---|
671 | 916 | |
---|
672 | 917 | /* |
---|
673 | 918 | * There is one channel for the IC and one per CPU (if any), each |
---|
.. | .. |
---|
692 | 937 | devm_kfree(&pdev->dev, sensor); |
---|
693 | 938 | continue; |
---|
694 | 939 | } |
---|
| 940 | + |
---|
| 941 | + /* |
---|
| 942 | + * The first channel that has a critical trip point registered |
---|
| 943 | + * in the DT will serve as interrupt source. Others possible |
---|
| 944 | + * critical trip points will simply be ignored by the driver. |
---|
| 945 | + */ |
---|
| 946 | + if (irq > 0 && !priv->overheat_sensor) |
---|
| 947 | + armada_configure_overheat_int(priv, tz, sensor->id); |
---|
695 | 948 | } |
---|
696 | 949 | |
---|
| 950 | + /* Just complain if no overheat interrupt was set up */ |
---|
| 951 | + if (!priv->overheat_sensor) |
---|
| 952 | + dev_warn(&pdev->dev, "Overheat interrupt not available\n"); |
---|
| 953 | + |
---|
697 | 954 | return 0; |
---|
698 | 955 | } |
---|
699 | 956 | |
---|