diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-05-16 07:56:57 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-05-16 07:56:57 -0700 |
commit | a455eda33faafcaac1effb31d682765b14ef868c (patch) | |
tree | 9a4ca7da47300ca9081445539ff337efcead4b6b | |
parent | cc7ce90153e74f8266eefee9fba466faa1a2d5df (diff) | |
parent | 37bcec5d9f71bd13142a97d2196b293c9ac23823 (diff) | |
download | lwn-a455eda33faafcaac1effb31d682765b14ef868c.tar.gz lwn-a455eda33faafcaac1effb31d682765b14ef868c.zip |
Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal
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
...
42 files changed, 2339 insertions, 605 deletions
diff --git a/Documentation/devicetree/bindings/thermal/amazon,al-thermal.txt b/Documentation/devicetree/bindings/thermal/amazon,al-thermal.txt new file mode 100644 index 000000000000..703979dbd577 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/amazon,al-thermal.txt @@ -0,0 +1,33 @@ +Amazon's Annapurna Labs Thermal Sensor + +Simple thermal device that allows temperature reading by a single MMIO +transaction. + +Required properties: +- compatible: "amazon,al-thermal". +- reg: The physical base address and length of the sensor's registers. +- #thermal-sensor-cells: Must be 1. See ./thermal.txt for a description. + +Example: + thermal: thermal { + compatible = "amazon,al-thermal"; + reg = <0x0 0x05002860 0x0 0x1>; + #thermal-sensor-cells = <0x1>; + }; + + thermal-zones { + thermal-z0 { + polling-delay-passive = <250>; + polling-delay = <1000>; + thermal-sensors = <&thermal 0>; + trips { + critical { + temperature = <105000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + }; + }; + diff --git a/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.txt b/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.txt index b6c0ae53d4dc..f02f38527a6b 100644 --- a/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.txt +++ b/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.txt @@ -52,13 +52,47 @@ Required properties : Must set as following values: TEGRA_SOCTHERM_THROT_LEVEL_LOW, TEGRA_SOCTHERM_THROT_LEVEL_MED TEGRA_SOCTHERM_THROT_LEVEL_HIGH, TEGRA_SOCTHERM_THROT_LEVEL_NONE + - nvidia,gpu-throt-level: This property is for Tegra124 and Tegra210. + It is the level of pulse skippers, which used to throttle clock + frequencies. It indicates gpu clock throttling depth and can be + programmed to any of the following values which represent a throttling + percentage: + TEGRA_SOCTHERM_THROT_LEVEL_NONE (0%) + TEGRA_SOCTHERM_THROT_LEVEL_LOW (50%), + TEGRA_SOCTHERM_THROT_LEVEL_MED (75%), + TEGRA_SOCTHERM_THROT_LEVEL_HIGH (85%). - #cooling-cells: Should be 1. This cooling device only support on/off state. See ./thermal.txt for a description of this property. + Optional properties: The following properties are T210 specific and + valid only for OCx throttle events. + - nvidia,count-threshold: Specifies the number of OC events that are + required for triggering an interrupt. Interrupts are not triggered if + the property is missing. A value of 0 will interrupt on every OC alarm. + - nvidia,polarity-active-low: Configures the polarity of the OC alaram + signal. If present, this means assert low, otherwise assert high. + - nvidia,alarm-filter: Number of clocks to filter event. When the filter + expires (which means the OC event has not occurred for a long time), + the counter is cleared and filter is rearmed. Default value is 0. + - nvidia,throttle-period-us: Specifies the number of uSec for which + throttling is engaged after the OC event is deasserted. Default value + is 0. + +Optional properties: +- nvidia,thermtrips : When present, this property specifies the temperature at + which the soctherm hardware will assert the thermal trigger signal to the + Power Management IC, which can be configured to reset or shutdown the device. + It is an array of pairs where each pair represents a tsensor id followed by a + temperature in milli Celcius. In the absence of this property the critical + trip point will be used for thermtrip temperature. + Note: -- the "critical" type trip points will be set to SOC_THERM hardware as the -shut down temperature. Once the temperature of this thermal zone is higher -than it, the system will be shutdown or reset by hardware. +- the "critical" type trip points will be used to set the temperature at which +the SOC_THERM hardware will assert a thermal trigger if the "nvidia,thermtrips" +property is missing. When the thermtrips property is present, the breach of a +critical trip point is reported back to the thermal framework to implement +software shutdown. + - the "hot" type trip points will be set to SOC_THERM hardware as the throttle temperature. Once the the temperature of this thermal zone is higher than it, it will trigger the HW throttle event. @@ -79,25 +113,32 @@ Example : #thermal-sensor-cells = <1>; + nvidia,thermtrips = <TEGRA124_SOCTHERM_SENSOR_CPU 102500 + TEGRA124_SOCTHERM_SENSOR_GPU 103000>; + throttle-cfgs { /* * When the "heavy" cooling device triggered, - * the HW will skip cpu clock's pulse in 85% depth + * the HW will skip cpu clock's pulse in 85% depth, + * skip gpu clock's pulse in 85% level */ throttle_heavy: heavy { nvidia,priority = <100>; nvidia,cpu-throt-percent = <85>; + nvidia,gpu-throt-level = <TEGRA_SOCTHERM_THROT_LEVEL_HIGH>; #cooling-cells = <1>; }; /* * When the "light" cooling device triggered, - * the HW will skip cpu clock's pulse in 50% depth + * the HW will skip cpu clock's pulse in 50% depth, + * skip gpu clock's pulse in 50% level */ throttle_light: light { nvidia,priority = <80>; nvidia,cpu-throt-percent = <50>; + nvidia,gpu-throt-level = <TEGRA_SOCTHERM_THROT_LEVEL_LOW>; #cooling-cells = <1>; }; @@ -107,6 +148,17 @@ Example : * arbiter will select the highest priority as the final throttle * settings to skip cpu pulse. */ + + throttle_oc1: oc1 { + nvidia,priority = <50>; + nvidia,polarity-active-low; + nvidia,count-threshold = <100>; + nvidia,alarm-filter = <5100000>; + nvidia,throttle-period-us = <0>; + nvidia,cpu-throt-percent = <75>; + nvidia,gpu-throt-level = + <TEGRA_SOCTHERM_THROT_LEVEL_MED>; + }; }; }; diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt index 1d9e8cf61018..673cc1831ee9 100644 --- a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt @@ -6,11 +6,14 @@ Required properties: - "qcom,msm8916-tsens" (MSM8916) - "qcom,msm8974-tsens" (MSM8974) - "qcom,msm8996-tsens" (MSM8996) + - "qcom,qcs404-tsens", "qcom,tsens-v1" (QCS404) - "qcom,msm8998-tsens", "qcom,tsens-v2" (MSM8998) - "qcom,sdm845-tsens", "qcom,tsens-v2" (SDM845) The generic "qcom,tsens-v2" property must be used as a fallback for any SoC with version 2 of the TSENS IP. MSM8996 is the only exception because the generic property did not exist when support was added. + Similarly, the generic "qcom,tsens-v1" property must be used as a fallback for + any SoC with version 1 of the TSENS IP. - reg: Address range of the thermal registers. New platforms containing v2.x.y of the TSENS IP must specify the SROT and TM @@ -39,3 +42,14 @@ tsens0: thermal-sensor@c263000 { #qcom,sensors = <13>; #thermal-sensor-cells = <1>; }; + +Example 3 (for any platform containing v1 of the TSENS IP): +tsens: thermal-sensor@4a9000 { + compatible = "qcom,qcs404-tsens", "qcom,tsens-v1"; + reg = <0x004a9000 0x1000>, /* TM */ + <0x004a8000 0x1000>; /* SROT */ + nvmem-cells = <&tsens_caldata>; + nvmem-cell-names = "calib"; + #qcom,sensors = <10>; + #thermal-sensor-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt b/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt index 43d744e5305e..c6aac9bcacf1 100644 --- a/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt @@ -2,6 +2,7 @@ Required properties: - compatible : should be "rockchip,<name>-tsadc" + "rockchip,px30-tsadc": found on PX30 SoCs "rockchip,rv1108-tsadc": found on RV1108 SoCs "rockchip,rk3228-tsadc": found on RK3228 SoCs "rockchip,rk3288-tsadc": found on RK3288 SoCs diff --git a/Documentation/devicetree/bindings/thermal/thermal-generic-adc.txt b/Documentation/devicetree/bindings/thermal/thermal-generic-adc.txt index d72355502b78..691a09db2fef 100644 --- a/Documentation/devicetree/bindings/thermal/thermal-generic-adc.txt +++ b/Documentation/devicetree/bindings/thermal/thermal-generic-adc.txt @@ -8,16 +8,22 @@ temperature using voltage-temperature lookup table. Required properties: =================== - compatible: Must be "generic-adc-thermal". +- #thermal-sensor-cells: Should be 1. See ./thermal.txt for a description + of this property. +Optional properties: +=================== - temperature-lookup-table: Two dimensional array of Integer; lookup table to map the relation between ADC value and temperature. When ADC is read, the value is looked up on the table to get the equivalent temperature. + The first value of the each row of array is the temperature in milliCelsius and second value of the each row of array is the ADC read value. -- #thermal-sensor-cells: Should be 1. See ./thermal.txt for a description - of this property. + + If not specified, driver assumes the ADC channel + gives milliCelsius directly. Example : #include <dt-bindings/thermal/thermal.h> diff --git a/MAINTAINERS b/MAINTAINERS index ee6cf4d1010c..59efb8bd33e0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -742,6 +742,12 @@ F: drivers/tty/serial/altera_jtaguart.c F: include/linux/altera_uart.h F: include/linux/altera_jtaguart.h +AMAZON ANNAPURNA LABS THERMAL MMIO DRIVER +M: Talel Shenhar <talel@amazon.com> +S: Maintained +F: Documentation/devicetree/bindings/thermal/amazon,al-thermal.txt +F: drivers/thermal/thermal_mmio.c + AMAZON ETHERNET DRIVERS M: Netanel Belgazal <netanel@amazon.com> R: Saeed Bishara <saeedb@amazon.com> diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index c4dd6301e7c8..0daf0b32aa4a 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -830,10 +830,8 @@ static int aspeed_create_pwm_cooling(struct device *dev, } snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%pOFn%d", child, pwm_port); - cdev->tcdev = thermal_of_cooling_device_register(child, - cdev->name, - cdev, - &aspeed_pwm_cool_ops); + cdev->tcdev = devm_thermal_of_cooling_device_register(dev, child, + cdev->name, cdev, &aspeed_pwm_cool_ops); if (IS_ERR(cdev->tcdev)) return PTR_ERR(cdev->tcdev); diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index f1bf67aca9e8..3f6e5b4e3997 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -498,6 +498,11 @@ static const struct of_device_id of_gpio_fan_match[] = { }; MODULE_DEVICE_TABLE(of, of_gpio_fan_match); +static void gpio_fan_stop(void *data) +{ + set_fan_speed(data, 0); +} + static int gpio_fan_probe(struct platform_device *pdev) { int err; @@ -532,6 +537,7 @@ static int gpio_fan_probe(struct platform_device *pdev) err = fan_ctrl_init(fan_data); if (err) return err; + devm_add_action_or_reset(dev, gpio_fan_stop, fan_data); } /* Make this driver part of hwmon class. */ @@ -543,32 +549,20 @@ static int gpio_fan_probe(struct platform_device *pdev) return PTR_ERR(fan_data->hwmon_dev); /* Optional cooling device register for Device tree platforms */ - fan_data->cdev = thermal_of_cooling_device_register(np, - "gpio-fan", - fan_data, - &gpio_fan_cool_ops); + fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np, + "gpio-fan", fan_data, &gpio_fan_cool_ops); dev_info(dev, "GPIO fan initialized\n"); return 0; } -static int gpio_fan_remove(struct platform_device *pdev) +static void gpio_fan_shutdown(struct platform_device *pdev) { struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); - if (!IS_ERR(fan_data->cdev)) - thermal_cooling_device_unregister(fan_data->cdev); - if (fan_data->gpios) set_fan_speed(fan_data, 0); - - return 0; -} - -static void gpio_fan_shutdown(struct platform_device *pdev) -{ - gpio_fan_remove(pdev); } #ifdef CONFIG_PM_SLEEP @@ -602,7 +596,6 @@ static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); static struct platform_driver gpio_fan_driver = { .probe = gpio_fan_probe, - .remove = gpio_fan_remove, .shutdown = gpio_fan_shutdown, .driver = { .name = "gpio-fan", diff --git a/drivers/hwmon/mlxreg-fan.c b/drivers/hwmon/mlxreg-fan.c index f816d2ae1e58..ed8d59d4eecb 100644 --- a/drivers/hwmon/mlxreg-fan.c +++ b/drivers/hwmon/mlxreg-fan.c @@ -465,42 +465,42 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan, static int mlxreg_fan_probe(struct platform_device *pdev) { struct mlxreg_core_platform_data *pdata; + struct device *dev = &pdev->dev; struct mlxreg_fan *fan; struct device *hwm; int err; - pdata = dev_get_platdata(&pdev->dev); + pdata = dev_get_platdata(dev); if (!pdata) { - dev_err(&pdev->dev, "Failed to get platform data.\n"); + dev_err(dev, "Failed to get platform data.\n"); return -EINVAL; } - fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL); + fan = devm_kzalloc(dev, sizeof(*fan), GFP_KERNEL); if (!fan) return -ENOMEM; - fan->dev = &pdev->dev; + fan->dev = dev; fan->regmap = pdata->regmap; - platform_set_drvdata(pdev, fan); err = mlxreg_fan_config(fan, pdata); if (err) return err; - hwm = devm_hwmon_device_register_with_info(&pdev->dev, "mlxreg_fan", + hwm = devm_hwmon_device_register_with_info(dev, "mlxreg_fan", fan, &mlxreg_fan_hwmon_chip_info, NULL); if (IS_ERR(hwm)) { - dev_err(&pdev->dev, "Failed to register hwmon device\n"); + dev_err(dev, "Failed to register hwmon device\n"); return PTR_ERR(hwm); } if (IS_REACHABLE(CONFIG_THERMAL)) { - fan->cdev = thermal_cooling_device_register("mlxreg_fan", fan, - &mlxreg_fan_cooling_ops); + fan->cdev = devm_thermal_of_cooling_device_register(dev, + NULL, "mlxreg_fan", fan, &mlxreg_fan_cooling_ops); if (IS_ERR(fan->cdev)) { - dev_err(&pdev->dev, "Failed to register cooling device\n"); + dev_err(dev, "Failed to register cooling device\n"); return PTR_ERR(fan->cdev); } } @@ -508,22 +508,11 @@ static int mlxreg_fan_probe(struct platform_device *pdev) return 0; } -static int mlxreg_fan_remove(struct platform_device *pdev) -{ - struct mlxreg_fan *fan = platform_get_drvdata(pdev); - - if (IS_REACHABLE(CONFIG_THERMAL)) - thermal_cooling_device_unregister(fan->cdev); - - return 0; -} - static struct platform_driver mlxreg_fan_driver = { .driver = { .name = "mlxreg-fan", }, .probe = mlxreg_fan_probe, - .remove = mlxreg_fan_remove, }; module_platform_driver(mlxreg_fan_driver); diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index 1dc0cd452498..09aaefa6fdb8 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -846,10 +846,8 @@ static int npcm7xx_create_pwm_cooling(struct device *dev, snprintf(cdev->name, THERMAL_NAME_LENGTH, "%pOFn%d", child, pwm_port); - cdev->tcdev = thermal_of_cooling_device_register(child, - cdev->name, - cdev, - &npcm7xx_pwm_cool_ops); + cdev->tcdev = devm_thermal_of_cooling_device_register(dev, child, + cdev->name, cdev, &npcm7xx_pwm_cool_ops); if (IS_ERR(cdev->tcdev)) return PTR_ERR(cdev->tcdev); diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index eead8afe6447..5fb2745f0226 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -273,27 +273,40 @@ static int pwm_fan_of_get_cooling_data(struct device *dev, return 0; } +static void pwm_fan_regulator_disable(void *data) +{ + regulator_disable(data); +} + +static void pwm_fan_pwm_disable(void *__ctx) +{ + struct pwm_fan_ctx *ctx = __ctx; + pwm_disable(ctx->pwm); + del_timer_sync(&ctx->rpm_timer); +} + static int pwm_fan_probe(struct platform_device *pdev) { struct thermal_cooling_device *cdev; + struct device *dev = &pdev->dev; struct pwm_fan_ctx *ctx; struct device *hwmon; int ret; struct pwm_state state = { }; u32 ppr = 2; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; mutex_init(&ctx->lock); - ctx->pwm = devm_of_pwm_get(&pdev->dev, pdev->dev.of_node, NULL); + ctx->pwm = devm_of_pwm_get(dev, dev->of_node, NULL); if (IS_ERR(ctx->pwm)) { ret = PTR_ERR(ctx->pwm); if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get PWM: %d\n", ret); + dev_err(dev, "Could not get PWM: %d\n", ret); return ret; } @@ -304,7 +317,7 @@ static int pwm_fan_probe(struct platform_device *pdev) if (ctx->irq == -EPROBE_DEFER) return ctx->irq; - ctx->reg_en = devm_regulator_get_optional(&pdev->dev, "fan"); + ctx->reg_en = devm_regulator_get_optional(dev, "fan"); if (IS_ERR(ctx->reg_en)) { if (PTR_ERR(ctx->reg_en) != -ENODEV) return PTR_ERR(ctx->reg_en); @@ -313,10 +326,11 @@ static int pwm_fan_probe(struct platform_device *pdev) } else { ret = regulator_enable(ctx->reg_en); if (ret) { - dev_err(&pdev->dev, - "Failed to enable fan supply: %d\n", ret); + dev_err(dev, "Failed to enable fan supply: %d\n", ret); return ret; } + devm_add_action_or_reset(dev, pwm_fan_regulator_disable, + ctx->reg_en); } ctx->pwm_value = MAX_PWM; @@ -328,91 +342,57 @@ static int pwm_fan_probe(struct platform_device *pdev) ret = pwm_apply_state(ctx->pwm, &state); if (ret) { - dev_err(&pdev->dev, "Failed to configure PWM: %d\n", ret); - goto err_reg_disable; + dev_err(dev, "Failed to configure PWM: %d\n", ret); + return ret; } - timer_setup(&ctx->rpm_timer, sample_timer, 0); + devm_add_action_or_reset(dev, pwm_fan_pwm_disable, ctx); - of_property_read_u32(pdev->dev.of_node, "pulses-per-revolution", &ppr); + of_property_read_u32(dev->of_node, "pulses-per-revolution", &ppr); ctx->pulses_per_revolution = ppr; if (!ctx->pulses_per_revolution) { - dev_err(&pdev->dev, "pulses-per-revolution can't be zero.\n"); - ret = -EINVAL; - goto err_pwm_disable; + dev_err(dev, "pulses-per-revolution can't be zero.\n"); + return -EINVAL; } if (ctx->irq > 0) { - ret = devm_request_irq(&pdev->dev, ctx->irq, pulse_handler, 0, + ret = devm_request_irq(dev, ctx->irq, pulse_handler, 0, pdev->name, ctx); if (ret) { - dev_err(&pdev->dev, - "Failed to request interrupt: %d\n", ret); - goto err_pwm_disable; + dev_err(dev, "Failed to request interrupt: %d\n", ret); + return ret; } ctx->sample_start = ktime_get(); mod_timer(&ctx->rpm_timer, jiffies + HZ); } - hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "pwmfan", + hwmon = devm_hwmon_device_register_with_groups(dev, "pwmfan", ctx, pwm_fan_groups); if (IS_ERR(hwmon)) { - ret = PTR_ERR(hwmon); - dev_err(&pdev->dev, - "Failed to register hwmon device: %d\n", ret); - goto err_del_timer; + dev_err(dev, "Failed to register hwmon device\n"); + return PTR_ERR(hwmon); } - ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx); + ret = pwm_fan_of_get_cooling_data(dev, ctx); if (ret) - goto err_del_timer; + return ret; ctx->pwm_fan_state = ctx->pwm_fan_max_state; if (IS_ENABLED(CONFIG_THERMAL)) { - cdev = thermal_of_cooling_device_register(pdev->dev.of_node, - "pwm-fan", ctx, - &pwm_fan_cooling_ops); + cdev = devm_thermal_of_cooling_device_register(dev, + dev->of_node, "pwm-fan", ctx, &pwm_fan_cooling_ops); if (IS_ERR(cdev)) { ret = PTR_ERR(cdev); - dev_err(&pdev->dev, + dev_err(dev, "Failed to register pwm-fan as cooling device: %d\n", ret); - goto err_del_timer; + return ret; } ctx->cdev = cdev; thermal_cdev_update(cdev); } return 0; - -err_del_timer: - del_timer_sync(&ctx->rpm_timer); - -err_pwm_disable: - state.enabled = false; - pwm_apply_state(ctx->pwm, &state); - -err_reg_disable: - if (ctx->reg_en) - regulator_disable(ctx->reg_en); - - return ret; -} - -static int pwm_fan_remove(struct platform_device *pdev) -{ - struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev); - - thermal_cooling_device_unregister(ctx->cdev); - del_timer_sync(&ctx->rpm_timer); - - if (ctx->pwm_value) - pwm_disable(ctx->pwm); - - if (ctx->reg_en) - regulator_disable(ctx->reg_en); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -480,7 +460,6 @@ MODULE_DEVICE_TABLE(of, of_pwm_fan_match); static struct platform_driver pwm_fan_driver = { .probe = pwm_fan_probe, - .remove = pwm_fan_remove, .driver = { .name = "pwm-fan", .pm = &pwm_fan_pm, diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 653aa27a25a4..66a709d5d6b9 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -200,6 +200,17 @@ config THERMAL_EMULATION because userland can easily disable the thermal policy by simply flooding this sysfs node with low temperature values. +config THERMAL_MMIO + tristate "Generic Thermal MMIO driver" + depends on OF || COMPILE_TEST + depends on HAS_IOMEM + help + This option enables the generic thermal MMIO driver that will use + memory-mapped reads to get the temperature. Any HW/System that + allows temperature reading by a single memory-mapped reading, be it + register or shared memory, is a potential candidate to work with this + driver. + config HISI_THERMAL tristate "Hisilicon thermal driver" depends on ARCH_HISI || COMPILE_TEST diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 486d682be047..74a37c7f847a 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -29,6 +29,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o # platform thermal drivers obj-y += broadcom/ +obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o diff --git a/drivers/thermal/broadcom/sr-thermal.c b/drivers/thermal/broadcom/sr-thermal.c index 2284cbecedf3..475ce2900771 100644 --- a/drivers/thermal/broadcom/sr-thermal.c +++ b/drivers/thermal/broadcom/sr-thermal.c @@ -3,7 +3,6 @@ * Copyright (C) 2018 Broadcom */ -#include <linux/acpi.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/platform_device.h> @@ -100,18 +99,11 @@ static const struct of_device_id sr_thermal_of_match[] = { }; MODULE_DEVICE_TABLE(of, sr_thermal_of_match); -static const struct acpi_device_id sr_thermal_acpi_ids[] = { - { .id = "BRCM0500" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(acpi, sr_thermal_acpi_ids); - static struct platform_driver sr_thermal_driver = { .probe = sr_thermal_probe, .driver = { .name = "sr-thermal", .of_match_table = sr_thermal_of_match, - .acpi_match_table = ACPI_PTR(sr_thermal_acpi_ids), }, }; module_platform_driver(sr_thermal_driver); diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index f7c1f49ec87f..4c5db59a619b 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -1,26 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 /* * linux/drivers/thermal/cpu_cooling.c * * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) - * Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org> * - * Copyright (C) 2014 Viresh Kumar <viresh.kumar@linaro.org> + * Copyright (C) 2012-2018 Linaro Limited. * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. + * Authors: Amit Daniel <amit.kachhap@linaro.org> + * Viresh Kumar <viresh.kumar@linaro.org> * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include <linux/module.h> #include <linux/thermal.h> @@ -99,7 +87,6 @@ struct cpufreq_cooling_device { unsigned int clipped_freq; unsigned int max_level; struct freq_table *freq_table; /* In descending order */ - struct thermal_cooling_device *cdev; struct cpufreq_policy *policy; struct list_head node; struct time_in_idle *idle_time; @@ -207,8 +194,7 @@ static int update_freq_table(struct cpufreq_cooling_device *cpufreq_cdev, dev = get_cpu_device(cpu); if (unlikely(!dev)) { - dev_warn(&cpufreq_cdev->cdev->device, - "No cpu device for cpu %d\n", cpu); + pr_warn("No cpu device for cpu %d\n", cpu); return -ENODEV; } @@ -458,7 +444,7 @@ static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev, load = 0; total_load += load; - if (trace_thermal_power_cpu_limit_enabled() && load_cpu) + if (load_cpu) load_cpu[i] = load; i++; @@ -541,7 +527,6 @@ static int cpufreq_power2state(struct thermal_cooling_device *cdev, struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; struct cpufreq_policy *policy = cpufreq_cdev->policy; - power = power > 0 ? power : 0; last_load = cpufreq_cdev->last_load ?: 1; normalised_power = (power * 100) / last_load; target_freq = cpu_power_to_freq(cpufreq_cdev, normalised_power); @@ -692,7 +677,6 @@ __cpufreq_cooling_register(struct device_node *np, goto remove_ida; cpufreq_cdev->clipped_freq = cpufreq_cdev->freq_table[0].frequency; - cpufreq_cdev->cdev = cdev; mutex_lock(&cooling_list_lock); /* Register the notifier for first cpufreq cooling device */ @@ -810,7 +794,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); - thermal_cooling_device_unregister(cpufreq_cdev->cdev); + thermal_cooling_device_unregister(cdev); ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); kfree(cpufreq_cdev->idle_time); kfree(cpufreq_cdev->freq_table); diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index 2df059cc07e2..dc5093be553e 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -5,6 +5,9 @@ * Copyright (C) 2013 Texas Instruments * Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com> */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/thermal.h> #include <linux/slab.h> #include <linux/types.h> diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile index 717a08600bb5..fc6fe50cdde4 100644 --- a/drivers/thermal/qcom/Makefile +++ b/drivers/thermal/qcom/Makefile @@ -1,3 +1,5 @@ obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o -qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-v2.o + +qcom_tsens-y += tsens.o tsens-common.o tsens-v0_1.o \ + tsens-8960.o tsens-v2.o tsens-v1.o obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o diff --git a/drivers/thermal/qcom/tsens-8916.c b/drivers/thermal/qcom/tsens-8916.c deleted file mode 100644 index c6dd620ac029..000000000000 --- a/drivers/thermal/qcom/tsens-8916.c +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2015, The Linux Foundation. All rights reserved. - */ - -#include <linux/platform_device.h> -#include "tsens.h" - -/* eeprom layout data for 8916 */ -#define BASE0_MASK 0x0000007f -#define BASE1_MASK 0xfe000000 -#define BASE0_SHIFT 0 -#define BASE1_SHIFT 25 - -#define S0_P1_MASK 0x00000f80 -#define S1_P1_MASK 0x003e0000 -#define S2_P1_MASK 0xf8000000 -#define S3_P1_MASK 0x000003e0 -#define S4_P1_MASK 0x000f8000 - -#define S0_P2_MASK 0x0001f000 -#define S1_P2_MASK 0x07c00000 -#define S2_P2_MASK 0x0000001f -#define S3_P2_MASK 0x00007c00 -#define S4_P2_MASK 0x01f00000 - -#define S0_P1_SHIFT 7 -#define S1_P1_SHIFT 17 -#define S2_P1_SHIFT 27 -#define S3_P1_SHIFT 5 -#define S4_P1_SHIFT 15 - -#define S0_P2_SHIFT 12 -#define S1_P2_SHIFT 22 -#define S2_P2_SHIFT 0 -#define S3_P2_SHIFT 10 -#define S4_P2_SHIFT 20 - -#define CAL_SEL_MASK 0xe0000000 -#define CAL_SEL_SHIFT 29 - -static int calibrate_8916(struct tsens_device *tmdev) -{ - int base0 = 0, base1 = 0, i; - u32 p1[5], p2[5]; - int mode = 0; - u32 *qfprom_cdata, *qfprom_csel; - - qfprom_cdata = (u32 *)qfprom_read(tmdev->dev, "calib"); - if (IS_ERR(qfprom_cdata)) - return PTR_ERR(qfprom_cdata); - - qfprom_csel = (u32 *)qfprom_read(tmdev->dev, "calib_sel"); - if (IS_ERR(qfprom_csel)) - return PTR_ERR(qfprom_csel); - - mode = (qfprom_csel[0] & CAL_SEL_MASK) >> CAL_SEL_SHIFT; - dev_dbg(tmdev->dev, "calibration mode is %d\n", mode); - - switch (mode) { - case TWO_PT_CALIB: - base1 = (qfprom_cdata[1] & BASE1_MASK) >> BASE1_SHIFT; - p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT; - p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT; - p2[2] = (qfprom_cdata[1] & S2_P2_MASK) >> S2_P2_SHIFT; - p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT; - p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT; - for (i = 0; i < tmdev->num_sensors; i++) - p2[i] = ((base1 + p2[i]) << 3); - /* Fall through */ - case ONE_PT_CALIB2: - base0 = (qfprom_cdata[0] & BASE0_MASK); - p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT; - p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT; - p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT; - p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT; - p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT; - for (i = 0; i < tmdev->num_sensors; i++) - p1[i] = (((base0) + p1[i]) << 3); - break; - default: - for (i = 0; i < tmdev->num_sensors; i++) { - p1[i] = 500; - p2[i] = 780; - } - break; - } - - compute_intercept_slope(tmdev, p1, p2, mode); - - return 0; -} - -static const struct tsens_ops ops_8916 = { - .init = init_common, - .calibrate = calibrate_8916, - .get_temp = get_temp_common, -}; - -const struct tsens_data data_8916 = { - .num_sensors = 5, - .ops = &ops_8916, - .reg_offsets = { [SROT_CTRL_OFFSET] = 0x0 }, - .hw_ids = (unsigned int []){0, 1, 2, 4, 5 }, -}; diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c index 0f0adb302a7b..8d9b721dadb6 100644 --- a/drivers/thermal/qcom/tsens-8960.c +++ b/drivers/thermal/qcom/tsens-8960.c @@ -56,21 +56,21 @@ #define TRDY_MASK BIT(7) #define TIMEOUT_US 100 -static int suspend_8960(struct tsens_device *tmdev) +static int suspend_8960(struct tsens_priv *priv) { int ret; unsigned int mask; - struct regmap *map = tmdev->tm_map; + struct regmap *map = priv->tm_map; - ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold); + ret = regmap_read(map, THRESHOLD_ADDR, &priv->ctx.threshold); if (ret) return ret; - ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control); + ret = regmap_read(map, CNTL_ADDR, &priv->ctx.control); if (ret) return ret; - if (tmdev->num_sensors > 1) + if (priv->num_sensors > 1) mask = SLP_CLK_ENA | EN; else mask = SLP_CLK_ENA_8660 | EN; @@ -82,10 +82,10 @@ static int suspend_8960(struct tsens_device *tmdev) return 0; } -static int resume_8960(struct tsens_device *tmdev) +static int resume_8960(struct tsens_priv *priv) { int ret; - struct regmap *map = tmdev->tm_map; + struct regmap *map = priv->tm_map; ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); if (ret) @@ -95,80 +95,80 @@ static int resume_8960(struct tsens_device *tmdev) * Separate CONFIG restore is not needed only for 8660 as * config is part of CTRL Addr and its restored as such */ - if (tmdev->num_sensors > 1) { + if (priv->num_sensors > 1) { ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); if (ret) return ret; } - ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold); + ret = regmap_write(map, THRESHOLD_ADDR, priv->ctx.threshold); if (ret) return ret; - ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control); + ret = regmap_write(map, CNTL_ADDR, priv->ctx.control); if (ret) return ret; return 0; } -static int enable_8960(struct tsens_device *tmdev, int id) +static int enable_8960(struct tsens_priv *priv, int id) { int ret; u32 reg, mask; - ret = regmap_read(tmdev->tm_map, CNTL_ADDR, ®); + ret = regmap_read(priv->tm_map, CNTL_ADDR, ®); if (ret) return ret; mask = BIT(id + SENSOR0_SHIFT); - ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg | SW_RST); + ret = regmap_write(priv->tm_map, CNTL_ADDR, reg | SW_RST); if (ret) return ret; - if (tmdev->num_sensors > 1) + if (priv->num_sensors > 1) reg |= mask | SLP_CLK_ENA | EN; else reg |= mask | SLP_CLK_ENA_8660 | EN; - ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg); + ret = regmap_write(priv->tm_map, CNTL_ADDR, reg); if (ret) return ret; return 0; } -static void disable_8960(struct tsens_device *tmdev) +static void disable_8960(struct tsens_priv *priv) { int ret; u32 reg_cntl; u32 mask; - mask = GENMASK(tmdev->num_sensors - 1, 0); + mask = GENMASK(priv->num_sensors - 1, 0); mask <<= SENSOR0_SHIFT; mask |= EN; - ret = regmap_read(tmdev->tm_map, CNTL_ADDR, ®_cntl); + ret = regmap_read(priv->tm_map, CNTL_ADDR, ®_cntl); if (ret) return; reg_cntl &= ~mask; - if (tmdev->num_sensors > 1) + if (priv->num_sensors > 1) reg_cntl &= ~SLP_CLK_ENA; else reg_cntl &= ~SLP_CLK_ENA_8660; - regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl); + regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); } -static int init_8960(struct tsens_device *tmdev) +static int init_8960(struct tsens_priv *priv) { int ret, i; u32 reg_cntl; - tmdev->tm_map = dev_get_regmap(tmdev->dev, NULL); - if (!tmdev->tm_map) + priv->tm_map = dev_get_regmap(priv->dev, NULL); + if (!priv->tm_map) return -ENODEV; /* @@ -177,21 +177,21 @@ static int init_8960(struct tsens_device *tmdev) * but the control registers stay in the same place, i.e * directly after the first 5 status registers. */ - for (i = 0; i < tmdev->num_sensors; i++) { + for (i = 0; i < priv->num_sensors; i++) { if (i >= 5) - tmdev->sensor[i].status = S0_STATUS_ADDR + 40; - tmdev->sensor[i].status += i * 4; + priv->sensor[i].status = S0_STATUS_ADDR + 40; + priv->sensor[i].status += i * 4; } reg_cntl = SW_RST; - ret = regmap_update_bits(tmdev->tm_map, CNTL_ADDR, SW_RST, reg_cntl); + ret = regmap_update_bits(priv->tm_map, CNTL_ADDR, SW_RST, reg_cntl); if (ret) return ret; - if (tmdev->num_sensors > 1) { + if (priv->num_sensors > 1) { reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); reg_cntl &= ~SW_RST; - ret = regmap_update_bits(tmdev->tm_map, CONFIG_ADDR, + ret = regmap_update_bits(priv->tm_map, CONFIG_ADDR, CONFIG_MASK, CONFIG); } else { reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16); @@ -199,30 +199,30 @@ static int init_8960(struct tsens_device *tmdev) reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660; } - reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT; - ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl); + reg_cntl |= GENMASK(priv->num_sensors - 1, 0) << SENSOR0_SHIFT; + ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); if (ret) return ret; reg_cntl |= EN; - ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl); + ret = regmap_write(priv->tm_map, CNTL_ADDR, reg_cntl); if (ret) return ret; return 0; } -static int calibrate_8960(struct tsens_device *tmdev) +static int calibrate_8960(struct tsens_priv *priv) { int i; char *data; - ssize_t num_read = tmdev->num_sensors; - struct tsens_sensor *s = tmdev->sensor; + ssize_t num_read = priv->num_sensors; + struct tsens_sensor *s = priv->sensor; - data = qfprom_read(tmdev->dev, "calib"); + data = qfprom_read(priv->dev, "calib"); if (IS_ERR(data)) - data = qfprom_read(tmdev->dev, "calib_backup"); + data = qfprom_read(priv->dev, "calib_backup"); if (IS_ERR(data)) return PTR_ERR(data); @@ -243,21 +243,21 @@ static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) return adc_code * slope + offset; } -static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp) +static int get_temp_8960(struct tsens_priv *priv, int id, int *temp) { int ret; u32 code, trdy; - const struct tsens_sensor *s = &tmdev->sensor[id]; + const struct tsens_sensor *s = &priv->sensor[id]; unsigned long timeout; timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); do { - ret = regmap_read(tmdev->tm_map, INT_STATUS_ADDR, &trdy); + ret = regmap_read(priv->tm_map, INT_STATUS_ADDR, &trdy); if (ret) return ret; if (!(trdy & TRDY_MASK)) continue; - ret = regmap_read(tmdev->tm_map, s->status, &code); + ret = regmap_read(priv->tm_map, s->status, &code); if (ret) return ret; *temp = code_to_mdegC(code, s); @@ -277,7 +277,7 @@ static const struct tsens_ops ops_8960 = { .resume = resume_8960, }; -const struct tsens_data data_8960 = { +const struct tsens_plat_data data_8960 = { .num_sensors = 11, .ops = &ops_8960, }; diff --git a/drivers/thermal/qcom/tsens-common.c b/drivers/thermal/qcom/tsens-common.c index f80c73f11740..928e8e81ba69 100644 --- a/drivers/thermal/qcom/tsens-common.c +++ b/drivers/thermal/qcom/tsens-common.c @@ -12,18 +12,6 @@ #include <linux/regmap.h> #include "tsens.h" -/* SROT */ -#define TSENS_EN BIT(0) - -/* TM */ -#define STATUS_OFFSET 0x30 -#define SN_ADDR_OFFSET 0x4 -#define SN_ST_TEMP_MASK 0x3ff -#define CAL_DEGC_PT1 30 -#define CAL_DEGC_PT2 120 -#define SLOPE_FACTOR 1000 -#define SLOPE_DEFAULT 3200 - char *qfprom_read(struct device *dev, const char *cname) { struct nvmem_cell *cell; @@ -46,18 +34,18 @@ char *qfprom_read(struct device *dev, const char *cname) * and offset values are derived from tz->tzp->slope and tz->tzp->offset * resp. */ -void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1, +void compute_intercept_slope(struct tsens_priv *priv, u32 *p1, u32 *p2, u32 mode) { int i; int num, den; - for (i = 0; i < tmdev->num_sensors; i++) { - dev_dbg(tmdev->dev, + for (i = 0; i < priv->num_sensors; i++) { + dev_dbg(priv->dev, "sensor%d - data_point1:%#x data_point2:%#x\n", i, p1[i], p2[i]); - tmdev->sensor[i].slope = SLOPE_DEFAULT; + priv->sensor[i].slope = SLOPE_DEFAULT; if (mode == TWO_PT_CALIB) { /* * slope (m) = adc_code2 - adc_code1 (y2 - y1)/ @@ -66,16 +54,30 @@ void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1, num = p2[i] - p1[i]; num *= SLOPE_FACTOR; den = CAL_DEGC_PT2 - CAL_DEGC_PT1; - tmdev->sensor[i].slope = num / den; + priv->sensor[i].slope = num / den; } - tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) - + priv->sensor[i].offset = (p1[i] * SLOPE_FACTOR) - (CAL_DEGC_PT1 * - tmdev->sensor[i].slope); - dev_dbg(tmdev->dev, "offset:%d\n", tmdev->sensor[i].offset); + priv->sensor[i].slope); + dev_dbg(priv->dev, "offset:%d\n", priv->sensor[i].offset); } } +bool is_sensor_enabled(struct tsens_priv *priv, u32 hw_id) +{ + u32 val; + int ret; + + if ((hw_id > (priv->num_sensors - 1)) || (hw_id < 0)) + return -EINVAL; + ret = regmap_field_read(priv->rf[SENSOR_EN], &val); + if (ret) + return ret; + + return val & (1 << hw_id); +} + static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) { int degc, num, den; @@ -95,18 +97,54 @@ static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) return degc; } -int get_temp_common(struct tsens_device *tmdev, int id, int *temp) +int get_temp_tsens_valid(struct tsens_priv *priv, int i, int *temp) { - struct tsens_sensor *s = &tmdev->sensor[id]; - u32 code; - unsigned int status_reg; + struct tsens_sensor *s = &priv->sensor[i]; + u32 temp_idx = LAST_TEMP_0 + s->hw_id; + u32 valid_idx = VALID_0 + s->hw_id; + u32 last_temp = 0, valid, mask; + int ret; + + ret = regmap_field_read(priv->rf[valid_idx], &valid); + if (ret) + return ret; + while (!valid) { + /* Valid bit is 0 for 6 AHB clock cycles. + * At 19.2MHz, 1 AHB clock is ~60ns. + * We should enter this loop very, very rarely. + */ + ndelay(400); + ret = regmap_field_read(priv->rf[valid_idx], &valid); + if (ret) + return ret; + } + + /* Valid bit is set, OK to read the temperature */ + ret = regmap_field_read(priv->rf[temp_idx], &last_temp); + if (ret) + return ret; + + if (priv->feat->adc) { + /* Convert temperature from ADC code to milliCelsius */ + *temp = code_to_degc(last_temp, s) * 1000; + } else { + mask = GENMASK(priv->fields[LAST_TEMP_0].msb, + priv->fields[LAST_TEMP_0].lsb); + /* Convert temperature from deciCelsius to milliCelsius */ + *temp = sign_extend32(last_temp, fls(mask) - 1) * 100; + } + + return 0; +} + +int get_temp_common(struct tsens_priv *priv, int i, int *temp) +{ + struct tsens_sensor *s = &priv->sensor[i]; int last_temp = 0, ret; - status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * SN_ADDR_OFFSET; - ret = regmap_read(tmdev->tm_map, status_reg, &code); + ret = regmap_field_read(priv->rf[LAST_TEMP_0 + s->hw_id], &last_temp); if (ret) return ret; - last_temp = code & SN_ST_TEMP_MASK; *temp = code_to_degc(last_temp, s) * 1000; @@ -127,21 +165,21 @@ static const struct regmap_config tsens_srot_config = { .reg_stride = 4, }; -int __init init_common(struct tsens_device *tmdev) +int __init init_common(struct tsens_priv *priv) { void __iomem *tm_base, *srot_base; + struct device *dev = priv->dev; struct resource *res; - u32 code; - int ret; - struct platform_device *op = of_find_device_by_node(tmdev->dev->of_node); - u16 ctrl_offset = tmdev->reg_offsets[SROT_CTRL_OFFSET]; + u32 enabled; + int ret, i, j; + struct platform_device *op = of_find_device_by_node(priv->dev->of_node); if (!op) return -EINVAL; if (op->num_resources > 1) { /* DT with separate SROT and TM address space */ - tmdev->tm_offset = 0; + priv->tm_offset = 0; res = platform_get_resource(op, IORESOURCE_MEM, 1); srot_base = devm_ioremap_resource(&op->dev, res); if (IS_ERR(srot_base)) { @@ -149,16 +187,15 @@ int __init init_common(struct tsens_device *tmdev) goto err_put_device; } - tmdev->srot_map = devm_regmap_init_mmio(tmdev->dev, srot_base, + priv->srot_map = devm_regmap_init_mmio(dev, srot_base, &tsens_srot_config); - if (IS_ERR(tmdev->srot_map)) { - ret = PTR_ERR(tmdev->srot_map); + if (IS_ERR(priv->srot_map)) { + ret = PTR_ERR(priv->srot_map); goto err_put_device; } - } else { /* old DTs where SROT and TM were in a contiguous 2K block */ - tmdev->tm_offset = 0x1000; + priv->tm_offset = 0x1000; } res = platform_get_resource(op, IORESOURCE_MEM, 0); @@ -168,19 +205,47 @@ int __init init_common(struct tsens_device *tmdev) goto err_put_device; } - tmdev->tm_map = devm_regmap_init_mmio(tmdev->dev, tm_base, &tsens_config); - if (IS_ERR(tmdev->tm_map)) { - ret = PTR_ERR(tmdev->tm_map); + priv->tm_map = devm_regmap_init_mmio(dev, tm_base, &tsens_config); + if (IS_ERR(priv->tm_map)) { + ret = PTR_ERR(priv->tm_map); goto err_put_device; } - if (tmdev->srot_map) { - ret = regmap_read(tmdev->srot_map, ctrl_offset, &code); - if (ret) + priv->rf[TSENS_EN] = devm_regmap_field_alloc(dev, priv->srot_map, + priv->fields[TSENS_EN]); + if (IS_ERR(priv->rf[TSENS_EN])) { + ret = PTR_ERR(priv->rf[TSENS_EN]); + goto err_put_device; + } + ret = regmap_field_read(priv->rf[TSENS_EN], &enabled); + if (ret) + goto err_put_device; + if (!enabled) { + dev_err(dev, "tsens device is not enabled\n"); + ret = -ENODEV; + goto err_put_device; + } + + priv->rf[SENSOR_EN] = devm_regmap_field_alloc(dev, priv->srot_map, + priv->fields[SENSOR_EN]); + if (IS_ERR(priv->rf[SENSOR_EN])) { + ret = PTR_ERR(priv->rf[SENSOR_EN]); + goto err_put_device; + } + /* now alloc regmap_fields in tm_map */ + for (i = 0, j = LAST_TEMP_0; i < priv->feat->max_sensors; i++, j++) { + priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map, + priv->fields[j]); + if (IS_ERR(priv->rf[j])) { + ret = PTR_ERR(priv->rf[j]); goto err_put_device; - if (!(code & TSENS_EN)) { - dev_err(tmdev->dev, "tsens device is not enabled\n"); - ret = -ENODEV; + } + } + for (i = 0, j = VALID_0; i < priv->feat->max_sensors; i++, j++) { + priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map, + priv->fields[j]); + if (IS_ERR(priv->rf[j])) { + ret = PTR_ERR(priv->rf[j]); goto err_put_device; } } diff --git a/drivers/thermal/qcom/tsens-8974.c b/drivers/thermal/qcom/tsens-v0_1.c index 3d3fda3d731b..a319283c223f 100644 --- a/drivers/thermal/qcom/tsens-8974.c +++ b/drivers/thermal/qcom/tsens-v0_1.c @@ -6,6 +6,48 @@ #include <linux/platform_device.h> #include "tsens.h" +/* ----- SROT ------ */ +#define SROT_CTRL_OFF 0x0000 + +/* ----- TM ------ */ +#define TM_INT_EN_OFF 0x0000 +#define TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF 0x0004 +#define TM_Sn_STATUS_OFF 0x0030 +#define TM_TRDY_OFF 0x005c + +/* eeprom layout data for 8916 */ +#define MSM8916_BASE0_MASK 0x0000007f +#define MSM8916_BASE1_MASK 0xfe000000 +#define MSM8916_BASE0_SHIFT 0 +#define MSM8916_BASE1_SHIFT 25 + +#define MSM8916_S0_P1_MASK 0x00000f80 +#define MSM8916_S1_P1_MASK 0x003e0000 +#define MSM8916_S2_P1_MASK 0xf8000000 +#define MSM8916_S3_P1_MASK 0x000003e0 +#define MSM8916_S4_P1_MASK 0x000f8000 + +#define MSM8916_S0_P2_MASK 0x0001f000 +#define MSM8916_S1_P2_MASK 0x07c00000 +#define MSM8916_S2_P2_MASK 0x0000001f +#define MSM8916_S3_P2_MASK 0x00007c00 +#define MSM8916_S4_P2_MASK 0x01f00000 + +#define MSM8916_S0_P1_SHIFT 7 +#define MSM8916_S1_P1_SHIFT 17 +#define MSM8916_S2_P1_SHIFT 27 +#define MSM8916_S3_P1_SHIFT 5 +#define MSM8916_S4_P1_SHIFT 15 + +#define MSM8916_S0_P2_SHIFT 12 +#define MSM8916_S1_P2_SHIFT 22 +#define MSM8916_S2_P2_SHIFT 0 +#define MSM8916_S3_P2_SHIFT 10 +#define MSM8916_S4_P2_SHIFT 20 + +#define MSM8916_CAL_SEL_MASK 0xe0000000 +#define MSM8916_CAL_SEL_SHIFT 29 + /* eeprom layout data for 8974 */ #define BASE1_MASK 0xff #define S0_P1_MASK 0x3f00 @@ -91,7 +133,59 @@ #define BIT_APPEND 0x3 -static int calibrate_8974(struct tsens_device *tmdev) +static int calibrate_8916(struct tsens_priv *priv) +{ + int base0 = 0, base1 = 0, i; + u32 p1[5], p2[5]; + int mode = 0; + u32 *qfprom_cdata, *qfprom_csel; + + qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib"); + if (IS_ERR(qfprom_cdata)) + return PTR_ERR(qfprom_cdata); + + qfprom_csel = (u32 *)qfprom_read(priv->dev, "calib_sel"); + if (IS_ERR(qfprom_csel)) + return PTR_ERR(qfprom_csel); + + mode = (qfprom_csel[0] & MSM8916_CAL_SEL_MASK) >> MSM8916_CAL_SEL_SHIFT; + dev_dbg(priv->dev, "calibration mode is %d\n", mode); + + switch (mode) { + case TWO_PT_CALIB: + base1 = (qfprom_cdata[1] & MSM8916_BASE1_MASK) >> MSM8916_BASE1_SHIFT; + p2[0] = (qfprom_cdata[0] & MSM8916_S0_P2_MASK) >> MSM8916_S0_P2_SHIFT; + p2[1] = (qfprom_cdata[0] & MSM8916_S1_P2_MASK) >> MSM8916_S1_P2_SHIFT; + p2[2] = (qfprom_cdata[1] & MSM8916_S2_P2_MASK) >> MSM8916_S2_P2_SHIFT; + p2[3] = (qfprom_cdata[1] & MSM8916_S3_P2_MASK) >> MSM8916_S3_P2_SHIFT; + p2[4] = (qfprom_cdata[1] & MSM8916_S4_P2_MASK) >> MSM8916_S4_P2_SHIFT; + for (i = 0; i < priv->num_sensors; i++) + p2[i] = ((base1 + p2[i]) << 3); + /* Fall through */ + case ONE_PT_CALIB2: + base0 = (qfprom_cdata[0] & MSM8916_BASE0_MASK); + p1[0] = (qfprom_cdata[0] & MSM8916_S0_P1_MASK) >> MSM8916_S0_P1_SHIFT; + p1[1] = (qfprom_cdata[0] & MSM8916_S1_P1_MASK) >> MSM8916_S1_P1_SHIFT; + p1[2] = (qfprom_cdata[0] & MSM8916_S2_P1_MASK) >> MSM8916_S2_P1_SHIFT; + p1[3] = (qfprom_cdata[1] & MSM8916_S3_P1_MASK) >> MSM8916_S3_P1_SHIFT; + p1[4] = (qfprom_cdata[1] & MSM8916_S4_P1_MASK) >> MSM8916_S4_P1_SHIFT; + for (i = 0; i < priv->num_sensors; i++) + p1[i] = (((base0) + p1[i]) << 3); + break; + default: + for (i = 0; i < priv->num_sensors; i++) { + p1[i] = 500; + p2[i] = 780; + } + break; + } + + compute_intercept_slope(priv, p1, p2, mode); + + return 0; +} + +static int calibrate_8974(struct tsens_priv *priv) { int base1 = 0, base2 = 0, i; u32 p1[11], p2[11]; @@ -99,11 +193,11 @@ static int calibrate_8974(struct tsens_device *tmdev) u32 *calib, *bkp; u32 calib_redun_sel; - calib = (u32 *)qfprom_read(tmdev->dev, "calib"); + calib = (u32 *)qfprom_read(priv->dev, "calib"); if (IS_ERR(calib)) return PTR_ERR(calib); - bkp = (u32 *)qfprom_read(tmdev->dev, "calib_backup"); + bkp = (u32 *)qfprom_read(priv->dev, "calib_backup"); if (IS_ERR(bkp)) return PTR_ERR(bkp); @@ -184,25 +278,25 @@ static int calibrate_8974(struct tsens_device *tmdev) switch (mode) { case ONE_PT_CALIB: - for (i = 0; i < tmdev->num_sensors; i++) + for (i = 0; i < priv->num_sensors; i++) p1[i] += (base1 << 2) | BIT_APPEND; break; case TWO_PT_CALIB: - for (i = 0; i < tmdev->num_sensors; i++) { + for (i = 0; i < priv->num_sensors; i++) { p2[i] += base2; p2[i] <<= 2; p2[i] |= BIT_APPEND; } /* Fall through */ case ONE_PT_CALIB2: - for (i = 0; i < tmdev->num_sensors; i++) { + for (i = 0; i < priv->num_sensors; i++) { p1[i] += base1; p1[i] <<= 2; p1[i] |= BIT_APPEND; } break; default: - for (i = 0; i < tmdev->num_sensors; i++) + for (i = 0; i < priv->num_sensors; i++) p2[i] = 780; p1[0] = 502; p1[1] = 509; @@ -218,19 +312,71 @@ static int calibrate_8974(struct tsens_device *tmdev) break; } - compute_intercept_slope(tmdev, p1, p2, mode); + compute_intercept_slope(priv, p1, p2, mode); return 0; } +/* v0.1: 8916, 8974 */ + +static const struct tsens_features tsens_v0_1_feat = { + .ver_major = VER_0_1, + .crit_int = 0, + .adc = 1, + .srot_split = 1, + .max_sensors = 11, +}; + +static const struct reg_field tsens_v0_1_regfields[MAX_REGFIELDS] = { + /* ----- SROT ------ */ + /* No VERSION information */ + + /* CTRL_OFFSET */ + [TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0), + [TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1), + [SENSOR_EN] = REG_FIELD(SROT_CTRL_OFF, 3, 13), + + /* ----- TM ------ */ + /* INTERRUPT ENABLE */ + [INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 0), + + /* Sn_STATUS */ + REG_FIELD_FOR_EACH_SENSOR11(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 9), + /* No VALID field on v0.1 */ + REG_FIELD_FOR_EACH_SENSOR11(MIN_STATUS, TM_Sn_STATUS_OFF, 10, 10), + REG_FIELD_FOR_EACH_SENSOR11(LOWER_STATUS, TM_Sn_STATUS_OFF, 11, 11), + REG_FIELD_FOR_EACH_SENSOR11(UPPER_STATUS, TM_Sn_STATUS_OFF, 12, 12), + /* No CRITICAL field on v0.1 */ + REG_FIELD_FOR_EACH_SENSOR11(MAX_STATUS, TM_Sn_STATUS_OFF, 13, 13), + + /* TRDY: 1=ready, 0=in progress */ + [TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0), +}; + +static const struct tsens_ops ops_8916 = { + .init = init_common, + .calibrate = calibrate_8916, + .get_temp = get_temp_common, +}; + +const struct tsens_plat_data data_8916 = { + .num_sensors = 5, + .ops = &ops_8916, + .hw_ids = (unsigned int []){0, 1, 2, 4, 5 }, + + .feat = &tsens_v0_1_feat, + .fields = tsens_v0_1_regfields, +}; + static const struct tsens_ops ops_8974 = { .init = init_common, .calibrate = calibrate_8974, .get_temp = get_temp_common, }; -const struct tsens_data data_8974 = { +const struct tsens_plat_data data_8974 = { .num_sensors = 11, .ops = &ops_8974, - .reg_offsets = { [SROT_CTRL_OFFSET] = 0x0 }, + .feat = &tsens_v0_1_feat, + .fields = tsens_v0_1_regfields, }; diff --git a/drivers/thermal/qcom/tsens-v1.c b/drivers/thermal/qcom/tsens-v1.c new file mode 100644 index 000000000000..10b595d4f619 --- /dev/null +++ b/drivers/thermal/qcom/tsens-v1.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019, Linaro Limited + */ + +#include <linux/bitops.h> +#include <linux/regmap.h> +#include <linux/delay.h> +#include "tsens.h" + +/* ----- SROT ------ */ +#define SROT_HW_VER_OFF 0x0000 +#define SROT_CTRL_OFF 0x0004 + +/* ----- TM ------ */ +#define TM_INT_EN_OFF 0x0000 +#define TM_Sn_UPPER_LOWER_STATUS_CTRL_OFF 0x0004 +#define TM_Sn_STATUS_OFF 0x0044 +#define TM_TRDY_OFF 0x0084 + +/* eeprom layout data for qcs404/405 (v1) */ +#define BASE0_MASK 0x000007f8 +#define BASE1_MASK 0x0007f800 +#define BASE0_SHIFT 3 +#define BASE1_SHIFT 11 + +#define S0_P1_MASK 0x0000003f +#define S1_P1_MASK 0x0003f000 +#define S2_P1_MASK 0x3f000000 +#define S3_P1_MASK 0x000003f0 +#define S4_P1_MASK 0x003f0000 +#define S5_P1_MASK 0x0000003f +#define S6_P1_MASK 0x0003f000 +#define S7_P1_MASK 0x3f000000 +#define S8_P1_MASK 0x000003f0 +#define S9_P1_MASK 0x003f0000 + +#define S0_P2_MASK 0x00000fc0 +#define S1_P2_MASK 0x00fc0000 +#define S2_P2_MASK_1_0 0xc0000000 +#define S2_P2_MASK_5_2 0x0000000f +#define S3_P2_MASK 0x0000fc00 +#define S4_P2_MASK 0x0fc00000 +#define S5_P2_MASK 0x00000fc0 +#define S6_P2_MASK 0x00fc0000 +#define S7_P2_MASK_1_0 0xc0000000 +#define S7_P2_MASK_5_2 0x0000000f +#define S8_P2_MASK 0x0000fc00 +#define S9_P2_MASK 0x0fc00000 + +#define S0_P1_SHIFT 0 +#define S0_P2_SHIFT 6 +#define S1_P1_SHIFT 12 +#define S1_P2_SHIFT 18 +#define S2_P1_SHIFT 24 +#define S2_P2_SHIFT_1_0 30 + +#define S2_P2_SHIFT_5_2 0 +#define S3_P1_SHIFT 4 +#define S3_P2_SHIFT 10 +#define S4_P1_SHIFT 16 +#define S4_P2_SHIFT 22 + +#define S5_P1_SHIFT 0 +#define S5_P2_SHIFT 6 +#define S6_P1_SHIFT 12 +#define S6_P2_SHIFT 18 +#define S7_P1_SHIFT 24 +#define S7_P2_SHIFT_1_0 30 + +#define S7_P2_SHIFT_5_2 0 +#define S8_P1_SHIFT 4 +#define S8_P2_SHIFT 10 +#define S9_P1_SHIFT 16 +#define S9_P2_SHIFT 22 + +#define CAL_SEL_MASK 7 +#define CAL_SEL_SHIFT 0 + +static int calibrate_v1(struct tsens_priv *priv) +{ + u32 base0 = 0, base1 = 0; + u32 p1[10], p2[10]; + u32 mode = 0, lsb = 0, msb = 0; + u32 *qfprom_cdata; + int i; + + qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib"); + if (IS_ERR(qfprom_cdata)) + return PTR_ERR(qfprom_cdata); + + mode = (qfprom_cdata[4] & CAL_SEL_MASK) >> CAL_SEL_SHIFT; + dev_dbg(priv->dev, "calibration mode is %d\n", mode); + + switch (mode) { + case TWO_PT_CALIB: + base1 = (qfprom_cdata[4] & BASE1_MASK) >> BASE1_SHIFT; + p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT; + p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT; + /* This value is split over two registers, 2 bits and 4 bits */ + lsb = (qfprom_cdata[0] & S2_P2_MASK_1_0) >> S2_P2_SHIFT_1_0; + msb = (qfprom_cdata[1] & S2_P2_MASK_5_2) >> S2_P2_SHIFT_5_2; + p2[2] = msb << 2 | lsb; + p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT; + p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT; + p2[5] = (qfprom_cdata[2] & S5_P2_MASK) >> S5_P2_SHIFT; + p2[6] = (qfprom_cdata[2] & S6_P2_MASK) >> S6_P2_SHIFT; + /* This value is split over two registers, 2 bits and 4 bits */ + lsb = (qfprom_cdata[2] & S7_P2_MASK_1_0) >> S7_P2_SHIFT_1_0; + msb = (qfprom_cdata[3] & S7_P2_MASK_5_2) >> S7_P2_SHIFT_5_2; + p2[7] = msb << 2 | lsb; + p2[8] = (qfprom_cdata[3] & S8_P2_MASK) >> S8_P2_SHIFT; + p2[9] = (qfprom_cdata[3] & S9_P2_MASK) >> S9_P2_SHIFT; + for (i = 0; i < priv->num_sensors; i++) + p2[i] = ((base1 + p2[i]) << 2); + /* Fall through */ + case ONE_PT_CALIB2: + base0 = (qfprom_cdata[4] & BASE0_MASK) >> BASE0_SHIFT; + p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT; + p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT; + p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT; + p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT; + p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT; + p1[5] = (qfprom_cdata[2] & S5_P1_MASK) >> S5_P1_SHIFT; + p1[6] = (qfprom_cdata[2] & S6_P1_MASK) >> S6_P1_SHIFT; + p1[7] = (qfprom_cdata[2] & S7_P1_MASK) >> S7_P1_SHIFT; + p1[8] = (qfprom_cdata[3] & S8_P1_MASK) >> S8_P1_SHIFT; + p1[9] = (qfprom_cdata[3] & S9_P1_MASK) >> S9_P1_SHIFT; + for (i = 0; i < priv->num_sensors; i++) + p1[i] = (((base0) + p1[i]) << 2); + break; + default: + for (i = 0; i < priv->num_sensors; i++) { + p1[i] = 500; + p2[i] = 780; + } + break; + } + + compute_intercept_slope(priv, p1, p2, mode); + + return 0; +} + +/* v1.x: qcs404,405 */ + +static const struct tsens_features tsens_v1_feat = { + .ver_major = VER_1_X, + .crit_int = 0, + .adc = 1, + .srot_split = 1, + .max_sensors = 11, +}; + +static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = { + /* ----- SROT ------ */ + /* VERSION */ + [VER_MAJOR] = REG_FIELD(SROT_HW_VER_OFF, 28, 31), + [VER_MINOR] = REG_FIELD(SROT_HW_VER_OFF, 16, 27), + [VER_STEP] = REG_FIELD(SROT_HW_VER_OFF, 0, 15), + /* CTRL_OFFSET */ + [TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0), + [TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1), + [SENSOR_EN] = REG_FIELD(SROT_CTRL_OFF, 3, 13), + + /* ----- TM ------ */ + /* INTERRUPT ENABLE */ + [INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 0), + + /* Sn_STATUS */ + REG_FIELD_FOR_EACH_SENSOR11(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 9), + REG_FIELD_FOR_EACH_SENSOR11(VALID, TM_Sn_STATUS_OFF, 14, 14), + REG_FIELD_FOR_EACH_SENSOR11(MIN_STATUS, TM_Sn_STATUS_OFF, 10, 10), + REG_FIELD_FOR_EACH_SENSOR11(LOWER_STATUS, TM_Sn_STATUS_OFF, 11, 11), + REG_FIELD_FOR_EACH_SENSOR11(UPPER_STATUS, TM_Sn_STATUS_OFF, 12, 12), + /* No CRITICAL field on v1.x */ + REG_FIELD_FOR_EACH_SENSOR11(MAX_STATUS, TM_Sn_STATUS_OFF, 13, 13), + + /* TRDY: 1=ready, 0=in progress */ + [TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0), +}; + +static const struct tsens_ops ops_generic_v1 = { + .init = init_common, + .calibrate = calibrate_v1, + .get_temp = get_temp_tsens_valid, +}; + +const struct tsens_plat_data data_tsens_v1 = { + .ops = &ops_generic_v1, + .feat = &tsens_v1_feat, + .fields = tsens_v1_regfields, +}; diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c index 381a212872bf..1099069f2aa3 100644 --- a/drivers/thermal/qcom/tsens-v2.c +++ b/drivers/thermal/qcom/tsens-v2.c @@ -4,76 +4,81 @@ * Copyright (c) 2018, Linaro Limited */ -#include <linux/regmap.h> #include <linux/bitops.h> +#include <linux/regmap.h> #include "tsens.h" -#define STATUS_OFFSET 0xa0 -#define LAST_TEMP_MASK 0xfff -#define STATUS_VALID_BIT BIT(21) +/* ----- SROT ------ */ +#define SROT_HW_VER_OFF 0x0000 +#define SROT_CTRL_OFF 0x0004 + +/* ----- TM ------ */ +#define TM_INT_EN_OFF 0x0004 +#define TM_UPPER_LOWER_INT_STATUS_OFF 0x0008 +#define TM_UPPER_LOWER_INT_CLEAR_OFF 0x000c +#define TM_UPPER_LOWER_INT_MASK_OFF 0x0010 +#define TM_CRITICAL_INT_STATUS_OFF 0x0014 +#define TM_CRITICAL_INT_CLEAR_OFF 0x0018 +#define TM_CRITICAL_INT_MASK_OFF 0x001c +#define TM_Sn_UPPER_LOWER_THRESHOLD_OFF 0x0020 +#define TM_Sn_CRITICAL_THRESHOLD_OFF 0x0060 +#define TM_Sn_STATUS_OFF 0x00a0 +#define TM_TRDY_OFF 0x00e4 -static int get_temp_tsens_v2(struct tsens_device *tmdev, int id, int *temp) -{ - struct tsens_sensor *s = &tmdev->sensor[id]; - u32 code; - unsigned int status_reg; - u32 last_temp = 0, last_temp2 = 0, last_temp3 = 0; - int ret; +/* v2.x: 8996, 8998, sdm845 */ - status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * 4; - ret = regmap_read(tmdev->tm_map, status_reg, &code); - if (ret) - return ret; - last_temp = code & LAST_TEMP_MASK; - if (code & STATUS_VALID_BIT) - goto done; +static const struct tsens_features tsens_v2_feat = { + .ver_major = VER_2_X, + .crit_int = 1, + .adc = 0, + .srot_split = 1, + .max_sensors = 16, +}; - /* Try a second time */ - ret = regmap_read(tmdev->tm_map, status_reg, &code); - if (ret) - return ret; - if (code & STATUS_VALID_BIT) { - last_temp = code & LAST_TEMP_MASK; - goto done; - } else { - last_temp2 = code & LAST_TEMP_MASK; - } +static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = { + /* ----- SROT ------ */ + /* VERSION */ + [VER_MAJOR] = REG_FIELD(SROT_HW_VER_OFF, 28, 31), + [VER_MINOR] = REG_FIELD(SROT_HW_VER_OFF, 16, 27), + [VER_STEP] = REG_FIELD(SROT_HW_VER_OFF, 0, 15), + /* CTRL_OFF */ + [TSENS_EN] = REG_FIELD(SROT_CTRL_OFF, 0, 0), + [TSENS_SW_RST] = REG_FIELD(SROT_CTRL_OFF, 1, 1), + [SENSOR_EN] = REG_FIELD(SROT_CTRL_OFF, 3, 18), - /* Try a third/last time */ - ret = regmap_read(tmdev->tm_map, status_reg, &code); - if (ret) - return ret; - if (code & STATUS_VALID_BIT) { - last_temp = code & LAST_TEMP_MASK; - goto done; - } else { - last_temp3 = code & LAST_TEMP_MASK; - } + /* ----- TM ------ */ + /* INTERRUPT ENABLE */ + /* v2 has separate enables for UPPER/LOWER/CRITICAL interrupts */ + [INT_EN] = REG_FIELD(TM_INT_EN_OFF, 0, 2), - if (last_temp == last_temp2) - last_temp = last_temp2; - else if (last_temp2 == last_temp3) - last_temp = last_temp3; -done: - /* Convert temperature from deciCelsius to milliCelsius */ - *temp = sign_extend32(last_temp, fls(LAST_TEMP_MASK) - 1) * 100; + /* Sn_STATUS */ + REG_FIELD_FOR_EACH_SENSOR16(LAST_TEMP, TM_Sn_STATUS_OFF, 0, 11), + REG_FIELD_FOR_EACH_SENSOR16(VALID, TM_Sn_STATUS_OFF, 21, 21), + REG_FIELD_FOR_EACH_SENSOR16(MIN_STATUS, TM_Sn_STATUS_OFF, 16, 16), + REG_FIELD_FOR_EACH_SENSOR16(LOWER_STATUS, TM_Sn_STATUS_OFF, 17, 17), + REG_FIELD_FOR_EACH_SENSOR16(UPPER_STATUS, TM_Sn_STATUS_OFF, 18, 18), + REG_FIELD_FOR_EACH_SENSOR16(CRITICAL_STATUS, TM_Sn_STATUS_OFF, 19, 19), + REG_FIELD_FOR_EACH_SENSOR16(MAX_STATUS, TM_Sn_STATUS_OFF, 20, 20), - return 0; -} + /* TRDY: 1=ready, 0=in progress */ + [TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0), +}; static const struct tsens_ops ops_generic_v2 = { .init = init_common, - .get_temp = get_temp_tsens_v2, + .get_temp = get_temp_tsens_valid, }; -const struct tsens_data data_tsens_v2 = { - .ops = &ops_generic_v2, - .reg_offsets = { [SROT_CTRL_OFFSET] = 0x4 }, +const struct tsens_plat_data data_tsens_v2 = { + .ops = &ops_generic_v2, + .feat = &tsens_v2_feat, + .fields = tsens_v2_regfields, }; /* Kept around for backward compatibility with old msm8996.dtsi */ -const struct tsens_data data_8996 = { +const struct tsens_plat_data data_8996 = { .num_sensors = 13, .ops = &ops_generic_v2, - .reg_offsets = { [SROT_CTRL_OFFSET] = 0x4 }, + .feat = &tsens_v2_feat, + .fields = tsens_v2_regfields, }; diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index f1ec9bbe4717..36b0b52db524 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c @@ -15,38 +15,38 @@ static int tsens_get_temp(void *data, int *temp) { const struct tsens_sensor *s = data; - struct tsens_device *tmdev = s->tmdev; + struct tsens_priv *priv = s->priv; - return tmdev->ops->get_temp(tmdev, s->id, temp); + return priv->ops->get_temp(priv, s->id, temp); } -static int tsens_get_trend(void *p, int trip, enum thermal_trend *trend) +static int tsens_get_trend(void *data, int trip, enum thermal_trend *trend) { - const struct tsens_sensor *s = p; - struct tsens_device *tmdev = s->tmdev; + const struct tsens_sensor *s = data; + struct tsens_priv *priv = s->priv; - if (tmdev->ops->get_trend) - return tmdev->ops->get_trend(tmdev, s->id, trend); + if (priv->ops->get_trend) + return priv->ops->get_trend(priv, s->id, trend); return -ENOTSUPP; } static int __maybe_unused tsens_suspend(struct device *dev) { - struct tsens_device *tmdev = dev_get_drvdata(dev); + struct tsens_priv *priv = dev_get_drvdata(dev); - if (tmdev->ops && tmdev->ops->suspend) - return tmdev->ops->suspend(tmdev); + if (priv->ops && priv->ops->suspend) + return priv->ops->suspend(priv); return 0; } static int __maybe_unused tsens_resume(struct device *dev) { - struct tsens_device *tmdev = dev_get_drvdata(dev); + struct tsens_priv *priv = dev_get_drvdata(dev); - if (tmdev->ops && tmdev->ops->resume) - return tmdev->ops->resume(tmdev); + if (priv->ops && priv->ops->resume) + return priv->ops->resume(priv); return 0; } @@ -64,6 +64,9 @@ static const struct of_device_id tsens_table[] = { .compatible = "qcom,msm8996-tsens", .data = &data_8996, }, { + .compatible = "qcom,tsens-v1", + .data = &data_tsens_v1, + }, { .compatible = "qcom,tsens-v2", .data = &data_tsens_v2, }, @@ -76,22 +79,27 @@ static const struct thermal_zone_of_device_ops tsens_of_ops = { .get_trend = tsens_get_trend, }; -static int tsens_register(struct tsens_device *tmdev) +static int tsens_register(struct tsens_priv *priv) { int i; struct thermal_zone_device *tzd; - for (i = 0; i < tmdev->num_sensors; i++) { - tmdev->sensor[i].tmdev = tmdev; - tmdev->sensor[i].id = i; - tzd = devm_thermal_zone_of_sensor_register(tmdev->dev, i, - &tmdev->sensor[i], + for (i = 0; i < priv->num_sensors; i++) { + if (!is_sensor_enabled(priv, priv->sensor[i].hw_id)) { + dev_err(priv->dev, "sensor %d: disabled\n", + priv->sensor[i].hw_id); + continue; + } + priv->sensor[i].priv = priv; + priv->sensor[i].id = i; + tzd = devm_thermal_zone_of_sensor_register(priv->dev, i, + &priv->sensor[i], &tsens_of_ops); if (IS_ERR(tzd)) continue; - tmdev->sensor[i].tzd = tzd; - if (tmdev->ops->enable) - tmdev->ops->enable(tmdev, i); + priv->sensor[i].tzd = tzd; + if (priv->ops->enable) + priv->ops->enable(priv, i); } return 0; } @@ -101,8 +109,8 @@ static int tsens_probe(struct platform_device *pdev) int ret, i; struct device *dev; struct device_node *np; - struct tsens_device *tmdev; - const struct tsens_data *data; + struct tsens_priv *priv; + const struct tsens_plat_data *data; const struct of_device_id *id; u32 num_sensors; @@ -129,55 +137,55 @@ static int tsens_probe(struct platform_device *pdev) return -EINVAL; } - tmdev = devm_kzalloc(dev, - struct_size(tmdev, sensor, num_sensors), + priv = devm_kzalloc(dev, + struct_size(priv, sensor, num_sensors), GFP_KERNEL); - if (!tmdev) + if (!priv) return -ENOMEM; - tmdev->dev = dev; - tmdev->num_sensors = num_sensors; - tmdev->ops = data->ops; - for (i = 0; i < tmdev->num_sensors; i++) { + priv->dev = dev; + priv->num_sensors = num_sensors; + priv->ops = data->ops; + for (i = 0; i < priv->num_sensors; i++) { if (data->hw_ids) - tmdev->sensor[i].hw_id = data->hw_ids[i]; + priv->sensor[i].hw_id = data->hw_ids[i]; else - tmdev->sensor[i].hw_id = i; - } - for (i = 0; i < REG_ARRAY_SIZE; i++) { - tmdev->reg_offsets[i] = data->reg_offsets[i]; + priv->sensor[i].hw_id = i; } + priv->feat = data->feat; + priv->fields = data->fields; - if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp) + if (!priv->ops || !priv->ops->init || !priv->ops->get_temp) return -EINVAL; - ret = tmdev->ops->init(tmdev); + ret = priv->ops->init(priv); if (ret < 0) { dev_err(dev, "tsens init failed\n"); return ret; } - if (tmdev->ops->calibrate) { - ret = tmdev->ops->calibrate(tmdev); + if (priv->ops->calibrate) { + ret = priv->ops->calibrate(priv); if (ret < 0) { - dev_err(dev, "tsens calibration failed\n"); + if (ret != -EPROBE_DEFER) + dev_err(dev, "tsens calibration failed\n"); return ret; } } - ret = tsens_register(tmdev); + ret = tsens_register(priv); - platform_set_drvdata(pdev, tmdev); + platform_set_drvdata(pdev, priv); return ret; } static int tsens_remove(struct platform_device *pdev) { - struct tsens_device *tmdev = platform_get_drvdata(pdev); + struct tsens_priv *priv = platform_get_drvdata(pdev); - if (tmdev->ops->disable) - tmdev->ops->disable(tmdev); + if (priv->ops->disable) + priv->ops->disable(priv); return 0; } diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h index 7b7feee5dc46..eefe3844fb4e 100644 --- a/drivers/thermal/qcom/tsens.h +++ b/drivers/thermal/qcom/tsens.h @@ -9,17 +9,39 @@ #define ONE_PT_CALIB 0x1 #define ONE_PT_CALIB2 0x2 #define TWO_PT_CALIB 0x3 +#define CAL_DEGC_PT1 30 +#define CAL_DEGC_PT2 120 +#define SLOPE_FACTOR 1000 +#define SLOPE_DEFAULT 3200 + #include <linux/thermal.h> +#include <linux/regmap.h> + +struct tsens_priv; -struct tsens_device; +enum tsens_ver { + VER_0_1 = 0, + VER_1_X, + VER_2_X, +}; +/** + * struct tsens_sensor - data for each sensor connected to the tsens device + * @priv: tsens device instance that this sensor is connected to + * @tzd: pointer to the thermal zone that this sensor is in + * @offset: offset of temperature adjustment curve + * @id: Sensor ID + * @hw_id: HW ID can be used in case of platform-specific IDs + * @slope: slope of temperature adjustment curve + * @status: 8960-specific variable to track 8960 and 8660 status register offset + */ struct tsens_sensor { - struct tsens_device *tmdev; + struct tsens_priv *priv; struct thermal_zone_device *tzd; int offset; - int id; - int hw_id; + unsigned int id; + unsigned int hw_id; int slope; u32 status; }; @@ -37,63 +59,274 @@ struct tsens_sensor { */ struct tsens_ops { /* mandatory callbacks */ - int (*init)(struct tsens_device *); - int (*calibrate)(struct tsens_device *); - int (*get_temp)(struct tsens_device *, int, int *); + int (*init)(struct tsens_priv *priv); + int (*calibrate)(struct tsens_priv *priv); + int (*get_temp)(struct tsens_priv *priv, int i, int *temp); /* optional callbacks */ - int (*enable)(struct tsens_device *, int); - void (*disable)(struct tsens_device *); - int (*suspend)(struct tsens_device *); - int (*resume)(struct tsens_device *); - int (*get_trend)(struct tsens_device *, int, enum thermal_trend *); + int (*enable)(struct tsens_priv *priv, int i); + void (*disable)(struct tsens_priv *priv); + int (*suspend)(struct tsens_priv *priv); + int (*resume)(struct tsens_priv *priv); + int (*get_trend)(struct tsens_priv *priv, int i, enum thermal_trend *trend); }; -enum reg_list { - SROT_CTRL_OFFSET, +#define REG_FIELD_FOR_EACH_SENSOR11(_name, _offset, _startbit, _stopbit) \ + [_name##_##0] = REG_FIELD(_offset, _startbit, _stopbit), \ + [_name##_##1] = REG_FIELD(_offset + 4, _startbit, _stopbit), \ + [_name##_##2] = REG_FIELD(_offset + 8, _startbit, _stopbit), \ + [_name##_##3] = REG_FIELD(_offset + 12, _startbit, _stopbit), \ + [_name##_##4] = REG_FIELD(_offset + 16, _startbit, _stopbit), \ + [_name##_##5] = REG_FIELD(_offset + 20, _startbit, _stopbit), \ + [_name##_##6] = REG_FIELD(_offset + 24, _startbit, _stopbit), \ + [_name##_##7] = REG_FIELD(_offset + 28, _startbit, _stopbit), \ + [_name##_##8] = REG_FIELD(_offset + 32, _startbit, _stopbit), \ + [_name##_##9] = REG_FIELD(_offset + 36, _startbit, _stopbit), \ + [_name##_##10] = REG_FIELD(_offset + 40, _startbit, _stopbit) - REG_ARRAY_SIZE, +#define REG_FIELD_FOR_EACH_SENSOR16(_name, _offset, _startbit, _stopbit) \ + [_name##_##0] = REG_FIELD(_offset, _startbit, _stopbit), \ + [_name##_##1] = REG_FIELD(_offset + 4, _startbit, _stopbit), \ + [_name##_##2] = REG_FIELD(_offset + 8, _startbit, _stopbit), \ + [_name##_##3] = REG_FIELD(_offset + 12, _startbit, _stopbit), \ + [_name##_##4] = REG_FIELD(_offset + 16, _startbit, _stopbit), \ + [_name##_##5] = REG_FIELD(_offset + 20, _startbit, _stopbit), \ + [_name##_##6] = REG_FIELD(_offset + 24, _startbit, _stopbit), \ + [_name##_##7] = REG_FIELD(_offset + 28, _startbit, _stopbit), \ + [_name##_##8] = REG_FIELD(_offset + 32, _startbit, _stopbit), \ + [_name##_##9] = REG_FIELD(_offset + 36, _startbit, _stopbit), \ + [_name##_##10] = REG_FIELD(_offset + 40, _startbit, _stopbit), \ + [_name##_##11] = REG_FIELD(_offset + 44, _startbit, _stopbit), \ + [_name##_##12] = REG_FIELD(_offset + 48, _startbit, _stopbit), \ + [_name##_##13] = REG_FIELD(_offset + 52, _startbit, _stopbit), \ + [_name##_##14] = REG_FIELD(_offset + 56, _startbit, _stopbit), \ + [_name##_##15] = REG_FIELD(_offset + 60, _startbit, _stopbit) + +/* reg_field IDs to use as an index into an array */ +enum regfield_ids { + /* ----- SROT ------ */ + /* HW_VER */ + VER_MAJOR = 0, + VER_MINOR, + VER_STEP, + /* CTRL_OFFSET */ + TSENS_EN = 3, + TSENS_SW_RST, + SENSOR_EN, + CODE_OR_TEMP, + + /* ----- TM ------ */ + /* STATUS */ + LAST_TEMP_0 = 7, /* Last temperature reading */ + LAST_TEMP_1, + LAST_TEMP_2, + LAST_TEMP_3, + LAST_TEMP_4, + LAST_TEMP_5, + LAST_TEMP_6, + LAST_TEMP_7, + LAST_TEMP_8, + LAST_TEMP_9, + LAST_TEMP_10, + LAST_TEMP_11, + LAST_TEMP_12, + LAST_TEMP_13, + LAST_TEMP_14, + LAST_TEMP_15, + VALID_0 = 23, /* VALID reading or not */ + VALID_1, + VALID_2, + VALID_3, + VALID_4, + VALID_5, + VALID_6, + VALID_7, + VALID_8, + VALID_9, + VALID_10, + VALID_11, + VALID_12, + VALID_13, + VALID_14, + VALID_15, + MIN_STATUS_0, /* MIN threshold violated */ + MIN_STATUS_1, + MIN_STATUS_2, + MIN_STATUS_3, + MIN_STATUS_4, + MIN_STATUS_5, + MIN_STATUS_6, + MIN_STATUS_7, + MIN_STATUS_8, + MIN_STATUS_9, + MIN_STATUS_10, + MIN_STATUS_11, + MIN_STATUS_12, + MIN_STATUS_13, + MIN_STATUS_14, + MIN_STATUS_15, + MAX_STATUS_0, /* MAX threshold violated */ + MAX_STATUS_1, + MAX_STATUS_2, + MAX_STATUS_3, + MAX_STATUS_4, + MAX_STATUS_5, + MAX_STATUS_6, + MAX_STATUS_7, + MAX_STATUS_8, + MAX_STATUS_9, + MAX_STATUS_10, + MAX_STATUS_11, + MAX_STATUS_12, + MAX_STATUS_13, + MAX_STATUS_14, + MAX_STATUS_15, + LOWER_STATUS_0, /* LOWER threshold violated */ + LOWER_STATUS_1, + LOWER_STATUS_2, + LOWER_STATUS_3, + LOWER_STATUS_4, + LOWER_STATUS_5, + LOWER_STATUS_6, + LOWER_STATUS_7, + LOWER_STATUS_8, + LOWER_STATUS_9, + LOWER_STATUS_10, + LOWER_STATUS_11, + LOWER_STATUS_12, + LOWER_STATUS_13, + LOWER_STATUS_14, + LOWER_STATUS_15, + UPPER_STATUS_0, /* UPPER threshold violated */ + UPPER_STATUS_1, + UPPER_STATUS_2, + UPPER_STATUS_3, + UPPER_STATUS_4, + UPPER_STATUS_5, + UPPER_STATUS_6, + UPPER_STATUS_7, + UPPER_STATUS_8, + UPPER_STATUS_9, + UPPER_STATUS_10, + UPPER_STATUS_11, + UPPER_STATUS_12, + UPPER_STATUS_13, + UPPER_STATUS_14, + UPPER_STATUS_15, + CRITICAL_STATUS_0, /* CRITICAL threshold violated */ + CRITICAL_STATUS_1, + CRITICAL_STATUS_2, + CRITICAL_STATUS_3, + CRITICAL_STATUS_4, + CRITICAL_STATUS_5, + CRITICAL_STATUS_6, + CRITICAL_STATUS_7, + CRITICAL_STATUS_8, + CRITICAL_STATUS_9, + CRITICAL_STATUS_10, + CRITICAL_STATUS_11, + CRITICAL_STATUS_12, + CRITICAL_STATUS_13, + CRITICAL_STATUS_14, + CRITICAL_STATUS_15, + /* TRDY */ + TRDY, + /* INTERRUPT ENABLE */ + INT_EN, /* Pre-V1, V1.x */ + LOW_INT_EN, /* V2.x */ + UP_INT_EN, /* V2.x */ + CRIT_INT_EN, /* V2.x */ + + /* Keep last */ + MAX_REGFIELDS }; /** - * struct tsens_data - tsens instance specific data - * @num_sensors: Max number of sensors supported by platform + * struct tsens_features - Features supported by the IP + * @ver_major: Major number of IP version + * @crit_int: does the IP support critical interrupts? + * @adc: do the sensors only output adc code (instead of temperature)? + * @srot_split: does the IP neatly splits the register space into SROT and TM, + * with SROT only being available to secure boot firmware? + * @max_sensors: maximum sensors supported by this version of the IP + */ +struct tsens_features { + unsigned int ver_major; + unsigned int crit_int:1; + unsigned int adc:1; + unsigned int srot_split:1; + unsigned int max_sensors; +}; + +/** + * struct tsens_plat_data - tsens compile-time platform data + * @num_sensors: Number of sensors supported by platform * @ops: operations the tsens instance supports * @hw_ids: Subset of sensors ids supported by platform, if not the first n - * @reg_offsets: Register offsets for commonly used registers + * @feat: features of the IP + * @fields: bitfield locations */ -struct tsens_data { +struct tsens_plat_data { const u32 num_sensors; const struct tsens_ops *ops; - const u16 reg_offsets[REG_ARRAY_SIZE]; unsigned int *hw_ids; + const struct tsens_features *feat; + const struct reg_field *fields; }; -/* Registers to be saved/restored across a context loss */ +/** + * struct tsens_context - Registers to be saved/restored across a context loss + */ struct tsens_context { int threshold; int control; }; -struct tsens_device { +/** + * struct tsens_priv - private data for each instance of the tsens IP + * @dev: pointer to struct device + * @num_sensors: number of sensors enabled on this device + * @tm_map: pointer to TM register address space + * @srot_map: pointer to SROT register address space + * @tm_offset: deal with old device trees that don't address TM and SROT + * address space separately + * @rf: array of regmap_fields used to store value of the field + * @ctx: registers to be saved and restored during suspend/resume + * @feat: features of the IP + * @fields: bitfield locations + * @ops: pointer to list of callbacks supported by this device + * @sensor: list of sensors attached to this device + */ +struct tsens_priv { struct device *dev; u32 num_sensors; struct regmap *tm_map; struct regmap *srot_map; u32 tm_offset; - u16 reg_offsets[REG_ARRAY_SIZE]; + struct regmap_field *rf[MAX_REGFIELDS]; struct tsens_context ctx; + const struct tsens_features *feat; + const struct reg_field *fields; const struct tsens_ops *ops; struct tsens_sensor sensor[0]; }; -char *qfprom_read(struct device *, const char *); -void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32); -int init_common(struct tsens_device *); -int get_temp_common(struct tsens_device *, int, int *); +char *qfprom_read(struct device *dev, const char *cname); +void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mode); +int init_common(struct tsens_priv *priv); +int get_temp_tsens_valid(struct tsens_priv *priv, int i, int *temp); +int get_temp_common(struct tsens_priv *priv, int i, int *temp); +bool is_sensor_enabled(struct tsens_priv *priv, u32 hw_id); + +/* TSENS target */ +extern const struct tsens_plat_data data_8960; + +/* TSENS v0.1 targets */ +extern const struct tsens_plat_data data_8916, data_8974; /* TSENS v1 targets */ -extern const struct tsens_data data_8916, data_8974, data_8960; +extern const struct tsens_plat_data data_tsens_v1; + /* TSENS v2 targets */ -extern const struct tsens_data data_8996, data_tsens_v2; +extern const struct tsens_plat_data data_8996, data_tsens_v2; #endif /* __QCOM_TSENS_H__ */ diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 3b5f5b3fb1bc..7b364933bfb1 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -193,11 +193,6 @@ static int qoriq_tmu_probe(struct platform_device *pdev) struct qoriq_tmu_data *data; struct device_node *np = pdev->dev.of_node; - if (!np) { - dev_err(&pdev->dev, "Device OF-Node is NULL"); - return -ENODEV; - } - data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data), GFP_KERNEL); if (!data) diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index 88fa41cf16e8..83f306265ee1 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -14,7 +14,6 @@ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <linux/spinlock.h> #include <linux/sys_soc.h> #include <linux/thermal.h> @@ -82,7 +81,6 @@ struct rcar_gen3_thermal_tsc { struct rcar_gen3_thermal_priv { struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; unsigned int num_tscs; - spinlock_t lock; /* Protect interrupts on and off */ void (*thermal_init)(struct rcar_gen3_thermal_tsc *tsc); }; @@ -232,38 +230,16 @@ static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data) { struct rcar_gen3_thermal_priv *priv = data; u32 status; - int i, ret = IRQ_HANDLED; + int i; - spin_lock(&priv->lock); for (i = 0; i < priv->num_tscs; i++) { status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR); rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0); if (status) - ret = IRQ_WAKE_THREAD; + thermal_zone_device_update(priv->tscs[i]->zone, + THERMAL_EVENT_UNSPECIFIED); } - if (ret == IRQ_WAKE_THREAD) - rcar_thermal_irq_set(priv, false); - - spin_unlock(&priv->lock); - - return ret; -} - -static irqreturn_t rcar_gen3_thermal_irq_thread(int irq, void *data) -{ - struct rcar_gen3_thermal_priv *priv = data; - unsigned long flags; - int i; - - for (i = 0; i < priv->num_tscs; i++) - thermal_zone_device_update(priv->tscs[i]->zone, - THERMAL_EVENT_UNSPECIFIED); - - spin_lock_irqsave(&priv->lock, flags); - rcar_thermal_irq_set(priv, true); - spin_unlock_irqrestore(&priv->lock, flags); - return IRQ_HANDLED; } @@ -307,7 +283,7 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc) usleep_range(1000, 2000); - rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0); rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0); rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, IRQ_TEMPD1 | IRQ_TEMP2); @@ -331,6 +307,9 @@ MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids); static int rcar_gen3_thermal_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct rcar_gen3_thermal_priv *priv = dev_get_drvdata(dev); + + rcar_thermal_irq_set(priv, false); pm_runtime_put(dev); pm_runtime_disable(dev); @@ -371,8 +350,6 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) if (soc_device_match(r8a7795es1)) priv->thermal_init = rcar_gen3_thermal_init_r8a7795es1; - spin_lock_init(&priv->lock); - platform_set_drvdata(pdev, priv); /* @@ -390,9 +367,9 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) if (!irqname) return -ENOMEM; - ret = devm_request_threaded_irq(dev, irq, rcar_gen3_thermal_irq, - rcar_gen3_thermal_irq_thread, - IRQF_SHARED, irqname, priv); + ret = devm_request_threaded_irq(dev, irq, NULL, + rcar_gen3_thermal_irq, + IRQF_ONESHOT, irqname, priv); if (ret) return ret; } @@ -433,10 +410,6 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) } tsc->zone = zone; - ret = of_thermal_get_ntrips(tsc->zone); - if (ret < 0) - goto error_unregister; - tsc->zone->tzp->no_hwmon = false; ret = thermal_add_hwmon_sysfs(tsc->zone); if (ret) @@ -448,6 +421,10 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) goto error_unregister; } + ret = of_thermal_get_ntrips(tsc->zone); + if (ret < 0) + goto error_unregister; + dev_info(dev, "TSC%d: Loaded %d trip points\n", i, ret); } diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 97462e9b40d8..d0873de718da 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -52,6 +52,7 @@ struct rcar_thermal_chip { unsigned int irq_per_ch : 1; unsigned int needs_suspend_resume : 1; unsigned int nirqs; + unsigned int ctemp_bands; }; static const struct rcar_thermal_chip rcar_thermal = { @@ -60,6 +61,7 @@ static const struct rcar_thermal_chip rcar_thermal = { .irq_per_ch = 0, .needs_suspend_resume = 0, .nirqs = 1, + .ctemp_bands = 1, }; static const struct rcar_thermal_chip rcar_gen2_thermal = { @@ -68,6 +70,7 @@ static const struct rcar_thermal_chip rcar_gen2_thermal = { .irq_per_ch = 0, .needs_suspend_resume = 0, .nirqs = 1, + .ctemp_bands = 1, }; static const struct rcar_thermal_chip rcar_gen3_thermal = { @@ -80,6 +83,7 @@ static const struct rcar_thermal_chip rcar_gen3_thermal = { * interrupts to detect a temperature change, rise or fall. */ .nirqs = 2, + .ctemp_bands = 2, }; struct rcar_thermal_priv { @@ -263,7 +267,12 @@ static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv, return ret; mutex_lock(&priv->lock); - tmp = MCELSIUS((priv->ctemp * 5) - 65); + if (priv->chip->ctemp_bands == 1) + tmp = MCELSIUS((priv->ctemp * 5) - 65); + else if (priv->ctemp < 24) + tmp = MCELSIUS(((priv->ctemp * 55) - 720) / 10); + else + tmp = MCELSIUS((priv->ctemp * 5) - 60); mutex_unlock(&priv->lock); if ((tmp < MCELSIUS(-45)) || (tmp > MCELSIUS(125))) { diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 9c7643d62ed7..bda1ca199abd 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -172,6 +172,9 @@ struct rockchip_thermal_data { int tshut_temp; enum tshut_mode tshut_mode; enum tshut_polarity tshut_polarity; + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state; + struct pinctrl_state *otp_state; }; /** @@ -222,11 +225,15 @@ struct rockchip_thermal_data { #define GRF_TSADC_TESTBIT_L 0x0e648 #define GRF_TSADC_TESTBIT_H 0x0e64c +#define PX30_GRF_SOC_CON2 0x0408 + #define GRF_SARADC_TESTBIT_ON (0x10001 << 2) #define GRF_TSADC_TESTBIT_H_ON (0x10001 << 2) #define GRF_TSADC_VCM_EN_L (0x10001 << 7) #define GRF_TSADC_VCM_EN_H (0x10001 << 7) +#define GRF_CON_TSADC_CH_INV (0x10001 << 1) + /** * struct tsadc_table - code to temperature conversion table * @code: the value of adc channel @@ -689,6 +696,13 @@ static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs, regs + TSADCV2_AUTO_CON); } +static void rk_tsadcv4_initialize(struct regmap *grf, void __iomem *regs, + enum tshut_polarity tshut_polarity) +{ + rk_tsadcv2_initialize(grf, regs, tshut_polarity); + regmap_write(grf, PX30_GRF_SOC_CON2, GRF_CON_TSADC_CH_INV); +} + static void rk_tsadcv2_irq_ack(void __iomem *regs) { u32 val; @@ -818,6 +832,30 @@ static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs, writel_relaxed(val, regs + TSADCV2_INT_EN); } +static const struct rockchip_tsadc_chip px30_tsadc_data = { + .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */ + .chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */ + .chn_num = 2, /* 2 channels for tsadc */ + + .tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */ + .tshut_temp = 95000, + + .initialize = rk_tsadcv4_initialize, + .irq_ack = rk_tsadcv3_irq_ack, + .control = rk_tsadcv3_control, + .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, + .set_tshut_temp = rk_tsadcv2_tshut_temp, + .set_tshut_mode = rk_tsadcv2_tshut_mode, + + .table = { + .id = rk3328_code_table, + .length = ARRAY_SIZE(rk3328_code_table), + .data_mask = TSADCV2_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + static const struct rockchip_tsadc_chip rv1108_tsadc_data = { .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */ .chn_num = 1, /* one channel for tsadc */ @@ -990,6 +1028,9 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = { }; static const struct of_device_id of_rockchip_thermal_match[] = { + { .compatible = "rockchip,px30-tsadc", + .data = (void *)&px30_tsadc_data, + }, { .compatible = "rockchip,rv1108-tsadc", .data = (void *)&rv1108_tsadc_data, @@ -1242,6 +1283,8 @@ static int rockchip_thermal_probe(struct platform_device *pdev) return error; } + thermal->chip->control(thermal->regs, false); + error = clk_prepare_enable(thermal->clk); if (error) { dev_err(&pdev->dev, "failed to enable converter clock: %d\n", @@ -1267,6 +1310,30 @@ static int rockchip_thermal_probe(struct platform_device *pdev) thermal->chip->initialize(thermal->grf, thermal->regs, thermal->tshut_polarity); + if (thermal->tshut_mode == TSHUT_MODE_GPIO) { + thermal->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(thermal->pinctrl)) { + dev_err(&pdev->dev, "failed to find thermal pinctrl\n"); + return PTR_ERR(thermal->pinctrl); + } + + thermal->gpio_state = pinctrl_lookup_state(thermal->pinctrl, + "gpio"); + if (IS_ERR_OR_NULL(thermal->gpio_state)) { + dev_err(&pdev->dev, "failed to find thermal gpio state\n"); + return -EINVAL; + } + + thermal->otp_state = pinctrl_lookup_state(thermal->pinctrl, + "otpout"); + if (IS_ERR_OR_NULL(thermal->otp_state)) { + dev_err(&pdev->dev, "failed to find thermal otpout state\n"); + return -EINVAL; + } + + pinctrl_select_state(thermal->pinctrl, thermal->otp_state); + } + for (i = 0; i < thermal->chip->chn_num; i++) { error = rockchip_thermal_register_sensor(pdev, thermal, &thermal->sensors[i], @@ -1337,8 +1404,8 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev) clk_disable(thermal->pclk); clk_disable(thermal->clk); - - pinctrl_pm_select_sleep_state(dev); + if (thermal->tshut_mode == TSHUT_MODE_GPIO) + pinctrl_select_state(thermal->pinctrl, thermal->gpio_state); return 0; } @@ -1383,7 +1450,8 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev) for (i = 0; i < thermal->chip->chn_num; i++) rockchip_thermal_toggle_sensor(&thermal->sensors[i], true); - pinctrl_pm_select_default_state(dev); + if (thermal->tshut_mode == TSHUT_MODE_GPIO) + pinctrl_select_state(thermal->pinctrl, thermal->otp_state); return 0; } diff --git a/drivers/thermal/st/Kconfig b/drivers/thermal/st/Kconfig index b80f9a9e4f8f..d8b1a4586d0b 100644 --- a/drivers/thermal/st/Kconfig +++ b/drivers/thermal/st/Kconfig @@ -3,9 +3,9 @@ # config ST_THERMAL - tristate "Thermal sensors on STMicroelectronics STi series of SoCs" - help - Support for thermal sensors on STMicroelectronics STi series of SoCs. + tristate "Thermal sensors on STMicroelectronics STi series of SoCs" + help + Support for thermal sensors on STMicroelectronics STi series of SoCs. config ST_THERMAL_SYSCFG select ST_THERMAL @@ -16,11 +16,11 @@ config ST_THERMAL_MEMMAP tristate "STi series memory mapped access based thermal sensors" config STM32_THERMAL - tristate "Thermal framework support on STMicroelectronics STM32 series of SoCs" - depends on MACH_STM32MP157 - default y - help - Support for thermal framework on STMicroelectronics STM32 series of - SoCs. This thermal driver allows to access to general thermal framework - functionalities and to acces to SoC sensor functionalities. This - configuration is fully dependent of MACH_STM32MP157. + tristate "Thermal framework support on STMicroelectronics STM32 series of SoCs" + depends on MACH_STM32MP157 + default y + help + Support for thermal framework on STMicroelectronics STM32 series of + SoCs. This thermal driver allows to access to general thermal framework + functionalities and to acces to SoC sensor functionalities. This + configuration is fully dependent of MACH_STM32MP157. diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c index bbd73c5a4a4e..cf9ddc52f30e 100644 --- a/drivers/thermal/st/stm_thermal.c +++ b/drivers/thermal/st/stm_thermal.c @@ -570,8 +570,7 @@ thermal_unprepare: static int stm_thermal_suspend(struct device *dev) { int ret; - struct platform_device *pdev = to_platform_device(dev); - struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev); + struct stm_thermal_sensor *sensor = dev_get_drvdata(dev); ret = stm_thermal_sensor_off(sensor); if (ret) @@ -585,8 +584,7 @@ static int stm_thermal_suspend(struct device *dev) static int stm_thermal_resume(struct device *dev) { int ret; - struct platform_device *pdev = to_platform_device(dev); - struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev); + struct stm_thermal_sensor *sensor = dev_get_drvdata(dev); ret = stm_thermal_prepare(sensor); if (ret) diff --git a/drivers/thermal/tegra/Kconfig b/drivers/thermal/tegra/Kconfig index f8740f7852e3..fc0b33b3f26b 100644 --- a/drivers/thermal/tegra/Kconfig +++ b/drivers/thermal/tegra/Kconfig @@ -14,7 +14,7 @@ config TEGRA_BPMP_THERMAL tristate "Tegra BPMP thermal sensing" depends on TEGRA_BPMP || COMPILE_TEST help - Enable this option for support for sensing system temperature of NVIDIA - Tegra systems-on-chip with the BPMP coprocessor (Tegra186). + Enable this option for support for sensing system temperature of NVIDIA + Tegra systems-on-chip with the BPMP coprocessor (Tegra186). endmenu diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 70043a28eb7a..fcf70a3728b6 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014 - 2018, NVIDIA CORPORATION. All rights reserved. * * Author: * Mikko Perttunen <mperttunen@nvidia.com> @@ -22,6 +23,8 @@ #include <linux/err.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -85,12 +88,51 @@ #define THERMCTL_LVL0_UP_STATS 0x10 #define THERMCTL_LVL0_DN_STATS 0x14 +#define THERMCTL_INTR_STATUS 0x84 + +#define TH_INTR_MD0_MASK BIT(25) +#define TH_INTR_MU0_MASK BIT(24) +#define TH_INTR_GD0_MASK BIT(17) +#define TH_INTR_GU0_MASK BIT(16) +#define TH_INTR_CD0_MASK BIT(9) +#define TH_INTR_CU0_MASK BIT(8) +#define TH_INTR_PD0_MASK BIT(1) +#define TH_INTR_PU0_MASK BIT(0) +#define TH_INTR_IGNORE_MASK 0xFCFCFCFC + #define THERMCTL_STATS_CTL 0x94 #define STATS_CTL_CLR_DN 0x8 #define STATS_CTL_EN_DN 0x4 #define STATS_CTL_CLR_UP 0x2 #define STATS_CTL_EN_UP 0x1 +#define OC1_CFG 0x310 +#define OC1_CFG_LONG_LATENCY_MASK BIT(6) +#define OC1_CFG_HW_RESTORE_MASK BIT(5) +#define OC1_CFG_PWR_GOOD_MASK_MASK BIT(4) +#define OC1_CFG_THROTTLE_MODE_MASK (0x3 << 2) +#define OC1_CFG_ALARM_POLARITY_MASK BIT(1) +#define OC1_CFG_EN_THROTTLE_MASK BIT(0) + +#define OC1_CNT_THRESHOLD 0x314 +#define OC1_THROTTLE_PERIOD 0x318 +#define OC1_ALARM_COUNT 0x31c +#define OC1_FILTER 0x320 +#define OC1_STATS 0x3a8 + +#define OC_INTR_STATUS 0x39c +#define OC_INTR_ENABLE 0x3a0 +#define OC_INTR_DISABLE 0x3a4 +#define OC_STATS_CTL 0x3c4 +#define OC_STATS_CTL_CLR_ALL 0x2 +#define OC_STATS_CTL_EN_ALL 0x1 + +#define OC_INTR_OC1_MASK BIT(0) +#define OC_INTR_OC2_MASK BIT(1) +#define OC_INTR_OC3_MASK BIT(2) +#define OC_INTR_OC4_MASK BIT(3) +#define OC_INTR_OC5_MASK BIT(4) + #define THROT_GLOBAL_CFG 0x400 #define THROT_GLOBAL_ENB_MASK BIT(0) @@ -160,6 +202,15 @@ /* get dividend from the depth */ #define THROT_DEPTH_DIVIDEND(depth) ((256 * (100 - (depth)) / 100) - 1) +/* gk20a nv_therm interface N:3 Mapping. Levels defined in tegra124-sochterm.h + * level vector + * NONE 3'b000 + * LOW 3'b001 + * MED 3'b011 + * HIGH 3'b111 + */ +#define THROT_LEVEL_TO_DEPTH(level) ((0x1 << (level)) - 1) + /* get THROT_PSKIP_xxx offset per LIGHT/HEAVY throt and CPU/GPU dev */ #define THROT_OFFSET 0x30 #define THROT_PSKIP_CTRL(throt, dev) (THROT_PSKIP_CTRL_LITE_CPU + \ @@ -173,6 +224,25 @@ #define THROT_DELAY_CTRL(throt) (THROT_DELAY_LITE + \ (THROT_OFFSET * throt)) +#define ALARM_OFFSET 0x14 +#define ALARM_CFG(throt) (OC1_CFG + \ + (ALARM_OFFSET * (throt - THROTTLE_OC1))) + +#define ALARM_CNT_THRESHOLD(throt) (OC1_CNT_THRESHOLD + \ + (ALARM_OFFSET * (throt - THROTTLE_OC1))) + +#define ALARM_THROTTLE_PERIOD(throt) (OC1_THROTTLE_PERIOD + \ + (ALARM_OFFSET * (throt - THROTTLE_OC1))) + +#define ALARM_ALARM_COUNT(throt) (OC1_ALARM_COUNT + \ + (ALARM_OFFSET * (throt - THROTTLE_OC1))) + +#define ALARM_FILTER(throt) (OC1_FILTER + \ + (ALARM_OFFSET * (throt - THROTTLE_OC1))) + +#define ALARM_STATS(throt) (OC1_STATS + \ + (4 * (throt - THROTTLE_OC1))) + /* get CCROC_THROT_PSKIP_xxx offset per HIGH/MED/LOW vect*/ #define CCROC_THROT_OFFSET 0x0c #define CCROC_THROT_PSKIP_CTRL_CPU_REG(vect) (CCROC_THROT_PSKIP_CTRL_CPU + \ @@ -184,15 +254,32 @@ #define THERMCTL_LVL_REGS_SIZE 0x20 #define THERMCTL_LVL_REG(rg, lv) ((rg) + ((lv) * THERMCTL_LVL_REGS_SIZE)) +#define OC_THROTTLE_MODE_DISABLED 0 +#define OC_THROTTLE_MODE_BRIEF 2 + static const int min_low_temp = -127000; static const int max_high_temp = 127000; enum soctherm_throttle_id { THROTTLE_LIGHT = 0, THROTTLE_HEAVY, + THROTTLE_OC1, + THROTTLE_OC2, + THROTTLE_OC3, + THROTTLE_OC4, + THROTTLE_OC5, /* OC5 is reserved */ THROTTLE_SIZE, }; +enum soctherm_oc_irq_id { + TEGRA_SOC_OC_IRQ_1, + TEGRA_SOC_OC_IRQ_2, + TEGRA_SOC_OC_IRQ_3, + TEGRA_SOC_OC_IRQ_4, + TEGRA_SOC_OC_IRQ_5, + TEGRA_SOC_OC_IRQ_MAX, +}; + enum soctherm_throttle_dev_id { THROTTLE_DEV_CPU = 0, THROTTLE_DEV_GPU, @@ -202,6 +289,11 @@ enum soctherm_throttle_dev_id { static const char *const throt_names[] = { [THROTTLE_LIGHT] = "light", [THROTTLE_HEAVY] = "heavy", + [THROTTLE_OC1] = "oc1", + [THROTTLE_OC2] = "oc2", + [THROTTLE_OC3] = "oc3", + [THROTTLE_OC4] = "oc4", + [THROTTLE_OC5] = "oc5", }; struct tegra_soctherm; @@ -213,12 +305,23 @@ struct tegra_thermctl_zone { const struct tegra_tsensor_group *sg; }; +struct soctherm_oc_cfg { + u32 active_low; + u32 throt_period; + u32 alarm_cnt_thresh; + u32 alarm_filter; + u32 mode; + bool intr_en; +}; + struct soctherm_throt_cfg { const char *name; unsigned int id; u8 priority; u8 cpu_throt_level; u32 cpu_throt_depth; + u32 gpu_throt_level; + struct soctherm_oc_cfg oc_cfg; struct thermal_cooling_device *cdev; bool init; }; @@ -231,6 +334,9 @@ struct tegra_soctherm { void __iomem *clk_regs; void __iomem *ccroc_regs; + int thermal_irq; + int edp_irq; + u32 *calib; struct thermal_zone_device **thermctl_tzs; struct tegra_soctherm_soc *soc; @@ -238,8 +344,19 @@ struct tegra_soctherm { struct soctherm_throt_cfg throt_cfgs[THROTTLE_SIZE]; struct dentry *debugfs_dir; + + struct mutex thermctl_lock; }; +struct soctherm_oc_irq_chip_data { + struct mutex irq_lock; /* serialize OC IRQs */ + struct irq_chip irq_chip; + struct irq_domain *domain; + int irq_enable; +}; + +static struct soctherm_oc_irq_chip_data soc_irq_cdata; + /** * ccroc_writel() - writes a value to a CCROC register * @ts: pointer to a struct tegra_soctherm @@ -446,6 +563,24 @@ find_throttle_cfg_by_name(struct tegra_soctherm *ts, const char *name) return NULL; } +static int tsensor_group_thermtrip_get(struct tegra_soctherm *ts, int id) +{ + int i, temp = min_low_temp; + struct tsensor_group_thermtrips *tt = ts->soc->thermtrips; + + if (id >= TEGRA124_SOCTHERM_SENSOR_NUM) + return temp; + + if (tt) { + for (i = 0; i < ts->soc->num_ttgs; i++) { + if (tt[i].id == id) + return tt[i].temp; + } + } + + return temp; +} + static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp) { struct tegra_thermctl_zone *zone = data; @@ -464,7 +599,16 @@ static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp) return ret; if (type == THERMAL_TRIP_CRITICAL) { - return thermtrip_program(dev, sg, temp); + /* + * If thermtrips property is set in DT, + * doesn't need to program critical type trip to HW, + * if not, program critical trip to HW. + */ + if (min_low_temp == tsensor_group_thermtrip_get(ts, sg->id)) + return thermtrip_program(dev, sg, temp); + else + return 0; + } else if (type == THERMAL_TRIP_HOT) { int i; @@ -519,10 +663,60 @@ static int tegra_thermctl_get_trend(void *data, int trip, return 0; } +static void thermal_irq_enable(struct tegra_thermctl_zone *zn) +{ + u32 r; + + /* multiple zones could be handling and setting trips at once */ + mutex_lock(&zn->ts->thermctl_lock); + r = readl(zn->ts->regs + THERMCTL_INTR_ENABLE); + r = REG_SET_MASK(r, zn->sg->thermctl_isr_mask, TH_INTR_UP_DN_EN); + writel(r, zn->ts->regs + THERMCTL_INTR_ENABLE); + mutex_unlock(&zn->ts->thermctl_lock); +} + +static void thermal_irq_disable(struct tegra_thermctl_zone *zn) +{ + u32 r; + + /* multiple zones could be handling and setting trips at once */ + mutex_lock(&zn->ts->thermctl_lock); + r = readl(zn->ts->regs + THERMCTL_INTR_DISABLE); + r = REG_SET_MASK(r, zn->sg->thermctl_isr_mask, 0); + writel(r, zn->ts->regs + THERMCTL_INTR_DISABLE); + mutex_unlock(&zn->ts->thermctl_lock); +} + +static int tegra_thermctl_set_trips(void *data, int lo, int hi) +{ + struct tegra_thermctl_zone *zone = data; + u32 r; + + thermal_irq_disable(zone); + + r = readl(zone->ts->regs + zone->sg->thermctl_lvl0_offset); + r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 0); + writel(r, zone->ts->regs + zone->sg->thermctl_lvl0_offset); + + lo = enforce_temp_range(zone->dev, lo) / zone->ts->soc->thresh_grain; + hi = enforce_temp_range(zone->dev, hi) / zone->ts->soc->thresh_grain; + dev_dbg(zone->dev, "%s hi:%d, lo:%d\n", __func__, hi, lo); + + r = REG_SET_MASK(r, zone->sg->thermctl_lvl0_up_thresh_mask, hi); + r = REG_SET_MASK(r, zone->sg->thermctl_lvl0_dn_thresh_mask, lo); + r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 1); + writel(r, zone->ts->regs + zone->sg->thermctl_lvl0_offset); + + thermal_irq_enable(zone); + + return 0; +} + static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = { .get_temp = tegra_thermctl_get_temp, .set_trip_temp = tegra_thermctl_set_trip_temp, .get_trend = tegra_thermctl_get_trend, + .set_trips = tegra_thermctl_set_trips, }; 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) * @dev: struct device * of the SOC_THERM instance * * Configure the SOC_THERM HW trip points, setting "THERMTRIP" - * "THROTTLE" trip points , using "critical" or "hot" type trip_temp + * "THROTTLE" trip points , using "thermtrips", "critical" or "hot" + * type trip_temp * from thermal zone. * After they have been configured, THERMTRIP or THROTTLE will take * action when the configured SoC thermal sensor group reaches a @@ -577,28 +772,23 @@ static int tegra_soctherm_set_hwtrips(struct device *dev, { struct tegra_soctherm *ts = dev_get_drvdata(dev); struct soctherm_throt_cfg *stc; - int i, trip, temperature; - int ret; + int i, trip, temperature, ret; - ret = tz->ops->get_crit_temp(tz, &temperature); - if (ret) { - dev_warn(dev, "thermtrip: %s: missing critical temperature\n", - sg->name); - goto set_throttle; - } + /* Get thermtrips. If missing, try to get critical trips. */ + temperature = tsensor_group_thermtrip_get(ts, sg->id); + if (min_low_temp == temperature) + if (tz->ops->get_crit_temp(tz, &temperature)) + temperature = max_high_temp; ret = thermtrip_program(dev, sg, temperature); if (ret) { - dev_err(dev, "thermtrip: %s: error during enable\n", - sg->name); + dev_err(dev, "thermtrip: %s: error during enable\n", sg->name); return ret; } - dev_info(dev, - "thermtrip: will shut down when %s reaches %d mC\n", + dev_info(dev, "thermtrip: will shut down when %s reaches %d mC\n", sg->name, temperature); -set_throttle: ret = get_hot_temp(tz, &trip, &temperature); if (ret) { dev_info(dev, "throttrip: %s: missing hot temperature\n", @@ -606,7 +796,7 @@ set_throttle: return 0; } - for (i = 0; i < THROTTLE_SIZE; i++) { + for (i = 0; i < THROTTLE_OC1; i++) { struct thermal_cooling_device *cdev; if (!ts->throt_cfgs[i].init) @@ -638,6 +828,461 @@ set_throttle: return 0; } +static irqreturn_t soctherm_thermal_isr(int irq, void *dev_id) +{ + struct tegra_soctherm *ts = dev_id; + u32 r; + + /* Case for no lock: + * Although interrupts are enabled in set_trips, there is still no need + * to lock here because the interrupts are disabled before programming + * new trip points. Hence there cant be a interrupt on the same sensor. + * An interrupt can however occur on a sensor while trips are being + * programmed on a different one. This beign a LEVEL interrupt won't + * cause a new interrupt but this is taken care of by the re-reading of + * the STATUS register in the thread function. + */ + r = readl(ts->regs + THERMCTL_INTR_STATUS); + writel(r, ts->regs + THERMCTL_INTR_DISABLE); + + return IRQ_WAKE_THREAD; +} + +/** + * soctherm_thermal_isr_thread() - Handles a thermal interrupt request + * @irq: The interrupt number being requested; not used + * @dev_id: Opaque pointer to tegra_soctherm; + * + * Clears the interrupt status register if there are expected + * interrupt bits set. + * The interrupt(s) are then handled by updating the corresponding + * thermal zones. + * + * An error is logged if any unexpected interrupt bits are set. + * + * Disabled interrupts are re-enabled. + * + * Return: %IRQ_HANDLED. Interrupt was handled and no further processing + * is needed. + */ +static irqreturn_t soctherm_thermal_isr_thread(int irq, void *dev_id) +{ + struct tegra_soctherm *ts = dev_id; + struct thermal_zone_device *tz; + u32 st, ex = 0, cp = 0, gp = 0, pl = 0, me = 0; + + st = readl(ts->regs + THERMCTL_INTR_STATUS); + + /* deliberately clear expected interrupts handled in SW */ + cp |= st & TH_INTR_CD0_MASK; + cp |= st & TH_INTR_CU0_MASK; + + gp |= st & TH_INTR_GD0_MASK; + gp |= st & TH_INTR_GU0_MASK; + + pl |= st & TH_INTR_PD0_MASK; + pl |= st & TH_INTR_PU0_MASK; + + me |= st & TH_INTR_MD0_MASK; + me |= st & TH_INTR_MU0_MASK; + + ex |= cp | gp | pl | me; + if (ex) { + writel(ex, ts->regs + THERMCTL_INTR_STATUS); + st &= ~ex; + + if (cp) { + tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_CPU]; + thermal_zone_device_update(tz, + THERMAL_EVENT_UNSPECIFIED); + } + + if (gp) { + tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_GPU]; + thermal_zone_device_update(tz, + THERMAL_EVENT_UNSPECIFIED); + } + + if (pl) { + tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_PLLX]; + thermal_zone_device_update(tz, + THERMAL_EVENT_UNSPECIFIED); + } + + if (me) { + tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_MEM]; + thermal_zone_device_update(tz, + THERMAL_EVENT_UNSPECIFIED); + } + } + + /* deliberately ignore expected interrupts NOT handled in SW */ + ex |= TH_INTR_IGNORE_MASK; + st &= ~ex; + + if (st) { + /* Whine about any other unexpected INTR bits still set */ + pr_err("soctherm: Ignored unexpected INTRs 0x%08x\n", st); + writel(st, ts->regs + THERMCTL_INTR_STATUS); + } + + return IRQ_HANDLED; +} + +/** + * soctherm_oc_intr_enable() - Enables the soctherm over-current interrupt + * @alarm: The soctherm throttle id + * @enable: Flag indicating enable the soctherm over-current + * interrupt or disable it + * + * Enables a specific over-current pins @alarm to raise an interrupt if the flag + * is set and the alarm corresponds to OC1, OC2, OC3, or OC4. + */ +static void soctherm_oc_intr_enable(struct tegra_soctherm *ts, + enum soctherm_throttle_id alarm, + bool enable) +{ + u32 r; + + if (!enable) + return; + + r = readl(ts->regs + OC_INTR_ENABLE); + switch (alarm) { + case THROTTLE_OC1: + r = REG_SET_MASK(r, OC_INTR_OC1_MASK, 1); + break; + case THROTTLE_OC2: + r = REG_SET_MASK(r, OC_INTR_OC2_MASK, 1); + break; + case THROTTLE_OC3: + r = REG_SET_MASK(r, OC_INTR_OC3_MASK, 1); + break; + case THROTTLE_OC4: + r = REG_SET_MASK(r, OC_INTR_OC4_MASK, 1); + break; + default: + r = 0; + break; + } + writel(r, ts->regs + OC_INTR_ENABLE); +} + +/** + * soctherm_handle_alarm() - Handles soctherm alarms + * @alarm: The soctherm throttle id + * + * "Handles" over-current alarms (OC1, OC2, OC3, and OC4) by printing + * a warning or informative message. + * + * Return: -EINVAL for @alarm = THROTTLE_OC3, otherwise 0 (success). + */ +static int soctherm_handle_alarm(enum soctherm_throttle_id alarm) +{ + int rv = -EINVAL; + + switch (alarm) { + case THROTTLE_OC1: + pr_debug("soctherm: Successfully handled OC1 alarm\n"); + rv = 0; + break; + + case THROTTLE_OC2: + pr_debug("soctherm: Successfully handled OC2 alarm\n"); + rv = 0; + break; + + case THROTTLE_OC3: + pr_debug("soctherm: Successfully handled OC3 alarm\n"); + rv = 0; + break; + + case THROTTLE_OC4: + pr_debug("soctherm: Successfully handled OC4 alarm\n"); + rv = 0; + break; + + default: + break; + } + + if (rv) + pr_err("soctherm: ERROR in handling %s alarm\n", + throt_names[alarm]); + + return rv; +} + +/** + * soctherm_edp_isr_thread() - log an over-current interrupt request + * @irq: OC irq number. Currently not being used. See description + * @arg: a void pointer for callback, currently not being used + * + * Over-current events are handled in hardware. This function is called to log + * and handle any OC events that happened. Additionally, it checks every + * over-current interrupt registers for registers are set but + * was not expected (i.e. any discrepancy in interrupt status) by the function, + * the discrepancy will logged. + * + * Return: %IRQ_HANDLED + */ +static irqreturn_t soctherm_edp_isr_thread(int irq, void *arg) +{ + struct tegra_soctherm *ts = arg; + u32 st, ex, oc1, oc2, oc3, oc4; + + st = readl(ts->regs + OC_INTR_STATUS); + + /* deliberately clear expected interrupts handled in SW */ + oc1 = st & OC_INTR_OC1_MASK; + oc2 = st & OC_INTR_OC2_MASK; + oc3 = st & OC_INTR_OC3_MASK; + oc4 = st & OC_INTR_OC4_MASK; + ex = oc1 | oc2 | oc3 | oc4; + + pr_err("soctherm: OC ALARM 0x%08x\n", ex); + if (ex) { + writel(st, ts->regs + OC_INTR_STATUS); + st &= ~ex; + + if (oc1 && !soctherm_handle_alarm(THROTTLE_OC1)) + soctherm_oc_intr_enable(ts, THROTTLE_OC1, true); + + if (oc2 && !soctherm_handle_alarm(THROTTLE_OC2)) + soctherm_oc_intr_enable(ts, THROTTLE_OC2, true); + + if (oc3 && !soctherm_handle_alarm(THROTTLE_OC3)) + soctherm_oc_intr_enable(ts, THROTTLE_OC3, true); + + if (oc4 && !soctherm_handle_alarm(THROTTLE_OC4)) + soctherm_oc_intr_enable(ts, THROTTLE_OC4, true); + + if (oc1 && soc_irq_cdata.irq_enable & BIT(0)) + handle_nested_irq( + irq_find_mapping(soc_irq_cdata.domain, 0)); + + if (oc2 && soc_irq_cdata.irq_enable & BIT(1)) + handle_nested_irq( + irq_find_mapping(soc_irq_cdata.domain, 1)); + + if (oc3 && soc_irq_cdata.irq_enable & BIT(2)) + handle_nested_irq( + irq_find_mapping(soc_irq_cdata.domain, 2)); + + if (oc4 && soc_irq_cdata.irq_enable & BIT(3)) + handle_nested_irq( + irq_find_mapping(soc_irq_cdata.domain, 3)); + } + + if (st) { + pr_err("soctherm: Ignored unexpected OC ALARM 0x%08x\n", st); + writel(st, ts->regs + OC_INTR_STATUS); + } + + return IRQ_HANDLED; +} + +/** + * soctherm_edp_isr() - Disables any active interrupts + * @irq: The interrupt request number + * @arg: Opaque pointer to an argument + * + * Writes to the OC_INTR_DISABLE register the over current interrupt status, + * masking any asserted interrupts. Doing this prevents the same interrupts + * from triggering this isr repeatedly. The thread woken by this isr will + * handle asserted interrupts and subsequently unmask/re-enable them. + * + * The OC_INTR_DISABLE register indicates which OC interrupts + * have been disabled. + * + * Return: %IRQ_WAKE_THREAD, handler requests to wake the handler thread + */ +static irqreturn_t soctherm_edp_isr(int irq, void *arg) +{ + struct tegra_soctherm *ts = arg; + u32 r; + + if (!ts) + return IRQ_NONE; + + r = readl(ts->regs + OC_INTR_STATUS); + writel(r, ts->regs + OC_INTR_DISABLE); + + return IRQ_WAKE_THREAD; +} + +/** + * soctherm_oc_irq_lock() - locks the over-current interrupt request + * @data: Interrupt request data + * + * Looks up the chip data from @data and locks the mutex associated with + * a particular over-current interrupt request. + */ +static void soctherm_oc_irq_lock(struct irq_data *data) +{ + struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); + + mutex_lock(&d->irq_lock); +} + +/** + * soctherm_oc_irq_sync_unlock() - Unlocks the OC interrupt request + * @data: Interrupt request data + * + * Looks up the interrupt request data @data and unlocks the mutex associated + * with a particular over-current interrupt request. + */ +static void soctherm_oc_irq_sync_unlock(struct irq_data *data) +{ + struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); + + mutex_unlock(&d->irq_lock); +} + +/** + * soctherm_oc_irq_enable() - Enables the SOC_THERM over-current interrupt queue + * @data: irq_data structure of the chip + * + * Sets the irq_enable bit of SOC_THERM allowing SOC_THERM + * to respond to over-current interrupts. + * + */ +static void soctherm_oc_irq_enable(struct irq_data *data) +{ + struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); + + d->irq_enable |= BIT(data->hwirq); +} + +/** + * soctherm_oc_irq_disable() - Disables overcurrent interrupt requests + * @irq_data: The interrupt request information + * + * Clears the interrupt request enable bit of the overcurrent + * interrupt request chip data. + * + * Return: Nothing is returned (void) + */ +static void soctherm_oc_irq_disable(struct irq_data *data) +{ + struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); + + d->irq_enable &= ~BIT(data->hwirq); +} + +static int soctherm_oc_irq_set_type(struct irq_data *data, unsigned int type) +{ + return 0; +} + +/** + * soctherm_oc_irq_map() - SOC_THERM interrupt request domain mapper + * @h: Interrupt request domain + * @virq: Virtual interrupt request number + * @hw: Hardware interrupt request number + * + * Mapping callback function for SOC_THERM's irq_domain. When a SOC_THERM + * interrupt request is called, the irq_domain takes the request's virtual + * request number (much like a virtual memory address) and maps it to a + * physical hardware request number. + * + * When a mapping doesn't already exist for a virtual request number, the + * irq_domain calls this function to associate the virtual request number with + * a hardware request number. + * + * Return: 0 + */ +static int soctherm_oc_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct soctherm_oc_irq_chip_data *data = h->host_data; + + irq_set_chip_data(virq, data); + irq_set_chip(virq, &data->irq_chip); + irq_set_nested_thread(virq, 1); + return 0; +} + +/** + * soctherm_irq_domain_xlate_twocell() - xlate for soctherm interrupts + * @d: Interrupt request domain + * @intspec: Array of u32s from DTs "interrupt" property + * @intsize: Number of values inside the intspec array + * @out_hwirq: HW IRQ value associated with this interrupt + * @out_type: The IRQ SENSE type for this interrupt. + * + * This Device Tree IRQ specifier translation function will translate a + * specific "interrupt" as defined by 2 DT values where the cell values map + * the hwirq number + 1 and linux irq flags. Since the output is the hwirq + * number, this function will subtract 1 from the value listed in DT. + * + * Return: 0 + */ +static int soctherm_irq_domain_xlate_twocell(struct irq_domain *d, + struct device_node *ctrlr, const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_type) +{ + if (WARN_ON(intsize < 2)) + return -EINVAL; + + /* + * The HW value is 1 index less than the DT IRQ values. + * i.e. OC4 goes to HW index 3. + */ + *out_hwirq = intspec[0] - 1; + *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; + return 0; +} + +static const struct irq_domain_ops soctherm_oc_domain_ops = { + .map = soctherm_oc_irq_map, + .xlate = soctherm_irq_domain_xlate_twocell, +}; + +/** + * soctherm_oc_int_init() - Initial enabling of the over + * current interrupts + * @np: The devicetree node for soctherm + * @num_irqs: The number of new interrupt requests + * + * Sets the over current interrupt request chip data + * + * Return: 0 on success or if overcurrent interrupts are not enabled, + * -ENOMEM (out of memory), or irq_base if the function failed to + * allocate the irqs + */ +static int soctherm_oc_int_init(struct device_node *np, int num_irqs) +{ + if (!num_irqs) { + pr_info("%s(): OC interrupts are not enabled\n", __func__); + return 0; + } + + mutex_init(&soc_irq_cdata.irq_lock); + soc_irq_cdata.irq_enable = 0; + + soc_irq_cdata.irq_chip.name = "soc_therm_oc"; + soc_irq_cdata.irq_chip.irq_bus_lock = soctherm_oc_irq_lock; + soc_irq_cdata.irq_chip.irq_bus_sync_unlock = + soctherm_oc_irq_sync_unlock; + soc_irq_cdata.irq_chip.irq_disable = soctherm_oc_irq_disable; + soc_irq_cdata.irq_chip.irq_enable = soctherm_oc_irq_enable; + soc_irq_cdata.irq_chip.irq_set_type = soctherm_oc_irq_set_type; + soc_irq_cdata.irq_chip.irq_set_wake = NULL; + + soc_irq_cdata.domain = irq_domain_add_linear(np, num_irqs, + &soctherm_oc_domain_ops, + &soc_irq_cdata); + + if (!soc_irq_cdata.domain) { + pr_err("%s: Failed to create IRQ domain\n", __func__); + return -ENOMEM; + } + + pr_debug("%s(): OC interrupts enabled successful\n", __func__); + return 0; +} + #ifdef CONFIG_DEBUG_FS static int regs_show(struct seq_file *s, void *data) { @@ -929,6 +1574,120 @@ static const struct thermal_cooling_device_ops throt_cooling_ops = { .set_cur_state = throt_set_cdev_state, }; +static int soctherm_thermtrips_parse(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra_soctherm *ts = dev_get_drvdata(dev); + struct tsensor_group_thermtrips *tt = ts->soc->thermtrips; + const int max_num_prop = ts->soc->num_ttgs * 2; + u32 *tlb; + int i, j, n, ret; + + if (!tt) + return -ENOMEM; + + n = of_property_count_u32_elems(dev->of_node, "nvidia,thermtrips"); + if (n <= 0) { + dev_info(dev, + "missing thermtrips, will use critical trips as shut down temp\n"); + return n; + } + + n = min(max_num_prop, n); + + tlb = devm_kcalloc(&pdev->dev, max_num_prop, sizeof(u32), GFP_KERNEL); + if (!tlb) + return -ENOMEM; + ret = of_property_read_u32_array(dev->of_node, "nvidia,thermtrips", + tlb, n); + if (ret) { + dev_err(dev, "invalid num ele: thermtrips:%d\n", ret); + return ret; + } + + i = 0; + for (j = 0; j < n; j = j + 2) { + if (tlb[j] >= TEGRA124_SOCTHERM_SENSOR_NUM) + continue; + + tt[i].id = tlb[j]; + tt[i].temp = tlb[j + 1]; + i++; + } + + return 0; +} + +static void soctherm_oc_cfg_parse(struct device *dev, + struct device_node *np_oc, + struct soctherm_throt_cfg *stc) +{ + u32 val; + + if (of_property_read_bool(np_oc, "nvidia,polarity-active-low")) + stc->oc_cfg.active_low = 1; + else + stc->oc_cfg.active_low = 0; + + if (!of_property_read_u32(np_oc, "nvidia,count-threshold", &val)) { + stc->oc_cfg.intr_en = 1; + stc->oc_cfg.alarm_cnt_thresh = val; + } + + if (!of_property_read_u32(np_oc, "nvidia,throttle-period-us", &val)) + stc->oc_cfg.throt_period = val; + + if (!of_property_read_u32(np_oc, "nvidia,alarm-filter", &val)) + stc->oc_cfg.alarm_filter = val; + + /* BRIEF throttling by default, do not support STICKY */ + stc->oc_cfg.mode = OC_THROTTLE_MODE_BRIEF; +} + +static int soctherm_throt_cfg_parse(struct device *dev, + struct device_node *np, + struct soctherm_throt_cfg *stc) +{ + struct tegra_soctherm *ts = dev_get_drvdata(dev); + int ret; + u32 val; + + ret = of_property_read_u32(np, "nvidia,priority", &val); + if (ret) { + dev_err(dev, "throttle-cfg: %s: invalid priority\n", stc->name); + return -EINVAL; + } + stc->priority = val; + + ret = of_property_read_u32(np, ts->soc->use_ccroc ? + "nvidia,cpu-throt-level" : + "nvidia,cpu-throt-percent", &val); + if (!ret) { + if (ts->soc->use_ccroc && + val <= TEGRA_SOCTHERM_THROT_LEVEL_HIGH) + stc->cpu_throt_level = val; + else if (!ts->soc->use_ccroc && val <= 100) + stc->cpu_throt_depth = val; + else + goto err; + } else { + goto err; + } + + ret = of_property_read_u32(np, "nvidia,gpu-throt-level", &val); + if (!ret && val <= TEGRA_SOCTHERM_THROT_LEVEL_HIGH) + stc->gpu_throt_level = val; + else + goto err; + + return 0; + +err: + dev_err(dev, "throttle-cfg: %s: no throt prop or invalid prop\n", + stc->name); + return -EINVAL; +} + /** * soctherm_init_hw_throt_cdev() - Parse the HW throttle configurations * and register them as cooling devices. @@ -939,8 +1698,7 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) struct tegra_soctherm *ts = dev_get_drvdata(dev); struct device_node *np_stc, *np_stcc; const char *name; - u32 val; - int i, r; + int i; for (i = 0; i < THROTTLE_SIZE; i++) { ts->throt_cfgs[i].name = throt_names[i]; @@ -958,6 +1716,7 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) for_each_child_of_node(np_stc, np_stcc) { struct soctherm_throt_cfg *stc; struct thermal_cooling_device *tcd; + int err; name = np_stcc->name; stc = find_throttle_cfg_by_name(ts, name); @@ -967,51 +1726,34 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) continue; } - r = of_property_read_u32(np_stcc, "nvidia,priority", &val); - if (r) { - dev_info(dev, - "throttle-cfg: %s: missing priority\n", name); - continue; + if (stc->init) { + dev_err(dev, "throttle-cfg: %s: redefined!\n", name); + of_node_put(np_stcc); + break; } - stc->priority = val; - - if (ts->soc->use_ccroc) { - r = of_property_read_u32(np_stcc, - "nvidia,cpu-throt-level", - &val); - if (r) { - dev_info(dev, - "throttle-cfg: %s: missing cpu-throt-level\n", - name); - continue; - } - stc->cpu_throt_level = val; + + err = soctherm_throt_cfg_parse(dev, np_stcc, stc); + if (err) + continue; + + if (stc->id >= THROTTLE_OC1) { + soctherm_oc_cfg_parse(dev, np_stcc, stc); + stc->init = true; } else { - r = of_property_read_u32(np_stcc, - "nvidia,cpu-throt-percent", - &val); - if (r) { - dev_info(dev, - "throttle-cfg: %s: missing cpu-throt-percent\n", - name); - continue; - } - stc->cpu_throt_depth = val; - } - tcd = thermal_of_cooling_device_register(np_stcc, + tcd = thermal_of_cooling_device_register(np_stcc, (char *)name, ts, &throt_cooling_ops); - of_node_put(np_stcc); - if (IS_ERR_OR_NULL(tcd)) { - dev_err(dev, - "throttle-cfg: %s: failed to register cooling device\n", - name); - continue; + if (IS_ERR_OR_NULL(tcd)) { + dev_err(dev, + "throttle-cfg: %s: failed to register cooling device\n", + name); + continue; + } + stc->cdev = tcd; + stc->init = true; } - stc->cdev = tcd; - stc->init = true; } of_node_put(np_stc); @@ -1141,6 +1883,50 @@ static void throttlectl_cpu_mn(struct tegra_soctherm *ts, } /** + * throttlectl_gpu_level_select() - selects throttling level for GPU + * @throt: the LIGHT/HEAVY of throttle event id + * + * This function programs soctherm's interface to GK20a NV_THERM to select + * pre-configured "Low", "Medium" or "Heavy" throttle levels. + * + * Return: boolean true if HW was programmed + */ +static void throttlectl_gpu_level_select(struct tegra_soctherm *ts, + enum soctherm_throttle_id throt) +{ + u32 r, level, throt_vect; + + level = ts->throt_cfgs[throt].gpu_throt_level; + throt_vect = THROT_LEVEL_TO_DEPTH(level); + r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_GPU)); + r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1); + r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT_GPU_MASK, throt_vect); + writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_GPU)); +} + +static int soctherm_oc_cfg_program(struct tegra_soctherm *ts, + enum soctherm_throttle_id throt) +{ + u32 r; + struct soctherm_oc_cfg *oc = &ts->throt_cfgs[throt].oc_cfg; + + if (oc->mode == OC_THROTTLE_MODE_DISABLED) + return -EINVAL; + + r = REG_SET_MASK(0, OC1_CFG_HW_RESTORE_MASK, 1); + r = REG_SET_MASK(r, OC1_CFG_THROTTLE_MODE_MASK, oc->mode); + r = REG_SET_MASK(r, OC1_CFG_ALARM_POLARITY_MASK, oc->active_low); + r = REG_SET_MASK(r, OC1_CFG_EN_THROTTLE_MASK, 1); + writel(r, ts->regs + ALARM_CFG(throt)); + writel(oc->throt_period, ts->regs + ALARM_THROTTLE_PERIOD(throt)); + writel(oc->alarm_cnt_thresh, ts->regs + ALARM_CNT_THRESHOLD(throt)); + writel(oc->alarm_filter, ts->regs + ALARM_FILTER(throt)); + soctherm_oc_intr_enable(ts, throt, oc->intr_en); + + return 0; +} + +/** * soctherm_throttle_program() - programs pulse skippers' configuration * @throt: the LIGHT/HEAVY of the throttle event id. * @@ -1156,12 +1942,17 @@ static void soctherm_throttle_program(struct tegra_soctherm *ts, if (!stc.init) return; + if ((throt >= THROTTLE_OC1) && (soctherm_oc_cfg_program(ts, throt))) + return; + /* Setup PSKIP parameters */ if (ts->soc->use_ccroc) throttlectl_cpu_level_select(ts, throt); else throttlectl_cpu_mn(ts, throt); + throttlectl_gpu_level_select(ts, throt); + r = REG_SET_MASK(0, THROT_PRIORITY_LITE_PRIO_MASK, stc.priority); writel(r, ts->regs + THROT_PRIORITY_CTRL(throt)); @@ -1215,6 +2006,57 @@ static void tegra_soctherm_throttle(struct device *dev) writel(v, ts->regs + THERMCTL_STATS_CTL); } +static int soctherm_interrupts_init(struct platform_device *pdev, + struct tegra_soctherm *tegra) +{ + struct device_node *np = pdev->dev.of_node; + int ret; + + ret = soctherm_oc_int_init(np, TEGRA_SOC_OC_IRQ_MAX); + if (ret < 0) { + dev_err(&pdev->dev, "soctherm_oc_int_init failed\n"); + return ret; + } + + tegra->thermal_irq = platform_get_irq(pdev, 0); + if (tegra->thermal_irq < 0) { + dev_dbg(&pdev->dev, "get 'thermal_irq' failed.\n"); + return 0; + } + + tegra->edp_irq = platform_get_irq(pdev, 1); + if (tegra->edp_irq < 0) { + dev_dbg(&pdev->dev, "get 'edp_irq' failed.\n"); + return 0; + } + + ret = devm_request_threaded_irq(&pdev->dev, + tegra->thermal_irq, + soctherm_thermal_isr, + soctherm_thermal_isr_thread, + IRQF_ONESHOT, + dev_name(&pdev->dev), + tegra); + if (ret < 0) { + dev_err(&pdev->dev, "request_irq 'thermal_irq' failed.\n"); + return ret; + } + + ret = devm_request_threaded_irq(&pdev->dev, + tegra->edp_irq, + soctherm_edp_isr, + soctherm_edp_isr_thread, + IRQF_ONESHOT, + "soctherm_edp", + tegra); + if (ret < 0) { + dev_err(&pdev->dev, "request_irq 'edp_irq' failed.\n"); + return ret; + } + + return 0; +} + static void soctherm_init(struct platform_device *pdev) { struct tegra_soctherm *tegra = platform_get_drvdata(pdev); @@ -1292,6 +2134,7 @@ static int tegra_soctherm_probe(struct platform_device *pdev) if (!tegra) return -ENOMEM; + mutex_init(&tegra->thermctl_lock); dev_set_drvdata(&pdev->dev, tegra); tegra->soc = soc; @@ -1370,6 +2213,8 @@ static int tegra_soctherm_probe(struct platform_device *pdev) if (err) return err; + soctherm_thermtrips_parse(pdev); + soctherm_init_hw_throt_cdev(pdev); soctherm_init(pdev); @@ -1406,6 +2251,8 @@ static int tegra_soctherm_probe(struct platform_device *pdev) goto disable_clocks; } + err = soctherm_interrupts_init(pdev, tegra); + soctherm_debug_init(pdev); return 0; diff --git a/drivers/thermal/tegra/soctherm.h b/drivers/thermal/tegra/soctherm.h index e96ca73fd780..70501e73d586 100644 --- a/drivers/thermal/tegra/soctherm.h +++ b/drivers/thermal/tegra/soctherm.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. * @@ -29,6 +30,14 @@ #define THERMCTL_THERMTRIP_CTL 0x80 /* BITs are defined in device file */ +#define THERMCTL_INTR_ENABLE 0x88 +#define THERMCTL_INTR_DISABLE 0x8c +#define TH_INTR_UP_DN_EN 0x3 +#define THERM_IRQ_MEM_MASK (TH_INTR_UP_DN_EN << 24) +#define THERM_IRQ_GPU_MASK (TH_INTR_UP_DN_EN << 16) +#define THERM_IRQ_CPU_MASK (TH_INTR_UP_DN_EN << 8) +#define THERM_IRQ_TSENSE_MASK (TH_INTR_UP_DN_EN << 0) + #define SENSOR_PDIV 0x1c0 #define SENSOR_PDIV_CPU_MASK (0xf << 12) #define SENSOR_PDIV_GPU_MASK (0xf << 8) @@ -70,6 +79,7 @@ struct tegra_tsensor_group { u32 thermtrip_enable_mask; u32 thermtrip_any_en_mask; u32 thermtrip_threshold_mask; + u32 thermctl_isr_mask; u16 thermctl_lvl0_offset; u32 thermctl_lvl0_up_thresh_mask; u32 thermctl_lvl0_dn_thresh_mask; @@ -92,6 +102,11 @@ struct tegra_tsensor { const struct tegra_tsensor_group *group; }; +struct tsensor_group_thermtrips { + u8 id; + u32 temp; +}; + struct tegra_soctherm_fuse { u32 fuse_base_cp_mask, fuse_base_cp_shift; u32 fuse_base_ft_mask, fuse_base_ft_shift; @@ -113,6 +128,7 @@ struct tegra_soctherm_soc { const int thresh_grain; const unsigned int bptt; const bool use_ccroc; + struct tsensor_group_thermtrips *thermtrips; }; int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse, diff --git a/drivers/thermal/tegra/tegra124-soctherm.c b/drivers/thermal/tegra/tegra124-soctherm.c index 36768630f78c..20ad27f4d1a1 100644 --- a/drivers/thermal/tegra/tegra124-soctherm.c +++ b/drivers/thermal/tegra/tegra124-soctherm.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * 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 = { .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA124_THERMTRIP_CPU_EN_MASK, .thermtrip_threshold_mask = TEGRA124_THERMTRIP_CPU_THRESH_MASK, + .thermctl_isr_mask = THERM_IRQ_CPU_MASK, .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU, .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK, .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 = { .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA124_THERMTRIP_GPU_EN_MASK, .thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK, + .thermctl_isr_mask = THERM_IRQ_GPU_MASK, .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU, .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK, .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 = { .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA124_THERMTRIP_TSENSE_EN_MASK, .thermtrip_threshold_mask = TEGRA124_THERMTRIP_TSENSE_THRESH_MASK, + .thermctl_isr_mask = THERM_IRQ_TSENSE_MASK, .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE, .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK, .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 = { .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA124_THERMTRIP_MEM_EN_MASK, .thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK, + .thermctl_isr_mask = THERM_IRQ_MEM_MASK, .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM, .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK, .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK, diff --git a/drivers/thermal/tegra/tegra132-soctherm.c b/drivers/thermal/tegra/tegra132-soctherm.c index 97fa30501eb1..b76308fdad9e 100644 --- a/drivers/thermal/tegra/tegra132-soctherm.c +++ b/drivers/thermal/tegra/tegra132-soctherm.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * 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 = { .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA132_THERMTRIP_CPU_EN_MASK, .thermtrip_threshold_mask = TEGRA132_THERMTRIP_CPU_THRESH_MASK, + .thermctl_isr_mask = THERM_IRQ_CPU_MASK, .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU, .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK, .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 = { .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA132_THERMTRIP_GPU_EN_MASK, .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK, + .thermctl_isr_mask = THERM_IRQ_GPU_MASK, .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU, .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK, .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 = { .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA132_THERMTRIP_TSENSE_EN_MASK, .thermtrip_threshold_mask = TEGRA132_THERMTRIP_TSENSE_THRESH_MASK, + .thermctl_isr_mask = THERM_IRQ_TSENSE_MASK, .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE, .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK, .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 = { .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA132_THERMTRIP_MEM_EN_MASK, .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK, + .thermctl_isr_mask = THERM_IRQ_MEM_MASK, .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM, .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK, .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK, diff --git a/drivers/thermal/tegra/tegra210-soctherm.c b/drivers/thermal/tegra/tegra210-soctherm.c index ad53169a8e95..d31b50050faa 100644 --- a/drivers/thermal/tegra/tegra210-soctherm.c +++ b/drivers/thermal/tegra/tegra210-soctherm.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * 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 = { .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA210_THERMTRIP_CPU_EN_MASK, .thermtrip_threshold_mask = TEGRA210_THERMTRIP_CPU_THRESH_MASK, + .thermctl_isr_mask = THERM_IRQ_CPU_MASK, .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU, .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK, .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 = { .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA210_THERMTRIP_GPU_EN_MASK, .thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK, + .thermctl_isr_mask = THERM_IRQ_GPU_MASK, .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU, .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK, .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 = { .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA210_THERMTRIP_TSENSE_EN_MASK, .thermtrip_threshold_mask = TEGRA210_THERMTRIP_TSENSE_THRESH_MASK, + .thermctl_isr_mask = THERM_IRQ_TSENSE_MASK, .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE, .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK, .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 = { .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA210_THERMTRIP_MEM_EN_MASK, .thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK, + .thermctl_isr_mask = THERM_IRQ_MEM_MASK, .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM, .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK, .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK, @@ -203,6 +208,13 @@ static const struct tegra_soctherm_fuse tegra210_soctherm_fuse = { .fuse_spare_realignment = 0, }; +struct tsensor_group_thermtrips tegra210_tsensor_thermtrips[] = { + {.id = TEGRA124_SOCTHERM_SENSOR_NUM}, + {.id = TEGRA124_SOCTHERM_SENSOR_NUM}, + {.id = TEGRA124_SOCTHERM_SENSOR_NUM}, + {.id = TEGRA124_SOCTHERM_SENSOR_NUM}, +}; + const struct tegra_soctherm_soc tegra210_soctherm = { .tsensors = tegra210_tsensors, .num_tsensors = ARRAY_SIZE(tegra210_tsensors), @@ -212,4 +224,5 @@ const struct tegra_soctherm_soc tegra210_soctherm = { .thresh_grain = TEGRA210_THRESH_GRAIN, .bptt = TEGRA210_BPTT, .use_ccroc = false, + .thermtrips = tegra210_tsensor_thermtrips, }; diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index e22fc60ad36d..deb244f12de4 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -29,6 +29,9 @@ static int gadc_thermal_adc_to_temp(struct gadc_thermal_info *gti, int val) int temp, temp_hi, temp_lo, adc_hi, adc_lo; int i; + if (!gti->lookup_table) + return val; + for (i = 0; i < gti->nlookup_table; i++) { if (val >= gti->lookup_table[2 * i + 1]) break; @@ -81,9 +84,9 @@ static int gadc_thermal_read_linear_lookup_table(struct device *dev, ntable = of_property_count_elems_of_size(np, "temperature-lookup-table", sizeof(u32)); - if (ntable < 0) { - dev_err(dev, "Lookup table is not provided\n"); - return ntable; + if (ntable <= 0) { + dev_notice(dev, "no lookup table, assuming DAC channel returns milliCelcius\n"); + return 0; } if (ntable % 2) { diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 6590bb5cb688..e0b530603db6 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1046,6 +1046,55 @@ thermal_of_cooling_device_register(struct device_node *np, } EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); +static void thermal_cooling_device_release(struct device *dev, void *res) +{ + thermal_cooling_device_unregister( + *(struct thermal_cooling_device **)res); +} + +/** + * devm_thermal_of_cooling_device_register() - register an OF thermal cooling + * device + * @dev: a valid struct device pointer of a sensor device. + * @np: a pointer to a device tree node. + * @type: the thermal cooling device type. + * @devdata: device private data. + * @ops: standard thermal cooling devices callbacks. + * + * This function will register a cooling device with device tree node reference. + * This interface function adds a new thermal cooling device (fan/processor/...) + * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself + * to all the thermal zone devices registered at the same time. + * + * Return: a pointer to the created struct thermal_cooling_device or an + * ERR_PTR. Caller must check return value with IS_ERR*() helpers. + */ +struct thermal_cooling_device * +devm_thermal_of_cooling_device_register(struct device *dev, + struct device_node *np, + char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + struct thermal_cooling_device **ptr, *tcd; + + ptr = devres_alloc(thermal_cooling_device_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + tcd = __thermal_cooling_device_register(np, type, devdata, ops); + if (IS_ERR(tcd)) { + devres_free(ptr); + return tcd; + } + + *ptr = tcd; + devres_add(dev, ptr); + + return tcd; +} +EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register); + static void __unbind(struct thermal_zone_device *tz, int mask, struct thermal_cooling_device *cdev) { diff --git a/drivers/thermal/thermal_mmio.c b/drivers/thermal/thermal_mmio.c new file mode 100644 index 000000000000..de3cceea23bc --- /dev/null +++ b/drivers/thermal/thermal_mmio.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + */ + +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/thermal.h> + +struct thermal_mmio { + void __iomem *mmio_base; + u32 (*read_mmio)(void __iomem *mmio_base); + u32 mask; + int factor; +}; + +static u32 thermal_mmio_readb(void __iomem *mmio_base) +{ + return readb(mmio_base); +} + +static int thermal_mmio_get_temperature(void *private, int *temp) +{ + int t; + struct thermal_mmio *sensor = + (struct thermal_mmio *)private; + + t = sensor->read_mmio(sensor->mmio_base) & sensor->mask; + t *= sensor->factor; + + *temp = t; + + return 0; +} + +static struct thermal_zone_of_device_ops thermal_mmio_ops = { + .get_temp = thermal_mmio_get_temperature, +}; + +static int thermal_mmio_probe(struct platform_device *pdev) +{ + struct resource *resource; + struct thermal_mmio *sensor; + int (*sensor_init_func)(struct platform_device *pdev, + struct thermal_mmio *sensor); + struct thermal_zone_device *thermal_zone; + int ret; + int temperature; + + sensor = devm_kzalloc(&pdev->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (IS_ERR(resource)) { + dev_err(&pdev->dev, + "fail to get platform memory resource (%ld)\n", + PTR_ERR(resource)); + return PTR_ERR(resource); + } + + sensor->mmio_base = devm_ioremap_resource(&pdev->dev, resource); + if (IS_ERR(sensor->mmio_base)) { + dev_err(&pdev->dev, "failed to ioremap memory (%ld)\n", + PTR_ERR(sensor->mmio_base)); + return PTR_ERR(sensor->mmio_base); + } + + sensor_init_func = device_get_match_data(&pdev->dev); + if (sensor_init_func) { + ret = sensor_init_func(pdev, sensor); + if (ret) { + dev_err(&pdev->dev, + "failed to initialize sensor (%d)\n", + ret); + return ret; + } + } + + thermal_zone = devm_thermal_zone_of_sensor_register(&pdev->dev, + 0, + sensor, + &thermal_mmio_ops); + if (IS_ERR(thermal_zone)) { + dev_err(&pdev->dev, + "failed to register sensor (%ld)\n", + PTR_ERR(thermal_zone)); + return PTR_ERR(thermal_zone); + } + + thermal_mmio_get_temperature(sensor, &temperature); + dev_info(&pdev->dev, + "thermal mmio sensor %s registered, current temperature: %d\n", + pdev->name, temperature); + + return 0; +} + +static int al_thermal_init(struct platform_device *pdev, + struct thermal_mmio *sensor) +{ + sensor->read_mmio = thermal_mmio_readb; + sensor->mask = 0xff; + sensor->factor = 1000; + + return 0; +} + +static const struct of_device_id thermal_mmio_id_table[] = { + { .compatible = "amazon,al-thermal", .data = al_thermal_init}, + {} +}; +MODULE_DEVICE_TABLE(of, thermal_mmio_id_table); + +static struct platform_driver thermal_mmio_driver = { + .probe = thermal_mmio_probe, + .driver = { + .name = "thermal-mmio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(thermal_mmio_id_table), + }, +}; + +module_platform_driver(thermal_mmio_driver); + +MODULE_AUTHOR("Talel Shenhar <talel@amazon.com>"); +MODULE_DESCRIPTION("Thermal MMIO Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/thermal/tegra124-soctherm.h b/include/dt-bindings/thermal/tegra124-soctherm.h index c15e8b709a0d..444c7bdde146 100644 --- a/include/dt-bindings/thermal/tegra124-soctherm.h +++ b/include/dt-bindings/thermal/tegra124-soctherm.h @@ -12,9 +12,9 @@ #define TEGRA124_SOCTHERM_SENSOR_PLLX 3 #define TEGRA124_SOCTHERM_SENSOR_NUM 4 -#define TEGRA_SOCTHERM_THROT_LEVEL_LOW 0 -#define TEGRA_SOCTHERM_THROT_LEVEL_MED 1 -#define TEGRA_SOCTHERM_THROT_LEVEL_HIGH 2 -#define TEGRA_SOCTHERM_THROT_LEVEL_NONE -1 +#define TEGRA_SOCTHERM_THROT_LEVEL_NONE 0 +#define TEGRA_SOCTHERM_THROT_LEVEL_LOW 1 +#define TEGRA_SOCTHERM_THROT_LEVEL_MED 2 +#define TEGRA_SOCTHERM_THROT_LEVEL_HIGH 3 #endif diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 5f4705f46c2f..4a22099ed8c0 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -447,6 +447,11 @@ struct thermal_cooling_device *thermal_cooling_device_register(char *, void *, struct thermal_cooling_device * thermal_of_cooling_device_register(struct device_node *np, char *, void *, const struct thermal_cooling_device_ops *); +struct thermal_cooling_device * +devm_thermal_of_cooling_device_register(struct device *dev, + struct device_node *np, + char *type, void *devdata, + const struct thermal_cooling_device_ops *ops); void thermal_cooling_device_unregister(struct thermal_cooling_device *); struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name); int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp); @@ -503,6 +508,14 @@ static inline struct thermal_cooling_device * thermal_of_cooling_device_register(struct device_node *np, char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { return ERR_PTR(-ENODEV); } +static inline struct thermal_cooling_device * +devm_thermal_of_cooling_device_register(struct device *dev, + struct device_node *np, + char *type, void *devdata, + const struct thermal_cooling_device_ops *ops) +{ + return ERR_PTR(-ENODEV); +} static inline void thermal_cooling_device_unregister( struct thermal_cooling_device *cdev) { } |