Pull thermal soc updates from Eduardo Valentin: - thermal core has a new devm_* API for registering cooling devices. I took the entire series, that is why you see changes on drivers/hwmon in this pull (Guenter Roeck) - rockchip thermal driver gains support to PX30 SoC (Elaine Zhang) - the generic-adc thermal driver now considers the lookup table DT property as optional (Jean-Francois Dagenais) - Refactoring of tsens thermal driver (Amit Kucheria) - Cleanups on cpu cooling driver (Daniel Lezcano) - broadcom thermal driver dropped support to ACPI (Srinath Mannam) - tegra thermal driver gains support to OC hw throttle and GPU throtle (Wei Ni) - Fixes in several thermal drivers. * 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal: (59 commits) hwmon: (pwm-fan) Use devm_thermal_of_cooling_device_register hwmon: (npcm750-pwm-fan) Use devm_thermal_of_cooling_device_register hwmon: (mlxreg-fan) Use devm_thermal_of_cooling_device_register hwmon: (gpio-fan) Use devm_thermal_of_cooling_device_register hwmon: (aspeed-pwm-tacho) Use devm_thermal_of_cooling_device_register thermal: rcar_gen3_thermal: Fix to show correct trip points number thermal: rcar_thermal: update calculation formula for R-Car Gen3 SoCs thermal: cpu_cooling: Actually trace CPU load in thermal_power_cpu_get_power thermal: rockchip: Support the PX30 SoC in thermal driver dt-bindings: rockchip-thermal: Support the PX30 SoC compatible thermal: rockchip: fix up the tsadc pinctrl setting error thermal: broadcom: Remove ACPI support thermal: Fix build error of missing devm_ioremap_resource on UM thermal/drivers/cpu_cooling: Remove pointless field thermal/drivers/cpu_cooling: Add Software Package Data Exchange (SPDX) thermal/drivers/cpu_cooling: Fixup the header and copyright thermal/drivers/cpu_cooling: Remove pointless test in power2state() thermal: rcar_gen3_thermal: disable interrupt in .remove thermal: rcar_gen3_thermal: fix interrupt type thermal: Introduce devm_thermal_of_cooling_device_register ...
Unified · Split
| @@ -0,0 +1,33 @@ | ||
| 1 | +Amazon's Annapurna Labs Thermal Sensor | |
| 2 | + | |
| 3 | +Simple thermal device that allows temperature reading by a single MMIO | |
| 4 | +transaction. | |
| 5 | + | |
| 6 | +Required properties: | |
| 7 | +- compatible: "amazon,al-thermal". | |
| 8 | +- reg: The physical base address and length of the sensor's registers. | |
| 9 | +- #thermal-sensor-cells: Must be 1. See ./thermal.txt for a description. | |
| 10 | + | |
| 11 | +Example: | |
| 12 | + thermal: thermal { | |
| 13 | + compatible = "amazon,al-thermal"; | |
| 14 | + reg = <0x0 0x05002860 0x0 0x1>; | |
| 15 | + #thermal-sensor-cells = <0x1>; | |
| 16 | + }; | |
| 17 | + | |
| 18 | + thermal-zones { | |
| 19 | + thermal-z0 { | |
| 20 | + polling-delay-passive = <250>; | |
| 21 | + polling-delay = <1000>; | |
| 22 | + thermal-sensors = <&thermal 0>; | |
| 23 | + trips { | |
| 24 | + critical { | |
| 25 | + temperature = <105000>; | |
| 26 | + hysteresis = <2000>; | |
| 27 | + type = "critical"; | |
| 28 | + }; | |
| 29 | + }; | |
| 30 | + | |
| 31 | + }; | |
| 32 | + }; | |
| 33 | + | |
| @@ -52,13 +52,47 @@ Required properties : · 51 unmodified lines | ||
| 52 | 52 | Must set as following values: |
| 53 | 53 | TEGRA_SOCTHERM_THROT_LEVEL_LOW, TEGRA_SOCTHERM_THROT_LEVEL_MED |
| 54 | 54 | TEGRA_SOCTHERM_THROT_LEVEL_HIGH, TEGRA_SOCTHERM_THROT_LEVEL_NONE |
| 55 | + - nvidia,gpu-throt-level: This property is for Tegra124 and Tegra210. | |
| 56 | + It is the level of pulse skippers, which used to throttle clock | |
| 57 | + frequencies. It indicates gpu clock throttling depth and can be | |
| 58 | + programmed to any of the following values which represent a throttling | |
| 59 | + percentage: | |
| 60 | + TEGRA_SOCTHERM_THROT_LEVEL_NONE (0%) | |
| 61 | + TEGRA_SOCTHERM_THROT_LEVEL_LOW (50%), | |
| 62 | + TEGRA_SOCTHERM_THROT_LEVEL_MED (75%), | |
| 63 | + TEGRA_SOCTHERM_THROT_LEVEL_HIGH (85%). | |
| 55 | 64 | - #cooling-cells: Should be 1. This cooling device only support on/off state. |
| 56 | 65 | See ./thermal.txt for a description of this property. |
| 57 | 66 | |
| 67 | + Optional properties: The following properties are T210 specific and | |
| 68 | + valid only for OCx throttle events. | |
| 69 | + - nvidia,count-threshold: Specifies the number of OC events that are | |
| 70 | + required for triggering an interrupt. Interrupts are not triggered if | |
| 71 | + the property is missing. A value of 0 will interrupt on every OC alarm. | |
| 72 | + - nvidia,polarity-active-low: Configures the polarity of the OC alaram | |
| 73 | + signal. If present, this means assert low, otherwise assert high. | |
| 74 | + - nvidia,alarm-filter: Number of clocks to filter event. When the filter | |
| 75 | + expires (which means the OC event has not occurred for a long time), | |
| 76 | + the counter is cleared and filter is rearmed. Default value is 0. | |
| 77 | + - nvidia,throttle-period-us: Specifies the number of uSec for which | |
| 78 | + throttling is engaged after the OC event is deasserted. Default value | |
| 79 | + is 0. | |
| 80 | + | |
| 81 | +Optional properties: | |
| 82 | +- nvidia,thermtrips : When present, this property specifies the temperature at | |
| 83 | + which the soctherm hardware will assert the thermal trigger signal to the | |
| 84 | + Power Management IC, which can be configured to reset or shutdown the device. | |
| 85 | + It is an array of pairs where each pair represents a tsensor id followed by a | |
| 86 | + temperature in milli Celcius. In the absence of this property the critical | |
| 87 | + trip point will be used for thermtrip temperature. | |
| 88 | + | |
| 58 | 89 | Note: |
| 59 | −- the "critical" type trip points will be set to SOC_THERM hardware as the | |
| 60 | −shut down temperature. Once the temperature of this thermal zone is higher | |
| 61 | −than it, the system will be shutdown or reset by hardware. | |
| 90 | +- the "critical" type trip points will be used to set the temperature at which | |
| 91 | +the SOC_THERM hardware will assert a thermal trigger if the "nvidia,thermtrips" | |
| 92 | +property is missing. When the thermtrips property is present, the breach of a | |
| 93 | +critical trip point is reported back to the thermal framework to implement | |
| 94 | +software shutdown. | |
| 95 | + | |
| 62 | 96 | - the "hot" type trip points will be set to SOC_THERM hardware as the throttle |
| 63 | 97 | temperature. Once the the temperature of this thermal zone is higher |
| 64 | 98 | than it, it will trigger the HW throttle event. |
| @@ -79,25 +113,32 @@ Example : · 14 unmodified lines | ||
| 79 | 113 | |
| 80 | 114 | #thermal-sensor-cells = <1>; |
| 81 | 115 | |
| 116 | + nvidia,thermtrips = <TEGRA124_SOCTHERM_SENSOR_CPU 102500 | |
| 117 | + TEGRA124_SOCTHERM_SENSOR_GPU 103000>; | |
| 118 | + | |
| 82 | 119 | throttle-cfgs { |
| 83 | 120 | /* |
| 84 | 121 | * When the "heavy" cooling device triggered, |
| 85 | − * the HW will skip cpu clock's pulse in 85% depth | |
| 122 | + * the HW will skip cpu clock's pulse in 85% depth, | |
| 123 | + * skip gpu clock's pulse in 85% level | |
| 86 | 124 | */ |
| 87 | 125 | throttle_heavy: heavy { |
| 88 | 126 | nvidia,priority = <100>; |
| 89 | 127 | nvidia,cpu-throt-percent = <85>; |
| 128 | + nvidia,gpu-throt-level = <TEGRA_SOCTHERM_THROT_LEVEL_HIGH>; | |
| 90 | 129 | |
| 91 | 130 | #cooling-cells = <1>; |
| 92 | 131 | }; |
| 93 | 132 | |
| 94 | 133 | /* |
| 95 | 134 | * When the "light" cooling device triggered, |
| 96 | − * the HW will skip cpu clock's pulse in 50% depth | |
| 135 | + * the HW will skip cpu clock's pulse in 50% depth, | |
| 136 | + * skip gpu clock's pulse in 50% level | |
| 97 | 137 | */ |
| 98 | 138 | throttle_light: light { |
| 99 | 139 | nvidia,priority = <80>; |
| 100 | 140 | nvidia,cpu-throt-percent = <50>; |
| 141 | + nvidia,gpu-throt-level = <TEGRA_SOCTHERM_THROT_LEVEL_LOW>; | |
| 101 | 142 | |
| 102 | 143 | #cooling-cells = <1>; |
| 103 | 144 | }; |
| @@ -107,6 +148,17 @@ Example : · 3 unmodified lines | ||
| 107 | 148 | * arbiter will select the highest priority as the final throttle |
| 108 | 149 | * settings to skip cpu pulse. |
| 109 | 150 | */ |
| 151 | + | |
| 152 | + throttle_oc1: oc1 { | |
| 153 | + nvidia,priority = <50>; | |
| 154 | + nvidia,polarity-active-low; | |
| 155 | + nvidia,count-threshold = <100>; | |
| 156 | + nvidia,alarm-filter = <5100000>; | |
| 157 | + nvidia,throttle-period-us = <0>; | |
| 158 | + nvidia,cpu-throt-percent = <75>; | |
| 159 | + nvidia,gpu-throt-level = | |
| 160 | + <TEGRA_SOCTHERM_THROT_LEVEL_MED>; | |
| 161 | + }; | |
| 110 | 162 | }; |
| 111 | 163 | }; |
| 112 | 164 | |
| @@ -6,11 +6,14 @@ Required properties: · 5 unmodified lines | ||
| 6 | 6 | - "qcom,msm8916-tsens" (MSM8916) |
| 7 | 7 | - "qcom,msm8974-tsens" (MSM8974) |
| 8 | 8 | - "qcom,msm8996-tsens" (MSM8996) |
| 9 | + - "qcom,qcs404-tsens", "qcom,tsens-v1" (QCS404) | |
| 9 | 10 | - "qcom,msm8998-tsens", "qcom,tsens-v2" (MSM8998) |
| 10 | 11 | - "qcom,sdm845-tsens", "qcom,tsens-v2" (SDM845) |
| 11 | 12 | The generic "qcom,tsens-v2" property must be used as a fallback for any SoC |
| 12 | 13 | with version 2 of the TSENS IP. MSM8996 is the only exception because the |
| 13 | 14 | generic property did not exist when support was added. |
| 15 | + Similarly, the generic "qcom,tsens-v1" property must be used as a fallback for | |
| 16 | + any SoC with version 1 of the TSENS IP. | |
| 14 | 17 | |
| 15 | 18 | - reg: Address range of the thermal registers. |
| 16 | 19 | New platforms containing v2.x.y of the TSENS IP must specify the SROT and TM |
| @@ -39,3 +42,14 @@ tsens0: thermal-sensor@c263000 { · 22 unmodified lines | ||
| 39 | 42 | #qcom,sensors = <13>; |
| 40 | 43 | #thermal-sensor-cells = <1>; |
| 41 | 44 | }; |
| 45 | + | |
| 46 | +Example 3 (for any platform containing v1 of the TSENS IP): | |
| 47 | +tsens: thermal-sensor@4a9000 { | |
| 48 | + compatible = "qcom,qcs404-tsens", "qcom,tsens-v1"; | |
| 49 | + reg = <0x004a9000 0x1000>, /* TM */ | |
| 50 | + <0x004a8000 0x1000>; /* SROT */ | |
| 51 | + nvmem-cells = <&tsens_caldata>; | |
| 52 | + nvmem-cell-names = "calib"; | |
| 53 | + #qcom,sensors = <10>; | |
| 54 | + #thermal-sensor-cells = <1>; | |
| 55 | + }; | |
| @@ -2,6 +2,7 @@ · 1 unmodified line | ||
| 2 | 2 | |
| 3 | 3 | Required properties: |
| 4 | 4 | - compatible : should be "rockchip,<name>-tsadc" |
| 5 | + "rockchip,px30-tsadc": found on PX30 SoCs | |
| 5 | 6 | "rockchip,rv1108-tsadc": found on RV1108 SoCs |
| 6 | 7 | "rockchip,rk3228-tsadc": found on RK3228 SoCs |
| 7 | 8 | "rockchip,rk3288-tsadc": found on RK3288 SoCs |
| @@ -8,16 +8,22 @@ temperature using voltage-temperature lookup table. · 7 unmodified lines | ||
| 8 | 8 | Required properties: |
| 9 | 9 | =================== |
| 10 | 10 | - compatible: Must be "generic-adc-thermal". |
| 11 | +- #thermal-sensor-cells: Should be 1. See ./thermal.txt for a description | |
| 12 | + of this property. | |
| 13 | +Optional properties: | |
| 14 | +=================== | |
| 11 | 15 | - temperature-lookup-table: Two dimensional array of Integer; lookup table |
| 12 | 16 | to map the relation between ADC value and |
| 13 | 17 | temperature. When ADC is read, the value is |
| 14 | 18 | looked up on the table to get the equivalent |
| 15 | 19 | temperature. |
| 20 | + | |
| 16 | 21 | The first value of the each row of array is the |
| 17 | 22 | temperature in milliCelsius and second value of |
| 18 | 23 | the each row of array is the ADC read value. |
| 19 | −- #thermal-sensor-cells: Should be 1. See ./thermal.txt for a description | |
| 20 | − of this property. | |
| 24 | + | |
| 25 | + If not specified, driver assumes the ADC channel | |
| 26 | + gives milliCelsius directly. | |
| 21 | 27 | |
| 22 | 28 | Example : |
| 23 | 29 | #include <dt-bindings/thermal/thermal.h> |
| @@ -742,6 +742,12 @@ F: drivers/tty/serial/altera_jtaguart.c · 741 unmodified lines | ||
| 742 | 742 | F: include/linux/altera_uart.h |
| 743 | 743 | F: include/linux/altera_jtaguart.h |
| 744 | 744 | |
| 745 | +AMAZON ANNAPURNA LABS THERMAL MMIO DRIVER | |
| 746 | +M: Talel Shenhar <talel@amazon.com> | |
| 747 | +S: Maintained | |
| 748 | +F: Documentation/devicetree/bindings/thermal/amazon,al-thermal.txt | |
| 749 | +F: drivers/thermal/thermal_mmio.c | |
| 750 | + | |
| 745 | 751 | AMAZON ETHERNET DRIVERS |
| 746 | 752 | M: Netanel Belgazal <netanel@amazon.com> |
| 747 | 753 | R: Saeed Bishara <saeedb@amazon.com> |
| @@ -830,10 +830,8 @@ static int aspeed_create_pwm_cooling(struct device *dev, · 829 unmodified lines | ||
| 830 | 830 | } |
| 831 | 831 | snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%pOFn%d", child, pwm_port); |
| 832 | 832 | |
| 833 | − cdev->tcdev = thermal_of_cooling_device_register(child, | |
| 834 | − cdev->name, | |
| 835 | − cdev, | |
| 836 | − &aspeed_pwm_cool_ops); | |
| 833 | + cdev->tcdev = devm_thermal_of_cooling_device_register(dev, child, | |
| 834 | + cdev->name, cdev, &aspeed_pwm_cool_ops); | |
| 837 | 835 | if (IS_ERR(cdev->tcdev)) |
| 838 | 836 | return PTR_ERR(cdev->tcdev); |
| 839 | 837 | |
| @@ -498,6 +498,11 @@ static const struct of_device_id of_gpio_fan_match[] = { · 497 unmodified lines | ||
| 498 | 498 | }; |
| 499 | 499 | MODULE_DEVICE_TABLE(of, of_gpio_fan_match); |
| 500 | 500 | |
| 501 | +static void gpio_fan_stop(void *data) | |
| 502 | +{ | |
| 503 | + set_fan_speed(data, 0); | |
| 504 | +} | |
| 505 | + | |
| 501 | 506 | static int gpio_fan_probe(struct platform_device *pdev) |
| 502 | 507 | { |
| 503 | 508 | int err; |
| @@ -532,6 +537,7 @@ static int gpio_fan_probe(struct platform_device *pdev) · 28 unmodified lines | ||
| 532 | 537 | err = fan_ctrl_init(fan_data); |
| 533 | 538 | if (err) |
| 534 | 539 | return err; |
| 540 | + devm_add_action_or_reset(dev, gpio_fan_stop, fan_data); | |
| 535 | 541 | } |
| 536 | 542 | |
| 537 | 543 | /* Make this driver part of hwmon class. */ |
| @@ -543,32 +549,20 @@ static int gpio_fan_probe(struct platform_device *pdev) · 5 unmodified lines | ||
| 543 | 549 | return PTR_ERR(fan_data->hwmon_dev); |
| 544 | 550 | |
| 545 | 551 | /* Optional cooling device register for Device tree platforms */ |
| 546 | − fan_data->cdev = thermal_of_cooling_device_register(np, | |
| 547 | − "gpio-fan", | |
| 548 | − fan_data, | |
| 549 | − &gpio_fan_cool_ops); | |
| 552 | + fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np, | |
| 553 | + "gpio-fan", fan_data, &gpio_fan_cool_ops); | |
| 550 | 554 | |
| 551 | 555 | dev_info(dev, "GPIO fan initialized\n"); |
| 552 | 556 | |
| 553 | 557 | return 0; |
| 554 | 558 | } |
| 555 | 559 | |
| 556 | −static int gpio_fan_remove(struct platform_device *pdev) | |
| 560 | +static void gpio_fan_shutdown(struct platform_device *pdev) | |
| 557 | 561 | { |
| 558 | 562 | struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); |
| 559 | 563 | |
| 560 | − if (!IS_ERR(fan_data->cdev)) | |
| 561 | − thermal_cooling_device_unregister(fan_data->cdev); | |
| 562 | − | |
| 563 | 564 | if (fan_data->gpios) |
| 564 | 565 | set_fan_speed(fan_data, 0); |
| 565 | − | |
| 566 | − return 0; | |
| 567 | −} | |
| 568 | − | |
| 569 | −static void gpio_fan_shutdown(struct platform_device *pdev) | |
| 570 | −{ | |
| 571 | − gpio_fan_remove(pdev); | |
| 572 | 566 | } |
| 573 | 567 | |
| 574 | 568 | #ifdef CONFIG_PM_SLEEP |
| @@ -602,7 +596,6 @@ static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); · 27 unmodified lines | ||
| 602 | 596 | |
| 603 | 597 | static struct platform_driver gpio_fan_driver = { |
| 604 | 598 | .probe = gpio_fan_probe, |
| 605 | − .remove = gpio_fan_remove, | |
| 606 | 599 | .shutdown = gpio_fan_shutdown, |
| 607 | 600 | .driver = { |
| 608 | 601 | .name = "gpio-fan", |
| @@ -465,42 +465,42 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan, · 464 unmodified lines | ||
| 465 | 465 | static int mlxreg_fan_probe(struct platform_device *pdev) |
| 466 | 466 | { |
| 467 | 467 | struct mlxreg_core_platform_data *pdata; |
| 468 | + struct device *dev = &pdev->dev; | |
| 468 | 469 | struct mlxreg_fan *fan; |
| 469 | 470 | struct device *hwm; |
| 470 | 471 | int err; |
| 471 | 472 | |
| 472 | − pdata = dev_get_platdata(&pdev->dev); | |
| 473 | + pdata = dev_get_platdata(dev); | |
| 473 | 474 | if (!pdata) { |
| 474 | − dev_err(&pdev->dev, "Failed to get platform data.\n"); | |
| 475 | + dev_err(dev, "Failed to get platform data.\n"); | |
| 475 | 476 | return -EINVAL; |
| 476 | 477 | } |
| 477 | 478 | |
| 478 | − fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL); | |
| 479 | + fan = devm_kzalloc(dev, sizeof(*fan), GFP_KERNEL); | |
| 479 | 480 | if (!fan) |
| 480 | 481 | return -ENOMEM; |
| 481 | 482 | |
| 482 | − fan->dev = &pdev->dev; | |
| 483 | + fan->dev = dev; | |
| 483 | 484 | fan->regmap = pdata->regmap; |
| 484 | − platform_set_drvdata(pdev, fan); | |
| 485 | 485 | |
| 486 | 486 | err = mlxreg_fan_config(fan, pdata); |
| 487 | 487 | if (err) |
| 488 | 488 | return err; |
| 489 | 489 | |
| 490 | − hwm = devm_hwmon_device_register_with_info(&pdev->dev, "mlxreg_fan", | |
| 490 | + hwm = devm_hwmon_device_register_with_info(dev, "mlxreg_fan", | |
| 491 | 491 | fan, |
| 492 | 492 | &mlxreg_fan_hwmon_chip_info, |
| 493 | 493 | NULL); |
| 494 | 494 | if (IS_ERR(hwm)) { |
| 495 | − dev_err(&pdev->dev, "Failed to register hwmon device\n"); | |
| 495 | + dev_err(dev, "Failed to register hwmon device\n"); | |
| 496 | 496 | return PTR_ERR(hwm); |
| 497 | 497 | } |
| 498 | 498 | |
| 499 | 499 | if (IS_REACHABLE(CONFIG_THERMAL)) { |
| 500 | − fan->cdev = thermal_cooling_device_register("mlxreg_fan", fan, | |
| 501 | − &mlxreg_fan_cooling_ops); | |
| 500 | + fan->cdev = devm_thermal_of_cooling_device_register(dev, | |
| 501 | + NULL, "mlxreg_fan", fan, &mlxreg_fan_cooling_ops); | |
| 502 | 502 | if (IS_ERR(fan->cdev)) { |
| 503 | − dev_err(&pdev->dev, "Failed to register cooling device\n"); | |
| 503 | + dev_err(dev, "Failed to register cooling device\n"); | |
| 504 | 504 | return PTR_ERR(fan->cdev); |
| 505 | 505 | } |
| 506 | 506 | } |
| @@ -508,22 +508,11 @@ static int mlxreg_fan_probe(struct platform_device *pdev) · 1 unmodified line | ||
| 508 | 508 | return 0; |
| 509 | 509 | } |
| 510 | 510 | |
| 511 | −static int mlxreg_fan_remove(struct platform_device *pdev) | |
| 512 | −{ | |
| 513 | − struct mlxreg_fan *fan = platform_get_drvdata(pdev); | |
| 514 | − | |
| 515 | − if (IS_REACHABLE(CONFIG_THERMAL)) | |
| 516 | − thermal_cooling_device_unregister(fan->cdev); | |
| 517 | − | |
| 518 | − return 0; | |
| 519 | −} | |
| 520 | − | |
| 521 | 511 | static struct platform_driver mlxreg_fan_driver = { |
| 522 | 512 | .driver = { |
| 523 | 513 | .name = "mlxreg-fan", |
| 524 | 514 | }, |
| 525 | 515 | .probe = mlxreg_fan_probe, |
| 526 | − .remove = mlxreg_fan_remove, | |
| 527 | 516 | }; |
| 528 | 517 | |
| 529 | 518 | module_platform_driver(mlxreg_fan_driver); |
| @@ -846,10 +846,8 @@ static int npcm7xx_create_pwm_cooling(struct device *dev, · 845 unmodified lines | ||
| 846 | 846 | snprintf(cdev->name, THERMAL_NAME_LENGTH, "%pOFn%d", child, |
| 847 | 847 | pwm_port); |
| 848 | 848 | |
| 849 | − cdev->tcdev = thermal_of_cooling_device_register(child, | |
| 850 | − cdev->name, | |
| 851 | − cdev, | |
| 852 | − &npcm7xx_pwm_cool_ops); | |
| 849 | + cdev->tcdev = devm_thermal_of_cooling_device_register(dev, child, | |
| 850 | + cdev->name, cdev, &npcm7xx_pwm_cool_ops); | |
| 853 | 851 | if (IS_ERR(cdev->tcdev)) |
| 854 | 852 | return PTR_ERR(cdev->tcdev); |
| 855 | 853 | |
| @@ -273,27 +273,40 @@ static int pwm_fan_of_get_cooling_data(struct device *dev, · 272 unmodified lines | ||
| 273 | 273 | return 0; |
| 274 | 274 | } |
| 275 | 275 | |
| 276 | +static void pwm_fan_regulator_disable(void *data) | |
| 277 | +{ | |
| 278 | + regulator_disable(data); | |
| 279 | +} | |
| 280 | + | |
| 281 | +static void pwm_fan_pwm_disable(void *__ctx) | |
| 282 | +{ | |
| 283 | + struct pwm_fan_ctx *ctx = __ctx; | |
| 284 | + pwm_disable(ctx->pwm); | |
| 285 | + del_timer_sync(&ctx->rpm_timer); | |
| 286 | +} | |
| 287 | + | |
| 276 | 288 | static int pwm_fan_probe(struct platform_device *pdev) |
| 277 | 289 | { |
| 278 | 290 | struct thermal_cooling_device *cdev; |
| 291 | + struct device *dev = &pdev->dev; | |
| 279 | 292 | struct pwm_fan_ctx *ctx; |
| 280 | 293 | struct device *hwmon; |
| 281 | 294 | int ret; |
| 282 | 295 | struct pwm_state state = { }; |
| 283 | 296 | u32 ppr = 2; |
| 284 | 297 | |
| 285 | − ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); | |
| 298 | + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); | |
| 286 | 299 | if (!ctx) |
| 287 | 300 | return -ENOMEM; |
| 288 | 301 | |
| 289 | 302 | mutex_init(&ctx->lock); |
| 290 | 303 | |
| 291 | − ctx->pwm = devm_of_pwm_get(&pdev->dev, pdev->dev.of_node, NULL); | |
| 304 | + ctx->pwm = devm_of_pwm_get(dev, dev->of_node, NULL); | |
| 292 | 305 | if (IS_ERR(ctx->pwm)) { |
| 293 | 306 | ret = PTR_ERR(ctx->pwm); |
| 294 | 307 | |
| 295 | 308 | if (ret != -EPROBE_DEFER) |
| 296 | − dev_err(&pdev->dev, "Could not get PWM: %d\n", ret); | |
| 309 | + dev_err(dev, "Could not get PWM: %d\n", ret); | |
| 297 | 310 | |
| 298 | 311 | return ret; |
| 299 | 312 | } |
| @@ -304,7 +317,7 @@ static int pwm_fan_probe(struct platform_device *pdev) · 4 unmodified lines | ||
| 304 | 317 | if (ctx->irq == -EPROBE_DEFER) |
| 305 | 318 | return ctx->irq; |
| 306 | 319 | |
| 307 | − ctx->reg_en = devm_regulator_get_optional(&pdev->dev, "fan"); | |
| 320 | + ctx->reg_en = devm_regulator_get_optional(dev, "fan"); | |
| 308 | 321 | if (IS_ERR(ctx->reg_en)) { |
| 309 | 322 | if (PTR_ERR(ctx->reg_en) != -ENODEV) |
| 310 | 323 | return PTR_ERR(ctx->reg_en); |
| @@ -313,10 +326,11 @@ static int pwm_fan_probe(struct platform_device *pdev) · 2 unmodified lines | ||
| 313 | 326 | } else { |
| 314 | 327 | ret = regulator_enable(ctx->reg_en); |
| 315 | 328 | if (ret) { |
| 316 | − dev_err(&pdev->dev, | |
| 317 | − "Failed to enable fan supply: %d\n", ret); | |
| 329 | + dev_err(dev, "Failed to enable fan supply: %d\n", ret); | |
| 318 | 330 | return ret; |
| 319 | 331 | } |
| 332 | + devm_add_action_or_reset(dev, pwm_fan_regulator_disable, | |
| 333 | + ctx->reg_en); | |
| 320 | 334 | } |
| 321 | 335 | |
| 322 | 336 | ctx->pwm_value = MAX_PWM; |
| @@ -328,91 +342,57 @@ static int pwm_fan_probe(struct platform_device *pdev) · 5 unmodified lines | ||
| 328 | 342 | |
| 329 | 343 | ret = pwm_apply_state(ctx->pwm, &state); |
| 330 | 344 | if (ret) { |
| 331 | − dev_err(&pdev->dev, "Failed to configure PWM: %d\n", ret); | |
| 332 | − goto err_reg_disable; | |
| 345 | + dev_err(dev, "Failed to configure PWM: %d\n", ret); | |
| 346 | + return ret; | |
| 333 | 347 | } |
| 334 | − | |
| 335 | 348 | timer_setup(&ctx->rpm_timer, sample_timer, 0); |
| 349 | + devm_add_action_or_reset(dev, pwm_fan_pwm_disable, ctx); | |
| 336 | 350 | |
| 337 | − of_property_read_u32(pdev->dev.of_node, "pulses-per-revolution", &ppr); | |
| 351 | + of_property_read_u32(dev->of_node, "pulses-per-revolution", &ppr); | |
| 338 | 352 | ctx->pulses_per_revolution = ppr; |
| 339 | 353 | if (!ctx->pulses_per_revolution) { |
| 340 | − dev_err(&pdev->dev, "pulses-per-revolution can't be zero.\n"); | |
| 341 | − ret = -EINVAL; | |
| 342 | − goto err_pwm_disable; | |
| 354 | + dev_err(dev, "pulses-per-revolution can't be zero.\n"); | |
| 355 | + return -EINVAL; | |
| 343 | 356 | } |
| 344 | 357 | |
| 345 | 358 | if (ctx->irq > 0) { |
| 346 | − ret = devm_request_irq(&pdev->dev, ctx->irq, pulse_handler, 0, | |
| 359 | + ret = devm_request_irq(dev, ctx->irq, pulse_handler, 0, | |
| 347 | 360 | pdev->name, ctx); |
| 348 | 361 | if (ret) { |
| 349 | − dev_err(&pdev->dev, | |
| 350 | − "Failed to request interrupt: %d\n", ret); | |
| 351 | − goto err_pwm_disable; | |
| 362 | + dev_err(dev, "Failed to request interrupt: %d\n", ret); | |
| 363 | + return ret; | |
| 352 | 364 | } |
| 353 | 365 | ctx->sample_start = ktime_get(); |
| 354 | 366 | mod_timer(&ctx->rpm_timer, jiffies + HZ); |
| 355 | 367 | } |
| 356 | 368 | |
| 357 | − hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "pwmfan", | |
| 369 | + hwmon = devm_hwmon_device_register_with_groups(dev, "pwmfan", | |
| 358 | 370 | ctx, pwm_fan_groups); |
| 359 | 371 | if (IS_ERR(hwmon)) { |
| 360 | − ret = PTR_ERR(hwmon); | |
| 361 | − dev_err(&pdev->dev, | |
| 362 | − "Failed to register hwmon device: %d\n", ret); | |
| 363 | − goto err_del_timer; | |
| 372 | + dev_err(dev, "Failed to register hwmon device\n"); | |
| 373 | + return PTR_ERR(hwmon); | |
| 364 | 374 | } |
| 365 | 375 | |
| 366 | − ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx); | |
| 376 | + ret = pwm_fan_of_get_cooling_data(dev, ctx); | |
| 367 | 377 | if (ret) |
| 368 | − goto err_del_timer; | |
| 378 | + return ret; | |
| 369 | 379 | |
| 370 | 380 | ctx->pwm_fan_state = ctx->pwm_fan_max_state; |
| 371 | 381 | if (IS_ENABLED(CONFIG_THERMAL)) { |
| 372 | − cdev = thermal_of_cooling_device_register(pdev->dev.of_node, | |
| 373 | − "pwm-fan", ctx, | |
| 374 | − &pwm_fan_cooling_ops); | |
| 382 | + cdev = devm_thermal_of_cooling_device_register(dev, | |
| 383 | + dev->of_node, "pwm-fan", ctx, &pwm_fan_cooling_ops); | |
| 375 | 384 | if (IS_ERR(cdev)) { |
| 376 | 385 | ret = PTR_ERR(cdev); |
| 377 | − dev_err(&pdev->dev, | |
| 386 | + dev_err(dev, | |
| 378 | 387 | "Failed to register pwm-fan as cooling device: %d\n", |
| 379 | 388 | ret); |
| 380 | − goto err_del_timer; | |
| 389 | + return ret; | |
| 381 | 390 | } |
| 382 | 391 | ctx->cdev = cdev; |
| 383 | 392 | thermal_cdev_update(cdev); |
| 384 | 393 | } |
| 385 | 394 | |
| 386 | 395 | return 0; |
| 387 | − | |
| 388 | −err_del_timer: | |
| 389 | − del_timer_sync(&ctx->rpm_timer); | |
| 390 | − | |
| 391 | −err_pwm_disable: | |
| 392 | − state.enabled = false; | |
| 393 | − pwm_apply_state(ctx->pwm, &state); | |
| 394 | − | |
| 395 | −err_reg_disable: | |
| 396 | − if (ctx->reg_en) | |
| 397 | − regulator_disable(ctx->reg_en); | |
| 398 | − | |
| 399 | − return ret; | |
| 400 | −} | |
| 401 | − | |
| 402 | −static int pwm_fan_remove(struct platform_device *pdev) | |
| 403 | −{ | |
| 404 | − struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev); | |
| 405 | − | |
| 406 | − thermal_cooling_device_unregister(ctx->cdev); | |
| 407 | − del_timer_sync(&ctx->rpm_timer); | |
| 408 | − | |
| 409 | − if (ctx->pwm_value) | |
| 410 | − pwm_disable(ctx->pwm); | |
| 411 | − | |
| 412 | − if (ctx->reg_en) | |
| 413 | − regulator_disable(ctx->reg_en); | |
| 414 | − | |
| 415 | − return 0; | |
| 416 | 396 | } |
| 417 | 397 | |
| 418 | 398 | #ifdef CONFIG_PM_SLEEP |
| @@ -480,7 +460,6 @@ MODULE_DEVICE_TABLE(of, of_pwm_fan_match); · 61 unmodified lines | ||
| 480 | 460 | |
| 481 | 461 | static struct platform_driver pwm_fan_driver = { |
| 482 | 462 | .probe = pwm_fan_probe, |
| 483 | − .remove = pwm_fan_remove, | |
| 484 | 463 | .driver = { |
| 485 | 464 | .name = "pwm-fan", |
| 486 | 465 | .pm = &pwm_fan_pm, |
| @@ -200,6 +200,17 @@ config THERMAL_EMULATION · 199 unmodified lines | ||
| 200 | 200 | because userland can easily disable the thermal policy by simply |
| 201 | 201 | flooding this sysfs node with low temperature values. |
| 202 | 202 | |
| 203 | +config THERMAL_MMIO | |
| 204 | + tristate "Generic Thermal MMIO driver" | |
| 205 | + depends on OF || COMPILE_TEST | |
| 206 | + depends on HAS_IOMEM | |
| 207 | + help | |
| 208 | + This option enables the generic thermal MMIO driver that will use | |
| 209 | + memory-mapped reads to get the temperature. Any HW/System that | |
| 210 | + allows temperature reading by a single memory-mapped reading, be it | |
| 211 | + register or shared memory, is a potential candidate to work with this | |
| 212 | + driver. | |
| 213 | + | |
| 203 | 214 | config HISI_THERMAL |
| 204 | 215 | tristate "Hisilicon thermal driver" |
| 205 | 216 | depends on ARCH_HISI || COMPILE_TEST |
| @@ -29,6 +29,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o · 28 unmodified lines | ||
| 29 | 29 | |
| 30 | 30 | # platform thermal drivers |
| 31 | 31 | obj-y += broadcom/ |
| 32 | +obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o | |
| 32 | 33 | obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o |
| 33 | 34 | obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o |
| 34 | 35 | obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o |
| @@ -3,7 +3,6 @@ · 2 unmodified lines | ||
| 3 | 3 | * Copyright (C) 2018 Broadcom |
| 4 | 4 | */ |
| 5 | 5 | |
| 6 | −#include <linux/acpi.h> | |
| 7 | 6 | #include <linux/module.h> |
| 8 | 7 | #include <linux/of_address.h> |
| 9 | 8 | #include <linux/platform_device.h> |
| @@ -100,18 +99,11 @@ static const struct of_device_id sr_thermal_of_match[] = { · 90 unmodified lines | ||
| 100 | 99 | }; |
| 101 | 100 | MODULE_DEVICE_TABLE(of, sr_thermal_of_match); |
| 102 | 101 | |
| 103 | −static const struct acpi_device_id sr_thermal_acpi_ids[] = { | |
| 104 | − { .id = "BRCM0500" }, | |
| 105 | − { /* sentinel */ } | |
| 106 | −}; | |
| 107 | −MODULE_DEVICE_TABLE(acpi, sr_thermal_acpi_ids); | |
| 108 | − | |
| 109 | 102 | static struct platform_driver sr_thermal_driver = { |
| 110 | 103 | .probe = sr_thermal_probe, |
| 111 | 104 | .driver = { |
| 112 | 105 | .name = "sr-thermal", |
| 113 | 106 | .of_match_table = sr_thermal_of_match, |
| 114 | − .acpi_match_table = ACPI_PTR(sr_thermal_acpi_ids), | |
| 115 | 107 | }, |
| 116 | 108 | }; |
| 117 | 109 | module_platform_driver(sr_thermal_driver); |
| @@ -1,26 +1,14 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 1 | 2 | /* |
| 2 | 3 | * linux/drivers/thermal/cpu_cooling.c |
| 3 | 4 | * |
| 4 | 5 | * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) |
| 5 | − * Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org> | |
| 6 | 6 | * |
| 7 | − * Copyright (C) 2014 Viresh Kumar <viresh.kumar@linaro.org> | |
| 7 | + * Copyright (C) 2012-2018 Linaro Limited. | |
| 8 | 8 | * |
| 9 | − * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 10 | − * This program is free software; you can redistribute it and/or modify | |
| 11 | − * it under the terms of the GNU General Public License as published by | |
| 12 | − * the Free Software Foundation; version 2 of the License. | |
| 9 | + * Authors: Amit Daniel <amit.kachhap@linaro.org> | |
| 10 | + * Viresh Kumar <viresh.kumar@linaro.org> | |
| 13 | 11 | * |
| 14 | − * This program is distributed in the hope that it will be useful, but | |
| 15 | − * WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 16 | − * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 17 | − * General Public License for more details. | |
| 18 | − * | |
| 19 | − * You should have received a copy of the GNU General Public License along | |
| 20 | − * with this program; if not, write to the Free Software Foundation, Inc., | |
| 21 | − * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
| 22 | − * | |
| 23 | − * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 24 | 12 | */ |
| 25 | 13 | #include <linux/module.h> |
| 26 | 14 | #include <linux/thermal.h> |
| @@ -99,7 +87,6 @@ struct cpufreq_cooling_device { · 72 unmodified lines | ||
| 99 | 87 | unsigned int clipped_freq; |
| 100 | 88 | unsigned int max_level; |
| 101 | 89 | struct freq_table *freq_table; /* In descending order */ |
| 102 | − struct thermal_cooling_device *cdev; | |
| 103 | 90 | struct cpufreq_policy *policy; |
| 104 | 91 | struct list_head node; |
| 105 | 92 | struct time_in_idle *idle_time; |
| @@ -207,8 +194,7 @@ static int update_freq_table(struct cpufreq_cooling_device *cpufreq_cdev, · 101 unmodified lines | ||
| 207 | 194 | |
| 208 | 195 | dev = get_cpu_device(cpu); |
| 209 | 196 | if (unlikely(!dev)) { |
| 210 | − dev_warn(&cpufreq_cdev->cdev->device, | |
| 211 | − "No cpu device for cpu %d\n", cpu); | |
| 197 | + pr_warn("No cpu device for cpu %d\n", cpu); | |
| 212 | 198 | return -ENODEV; |
| 213 | 199 | } |
| 214 | 200 | |
| @@ -458,7 +444,7 @@ static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev, · 243 unmodified lines | ||
| 458 | 444 | load = 0; |
| 459 | 445 | |
| 460 | 446 | total_load += load; |
| 461 | − if (trace_thermal_power_cpu_limit_enabled() && load_cpu) | |
| 447 | + if (load_cpu) | |
| 462 | 448 | load_cpu[i] = load; |
| 463 | 449 | |
| 464 | 450 | i++; |
| @@ -541,7 +527,6 @@ static int cpufreq_power2state(struct thermal_cooling_device *cdev, · 76 unmodified lines | ||
| 541 | 527 | struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; |
| 542 | 528 | struct cpufreq_policy *policy = cpufreq_cdev->policy; |
| 543 | 529 | |
| 544 | − power = power > 0 ? power : 0; | |
| 545 | 530 | last_load = cpufreq_cdev->last_load ?: 1; |
| 546 | 531 | normalised_power = (power * 100) / last_load; |
| 547 | 532 | target_freq = cpu_power_to_freq(cpufreq_cdev, normalised_power); |
| @@ -692,7 +677,6 @@ __cpufreq_cooling_register(struct device_node *np, · 144 unmodified lines | ||
| 692 | 677 | goto remove_ida; |
| 693 | 678 | |
| 694 | 679 | cpufreq_cdev->clipped_freq = cpufreq_cdev->freq_table[0].frequency; |
| 695 | − cpufreq_cdev->cdev = cdev; | |
| 696 | 680 | |
| 697 | 681 | mutex_lock(&cooling_list_lock); |
| 698 | 682 | /* Register the notifier for first cpufreq cooling device */ |
| @@ -810,7 +794,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) · 111 unmodified lines | ||
| 810 | 794 | cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, |
| 811 | 795 | CPUFREQ_POLICY_NOTIFIER); |
| 812 | 796 | |
| 813 | − thermal_cooling_device_unregister(cpufreq_cdev->cdev); | |
| 797 | + thermal_cooling_device_unregister(cdev); | |
| 814 | 798 | ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); |
| 815 | 799 | kfree(cpufreq_cdev->idle_time); |
| 816 | 800 | kfree(cpufreq_cdev->freq_table); |
| @@ -5,6 +5,9 @@ · 4 unmodified lines | ||
| 5 | 5 | * Copyright (C) 2013 Texas Instruments |
| 6 | 6 | * Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com> |
| 7 | 7 | */ |
| 8 | + | |
| 9 | +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
| 10 | + | |
| 8 | 11 | #include <linux/thermal.h> |
| 9 | 12 | #include <linux/slab.h> |
| 10 | 13 | #include <linux/types.h> |
| @@ -1,3 +1,5 @@ | ||
| 1 | 1 | obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o |
| 2 | −qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-v2.o | |
| 2 | + | |
| 3 | +qcom_tsens-y += tsens.o tsens-common.o tsens-v0_1.o \ | |
| 4 | + tsens-8960.o tsens-v2.o tsens-v1.o | |
| 3 | 5 | obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o |
| @@ -1,105 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0 | |
| 2 | −/* | |
| 3 | − * Copyright (c) 2015, The Linux Foundation. All rights reserved. | |
| 4 | − */ | |
| 5 | − | |
| 6 | −#include <linux/platform_device.h> | |
| 7 | −#include "tsens.h" | |
| 8 | − | |
| 9 | −/* eeprom layout data for 8916 */ | |
| 10 | −#define BASE0_MASK 0x0000007f | |
| 11 | −#define BASE1_MASK 0xfe000000 | |
| 12 | −#define BASE0_SHIFT 0 | |
| 13 | −#define BASE1_SHIFT 25 | |
| 14 | − | |
| 15 | −#define S0_P1_MASK 0x00000f80 | |
| 16 | −#define S1_P1_MASK 0x003e0000 | |
| 17 | −#define S2_P1_MASK 0xf8000000 | |
| 18 | −#define S3_P1_MASK 0x000003e0 | |
| 19 | −#define S4_P1_MASK 0x000f8000 | |
| 20 | − | |
| 21 | −#define S0_P2_MASK 0x0001f000 | |
| 22 | −#define S1_P2_MASK 0x07c00000 | |
| 23 | −#define S2_P2_MASK 0x0000001f | |
| 24 | −#define S3_P2_MASK 0x00007c00 | |
| 25 | −#define S4_P2_MASK 0x01f00000 | |
| 26 | − | |
| 27 | −#define S0_P1_SHIFT 7 | |
| 28 | −#define S1_P1_SHIFT 17 | |
| 29 | −#define S2_P1_SHIFT 27 | |
| 30 | −#define S3_P1_SHIFT 5 | |
| 31 | −#define S4_P1_SHIFT 15 | |
| 32 | − | |
| 33 | −#define S0_P2_SHIFT 12 | |
| 34 | −#define S1_P2_SHIFT 22 | |
| 35 | −#define S2_P2_SHIFT 0 | |
| 36 | −#define S3_P2_SHIFT 10 | |
| 37 | −#define S4_P2_SHIFT 20 | |
| 38 | − | |
| 39 | −#define CAL_SEL_MASK 0xe0000000 | |
| 40 | −#define CAL_SEL_SHIFT 29 | |
| 41 | − | |
| 42 | −static int calibrate_8916(struct tsens_device *tmdev) | |
| 43 | −{ | |
| 44 | − int base0 = 0, base1 = 0, i; | |
| 45 | − u32 p1[5], p2[5]; | |
| 46 | − int mode = 0; | |
| 47 | − u32 *qfprom_cdata, *qfprom_csel; | |
| 48 | − | |
| 49 | − qfprom_cdata = (u32 *)qfprom_read(tmdev->dev, "calib"); | |
| 50 | − if (IS_ERR(qfprom_cdata)) | |
| 51 | − return PTR_ERR(qfprom_cdata); | |
| 52 | − | |
| 53 | − qfprom_csel = (u32 *)qfprom_read(tmdev->dev, "calib_sel"); | |
| 54 | − if (IS_ERR(qfprom_csel)) | |
| 55 | − return PTR_ERR(qfprom_csel); | |
| 56 | − | |
| 57 | − mode = (qfprom_csel[0] & CAL_SEL_MASK) >> CAL_SEL_SHIFT; | |
| 58 | − dev_dbg(tmdev->dev, "calibration mode is %d\n", mode); | |
| 59 | − | |
| 60 | − switch (mode) { | |
| 61 | − case TWO_PT_CALIB: | |
| 62 | − base1 = (qfprom_cdata[1] & BASE1_MASK) >> BASE1_SHIFT; | |
| 63 | − p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT; | |
| 64 | − p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT; | |
| 65 | − p2[2] = (qfprom_cdata[1] & S2_P2_MASK) >> S2_P2_SHIFT; | |
| 66 | − p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT; | |
| 67 | − p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT; | |
| 68 | − for (i = 0; i < tmdev->num_sensors; i++) | |
| 69 | − p2[i] = ((base1 + p2[i]) << 3); | |
| 70 | − /* Fall through */ | |
| 71 | − case ONE_PT_CALIB2: | |
| 72 | − base0 = (qfprom_cdata[0] & BASE0_MASK); | |
| 73 | − p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT; | |
| 74 | − p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT; | |
| 75 | − p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT; | |
| 76 | − p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT; | |
| 77 | − p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT; | |
| 78 | − for (i = 0; i < tmdev->num_sensors; i++) | |
| 79 | − p1[i] = (((base0) + p1[i]) << 3); | |
| 80 | − break; | |
| 81 | − default: | |
| 82 | − for (i = 0; i < tmdev->num_sensors; i++) { | |
| 83 | − p1[i] = 500; | |
| 84 | − p2[i] = 780; | |
| 85 | − } | |
| 86 | − break; | |
| 87 | − } | |
| 88 | − | |
| 89 | − compute_intercept_slope(tmdev, p1, p2, mode); | |
| 90 | − | |
| 91 | − return 0; | |
| 92 | −} | |
| 93 | − | |
| 94 | −static const struct tsens_ops ops_8916 = { | |
| 95 | − .init = init_common, | |
| 96 | − .calibrate = calibrate_8916, | |
| 97 | − .get_temp = get_temp_common, | |
| 98 | −}; | |
| 99 | − | |
| 100 | −const struct tsens_data data_8916 = { | |
| 101 | − .num_sensors = 5, | |
| 102 | − .ops = &ops_8916, | |
| 103 | − .reg_offsets = { [SROT_CTRL_OFFSET] = 0x0 }, | |
| 104 | − .hw_ids = (unsigned int []){0, 1, 2, 4, 5 }, | |
| 105 | −}; | |
| @@ -56,21 +56,21 @@ · 55 unmodified lines | ||
| 56 | 56 | #define TRDY_MASK BIT(7) |
| 57 | 57 | #define TIMEOUT_US 100 |
| 58 | 58 | |
| 59 | −static int suspend_8960(struct tsens_device *tmdev) | |
| 59 | +static int suspend_8960(struct tsens_priv *priv) | |
| 60 | 60 | { |
| 61 | 61 | int ret; |
| 62 | 62 | unsigned int mask; |
| 63 | − struct regmap *map = tmdev->tm_map; | |
| 63 | + struct regmap *map = priv->tm_map; | |
| 64 | 64 | |
| 65 | − ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold); | |
| 65 | + ret = regmap_read(map, THRESHOLD_ADDR, &priv->ctx.threshold); | |
| 66 | 66 | if (ret) |
| 67 | 67 | return ret; |
| 68 | 68 | |
| 69 | − ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control); | |
| 69 | + ret = regmap_read(map, CNTL_ADDR, &priv->ctx.control); | |
| 70 | 70 | if (ret) |
| 71 | 71 | return ret; |
| 72 | 72 | |
| 73 | − if (tmdev->num_sensors > 1) | |
| 73 | + if (priv->num_sensors > 1) | |
| 74 | 74 | mask = SLP_CLK_ENA | EN; |
| 75 | 75 | else |
| 76 | 76 | mask = SLP_CLK_ENA_8660 | EN; |
| @@ -82,10 +82,10 @@ static int suspend_8960(struct tsens_device *tmdev) · 5 unmodified lines | ||
| 82 | 82 | return 0; |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | −static int resume_8960(struct tsens_device *tmdev) | |
| 85 | +static int resume_8960(struct tsens_priv *priv) | |
| 86 | 86 | { |
| 87 | 87 | int ret; |
| 88 | − struct regmap *map = tmdev->tm_map; | |
| 88 | + struct regmap *map = priv->tm_map; | |
| 89 | 89 | |
| 90 | 90 | ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); |
| 91 | 91 | if (ret) |
| @@ -95,80 +95,80 @@ static int resume_8960(struct tsens_device *tmdev) · 3 unmodified lines | ||
| 95 | 95 | * Separate CONFIG restore is not needed only for 8660 as |
| 96 | 96 | * config is part of CTRL Addr and its restored as such |
| 97 | 97 | */ |
| 98 | − if (tmdev->num_sensors > 1) { | |
| 98 | + if (priv->num_sensors > 1) { | |
| 99 | 99 | ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); |
| 100 | 100 | if (ret) |
| 101 | 101 | return ret; |
| 102 | 102 | } |
| 103 | 103 | |
| 104 | − ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold); | |
| 104 | + ret = regmap_write(map, THRESHOLD_ADDR, priv->ctx.threshold); | |
| 105 | 105 | if (ret) |
| 106 | 106 | return ret; |
| 107 | 107 | |
| 108 | − ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control); | |
| 108 | + ret = regmap_write(map, CNTL_ADDR, priv->ctx.control); | |
| 109 | 109 | if (ret) |
| 110 | 110 | return ret; |
| 111 | 111 | |
| 112 | 112 | return 0; |
| 113 | 113 | } |
| 114 | 114 | |
| 115 | −static int enable_8960(struct tsens_device *tmdev, int id) | |
| 115 | +static int enable_8960(struct tsens_priv *priv, int id) | |
| 116 | 116 | { |
| 117 | 117 | int ret; |
| 118 | 118 | u32 reg, mask; |
| 119 | 119 | |
| 120 | − ret = regmap_read(tmdev->tm_map, CNTL_ADDR, ®); | |
| 120 | + ret = regmap_read(priv->tm_map, CNTL_ADDR, ®); | |
| 121 | 121 | if (ret) |
| 122 | 122 | return ret; |
| 123 | 123 | |
| 124 | 124 | mask = BIT(id + SENSOR0_SHIFT); |
| 125 | − ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg | SW_RST); | |
| 125 | + ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST); | |
| 126 | 126 | if (ret) |
| 127 | 127 | return ret; |
| 128 | 128 | |
| 129 | − if (tmdev->num_sensors > 1) | |
| 129 | + if (priv->num_sensors > 1) | |
| 130 | 130 | reg |= mask | SLP_CLK_ENA | EN; |
| 131 | 131 | else |
| 132 | 132 | reg |= mask | SLP_CLK_ENA_8660 | EN; |
| 133 | 133 | |
| 134 | − ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg); | |
| 134 | + ret = regmap_write(priv->tm_map, CNTL_ADDR, reg); | |
| 135 | 135 | if (ret) |
| 136 | 136 | return ret; |
| 137 | 137 | |
| 138 | 138 | return 0; |
| 139 | 139 | } |
| 140 | 140 | |
| 141 | −static void disable_8960(struct tsens_device *tmdev) | |
| 141 | +static void disable_8960(struct tsens_priv *priv) | |
| 142 | 142 | { |
| 143 | 143 | int ret; |
| 144 | 144 | u32 reg_cntl; |
| 145 | 145 | u32 mask; |
| 146 | 146 | |
| 147 | − mask = GENMASK(tmdev->num_sensors - 1, 0); | |
| 147 | + mask = GENMASK(priv->num_sensors - 1, 0); | |
| 148 | 148 | mask <<= SENSOR0_SHIFT; |
| 149 | 149 | mask |= EN; |
| 150 | 150 | |
| 151 | − ret = regmap_read(tmdev->tm_map, CNTL_ADDR, ®_cntl); | |
| 151 | + ret = regmap_read(priv->tm_map, CNTL_ADDR, ®_cntl); | |
| 152 | 152 | if (ret) |
| 153 | 153 | return; |
| 154 | 154 | |
| 155 | 155 | reg_cntl &= ~mask; |
| 156 | 156 | |
| 157 | − if (tmdev->num_sensors > 1) | |
| 157 | + if (priv->num_sensors > 1) | |
| 158 | 158 | reg_cntl &= ~SLP_CLK_ENA; |
| 159 | 159 | else |
| 160 | 160 | reg_cntl &= ~SLP_CLK_ENA_8660; |
| 161 | 161 | |
| 162 | − regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl); | |
| 162 | + regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); | |
| 163 | 163 | } |
| 164 | 164 | |
| 165 | −static int init_8960(struct tsens_device *tmdev) | |
| 165 | +static int init_8960(struct tsens_priv *priv) | |
| 166 | 166 | { |
| 167 | 167 | int ret, i; |
| 168 | 168 | u32 reg_cntl; |
| 169 | 169 | |
| 170 | − tmdev->tm_map = dev_get_regmap(tmdev->dev, NULL); | |
| 171 | − if (!tmdev->tm_map) | |
| 170 | + priv->tm_map = dev_get_regmap(priv->dev, NULL); | |
| 171 | + if (!priv->tm_map) | |
| 172 | 172 | return -ENODEV; |
| 173 | 173 | |
| 174 | 174 | /* |
| @@ -177,21 +177,21 @@ static int init_8960(struct tsens_device *tmdev) · 2 unmodified lines | ||
| 177 | 177 | * but the control registers stay in the same place, i.e |
| 178 | 178 | * directly after the first 5 status registers. |
| 179 | 179 | */ |
| 180 | − for (i = 0; i < tmdev->num_sensors; i++) { | |
| 180 | + for (i = 0; i < priv->num_sensors; i++) { | |
| 181 | 181 | if (i >= 5) |
| 182 | − tmdev->sensor[i].status = S0_STATUS_ADDR + 40; | |
| 183 | − tmdev->sensor[i].status += i * 4; | |
| 182 | + priv->sensor[i].status = S0_STATUS_ADDR + 40; | |
| 183 | + priv->sensor[i].status += i * 4; | |
| 184 | 184 | } |
| 185 | 185 | |
| 186 | 186 | reg_cntl = SW_RST; |
| 187 | − ret = regmap_update_bits(tmdev->tm_map, CNTL_ADDR, SW_RST, reg_cntl); | |
| 187 | + ret = regmap_update_bits(priv->tm_map, CNTL_ADDR, SW_RST, reg_cntl); | |
| 188 | 188 | if (ret) |
| 189 | 189 | return ret; |
| 190 | 190 | |
| 191 | − if (tmdev->num_sensors > 1) { | |
| 191 | + if (priv->num_sensors > 1) { | |
| 192 | 192 | reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); |
| 193 | 193 | reg_cntl &= ~SW_RST; |
| 194 | − ret = regmap_update_bits(tmdev->tm_map, CONFIG_ADDR, | |
| 194 | + ret = regmap_update_bits(priv->tm_map, CONFIG_ADDR, | |
| 195 | 195 | CONFIG_MASK, CONFIG); |
| 196 | 196 | } else { |
| 197 | 197 | reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16); |
| @@ -199,30 +199,30 @@ static int init_8960(struct tsens_device *tmdev) · 1 unmodified line | ||
| 199 | 199 | reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660; |
| 200 | 200 | } |
| 201 | 201 | |
| 202 | − reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT; | |
| 203 | − ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl); | |
| 202 | + reg_cntl |= GENMASK(priv->num_sensors - 1, 0) << SENSOR0_SHIFT; | |
| 203 | + ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); | |
| 204 | 204 | if (ret) |
| 205 | 205 | return ret; |
| 206 | 206 | |
| 207 | 207 | reg_cntl |= EN; |
| 208 | − ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl); | |
| 208 | + ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); | |
| 209 | 209 | if (ret) |
| 210 | 210 | return ret; |
| 211 | 211 | |
| 212 | 212 | return 0; |
| 213 | 213 | } |
| 214 | 214 | |
| 215 | −static int calibrate_8960(struct tsens_device *tmdev) | |
| 215 | +static int calibrate_8960(struct tsens_priv *priv) | |
| 216 | 216 | { |
| 217 | 217 | int i; |
| 218 | 218 | char *data; |
| 219 | 219 | |
| 220 | − ssize_t num_read = tmdev->num_sensors; | |
| 221 | − struct tsens_sensor *s = tmdev->sensor; | |
| 220 | + ssize_t num_read = priv->num_sensors; | |
| 221 | + struct tsens_sensor *s = priv->sensor; | |
| 222 | 222 | |
| 223 | − data = qfprom_read(tmdev->dev, "calib"); | |
| 223 | + data = qfprom_read(priv->dev, "calib"); | |
| 224 | 224 | if (IS_ERR(data)) |
| 225 | − data = qfprom_read(tmdev->dev, "calib_backup"); | |
| 225 | + data = qfprom_read(priv->dev, "calib_backup"); | |
| 226 | 226 | if (IS_ERR(data)) |
| 227 | 227 | return PTR_ERR(data); |
| 228 | 228 | |
| @@ -243,21 +243,21 @@ static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) · 14 unmodified lines | ||
| 243 | 243 | return adc_code * slope + offset; |
| 244 | 244 | } |
| 245 | 245 | |
| 246 | −static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp) | |
| 246 | +static int get_temp_8960(struct tsens_priv *priv, int id, int *temp) | |
| 247 | 247 | { |
| 248 | 248 | int ret; |
| 249 | 249 | u32 code, trdy; |
| 250 | − const struct tsens_sensor *s = &tmdev->sensor[id]; | |
| 250 | + const struct tsens_sensor *s = &priv->sensor[id]; | |
| 251 | 251 | unsigned long timeout; |
| 252 | 252 | |
| 253 | 253 | timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); |
| 254 | 254 | do { |
| 255 | − ret = regmap_read(tmdev->tm_map, INT_STATUS_ADDR, &trdy); | |
| 255 | + ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy); | |
| 256 | 256 | if (ret) |
| 257 | 257 | return ret; |
| 258 | 258 | if (!(trdy & TRDY_MASK)) |
| 259 | 259 | continue; |
| 260 | − ret = regmap_read(tmdev->tm_map, s->status, &code); | |
| 260 | + ret = regmap_read(priv->tm_map, s->status, &code); | |
| 261 | 261 | if (ret) |
| 262 | 262 | return ret; |
| 263 | 263 | *temp = code_to_mdegC(code, s); |
| @@ -277,7 +277,7 @@ static const struct tsens_ops ops_8960 = { · 13 unmodified lines | ||
| 277 | 277 | .resume = resume_8960, |
| 278 | 278 | }; |
| 279 | 279 | |
| 280 | −const struct tsens_data data_8960 = { | |
| 280 | +const struct tsens_plat_data data_8960 = { | |
| 281 | 281 | .num_sensors = 11, |
| 282 | 282 | .ops = &ops_8960, |
| 283 | 283 | }; |
| @@ -12,18 +12,6 @@ · 11 unmodified lines | ||
| 12 | 12 | #include <linux/regmap.h> |
| 13 | 13 | #include "tsens.h" |
| 14 | 14 | |
| 15 | −/* SROT */ | |
| 16 | −#define TSENS_EN BIT(0) | |
| 17 | − | |
| 18 | −/* TM */ | |
| 19 | −#define STATUS_OFFSET 0x30 | |
| 20 | −#define SN_ADDR_OFFSET 0x4 | |
| 21 | −#define SN_ST_TEMP_MASK 0x3ff | |
| 22 | −#define CAL_DEGC_PT1 30 | |
| 23 | −#define CAL_DEGC_PT2 120 | |
| 24 | −#define SLOPE_FACTOR 1000 | |
| 25 | −#define SLOPE_DEFAULT 3200 | |
| 26 | − | |
| 27 | 15 | char *qfprom_read(struct device *dev, const char *cname) |
| 28 | 16 | { |
| 29 | 17 | struct nvmem_cell *cell; |
| @@ -46,18 +34,18 @@ char *qfprom_read(struct device *dev, const char *cname) · 16 unmodified lines | ||
| 46 | 34 | * and offset values are derived from tz->tzp->slope and tz->tzp->offset |
| 47 | 35 | * resp. |
| 48 | 36 | */ |
| 49 | −void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1, | |
| 37 | +void compute_intercept_slope(struct tsens_priv *priv, u32 *p1, | |
| 50 | 38 | u32 *p2, u32 mode) |
| 51 | 39 | { |
| 52 | 40 | int i; |
| 53 | 41 | int num, den; |
| 54 | 42 | |
| 55 | − for (i = 0; i < tmdev->num_sensors; i++) { | |
| 56 | − dev_dbg(tmdev->dev, | |
| 43 | + for (i = 0; i < priv->num_sensors; i++) { | |
| 44 | + dev_dbg(priv->dev, | |
| 57 | 45 | "sensor%d - data_point1:%#x data_point2:%#x\n", |
| 58 | 46 | i, p1[i], p2[i]); |
| 59 | 47 | |
| 60 | − tmdev->sensor[i].slope = SLOPE_DEFAULT; | |
| 48 | + priv->sensor[i].slope = SLOPE_DEFAULT; | |
| 61 | 49 | if (mode == TWO_PT_CALIB) { |
| 62 | 50 | /* |
| 63 | 51 | * slope (m) = adc_code2 - adc_code1 (y2 - y1)/ |
| @@ -66,16 +54,30 @@ void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1, · 2 unmodified lines | ||
| 66 | 54 | num = p2[i] - p1[i]; |
| 67 | 55 | num *= SLOPE_FACTOR; |
| 68 | 56 | den = CAL_DEGC_PT2 - CAL_DEGC_PT1; |
| 69 | − tmdev->sensor[i].slope = num / den; | |
| 57 | + priv->sensor[i].slope = num / den; | |
| 70 | 58 | } |
| 71 | 59 | |
| 72 | − tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) - | |
| 60 | + priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) - | |
| 73 | 61 | (CAL_DEGC_PT1 * |
| 74 | − tmdev->sensor[i].slope); | |
| 75 | − dev_dbg(tmdev->dev, "offset:%d\n", tmdev->sensor[i].offset); | |
| 62 | + priv->sensor[i].slope); | |
| 63 | + dev_dbg(priv->dev, "offset:%d\n", priv->sensor[i].offset); | |
| 76 | 64 | } |
| 77 | 65 | } |
| 78 | 66 | |
| 67 | +bool is_sensor_enabled(struct tsens_priv *priv, u32 hw_id) | |
| 68 | +{ | |
| 69 | + u32 val; | |
| 70 | + int ret; | |
| 71 | + | |
| 72 | + if ((hw_id > (priv->num_sensors - 1)) || (hw_id < 0)) | |
| 73 | + return -EINVAL; | |
| 74 | + ret = regmap_field_read(priv->rf[SENSOR_EN], &val); | |
| 75 | + if (ret) | |
| 76 | + return ret; | |
| 77 | + | |
| 78 | + return val & (1 << hw_id); | |
| 79 | +} | |
| 80 | + | |
| 79 | 81 | static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) |
| 80 | 82 | { |
| 81 | 83 | int degc, num, den; |
| @@ -95,18 +97,54 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) · 13 unmodified lines | ||
| 95 | 97 | return degc; |
| 96 | 98 | } |
| 97 | 99 | |
| 98 | −int get_temp_common(struct tsens_device *tmdev, int id, int *temp) | |
| 100 | +int get_temp_tsens_valid(struct tsens_priv *priv, int i, int *temp) | |
| 99 | 101 | { |
| 100 | − struct tsens_sensor *s = &tmdev->sensor[id]; | |
| 101 | − u32 code; | |
| 102 | − unsigned int status_reg; | |
| 102 | + struct tsens_sensor *s = &priv->sensor[i]; | |
| 103 | + u32 temp_idx = LAST_TEMP_0 + s->hw_id; | |
| 104 | + u32 valid_idx = VALID_0 + s->hw_id; | |
| 105 | + u32 last_temp = 0, valid, mask; | |
| 106 | + int ret; | |
| 107 | + | |
| 108 | + ret = regmap_field_read(priv->rf[valid_idx], &valid); | |
| 109 | + if (ret) | |
| 110 | + return ret; | |
| 111 | + while (!valid) { | |
| 112 | + /* Valid bit is 0 for 6 AHB clock cycles. | |
| 113 | + * At 19.2MHz, 1 AHB clock is ~60ns. | |
| 114 | + * We should enter this loop very, very rarely. | |
| 115 | + */ | |
| 116 | + ndelay(400); | |
| 117 | + ret = regmap_field_read(priv->rf[valid_idx], &valid); | |
| 118 | + if (ret) | |
| 119 | + return ret; | |
| 120 | + } | |
| 121 | + | |
| 122 | + /* Valid bit is set, OK to read the temperature */ | |
| 123 | + ret = regmap_field_read(priv->rf[temp_idx], &last_temp); | |
| 124 | + if (ret) | |
| 125 | + return ret; | |
| 126 | + | |
| 127 | + if (priv->feat->adc) { | |
| 128 | + /* Convert temperature from ADC code to milliCelsius */ | |
| 129 | + *temp = code_to_degc(last_temp, s) * 1000; | |
| 130 | + } else { | |
| 131 | + mask = GENMASK(priv->fields[LAST_TEMP_0].msb, | |
| 132 | + priv->fields[LAST_TEMP_0].lsb); | |
| 133 | + /* Convert temperature from deciCelsius to milliCelsius */ | |
| 134 | + *temp = sign_extend32(last_temp, fls(mask) - 1) * 100; | |
| 135 | + } | |
| 136 | + | |
| 137 | + return 0; | |
| 138 | +} | |
| 139 | + | |
| 140 | +int get_temp_common(struct tsens_priv *priv, int i, int *temp) | |
| 141 | +{ | |
| 142 | + struct tsens_sensor *s = &priv->sensor[i]; | |
| 103 | 143 | int last_temp = 0, ret; |
| 104 | 144 | |
| 105 | − status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * SN_ADDR_OFFSET; | |
| 106 | − ret = regmap_read(tmdev->tm_map, status_reg, &code); | |
| 145 | + ret = regmap_field_read(priv->rf[LAST_TEMP_0 + s->hw_id], &last_temp); | |
| 107 | 146 | if (ret) |
| 108 | 147 | return ret; |
| 109 | − last_temp = code & SN_ST_TEMP_MASK; | |
| 110 | 148 | |
| 111 | 149 | *temp = code_to_degc(last_temp, s) * 1000; |
| 112 | 150 | |
| @@ -127,21 +165,21 @@ static const struct regmap_config tsens_srot_config = { · 14 unmodified lines | ||
| 127 | 165 | .reg_stride = 4, |
| 128 | 166 | }; |
| 129 | 167 | |
| 130 | −int __init init_common(struct tsens_device *tmdev) | |
| 168 | +int __init init_common(struct tsens_priv *priv) | |
| 131 | 169 | { |
| 132 | 170 | void __iomem *tm_base, *srot_base; |
| 171 | + struct device *dev = priv->dev; | |
| 133 | 172 | struct resource *res; |
| 134 | − u32 code; | |
| 135 | − int ret; | |
| 136 | − struct platform_device *op = of_find_device_by_node(tmdev->dev->of_node); | |
| 137 | − u16 ctrl_offset = tmdev->reg_offsets[SROT_CTRL_OFFSET]; | |
| 173 | + u32 enabled; | |
| 174 | + int ret, i, j; | |
| 175 | + struct platform_device *op = of_find_device_by_node(priv->dev->of_node); | |
| 138 | 176 | |
| 139 | 177 | if (!op) |
| 140 | 178 | return -EINVAL; |
| 141 | 179 | |
| 142 | 180 | if (op->num_resources > 1) { |
| 143 | 181 | /* DT with separate SROT and TM address space */ |
| 144 | − tmdev->tm_offset = 0; | |
| 182 | + priv->tm_offset = 0; | |
| 145 | 183 | res = platform_get_resource(op, IORESOURCE_MEM, 1); |
| 146 | 184 | srot_base = devm_ioremap_resource(&op->dev, res); |
| 147 | 185 | if (IS_ERR(srot_base)) { |
| @@ -149,16 +187,15 @@ int __init init_common(struct tsens_device *tmdev) · 1 unmodified line | ||
| 149 | 187 | goto err_put_device; |
| 150 | 188 | } |
| 151 | 189 | |
| 152 | − tmdev->srot_map = devm_regmap_init_mmio(tmdev->dev, srot_base, | |
| 190 | + priv->srot_map = devm_regmap_init_mmio(dev, srot_base, | |
| 153 | 191 | &tsens_srot_config); |
| 154 | − if (IS_ERR(tmdev->srot_map)) { | |
| 155 | − ret = PTR_ERR(tmdev->srot_map); | |
| 192 | + if (IS_ERR(priv->srot_map)) { | |
| 193 | + ret = PTR_ERR(priv->srot_map); | |
| 156 | 194 | goto err_put_device; |
| 157 | 195 | } |
| 158 | − | |
| 159 | 196 | } else { |
| 160 | 197 | /* old DTs where SROT and TM were in a contiguous 2K block */ |
| 161 | − tmdev->tm_offset = 0x1000; | |
| 198 | + priv->tm_offset = 0x1000; | |
| 162 | 199 | } |
| 163 | 200 | |
| 164 | 201 | res = platform_get_resource(op, IORESOURCE_MEM, 0); |
| @@ -168,19 +205,47 @@ int __init init_common(struct tsens_device *tmdev) · 3 unmodified lines | ||
| 168 | 205 | goto err_put_device; |
| 169 | 206 | } |
| 170 | 207 | |
| 171 | − tmdev->tm_map = devm_regmap_init_mmio(tmdev->dev, tm_base, &tsens_config); | |
| 172 | − if (IS_ERR(tmdev->tm_map)) { | |
| 173 | − ret = PTR_ERR(tmdev->tm_map); | |
| 208 | + priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config); | |
| 209 | + if (IS_ERR(priv->tm_map)) { | |
| 210 | + ret = PTR_ERR(priv->tm_map); | |
| 174 | 211 | goto err_put_device; |
| 175 | 212 | } |
| 176 | 213 | |
| 177 | − if (tmdev->srot_map) { | |
| 178 | − ret = regmap_read(tmdev->srot_map, ctrl_offset, &code); | |
| 179 | − if (ret) | |
| 214 | + priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map, | |
| 215 | + priv->fields[TSENS_EN]); | |
| 216 | + if (IS_ERR(priv->rf[TSENS_EN])) { | |
| 217 | + ret = PTR_ERR(priv->rf[TSENS_EN]); | |
| 218 | + goto err_put_device; | |
| 219 | + } | |
| 220 | + ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); | |
| 221 | + if (ret) | |
| 222 | + goto err_put_device; | |
| 223 | + if (!enabled) { | |
| 224 | + dev_err(dev, "tsens device is not enabled\n"); | |
| 225 | + ret = -ENODEV; | |
| 226 | + goto err_put_device; | |
| 227 | + } | |
| 228 | + | |
| 229 | + priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map, | |
| 230 | + priv->fields[SENSOR_EN]); | |
| 231 | + if (IS_ERR(priv->rf[SENSOR_EN])) { | |
| 232 | + ret = PTR_ERR(priv->rf[SENSOR_EN]); | |
| 233 | + goto err_put_device; | |
| 234 | + } | |
| 235 | + /* now alloc regmap_fields in tm_map */ | |
| 236 | + for (i = 0, j = LAST_TEMP_0; i < priv->feat->max_sensors; i++, j++) { | |
| 237 | + priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map, | |
| 238 | + priv->fields[j]); | |
| 239 | + if (IS_ERR(priv->rf[j])) { | |
| 240 | + ret = PTR_ERR(priv->rf[j]); | |
| 180 | 241 | goto err_put_device; |
| 181 | − if (!(code & TSENS_EN)) { | |
| 182 | − dev_err(tmdev->dev, "tsens device is not enabled\n"); | |
| 183 | − ret = -ENODEV; | |
| 242 | + } | |
| 243 | + } | |
| 244 | + for (i = 0, j = VALID_0; i < priv->feat->max_sensors; i++, j++) { | |
| 245 | + priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map, | |
| 246 | + priv->fields[j]); | |
| 247 | + if (IS_ERR(priv->rf[j])) { | |
| 248 | + ret = PTR_ERR(priv->rf[j]); | |
| 184 | 249 | goto err_put_device; |
| 185 | 250 | } |
| 186 | 251 | } |
| @@ -6,6 +6,48 @@ · 5 unmodified lines | ||
| 6 | 6 | #include <linux/platform_device.h> |
| 7 | 7 | #include "tsens.h" |
| 8 | 8 | |
| 9 | +/* ----- SROT ------ */ | |
| 10 | +#define SROT_CTRL_OFF 0x0000 | |
| 11 | + | |
| 12 | +/* ----- TM ------ */ | |
| 13 | +#define TM_INT_EN_OFF 0x0000 | |
| 14 | +#define TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF 0x0004 | |
| 15 | +#define TM_Sn_STATUS_OFF 0x0030 | |
| 16 | +#define TM_TRDY_OFF 0x005c | |
| 17 | + | |
| 18 | +/* eeprom layout data for 8916 */ | |
| 19 | +#define MSM8916_BASE0_MASK 0x0000007f | |
| 20 | +#define MSM8916_BASE1_MASK 0xfe000000 | |
| 21 | +#define MSM8916_BASE0_SHIFT 0 | |
| 22 | +#define MSM8916_BASE1_SHIFT 25 | |
| 23 | + | |
| 24 | +#define MSM8916_S0_P1_MASK 0x00000f80 | |
| 25 | +#define MSM8916_S1_P1_MASK 0x003e0000 | |
| 26 | +#define MSM8916_S2_P1_MASK 0xf8000000 | |
| 27 | +#define MSM8916_S3_P1_MASK 0x000003e0 | |
| 28 | +#define MSM8916_S4_P1_MASK 0x000f8000 | |
| 29 | + | |
| 30 | +#define MSM8916_S0_P2_MASK 0x0001f000 | |
| 31 | +#define MSM8916_S1_P2_MASK 0x07c00000 | |
| 32 | +#define MSM8916_S2_P2_MASK 0x0000001f | |
| 33 | +#define MSM8916_S3_P2_MASK 0x00007c00 | |
| 34 | +#define MSM8916_S4_P2_MASK 0x01f00000 | |
| 35 | + | |
| 36 | +#define MSM8916_S0_P1_SHIFT 7 | |
| 37 | +#define MSM8916_S1_P1_SHIFT 17 | |
| 38 | +#define MSM8916_S2_P1_SHIFT 27 | |
| 39 | +#define MSM8916_S3_P1_SHIFT 5 | |
| 40 | +#define MSM8916_S4_P1_SHIFT 15 | |
| 41 | + | |
| 42 | +#define MSM8916_S0_P2_SHIFT 12 | |
| 43 | +#define MSM8916_S1_P2_SHIFT 22 | |
| 44 | +#define MSM8916_S2_P2_SHIFT 0 | |
| 45 | +#define MSM8916_S3_P2_SHIFT 10 | |
| 46 | +#define MSM8916_S4_P2_SHIFT 20 | |
| 47 | + | |
| 48 | +#define MSM8916_CAL_SEL_MASK 0xe0000000 | |
| 49 | +#define MSM8916_CAL_SEL_SHIFT 29 | |
| 50 | + | |
| 9 | 51 | /* eeprom layout data for 8974 */ |
| 10 | 52 | #define BASE1_MASK 0xff |
| 11 | 53 | #define S0_P1_MASK 0x3f00 |
| @@ -91,7 +133,59 @@ · 79 unmodified lines | ||
| 91 | 133 | |
| 92 | 134 | #define BIT_APPEND 0x3 |
| 93 | 135 | |
| 94 | −static int calibrate_8974(struct tsens_device *tmdev) | |
| 136 | +static int calibrate_8916(struct tsens_priv *priv) | |
| 137 | +{ | |
| 138 | + int base0 = 0, base1 = 0, i; | |
| 139 | + u32 p1[5], p2[5]; | |
| 140 | + int mode = 0; | |
| 141 | + u32 *qfprom_cdata, *qfprom_csel; | |
| 142 | + | |
| 143 | + qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib"); | |
| 144 | + if (IS_ERR(qfprom_cdata)) | |
| 145 | + return PTR_ERR(qfprom_cdata); | |
| 146 | + | |
| 147 | + qfprom_csel = (u32 *)qfprom_read(priv->dev, "calib_sel"); | |
| 148 | + if (IS_ERR(qfprom_csel)) | |
| 149 | + return PTR_ERR(qfprom_csel); | |
| 150 | + | |
| 151 | + mode = (qfprom_csel[0] & MSM8916_CAL_SEL_MASK) >> MSM8916_CAL_SEL_SHIFT; | |
| 152 | + dev_dbg(priv->dev, "calibration mode is %d\n", mode); | |
| 153 | + | |
| 154 | + switch (mode) { | |
| 155 | + case TWO_PT_CALIB: | |
| 156 | + base1 = (qfprom_cdata[1] & MSM8916_BASE1_MASK) >> MSM8916_BASE1_SHIFT; | |
| 157 | + p2[0] = (qfprom_cdata[0] & MSM8916_S0_P2_MASK) >> MSM8916_S0_P2_SHIFT; | |
| 158 | + p2[1] = (qfprom_cdata[0] & MSM8916_S1_P2_MASK) >> MSM8916_S1_P2_SHIFT; | |
| 159 | + p2[2] = (qfprom_cdata[1] & MSM8916_S2_P2_MASK) >> MSM8916_S2_P2_SHIFT; | |
| 160 | + p2[3] = (qfprom_cdata[1] & MSM8916_S3_P2_MASK) >> MSM8916_S3_P2_SHIFT; | |
| 161 | + p2[4] = (qfprom_cdata[1] & MSM8916_S4_P2_MASK) >> MSM8916_S4_P2_SHIFT; | |
| 162 | + for (i = 0; i < priv->num_sensors; i++) | |
| 163 | + p2[i] = ((base1 + p2[i]) << 3); | |
| 164 | + /* Fall through */ | |
| 165 | + case ONE_PT_CALIB2: | |
| 166 | + base0 = (qfprom_cdata[0] & MSM8916_BASE0_MASK); | |
| 167 | + p1[0] = (qfprom_cdata[0] & MSM8916_S0_P1_MASK) >> MSM8916_S0_P1_SHIFT; | |
| 168 | + p1[1] = (qfprom_cdata[0] & MSM8916_S1_P1_MASK) >> MSM8916_S1_P1_SHIFT; | |
| 169 | + p1[2] = (qfprom_cdata[0] & MSM8916_S2_P1_MASK) >> MSM8916_S2_P1_SHIFT; | |
| 170 | + p1[3] = (qfprom_cdata[1] & MSM8916_S3_P1_MASK) >> MSM8916_S3_P1_SHIFT; | |
| 171 | + p1[4] = (qfprom_cdata[1] & MSM8916_S4_P1_MASK) >> MSM8916_S4_P1_SHIFT; | |
| 172 | + for (i = 0; i < priv->num_sensors; i++) | |
| 173 | + p1[i] = (((base0) + p1[i]) << 3); | |
| 174 | + break; | |
| 175 | + default: | |
| 176 | + for (i = 0; i < priv->num_sensors; i++) { | |
| 177 | + p1[i] = 500; | |
| 178 | + p2[i] = 780; | |
| 179 | + } | |
| 180 | + break; | |
| 181 | + } | |
| 182 | + | |
| 183 | + compute_intercept_slope(priv, p1, p2, mode); | |
| 184 | + | |
| 185 | + return 0; | |
| 186 | +} | |
| 187 | + | |
| 188 | +static int calibrate_8974(struct tsens_priv *priv) | |
| 95 | 189 | { |
| 96 | 190 | int base1 = 0, base2 = 0, i; |
| 97 | 191 | u32 p1[11], p2[11]; |
| @@ -99,11 +193,11 @@ static int calibrate_8974(struct tsens_device *tmdev) · 1 unmodified line | ||
| 99 | 193 | u32 *calib, *bkp; |
| 100 | 194 | u32 calib_redun_sel; |
| 101 | 195 | |
| 102 | − calib = (u32 *)qfprom_read(tmdev->dev, "calib"); | |
| 196 | + calib = (u32 *)qfprom_read(priv->dev, "calib"); | |
| 103 | 197 | if (IS_ERR(calib)) |
| 104 | 198 | return PTR_ERR(calib); |
| 105 | 199 | |
| 106 | − bkp = (u32 *)qfprom_read(tmdev->dev, "calib_backup"); | |
| 200 | + bkp = (u32 *)qfprom_read(priv->dev, "calib_backup"); | |
| 107 | 201 | if (IS_ERR(bkp)) |
| 108 | 202 | return PTR_ERR(bkp); |
| 109 | 203 | |
| @@ -184,25 +278,25 @@ static int calibrate_8974(struct tsens_device *tmdev) · 74 unmodified lines | ||
| 184 | 278 | |
| 185 | 279 | switch (mode) { |
| 186 | 280 | case ONE_PT_CALIB: |
| 187 | − for (i = 0; i < tmdev->num_sensors; i++) | |
| 281 | + for (i = 0; i < priv->num_sensors; i++) | |
| 188 | 282 | p1[i] += (base1 << 2) | BIT_APPEND; |
| 189 | 283 | break; |
| 190 | 284 | case TWO_PT_CALIB: |
| 191 | − for (i = 0; i < tmdev->num_sensors; i++) { | |
| 285 | + for (i = 0; i < priv->num_sensors; i++) { | |
| 192 | 286 | p2[i] += base2; |
| 193 | 287 | p2[i] <<= 2; |
| 194 | 288 | p2[i] |= BIT_APPEND; |
| 195 | 289 | } |
| 196 | 290 | /* Fall through */ |
| 197 | 291 | case ONE_PT_CALIB2: |
| 198 | − for (i = 0; i < tmdev->num_sensors; i++) { | |
| 292 | + for (i = 0; i < priv->num_sensors; i++) { | |
| 199 | 293 | p1[i] += base1; |
| 200 | 294 | p1[i] <<= 2; |
| 201 | 295 | p1[i] |= BIT_APPEND; |
| 202 | 296 | } |
| 203 | 297 | break; |
| 204 | 298 | default: |
| 205 | − for (i = 0; i < tmdev->num_sensors; i++) | |
| 299 | + for (i = 0; i < priv->num_sensors; i++) | |
| 206 | 300 | p2[i] = 780; |
| 207 | 301 | p1[0] = 502; |
| 208 | 302 | p1[1] = 509; |
| @@ -218,19 +312,71 @@ static int calibrate_8974(struct tsens_device *tmdev) · 9 unmodified lines | ||
| 218 | 312 | break; |
| 219 | 313 | } |
| 220 | 314 | |
| 221 | − compute_intercept_slope(tmdev, p1, p2, mode); | |
| 315 | + compute_intercept_slope(priv, p1, p2, mode); | |
| 222 | 316 | |
| 223 | 317 | return 0; |
| 224 | 318 | } |
| 225 | 319 | |
| 320 | +/* v0.1: 8916, 8974 */ | |
| 321 | + | |
| 322 | +static const struct tsens_features tsens_v0_1_feat = { | |
| 323 | + .ver_major = VER_0_1, | |
| 324 | + .crit_int = 0, | |
| 325 | + .adc = 1, | |
| 326 | + .srot_split = 1, | |
| 327 | + .max_sensors = 11, | |
| 328 | +}; | |
| 329 | + | |
| 330 | +static const struct reg_field tsens_v0_1_regfields[MAX_REGFIELDS] = { | |
| 331 | + /* ----- SROT ------ */ | |
| 332 | + /* No VERSION information */ | |
| 333 | + | |
| 334 | + /* CTRL_OFFSET */ | |
| 335 | + [TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0), | |
| 336 | + [TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1), | |
| 337 | + [SENSOR_EN] = REG_FIELD(SROT_CTRL_OFF, 3, 13), | |
| 338 | + | |
| 339 | + /* ----- TM ------ */ | |
| 340 | + /* INTERRUPT ENABLE */ | |
| 341 | + [INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 0), | |
| 342 | + | |
| 343 | + /* Sn_STATUS */ | |
| 344 | + REG_FIELD_FOR_EACH_SENSOR11(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 9), | |
| 345 | + /* No VALID field on v0.1 */ | |
| 346 | + REG_FIELD_FOR_EACH_SENSOR11(MIN_STATUS, TM_Sn_STATUS_OFF, 10, 10), | |
| 347 | + REG_FIELD_FOR_EACH_SENSOR11(LOWER_STATUS, TM_Sn_STATUS_OFF, 11, 11), | |
| 348 | + REG_FIELD_FOR_EACH_SENSOR11(UPPER_STATUS, TM_Sn_STATUS_OFF, 12, 12), | |
| 349 | + /* No CRITICAL field on v0.1 */ | |
| 350 | + REG_FIELD_FOR_EACH_SENSOR11(MAX_STATUS, TM_Sn_STATUS_OFF, 13, 13), | |
| 351 | + | |
| 352 | + /* TRDY: 1=ready, 0=in progress */ | |
| 353 | + [TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0), | |
| 354 | +}; | |
| 355 | + | |
| 356 | +static const struct tsens_ops ops_8916 = { | |
| 357 | + .init = init_common, | |
| 358 | + .calibrate = calibrate_8916, | |
| 359 | + .get_temp = get_temp_common, | |
| 360 | +}; | |
| 361 | + | |
| 362 | +const struct tsens_plat_data data_8916 = { | |
| 363 | + .num_sensors = 5, | |
| 364 | + .ops = &ops_8916, | |
| 365 | + .hw_ids = (unsigned int []){0, 1, 2, 4, 5 }, | |
| 366 | + | |
| 367 | + .feat = &tsens_v0_1_feat, | |
| 368 | + .fields = tsens_v0_1_regfields, | |
| 369 | +}; | |
| 370 | + | |
| 226 | 371 | static const struct tsens_ops ops_8974 = { |
| 227 | 372 | .init = init_common, |
| 228 | 373 | .calibrate = calibrate_8974, |
| 229 | 374 | .get_temp = get_temp_common, |
| 230 | 375 | }; |
| 231 | 376 | |
| 232 | −const struct tsens_data data_8974 = { | |
| 377 | +const struct tsens_plat_data data_8974 = { | |
| 233 | 378 | .num_sensors = 11, |
| 234 | 379 | .ops = &ops_8974, |
| 235 | − .reg_offsets = { [SROT_CTRL_OFFSET] = 0x0 }, | |
| 380 | + .feat = &tsens_v0_1_feat, | |
| 381 | + .fields = tsens_v0_1_regfields, | |
| 236 | 382 | }; |
| @@ -0,0 +1,193 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * Copyright (c) 2019, Linaro Limited | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/bitops.h> | |
| 7 | +#include <linux/regmap.h> | |
| 8 | +#include <linux/delay.h> | |
| 9 | +#include "tsens.h" | |
| 10 | + | |
| 11 | +/* ----- SROT ------ */ | |
| 12 | +#define SROT_HW_VER_OFF 0x0000 | |
| 13 | +#define SROT_CTRL_OFF 0x0004 | |
| 14 | + | |
| 15 | +/* ----- TM ------ */ | |
| 16 | +#define TM_INT_EN_OFF 0x0000 | |
| 17 | +#define TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF 0x0004 | |
| 18 | +#define TM_Sn_STATUS_OFF 0x0044 | |
| 19 | +#define TM_TRDY_OFF 0x0084 | |
| 20 | + | |
| 21 | +/* eeprom layout data for qcs404/405 (v1) */ | |
| 22 | +#define BASE0_MASK 0x000007f8 | |
| 23 | +#define BASE1_MASK 0x0007f800 | |
| 24 | +#define BASE0_SHIFT 3 | |
| 25 | +#define BASE1_SHIFT 11 | |
| 26 | + | |
| 27 | +#define S0_P1_MASK 0x0000003f | |
| 28 | +#define S1_P1_MASK 0x0003f000 | |
| 29 | +#define S2_P1_MASK 0x3f000000 | |
| 30 | +#define S3_P1_MASK 0x000003f0 | |
| 31 | +#define S4_P1_MASK 0x003f0000 | |
| 32 | +#define S5_P1_MASK 0x0000003f | |
| 33 | +#define S6_P1_MASK 0x0003f000 | |
| 34 | +#define S7_P1_MASK 0x3f000000 | |
| 35 | +#define S8_P1_MASK 0x000003f0 | |
| 36 | +#define S9_P1_MASK 0x003f0000 | |
| 37 | + | |
| 38 | +#define S0_P2_MASK 0x00000fc0 | |
| 39 | +#define S1_P2_MASK 0x00fc0000 | |
| 40 | +#define S2_P2_MASK_1_0 0xc0000000 | |
| 41 | +#define S2_P2_MASK_5_2 0x0000000f | |
| 42 | +#define S3_P2_MASK 0x0000fc00 | |
| 43 | +#define S4_P2_MASK 0x0fc00000 | |
| 44 | +#define S5_P2_MASK 0x00000fc0 | |
| 45 | +#define S6_P2_MASK 0x00fc0000 | |
| 46 | +#define S7_P2_MASK_1_0 0xc0000000 | |
| 47 | +#define S7_P2_MASK_5_2 0x0000000f | |
| 48 | +#define S8_P2_MASK 0x0000fc00 | |
| 49 | +#define S9_P2_MASK 0x0fc00000 | |
| 50 | + | |
| 51 | +#define S0_P1_SHIFT 0 | |
| 52 | +#define S0_P2_SHIFT 6 | |
| 53 | +#define S1_P1_SHIFT 12 | |
| 54 | +#define S1_P2_SHIFT 18 | |
| 55 | +#define S2_P1_SHIFT 24 | |
| 56 | +#define S2_P2_SHIFT_1_0 30 | |
| 57 | + | |
| 58 | +#define S2_P2_SHIFT_5_2 0 | |
| 59 | +#define S3_P1_SHIFT 4 | |
| 60 | +#define S3_P2_SHIFT 10 | |
| 61 | +#define S4_P1_SHIFT 16 | |
| 62 | +#define S4_P2_SHIFT 22 | |
| 63 | + | |
| 64 | +#define S5_P1_SHIFT 0 | |
| 65 | +#define S5_P2_SHIFT 6 | |
| 66 | +#define S6_P1_SHIFT 12 | |
| 67 | +#define S6_P2_SHIFT 18 | |
| 68 | +#define S7_P1_SHIFT 24 | |
| 69 | +#define S7_P2_SHIFT_1_0 30 | |
| 70 | + | |
| 71 | +#define S7_P2_SHIFT_5_2 0 | |
| 72 | +#define S8_P1_SHIFT 4 | |
| 73 | +#define S8_P2_SHIFT 10 | |
| 74 | +#define S9_P1_SHIFT 16 | |
| 75 | +#define S9_P2_SHIFT 22 | |
| 76 | + | |
| 77 | +#define CAL_SEL_MASK 7 | |
| 78 | +#define CAL_SEL_SHIFT 0 | |
| 79 | + | |
| 80 | +static int calibrate_v1(struct tsens_priv *priv) | |
| 81 | +{ | |
| 82 | + u32 base0 = 0, base1 = 0; | |
| 83 | + u32 p1[10], p2[10]; | |
| 84 | + u32 mode = 0, lsb = 0, msb = 0; | |
| 85 | + u32 *qfprom_cdata; | |
| 86 | + int i; | |
| 87 | + | |
| 88 | + qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib"); | |
| 89 | + if (IS_ERR(qfprom_cdata)) | |
| 90 | + return PTR_ERR(qfprom_cdata); | |
| 91 | + | |
| 92 | + mode = (qfprom_cdata[4] & CAL_SEL_MASK) >> CAL_SEL_SHIFT; | |
| 93 | + dev_dbg(priv->dev, "calibration mode is %d\n", mode); | |
| 94 | + | |
| 95 | + switch (mode) { | |
| 96 | + case TWO_PT_CALIB: | |
| 97 | + base1 = (qfprom_cdata[4] & BASE1_MASK) >> BASE1_SHIFT; | |
| 98 | + p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT; | |
| 99 | + p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT; | |
| 100 | + /* This value is split over two registers, 2 bits and 4 bits */ | |
| 101 | + lsb = (qfprom_cdata[0] & S2_P2_MASK_1_0) >> S2_P2_SHIFT_1_0; | |
| 102 | + msb = (qfprom_cdata[1] & S2_P2_MASK_5_2) >> S2_P2_SHIFT_5_2; | |
| 103 | + p2[2] = msb << 2 | lsb; | |
| 104 | + p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT; | |
| 105 | + p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT; | |
| 106 | + p2[5] = (qfprom_cdata[2] & S5_P2_MASK) >> S5_P2_SHIFT; | |
| 107 | + p2[6] = (qfprom_cdata[2] & S6_P2_MASK) >> S6_P2_SHIFT; | |
| 108 | + /* This value is split over two registers, 2 bits and 4 bits */ | |
| 109 | + lsb = (qfprom_cdata[2] & S7_P2_MASK_1_0) >> S7_P2_SHIFT_1_0; | |
| 110 | + msb = (qfprom_cdata[3] & S7_P2_MASK_5_2) >> S7_P2_SHIFT_5_2; | |
| 111 | + p2[7] = msb << 2 | lsb; | |
| 112 | + p2[8] = (qfprom_cdata[3] & S8_P2_MASK) >> S8_P2_SHIFT; | |
| 113 | + p2[9] = (qfprom_cdata[3] & S9_P2_MASK) >> S9_P2_SHIFT; | |
| 114 | + for (i = 0; i < priv->num_sensors; i++) | |
| 115 | + p2[i] = ((base1 + p2[i]) << 2); | |
| 116 | + /* Fall through */ | |
| 117 | + case ONE_PT_CALIB2: | |
| 118 | + base0 = (qfprom_cdata[4] & BASE0_MASK) >> BASE0_SHIFT; | |
| 119 | + p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT; | |
| 120 | + p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT; | |
| 121 | + p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT; | |
| 122 | + p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT; | |
| 123 | + p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT; | |
| 124 | + p1[5] = (qfprom_cdata[2] & S5_P1_MASK) >> S5_P1_SHIFT; | |
| 125 | + p1[6] = (qfprom_cdata[2] & S6_P1_MASK) >> S6_P1_SHIFT; | |
| 126 | + p1[7] = (qfprom_cdata[2] & S7_P1_MASK) >> S7_P1_SHIFT; | |
| 127 | + p1[8] = (qfprom_cdata[3] & S8_P1_MASK) >> S8_P1_SHIFT; | |
| 128 | + p1[9] = (qfprom_cdata[3] & S9_P1_MASK) >> S9_P1_SHIFT; | |
| 129 | + for (i = 0; i < priv->num_sensors; i++) | |
| 130 | + p1[i] = (((base0) + p1[i]) << 2); | |
| 131 | + break; | |
| 132 | + default: | |
| 133 | + for (i = 0; i < priv->num_sensors; i++) { | |
| 134 | + p1[i] = 500; | |
| 135 | + p2[i] = 780; | |
| 136 | + } | |
| 137 | + break; | |
| 138 | + } | |
| 139 | + | |
| 140 | + compute_intercept_slope(priv, p1, p2, mode); | |
| 141 | + | |
| 142 | + return 0; | |
| 143 | +} | |
| 144 | + | |
| 145 | +/* v1.x: qcs404,405 */ | |
| 146 | + | |
| 147 | +static const struct tsens_features tsens_v1_feat = { | |
| 148 | + .ver_major = VER_1_X, | |
| 149 | + .crit_int = 0, | |
| 150 | + .adc = 1, | |
| 151 | + .srot_split = 1, | |
| 152 | + .max_sensors = 11, | |
| 153 | +}; | |
| 154 | + | |
| 155 | +static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = { | |
| 156 | + /* ----- SROT ------ */ | |
| 157 | + /* VERSION */ | |
| 158 | + [VER_MAJOR] = REG_FIELD(SROT_HW_VER_OFF, 28, 31), | |
| 159 | + [VER_MINOR] = REG_FIELD(SROT_HW_VER_OFF, 16, 27), | |
| 160 | + [VER_STEP] = REG_FIELD(SROT_HW_VER_OFF, 0, 15), | |
| 161 | + /* CTRL_OFFSET */ | |
| 162 | + [TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0), | |
| 163 | + [TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1), | |
| 164 | + [SENSOR_EN] = REG_FIELD(SROT_CTRL_OFF, 3, 13), | |
| 165 | + | |
| 166 | + /* ----- TM ------ */ | |
| 167 | + /* INTERRUPT ENABLE */ | |
| 168 | + [INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 0), | |
| 169 | + | |
| 170 | + /* Sn_STATUS */ | |
| 171 | + REG_FIELD_FOR_EACH_SENSOR11(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 9), | |
| 172 | + REG_FIELD_FOR_EACH_SENSOR11(VALID, TM_Sn_STATUS_OFF, 14, 14), | |
| 173 | + REG_FIELD_FOR_EACH_SENSOR11(MIN_STATUS, TM_Sn_STATUS_OFF, 10, 10), | |
| 174 | + REG_FIELD_FOR_EACH_SENSOR11(LOWER_STATUS, TM_Sn_STATUS_OFF, 11, 11), | |
| 175 | + REG_FIELD_FOR_EACH_SENSOR11(UPPER_STATUS, TM_Sn_STATUS_OFF, 12, 12), | |
| 176 | + /* No CRITICAL field on v1.x */ | |
| 177 | + REG_FIELD_FOR_EACH_SENSOR11(MAX_STATUS, TM_Sn_STATUS_OFF, 13, 13), | |
| 178 | + | |
| 179 | + /* TRDY: 1=ready, 0=in progress */ | |
| 180 | + [TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0), | |
| 181 | +}; | |
| 182 | + | |
| 183 | +static const struct tsens_ops ops_generic_v1 = { | |
| 184 | + .init = init_common, | |
| 185 | + .calibrate = calibrate_v1, | |
| 186 | + .get_temp = get_temp_tsens_valid, | |
| 187 | +}; | |
| 188 | + | |
| 189 | +const struct tsens_plat_data data_tsens_v1 = { | |
| 190 | + .ops = &ops_generic_v1, | |
| 191 | + .feat = &tsens_v1_feat, | |
| 192 | + .fields = tsens_v1_regfields, | |
| 193 | +}; | |
| @@ -4,76 +4,81 @@ · 3 unmodified lines | ||
| 4 | 4 | * Copyright (c) 2018, Linaro Limited |
| 5 | 5 | */ |
| 6 | 6 | |
| 7 | −#include <linux/regmap.h> | |
| 8 | 7 | #include <linux/bitops.h> |
| 8 | +#include <linux/regmap.h> | |
| 9 | 9 | #include "tsens.h" |
| 10 | 10 | |
| 11 | −#define STATUS_OFFSET 0xa0 | |
| 12 | −#define LAST_TEMP_MASK 0xfff | |
| 13 | −#define STATUS_VALID_BIT BIT(21) | |
| 11 | +/* ----- SROT ------ */ | |
| 12 | +#define SROT_HW_VER_OFF 0x0000 | |
| 13 | +#define SROT_CTRL_OFF 0x0004 | |
| 14 | + | |
| 15 | +/* ----- TM ------ */ | |
| 16 | +#define TM_INT_EN_OFF 0x0004 | |
| 17 | +#define TM_UPPER_LOWER_INT_STATUS_OFF 0x0008 | |
| 18 | +#define TM_UPPER_LOWER_INT_CLEAR_OFF 0x000c | |
| 19 | +#define TM_UPPER_LOWER_INT_MASK_OFF 0x0010 | |
| 20 | +#define TM_CRITICAL_INT_STATUS_OFF 0x0014 | |
| 21 | +#define TM_CRITICAL_INT_CLEAR_OFF 0x0018 | |
| 22 | +#define TM_CRITICAL_INT_MASK_OFF 0x001c | |
| 23 | +#define TM_Sn_UPPER_LOWER_THRESHOLD_OFF 0x0020 | |
| 24 | +#define TM_Sn_CRITICAL_THRESHOLD_OFF 0x0060 | |
| 25 | +#define TM_Sn_STATUS_OFF 0x00a0 | |
| 26 | +#define TM_TRDY_OFF 0x00e4 | |
| 14 | 27 | |
| 15 | −static int get_temp_tsens_v2(struct tsens_device *tmdev, int id, int *temp) | |
| 16 | −{ | |
| 17 | − struct tsens_sensor *s = &tmdev->sensor[id]; | |
| 18 | − u32 code; | |
| 19 | − unsigned int status_reg; | |
| 20 | − u32 last_temp = 0, last_temp2 = 0, last_temp3 = 0; | |
| 21 | − int ret; | |
| 28 | +/* v2.x: 8996, 8998, sdm845 */ | |
| 22 | 29 | |
| 23 | − status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * 4; | |
| 24 | − ret = regmap_read(tmdev->tm_map, status_reg, &code); | |
| 25 | − if (ret) | |
| 26 | − return ret; | |
| 27 | − last_temp = code & LAST_TEMP_MASK; | |
| 28 | − if (code & STATUS_VALID_BIT) | |
| 29 | − goto done; | |
| 30 | +static const struct tsens_features tsens_v2_feat = { | |
| 31 | + .ver_major = VER_2_X, | |
| 32 | + .crit_int = 1, | |
| 33 | + .adc = 0, | |
| 34 | + .srot_split = 1, | |
| 35 | + .max_sensors = 16, | |
| 36 | +}; | |
| 30 | 37 | |
| 31 | − /* Try a second time */ | |
| 32 | − ret = regmap_read(tmdev->tm_map, status_reg, &code); | |
| 33 | − if (ret) | |
| 34 | − return ret; | |
| 35 | − if (code & STATUS_VALID_BIT) { | |
| 36 | − last_temp = code & LAST_TEMP_MASK; | |
| 37 | − goto done; | |
| 38 | − } else { | |
| 39 | − last_temp2 = code & LAST_TEMP_MASK; | |
| 40 | − } | |
| 38 | +static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { | |
| 39 | + /* ----- SROT ------ */ | |
| 40 | + /* VERSION */ | |
| 41 | + [VER_MAJOR] = REG_FIELD(SROT_HW_VER_OFF, 28, 31), | |
| 42 | + [VER_MINOR] = REG_FIELD(SROT_HW_VER_OFF, 16, 27), | |
| 43 | + [VER_STEP] = REG_FIELD(SROT_HW_VER_OFF, 0, 15), | |
| 44 | + /* CTRL_OFF */ | |
| 45 | + [TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0), | |
| 46 | + [TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1), | |
| 47 | + [SENSOR_EN] = REG_FIELD(SROT_CTRL_OFF, 3, 18), | |
| 41 | 48 | |
| 42 | − /* Try a third/last time */ | |
| 43 | − ret = regmap_read(tmdev->tm_map, status_reg, &code); | |
| 44 | − if (ret) | |
| 45 | − return ret; | |
| 46 | − if (code & STATUS_VALID_BIT) { | |
| 47 | − last_temp = code & LAST_TEMP_MASK; | |
| 48 | − goto done; | |
| 49 | − } else { | |
| 50 | − last_temp3 = code & LAST_TEMP_MASK; | |
| 51 | − } | |
| 49 | + /* ----- TM ------ */ | |
| 50 | + /* INTERRUPT ENABLE */ | |
| 51 | + /* v2 has separate enables for UPPER/LOWER/CRITICAL interrupts */ | |
| 52 | + [INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 2), | |
| 52 | 53 | |
| 53 | − if (last_temp == last_temp2) | |
| 54 | − last_temp = last_temp2; | |
| 55 | − else if (last_temp2 == last_temp3) | |
| 56 | − last_temp = last_temp3; | |
| 57 | −done: | |
| 58 | − /* Convert temperature from deciCelsius to milliCelsius */ | |
| 59 | − *temp = sign_extend32(last_temp, fls(LAST_TEMP_MASK) - 1) * 100; | |
| 54 | + /* Sn_STATUS */ | |
| 55 | + REG_FIELD_FOR_EACH_SENSOR16(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 11), | |
| 56 | + REG_FIELD_FOR_EACH_SENSOR16(VALID, TM_Sn_STATUS_OFF, 21, 21), | |
| 57 | + REG_FIELD_FOR_EACH_SENSOR16(MIN_STATUS, TM_Sn_STATUS_OFF, 16, 16), | |
| 58 | + REG_FIELD_FOR_EACH_SENSOR16(LOWER_STATUS, TM_Sn_STATUS_OFF, 17, 17), | |
| 59 | + REG_FIELD_FOR_EACH_SENSOR16(UPPER_STATUS, TM_Sn_STATUS_OFF, 18, 18), | |
| 60 | + REG_FIELD_FOR_EACH_SENSOR16(CRITICAL_STATUS, TM_Sn_STATUS_OFF, 19, 19), | |
| 61 | + REG_FIELD_FOR_EACH_SENSOR16(MAX_STATUS, TM_Sn_STATUS_OFF, 20, 20), | |
| 60 | 62 | |
| 61 | − return 0; | |
| 62 | −} | |
| 63 | + /* TRDY: 1=ready, 0=in progress */ | |
| 64 | + [TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0), | |
| 65 | +}; | |
| 63 | 66 | |
| 64 | 67 | static const struct tsens_ops ops_generic_v2 = { |
| 65 | 68 | .init = init_common, |
| 66 | − .get_temp = get_temp_tsens_v2, | |
| 69 | + .get_temp = get_temp_tsens_valid, | |
| 67 | 70 | }; |
| 68 | 71 | |
| 69 | −const struct tsens_data data_tsens_v2 = { | |
| 70 | − .ops = &ops_generic_v2, | |
| 71 | − .reg_offsets = { [SROT_CTRL_OFFSET] = 0x4 }, | |
| 72 | +const struct tsens_plat_data data_tsens_v2 = { | |
| 73 | + .ops = &ops_generic_v2, | |
| 74 | + .feat = &tsens_v2_feat, | |
| 75 | + .fields = tsens_v2_regfields, | |
| 72 | 76 | }; |
| 73 | 77 | |
| 74 | 78 | /* Kept around for backward compatibility with old msm8996.dtsi */ |
| 75 | −const struct tsens_data data_8996 = { | |
| 79 | +const struct tsens_plat_data data_8996 = { | |
| 76 | 80 | .num_sensors = 13, |
| 77 | 81 | .ops = &ops_generic_v2, |
| 78 | − .reg_offsets = { [SROT_CTRL_OFFSET] = 0x4 }, | |
| 82 | + .feat = &tsens_v2_feat, | |
| 83 | + .fields = tsens_v2_regfields, | |
| 79 | 84 | }; |
| @@ -15,38 +15,38 @@ · 14 unmodified lines | ||
| 15 | 15 | static int tsens_get_temp(void *data, int *temp) |
| 16 | 16 | { |
| 17 | 17 | const struct tsens_sensor *s = data; |
| 18 | − struct tsens_device *tmdev = s->tmdev; | |
| 18 | + struct tsens_priv *priv = s->priv; | |
| 19 | 19 | |
| 20 | − return tmdev->ops->get_temp(tmdev, s->id, temp); | |
| 20 | + return priv->ops->get_temp(priv, s->id, temp); | |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | −static int tsens_get_trend(void *p, int trip, enum thermal_trend *trend) | |
| 23 | +static int tsens_get_trend(void *data, int trip, enum thermal_trend *trend) | |
| 24 | 24 | { |
| 25 | − const struct tsens_sensor *s = p; | |
| 26 | − struct tsens_device *tmdev = s->tmdev; | |
| 25 | + const struct tsens_sensor *s = data; | |
| 26 | + struct tsens_priv *priv = s->priv; | |
| 27 | 27 | |
| 28 | − if (tmdev->ops->get_trend) | |
| 29 | − return tmdev->ops->get_trend(tmdev, s->id, trend); | |
| 28 | + if (priv->ops->get_trend) | |
| 29 | + return priv->ops->get_trend(priv, s->id, trend); | |
| 30 | 30 | |
| 31 | 31 | return -ENOTSUPP; |
| 32 | 32 | } |
| 33 | 33 | |
| 34 | 34 | static int __maybe_unused tsens_suspend(struct device *dev) |
| 35 | 35 | { |
| 36 | − struct tsens_device *tmdev = dev_get_drvdata(dev); | |
| 36 | + struct tsens_priv *priv = dev_get_drvdata(dev); | |
| 37 | 37 | |
| 38 | − if (tmdev->ops && tmdev->ops->suspend) | |
| 39 | − return tmdev->ops->suspend(tmdev); | |
| 38 | + if (priv->ops && priv->ops->suspend) | |
| 39 | + return priv->ops->suspend(priv); | |
| 40 | 40 | |
| 41 | 41 | return 0; |
| 42 | 42 | } |
| 43 | 43 | |
| 44 | 44 | static int __maybe_unused tsens_resume(struct device *dev) |
| 45 | 45 | { |
| 46 | − struct tsens_device *tmdev = dev_get_drvdata(dev); | |
| 46 | + struct tsens_priv *priv = dev_get_drvdata(dev); | |
| 47 | 47 | |
| 48 | − if (tmdev->ops && tmdev->ops->resume) | |
| 49 | − return tmdev->ops->resume(tmdev); | |
| 48 | + if (priv->ops && priv->ops->resume) | |
| 49 | + return priv->ops->resume(priv); | |
| 50 | 50 | |
| 51 | 51 | return 0; |
| 52 | 52 | } |
| @@ -63,6 +63,9 @@ static const struct of_device_id tsens_table[] = { · 10 unmodified lines | ||
| 63 | 63 | }, { |
| 64 | 64 | .compatible = "qcom,msm8996-tsens", |
| 65 | 65 | .data = &data_8996, |
| 66 | + }, { | |
| 67 | + .compatible = "qcom,tsens-v1", | |
| 68 | + .data = &data_tsens_v1, | |
| 66 | 69 | }, { |
| 67 | 70 | .compatible = "qcom,tsens-v2", |
| 68 | 71 | .data = &data_tsens_v2, |
| @@ -76,22 +79,27 @@ static const struct thermal_zone_of_device_ops tsens_of_ops = { · 7 unmodified lines | ||
| 76 | 79 | .get_trend = tsens_get_trend, |
| 77 | 80 | }; |
| 78 | 81 | |
| 79 | −static int tsens_register(struct tsens_device *tmdev) | |
| 82 | +static int tsens_register(struct tsens_priv *priv) | |
| 80 | 83 | { |
| 81 | 84 | int i; |
| 82 | 85 | struct thermal_zone_device *tzd; |
| 83 | 86 | |
| 84 | − for (i = 0; i < tmdev->num_sensors; i++) { | |
| 85 | − tmdev->sensor[i].tmdev = tmdev; | |
| 86 | − tmdev->sensor[i].id = i; | |
| 87 | − tzd = devm_thermal_zone_of_sensor_register(tmdev->dev, i, | |
| 88 | − &tmdev->sensor[i], | |
| 87 | + for (i = 0; i < priv->num_sensors; i++) { | |
| 88 | + if (!is_sensor_enabled(priv, priv->sensor[i].hw_id)) { | |
| 89 | + dev_err(priv->dev, "sensor %d: disabled\n", | |
| 90 | + priv->sensor[i].hw_id); | |
| 91 | + continue; | |
| 92 | + } | |
| 93 | + priv->sensor[i].priv = priv; | |
| 94 | + priv->sensor[i].id = i; | |
| 95 | + tzd = devm_thermal_zone_of_sensor_register(priv->dev, i, | |
| 96 | + &priv->sensor[i], | |
| 89 | 97 | &tsens_of_ops); |
| 90 | 98 | if (IS_ERR(tzd)) |
| 91 | 99 | continue; |
| 92 | − tmdev->sensor[i].tzd = tzd; | |
| 93 | − if (tmdev->ops->enable) | |
| 94 | − tmdev->ops->enable(tmdev, i); | |
| 100 | + priv->sensor[i].tzd = tzd; | |
| 101 | + if (priv->ops->enable) | |
| 102 | + priv->ops->enable(priv, i); | |
| 95 | 103 | } |
| 96 | 104 | return 0; |
| 97 | 105 | } |
| @@ -101,8 +109,8 @@ static int tsens_probe(struct platform_device *pdev) · 3 unmodified lines | ||
| 101 | 109 | int ret, i; |
| 102 | 110 | struct device *dev; |
| 103 | 111 | struct device_node *np; |
| 104 | − struct tsens_device *tmdev; | |
| 105 | − const struct tsens_data *data; | |
| 112 | + struct tsens_priv *priv; | |
| 113 | + const struct tsens_plat_data *data; | |
| 106 | 114 | const struct of_device_id *id; |
| 107 | 115 | u32 num_sensors; |
| 108 | 116 | |
| @@ -129,55 +137,55 @@ static int tsens_probe(struct platform_device *pdev) · 20 unmodified lines | ||
| 129 | 137 | return -EINVAL; |
| 130 | 138 | } |
| 131 | 139 | |
| 132 | − tmdev = devm_kzalloc(dev, | |
| 133 | − struct_size(tmdev, sensor, num_sensors), | |
| 140 | + priv = devm_kzalloc(dev, | |
| 141 | + struct_size(priv, sensor, num_sensors), | |
| 134 | 142 | GFP_KERNEL); |
| 135 | − if (!tmdev) | |
| 143 | + if (!priv) | |
| 136 | 144 | return -ENOMEM; |
| 137 | 145 | |
| 138 | − tmdev->dev = dev; | |
| 139 | − tmdev->num_sensors = num_sensors; | |
| 140 | − tmdev->ops = data->ops; | |
| 141 | − for (i = 0; i < tmdev->num_sensors; i++) { | |
| 146 | + priv->dev = dev; | |
| 147 | + priv->num_sensors = num_sensors; | |
| 148 | + priv->ops = data->ops; | |
| 149 | + for (i = 0; i < priv->num_sensors; i++) { | |
| 142 | 150 | if (data->hw_ids) |
| 143 | − tmdev->sensor[i].hw_id = data->hw_ids[i]; | |
| 151 | + priv->sensor[i].hw_id = data->hw_ids[i]; | |
| 144 | 152 | else |
| 145 | − tmdev->sensor[i].hw_id = i; | |
| 146 | − } | |
| 147 | − for (i = 0; i < REG_ARRAY_SIZE; i++) { | |
| 148 | − tmdev->reg_offsets[i] = data->reg_offsets[i]; | |
| 153 | + priv->sensor[i].hw_id = i; | |
| 149 | 154 | } |
| 155 | + priv->feat = data->feat; | |
| 156 | + priv->fields = data->fields; | |
| 150 | 157 | |
| 151 | − if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp) | |
| 158 | + if (!priv->ops || !priv->ops->init || !priv->ops->get_temp) | |
| 152 | 159 | return -EINVAL; |
| 153 | 160 | |
| 154 | − ret = tmdev->ops->init(tmdev); | |
| 161 | + ret = priv->ops->init(priv); | |
| 155 | 162 | if (ret < 0) { |
| 156 | 163 | dev_err(dev, "tsens init failed\n"); |
| 157 | 164 | return ret; |
| 158 | 165 | } |
| 159 | 166 | |
| 160 | − if (tmdev->ops->calibrate) { | |
| 161 | − ret = tmdev->ops->calibrate(tmdev); | |
| 167 | + if (priv->ops->calibrate) { | |
| 168 | + ret = priv->ops->calibrate(priv); | |
| 162 | 169 | if (ret < 0) { |
| 163 | − dev_err(dev, "tsens calibration failed\n"); | |
| 170 | + if (ret != -EPROBE_DEFER) | |
| 171 | + dev_err(dev, "tsens calibration failed\n"); | |
| 164 | 172 | return ret; |
| 165 | 173 | } |
| 166 | 174 | } |
| 167 | 175 | |
| 168 | − ret = tsens_register(tmdev); | |
| 176 | + ret = tsens_register(priv); | |
| 169 | 177 | |
| 170 | − platform_set_drvdata(pdev, tmdev); | |
| 178 | + platform_set_drvdata(pdev, priv); | |
| 171 | 179 | |
| 172 | 180 | return ret; |
| 173 | 181 | } |
| 174 | 182 | |
| 175 | 183 | static int tsens_remove(struct platform_device *pdev) |
| 176 | 184 | { |
| 177 | − struct tsens_device *tmdev = platform_get_drvdata(pdev); | |
| 185 | + struct tsens_priv *priv = platform_get_drvdata(pdev); | |
| 178 | 186 | |
| 179 | − if (tmdev->ops->disable) | |
| 180 | − tmdev->ops->disable(tmdev); | |
| 187 | + if (priv->ops->disable) | |
| 188 | + priv->ops->disable(priv); | |
| 181 | 189 | |
| 182 | 190 | return 0; |
| 183 | 191 | } |
| @@ -9,17 +9,39 @@ · 8 unmodified lines | ||
| 9 | 9 | #define ONE_PT_CALIB 0x1 |
| 10 | 10 | #define ONE_PT_CALIB2 0x2 |
| 11 | 11 | #define TWO_PT_CALIB 0x3 |
| 12 | +#define CAL_DEGC_PT1 30 | |
| 13 | +#define CAL_DEGC_PT2 120 | |
| 14 | +#define SLOPE_FACTOR 1000 | |
| 15 | +#define SLOPE_DEFAULT 3200 | |
| 16 | + | |
| 12 | 17 | |
| 13 | 18 | #include <linux/thermal.h> |
| 19 | +#include <linux/regmap.h> | |
| 20 | + | |
| 21 | +struct tsens_priv; | |
| 14 | 22 | |
| 15 | −struct tsens_device; | |
| 23 | +enum tsens_ver { | |
| 24 | + VER_0_1 = 0, | |
| 25 | + VER_1_X, | |
| 26 | + VER_2_X, | |
| 27 | +}; | |
| 16 | 28 | |
| 29 | +/** | |
| 30 | + * struct tsens_sensor - data for each sensor connected to the tsens device | |
| 31 | + * @priv: tsens device instance that this sensor is connected to | |
| 32 | + * @tzd: pointer to the thermal zone that this sensor is in | |
| 33 | + * @offset: offset of temperature adjustment curve | |
| 34 | + * @id: Sensor ID | |
| 35 | + * @hw_id: HW ID can be used in case of platform-specific IDs | |
| 36 | + * @slope: slope of temperature adjustment curve | |
| 37 | + * @status: 8960-specific variable to track 8960 and 8660 status register offset | |
| 38 | + */ | |
| 17 | 39 | struct tsens_sensor { |
| 18 | − struct tsens_device *tmdev; | |
| 40 | + struct tsens_priv *priv; | |
| 19 | 41 | struct thermal_zone_device *tzd; |
| 20 | 42 | int offset; |
| 21 | − int id; | |
| 22 | − int hw_id; | |
| 43 | + unsigned int id; | |
| 44 | + unsigned int hw_id; | |
| 23 | 45 | int slope; |
| 24 | 46 | u32 status; |
| 25 | 47 | }; |
| @@ -37,63 +59,274 @@ struct tsens_sensor { · 11 unmodified lines | ||
| 37 | 59 | */ |
| 38 | 60 | struct tsens_ops { |
| 39 | 61 | /* mandatory callbacks */ |
| 40 | − int (*init)(struct tsens_device *); | |
| 41 | − int (*calibrate)(struct tsens_device *); | |
| 42 | − int (*get_temp)(struct tsens_device *, int, int *); | |
| 62 | + int (*init)(struct tsens_priv *priv); | |
| 63 | + int (*calibrate)(struct tsens_priv *priv); | |
| 64 | + int (*get_temp)(struct tsens_priv *priv, int i, int *temp); | |
| 43 | 65 | /* optional callbacks */ |
| 44 | − int (*enable)(struct tsens_device *, int); | |
| 45 | − void (*disable)(struct tsens_device *); | |
| 46 | − int (*suspend)(struct tsens_device *); | |
| 47 | − int (*resume)(struct tsens_device *); | |
| 48 | − int (*get_trend)(struct tsens_device *, int, enum thermal_trend *); | |
| 66 | + int (*enable)(struct tsens_priv *priv, int i); | |
| 67 | + void (*disable)(struct tsens_priv *priv); | |
| 68 | + int (*suspend)(struct tsens_priv *priv); | |
| 69 | + int (*resume)(struct tsens_priv *priv); | |
| 70 | + int (*get_trend)(struct tsens_priv *priv, int i, enum thermal_trend *trend); | |
| 49 | 71 | }; |
| 50 | 72 | |
| 51 | −enum reg_list { | |
| 52 | − SROT_CTRL_OFFSET, | |
| 73 | +#define REG_FIELD_FOR_EACH_SENSOR11(_name, _offset, _startbit, _stopbit) \ | |
| 74 | + [_name##_##0] = REG_FIELD(_offset, _startbit, _stopbit), \ | |
| 75 | + [_name##_##1] = REG_FIELD(_offset + 4, _startbit, _stopbit), \ | |
| 76 | + [_name##_##2] = REG_FIELD(_offset + 8, _startbit, _stopbit), \ | |
| 77 | + [_name##_##3] = REG_FIELD(_offset + 12, _startbit, _stopbit), \ | |
| 78 | + [_name##_##4] = REG_FIELD(_offset + 16, _startbit, _stopbit), \ | |
| 79 | + [_name##_##5] = REG_FIELD(_offset + 20, _startbit, _stopbit), \ | |
| 80 | + [_name##_##6] = REG_FIELD(_offset + 24, _startbit, _stopbit), \ | |
| 81 | + [_name##_##7] = REG_FIELD(_offset + 28, _startbit, _stopbit), \ | |
| 82 | + [_name##_##8] = REG_FIELD(_offset + 32, _startbit, _stopbit), \ | |
| 83 | + [_name##_##9] = REG_FIELD(_offset + 36, _startbit, _stopbit), \ | |
| 84 | + [_name##_##10] = REG_FIELD(_offset + 40, _startbit, _stopbit) | |
| 53 | 85 | |
| 54 | − REG_ARRAY_SIZE, | |
| 86 | +#define REG_FIELD_FOR_EACH_SENSOR16(_name, _offset, _startbit, _stopbit) \ | |
| 87 | + [_name##_##0] = REG_FIELD(_offset, _startbit, _stopbit), \ | |
| 88 | + [_name##_##1] = REG_FIELD(_offset + 4, _startbit, _stopbit), \ | |
| 89 | + [_name##_##2] = REG_FIELD(_offset + 8, _startbit, _stopbit), \ | |
| 90 | + [_name##_##3] = REG_FIELD(_offset + 12, _startbit, _stopbit), \ | |
| 91 | + [_name##_##4] = REG_FIELD(_offset + 16, _startbit, _stopbit), \ | |
| 92 | + [_name##_##5] = REG_FIELD(_offset + 20, _startbit, _stopbit), \ | |
| 93 | + [_name##_##6] = REG_FIELD(_offset + 24, _startbit, _stopbit), \ | |
| 94 | + [_name##_##7] = REG_FIELD(_offset + 28, _startbit, _stopbit), \ | |
| 95 | + [_name##_##8] = REG_FIELD(_offset + 32, _startbit, _stopbit), \ | |
| 96 | + [_name##_##9] = REG_FIELD(_offset + 36, _startbit, _stopbit), \ | |
| 97 | + [_name##_##10] = REG_FIELD(_offset + 40, _startbit, _stopbit), \ | |
| 98 | + [_name##_##11] = REG_FIELD(_offset + 44, _startbit, _stopbit), \ | |
| 99 | + [_name##_##12] = REG_FIELD(_offset + 48, _startbit, _stopbit), \ | |
| 100 | + [_name##_##13] = REG_FIELD(_offset + 52, _startbit, _stopbit), \ | |
| 101 | + [_name##_##14] = REG_FIELD(_offset + 56, _startbit, _stopbit), \ | |
| 102 | + [_name##_##15] = REG_FIELD(_offset + 60, _startbit, _stopbit) | |
| 103 | + | |
| 104 | +/* reg_field IDs to use as an index into an array */ | |
| 105 | +enum regfield_ids { | |
| 106 | + /* ----- SROT ------ */ | |
| 107 | + /* HW_VER */ | |
| 108 | + VER_MAJOR = 0, | |
| 109 | + VER_MINOR, | |
| 110 | + VER_STEP, | |
| 111 | + /* CTRL_OFFSET */ | |
| 112 | + TSENS_EN = 3, | |
| 113 | + TSENS_SW_RST, | |
| 114 | + SENSOR_EN, | |
| 115 | + CODE_OR_TEMP, | |
| 116 | + | |
| 117 | + /* ----- TM ------ */ | |
| 118 | + /* STATUS */ | |
| 119 | + LAST_TEMP_0 = 7, /* Last temperature reading */ | |
| 120 | + LAST_TEMP_1, | |
| 121 | + LAST_TEMP_2, | |
| 122 | + LAST_TEMP_3, | |
| 123 | + LAST_TEMP_4, | |
| 124 | + LAST_TEMP_5, | |
| 125 | + LAST_TEMP_6, | |
| 126 | + LAST_TEMP_7, | |
| 127 | + LAST_TEMP_8, | |
| 128 | + LAST_TEMP_9, | |
| 129 | + LAST_TEMP_10, | |
| 130 | + LAST_TEMP_11, | |
| 131 | + LAST_TEMP_12, | |
| 132 | + LAST_TEMP_13, | |
| 133 | + LAST_TEMP_14, | |
| 134 | + LAST_TEMP_15, | |
| 135 | + VALID_0 = 23, /* VALID reading or not */ | |
| 136 | + VALID_1, | |
| 137 | + VALID_2, | |
| 138 | + VALID_3, | |
| 139 | + VALID_4, | |
| 140 | + VALID_5, | |
| 141 | + VALID_6, | |
| 142 | + VALID_7, | |
| 143 | + VALID_8, | |
| 144 | + VALID_9, | |
| 145 | + VALID_10, | |
| 146 | + VALID_11, | |
| 147 | + VALID_12, | |
| 148 | + VALID_13, | |
| 149 | + VALID_14, | |
| 150 | + VALID_15, | |
| 151 | + MIN_STATUS_0, /* MIN threshold violated */ | |
| 152 | + MIN_STATUS_1, | |
| 153 | + MIN_STATUS_2, | |
| 154 | + MIN_STATUS_3, | |
| 155 | + MIN_STATUS_4, | |
| 156 | + MIN_STATUS_5, | |
| 157 | + MIN_STATUS_6, | |
| 158 | + MIN_STATUS_7, | |
| 159 | + MIN_STATUS_8, | |
| 160 | + MIN_STATUS_9, | |
| 161 | + MIN_STATUS_10, | |
| 162 | + MIN_STATUS_11, | |
| 163 | + MIN_STATUS_12, | |
| 164 | + MIN_STATUS_13, | |
| 165 | + MIN_STATUS_14, | |
| 166 | + MIN_STATUS_15, | |
| 167 | + MAX_STATUS_0, /* MAX threshold violated */ | |
| 168 | + MAX_STATUS_1, | |
| 169 | + MAX_STATUS_2, | |
| 170 | + MAX_STATUS_3, | |
| 171 | + MAX_STATUS_4, | |
| 172 | + MAX_STATUS_5, | |
| 173 | + MAX_STATUS_6, | |
| 174 | + MAX_STATUS_7, | |
| 175 | + MAX_STATUS_8, | |
| 176 | + MAX_STATUS_9, | |
| 177 | + MAX_STATUS_10, | |
| 178 | + MAX_STATUS_11, | |
| 179 | + MAX_STATUS_12, | |
| 180 | + MAX_STATUS_13, | |
| 181 | + MAX_STATUS_14, | |
| 182 | + MAX_STATUS_15, | |
| 183 | + LOWER_STATUS_0, /* LOWER threshold violated */ | |
| 184 | + LOWER_STATUS_1, | |
| 185 | + LOWER_STATUS_2, | |
| 186 | + LOWER_STATUS_3, | |
| 187 | + LOWER_STATUS_4, | |
| 188 | + LOWER_STATUS_5, | |
| 189 | + LOWER_STATUS_6, | |
| 190 | + LOWER_STATUS_7, | |
| 191 | + LOWER_STATUS_8, | |
| 192 | + LOWER_STATUS_9, | |
| 193 | + LOWER_STATUS_10, | |
| 194 | + LOWER_STATUS_11, | |
| 195 | + LOWER_STATUS_12, | |
| 196 | + LOWER_STATUS_13, | |
| 197 | + LOWER_STATUS_14, | |
| 198 | + LOWER_STATUS_15, | |
| 199 | + UPPER_STATUS_0, /* UPPER threshold violated */ | |
| 200 | + UPPER_STATUS_1, | |
| 201 | + UPPER_STATUS_2, | |
| 202 | + UPPER_STATUS_3, | |
| 203 | + UPPER_STATUS_4, | |
| 204 | + UPPER_STATUS_5, | |
| 205 | + UPPER_STATUS_6, | |
| 206 | + UPPER_STATUS_7, | |
| 207 | + UPPER_STATUS_8, | |
| 208 | + UPPER_STATUS_9, | |
| 209 | + UPPER_STATUS_10, | |
| 210 | + UPPER_STATUS_11, | |
| 211 | + UPPER_STATUS_12, | |
| 212 | + UPPER_STATUS_13, | |
| 213 | + UPPER_STATUS_14, | |
| 214 | + UPPER_STATUS_15, | |
| 215 | + CRITICAL_STATUS_0, /* CRITICAL threshold violated */ | |
| 216 | + CRITICAL_STATUS_1, | |
| 217 | + CRITICAL_STATUS_2, | |
| 218 | + CRITICAL_STATUS_3, | |
| 219 | + CRITICAL_STATUS_4, | |
| 220 | + CRITICAL_STATUS_5, | |
| 221 | + CRITICAL_STATUS_6, | |
| 222 | + CRITICAL_STATUS_7, | |
| 223 | + CRITICAL_STATUS_8, | |
| 224 | + CRITICAL_STATUS_9, | |
| 225 | + CRITICAL_STATUS_10, | |
| 226 | + CRITICAL_STATUS_11, | |
| 227 | + CRITICAL_STATUS_12, | |
| 228 | + CRITICAL_STATUS_13, | |
| 229 | + CRITICAL_STATUS_14, | |
| 230 | + CRITICAL_STATUS_15, | |
| 231 | + /* TRDY */ | |
| 232 | + TRDY, | |
| 233 | + /* INTERRUPT ENABLE */ | |
| 234 | + INT_EN, /* Pre-V1, V1.x */ | |
| 235 | + LOW_INT_EN, /* V2.x */ | |
| 236 | + UP_INT_EN, /* V2.x */ | |
| 237 | + CRIT_INT_EN, /* V2.x */ | |
| 238 | + | |
| 239 | + /* Keep last */ | |
| 240 | + MAX_REGFIELDS | |
| 55 | 241 | }; |
| 56 | 242 | |
| 57 | 243 | /** |
| 58 | − * struct tsens_data - tsens instance specific data | |
| 59 | − * @num_sensors: Max number of sensors supported by platform | |
| 244 | + * struct tsens_features - Features supported by the IP | |
| 245 | + * @ver_major: Major number of IP version | |
| 246 | + * @crit_int: does the IP support critical interrupts? | |
| 247 | + * @adc: do the sensors only output adc code (instead of temperature)? | |
| 248 | + * @srot_split: does the IP neatly splits the register space into SROT and TM, | |
| 249 | + * with SROT only being available to secure boot firmware? | |
| 250 | + * @max_sensors: maximum sensors supported by this version of the IP | |
| 251 | + */ | |
| 252 | +struct tsens_features { | |
| 253 | + unsigned int ver_major; | |
| 254 | + unsigned int crit_int:1; | |
| 255 | + unsigned int adc:1; | |
| 256 | + unsigned int srot_split:1; | |
| 257 | + unsigned int max_sensors; | |
| 258 | +}; | |
| 259 | + | |
| 260 | +/** | |
| 261 | + * struct tsens_plat_data - tsens compile-time platform data | |
| 262 | + * @num_sensors: Number of sensors supported by platform | |
| 60 | 263 | * @ops: operations the tsens instance supports |
| 61 | 264 | * @hw_ids: Subset of sensors ids supported by platform, if not the first n |
| 62 | − * @reg_offsets: Register offsets for commonly used registers | |
| 265 | + * @feat: features of the IP | |
| 266 | + * @fields: bitfield locations | |
| 63 | 267 | */ |
| 64 | −struct tsens_data { | |
| 268 | +struct tsens_plat_data { | |
| 65 | 269 | const u32 num_sensors; |
| 66 | 270 | const struct tsens_ops *ops; |
| 67 | − const u16 reg_offsets[REG_ARRAY_SIZE]; | |
| 68 | 271 | unsigned int *hw_ids; |
| 272 | + const struct tsens_features *feat; | |
| 273 | + const struct reg_field *fields; | |
| 69 | 274 | }; |
| 70 | 275 | |
| 71 | −/* Registers to be saved/restored across a context loss */ | |
| 276 | +/** | |
| 277 | + * struct tsens_context - Registers to be saved/restored across a context loss | |
| 278 | + */ | |
| 72 | 279 | struct tsens_context { |
| 73 | 280 | int threshold; |
| 74 | 281 | int control; |
| 75 | 282 | }; |
| 76 | 283 | |
| 77 | −struct tsens_device { | |
| 284 | +/** | |
| 285 | + * struct tsens_priv - private data for each instance of the tsens IP | |
| 286 | + * @dev: pointer to struct device | |
| 287 | + * @num_sensors: number of sensors enabled on this device | |
| 288 | + * @tm_map: pointer to TM register address space | |
| 289 | + * @srot_map: pointer to SROT register address space | |
| 290 | + * @tm_offset: deal with old device trees that don't address TM and SROT | |
| 291 | + * address space separately | |
| 292 | + * @rf: array of regmap_fields used to store value of the field | |
| 293 | + * @ctx: registers to be saved and restored during suspend/resume | |
| 294 | + * @feat: features of the IP | |
| 295 | + * @fields: bitfield locations | |
| 296 | + * @ops: pointer to list of callbacks supported by this device | |
| 297 | + * @sensor: list of sensors attached to this device | |
| 298 | + */ | |
| 299 | +struct tsens_priv { | |
| 78 | 300 | struct device *dev; |
| 79 | 301 | u32 num_sensors; |
| 80 | 302 | struct regmap *tm_map; |
| 81 | 303 | struct regmap *srot_map; |
| 82 | 304 | u32 tm_offset; |
| 83 | − u16 reg_offsets[REG_ARRAY_SIZE]; | |
| 305 | + struct regmap_field *rf[MAX_REGFIELDS]; | |
| 84 | 306 | struct tsens_context ctx; |
| 307 | + const struct tsens_features *feat; | |
| 308 | + const struct reg_field *fields; | |
| 85 | 309 | const struct tsens_ops *ops; |
| 86 | 310 | struct tsens_sensor sensor[0]; |
| 87 | 311 | }; |
| 88 | 312 | |
| 89 | −char *qfprom_read(struct device *, const char *); | |
| 90 | −void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32); | |
| 91 | −int init_common(struct tsens_device *); | |
| 92 | −int get_temp_common(struct tsens_device *, int, int *); | |
| 313 | +char *qfprom_read(struct device *dev, const char *cname); | |
| 314 | +void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mode); | |
| 315 | +int init_common(struct tsens_priv *priv); | |
| 316 | +int get_temp_tsens_valid(struct tsens_priv *priv, int i, int *temp); | |
| 317 | +int get_temp_common(struct tsens_priv *priv, int i, int *temp); | |
| 318 | +bool is_sensor_enabled(struct tsens_priv *priv, u32 hw_id); | |
| 319 | + | |
| 320 | +/* TSENS target */ | |
| 321 | +extern const struct tsens_plat_data data_8960; | |
| 322 | + | |
| 323 | +/* TSENS v0.1 targets */ | |
| 324 | +extern const struct tsens_plat_data data_8916, data_8974; | |
| 93 | 325 | |
| 94 | 326 | /* TSENS v1 targets */ |
| 95 | −extern const struct tsens_data data_8916, data_8974, data_8960; | |
| 327 | +extern const struct tsens_plat_data data_tsens_v1; | |
| 328 | + | |
| 96 | 329 | /* TSENS v2 targets */ |
| 97 | −extern const struct tsens_data data_8996, data_tsens_v2; | |
| 330 | +extern const struct tsens_plat_data data_8996, data_tsens_v2; | |
| 98 | 331 | |
| 99 | 332 | #endif /* __QCOM_TSENS_H__ */ |
| @@ -193,11 +193,6 @@ static int qoriq_tmu_probe(struct platform_device *pdev) · 192 unmodified lines | ||
| 193 | 193 | struct qoriq_tmu_data *data; |
| 194 | 194 | struct device_node *np = pdev->dev.of_node; |
| 195 | 195 | |
| 196 | − if (!np) { | |
| 197 | − dev_err(&pdev->dev, "Device OF-Node is NULL"); | |
| 198 | − return -ENODEV; | |
| 199 | − } | |
| 200 | − | |
| 201 | 196 | data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data), |
| 202 | 197 | GFP_KERNEL); |
| 203 | 198 | if (!data) |
| @@ -14,7 +14,6 @@ · 13 unmodified lines | ||
| 14 | 14 | #include <linux/of_device.h> |
| 15 | 15 | #include <linux/platform_device.h> |
| 16 | 16 | #include <linux/pm_runtime.h> |
| 17 | −#include <linux/spinlock.h> | |
| 18 | 17 | #include <linux/sys_soc.h> |
| 19 | 18 | #include <linux/thermal.h> |
| 20 | 19 | |
| @@ -82,7 +81,6 @@ struct rcar_gen3_thermal_tsc { · 61 unmodified lines | ||
| 82 | 81 | struct rcar_gen3_thermal_priv { |
| 83 | 82 | struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; |
| 84 | 83 | unsigned int num_tscs; |
| 85 | − spinlock_t lock; /* Protect interrupts on and off */ | |
| 86 | 84 | void (*thermal_init)(struct rcar_gen3_thermal_tsc *tsc); |
| 87 | 85 | }; |
| 88 | 86 | |
| @@ -232,38 +230,16 @@ static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data) · 143 unmodified lines | ||
| 232 | 230 | { |
| 233 | 231 | struct rcar_gen3_thermal_priv *priv = data; |
| 234 | 232 | u32 status; |
| 235 | − int i, ret = IRQ_HANDLED; | |
| 233 | + int i; | |
| 236 | 234 | |
| 237 | − spin_lock(&priv->lock); | |
| 238 | 235 | for (i = 0; i < priv->num_tscs; i++) { |
| 239 | 236 | status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR); |
| 240 | 237 | rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0); |
| 241 | 238 | if (status) |
| 242 | − ret = IRQ_WAKE_THREAD; | |
| 239 | + thermal_zone_device_update(priv->tscs[i]->zone, | |
| 240 | + THERMAL_EVENT_UNSPECIFIED); | |
| 243 | 241 | } |
| 244 | 242 | |
| 245 | − if (ret == IRQ_WAKE_THREAD) | |
| 246 | − rcar_thermal_irq_set(priv, false); | |
| 247 | − | |
| 248 | − spin_unlock(&priv->lock); | |
| 249 | − | |
| 250 | − return ret; | |
| 251 | −} | |
| 252 | − | |
| 253 | −static irqreturn_t rcar_gen3_thermal_irq_thread(int irq, void *data) | |
| 254 | −{ | |
| 255 | − struct rcar_gen3_thermal_priv *priv = data; | |
| 256 | − unsigned long flags; | |
| 257 | − int i; | |
| 258 | − | |
| 259 | − for (i = 0; i < priv->num_tscs; i++) | |
| 260 | − thermal_zone_device_update(priv->tscs[i]->zone, | |
| 261 | − THERMAL_EVENT_UNSPECIFIED); | |
| 262 | − | |
| 263 | − spin_lock_irqsave(&priv->lock, flags); | |
| 264 | − rcar_thermal_irq_set(priv, true); | |
| 265 | − spin_unlock_irqrestore(&priv->lock, flags); | |
| 266 | − | |
| 267 | 243 | return IRQ_HANDLED; |
| 268 | 244 | } |
| 269 | 245 | |
| @@ -307,7 +283,7 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc) · 37 unmodified lines | ||
| 307 | 283 | |
| 308 | 284 | usleep_range(1000, 2000); |
| 309 | 285 | |
| 310 | − rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); | |
| 286 | + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0); | |
| 311 | 287 | rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0); |
| 312 | 288 | rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, IRQ_TEMPD1 | IRQ_TEMP2); |
| 313 | 289 | |
| @@ -331,6 +307,9 @@ MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids); · 17 unmodified lines | ||
| 331 | 307 | static int rcar_gen3_thermal_remove(struct platform_device *pdev) |
| 332 | 308 | { |
| 333 | 309 | struct device *dev = &pdev->dev; |
| 310 | + struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev); | |
| 311 | + | |
| 312 | + rcar_thermal_irq_set(priv, false); | |
| 334 | 313 | |
| 335 | 314 | pm_runtime_put(dev); |
| 336 | 315 | pm_runtime_disable(dev); |
| @@ -371,8 +350,6 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) · 34 unmodified lines | ||
| 371 | 350 | if (soc_device_match(r8a7795es1)) |
| 372 | 351 | priv->thermal_init = rcar_gen3_thermal_init_r8a7795es1; |
| 373 | 352 | |
| 374 | − spin_lock_init(&priv->lock); | |
| 375 | − | |
| 376 | 353 | platform_set_drvdata(pdev, priv); |
| 377 | 354 | |
| 378 | 355 | /* |
| @@ -390,9 +367,9 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) · 11 unmodified lines | ||
| 390 | 367 | if (!irqname) |
| 391 | 368 | return -ENOMEM; |
| 392 | 369 | |
| 393 | − ret = devm_request_threaded_irq(dev, irq, rcar_gen3_thermal_irq, | |
| 394 | − rcar_gen3_thermal_irq_thread, | |
| 395 | − IRQF_SHARED, irqname, priv); | |
| 370 | + ret = devm_request_threaded_irq(dev, irq, NULL, | |
| 371 | + rcar_gen3_thermal_irq, | |
| 372 | + IRQF_ONESHOT, irqname, priv); | |
| 396 | 373 | if (ret) |
| 397 | 374 | return ret; |
| 398 | 375 | } |
| @@ -433,10 +410,6 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) · 34 unmodified lines | ||
| 433 | 410 | } |
| 434 | 411 | tsc->zone = zone; |
| 435 | 412 | |
| 436 | − ret = of_thermal_get_ntrips(tsc->zone); | |
| 437 | − if (ret < 0) | |
| 438 | − goto error_unregister; | |
| 439 | − | |
| 440 | 413 | tsc->zone->tzp->no_hwmon = false; |
| 441 | 414 | ret = thermal_add_hwmon_sysfs(tsc->zone); |
| 442 | 415 | if (ret) |
| @@ -448,6 +421,10 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) · 5 unmodified lines | ||
| 448 | 421 | goto error_unregister; |
| 449 | 422 | } |
| 450 | 423 | |
| 424 | + ret = of_thermal_get_ntrips(tsc->zone); | |
| 425 | + if (ret < 0) | |
| 426 | + goto error_unregister; | |
| 427 | + | |
| 451 | 428 | dev_info(dev, "TSC%d: Loaded %d trip points\n", i, ret); |
| 452 | 429 | } |
| 453 | 430 | |
| @@ -52,6 +52,7 @@ struct rcar_thermal_chip { · 51 unmodified lines | ||
| 52 | 52 | unsigned int irq_per_ch : 1; |
| 53 | 53 | unsigned int needs_suspend_resume : 1; |
| 54 | 54 | unsigned int nirqs; |
| 55 | + unsigned int ctemp_bands; | |
| 55 | 56 | }; |
| 56 | 57 | |
| 57 | 58 | static const struct rcar_thermal_chip rcar_thermal = { |
| @@ -60,6 +61,7 @@ static const struct rcar_thermal_chip rcar_thermal = { · 2 unmodified lines | ||
| 60 | 61 | .irq_per_ch = 0, |
| 61 | 62 | .needs_suspend_resume = 0, |
| 62 | 63 | .nirqs = 1, |
| 64 | + .ctemp_bands = 1, | |
| 63 | 65 | }; |
| 64 | 66 | |
| 65 | 67 | static const struct rcar_thermal_chip rcar_gen2_thermal = { |
| @@ -68,6 +70,7 @@ static const struct rcar_thermal_chip rcar_gen2_thermal = { · 2 unmodified lines | ||
| 68 | 70 | .irq_per_ch = 0, |
| 69 | 71 | .needs_suspend_resume = 0, |
| 70 | 72 | .nirqs = 1, |
| 73 | + .ctemp_bands = 1, | |
| 71 | 74 | }; |
| 72 | 75 | |
| 73 | 76 | static const struct rcar_thermal_chip rcar_gen3_thermal = { |
| @@ -80,6 +83,7 @@ static const struct rcar_thermal_chip rcar_gen3_thermal = { · 6 unmodified lines | ||
| 80 | 83 | * interrupts to detect a temperature change, rise or fall. |
| 81 | 84 | */ |
| 82 | 85 | .nirqs = 2, |
| 86 | + .ctemp_bands = 2, | |
| 83 | 87 | }; |
| 84 | 88 | |
| 85 | 89 | struct rcar_thermal_priv { |
| @@ -263,7 +267,12 @@ static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv, · 177 unmodified lines | ||
| 263 | 267 | return ret; |
| 264 | 268 | |
| 265 | 269 | mutex_lock(&priv->lock); |
| 266 | − tmp = MCELSIUS((priv->ctemp * 5) - 65); | |
| 270 | + if (priv->chip->ctemp_bands == 1) | |
| 271 | + tmp = MCELSIUS((priv->ctemp * 5) - 65); | |
| 272 | + else if (priv->ctemp < 24) | |
| 273 | + tmp = MCELSIUS(((priv->ctemp * 55) - 720) / 10); | |
| 274 | + else | |
| 275 | + tmp = MCELSIUS((priv->ctemp * 5) - 60); | |
| 267 | 276 | mutex_unlock(&priv->lock); |
| 268 | 277 | |
| 269 | 278 | if ((tmp < MCELSIUS(-45)) || (tmp > MCELSIUS(125))) { |
| @@ -172,6 +172,9 @@ struct rockchip_thermal_data { · 171 unmodified lines | ||
| 172 | 172 | int tshut_temp; |
| 173 | 173 | enum tshut_mode tshut_mode; |
| 174 | 174 | enum tshut_polarity tshut_polarity; |
| 175 | + struct pinctrl *pinctrl; | |
| 176 | + struct pinctrl_state *gpio_state; | |
| 177 | + struct pinctrl_state *otp_state; | |
| 175 | 178 | }; |
| 176 | 179 | |
| 177 | 180 | /** |
| @@ -222,11 +225,15 @@ struct rockchip_thermal_data { · 44 unmodified lines | ||
| 222 | 225 | #define GRF_TSADC_TESTBIT_L 0x0e648 |
| 223 | 226 | #define GRF_TSADC_TESTBIT_H 0x0e64c |
| 224 | 227 | |
| 228 | +#define PX30_GRF_SOC_CON2 0x0408 | |
| 229 | + | |
| 225 | 230 | #define GRF_SARADC_TESTBIT_ON (0x10001 << 2) |
| 226 | 231 | #define GRF_TSADC_TESTBIT_H_ON (0x10001 << 2) |
| 227 | 232 | #define GRF_TSADC_VCM_EN_L (0x10001 << 7) |
| 228 | 233 | #define GRF_TSADC_VCM_EN_H (0x10001 << 7) |
| 229 | 234 | |
| 235 | +#define GRF_CON_TSADC_CH_INV (0x10001 << 1) | |
| 236 | + | |
| 230 | 237 | /** |
| 231 | 238 | * struct tsadc_table - code to temperature conversion table |
| 232 | 239 | * @code: the value of adc channel |
| @@ -689,6 +696,13 @@ static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs, · 456 unmodified lines | ||
| 689 | 696 | regs + TSADCV2_AUTO_CON); |
| 690 | 697 | } |
| 691 | 698 | |
| 699 | +static void rk_tsadcv4_initialize(struct regmap *grf, void __iomem *regs, | |
| 700 | + enum tshut_polarity tshut_polarity) | |
| 701 | +{ | |
| 702 | + rk_tsadcv2_initialize(grf, regs, tshut_polarity); | |
| 703 | + regmap_write(grf, PX30_GRF_SOC_CON2, GRF_CON_TSADC_CH_INV); | |
| 704 | +} | |
| 705 | + | |
| 692 | 706 | static void rk_tsadcv2_irq_ack(void __iomem *regs) |
| 693 | 707 | { |
| 694 | 708 | u32 val; |
| @@ -818,6 +832,30 @@ static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs, · 123 unmodified lines | ||
| 818 | 832 | writel_relaxed(val, regs + TSADCV2_INT_EN); |
| 819 | 833 | } |
| 820 | 834 | |
| 835 | +static const struct rockchip_tsadc_chip px30_tsadc_data = { | |
| 836 | + .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */ | |
| 837 | + .chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */ | |
| 838 | + .chn_num = 2, /* 2 channels for tsadc */ | |
| 839 | + | |
| 840 | + .tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */ | |
| 841 | + .tshut_temp = 95000, | |
| 842 | + | |
| 843 | + .initialize = rk_tsadcv4_initialize, | |
| 844 | + .irq_ack = rk_tsadcv3_irq_ack, | |
| 845 | + .control = rk_tsadcv3_control, | |
| 846 | + .get_temp = rk_tsadcv2_get_temp, | |
| 847 | + .set_alarm_temp = rk_tsadcv2_alarm_temp, | |
| 848 | + .set_tshut_temp = rk_tsadcv2_tshut_temp, | |
| 849 | + .set_tshut_mode = rk_tsadcv2_tshut_mode, | |
| 850 | + | |
| 851 | + .table = { | |
| 852 | + .id = rk3328_code_table, | |
| 853 | + .length = ARRAY_SIZE(rk3328_code_table), | |
| 854 | + .data_mask = TSADCV2_DATA_MASK, | |
| 855 | + .mode = ADC_INCREMENT, | |
| 856 | + }, | |
| 857 | +}; | |
| 858 | + | |
| 821 | 859 | static const struct rockchip_tsadc_chip rv1108_tsadc_data = { |
| 822 | 860 | .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */ |
| 823 | 861 | .chn_num = 1, /* one channel for tsadc */ |
| @@ -990,6 +1028,9 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = { · 166 unmodified lines | ||
| 990 | 1028 | }; |
| 991 | 1029 | |
| 992 | 1030 | static const struct of_device_id of_rockchip_thermal_match[] = { |
| 1031 | + { .compatible = "rockchip,px30-tsadc", | |
| 1032 | + .data = (void *)&px30_tsadc_data, | |
| 1033 | + }, | |
| 993 | 1034 | { |
| 994 | 1035 | .compatible = "rockchip,rv1108-tsadc", |
| 995 | 1036 | .data = (void *)&rv1108_tsadc_data, |
| @@ -1242,6 +1283,8 @@ static int rockchip_thermal_probe(struct platform_device *pdev) · 246 unmodified lines | ||
| 1242 | 1283 | return error; |
| 1243 | 1284 | } |
| 1244 | 1285 | |
| 1286 | + thermal->chip->control(thermal->regs, false); | |
| 1287 | + | |
| 1245 | 1288 | error = clk_prepare_enable(thermal->clk); |
| 1246 | 1289 | if (error) { |
| 1247 | 1290 | dev_err(&pdev->dev, "failed to enable converter clock: %d\n", |
| @@ -1267,6 +1310,30 @@ static int rockchip_thermal_probe(struct platform_device *pdev) · 19 unmodified lines | ||
| 1267 | 1310 | thermal->chip->initialize(thermal->grf, thermal->regs, |
| 1268 | 1311 | thermal->tshut_polarity); |
| 1269 | 1312 | |
| 1313 | + if (thermal->tshut_mode == TSHUT_MODE_GPIO) { | |
| 1314 | + thermal->pinctrl = devm_pinctrl_get(&pdev->dev); | |
| 1315 | + if (IS_ERR(thermal->pinctrl)) { | |
| 1316 | + dev_err(&pdev->dev, "failed to find thermal pinctrl\n"); | |
| 1317 | + return PTR_ERR(thermal->pinctrl); | |
| 1318 | + } | |
| 1319 | + | |
| 1320 | + thermal->gpio_state = pinctrl_lookup_state(thermal->pinctrl, | |
| 1321 | + "gpio"); | |
| 1322 | + if (IS_ERR_OR_NULL(thermal->gpio_state)) { | |
| 1323 | + dev_err(&pdev->dev, "failed to find thermal gpio state\n"); | |
| 1324 | + return -EINVAL; | |
| 1325 | + } | |
| 1326 | + | |
| 1327 | + thermal->otp_state = pinctrl_lookup_state(thermal->pinctrl, | |
| 1328 | + "otpout"); | |
| 1329 | + if (IS_ERR_OR_NULL(thermal->otp_state)) { | |
| 1330 | + dev_err(&pdev->dev, "failed to find thermal otpout state\n"); | |
| 1331 | + return -EINVAL; | |
| 1332 | + } | |
| 1333 | + | |
| 1334 | + pinctrl_select_state(thermal->pinctrl, thermal->otp_state); | |
| 1335 | + } | |
| 1336 | + | |
| 1270 | 1337 | for (i = 0; i < thermal->chip->chn_num; i++) { |
| 1271 | 1338 | error = rockchip_thermal_register_sensor(pdev, thermal, |
| 1272 | 1339 | &thermal->sensors[i], |
| @@ -1337,8 +1404,8 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev) · 64 unmodified lines | ||
| 1337 | 1404 | |
| 1338 | 1405 | clk_disable(thermal->pclk); |
| 1339 | 1406 | clk_disable(thermal->clk); |
| 1340 | − | |
| 1341 | − pinctrl_pm_select_sleep_state(dev); | |
| 1407 | + if (thermal->tshut_mode == TSHUT_MODE_GPIO) | |
| 1408 | + pinctrl_select_state(thermal->pinctrl, thermal->gpio_state); | |
| 1342 | 1409 | |
| 1343 | 1410 | return 0; |
| 1344 | 1411 | } |
| @@ -1383,7 +1450,8 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev) · 38 unmodified lines | ||
| 1383 | 1450 | for (i = 0; i < thermal->chip->chn_num; i++) |
| 1384 | 1451 | rockchip_thermal_toggle_sensor(&thermal->sensors[i], true); |
| 1385 | 1452 | |
| 1386 | − pinctrl_pm_select_default_state(dev); | |
| 1453 | + if (thermal->tshut_mode == TSHUT_MODE_GPIO) | |
| 1454 | + pinctrl_select_state(thermal->pinctrl, thermal->otp_state); | |
| 1387 | 1455 | |
| 1388 | 1456 | return 0; |
| 1389 | 1457 | } |
| @@ -3,9 +3,9 @@ · 2 unmodified lines | ||
| 3 | 3 | # |
| 4 | 4 | |
| 5 | 5 | config ST_THERMAL |
| 6 | − tristate "Thermal sensors on STMicroelectronics STi series of SoCs" | |
| 7 | − help | |
| 8 | − Support for thermal sensors on STMicroelectronics STi series of SoCs. | |
| 6 | + tristate "Thermal sensors on STMicroelectronics STi series of SoCs" | |
| 7 | + help | |
| 8 | + Support for thermal sensors on STMicroelectronics STi series of SoCs. | |
| 9 | 9 | |
| 10 | 10 | config ST_THERMAL_SYSCFG |
| 11 | 11 | select ST_THERMAL |
| @@ -16,11 +16,11 @@ config ST_THERMAL_MEMMAP · 4 unmodified lines | ||
| 16 | 16 | tristate "STi series memory mapped access based thermal sensors" |
| 17 | 17 | |
| 18 | 18 | config STM32_THERMAL |
| 19 | − tristate "Thermal framework support on STMicroelectronics STM32 series of SoCs" | |
| 20 | − depends on MACH_STM32MP157 | |
| 21 | − default y | |
| 22 | − help | |
| 23 | − Support for thermal framework on STMicroelectronics STM32 series of | |
| 24 | − SoCs. This thermal driver allows to access to general thermal framework | |
| 25 | − functionalities and to acces to SoC sensor functionalities. This | |
| 26 | − configuration is fully dependent of MACH_STM32MP157. | |
| 19 | + tristate "Thermal framework support on STMicroelectronics STM32 series of SoCs" | |
| 20 | + depends on MACH_STM32MP157 | |
| 21 | + default y | |
| 22 | + help | |
| 23 | + Support for thermal framework on STMicroelectronics STM32 series of | |
| 24 | + SoCs. This thermal driver allows to access to general thermal framework | |
| 25 | + functionalities and to acces to SoC sensor functionalities. This | |
| 26 | + configuration is fully dependent of MACH_STM32MP157. | |
| @@ -570,8 +570,7 @@ thermal_unprepare: · 569 unmodified lines | ||
| 570 | 570 | static int stm_thermal_suspend(struct device *dev) |
| 571 | 571 | { |
| 572 | 572 | int ret; |
| 573 | − struct platform_device *pdev = to_platform_device(dev); | |
| 574 | − struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev); | |
| 573 | + struct stm_thermal_sensor *sensor = dev_get_drvdata(dev); | |
| 575 | 574 | |
| 576 | 575 | ret = stm_thermal_sensor_off(sensor); |
| 577 | 576 | if (ret) |
| @@ -585,8 +584,7 @@ static int stm_thermal_suspend(struct device *dev) · 7 unmodified lines | ||
| 585 | 584 | static int stm_thermal_resume(struct device *dev) |
| 586 | 585 | { |
| 587 | 586 | int ret; |
| 588 | − struct platform_device *pdev = to_platform_device(dev); | |
| 589 | − struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev); | |
| 587 | + struct stm_thermal_sensor *sensor = dev_get_drvdata(dev); | |
| 590 | 588 | |
| 591 | 589 | ret = stm_thermal_prepare(sensor); |
| 592 | 590 | if (ret) |
| @@ -14,7 +14,7 @@ config TEGRA_BPMP_THERMAL · 13 unmodified lines | ||
| 14 | 14 | tristate "Tegra BPMP thermal sensing" |
| 15 | 15 | depends on TEGRA_BPMP || COMPILE_TEST |
| 16 | 16 | help |
| 17 | − Enable this option for support for sensing system temperature of NVIDIA | |
| 18 | − Tegra systems-on-chip with the BPMP coprocessor (Tegra186). | |
| 17 | + Enable this option for support for sensing system temperature of NVIDIA | |
| 18 | + Tegra systems-on-chip with the BPMP coprocessor (Tegra186). | |
| 19 | 19 | |
| 20 | 20 | endmenu |
| @@ -1,5 +1,6 @@ | ||
| 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,6 +23,8 @@ · 16 unmodified lines | ||
| 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,12 +88,51 @@ · 57 unmodified lines | ||
| 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 |
| 93 | 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) | |
| 135 | + | |
| 94 | 136 | #define THROT_GLOBAL_CFG 0x400 |
| 95 | 137 | #define THROT_GLOBAL_ENB_MASK BIT(0) |
| 96 | 138 | |
| @@ -160,6 +202,15 @@ · 63 unmodified lines | ||
| 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-sochterm.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,6 +224,25 @@ · 7 unmodified lines | ||
| 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,15 +254,32 @@ · 5 unmodified lines | ||
| 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, |
| 194 | 272 | }; |
| 195 | 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, | |
| 281 | +}; | |
| 282 | + | |
| 196 | 283 | enum soctherm_throttle_dev_id { |
| 197 | 284 | THROTTLE_DEV_CPU = 0, |
| 198 | 285 | THROTTLE_DEV_GPU, |
| @@ -202,6 +289,11 @@ enum soctherm_throttle_dev_id { · 3 unmodified lines | ||
| 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,12 +305,23 @@ struct tegra_thermctl_zone { · 5 unmodified lines | ||
| 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,6 +334,9 @@ struct tegra_soctherm { · 6 unmodified lines | ||
| 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,8 +344,19 @@ struct tegra_soctherm { · 1 unmodified line | ||
| 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 | }; |
| 242 | 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; | |
| 359 | + | |
| 243 | 360 | /** |
| 244 | 361 | * ccroc_writel() - writes a value to a CCROC register |
| 245 | 362 | * @ts: pointer to a struct tegra_soctherm |
| @@ -446,6 +563,24 @@ find_throttle_cfg_by_name(struct tegra_soctherm *ts, const char *name) · 200 unmodified lines | ||
| 446 | 563 | return NULL; |
| 447 | 564 | } |
| 448 | 565 | |
| 566 | +static int tsensor_group_thermtrip_get(struct tegra_soctherm *ts, int id) | |
| 567 | +{ | |
| 568 | + int i, temp = min_low_temp; | |
| 569 | + struct tsensor_group_thermtrips *tt = ts->soc->thermtrips; | |
| 570 | + | |
| 571 | + if (id >= TEGRA124_SOCTHERM_SENSOR_NUM) | |
| 572 | + return temp; | |
| 573 | + | |
| 574 | + if (tt) { | |
| 575 | + for (i = 0; i < ts->soc->num_ttgs; i++) { | |
| 576 | + if (tt[i].id == id) | |
| 577 | + return tt[i].temp; | |
| 578 | + } | |
| 579 | + } | |
| 580 | + | |
| 581 | + return temp; | |
| 582 | +} | |
| 583 | + | |
| 449 | 584 | static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp) |
| 450 | 585 | { |
| 451 | 586 | struct tegra_thermctl_zone *zone = data; |
| @@ -464,7 +599,16 @@ static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp) · 12 unmodified lines | ||
| 464 | 599 | return ret; |
| 465 | 600 | |
| 466 | 601 | if (type == THERMAL_TRIP_CRITICAL) { |
| 467 | − return thermtrip_program(dev, sg, temp); | |
| 602 | + /* | |
| 603 | + * If thermtrips property is set in DT, | |
| 604 | + * doesn't need to program critical type trip to HW, | |
| 605 | + * if not, program critical trip to HW. | |
| 606 | + */ | |
| 607 | + if (min_low_temp == tsensor_group_thermtrip_get(ts, sg->id)) | |
| 608 | + return thermtrip_program(dev, sg, temp); | |
| 609 | + else | |
| 610 | + return 0; | |
| 611 | + | |
| 468 | 612 | } else if (type == THERMAL_TRIP_HOT) { |
| 469 | 613 | int i; |
| 470 | 614 | |
| @@ -519,10 +663,60 @@ static int tegra_thermctl_get_trend(void *data, int trip, · 48 unmodified lines | ||
| 519 | 663 | return 0; |
| 520 | 664 | } |
| 521 | 665 | |
| 666 | +static void thermal_irq_enable(struct tegra_thermctl_zone *zn) | |
| 667 | +{ | |
| 668 | + u32 r; | |
| 669 | + | |
| 670 | + /* multiple zones could be handling and setting trips at once */ | |
| 671 | + mutex_lock(&zn->ts->thermctl_lock); | |
| 672 | + r = readl(zn->ts->regs + THERMCTL_INTR_ENABLE); | |
| 673 | + r = REG_SET_MASK(r, zn->sg->thermctl_isr_mask, TH_INTR_UP_DN_EN); | |
| 674 | + writel(r, zn->ts->regs + THERMCTL_INTR_ENABLE); | |
| 675 | + mutex_unlock(&zn->ts->thermctl_lock); | |
| 676 | +} | |
| 677 | + | |
| 678 | +static void thermal_irq_disable(struct tegra_thermctl_zone *zn) | |
| 679 | +{ | |
| 680 | + u32 r; | |
| 681 | + | |
| 682 | + /* multiple zones could be handling and setting trips at once */ | |
| 683 | + mutex_lock(&zn->ts->thermctl_lock); | |
| 684 | + r = readl(zn->ts->regs + THERMCTL_INTR_DISABLE); | |
| 685 | + r = REG_SET_MASK(r, zn->sg->thermctl_isr_mask, 0); | |
| 686 | + writel(r, zn->ts->regs + THERMCTL_INTR_DISABLE); | |
| 687 | + mutex_unlock(&zn->ts->thermctl_lock); | |
| 688 | +} | |
| 689 | + | |
| 690 | +static int tegra_thermctl_set_trips(void *data, int lo, int hi) | |
| 691 | +{ | |
| 692 | + struct tegra_thermctl_zone *zone = data; | |
| 693 | + u32 r; | |
| 694 | + | |
| 695 | + thermal_irq_disable(zone); | |
| 696 | + | |
| 697 | + r = readl(zone->ts->regs + zone->sg->thermctl_lvl0_offset); | |
| 698 | + r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 0); | |
| 699 | + writel(r, zone->ts->regs + zone->sg->thermctl_lvl0_offset); | |
| 700 | + | |
| 701 | + lo = enforce_temp_range(zone->dev, lo) / zone->ts->soc->thresh_grain; | |
| 702 | + hi = enforce_temp_range(zone->dev, hi) / zone->ts->soc->thresh_grain; | |
| 703 | + dev_dbg(zone->dev, "%s hi:%d, lo:%d\n", __func__, hi, lo); | |
| 704 | + | |
| 705 | + r = REG_SET_MASK(r, zone->sg->thermctl_lvl0_up_thresh_mask, hi); | |
| 706 | + r = REG_SET_MASK(r, zone->sg->thermctl_lvl0_dn_thresh_mask, lo); | |
| 707 | + r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 1); | |
| 708 | + writel(r, zone->ts->regs + zone->sg->thermctl_lvl0_offset); | |
| 709 | + | |
| 710 | + thermal_irq_enable(zone); | |
| 711 | + | |
| 712 | + return 0; | |
| 713 | +} | |
| 714 | + | |
| 522 | 715 | static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = { |
| 523 | 716 | .get_temp = tegra_thermctl_get_temp, |
| 524 | 717 | .set_trip_temp = tegra_thermctl_set_trip_temp, |
| 525 | 718 | .get_trend = tegra_thermctl_get_trend, |
| 719 | + .set_trips = tegra_thermctl_set_trips, | |
| 526 | 720 | }; |
| 527 | 721 | |
| 528 | 722 | static int get_hot_temp(struct thermal_zone_device *tz, int *trip, int *temp) |
| @@ -555,7 +749,8 @@ static int get_hot_temp(struct thermal_zone_device *tz, int *trip, int *temp) · 26 unmodified lines | ||
| 555 | 749 | * @dev: struct device * of the SOC_THERM instance |
| 556 | 750 | * |
| 557 | 751 | * Configure the SOC_THERM HW trip points, setting "THERMTRIP" |
| 558 | − * "THROTTLE" trip points , using "critical" or "hot" type trip_temp | |
| 752 | + * "THROTTLE" trip points , using "thermtrips", "critical" or "hot" | |
| 753 | + * type trip_temp | |
| 559 | 754 | * from thermal zone. |
| 560 | 755 | * After they have been configured, THERMTRIP or THROTTLE will take |
| 561 | 756 | * action when the configured SoC thermal sensor group reaches a |
| @@ -577,28 +772,23 @@ static int tegra_soctherm_set_hwtrips(struct device *dev, · 15 unmodified lines | ||
| 577 | 772 | { |
| 578 | 773 | struct tegra_soctherm *ts = dev_get_drvdata(dev); |
| 579 | 774 | struct soctherm_throt_cfg *stc; |
| 580 | − int i, trip, temperature; | |
| 581 | − int ret; | |
| 775 | + int i, trip, temperature, ret; | |
| 582 | 776 | |
| 583 | − ret = tz->ops->get_crit_temp(tz, &temperature); | |
| 584 | − if (ret) { | |
| 585 | − dev_warn(dev, "thermtrip: %s: missing critical temperature\n", | |
| 586 | − sg->name); | |
| 587 | − goto set_throttle; | |
| 588 | − } | |
| 777 | + /* Get thermtrips. If missing, try to get critical trips. */ | |
| 778 | + temperature = tsensor_group_thermtrip_get(ts, sg->id); | |
| 779 | + if (min_low_temp == temperature) | |
| 780 | + if (tz->ops->get_crit_temp(tz, &temperature)) | |
| 781 | + temperature = max_high_temp; | |
| 589 | 782 | |
| 590 | 783 | ret = thermtrip_program(dev, sg, temperature); |
| 591 | 784 | if (ret) { |
| 592 | − dev_err(dev, "thermtrip: %s: error during enable\n", | |
| 593 | − sg->name); | |
| 785 | + dev_err(dev, "thermtrip: %s: error during enable\n", sg->name); | |
| 594 | 786 | return ret; |
| 595 | 787 | } |
| 596 | 788 | |
| 597 | − dev_info(dev, | |
| 598 | − "thermtrip: will shut down when %s reaches %d mC\n", | |
| 789 | + dev_info(dev, "thermtrip: will shut down when %s reaches %d mC\n", | |
| 599 | 790 | sg->name, temperature); |
| 600 | 791 | |
| 601 | −set_throttle: | |
| 602 | 792 | ret = get_hot_temp(tz, &trip, &temperature); |
| 603 | 793 | if (ret) { |
| 604 | 794 | dev_info(dev, "throttrip: %s: missing hot temperature\n", |
| @@ -606,7 +796,7 @@ set_throttle: · 1 unmodified line | ||
| 606 | 796 | return 0; |
| 607 | 797 | } |
| 608 | 798 | |
| 609 | − for (i = 0; i < THROTTLE_SIZE; i++) { | |
| 799 | + for (i = 0; i < THROTTLE_OC1; i++) { | |
| 610 | 800 | struct thermal_cooling_device *cdev; |
| 611 | 801 | |
| 612 | 802 | if (!ts->throt_cfgs[i].init) |
| @@ -638,6 +828,461 @@ set_throttle: · 25 unmodified lines | ||
| 638 | 828 | return 0; |
| 639 | 829 | } |
| 640 | 830 | |
| 831 | +static irqreturn_t soctherm_thermal_isr(int irq, void *dev_id) | |
| 832 | +{ | |
| 833 | + struct tegra_soctherm *ts = dev_id; | |
| 834 | + u32 r; | |
| 835 | + | |
| 836 | + /* Case for no lock: | |
| 837 | + * Although interrupts are enabled in set_trips, there is still no need | |
| 838 | + * to lock here because the interrupts are disabled before programming | |
| 839 | + * new trip points. Hence there cant be a interrupt on the same sensor. | |
| 840 | + * An interrupt can however occur on a sensor while trips are being | |
| 841 | + * programmed on a different one. This beign a LEVEL interrupt won't | |
| 842 | + * cause a new interrupt but this is taken care of by the re-reading of | |
| 843 | + * the STATUS register in the thread function. | |
| 844 | + */ | |
| 845 | + r = readl(ts->regs + THERMCTL_INTR_STATUS); | |
| 846 | + writel(r, ts->regs + THERMCTL_INTR_DISABLE); | |
| 847 | + | |
| 848 | + return IRQ_WAKE_THREAD; | |
| 849 | +} | |
| 850 | + | |
| 851 | +/** | |
| 852 | + * soctherm_thermal_isr_thread() - Handles a thermal interrupt request | |
| 853 | + * @irq: The interrupt number being requested; not used | |
| 854 | + * @dev_id: Opaque pointer to tegra_soctherm; | |
| 855 | + * | |
| 856 | + * Clears the interrupt status register if there are expected | |
| 857 | + * interrupt bits set. | |
| 858 | + * The interrupt(s) are then handled by updating the corresponding | |
| 859 | + * thermal zones. | |
| 860 | + * | |
| 861 | + * An error is logged if any unexpected interrupt bits are set. | |
| 862 | + * | |
| 863 | + * Disabled interrupts are re-enabled. | |
| 864 | + * | |
| 865 | + * Return: %IRQ_HANDLED. Interrupt was handled and no further processing | |
| 866 | + * is needed. | |
| 867 | + */ | |
| 868 | +static irqreturn_t soctherm_thermal_isr_thread(int irq, void *dev_id) | |
| 869 | +{ | |
| 870 | + struct tegra_soctherm *ts = dev_id; | |
| 871 | + struct thermal_zone_device *tz; | |
| 872 | + u32 st, ex = 0, cp = 0, gp = 0, pl = 0, me = 0; | |
| 873 | + | |
| 874 | + st = readl(ts->regs + THERMCTL_INTR_STATUS); | |
| 875 | + | |
| 876 | + /* deliberately clear expected interrupts handled in SW */ | |
| 877 | + cp |= st & TH_INTR_CD0_MASK; | |
| 878 | + cp |= st & TH_INTR_CU0_MASK; | |
| 879 | + | |
| 880 | + gp |= st & TH_INTR_GD0_MASK; | |
| 881 | + gp |= st & TH_INTR_GU0_MASK; | |
| 882 | + | |
| 883 | + pl |= st & TH_INTR_PD0_MASK; | |
| 884 | + pl |= st & TH_INTR_PU0_MASK; | |
| 885 | + | |
| 886 | + me |= st & TH_INTR_MD0_MASK; | |
| 887 | + me |= st & TH_INTR_MU0_MASK; | |
| 888 | + | |
| 889 | + ex |= cp | gp | pl | me; | |
| 890 | + if (ex) { | |
| 891 | + writel(ex, ts->regs + THERMCTL_INTR_STATUS); | |
| 892 | + st &= ~ex; | |
| 893 | + | |
| 894 | + if (cp) { | |
| 895 | + tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_CPU]; | |
| 896 | + thermal_zone_device_update(tz, | |
| 897 | + THERMAL_EVENT_UNSPECIFIED); | |
| 898 | + } | |
| 899 | + | |
| 900 | + if (gp) { | |
| 901 | + tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_GPU]; | |
| 902 | + thermal_zone_device_update(tz, | |
| 903 | + THERMAL_EVENT_UNSPECIFIED); | |
| 904 | + } | |
| 905 | + | |
| 906 | + if (pl) { | |
| 907 | + tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_PLLX]; | |
| 908 | + thermal_zone_device_update(tz, | |
| 909 | + THERMAL_EVENT_UNSPECIFIED); | |
| 910 | + } | |
| 911 | + | |
| 912 | + if (me) { | |
| 913 | + tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_MEM]; | |
| 914 | + thermal_zone_device_update(tz, | |
| 915 | + THERMAL_EVENT_UNSPECIFIED); | |
| 916 | + } | |
| 917 | + } | |
| 918 | + | |
| 919 | + /* deliberately ignore expected interrupts NOT handled in SW */ | |
| 920 | + ex |= TH_INTR_IGNORE_MASK; | |
| 921 | + st &= ~ex; | |
| 922 | + | |
| 923 | + if (st) { | |
| 924 | + /* Whine about any other unexpected INTR bits still set */ | |
| 925 | + pr_err("soctherm: Ignored unexpected INTRs 0x%08x\n", st); | |
| 926 | + writel(st, ts->regs + THERMCTL_INTR_STATUS); | |
| 927 | + } | |
| 928 | + | |
| 929 | + return IRQ_HANDLED; | |
| 930 | +} | |
| 931 | + | |
| 932 | +/** | |
| 933 | + * soctherm_oc_intr_enable() - Enables the soctherm over-current interrupt | |
| 934 | + * @alarm: The soctherm throttle id | |
| 935 | + * @enable: Flag indicating enable the soctherm over-current | |
| 936 | + * interrupt or disable it | |
| 937 | + * | |
| 938 | + * Enables a specific over-current pins @alarm to raise an interrupt if the flag | |
| 939 | + * is set and the alarm corresponds to OC1, OC2, OC3, or OC4. | |
| 940 | + */ | |
| 941 | +static void soctherm_oc_intr_enable(struct tegra_soctherm *ts, | |
| 942 | + enum soctherm_throttle_id alarm, | |
| 943 | + bool enable) | |
| 944 | +{ | |
| 945 | + u32 r; | |
| 946 | + | |
| 947 | + if (!enable) | |
| 948 | + return; | |
| 949 | + | |
| 950 | + r = readl(ts->regs + OC_INTR_ENABLE); | |
| 951 | + switch (alarm) { | |
| 952 | + case THROTTLE_OC1: | |
| 953 | + r = REG_SET_MASK(r, OC_INTR_OC1_MASK, 1); | |
| 954 | + break; | |
| 955 | + case THROTTLE_OC2: | |
| 956 | + r = REG_SET_MASK(r, OC_INTR_OC2_MASK, 1); | |
| 957 | + break; | |
| 958 | + case THROTTLE_OC3: | |
| 959 | + r = REG_SET_MASK(r, OC_INTR_OC3_MASK, 1); | |
| 960 | + break; | |
| 961 | + case THROTTLE_OC4: | |
| 962 | + r = REG_SET_MASK(r, OC_INTR_OC4_MASK, 1); | |
| 963 | + break; | |
| 964 | + default: | |
| 965 | + r = 0; | |
| 966 | + break; | |
| 967 | + } | |
| 968 | + writel(r, ts->regs + OC_INTR_ENABLE); | |
| 969 | +} | |
| 970 | + | |
| 971 | +/** | |
| 972 | + * soctherm_handle_alarm() - Handles soctherm alarms | |
| 973 | + * @alarm: The soctherm throttle id | |
| 974 | + * | |
| 975 | + * "Handles" over-current alarms (OC1, OC2, OC3, and OC4) by printing | |
| 976 | + * a warning or informative message. | |
| 977 | + * | |
| 978 | + * Return: -EINVAL for @alarm = THROTTLE_OC3, otherwise 0 (success). | |
| 979 | + */ | |
| 980 | +static int soctherm_handle_alarm(enum soctherm_throttle_id alarm) | |
| 981 | +{ | |
| 982 | + int rv = -EINVAL; | |
| 983 | + | |
| 984 | + switch (alarm) { | |
| 985 | + case THROTTLE_OC1: | |
| 986 | + pr_debug("soctherm: Successfully handled OC1 alarm\n"); | |
| 987 | + rv = 0; | |
| 988 | + break; | |
| 989 | + | |
| 990 | + case THROTTLE_OC2: | |
| 991 | + pr_debug("soctherm: Successfully handled OC2 alarm\n"); | |
| 992 | + rv = 0; | |
| 993 | + break; | |
| 994 | + | |
| 995 | + case THROTTLE_OC3: | |
| 996 | + pr_debug("soctherm: Successfully handled OC3 alarm\n"); | |
| 997 | + rv = 0; | |
| 998 | + break; | |
| 999 | + | |
| 1000 | + case THROTTLE_OC4: | |
| 1001 | + pr_debug("soctherm: Successfully handled OC4 alarm\n"); | |
| 1002 | + rv = 0; | |
| 1003 | + break; | |
| 1004 | + | |
| 1005 | + default: | |
| 1006 | + break; | |
| 1007 | + } | |
| 1008 | + | |
| 1009 | + if (rv) | |
| 1010 | + pr_err("soctherm: ERROR in handling %s alarm\n", | |
| 1011 | + throt_names[alarm]); | |
| 1012 | + | |
| 1013 | + return rv; | |
| 1014 | +} | |
| 1015 | + | |
| 1016 | +/** | |
| 1017 | + * soctherm_edp_isr_thread() - log an over-current interrupt request | |
| 1018 | + * @irq: OC irq number. Currently not being used. See description | |
| 1019 | + * @arg: a void pointer for callback, currently not being used | |
| 1020 | + * | |
| 1021 | + * Over-current events are handled in hardware. This function is called to log | |
| 1022 | + * and handle any OC events that happened. Additionally, it checks every | |
| 1023 | + * over-current interrupt registers for registers are set but | |
| 1024 | + * was not expected (i.e. any discrepancy in interrupt status) by the function, | |
| 1025 | + * the discrepancy will logged. | |
| 1026 | + * | |
| 1027 | + * Return: %IRQ_HANDLED | |
| 1028 | + */ | |
| 1029 | +static irqreturn_t soctherm_edp_isr_thread(int irq, void *arg) | |
| 1030 | +{ | |
| 1031 | + struct tegra_soctherm *ts = arg; | |
| 1032 | + u32 st, ex, oc1, oc2, oc3, oc4; | |
| 1033 | + | |
| 1034 | + st = readl(ts->regs + OC_INTR_STATUS); | |
| 1035 | + | |
| 1036 | + /* deliberately clear expected interrupts handled in SW */ | |
| 1037 | + oc1 = st & OC_INTR_OC1_MASK; | |
| 1038 | + oc2 = st & OC_INTR_OC2_MASK; | |
| 1039 | + oc3 = st & OC_INTR_OC3_MASK; | |
| 1040 | + oc4 = st & OC_INTR_OC4_MASK; | |
| 1041 | + ex = oc1 | oc2 | oc3 | oc4; | |
| 1042 | + | |
| 1043 | + pr_err("soctherm: OC ALARM 0x%08x\n", ex); | |
| 1044 | + if (ex) { | |
| 1045 | + writel(st, ts->regs + OC_INTR_STATUS); | |
| 1046 | + st &= ~ex; | |
| 1047 | + | |
| 1048 | + if (oc1 && !soctherm_handle_alarm(THROTTLE_OC1)) | |
| 1049 | + soctherm_oc_intr_enable(ts, THROTTLE_OC1, true); | |
| 1050 | + | |
| 1051 | + if (oc2 && !soctherm_handle_alarm(THROTTLE_OC2)) | |
| 1052 | + soctherm_oc_intr_enable(ts, THROTTLE_OC2, true); | |
| 1053 | + | |
| 1054 | + if (oc3 && !soctherm_handle_alarm(THROTTLE_OC3)) | |
| 1055 | + soctherm_oc_intr_enable(ts, THROTTLE_OC3, true); | |
| 1056 | + | |
| 1057 | + if (oc4 && !soctherm_handle_alarm(THROTTLE_OC4)) | |
| 1058 | + soctherm_oc_intr_enable(ts, THROTTLE_OC4, true); | |
| 1059 | + | |
| 1060 | + if (oc1 && soc_irq_cdata.irq_enable & BIT(0)) | |
| 1061 | + handle_nested_irq( | |
| 1062 | + irq_find_mapping(soc_irq_cdata.domain, 0)); | |
| 1063 | + | |
| 1064 | + if (oc2 && soc_irq_cdata.irq_enable & BIT(1)) | |
| 1065 | + handle_nested_irq( | |
| 1066 | + irq_find_mapping(soc_irq_cdata.domain, 1)); | |
| 1067 | + | |
| 1068 | + if (oc3 && soc_irq_cdata.irq_enable & BIT(2)) | |
| 1069 | + handle_nested_irq( | |
| 1070 | + irq_find_mapping(soc_irq_cdata.domain, 2)); | |
| 1071 | + | |
| 1072 | + if (oc4 && soc_irq_cdata.irq_enable & BIT(3)) | |
| 1073 | + handle_nested_irq( | |
| 1074 | + irq_find_mapping(soc_irq_cdata.domain, 3)); | |
| 1075 | + } | |
| 1076 | + | |
| 1077 | + if (st) { | |
| 1078 | + pr_err("soctherm: Ignored unexpected OC ALARM 0x%08x\n", st); | |
| 1079 | + writel(st, ts->regs + OC_INTR_STATUS); | |
| 1080 | + } | |
| 1081 | + | |
| 1082 | + return IRQ_HANDLED; | |
| 1083 | +} | |
| 1084 | + | |
| 1085 | +/** | |
| 1086 | + * soctherm_edp_isr() - Disables any active interrupts | |
| 1087 | + * @irq: The interrupt request number | |
| 1088 | + * @arg: Opaque pointer to an argument | |
| 1089 | + * | |
| 1090 | + * Writes to the OC_INTR_DISABLE register the over current interrupt status, | |
| 1091 | + * masking any asserted interrupts. Doing this prevents the same interrupts | |
| 1092 | + * from triggering this isr repeatedly. The thread woken by this isr will | |
| 1093 | + * handle asserted interrupts and subsequently unmask/re-enable them. | |
| 1094 | + * | |
| 1095 | + * The OC_INTR_DISABLE register indicates which OC interrupts | |
| 1096 | + * have been disabled. | |
| 1097 | + * | |
| 1098 | + * Return: %IRQ_WAKE_THREAD, handler requests to wake the handler thread | |
| 1099 | + */ | |
| 1100 | +static irqreturn_t soctherm_edp_isr(int irq, void *arg) | |
| 1101 | +{ | |
| 1102 | + struct tegra_soctherm *ts = arg; | |
| 1103 | + u32 r; | |
| 1104 | + | |
| 1105 | + if (!ts) | |
| 1106 | + return IRQ_NONE; | |
| 1107 | + | |
| 1108 | + r = readl(ts->regs + OC_INTR_STATUS); | |
| 1109 | + writel(r, ts->regs + OC_INTR_DISABLE); | |
| 1110 | + | |
| 1111 | + return IRQ_WAKE_THREAD; | |
| 1112 | +} | |
| 1113 | + | |
| 1114 | +/** | |
| 1115 | + * soctherm_oc_irq_lock() - locks the over-current interrupt request | |
| 1116 | + * @data: Interrupt request data | |
| 1117 | + * | |
| 1118 | + * Looks up the chip data from @data and locks the mutex associated with | |
| 1119 | + * a particular over-current interrupt request. | |
| 1120 | + */ | |
| 1121 | +static void soctherm_oc_irq_lock(struct irq_data *data) | |
| 1122 | +{ | |
| 1123 | + struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
| 1124 | + | |
| 1125 | + mutex_lock(&d->irq_lock); | |
| 1126 | +} | |
| 1127 | + | |
| 1128 | +/** | |
| 1129 | + * soctherm_oc_irq_sync_unlock() - Unlocks the OC interrupt request | |
| 1130 | + * @data: Interrupt request data | |
| 1131 | + * | |
| 1132 | + * Looks up the interrupt request data @data and unlocks the mutex associated | |
| 1133 | + * with a particular over-current interrupt request. | |
| 1134 | + */ | |
| 1135 | +static void soctherm_oc_irq_sync_unlock(struct irq_data *data) | |
| 1136 | +{ | |
| 1137 | + struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
| 1138 | + | |
| 1139 | + mutex_unlock(&d->irq_lock); | |
| 1140 | +} | |
| 1141 | + | |
| 1142 | +/** | |
| 1143 | + * soctherm_oc_irq_enable() - Enables the SOC_THERM over-current interrupt queue | |
| 1144 | + * @data: irq_data structure of the chip | |
| 1145 | + * | |
| 1146 | + * Sets the irq_enable bit of SOC_THERM allowing SOC_THERM | |
| 1147 | + * to respond to over-current interrupts. | |
| 1148 | + * | |
| 1149 | + */ | |
| 1150 | +static void soctherm_oc_irq_enable(struct irq_data *data) | |
| 1151 | +{ | |
| 1152 | + struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
| 1153 | + | |
| 1154 | + d->irq_enable |= BIT(data->hwirq); | |
| 1155 | +} | |
| 1156 | + | |
| 1157 | +/** | |
| 1158 | + * soctherm_oc_irq_disable() - Disables overcurrent interrupt requests | |
| 1159 | + * @irq_data: The interrupt request information | |
| 1160 | + * | |
| 1161 | + * Clears the interrupt request enable bit of the overcurrent | |
| 1162 | + * interrupt request chip data. | |
| 1163 | + * | |
| 1164 | + * Return: Nothing is returned (void) | |
| 1165 | + */ | |
| 1166 | +static void soctherm_oc_irq_disable(struct irq_data *data) | |
| 1167 | +{ | |
| 1168 | + struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
| 1169 | + | |
| 1170 | + d->irq_enable &= ~BIT(data->hwirq); | |
| 1171 | +} | |
| 1172 | + | |
| 1173 | +static int soctherm_oc_irq_set_type(struct irq_data *data, unsigned int type) | |
| 1174 | +{ | |
| 1175 | + return 0; | |
| 1176 | +} | |
| 1177 | + | |
| 1178 | +/** | |
| 1179 | + * soctherm_oc_irq_map() - SOC_THERM interrupt request domain mapper | |
| 1180 | + * @h: Interrupt request domain | |
| 1181 | + * @virq: Virtual interrupt request number | |
| 1182 | + * @hw: Hardware interrupt request number | |
| 1183 | + * | |
| 1184 | + * Mapping callback function for SOC_THERM's irq_domain. When a SOC_THERM | |
| 1185 | + * interrupt request is called, the irq_domain takes the request's virtual | |
| 1186 | + * request number (much like a virtual memory address) and maps it to a | |
| 1187 | + * physical hardware request number. | |
| 1188 | + * | |
| 1189 | + * When a mapping doesn't already exist for a virtual request number, the | |
| 1190 | + * irq_domain calls this function to associate the virtual request number with | |
| 1191 | + * a hardware request number. | |
| 1192 | + * | |
| 1193 | + * Return: 0 | |
| 1194 | + */ | |
| 1195 | +static int soctherm_oc_irq_map(struct irq_domain *h, unsigned int virq, | |
| 1196 | + irq_hw_number_t hw) | |
| 1197 | +{ | |
| 1198 | + struct soctherm_oc_irq_chip_data *data = h->host_data; | |
| 1199 | + | |
| 1200 | + irq_set_chip_data(virq, data); | |
| 1201 | + irq_set_chip(virq, &data->irq_chip); | |
| 1202 | + irq_set_nested_thread(virq, 1); | |
| 1203 | + return 0; | |
| 1204 | +} | |
| 1205 | + | |
| 1206 | +/** | |
| 1207 | + * soctherm_irq_domain_xlate_twocell() - xlate for soctherm interrupts | |
| 1208 | + * @d: Interrupt request domain | |
| 1209 | + * @intspec: Array of u32s from DTs "interrupt" property | |
| 1210 | + * @intsize: Number of values inside the intspec array | |
| 1211 | + * @out_hwirq: HW IRQ value associated with this interrupt | |
| 1212 | + * @out_type: The IRQ SENSE type for this interrupt. | |
| 1213 | + * | |
| 1214 | + * This Device Tree IRQ specifier translation function will translate a | |
| 1215 | + * specific "interrupt" as defined by 2 DT values where the cell values map | |
| 1216 | + * the hwirq number + 1 and linux irq flags. Since the output is the hwirq | |
| 1217 | + * number, this function will subtract 1 from the value listed in DT. | |
| 1218 | + * | |
| 1219 | + * Return: 0 | |
| 1220 | + */ | |
| 1221 | +static int soctherm_irq_domain_xlate_twocell(struct irq_domain *d, | |
| 1222 | + struct device_node *ctrlr, const u32 *intspec, unsigned int intsize, | |
| 1223 | + irq_hw_number_t *out_hwirq, unsigned int *out_type) | |
| 1224 | +{ | |
| 1225 | + if (WARN_ON(intsize < 2)) | |
| 1226 | + return -EINVAL; | |
| 1227 | + | |
| 1228 | + /* | |
| 1229 | + * The HW value is 1 index less than the DT IRQ values. | |
| 1230 | + * i.e. OC4 goes to HW index 3. | |
| 1231 | + */ | |
| 1232 | + *out_hwirq = intspec[0] - 1; | |
| 1233 | + *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; | |
| 1234 | + return 0; | |
| 1235 | +} | |
| 1236 | + | |
| 1237 | +static const struct irq_domain_ops soctherm_oc_domain_ops = { | |
| 1238 | + .map = soctherm_oc_irq_map, | |
| 1239 | + .xlate = soctherm_irq_domain_xlate_twocell, | |
| 1240 | +}; | |
| 1241 | + | |
| 1242 | +/** | |
| 1243 | + * soctherm_oc_int_init() - Initial enabling of the over | |
| 1244 | + * current interrupts | |
| 1245 | + * @np: The devicetree node for soctherm | |
| 1246 | + * @num_irqs: The number of new interrupt requests | |
| 1247 | + * | |
| 1248 | + * Sets the over current interrupt request chip data | |
| 1249 | + * | |
| 1250 | + * Return: 0 on success or if overcurrent interrupts are not enabled, | |
| 1251 | + * -ENOMEM (out of memory), or irq_base if the function failed to | |
| 1252 | + * allocate the irqs | |
| 1253 | + */ | |
| 1254 | +static int soctherm_oc_int_init(struct device_node *np, int num_irqs) | |
| 1255 | +{ | |
| 1256 | + if (!num_irqs) { | |
| 1257 | + pr_info("%s(): OC interrupts are not enabled\n", __func__); | |
| 1258 | + return 0; | |
| 1259 | + } | |
| 1260 | + | |
| 1261 | + mutex_init(&soc_irq_cdata.irq_lock); | |
| 1262 | + soc_irq_cdata.irq_enable = 0; | |
| 1263 | + | |
| 1264 | + soc_irq_cdata.irq_chip.name = "soc_therm_oc"; | |
| 1265 | + soc_irq_cdata.irq_chip.irq_bus_lock = soctherm_oc_irq_lock; | |
| 1266 | + soc_irq_cdata.irq_chip.irq_bus_sync_unlock = | |
| 1267 | + soctherm_oc_irq_sync_unlock; | |
| 1268 | + soc_irq_cdata.irq_chip.irq_disable = soctherm_oc_irq_disable; | |
| 1269 | + soc_irq_cdata.irq_chip.irq_enable = soctherm_oc_irq_enable; | |
| 1270 | + soc_irq_cdata.irq_chip.irq_set_type = soctherm_oc_irq_set_type; | |
| 1271 | + soc_irq_cdata.irq_chip.irq_set_wake = NULL; | |
| 1272 | + | |
| 1273 | + soc_irq_cdata.domain = irq_domain_add_linear(np, num_irqs, | |
| 1274 | + &soctherm_oc_domain_ops, | |
| 1275 | + &soc_irq_cdata); | |
| 1276 | + | |
| 1277 | + if (!soc_irq_cdata.domain) { | |
| 1278 | + pr_err("%s: Failed to create IRQ domain\n", __func__); | |
| 1279 | + return -ENOMEM; | |
| 1280 | + } | |
| 1281 | + | |
| 1282 | + pr_debug("%s(): OC interrupts enabled successful\n", __func__); | |
| 1283 | + return 0; | |
| 1284 | +} | |
| 1285 | + | |
| 641 | 1286 | #ifdef CONFIG_DEBUG_FS |
| 642 | 1287 | static int regs_show(struct seq_file *s, void *data) |
| 643 | 1288 | { |
| @@ -929,6 +1574,120 @@ static const struct thermal_cooling_device_ops throt_cooling_ops = { · 285 unmodified lines | ||
| 929 | 1574 | .set_cur_state = throt_set_cdev_state, |
| 930 | 1575 | }; |
| 931 | 1576 | |
| 1577 | +static int soctherm_thermtrips_parse(struct platform_device *pdev) | |
| 1578 | +{ | |
| 1579 | + struct device *dev = &pdev->dev; | |
| 1580 | + struct tegra_soctherm *ts = dev_get_drvdata(dev); | |
| 1581 | + struct tsensor_group_thermtrips *tt = ts->soc->thermtrips; | |
| 1582 | + const int max_num_prop = ts->soc->num_ttgs * 2; | |
| 1583 | + u32 *tlb; | |
| 1584 | + int i, j, n, ret; | |
| 1585 | + | |
| 1586 | + if (!tt) | |
| 1587 | + return -ENOMEM; | |
| 1588 | + | |
| 1589 | + n = of_property_count_u32_elems(dev->of_node, "nvidia,thermtrips"); | |
| 1590 | + if (n <= 0) { | |
| 1591 | + dev_info(dev, | |
| 1592 | + "missing thermtrips, will use critical trips as shut down temp\n"); | |
| 1593 | + return n; | |
| 1594 | + } | |
| 1595 | + | |
| 1596 | + n = min(max_num_prop, n); | |
| 1597 | + | |
| 1598 | + tlb = devm_kcalloc(&pdev->dev, max_num_prop, sizeof(u32), GFP_KERNEL); | |
| 1599 | + if (!tlb) | |
| 1600 | + return -ENOMEM; | |
| 1601 | + ret = of_property_read_u32_array(dev->of_node, "nvidia,thermtrips", | |
| 1602 | + tlb, n); | |
| 1603 | + if (ret) { | |
| 1604 | + dev_err(dev, "invalid num ele: thermtrips:%d\n", ret); | |
| 1605 | + return ret; | |
| 1606 | + } | |
| 1607 | + | |
| 1608 | + i = 0; | |
| 1609 | + for (j = 0; j < n; j = j + 2) { | |
| 1610 | + if (tlb[j] >= TEGRA124_SOCTHERM_SENSOR_NUM) | |
| 1611 | + continue; | |
| 1612 | + | |
| 1613 | + tt[i].id = tlb[j]; | |
| 1614 | + tt[i].temp = tlb[j + 1]; | |
| 1615 | + i++; | |
| 1616 | + } | |
| 1617 | + | |
| 1618 | + return 0; | |
| 1619 | +} | |
| 1620 | + | |
| 1621 | +static void soctherm_oc_cfg_parse(struct device *dev, | |
| 1622 | + struct device_node *np_oc, | |
| 1623 | + struct soctherm_throt_cfg *stc) | |
| 1624 | +{ | |
| 1625 | + u32 val; | |
| 1626 | + | |
| 1627 | + if (of_property_read_bool(np_oc, "nvidia,polarity-active-low")) | |
| 1628 | + stc->oc_cfg.active_low = 1; | |
| 1629 | + else | |
| 1630 | + stc->oc_cfg.active_low = 0; | |
| 1631 | + | |
| 1632 | + if (!of_property_read_u32(np_oc, "nvidia,count-threshold", &val)) { | |
| 1633 | + stc->oc_cfg.intr_en = 1; | |
| 1634 | + stc->oc_cfg.alarm_cnt_thresh = val; | |
| 1635 | + } | |
| 1636 | + | |
| 1637 | + if (!of_property_read_u32(np_oc, "nvidia,throttle-period-us", &val)) | |
| 1638 | + stc->oc_cfg.throt_period = val; | |
| 1639 | + | |
| 1640 | + if (!of_property_read_u32(np_oc, "nvidia,alarm-filter", &val)) | |
| 1641 | + stc->oc_cfg.alarm_filter = val; | |
| 1642 | + | |
| 1643 | + /* BRIEF throttling by default, do not support STICKY */ | |
| 1644 | + stc->oc_cfg.mode = OC_THROTTLE_MODE_BRIEF; | |
| 1645 | +} | |
| 1646 | + | |
| 1647 | +static int soctherm_throt_cfg_parse(struct device *dev, | |
| 1648 | + struct device_node *np, | |
| 1649 | + struct soctherm_throt_cfg *stc) | |
| 1650 | +{ | |
| 1651 | + struct tegra_soctherm *ts = dev_get_drvdata(dev); | |
| 1652 | + int ret; | |
| 1653 | + u32 val; | |
| 1654 | + | |
| 1655 | + ret = of_property_read_u32(np, "nvidia,priority", &val); | |
| 1656 | + if (ret) { | |
| 1657 | + dev_err(dev, "throttle-cfg: %s: invalid priority\n", stc->name); | |
| 1658 | + return -EINVAL; | |
| 1659 | + } | |
| 1660 | + stc->priority = val; | |
| 1661 | + | |
| 1662 | + ret = of_property_read_u32(np, ts->soc->use_ccroc ? | |
| 1663 | + "nvidia,cpu-throt-level" : | |
| 1664 | + "nvidia,cpu-throt-percent", &val); | |
| 1665 | + if (!ret) { | |
| 1666 | + if (ts->soc->use_ccroc && | |
| 1667 | + val <= TEGRA_SOCTHERM_THROT_LEVEL_HIGH) | |
| 1668 | + stc->cpu_throt_level = val; | |
| 1669 | + else if (!ts->soc->use_ccroc && val <= 100) | |
| 1670 | + stc->cpu_throt_depth = val; | |
| 1671 | + else | |
| 1672 | + goto err; | |
| 1673 | + } else { | |
| 1674 | + goto err; | |
| 1675 | + } | |
| 1676 | + | |
| 1677 | + ret = of_property_read_u32(np, "nvidia,gpu-throt-level", &val); | |
| 1678 | + if (!ret && val <= TEGRA_SOCTHERM_THROT_LEVEL_HIGH) | |
| 1679 | + stc->gpu_throt_level = val; | |
| 1680 | + else | |
| 1681 | + goto err; | |
| 1682 | + | |
| 1683 | + return 0; | |
| 1684 | + | |
| 1685 | +err: | |
| 1686 | + dev_err(dev, "throttle-cfg: %s: no throt prop or invalid prop\n", | |
| 1687 | + stc->name); | |
| 1688 | + return -EINVAL; | |
| 1689 | +} | |
| 1690 | + | |
| 932 | 1691 | /** |
| 933 | 1692 | * soctherm_init_hw_throt_cdev() - Parse the HW throttle configurations |
| 934 | 1693 | * and register them as cooling devices. |
| @@ -939,8 +1698,7 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) · 4 unmodified lines | ||
| 939 | 1698 | struct tegra_soctherm *ts = dev_get_drvdata(dev); |
| 940 | 1699 | struct device_node *np_stc, *np_stcc; |
| 941 | 1700 | const char *name; |
| 942 | − u32 val; | |
| 943 | − int i, r; | |
| 1701 | + int i; | |
| 944 | 1702 | |
| 945 | 1703 | for (i = 0; i < THROTTLE_SIZE; i++) { |
| 946 | 1704 | ts->throt_cfgs[i].name = throt_names[i]; |
| @@ -958,6 +1716,7 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) · 11 unmodified lines | ||
| 958 | 1716 | for_each_child_of_node(np_stc, np_stcc) { |
| 959 | 1717 | struct soctherm_throt_cfg *stc; |
| 960 | 1718 | struct thermal_cooling_device *tcd; |
| 1719 | + int err; | |
| 961 | 1720 | |
| 962 | 1721 | name = np_stcc->name; |
| 963 | 1722 | stc = find_throttle_cfg_by_name(ts, name); |
| @@ -967,51 +1726,34 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) · 3 unmodified lines | ||
| 967 | 1726 | continue; |
| 968 | 1727 | } |
| 969 | 1728 | |
| 970 | − r = of_property_read_u32(np_stcc, "nvidia,priority", &val); | |
| 971 | − if (r) { | |
| 972 | − dev_info(dev, | |
| 973 | − "throttle-cfg: %s: missing priority\n", name); | |
| 974 | − continue; | |
| 1729 | + if (stc->init) { | |
| 1730 | + dev_err(dev, "throttle-cfg: %s: redefined!\n", name); | |
| 1731 | + of_node_put(np_stcc); | |
| 1732 | + break; | |
| 975 | 1733 | } |
| 976 | − stc->priority = val; | |
| 977 | − | |
| 978 | − if (ts->soc->use_ccroc) { | |
| 979 | − r = of_property_read_u32(np_stcc, | |
| 980 | − "nvidia,cpu-throt-level", | |
| 981 | − &val); | |
| 982 | − if (r) { | |
| 983 | − dev_info(dev, | |
| 984 | − "throttle-cfg: %s: missing cpu-throt-level\n", | |
| 985 | − name); | |
| 986 | − continue; | |
| 987 | − } | |
| 988 | − stc->cpu_throt_level = val; | |
| 1734 | + | |
| 1735 | + err = soctherm_throt_cfg_parse(dev, np_stcc, stc); | |
| 1736 | + if (err) | |
| 1737 | + continue; | |
| 1738 | + | |
| 1739 | + if (stc->id >= THROTTLE_OC1) { | |
| 1740 | + soctherm_oc_cfg_parse(dev, np_stcc, stc); | |
| 1741 | + stc->init = true; | |
| 989 | 1742 | } else { |
| 990 | − r = of_property_read_u32(np_stcc, | |
| 991 | − "nvidia,cpu-throt-percent", | |
| 992 | − &val); | |
| 993 | − if (r) { | |
| 994 | − dev_info(dev, | |
| 995 | − "throttle-cfg: %s: missing cpu-throt-percent\n", | |
| 996 | − name); | |
| 997 | − continue; | |
| 998 | − } | |
| 999 | − stc->cpu_throt_depth = val; | |
| 1000 | − } | |
| 1001 | 1743 | |
| 1002 | − tcd = thermal_of_cooling_device_register(np_stcc, | |
| 1744 | + tcd = thermal_of_cooling_device_register(np_stcc, | |
| 1003 | 1745 | (char *)name, ts, |
| 1004 | 1746 | &throt_cooling_ops); |
| 1005 | − of_node_put(np_stcc); | |
| 1006 | − if (IS_ERR_OR_NULL(tcd)) { | |
| 1007 | − dev_err(dev, | |
| 1008 | − "throttle-cfg: %s: failed to register cooling device\n", | |
| 1009 | − name); | |
| 1010 | − continue; | |
| 1747 | + if (IS_ERR_OR_NULL(tcd)) { | |
| 1748 | + dev_err(dev, | |
| 1749 | + "throttle-cfg: %s: failed to register cooling device\n", | |
| 1750 | + name); | |
| 1751 | + continue; | |
| 1752 | + } | |
| 1753 | + stc->cdev = tcd; | |
| 1754 | + stc->init = true; | |
| 1011 | 1755 | } |
| 1012 | 1756 | |
| 1013 | − stc->cdev = tcd; | |
| 1014 | − stc->init = true; | |
| 1015 | 1757 | } |
| 1016 | 1758 | |
| 1017 | 1759 | of_node_put(np_stc); |
| @@ -1140,6 +1882,50 @@ static void throttlectl_cpu_mn(struct tegra_soctherm *ts, · 122 unmodified lines | ||
| 1140 | 1882 | writel(r, ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU)); |
| 1141 | 1883 | } |
| 1142 | 1884 | |
| 1885 | +/** | |
| 1886 | + * throttlectl_gpu_level_select() - selects throttling level for GPU | |
| 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 | + | |
| 1143 | 1929 | /** |
| 1144 | 1930 | * soctherm_throttle_program() - programs pulse skippers' configuration |
| 1145 | 1931 | * @throt: the LIGHT/HEAVY of the throttle event id. |
| @@ -1156,12 +1942,17 @@ static void soctherm_throttle_program(struct tegra_soctherm *ts, · 10 unmodified lines | ||
| 1156 | 1942 | if (!stc.init) |
| 1157 | 1943 | return; |
| 1158 | 1944 | |
| 1945 | + if ((throt >= THROTTLE_OC1) && (soctherm_oc_cfg_program(ts, throt))) | |
| 1946 | + return; | |
| 1947 | + | |
| 1159 | 1948 | /* Setup PSKIP parameters */ |
| 1160 | 1949 | if (ts->soc->use_ccroc) |
| 1161 | 1950 | throttlectl_cpu_level_select(ts, throt); |
| 1162 | 1951 | else |
| 1163 | 1952 | throttlectl_cpu_mn(ts, throt); |
| 1164 | 1953 | |
| 1954 | + throttlectl_gpu_level_select(ts, throt); | |
| 1955 | + | |
| 1165 | 1956 | r = REG_SET_MASK(0, THROT_PRIORITY_LITE_PRIO_MASK, stc.priority); |
| 1166 | 1957 | writel(r, ts->regs + THROT_PRIORITY_CTRL(throt)); |
| 1167 | 1958 | |
| @@ -1215,6 +2006,57 @@ static void tegra_soctherm_throttle(struct device *dev) · 47 unmodified lines | ||
| 1215 | 2006 | writel(v, ts->regs + THERMCTL_STATS_CTL); |
| 1216 | 2007 | } |
| 1217 | 2008 | |
| 2009 | +static int soctherm_interrupts_init(struct platform_device *pdev, | |
| 2010 | + struct tegra_soctherm *tegra) | |
| 2011 | +{ | |
| 2012 | + struct device_node *np = pdev->dev.of_node; | |
| 2013 | + int ret; | |
| 2014 | + | |
| 2015 | + ret = soctherm_oc_int_init(np, TEGRA_SOC_OC_IRQ_MAX); | |
| 2016 | + if (ret < 0) { | |
| 2017 | + dev_err(&pdev->dev, "soctherm_oc_int_init failed\n"); | |
| 2018 | + return ret; | |
| 2019 | + } | |
| 2020 | + | |
| 2021 | + tegra->thermal_irq = platform_get_irq(pdev, 0); | |
| 2022 | + if (tegra->thermal_irq < 0) { | |
| 2023 | + dev_dbg(&pdev->dev, "get 'thermal_irq' failed.\n"); | |
| 2024 | + return 0; | |
| 2025 | + } | |
| 2026 | + | |
| 2027 | + tegra->edp_irq = platform_get_irq(pdev, 1); | |
| 2028 | + if (tegra->edp_irq < 0) { | |
| 2029 | + dev_dbg(&pdev->dev, "get 'edp_irq' failed.\n"); | |
| 2030 | + return 0; | |
| 2031 | + } | |
| 2032 | + | |
| 2033 | + ret = devm_request_threaded_irq(&pdev->dev, | |
| 2034 | + tegra->thermal_irq, | |
| 2035 | + soctherm_thermal_isr, | |
| 2036 | + soctherm_thermal_isr_thread, | |
| 2037 | + IRQF_ONESHOT, | |
| 2038 | + dev_name(&pdev->dev), | |
| 2039 | + tegra); | |
| 2040 | + if (ret < 0) { | |
| 2041 | + dev_err(&pdev->dev, "request_irq 'thermal_irq' failed.\n"); | |
| 2042 | + return ret; | |
| 2043 | + } | |
| 2044 | + | |
| 2045 | + ret = devm_request_threaded_irq(&pdev->dev, | |
| 2046 | + tegra->edp_irq, | |
| 2047 | + soctherm_edp_isr, | |
| 2048 | + soctherm_edp_isr_thread, | |
| 2049 | + IRQF_ONESHOT, | |
| 2050 | + "soctherm_edp", | |
| 2051 | + tegra); | |
| 2052 | + if (ret < 0) { | |
| 2053 | + dev_err(&pdev->dev, "request_irq 'edp_irq' failed.\n"); | |
| 2054 | + return ret; | |
| 2055 | + } | |
| 2056 | + | |
| 2057 | + return 0; | |
| 2058 | +} | |
| 2059 | + | |
| 1218 | 2060 | static void soctherm_init(struct platform_device *pdev) |
| 1219 | 2061 | { |
| 1220 | 2062 | struct tegra_soctherm *tegra = platform_get_drvdata(pdev); |
| @@ -1292,6 +2134,7 @@ static int tegra_soctherm_probe(struct platform_device *pdev) · 71 unmodified lines | ||
| 1292 | 2134 | if (!tegra) |
| 1293 | 2135 | return -ENOMEM; |
| 1294 | 2136 | |
| 2137 | + mutex_init(&tegra->thermctl_lock); | |
| 1295 | 2138 | dev_set_drvdata(&pdev->dev, tegra); |
| 1296 | 2139 | |
| 1297 | 2140 | tegra->soc = soc; |
| @@ -1370,6 +2213,8 @@ static int tegra_soctherm_probe(struct platform_device *pdev) · 72 unmodified lines | ||
| 1370 | 2213 | if (err) |
| 1371 | 2214 | return err; |
| 1372 | 2215 | |
| 2216 | + soctherm_thermtrips_parse(pdev); | |
| 2217 | + | |
| 1373 | 2218 | soctherm_init_hw_throt_cdev(pdev); |
| 1374 | 2219 | |
| 1375 | 2220 | soctherm_init(pdev); |
| @@ -1406,6 +2251,8 @@ static int tegra_soctherm_probe(struct platform_device *pdev) · 30 unmodified lines | ||
| 1406 | 2251 | goto disable_clocks; |
| 1407 | 2252 | } |
| 1408 | 2253 | |
| 2254 | + err = soctherm_interrupts_init(pdev, tegra); | |
| 2255 | + | |
| 1409 | 2256 | soctherm_debug_init(pdev); |
| 1410 | 2257 | |
| 1411 | 2258 | return 0; |
| @@ -1,3 +1,4 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ | |
| 1 | 2 | /* |
| 2 | 3 | * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. |
| 3 | 4 | * |
| @@ -29,6 +30,14 @@ · 25 unmodified lines | ||
| 29 | 30 | #define THERMCTL_THERMTRIP_CTL 0x80 |
| 30 | 31 | /* BITs are defined in device file */ |
| 31 | 32 | |
| 33 | +#define THERMCTL_INTR_ENABLE 0x88 | |
| 34 | +#define THERMCTL_INTR_DISABLE 0x8c | |
| 35 | +#define TH_INTR_UP_DN_EN 0x3 | |
| 36 | +#define THERM_IRQ_MEM_MASK (TH_INTR_UP_DN_EN << 24) | |
| 37 | +#define THERM_IRQ_GPU_MASK (TH_INTR_UP_DN_EN << 16) | |
| 38 | +#define THERM_IRQ_CPU_MASK (TH_INTR_UP_DN_EN << 8) | |
| 39 | +#define THERM_IRQ_TSENSE_MASK (TH_INTR_UP_DN_EN << 0) | |
| 40 | + | |
| 32 | 41 | #define SENSOR_PDIV 0x1c0 |
| 33 | 42 | #define SENSOR_PDIV_CPU_MASK (0xf << 12) |
| 34 | 43 | #define SENSOR_PDIV_GPU_MASK (0xf << 8) |
| @@ -70,6 +79,7 @@ struct tegra_tsensor_group { · 35 unmodified lines | ||
| 70 | 79 | u32 thermtrip_enable_mask; |
| 71 | 80 | u32 thermtrip_any_en_mask; |
| 72 | 81 | u32 thermtrip_threshold_mask; |
| 82 | + u32 thermctl_isr_mask; | |
| 73 | 83 | u16 thermctl_lvl0_offset; |
| 74 | 84 | u32 thermctl_lvl0_up_thresh_mask; |
| 75 | 85 | u32 thermctl_lvl0_dn_thresh_mask; |
| @@ -92,6 +102,11 @@ struct tegra_tsensor { · 16 unmodified lines | ||
| 92 | 102 | const struct tegra_tsensor_group *group; |
| 93 | 103 | }; |
| 94 | 104 | |
| 105 | +struct tsensor_group_thermtrips { | |
| 106 | + u8 id; | |
| 107 | + u32 temp; | |
| 108 | +}; | |
| 109 | + | |
| 95 | 110 | struct tegra_soctherm_fuse { |
| 96 | 111 | u32 fuse_base_cp_mask, fuse_base_cp_shift; |
| 97 | 112 | u32 fuse_base_ft_mask, fuse_base_ft_shift; |
| @@ -113,6 +128,7 @@ struct tegra_soctherm_soc { · 15 unmodified lines | ||
| 113 | 128 | const int thresh_grain; |
| 114 | 129 | const unsigned int bptt; |
| 115 | 130 | const bool use_ccroc; |
| 131 | + struct tsensor_group_thermtrips *thermtrips; | |
| 116 | 132 | }; |
| 117 | 133 | |
| 118 | 134 | int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse, |
| @@ -1,5 +1,6 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 1 | 2 | /* |
| 2 | − * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. | |
| 3 | + * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. | |
| 3 | 4 | * |
| 4 | 5 | * This software is licensed under the terms of the GNU General Public |
| 5 | 6 | * License version 2, as published by the Free Software Foundation, and |
| @@ -55,6 +56,7 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_cpu = { · 49 unmodified lines | ||
| 55 | 56 | .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK, |
| 56 | 57 | .thermtrip_enable_mask = TEGRA124_THERMTRIP_CPU_EN_MASK, |
| 57 | 58 | .thermtrip_threshold_mask = TEGRA124_THERMTRIP_CPU_THRESH_MASK, |
| 59 | + .thermctl_isr_mask = THERM_IRQ_CPU_MASK, | |
| 58 | 60 | .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU, |
| 59 | 61 | .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK, |
| 60 | 62 | .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK, |
| @@ -73,6 +75,7 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_gpu = { · 12 unmodified lines | ||
| 73 | 75 | .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK, |
| 74 | 76 | .thermtrip_enable_mask = TEGRA124_THERMTRIP_GPU_EN_MASK, |
| 75 | 77 | .thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK, |
| 78 | + .thermctl_isr_mask = THERM_IRQ_GPU_MASK, | |
| 76 | 79 | .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU, |
| 77 | 80 | .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK, |
| 78 | 81 | .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK, |
| @@ -89,6 +92,7 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_pll = { · 10 unmodified lines | ||
| 89 | 92 | .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK, |
| 90 | 93 | .thermtrip_enable_mask = TEGRA124_THERMTRIP_TSENSE_EN_MASK, |
| 91 | 94 | .thermtrip_threshold_mask = TEGRA124_THERMTRIP_TSENSE_THRESH_MASK, |
| 95 | + .thermctl_isr_mask = THERM_IRQ_TSENSE_MASK, | |
| 92 | 96 | .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE, |
| 93 | 97 | .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK, |
| 94 | 98 | .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK, |
| @@ -107,6 +111,7 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_mem = { · 12 unmodified lines | ||
| 107 | 111 | .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK, |
| 108 | 112 | .thermtrip_enable_mask = TEGRA124_THERMTRIP_MEM_EN_MASK, |
| 109 | 113 | .thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK, |
| 114 | + .thermctl_isr_mask = THERM_IRQ_MEM_MASK, | |
| 110 | 115 | .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM, |
| 111 | 116 | .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK, |
| 112 | 117 | .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK, |
| @@ -1,5 +1,6 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 1 | 2 | /* |
| 2 | − * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. | |
| 3 | + * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. | |
| 3 | 4 | * |
| 4 | 5 | * This software is licensed under the terms of the GNU General Public |
| 5 | 6 | * License version 2, as published by the Free Software Foundation, and |
| @@ -55,6 +56,7 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_cpu = { · 49 unmodified lines | ||
| 55 | 56 | .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK, |
| 56 | 57 | .thermtrip_enable_mask = TEGRA132_THERMTRIP_CPU_EN_MASK, |
| 57 | 58 | .thermtrip_threshold_mask = TEGRA132_THERMTRIP_CPU_THRESH_MASK, |
| 59 | + .thermctl_isr_mask = THERM_IRQ_CPU_MASK, | |
| 58 | 60 | .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU, |
| 59 | 61 | .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK, |
| 60 | 62 | .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK, |
| @@ -73,6 +75,7 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_gpu = { · 12 unmodified lines | ||
| 73 | 75 | .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK, |
| 74 | 76 | .thermtrip_enable_mask = TEGRA132_THERMTRIP_GPU_EN_MASK, |
| 75 | 77 | .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK, |
| 78 | + .thermctl_isr_mask = THERM_IRQ_GPU_MASK, | |
| 76 | 79 | .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU, |
| 77 | 80 | .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK, |
| 78 | 81 | .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK, |
| @@ -89,6 +92,7 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_pll = { · 10 unmodified lines | ||
| 89 | 92 | .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK, |
| 90 | 93 | .thermtrip_enable_mask = TEGRA132_THERMTRIP_TSENSE_EN_MASK, |
| 91 | 94 | .thermtrip_threshold_mask = TEGRA132_THERMTRIP_TSENSE_THRESH_MASK, |
| 95 | + .thermctl_isr_mask = THERM_IRQ_TSENSE_MASK, | |
| 92 | 96 | .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE, |
| 93 | 97 | .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK, |
| 94 | 98 | .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK, |
| @@ -107,6 +111,7 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_mem = { · 12 unmodified lines | ||
| 107 | 111 | .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK, |
| 108 | 112 | .thermtrip_enable_mask = TEGRA132_THERMTRIP_MEM_EN_MASK, |
| 109 | 113 | .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK, |
| 114 | + .thermctl_isr_mask = THERM_IRQ_MEM_MASK, | |
| 110 | 115 | .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM, |
| 111 | 116 | .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK, |
| 112 | 117 | .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK, |
| @@ -1,5 +1,6 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 1 | 2 | /* |
| 2 | − * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. | |
| 3 | + * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. | |
| 3 | 4 | * |
| 4 | 5 | * This software is licensed under the terms of the GNU General Public |
| 5 | 6 | * License version 2, as published by the Free Software Foundation, and |
| @@ -56,6 +57,7 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_cpu = { · 50 unmodified lines | ||
| 56 | 57 | .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK, |
| 57 | 58 | .thermtrip_enable_mask = TEGRA210_THERMTRIP_CPU_EN_MASK, |
| 58 | 59 | .thermtrip_threshold_mask = TEGRA210_THERMTRIP_CPU_THRESH_MASK, |
| 60 | + .thermctl_isr_mask = THERM_IRQ_CPU_MASK, | |
| 59 | 61 | .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU, |
| 60 | 62 | .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK, |
| 61 | 63 | .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK, |
| @@ -74,6 +76,7 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_gpu = { · 12 unmodified lines | ||
| 74 | 76 | .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK, |
| 75 | 77 | .thermtrip_enable_mask = TEGRA210_THERMTRIP_GPU_EN_MASK, |
| 76 | 78 | .thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK, |
| 79 | + .thermctl_isr_mask = THERM_IRQ_GPU_MASK, | |
| 77 | 80 | .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU, |
| 78 | 81 | .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK, |
| 79 | 82 | .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK, |
| @@ -90,6 +93,7 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_pll = { · 10 unmodified lines | ||
| 90 | 93 | .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK, |
| 91 | 94 | .thermtrip_enable_mask = TEGRA210_THERMTRIP_TSENSE_EN_MASK, |
| 92 | 95 | .thermtrip_threshold_mask = TEGRA210_THERMTRIP_TSENSE_THRESH_MASK, |
| 96 | + .thermctl_isr_mask = THERM_IRQ_TSENSE_MASK, | |
| 93 | 97 | .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE, |
| 94 | 98 | .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK, |
| 95 | 99 | .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK, |
| @@ -108,6 +112,7 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_mem = { · 12 unmodified lines | ||
| 108 | 112 | .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK, |
| 109 | 113 | .thermtrip_enable_mask = TEGRA210_THERMTRIP_MEM_EN_MASK, |
| 110 | 114 | .thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK, |
| 115 | + .thermctl_isr_mask = THERM_IRQ_MEM_MASK, | |
| 111 | 116 | .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM, |
| 112 | 117 | .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK, |
| 113 | 118 | .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK, |
| @@ -203,6 +208,13 @@ static const struct tegra_soctherm_fuse tegra210_soctherm_fuse = { · 89 unmodified lines | ||
| 203 | 208 | .fuse_spare_realignment = 0, |
| 204 | 209 | }; |
| 205 | 210 | |
| 211 | +struct tsensor_group_thermtrips tegra210_tsensor_thermtrips[] = { | |
| 212 | + {.id = TEGRA124_SOCTHERM_SENSOR_NUM}, | |
| 213 | + {.id = TEGRA124_SOCTHERM_SENSOR_NUM}, | |
| 214 | + {.id = TEGRA124_SOCTHERM_SENSOR_NUM}, | |
| 215 | + {.id = TEGRA124_SOCTHERM_SENSOR_NUM}, | |
| 216 | +}; | |
| 217 | + | |
| 206 | 218 | const struct tegra_soctherm_soc tegra210_soctherm = { |
| 207 | 219 | .tsensors = tegra210_tsensors, |
| 208 | 220 | .num_tsensors = ARRAY_SIZE(tegra210_tsensors), |
| @@ -212,4 +224,5 @@ const struct tegra_soctherm_soc tegra210_soctherm = { · 3 unmodified lines | ||
| 212 | 224 | .thresh_grain = TEGRA210_THRESH_GRAIN, |
| 213 | 225 | .bptt = TEGRA210_BPTT, |
| 214 | 226 | .use_ccroc = false, |
| 227 | + .thermtrips = tegra210_tsensor_thermtrips, | |
| 215 | 228 | }; |
| @@ -29,6 +29,9 @@ static int gadc_thermal_adc_to_temp(struct gadc_thermal_info *gti, int val) · 28 unmodified lines | ||
| 29 | 29 | int temp, temp_hi, temp_lo, adc_hi, adc_lo; |
| 30 | 30 | int i; |
| 31 | 31 | |
| 32 | + if (!gti->lookup_table) | |
| 33 | + return val; | |
| 34 | + | |
| 32 | 35 | for (i = 0; i < gti->nlookup_table; i++) { |
| 33 | 36 | if (val >= gti->lookup_table[2 * i + 1]) |
| 34 | 37 | break; |
| @@ -81,9 +84,9 @@ static int gadc_thermal_read_linear_lookup_table(struct device *dev, · 46 unmodified lines | ||
| 81 | 84 | |
| 82 | 85 | ntable = of_property_count_elems_of_size(np, "temperature-lookup-table", |
| 83 | 86 | sizeof(u32)); |
| 84 | − if (ntable < 0) { | |
| 85 | − dev_err(dev, "Lookup table is not provided\n"); | |
| 86 | − return ntable; | |
| 87 | + if (ntable <= 0) { | |
| 88 | + dev_notice(dev, "no lookup table, assuming DAC channel returns milliCelcius\n"); | |
| 89 | + return 0; | |
| 87 | 90 | } |
| 88 | 91 | |
| 89 | 92 | if (ntable % 2) { |
| @@ -1046,6 +1046,55 @@ thermal_of_cooling_device_register(struct device_node *np, · 1045 unmodified lines | ||
| 1046 | 1046 | } |
| 1047 | 1047 | EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); |
| 1048 | 1048 | |
| 1049 | +static void thermal_cooling_device_release(struct device *dev, void *res) | |
| 1050 | +{ | |
| 1051 | + thermal_cooling_device_unregister( | |
| 1052 | + *(struct thermal_cooling_device **)res); | |
| 1053 | +} | |
| 1054 | + | |
| 1055 | +/** | |
| 1056 | + * devm_thermal_of_cooling_device_register() - register an OF thermal cooling | |
| 1057 | + * device | |
| 1058 | + * @dev: a valid struct device pointer of a sensor device. | |
| 1059 | + * @np: a pointer to a device tree node. | |
| 1060 | + * @type: the thermal cooling device type. | |
| 1061 | + * @devdata: device private data. | |
| 1062 | + * @ops: standard thermal cooling devices callbacks. | |
| 1063 | + * | |
| 1064 | + * This function will register a cooling device with device tree node reference. | |
| 1065 | + * This interface function adds a new thermal cooling device (fan/processor/...) | |
| 1066 | + * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself | |
| 1067 | + * to all the thermal zone devices registered at the same time. | |
| 1068 | + * | |
| 1069 | + * Return: a pointer to the created struct thermal_cooling_device or an | |
| 1070 | + * ERR_PTR. Caller must check return value with IS_ERR*() helpers. | |
| 1071 | + */ | |
| 1072 | +struct thermal_cooling_device * | |
| 1073 | +devm_thermal_of_cooling_device_register(struct device *dev, | |
| 1074 | + struct device_node *np, | |
| 1075 | + char *type, void *devdata, | |
| 1076 | + const struct thermal_cooling_device_ops *ops) | |
| 1077 | +{ | |
| 1078 | + struct thermal_cooling_device **ptr, *tcd; | |
| 1079 | + | |
| 1080 | + ptr = devres_alloc(thermal_cooling_device_release, sizeof(*ptr), | |
| 1081 | + GFP_KERNEL); | |
| 1082 | + if (!ptr) | |
| 1083 | + return ERR_PTR(-ENOMEM); | |
| 1084 | + | |
| 1085 | + tcd = __thermal_cooling_device_register(np, type, devdata, ops); | |
| 1086 | + if (IS_ERR(tcd)) { | |
| 1087 | + devres_free(ptr); | |
| 1088 | + return tcd; | |
| 1089 | + } | |
| 1090 | + | |
| 1091 | + *ptr = tcd; | |
| 1092 | + devres_add(dev, ptr); | |
| 1093 | + | |
| 1094 | + return tcd; | |
| 1095 | +} | |
| 1096 | +EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register); | |
| 1097 | + | |
| 1049 | 1098 | static void __unbind(struct thermal_zone_device *tz, int mask, |
| 1050 | 1099 | struct thermal_cooling_device *cdev) |
| 1051 | 1100 | { |
| @@ -0,0 +1,129 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/module.h> | |
| 7 | +#include <linux/of_address.h> | |
| 8 | +#include <linux/platform_device.h> | |
| 9 | +#include <linux/thermal.h> | |
| 10 | + | |
| 11 | +struct thermal_mmio { | |
| 12 | + void __iomem *mmio_base; | |
| 13 | + u32 (*read_mmio)(void __iomem *mmio_base); | |
| 14 | + u32 mask; | |
| 15 | + int factor; | |
| 16 | +}; | |
| 17 | + | |
| 18 | +static u32 thermal_mmio_readb(void __iomem *mmio_base) | |
| 19 | +{ | |
| 20 | + return readb(mmio_base); | |
| 21 | +} | |
| 22 | + | |
| 23 | +static int thermal_mmio_get_temperature(void *private, int *temp) | |
| 24 | +{ | |
| 25 | + int t; | |
| 26 | + struct thermal_mmio *sensor = | |
| 27 | + (struct thermal_mmio *)private; | |
| 28 | + | |
| 29 | + t = sensor->read_mmio(sensor->mmio_base) & sensor->mask; | |
| 30 | + t *= sensor->factor; | |
| 31 | + | |
| 32 | + *temp = t; | |
| 33 | + | |
| 34 | + return 0; | |
| 35 | +} | |
| 36 | + | |
| 37 | +static struct thermal_zone_of_device_ops thermal_mmio_ops = { | |
| 38 | + .get_temp = thermal_mmio_get_temperature, | |
| 39 | +}; | |
| 40 | + | |
| 41 | +static int thermal_mmio_probe(struct platform_device *pdev) | |
| 42 | +{ | |
| 43 | + struct resource *resource; | |
| 44 | + struct thermal_mmio *sensor; | |
| 45 | + int (*sensor_init_func)(struct platform_device *pdev, | |
| 46 | + struct thermal_mmio *sensor); | |
| 47 | + struct thermal_zone_device *thermal_zone; | |
| 48 | + int ret; | |
| 49 | + int temperature; | |
| 50 | + | |
| 51 | + sensor = devm_kzalloc(&pdev->dev, sizeof(*sensor), GFP_KERNEL); | |
| 52 | + if (!sensor) | |
| 53 | + return -ENOMEM; | |
| 54 | + | |
| 55 | + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 56 | + if (IS_ERR(resource)) { | |
| 57 | + dev_err(&pdev->dev, | |
| 58 | + "fail to get platform memory resource (%ld)\n", | |
| 59 | + PTR_ERR(resource)); | |
| 60 | + return PTR_ERR(resource); | |
| 61 | + } | |
| 62 | + | |
| 63 | + sensor->mmio_base = devm_ioremap_resource(&pdev->dev, resource); | |
| 64 | + if (IS_ERR(sensor->mmio_base)) { | |
| 65 | + dev_err(&pdev->dev, "failed to ioremap memory (%ld)\n", | |
| 66 | + PTR_ERR(sensor->mmio_base)); | |
| 67 | + return PTR_ERR(sensor->mmio_base); | |
| 68 | + } | |
| 69 | + | |
| 70 | + sensor_init_func = device_get_match_data(&pdev->dev); | |
| 71 | + if (sensor_init_func) { | |
| 72 | + ret = sensor_init_func(pdev, sensor); | |
| 73 | + if (ret) { | |
| 74 | + dev_err(&pdev->dev, | |
| 75 | + "failed to initialize sensor (%d)\n", | |
| 76 | + ret); | |
| 77 | + return ret; | |
| 78 | + } | |
| 79 | + } | |
| 80 | + | |
| 81 | + thermal_zone = devm_thermal_zone_of_sensor_register(&pdev->dev, | |
| 82 | + 0, | |
| 83 | + sensor, | |
| 84 | + &thermal_mmio_ops); | |
| 85 | + if (IS_ERR(thermal_zone)) { | |
| 86 | + dev_err(&pdev->dev, | |
| 87 | + "failed to register sensor (%ld)\n", | |
| 88 | + PTR_ERR(thermal_zone)); | |
| 89 | + return PTR_ERR(thermal_zone); | |
| 90 | + } | |
| 91 | + | |
| 92 | + thermal_mmio_get_temperature(sensor, &temperature); | |
| 93 | + dev_info(&pdev->dev, | |
| 94 | + "thermal mmio sensor %s registered, current temperature: %d\n", | |
| 95 | + pdev->name, temperature); | |
| 96 | + | |
| 97 | + return 0; | |
| 98 | +} | |
| 99 | + | |
| 100 | +static int al_thermal_init(struct platform_device *pdev, | |
| 101 | + struct thermal_mmio *sensor) | |
| 102 | +{ | |
| 103 | + sensor->read_mmio = thermal_mmio_readb; | |
| 104 | + sensor->mask = 0xff; | |
| 105 | + sensor->factor = 1000; | |
| 106 | + | |
| 107 | + return 0; | |
| 108 | +} | |
| 109 | + | |
| 110 | +static const struct of_device_id thermal_mmio_id_table[] = { | |
| 111 | + { .compatible = "amazon,al-thermal", .data = al_thermal_init}, | |
| 112 | + {} | |
| 113 | +}; | |
| 114 | +MODULE_DEVICE_TABLE(of, thermal_mmio_id_table); | |
| 115 | + | |
| 116 | +static struct platform_driver thermal_mmio_driver = { | |
| 117 | + .probe = thermal_mmio_probe, | |
| 118 | + .driver = { | |
| 119 | + .name = "thermal-mmio", | |
| 120 | + .owner = THIS_MODULE, | |
| 121 | + .of_match_table = of_match_ptr(thermal_mmio_id_table), | |
| 122 | + }, | |
| 123 | +}; | |
| 124 | + | |
| 125 | +module_platform_driver(thermal_mmio_driver); | |
| 126 | + | |
| 127 | +MODULE_AUTHOR("Talel Shenhar <talel@amazon.com>"); | |
| 128 | +MODULE_DESCRIPTION("Thermal MMIO Driver"); | |
| 129 | +MODULE_LICENSE("GPL v2"); | |
| @@ -12,9 +12,9 @@ · 11 unmodified lines | ||
| 12 | 12 | #define TEGRA124_SOCTHERM_SENSOR_PLLX 3 |
| 13 | 13 | #define TEGRA124_SOCTHERM_SENSOR_NUM 4 |
| 14 | 14 | |
| 15 | −#define TEGRA_SOCTHERM_THROT_LEVEL_LOW 0 | |
| 16 | −#define TEGRA_SOCTHERM_THROT_LEVEL_MED 1 | |
| 17 | −#define TEGRA_SOCTHERM_THROT_LEVEL_HIGH 2 | |
| 18 | −#define TEGRA_SOCTHERM_THROT_LEVEL_NONE -1 | |
| 15 | +#define TEGRA_SOCTHERM_THROT_LEVEL_NONE 0 | |
| 16 | +#define TEGRA_SOCTHERM_THROT_LEVEL_LOW 1 | |
| 17 | +#define TEGRA_SOCTHERM_THROT_LEVEL_MED 2 | |
| 18 | +#define TEGRA_SOCTHERM_THROT_LEVEL_HIGH 3 | |
| 19 | 19 | |
| 20 | 20 | #endif |
| @@ -447,6 +447,11 @@ struct thermal_cooling_device *thermal_cooling_device_register(char *, void *, · 446 unmodified lines | ||
| 447 | 447 | struct thermal_cooling_device * |
| 448 | 448 | thermal_of_cooling_device_register(struct device_node *np, char *, void *, |
| 449 | 449 | const struct thermal_cooling_device_ops *); |
| 450 | +struct thermal_cooling_device * | |
| 451 | +devm_thermal_of_cooling_device_register(struct device *dev, | |
| 452 | + struct device_node *np, | |
| 453 | + char *type, void *devdata, | |
| 454 | + const struct thermal_cooling_device_ops *ops); | |
| 450 | 455 | void thermal_cooling_device_unregister(struct thermal_cooling_device *); |
| 451 | 456 | struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name); |
| 452 | 457 | int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp); |
| @@ -503,6 +508,14 @@ static inline struct thermal_cooling_device * · 50 unmodified lines | ||
| 503 | 508 | thermal_of_cooling_device_register(struct device_node *np, |
| 504 | 509 | char *type, void *devdata, const struct thermal_cooling_device_ops *ops) |
| 505 | 510 | { return ERR_PTR(-ENODEV); } |
| 511 | +static inline struct thermal_cooling_device * | |
| 512 | +devm_thermal_of_cooling_device_register(struct device *dev, | |
| 513 | + struct device_node *np, | |
| 514 | + char *type, void *devdata, | |
| 515 | + const struct thermal_cooling_device_ops *ops) | |
| 516 | +{ | |
| 517 | + return ERR_PTR(-ENODEV); | |
| 518 | +} | |
| 506 | 519 | static inline void thermal_cooling_device_unregister( |
| 507 | 520 | struct thermal_cooling_device *cdev) |
| 508 | 521 | { } |