diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-10-26 15:04:29 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-10-26 15:04:29 -0400 |
| commit | 0ef7791e2bfb2e10aa95dc492eab72074cef9942 (patch) | |
| tree | 8b14a15cddceaf05c116b57ad59212fe651fb26a | |
| parent | befa93633193e5327e4045d1e5fa29114580fa5d (diff) | |
| parent | 760eea43f8c6d48684f1f34b8a02fddc1456e849 (diff) | |
Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal
Pull thermal SoC updates from Eduardo Valentin:
"Several new things coming up. Specifics:
- Rework of tsens and hisi thermal drivers
- OF-thermal now allows sharing multiple cooling devices on maps
- Added support for r8a7744 and R8A77970 on rcar thermal driver
- Added support for r8a774a1 on rcar_gen3 thermal driver
- New thermal driver stm32
- Fixes on multiple thermal drivers: of-thermal, imx, qoriq, armada,
qcom-spmi, rcar, da9062/61"
* 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal: (41 commits)
thermal: da9062/61: Prevent hardware access during system suspend
thermal: rcar_thermal: Prevent doing work after unbind
thermal: rcar_thermal: Prevent hardware access during system suspend
thermal: rcar_gen3_thermal: add R8A77980 support
dt-bindings: thermal: rcar-gen3-thermal: document R8A77980 bindings
thermal: add stm32 thermal driver
dt-bindings: stm32-thermal: add binding documentation
thermal: rcar_thermal: add R8A77970 support
dt-bindings: thermal: rcar-thermal: document R8A77970 bindings
thermal: rcar_thermal: fix duplicate IRQ request
dt-bindings: thermal: rcar: Add device tree support for r8a7744
thermal/drivers/hisi: Add the dual clusters sensors for hi3660
thermal/drivers/hisi: Add more sensors channel
thermal/drivers/hisi: Remove pointless irq field
thermal/drivers/hisi: Use platform_get_irq_byname
thermal/drivers/hisi: Replace macro name with relevant sensor location
thermal/drivers/hisi: Add multiple sensors support
thermal/drivers/hisi: Prepare to support multiple sensors
thermal/drivers/hisi: Factor out the probe functions
thermal/drivers/hisi: Set the thermal zone private data to the sensor pointer
...
27 files changed, 1365 insertions, 303 deletions
diff --git a/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt b/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt index 290ec06fa33a..0273a92a2a84 100644 --- a/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt +++ b/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt | |||
| @@ -6,8 +6,7 @@ interrupt signal and status register to identify high PMIC die temperature. | |||
| 6 | 6 | ||
| 7 | Required properties: | 7 | Required properties: |
| 8 | - compatible: Should contain "qcom,spmi-temp-alarm". | 8 | - compatible: Should contain "qcom,spmi-temp-alarm". |
| 9 | - reg: Specifies the SPMI address and length of the controller's | 9 | - reg: Specifies the SPMI address. |
| 10 | registers. | ||
| 11 | - interrupts: PMIC temperature alarm interrupt. | 10 | - interrupts: PMIC temperature alarm interrupt. |
| 12 | - #thermal-sensor-cells: Should be 0. See thermal.txt for a description. | 11 | - #thermal-sensor-cells: Should be 0. See thermal.txt for a description. |
| 13 | 12 | ||
| @@ -20,7 +19,7 @@ Example: | |||
| 20 | 19 | ||
| 21 | pm8941_temp: thermal-alarm@2400 { | 20 | pm8941_temp: thermal-alarm@2400 { |
| 22 | compatible = "qcom,spmi-temp-alarm"; | 21 | compatible = "qcom,spmi-temp-alarm"; |
| 23 | reg = <0x2400 0x100>; | 22 | reg = <0x2400>; |
| 24 | interrupts = <0 0x24 0 IRQ_TYPE_EDGE_RISING>; | 23 | interrupts = <0 0x24 0 IRQ_TYPE_EDGE_RISING>; |
| 25 | #thermal-sensor-cells = <0>; | 24 | #thermal-sensor-cells = <0>; |
| 26 | 25 | ||
| @@ -36,19 +35,14 @@ Example: | |||
| 36 | thermal-sensors = <&pm8941_temp>; | 35 | thermal-sensors = <&pm8941_temp>; |
| 37 | 36 | ||
| 38 | trips { | 37 | trips { |
| 39 | passive { | 38 | stage1 { |
| 40 | temperature = <1050000>; | 39 | temperature = <105000>; |
| 41 | hysteresis = <2000>; | 40 | hysteresis = <2000>; |
| 42 | type = "passive"; | 41 | type = "passive"; |
| 43 | }; | 42 | }; |
| 44 | alert { | 43 | stage2 { |
| 45 | temperature = <125000>; | 44 | temperature = <125000>; |
| 46 | hysteresis = <2000>; | 45 | hysteresis = <2000>; |
| 47 | type = "hot"; | ||
| 48 | }; | ||
| 49 | crit { | ||
| 50 | temperature = <145000>; | ||
| 51 | hysteresis = <2000>; | ||
| 52 | type = "critical"; | 46 | type = "critical"; |
| 53 | }; | 47 | }; |
| 54 | }; | 48 | }; |
diff --git a/Documentation/devicetree/bindings/thermal/qoriq-thermal.txt b/Documentation/devicetree/bindings/thermal/qoriq-thermal.txt index 20ca4ef9d776..04cbb90a5d3e 100644 --- a/Documentation/devicetree/bindings/thermal/qoriq-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/qoriq-thermal.txt | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | * Thermal Monitoring Unit (TMU) on Freescale QorIQ SoCs | 1 | * Thermal Monitoring Unit (TMU) on Freescale QorIQ SoCs |
| 2 | 2 | ||
| 3 | Required properties: | 3 | Required properties: |
| 4 | - compatible : Must include "fsl,qoriq-tmu". The version of the device is | 4 | - compatible : Must include "fsl,qoriq-tmu" or "fsl,imx8mq-tmu". The |
| 5 | determined by the TMU IP Block Revision Register (IPBRR0) at | 5 | version of the device is determined by the TMU IP Block Revision |
| 6 | offset 0x0BF8. | 6 | Register (IPBRR0) at offset 0x0BF8. |
| 7 | Table of correspondences between IPBRR0 values and example chips: | 7 | Table of correspondences between IPBRR0 values and example chips: |
| 8 | Value Device | 8 | Value Device |
| 9 | ---------- ----- | 9 | ---------- ----- |
diff --git a/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt b/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt index cfa154bb0fa7..ad9a435afef4 100644 --- a/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt | |||
| @@ -7,9 +7,11 @@ inside the LSI. | |||
| 7 | Required properties: | 7 | Required properties: |
| 8 | - compatible : "renesas,<soctype>-thermal", | 8 | - compatible : "renesas,<soctype>-thermal", |
| 9 | Examples with soctypes are: | 9 | Examples with soctypes are: |
| 10 | - "renesas,r8a774a1-thermal" (RZ/G2M) | ||
| 10 | - "renesas,r8a7795-thermal" (R-Car H3) | 11 | - "renesas,r8a7795-thermal" (R-Car H3) |
| 11 | - "renesas,r8a7796-thermal" (R-Car M3-W) | 12 | - "renesas,r8a7796-thermal" (R-Car M3-W) |
| 12 | - "renesas,r8a77965-thermal" (R-Car M3-N) | 13 | - "renesas,r8a77965-thermal" (R-Car M3-N) |
| 14 | - "renesas,r8a77980-thermal" (R-Car V3H) | ||
| 13 | - reg : Address ranges of the thermal registers. Each sensor | 15 | - reg : Address ranges of the thermal registers. Each sensor |
| 14 | needs one address range. Sorting must be done in | 16 | needs one address range. Sorting must be done in |
| 15 | increasing order according to datasheet, i.e. | 17 | increasing order according to datasheet, i.e. |
| @@ -19,7 +21,8 @@ Required properties: | |||
| 19 | 21 | ||
| 20 | Optional properties: | 22 | Optional properties: |
| 21 | 23 | ||
| 22 | - interrupts : interrupts routed to the TSC (3 for H3, M3-W and M3-N) | 24 | - interrupts : interrupts routed to the TSC (3 for H3, M3-W, M3-N, |
| 25 | and V3H) | ||
| 23 | - power-domain : Must contain a reference to the power domain. This | 26 | - power-domain : Must contain a reference to the power domain. This |
| 24 | property is mandatory if the thermal sensor instance | 27 | property is mandatory if the thermal sensor instance |
| 25 | is part of a controllable power domain. | 28 | is part of a controllable power domain. |
diff --git a/Documentation/devicetree/bindings/thermal/rcar-thermal.txt b/Documentation/devicetree/bindings/thermal/rcar-thermal.txt index 67c563f1b4c4..73e1613d2cb0 100644 --- a/Documentation/devicetree/bindings/thermal/rcar-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/rcar-thermal.txt | |||
| @@ -4,15 +4,17 @@ Required properties: | |||
| 4 | - compatible : "renesas,thermal-<soctype>", | 4 | - compatible : "renesas,thermal-<soctype>", |
| 5 | "renesas,rcar-gen2-thermal" (with thermal-zone) or | 5 | "renesas,rcar-gen2-thermal" (with thermal-zone) or |
| 6 | "renesas,rcar-thermal" (without thermal-zone) as | 6 | "renesas,rcar-thermal" (without thermal-zone) as |
| 7 | fallback except R-Car D3. | 7 | fallback except R-Car V3M/D3. |
| 8 | Examples with soctypes are: | 8 | Examples with soctypes are: |
| 9 | - "renesas,thermal-r8a73a4" (R-Mobile APE6) | 9 | - "renesas,thermal-r8a73a4" (R-Mobile APE6) |
| 10 | - "renesas,thermal-r8a7743" (RZ/G1M) | 10 | - "renesas,thermal-r8a7743" (RZ/G1M) |
| 11 | - "renesas,thermal-r8a7744" (RZ/G1N) | ||
| 11 | - "renesas,thermal-r8a7779" (R-Car H1) | 12 | - "renesas,thermal-r8a7779" (R-Car H1) |
| 12 | - "renesas,thermal-r8a7790" (R-Car H2) | 13 | - "renesas,thermal-r8a7790" (R-Car H2) |
| 13 | - "renesas,thermal-r8a7791" (R-Car M2-W) | 14 | - "renesas,thermal-r8a7791" (R-Car M2-W) |
| 14 | - "renesas,thermal-r8a7792" (R-Car V2H) | 15 | - "renesas,thermal-r8a7792" (R-Car V2H) |
| 15 | - "renesas,thermal-r8a7793" (R-Car M2-N) | 16 | - "renesas,thermal-r8a7793" (R-Car M2-N) |
| 17 | - "renesas,thermal-r8a77970" (R-Car V3M) | ||
| 16 | - "renesas,thermal-r8a77995" (R-Car D3) | 18 | - "renesas,thermal-r8a77995" (R-Car D3) |
| 17 | - reg : Address range of the thermal registers. | 19 | - reg : Address range of the thermal registers. |
| 18 | The 1st reg will be recognized as common register | 20 | The 1st reg will be recognized as common register |
| @@ -21,7 +23,7 @@ Required properties: | |||
| 21 | Option properties: | 23 | Option properties: |
| 22 | 24 | ||
| 23 | - interrupts : If present should contain 3 interrupts for | 25 | - interrupts : If present should contain 3 interrupts for |
| 24 | R-Car D3 or 1 interrupt otherwise. | 26 | R-Car V3M/D3 or 1 interrupt otherwise. |
| 25 | 27 | ||
| 26 | Example (non interrupt support): | 28 | Example (non interrupt support): |
| 27 | 29 | ||
diff --git a/Documentation/devicetree/bindings/thermal/stm32-thermal.txt b/Documentation/devicetree/bindings/thermal/stm32-thermal.txt new file mode 100644 index 000000000000..8c0d5a4d8031 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/stm32-thermal.txt | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | Binding for Thermal Sensor for STMicroelectronics STM32 series of SoCs. | ||
| 2 | |||
| 3 | On STM32 SoCs, the Digital Temperature Sensor (DTS) is in charge of managing an | ||
| 4 | analog block which delivers a frequency depending on the internal SoC's | ||
| 5 | temperature. By using a reference frequency, DTS is able to provide a sample | ||
| 6 | number which can be translated into a temperature by the user. | ||
| 7 | |||
| 8 | DTS provides interrupt notification mechanism by threshold. This mechanism | ||
| 9 | offers two temperature trip points: passive and critical. The first is intended | ||
| 10 | for passive cooling notification while the second is used for over-temperature | ||
| 11 | reset. | ||
| 12 | |||
| 13 | Required parameters: | ||
| 14 | ------------------- | ||
| 15 | |||
| 16 | compatible: Should be "st,stm32-thermal" | ||
| 17 | reg: This should be the physical base address and length of the | ||
| 18 | sensor's registers. | ||
| 19 | clocks: Phandle of the clock used by the thermal sensor. | ||
| 20 | See: Documentation/devicetree/bindings/clock/clock-bindings.txt | ||
| 21 | clock-names: Should be "pclk" for register access clock and reference clock. | ||
| 22 | See: Documentation/devicetree/bindings/resource-names.txt | ||
| 23 | #thermal-sensor-cells: Should be 0. See ./thermal.txt for a description. | ||
| 24 | interrupts: Standard way to define interrupt number. | ||
| 25 | |||
| 26 | Example: | ||
| 27 | |||
| 28 | thermal-zones { | ||
| 29 | cpu_thermal: cpu-thermal { | ||
| 30 | polling-delay-passive = <0>; | ||
| 31 | polling-delay = <0>; | ||
| 32 | |||
| 33 | thermal-sensors = <&thermal>; | ||
| 34 | |||
| 35 | trips { | ||
| 36 | cpu_alert1: cpu-alert1 { | ||
| 37 | temperature = <85000>; | ||
| 38 | hysteresis = <0>; | ||
| 39 | type = "passive"; | ||
| 40 | }; | ||
| 41 | |||
| 42 | cpu-crit: cpu-crit { | ||
| 43 | temperature = <120000>; | ||
| 44 | hysteresis = <0>; | ||
| 45 | type = "critical"; | ||
| 46 | }; | ||
| 47 | }; | ||
| 48 | |||
| 49 | cooling-maps { | ||
| 50 | }; | ||
| 51 | }; | ||
| 52 | }; | ||
| 53 | |||
| 54 | thermal: thermal@50028000 { | ||
| 55 | compatible = "st,stm32-thermal"; | ||
| 56 | reg = <0x50028000 0x100>; | ||
| 57 | clocks = <&rcc TMPSENS>; | ||
| 58 | clock-names = "pclk"; | ||
| 59 | #thermal-sensor-cells = <0>; | ||
| 60 | interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>; | ||
| 61 | }; | ||
diff --git a/Documentation/devicetree/bindings/thermal/thermal.txt b/Documentation/devicetree/bindings/thermal/thermal.txt index eb7ee91556a5..ca14ba959e0d 100644 --- a/Documentation/devicetree/bindings/thermal/thermal.txt +++ b/Documentation/devicetree/bindings/thermal/thermal.txt | |||
| @@ -152,7 +152,7 @@ Optional property: | |||
| 152 | Elem size: one cell the sensors listed in the thermal-sensors property. | 152 | Elem size: one cell the sensors listed in the thermal-sensors property. |
| 153 | Elem type: signed Coefficients defaults to 1, in case this property | 153 | Elem type: signed Coefficients defaults to 1, in case this property |
| 154 | is not specified. A simple linear polynomial is used: | 154 | is not specified. A simple linear polynomial is used: |
| 155 | Z = c0 * x0 + c1 + x1 + ... + c(n-1) * x(n-1) + cn. | 155 | Z = c0 * x0 + c1 * x1 + ... + c(n-1) * x(n-1) + cn. |
| 156 | 156 | ||
| 157 | The coefficients are ordered and they match with sensors | 157 | The coefficients are ordered and they match with sensors |
| 158 | by means of sensor ID. Additional coefficients are | 158 | by means of sensor ID. Additional coefficients are |
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 0e69edc77d18..5422523c03f8 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig | |||
| @@ -432,7 +432,7 @@ source "drivers/thermal/samsung/Kconfig" | |||
| 432 | endmenu | 432 | endmenu |
| 433 | 433 | ||
| 434 | menu "STMicroelectronics thermal drivers" | 434 | menu "STMicroelectronics thermal drivers" |
| 435 | depends on ARCH_STI && OF | 435 | depends on (ARCH_STI || ARCH_STM32) && OF |
| 436 | source "drivers/thermal/st/Kconfig" | 436 | source "drivers/thermal/st/Kconfig" |
| 437 | endmenu | 437 | endmenu |
| 438 | 438 | ||
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 610344eb3e03..82bb50dc6423 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile | |||
| @@ -53,7 +53,7 @@ obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ | |||
| 53 | obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ | 53 | obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ |
| 54 | obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o | 54 | obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o |
| 55 | obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o | 55 | obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o |
| 56 | obj-$(CONFIG_ST_THERMAL) += st/ | 56 | obj-y += st/ |
| 57 | obj-$(CONFIG_QCOM_TSENS) += qcom/ | 57 | obj-$(CONFIG_QCOM_TSENS) += qcom/ |
| 58 | obj-y += tegra/ | 58 | obj-y += tegra/ |
| 59 | obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o | 59 | obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o |
diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index 2c2f6d93034e..92f67d40f2e9 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c | |||
| @@ -526,8 +526,8 @@ static int armada_thermal_probe_legacy(struct platform_device *pdev, | |||
| 526 | 526 | ||
| 527 | /* First memory region points towards the status register */ | 527 | /* First memory region points towards the status register */ |
| 528 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 528 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 529 | if (IS_ERR(res)) | 529 | if (!res) |
| 530 | return PTR_ERR(res); | 530 | return -EIO; |
| 531 | 531 | ||
| 532 | /* | 532 | /* |
| 533 | * Edit the resource start address and length to map over all the | 533 | * Edit the resource start address and length to map over all the |
diff --git a/drivers/thermal/da9062-thermal.c b/drivers/thermal/da9062-thermal.c index dd8dd947b7f0..01b0cb994457 100644 --- a/drivers/thermal/da9062-thermal.c +++ b/drivers/thermal/da9062-thermal.c | |||
| @@ -106,7 +106,7 @@ static void da9062_thermal_poll_on(struct work_struct *work) | |||
| 106 | THERMAL_EVENT_UNSPECIFIED); | 106 | THERMAL_EVENT_UNSPECIFIED); |
| 107 | 107 | ||
| 108 | delay = msecs_to_jiffies(thermal->zone->passive_delay); | 108 | delay = msecs_to_jiffies(thermal->zone->passive_delay); |
| 109 | schedule_delayed_work(&thermal->work, delay); | 109 | queue_delayed_work(system_freezable_wq, &thermal->work, delay); |
| 110 | return; | 110 | return; |
| 111 | } | 111 | } |
| 112 | 112 | ||
| @@ -125,7 +125,7 @@ static irqreturn_t da9062_thermal_irq_handler(int irq, void *data) | |||
| 125 | struct da9062_thermal *thermal = data; | 125 | struct da9062_thermal *thermal = data; |
| 126 | 126 | ||
| 127 | disable_irq_nosync(thermal->irq); | 127 | disable_irq_nosync(thermal->irq); |
| 128 | schedule_delayed_work(&thermal->work, 0); | 128 | queue_delayed_work(system_freezable_wq, &thermal->work, 0); |
| 129 | 129 | ||
| 130 | return IRQ_HANDLED; | 130 | return IRQ_HANDLED; |
| 131 | } | 131 | } |
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index 761d0559c268..c4111a98f1a7 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c | |||
| @@ -55,25 +55,39 @@ | |||
| 55 | #define HI3660_TEMP_STEP (205) | 55 | #define HI3660_TEMP_STEP (205) |
| 56 | #define HI3660_TEMP_LAG (4000) | 56 | #define HI3660_TEMP_LAG (4000) |
| 57 | 57 | ||
| 58 | #define HI6220_DEFAULT_SENSOR 2 | 58 | #define HI6220_CLUSTER0_SENSOR 2 |
| 59 | #define HI3660_DEFAULT_SENSOR 1 | 59 | #define HI6220_CLUSTER1_SENSOR 1 |
| 60 | |||
| 61 | #define HI3660_LITTLE_SENSOR 0 | ||
| 62 | #define HI3660_BIG_SENSOR 1 | ||
| 63 | #define HI3660_G3D_SENSOR 2 | ||
| 64 | #define HI3660_MODEM_SENSOR 3 | ||
| 65 | |||
| 66 | struct hisi_thermal_data; | ||
| 60 | 67 | ||
| 61 | struct hisi_thermal_sensor { | 68 | struct hisi_thermal_sensor { |
| 69 | struct hisi_thermal_data *data; | ||
| 62 | struct thermal_zone_device *tzd; | 70 | struct thermal_zone_device *tzd; |
| 71 | const char *irq_name; | ||
| 63 | uint32_t id; | 72 | uint32_t id; |
| 64 | uint32_t thres_temp; | 73 | uint32_t thres_temp; |
| 65 | }; | 74 | }; |
| 66 | 75 | ||
| 76 | struct hisi_thermal_ops { | ||
| 77 | int (*get_temp)(struct hisi_thermal_sensor *sensor); | ||
| 78 | int (*enable_sensor)(struct hisi_thermal_sensor *sensor); | ||
| 79 | int (*disable_sensor)(struct hisi_thermal_sensor *sensor); | ||
| 80 | int (*irq_handler)(struct hisi_thermal_sensor *sensor); | ||
| 81 | int (*probe)(struct hisi_thermal_data *data); | ||
| 82 | }; | ||
| 83 | |||
| 67 | struct hisi_thermal_data { | 84 | struct hisi_thermal_data { |
| 68 | int (*get_temp)(struct hisi_thermal_data *data); | 85 | const struct hisi_thermal_ops *ops; |
| 69 | int (*enable_sensor)(struct hisi_thermal_data *data); | 86 | struct hisi_thermal_sensor *sensor; |
| 70 | int (*disable_sensor)(struct hisi_thermal_data *data); | ||
| 71 | int (*irq_handler)(struct hisi_thermal_data *data); | ||
| 72 | struct platform_device *pdev; | 87 | struct platform_device *pdev; |
| 73 | struct clk *clk; | 88 | struct clk *clk; |
| 74 | struct hisi_thermal_sensor sensor; | ||
| 75 | void __iomem *regs; | 89 | void __iomem *regs; |
| 76 | int irq; | 90 | int nr_sensors; |
| 77 | }; | 91 | }; |
| 78 | 92 | ||
| 79 | /* | 93 | /* |
| @@ -266,30 +280,40 @@ static inline void hi6220_thermal_hdak_set(void __iomem *addr, int value) | |||
| 266 | (value << 4), addr + HI6220_TEMP0_CFG); | 280 | (value << 4), addr + HI6220_TEMP0_CFG); |
| 267 | } | 281 | } |
| 268 | 282 | ||
| 269 | static int hi6220_thermal_irq_handler(struct hisi_thermal_data *data) | 283 | static int hi6220_thermal_irq_handler(struct hisi_thermal_sensor *sensor) |
| 270 | { | 284 | { |
| 285 | struct hisi_thermal_data *data = sensor->data; | ||
| 286 | |||
| 271 | hi6220_thermal_alarm_clear(data->regs, 1); | 287 | hi6220_thermal_alarm_clear(data->regs, 1); |
| 272 | return 0; | 288 | return 0; |
| 273 | } | 289 | } |
| 274 | 290 | ||
| 275 | static int hi3660_thermal_irq_handler(struct hisi_thermal_data *data) | 291 | static int hi3660_thermal_irq_handler(struct hisi_thermal_sensor *sensor) |
| 276 | { | 292 | { |
| 277 | hi3660_thermal_alarm_clear(data->regs, data->sensor.id, 1); | 293 | struct hisi_thermal_data *data = sensor->data; |
| 294 | |||
| 295 | hi3660_thermal_alarm_clear(data->regs, sensor->id, 1); | ||
| 278 | return 0; | 296 | return 0; |
| 279 | } | 297 | } |
| 280 | 298 | ||
| 281 | static int hi6220_thermal_get_temp(struct hisi_thermal_data *data) | 299 | static int hi6220_thermal_get_temp(struct hisi_thermal_sensor *sensor) |
| 282 | { | 300 | { |
| 301 | struct hisi_thermal_data *data = sensor->data; | ||
| 302 | |||
| 283 | return hi6220_thermal_get_temperature(data->regs); | 303 | return hi6220_thermal_get_temperature(data->regs); |
| 284 | } | 304 | } |
| 285 | 305 | ||
| 286 | static int hi3660_thermal_get_temp(struct hisi_thermal_data *data) | 306 | static int hi3660_thermal_get_temp(struct hisi_thermal_sensor *sensor) |
| 287 | { | 307 | { |
| 288 | return hi3660_thermal_get_temperature(data->regs, data->sensor.id); | 308 | struct hisi_thermal_data *data = sensor->data; |
| 309 | |||
| 310 | return hi3660_thermal_get_temperature(data->regs, sensor->id); | ||
| 289 | } | 311 | } |
| 290 | 312 | ||
| 291 | static int hi6220_thermal_disable_sensor(struct hisi_thermal_data *data) | 313 | static int hi6220_thermal_disable_sensor(struct hisi_thermal_sensor *sensor) |
| 292 | { | 314 | { |
| 315 | struct hisi_thermal_data *data = sensor->data; | ||
| 316 | |||
| 293 | /* disable sensor module */ | 317 | /* disable sensor module */ |
| 294 | hi6220_thermal_enable(data->regs, 0); | 318 | hi6220_thermal_enable(data->regs, 0); |
| 295 | hi6220_thermal_alarm_enable(data->regs, 0); | 319 | hi6220_thermal_alarm_enable(data->regs, 0); |
| @@ -300,16 +324,18 @@ static int hi6220_thermal_disable_sensor(struct hisi_thermal_data *data) | |||
| 300 | return 0; | 324 | return 0; |
| 301 | } | 325 | } |
| 302 | 326 | ||
| 303 | static int hi3660_thermal_disable_sensor(struct hisi_thermal_data *data) | 327 | static int hi3660_thermal_disable_sensor(struct hisi_thermal_sensor *sensor) |
| 304 | { | 328 | { |
| 329 | struct hisi_thermal_data *data = sensor->data; | ||
| 330 | |||
| 305 | /* disable sensor module */ | 331 | /* disable sensor module */ |
| 306 | hi3660_thermal_alarm_enable(data->regs, data->sensor.id, 0); | 332 | hi3660_thermal_alarm_enable(data->regs, sensor->id, 0); |
| 307 | return 0; | 333 | return 0; |
| 308 | } | 334 | } |
| 309 | 335 | ||
| 310 | static int hi6220_thermal_enable_sensor(struct hisi_thermal_data *data) | 336 | static int hi6220_thermal_enable_sensor(struct hisi_thermal_sensor *sensor) |
| 311 | { | 337 | { |
| 312 | struct hisi_thermal_sensor *sensor = &data->sensor; | 338 | struct hisi_thermal_data *data = sensor->data; |
| 313 | int ret; | 339 | int ret; |
| 314 | 340 | ||
| 315 | /* enable clock for tsensor */ | 341 | /* enable clock for tsensor */ |
| @@ -345,10 +371,10 @@ static int hi6220_thermal_enable_sensor(struct hisi_thermal_data *data) | |||
| 345 | return 0; | 371 | return 0; |
| 346 | } | 372 | } |
| 347 | 373 | ||
| 348 | static int hi3660_thermal_enable_sensor(struct hisi_thermal_data *data) | 374 | static int hi3660_thermal_enable_sensor(struct hisi_thermal_sensor *sensor) |
| 349 | { | 375 | { |
| 350 | unsigned int value; | 376 | unsigned int value; |
| 351 | struct hisi_thermal_sensor *sensor = &data->sensor; | 377 | struct hisi_thermal_data *data = sensor->data; |
| 352 | 378 | ||
| 353 | /* disable interrupt */ | 379 | /* disable interrupt */ |
| 354 | hi3660_thermal_alarm_enable(data->regs, sensor->id, 0); | 380 | hi3660_thermal_alarm_enable(data->regs, sensor->id, 0); |
| @@ -371,21 +397,8 @@ static int hi6220_thermal_probe(struct hisi_thermal_data *data) | |||
| 371 | { | 397 | { |
| 372 | struct platform_device *pdev = data->pdev; | 398 | struct platform_device *pdev = data->pdev; |
| 373 | struct device *dev = &pdev->dev; | 399 | struct device *dev = &pdev->dev; |
| 374 | struct resource *res; | ||
| 375 | int ret; | 400 | int ret; |
| 376 | 401 | ||
| 377 | data->get_temp = hi6220_thermal_get_temp; | ||
| 378 | data->enable_sensor = hi6220_thermal_enable_sensor; | ||
| 379 | data->disable_sensor = hi6220_thermal_disable_sensor; | ||
| 380 | data->irq_handler = hi6220_thermal_irq_handler; | ||
| 381 | |||
| 382 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 383 | data->regs = devm_ioremap_resource(dev, res); | ||
| 384 | if (IS_ERR(data->regs)) { | ||
| 385 | dev_err(dev, "failed to get io address\n"); | ||
| 386 | return PTR_ERR(data->regs); | ||
| 387 | } | ||
| 388 | |||
| 389 | data->clk = devm_clk_get(dev, "thermal_clk"); | 402 | data->clk = devm_clk_get(dev, "thermal_clk"); |
| 390 | if (IS_ERR(data->clk)) { | 403 | if (IS_ERR(data->clk)) { |
| 391 | ret = PTR_ERR(data->clk); | 404 | ret = PTR_ERR(data->clk); |
| @@ -394,11 +407,14 @@ static int hi6220_thermal_probe(struct hisi_thermal_data *data) | |||
| 394 | return ret; | 407 | return ret; |
| 395 | } | 408 | } |
| 396 | 409 | ||
| 397 | data->irq = platform_get_irq(pdev, 0); | 410 | data->sensor = devm_kzalloc(dev, sizeof(*data->sensor), GFP_KERNEL); |
| 398 | if (data->irq < 0) | 411 | if (!data->sensor) |
| 399 | return data->irq; | 412 | return -ENOMEM; |
| 400 | 413 | ||
| 401 | data->sensor.id = HI6220_DEFAULT_SENSOR; | 414 | data->sensor[0].id = HI6220_CLUSTER0_SENSOR; |
| 415 | data->sensor[0].irq_name = "tsensor_intr"; | ||
| 416 | data->sensor[0].data = data; | ||
| 417 | data->nr_sensors = 1; | ||
| 402 | 418 | ||
| 403 | return 0; | 419 | return 0; |
| 404 | } | 420 | } |
| @@ -407,38 +423,34 @@ static int hi3660_thermal_probe(struct hisi_thermal_data *data) | |||
| 407 | { | 423 | { |
| 408 | struct platform_device *pdev = data->pdev; | 424 | struct platform_device *pdev = data->pdev; |
| 409 | struct device *dev = &pdev->dev; | 425 | struct device *dev = &pdev->dev; |
| 410 | struct resource *res; | ||
| 411 | 426 | ||
| 412 | data->get_temp = hi3660_thermal_get_temp; | 427 | data->nr_sensors = 2; |
| 413 | data->enable_sensor = hi3660_thermal_enable_sensor; | ||
| 414 | data->disable_sensor = hi3660_thermal_disable_sensor; | ||
| 415 | data->irq_handler = hi3660_thermal_irq_handler; | ||
| 416 | 428 | ||
| 417 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 429 | data->sensor = devm_kzalloc(dev, sizeof(*data->sensor) * |
| 418 | data->regs = devm_ioremap_resource(dev, res); | 430 | data->nr_sensors, GFP_KERNEL); |
| 419 | if (IS_ERR(data->regs)) { | 431 | if (!data->sensor) |
| 420 | dev_err(dev, "failed to get io address\n"); | 432 | return -ENOMEM; |
| 421 | return PTR_ERR(data->regs); | ||
| 422 | } | ||
| 423 | 433 | ||
| 424 | data->irq = platform_get_irq(pdev, 0); | 434 | data->sensor[0].id = HI3660_BIG_SENSOR; |
| 425 | if (data->irq < 0) | 435 | data->sensor[0].irq_name = "tsensor_a73"; |
| 426 | return data->irq; | 436 | data->sensor[0].data = data; |
| 427 | 437 | ||
| 428 | data->sensor.id = HI3660_DEFAULT_SENSOR; | 438 | data->sensor[1].id = HI3660_LITTLE_SENSOR; |
| 439 | data->sensor[1].irq_name = "tsensor_a53"; | ||
| 440 | data->sensor[1].data = data; | ||
| 429 | 441 | ||
| 430 | return 0; | 442 | return 0; |
| 431 | } | 443 | } |
| 432 | 444 | ||
| 433 | static int hisi_thermal_get_temp(void *__data, int *temp) | 445 | static int hisi_thermal_get_temp(void *__data, int *temp) |
| 434 | { | 446 | { |
| 435 | struct hisi_thermal_data *data = __data; | 447 | struct hisi_thermal_sensor *sensor = __data; |
| 436 | struct hisi_thermal_sensor *sensor = &data->sensor; | 448 | struct hisi_thermal_data *data = sensor->data; |
| 437 | 449 | ||
| 438 | *temp = data->get_temp(data); | 450 | *temp = data->ops->get_temp(sensor); |
| 439 | 451 | ||
| 440 | dev_dbg(&data->pdev->dev, "id=%d, temp=%d, thres=%d\n", | 452 | dev_dbg(&data->pdev->dev, "tzd=%p, id=%d, temp=%d, thres=%d\n", |
| 441 | sensor->id, *temp, sensor->thres_temp); | 453 | sensor->tzd, sensor->id, *temp, sensor->thres_temp); |
| 442 | 454 | ||
| 443 | return 0; | 455 | return 0; |
| 444 | } | 456 | } |
| @@ -449,38 +461,39 @@ static const struct thermal_zone_of_device_ops hisi_of_thermal_ops = { | |||
| 449 | 461 | ||
| 450 | static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) | 462 | static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) |
| 451 | { | 463 | { |
| 452 | struct hisi_thermal_data *data = dev; | 464 | struct hisi_thermal_sensor *sensor = dev; |
| 453 | struct hisi_thermal_sensor *sensor = &data->sensor; | 465 | struct hisi_thermal_data *data = sensor->data; |
| 454 | int temp = 0; | 466 | int temp = 0; |
| 455 | 467 | ||
| 456 | data->irq_handler(data); | 468 | data->ops->irq_handler(sensor); |
| 457 | 469 | ||
| 458 | hisi_thermal_get_temp(data, &temp); | 470 | hisi_thermal_get_temp(sensor, &temp); |
| 459 | 471 | ||
| 460 | if (temp >= sensor->thres_temp) { | 472 | if (temp >= sensor->thres_temp) { |
| 461 | dev_crit(&data->pdev->dev, "THERMAL ALARM: %d > %d\n", | 473 | dev_crit(&data->pdev->dev, |
| 462 | temp, sensor->thres_temp); | 474 | "sensor <%d> THERMAL ALARM: %d > %d\n", |
| 475 | sensor->id, temp, sensor->thres_temp); | ||
| 463 | 476 | ||
| 464 | thermal_zone_device_update(data->sensor.tzd, | 477 | thermal_zone_device_update(sensor->tzd, |
| 465 | THERMAL_EVENT_UNSPECIFIED); | 478 | THERMAL_EVENT_UNSPECIFIED); |
| 466 | 479 | ||
| 467 | } else { | 480 | } else { |
| 468 | dev_crit(&data->pdev->dev, "THERMAL ALARM stopped: %d < %d\n", | 481 | dev_crit(&data->pdev->dev, |
| 469 | temp, sensor->thres_temp); | 482 | "sensor <%d> THERMAL ALARM stopped: %d < %d\n", |
| 483 | sensor->id, temp, sensor->thres_temp); | ||
| 470 | } | 484 | } |
| 471 | 485 | ||
| 472 | return IRQ_HANDLED; | 486 | return IRQ_HANDLED; |
| 473 | } | 487 | } |
| 474 | 488 | ||
| 475 | static int hisi_thermal_register_sensor(struct platform_device *pdev, | 489 | static int hisi_thermal_register_sensor(struct platform_device *pdev, |
| 476 | struct hisi_thermal_data *data, | ||
| 477 | struct hisi_thermal_sensor *sensor) | 490 | struct hisi_thermal_sensor *sensor) |
| 478 | { | 491 | { |
| 479 | int ret, i; | 492 | int ret, i; |
| 480 | const struct thermal_trip *trip; | 493 | const struct thermal_trip *trip; |
| 481 | 494 | ||
| 482 | sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, | 495 | sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, |
| 483 | sensor->id, data, | 496 | sensor->id, sensor, |
| 484 | &hisi_of_thermal_ops); | 497 | &hisi_of_thermal_ops); |
| 485 | if (IS_ERR(sensor->tzd)) { | 498 | if (IS_ERR(sensor->tzd)) { |
| 486 | ret = PTR_ERR(sensor->tzd); | 499 | ret = PTR_ERR(sensor->tzd); |
| @@ -502,14 +515,30 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev, | |||
| 502 | return 0; | 515 | return 0; |
| 503 | } | 516 | } |
| 504 | 517 | ||
| 518 | static const struct hisi_thermal_ops hi6220_ops = { | ||
| 519 | .get_temp = hi6220_thermal_get_temp, | ||
| 520 | .enable_sensor = hi6220_thermal_enable_sensor, | ||
| 521 | .disable_sensor = hi6220_thermal_disable_sensor, | ||
| 522 | .irq_handler = hi6220_thermal_irq_handler, | ||
| 523 | .probe = hi6220_thermal_probe, | ||
| 524 | }; | ||
| 525 | |||
| 526 | static const struct hisi_thermal_ops hi3660_ops = { | ||
| 527 | .get_temp = hi3660_thermal_get_temp, | ||
| 528 | .enable_sensor = hi3660_thermal_enable_sensor, | ||
| 529 | .disable_sensor = hi3660_thermal_disable_sensor, | ||
| 530 | .irq_handler = hi3660_thermal_irq_handler, | ||
| 531 | .probe = hi3660_thermal_probe, | ||
| 532 | }; | ||
| 533 | |||
| 505 | static const struct of_device_id of_hisi_thermal_match[] = { | 534 | static const struct of_device_id of_hisi_thermal_match[] = { |
| 506 | { | 535 | { |
| 507 | .compatible = "hisilicon,tsensor", | 536 | .compatible = "hisilicon,tsensor", |
| 508 | .data = hi6220_thermal_probe | 537 | .data = &hi6220_ops, |
| 509 | }, | 538 | }, |
| 510 | { | 539 | { |
| 511 | .compatible = "hisilicon,hi3660-tsensor", | 540 | .compatible = "hisilicon,hi3660-tsensor", |
| 512 | .data = hi3660_thermal_probe | 541 | .data = &hi3660_ops, |
| 513 | }, | 542 | }, |
| 514 | { /* end */ } | 543 | { /* end */ } |
| 515 | }; | 544 | }; |
| @@ -527,9 +556,9 @@ static void hisi_thermal_toggle_sensor(struct hisi_thermal_sensor *sensor, | |||
| 527 | static int hisi_thermal_probe(struct platform_device *pdev) | 556 | static int hisi_thermal_probe(struct platform_device *pdev) |
| 528 | { | 557 | { |
| 529 | struct hisi_thermal_data *data; | 558 | struct hisi_thermal_data *data; |
| 530 | int (*platform_probe)(struct hisi_thermal_data *); | ||
| 531 | struct device *dev = &pdev->dev; | 559 | struct device *dev = &pdev->dev; |
| 532 | int ret; | 560 | struct resource *res; |
| 561 | int i, ret; | ||
| 533 | 562 | ||
| 534 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | 563 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); |
| 535 | if (!data) | 564 | if (!data) |
| @@ -537,41 +566,50 @@ static int hisi_thermal_probe(struct platform_device *pdev) | |||
| 537 | 566 | ||
| 538 | data->pdev = pdev; | 567 | data->pdev = pdev; |
| 539 | platform_set_drvdata(pdev, data); | 568 | platform_set_drvdata(pdev, data); |
| 569 | data->ops = of_device_get_match_data(dev); | ||
| 540 | 570 | ||
| 541 | platform_probe = of_device_get_match_data(dev); | 571 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 542 | if (!platform_probe) { | 572 | data->regs = devm_ioremap_resource(dev, res); |
| 543 | dev_err(dev, "failed to get probe func\n"); | 573 | if (IS_ERR(data->regs)) { |
| 544 | return -EINVAL; | 574 | dev_err(dev, "failed to get io address\n"); |
| 575 | return PTR_ERR(data->regs); | ||
| 545 | } | 576 | } |
| 546 | 577 | ||
| 547 | ret = platform_probe(data); | 578 | ret = data->ops->probe(data); |
| 548 | if (ret) | 579 | if (ret) |
| 549 | return ret; | 580 | return ret; |
| 550 | 581 | ||
| 551 | ret = hisi_thermal_register_sensor(pdev, data, | 582 | for (i = 0; i < data->nr_sensors; i++) { |
| 552 | &data->sensor); | 583 | struct hisi_thermal_sensor *sensor = &data->sensor[i]; |
| 553 | if (ret) { | ||
| 554 | dev_err(dev, "failed to register thermal sensor: %d\n", ret); | ||
| 555 | return ret; | ||
| 556 | } | ||
| 557 | 584 | ||
| 558 | ret = data->enable_sensor(data); | 585 | ret = hisi_thermal_register_sensor(pdev, sensor); |
| 559 | if (ret) { | 586 | if (ret) { |
| 560 | dev_err(dev, "Failed to setup the sensor: %d\n", ret); | 587 | dev_err(dev, "failed to register thermal sensor: %d\n", |
| 561 | return ret; | 588 | ret); |
| 562 | } | 589 | return ret; |
| 590 | } | ||
| 591 | |||
| 592 | ret = platform_get_irq_byname(pdev, sensor->irq_name); | ||
| 593 | if (ret < 0) | ||
| 594 | return ret; | ||
| 563 | 595 | ||
| 564 | if (data->irq) { | 596 | ret = devm_request_threaded_irq(dev, ret, NULL, |
| 565 | ret = devm_request_threaded_irq(dev, data->irq, NULL, | 597 | hisi_thermal_alarm_irq_thread, |
| 566 | hisi_thermal_alarm_irq_thread, | 598 | IRQF_ONESHOT, sensor->irq_name, |
| 567 | IRQF_ONESHOT, "hisi_thermal", data); | 599 | sensor); |
| 568 | if (ret < 0) { | 600 | if (ret < 0) { |
| 569 | dev_err(dev, "failed to request alarm irq: %d\n", ret); | 601 | dev_err(dev, "Failed to request alarm irq: %d\n", ret); |
| 570 | return ret; | 602 | return ret; |
| 571 | } | 603 | } |
| 572 | } | ||
| 573 | 604 | ||
| 574 | hisi_thermal_toggle_sensor(&data->sensor, true); | 605 | ret = data->ops->enable_sensor(sensor); |
| 606 | if (ret) { | ||
| 607 | dev_err(dev, "Failed to setup the sensor: %d\n", ret); | ||
| 608 | return ret; | ||
| 609 | } | ||
| 610 | |||
| 611 | hisi_thermal_toggle_sensor(sensor, true); | ||
| 612 | } | ||
| 575 | 613 | ||
| 576 | return 0; | 614 | return 0; |
| 577 | } | 615 | } |
| @@ -579,11 +617,14 @@ static int hisi_thermal_probe(struct platform_device *pdev) | |||
| 579 | static int hisi_thermal_remove(struct platform_device *pdev) | 617 | static int hisi_thermal_remove(struct platform_device *pdev) |
| 580 | { | 618 | { |
| 581 | struct hisi_thermal_data *data = platform_get_drvdata(pdev); | 619 | struct hisi_thermal_data *data = platform_get_drvdata(pdev); |
| 582 | struct hisi_thermal_sensor *sensor = &data->sensor; | 620 | int i; |
| 583 | 621 | ||
| 584 | hisi_thermal_toggle_sensor(sensor, false); | 622 | for (i = 0; i < data->nr_sensors; i++) { |
| 623 | struct hisi_thermal_sensor *sensor = &data->sensor[i]; | ||
| 585 | 624 | ||
| 586 | data->disable_sensor(data); | 625 | hisi_thermal_toggle_sensor(sensor, false); |
| 626 | data->ops->disable_sensor(sensor); | ||
| 627 | } | ||
| 587 | 628 | ||
| 588 | return 0; | 629 | return 0; |
| 589 | } | 630 | } |
| @@ -592,8 +633,10 @@ static int hisi_thermal_remove(struct platform_device *pdev) | |||
| 592 | static int hisi_thermal_suspend(struct device *dev) | 633 | static int hisi_thermal_suspend(struct device *dev) |
| 593 | { | 634 | { |
| 594 | struct hisi_thermal_data *data = dev_get_drvdata(dev); | 635 | struct hisi_thermal_data *data = dev_get_drvdata(dev); |
| 636 | int i; | ||
| 595 | 637 | ||
| 596 | data->disable_sensor(data); | 638 | for (i = 0; i < data->nr_sensors; i++) |
| 639 | data->ops->disable_sensor(&data->sensor[i]); | ||
| 597 | 640 | ||
| 598 | return 0; | 641 | return 0; |
| 599 | } | 642 | } |
| @@ -601,8 +644,12 @@ static int hisi_thermal_suspend(struct device *dev) | |||
| 601 | static int hisi_thermal_resume(struct device *dev) | 644 | static int hisi_thermal_resume(struct device *dev) |
| 602 | { | 645 | { |
| 603 | struct hisi_thermal_data *data = dev_get_drvdata(dev); | 646 | struct hisi_thermal_data *data = dev_get_drvdata(dev); |
| 647 | int i, ret = 0; | ||
| 648 | |||
| 649 | for (i = 0; i < data->nr_sensors; i++) | ||
| 650 | ret |= data->ops->enable_sensor(&data->sensor[i]); | ||
| 604 | 651 | ||
| 605 | return data->enable_sensor(data); | 652 | return ret; |
| 606 | } | 653 | } |
| 607 | #endif | 654 | #endif |
| 608 | 655 | ||
diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index aa452acb60b6..15661549eb67 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c | |||
| @@ -725,7 +725,7 @@ static int imx_thermal_probe(struct platform_device *pdev) | |||
| 725 | } else { | 725 | } else { |
| 726 | ret = imx_init_from_tempmon_data(pdev); | 726 | ret = imx_init_from_tempmon_data(pdev); |
| 727 | if (ret) { | 727 | if (ret) { |
| 728 | dev_err(&pdev->dev, "failed to init from from fsl,tempmon-data\n"); | 728 | dev_err(&pdev->dev, "failed to init from fsl,tempmon-data\n"); |
| 729 | return ret; | 729 | return ret; |
| 730 | } | 730 | } |
| 731 | } | 731 | } |
| @@ -762,9 +762,7 @@ static int imx_thermal_probe(struct platform_device *pdev) | |||
| 762 | if (ret != -EPROBE_DEFER) | 762 | if (ret != -EPROBE_DEFER) |
| 763 | dev_err(&pdev->dev, | 763 | dev_err(&pdev->dev, |
| 764 | "failed to get thermal clk: %d\n", ret); | 764 | "failed to get thermal clk: %d\n", ret); |
| 765 | cpufreq_cooling_unregister(data->cdev); | 765 | goto cpufreq_put; |
| 766 | cpufreq_cpu_put(data->policy); | ||
| 767 | return ret; | ||
| 768 | } | 766 | } |
| 769 | 767 | ||
| 770 | /* | 768 | /* |
| @@ -777,9 +775,7 @@ static int imx_thermal_probe(struct platform_device *pdev) | |||
| 777 | ret = clk_prepare_enable(data->thermal_clk); | 775 | ret = clk_prepare_enable(data->thermal_clk); |
| 778 | if (ret) { | 776 | if (ret) { |
| 779 | dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret); | 777 | dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret); |
| 780 | cpufreq_cooling_unregister(data->cdev); | 778 | goto cpufreq_put; |
| 781 | cpufreq_cpu_put(data->policy); | ||
| 782 | return ret; | ||
| 783 | } | 779 | } |
| 784 | 780 | ||
| 785 | data->tz = thermal_zone_device_register("imx_thermal_zone", | 781 | data->tz = thermal_zone_device_register("imx_thermal_zone", |
| @@ -792,10 +788,7 @@ static int imx_thermal_probe(struct platform_device *pdev) | |||
| 792 | ret = PTR_ERR(data->tz); | 788 | ret = PTR_ERR(data->tz); |
| 793 | dev_err(&pdev->dev, | 789 | dev_err(&pdev->dev, |
| 794 | "failed to register thermal zone device %d\n", ret); | 790 | "failed to register thermal zone device %d\n", ret); |
| 795 | clk_disable_unprepare(data->thermal_clk); | 791 | goto clk_disable; |
| 796 | cpufreq_cooling_unregister(data->cdev); | ||
| 797 | cpufreq_cpu_put(data->policy); | ||
| 798 | return ret; | ||
| 799 | } | 792 | } |
| 800 | 793 | ||
| 801 | dev_info(&pdev->dev, "%s CPU temperature grade - max:%dC" | 794 | dev_info(&pdev->dev, "%s CPU temperature grade - max:%dC" |
| @@ -827,14 +820,20 @@ static int imx_thermal_probe(struct platform_device *pdev) | |||
| 827 | 0, "imx_thermal", data); | 820 | 0, "imx_thermal", data); |
| 828 | if (ret < 0) { | 821 | if (ret < 0) { |
| 829 | dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret); | 822 | dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret); |
| 830 | clk_disable_unprepare(data->thermal_clk); | 823 | goto thermal_zone_unregister; |
| 831 | thermal_zone_device_unregister(data->tz); | ||
| 832 | cpufreq_cooling_unregister(data->cdev); | ||
| 833 | cpufreq_cpu_put(data->policy); | ||
| 834 | return ret; | ||
| 835 | } | 824 | } |
| 836 | 825 | ||
| 837 | return 0; | 826 | return 0; |
| 827 | |||
| 828 | thermal_zone_unregister: | ||
| 829 | thermal_zone_device_unregister(data->tz); | ||
| 830 | clk_disable: | ||
| 831 | clk_disable_unprepare(data->thermal_clk); | ||
| 832 | cpufreq_put: | ||
| 833 | cpufreq_cooling_unregister(data->cdev); | ||
| 834 | cpufreq_cpu_put(data->policy); | ||
| 835 | |||
| 836 | return ret; | ||
| 838 | } | 837 | } |
| 839 | 838 | ||
| 840 | static int imx_thermal_remove(struct platform_device *pdev) | 839 | static int imx_thermal_remove(struct platform_device *pdev) |
diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index 4f2816559205..4bfdb4a1e47d 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c | |||
| @@ -19,23 +19,34 @@ | |||
| 19 | /*** Private data structures to represent thermal device tree data ***/ | 19 | /*** Private data structures to represent thermal device tree data ***/ |
| 20 | 20 | ||
| 21 | /** | 21 | /** |
| 22 | * struct __thermal_bind_param - a match between trip and cooling device | 22 | * struct __thermal_cooling_bind_param - a cooling device for a trip point |
| 23 | * @cooling_device: a pointer to identify the referred cooling device | 23 | * @cooling_device: a pointer to identify the referred cooling device |
| 24 | * @trip_id: the trip point index | ||
| 25 | * @usage: the percentage (from 0 to 100) of cooling contribution | ||
| 26 | * @min: minimum cooling state used at this trip point | 24 | * @min: minimum cooling state used at this trip point |
| 27 | * @max: maximum cooling state used at this trip point | 25 | * @max: maximum cooling state used at this trip point |
| 28 | */ | 26 | */ |
| 29 | 27 | ||
| 30 | struct __thermal_bind_params { | 28 | struct __thermal_cooling_bind_param { |
| 31 | struct device_node *cooling_device; | 29 | struct device_node *cooling_device; |
| 32 | unsigned int trip_id; | ||
| 33 | unsigned int usage; | ||
| 34 | unsigned long min; | 30 | unsigned long min; |
| 35 | unsigned long max; | 31 | unsigned long max; |
| 36 | }; | 32 | }; |
| 37 | 33 | ||
| 38 | /** | 34 | /** |
| 35 | * struct __thermal_bind_param - a match between trip and cooling device | ||
| 36 | * @tcbp: a pointer to an array of cooling devices | ||
| 37 | * @count: number of elements in array | ||
| 38 | * @trip_id: the trip point index | ||
| 39 | * @usage: the percentage (from 0 to 100) of cooling contribution | ||
| 40 | */ | ||
| 41 | |||
| 42 | struct __thermal_bind_params { | ||
| 43 | struct __thermal_cooling_bind_param *tcbp; | ||
| 44 | unsigned int count; | ||
| 45 | unsigned int trip_id; | ||
| 46 | unsigned int usage; | ||
| 47 | }; | ||
| 48 | |||
| 49 | /** | ||
| 39 | * struct __thermal_zone - internal representation of a thermal zone | 50 | * struct __thermal_zone - internal representation of a thermal zone |
| 40 | * @mode: current thermal zone device mode (enabled/disabled) | 51 | * @mode: current thermal zone device mode (enabled/disabled) |
| 41 | * @passive_delay: polling interval while passive cooling is activated | 52 | * @passive_delay: polling interval while passive cooling is activated |
| @@ -192,25 +203,31 @@ static int of_thermal_bind(struct thermal_zone_device *thermal, | |||
| 192 | struct thermal_cooling_device *cdev) | 203 | struct thermal_cooling_device *cdev) |
| 193 | { | 204 | { |
| 194 | struct __thermal_zone *data = thermal->devdata; | 205 | struct __thermal_zone *data = thermal->devdata; |
| 195 | int i; | 206 | struct __thermal_bind_params *tbp; |
| 207 | struct __thermal_cooling_bind_param *tcbp; | ||
| 208 | int i, j; | ||
| 196 | 209 | ||
| 197 | if (!data || IS_ERR(data)) | 210 | if (!data || IS_ERR(data)) |
| 198 | return -ENODEV; | 211 | return -ENODEV; |
| 199 | 212 | ||
| 200 | /* find where to bind */ | 213 | /* find where to bind */ |
| 201 | for (i = 0; i < data->num_tbps; i++) { | 214 | for (i = 0; i < data->num_tbps; i++) { |
| 202 | struct __thermal_bind_params *tbp = data->tbps + i; | 215 | tbp = data->tbps + i; |
| 203 | 216 | ||
| 204 | if (tbp->cooling_device == cdev->np) { | 217 | for (j = 0; j < tbp->count; j++) { |
| 205 | int ret; | 218 | tcbp = tbp->tcbp + j; |
| 206 | 219 | ||
| 207 | ret = thermal_zone_bind_cooling_device(thermal, | 220 | if (tcbp->cooling_device == cdev->np) { |
| 221 | int ret; | ||
| 222 | |||
| 223 | ret = thermal_zone_bind_cooling_device(thermal, | ||
| 208 | tbp->trip_id, cdev, | 224 | tbp->trip_id, cdev, |
| 209 | tbp->max, | 225 | tcbp->max, |
| 210 | tbp->min, | 226 | tcbp->min, |
| 211 | tbp->usage); | 227 | tbp->usage); |
| 212 | if (ret) | 228 | if (ret) |
| 213 | return ret; | 229 | return ret; |
| 230 | } | ||
| 214 | } | 231 | } |
| 215 | } | 232 | } |
| 216 | 233 | ||
| @@ -221,22 +238,28 @@ static int of_thermal_unbind(struct thermal_zone_device *thermal, | |||
| 221 | struct thermal_cooling_device *cdev) | 238 | struct thermal_cooling_device *cdev) |
| 222 | { | 239 | { |
| 223 | struct __thermal_zone *data = thermal->devdata; | 240 | struct __thermal_zone *data = thermal->devdata; |
| 224 | int i; | 241 | struct __thermal_bind_params *tbp; |
| 242 | struct __thermal_cooling_bind_param *tcbp; | ||
| 243 | int i, j; | ||
| 225 | 244 | ||
| 226 | if (!data || IS_ERR(data)) | 245 | if (!data || IS_ERR(data)) |
| 227 | return -ENODEV; | 246 | return -ENODEV; |
| 228 | 247 | ||
| 229 | /* find where to unbind */ | 248 | /* find where to unbind */ |
| 230 | for (i = 0; i < data->num_tbps; i++) { | 249 | for (i = 0; i < data->num_tbps; i++) { |
| 231 | struct __thermal_bind_params *tbp = data->tbps + i; | 250 | tbp = data->tbps + i; |
| 251 | |||
| 252 | for (j = 0; j < tbp->count; j++) { | ||
| 253 | tcbp = tbp->tcbp + j; | ||
| 232 | 254 | ||
| 233 | if (tbp->cooling_device == cdev->np) { | 255 | if (tcbp->cooling_device == cdev->np) { |
| 234 | int ret; | 256 | int ret; |
| 235 | 257 | ||
| 236 | ret = thermal_zone_unbind_cooling_device(thermal, | 258 | ret = thermal_zone_unbind_cooling_device(thermal, |
| 237 | tbp->trip_id, cdev); | 259 | tbp->trip_id, cdev); |
| 238 | if (ret) | 260 | if (ret) |
| 239 | return ret; | 261 | return ret; |
| 262 | } | ||
| 240 | } | 263 | } |
| 241 | } | 264 | } |
| 242 | 265 | ||
| @@ -486,8 +509,8 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data, | |||
| 486 | if (sensor_specs.args_count >= 1) { | 509 | if (sensor_specs.args_count >= 1) { |
| 487 | id = sensor_specs.args[0]; | 510 | id = sensor_specs.args[0]; |
| 488 | WARN(sensor_specs.args_count > 1, | 511 | WARN(sensor_specs.args_count > 1, |
| 489 | "%s: too many cells in sensor specifier %d\n", | 512 | "%pOFn: too many cells in sensor specifier %d\n", |
| 490 | sensor_specs.np->name, sensor_specs.args_count); | 513 | sensor_specs.np, sensor_specs.args_count); |
| 491 | } else { | 514 | } else { |
| 492 | id = 0; | 515 | id = 0; |
| 493 | } | 516 | } |
| @@ -655,8 +678,9 @@ static int thermal_of_populate_bind_params(struct device_node *np, | |||
| 655 | int ntrips) | 678 | int ntrips) |
| 656 | { | 679 | { |
| 657 | struct of_phandle_args cooling_spec; | 680 | struct of_phandle_args cooling_spec; |
| 681 | struct __thermal_cooling_bind_param *__tcbp; | ||
| 658 | struct device_node *trip; | 682 | struct device_node *trip; |
| 659 | int ret, i; | 683 | int ret, i, count; |
| 660 | u32 prop; | 684 | u32 prop; |
| 661 | 685 | ||
| 662 | /* Default weight. Usage is optional */ | 686 | /* Default weight. Usage is optional */ |
| @@ -683,20 +707,44 @@ static int thermal_of_populate_bind_params(struct device_node *np, | |||
| 683 | goto end; | 707 | goto end; |
| 684 | } | 708 | } |
| 685 | 709 | ||
| 686 | ret = of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells", | 710 | count = of_count_phandle_with_args(np, "cooling-device", |
| 687 | 0, &cooling_spec); | 711 | "#cooling-cells"); |
| 688 | if (ret < 0) { | 712 | if (!count) { |
| 689 | pr_err("missing cooling_device property\n"); | 713 | pr_err("Add a cooling_device property with at least one device\n"); |
| 690 | goto end; | 714 | goto end; |
| 691 | } | 715 | } |
| 692 | __tbp->cooling_device = cooling_spec.np; | 716 | |
| 693 | if (cooling_spec.args_count >= 2) { /* at least min and max */ | 717 | __tcbp = kcalloc(count, sizeof(*__tcbp), GFP_KERNEL); |
| 694 | __tbp->min = cooling_spec.args[0]; | 718 | if (!__tcbp) |
| 695 | __tbp->max = cooling_spec.args[1]; | 719 | goto end; |
| 696 | } else { | 720 | |
| 697 | pr_err("wrong reference to cooling device, missing limits\n"); | 721 | for (i = 0; i < count; i++) { |
| 722 | ret = of_parse_phandle_with_args(np, "cooling-device", | ||
| 723 | "#cooling-cells", i, &cooling_spec); | ||
| 724 | if (ret < 0) { | ||
| 725 | pr_err("Invalid cooling-device entry\n"); | ||
| 726 | goto free_tcbp; | ||
| 727 | } | ||
| 728 | |||
| 729 | __tcbp[i].cooling_device = cooling_spec.np; | ||
| 730 | |||
| 731 | if (cooling_spec.args_count >= 2) { /* at least min and max */ | ||
| 732 | __tcbp[i].min = cooling_spec.args[0]; | ||
| 733 | __tcbp[i].max = cooling_spec.args[1]; | ||
| 734 | } else { | ||
| 735 | pr_err("wrong reference to cooling device, missing limits\n"); | ||
| 736 | } | ||
| 698 | } | 737 | } |
| 699 | 738 | ||
| 739 | __tbp->tcbp = __tcbp; | ||
| 740 | __tbp->count = count; | ||
| 741 | |||
| 742 | goto end; | ||
| 743 | |||
| 744 | free_tcbp: | ||
| 745 | for (i = i - 1; i >= 0; i--) | ||
| 746 | of_node_put(__tcbp[i].cooling_device); | ||
| 747 | kfree(__tcbp); | ||
| 700 | end: | 748 | end: |
| 701 | of_node_put(trip); | 749 | of_node_put(trip); |
| 702 | 750 | ||
| @@ -903,8 +951,16 @@ finish: | |||
| 903 | return tz; | 951 | return tz; |
| 904 | 952 | ||
| 905 | free_tbps: | 953 | free_tbps: |
| 906 | for (i = i - 1; i >= 0; i--) | 954 | for (i = i - 1; i >= 0; i--) { |
| 907 | of_node_put(tz->tbps[i].cooling_device); | 955 | struct __thermal_bind_params *tbp = tz->tbps + i; |
| 956 | int j; | ||
| 957 | |||
| 958 | for (j = 0; j < tbp->count; j++) | ||
| 959 | of_node_put(tbp->tcbp[j].cooling_device); | ||
| 960 | |||
| 961 | kfree(tbp->tcbp); | ||
| 962 | } | ||
| 963 | |||
| 908 | kfree(tz->tbps); | 964 | kfree(tz->tbps); |
| 909 | free_trips: | 965 | free_trips: |
| 910 | for (i = 0; i < tz->ntrips; i++) | 966 | for (i = 0; i < tz->ntrips; i++) |
| @@ -920,10 +976,18 @@ free_tz: | |||
| 920 | 976 | ||
| 921 | static inline void of_thermal_free_zone(struct __thermal_zone *tz) | 977 | static inline void of_thermal_free_zone(struct __thermal_zone *tz) |
| 922 | { | 978 | { |
| 923 | int i; | 979 | struct __thermal_bind_params *tbp; |
| 980 | int i, j; | ||
| 981 | |||
| 982 | for (i = 0; i < tz->num_tbps; i++) { | ||
| 983 | tbp = tz->tbps + i; | ||
| 984 | |||
| 985 | for (j = 0; j < tbp->count; j++) | ||
| 986 | of_node_put(tbp->tcbp[j].cooling_device); | ||
| 987 | |||
| 988 | kfree(tbp->tcbp); | ||
| 989 | } | ||
| 924 | 990 | ||
| 925 | for (i = 0; i < tz->num_tbps; i++) | ||
| 926 | of_node_put(tz->tbps[i].cooling_device); | ||
| 927 | kfree(tz->tbps); | 991 | kfree(tz->tbps); |
| 928 | for (i = 0; i < tz->ntrips; i++) | 992 | for (i = 0; i < tz->ntrips; i++) |
| 929 | of_node_put(tz->trips[i].np); | 993 | of_node_put(tz->trips[i].np); |
| @@ -963,8 +1027,8 @@ int __init of_parse_thermal_zones(void) | |||
| 963 | 1027 | ||
| 964 | tz = thermal_of_build_thermal_zone(child); | 1028 | tz = thermal_of_build_thermal_zone(child); |
| 965 | if (IS_ERR(tz)) { | 1029 | if (IS_ERR(tz)) { |
| 966 | pr_err("failed to build thermal zone %s: %ld\n", | 1030 | pr_err("failed to build thermal zone %pOFn: %ld\n", |
| 967 | child->name, | 1031 | child, |
| 968 | PTR_ERR(tz)); | 1032 | PTR_ERR(tz)); |
| 969 | continue; | 1033 | continue; |
| 970 | } | 1034 | } |
| @@ -998,7 +1062,7 @@ int __init of_parse_thermal_zones(void) | |||
| 998 | tz->passive_delay, | 1062 | tz->passive_delay, |
| 999 | tz->polling_delay); | 1063 | tz->polling_delay); |
| 1000 | if (IS_ERR(zone)) { | 1064 | if (IS_ERR(zone)) { |
| 1001 | pr_err("Failed to build %s zone %ld\n", child->name, | 1065 | pr_err("Failed to build %pOFn zone %ld\n", child, |
| 1002 | PTR_ERR(zone)); | 1066 | PTR_ERR(zone)); |
| 1003 | kfree(tzp); | 1067 | kfree(tzp); |
| 1004 | kfree(ops); | 1068 | kfree(ops); |
diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom-spmi-temp-alarm.c index ad4f3a8d6560..b2d5d5bf4a9b 100644 --- a/drivers/thermal/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom-spmi-temp-alarm.c | |||
| @@ -23,6 +23,8 @@ | |||
| 23 | #include <linux/regmap.h> | 23 | #include <linux/regmap.h> |
| 24 | #include <linux/thermal.h> | 24 | #include <linux/thermal.h> |
| 25 | 25 | ||
| 26 | #include "thermal_core.h" | ||
| 27 | |||
| 26 | #define QPNP_TM_REG_TYPE 0x04 | 28 | #define QPNP_TM_REG_TYPE 0x04 |
| 27 | #define QPNP_TM_REG_SUBTYPE 0x05 | 29 | #define QPNP_TM_REG_SUBTYPE 0x05 |
| 28 | #define QPNP_TM_REG_STATUS 0x08 | 30 | #define QPNP_TM_REG_STATUS 0x08 |
| @@ -37,9 +39,11 @@ | |||
| 37 | #define STATUS_GEN2_STATE_MASK GENMASK(6, 4) | 39 | #define STATUS_GEN2_STATE_MASK GENMASK(6, 4) |
| 38 | #define STATUS_GEN2_STATE_SHIFT 4 | 40 | #define STATUS_GEN2_STATE_SHIFT 4 |
| 39 | 41 | ||
| 40 | #define SHUTDOWN_CTRL1_OVERRIDE_MASK GENMASK(7, 6) | 42 | #define SHUTDOWN_CTRL1_OVERRIDE_S2 BIT(6) |
| 41 | #define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0) | 43 | #define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0) |
| 42 | 44 | ||
| 45 | #define SHUTDOWN_CTRL1_RATE_25HZ BIT(3) | ||
| 46 | |||
| 43 | #define ALARM_CTRL_FORCE_ENABLE BIT(7) | 47 | #define ALARM_CTRL_FORCE_ENABLE BIT(7) |
| 44 | 48 | ||
| 45 | /* | 49 | /* |
| @@ -56,12 +60,19 @@ | |||
| 56 | #define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */ | 60 | #define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */ |
| 57 | 61 | ||
| 58 | #define THRESH_MIN 0 | 62 | #define THRESH_MIN 0 |
| 63 | #define THRESH_MAX 3 | ||
| 64 | |||
| 65 | /* Stage 2 Threshold Min: 125 C */ | ||
| 66 | #define STAGE2_THRESHOLD_MIN 125000 | ||
| 67 | /* Stage 2 Threshold Max: 140 C */ | ||
| 68 | #define STAGE2_THRESHOLD_MAX 140000 | ||
| 59 | 69 | ||
| 60 | /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */ | 70 | /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */ |
| 61 | #define DEFAULT_TEMP 37000 | 71 | #define DEFAULT_TEMP 37000 |
| 62 | 72 | ||
| 63 | struct qpnp_tm_chip { | 73 | struct qpnp_tm_chip { |
| 64 | struct regmap *map; | 74 | struct regmap *map; |
| 75 | struct device *dev; | ||
| 65 | struct thermal_zone_device *tz_dev; | 76 | struct thermal_zone_device *tz_dev; |
| 66 | unsigned int subtype; | 77 | unsigned int subtype; |
| 67 | long temp; | 78 | long temp; |
| @@ -69,6 +80,10 @@ struct qpnp_tm_chip { | |||
| 69 | unsigned int stage; | 80 | unsigned int stage; |
| 70 | unsigned int prev_stage; | 81 | unsigned int prev_stage; |
| 71 | unsigned int base; | 82 | unsigned int base; |
| 83 | /* protects .thresh, .stage and chip registers */ | ||
| 84 | struct mutex lock; | ||
| 85 | bool initialized; | ||
| 86 | |||
| 72 | struct iio_channel *adc; | 87 | struct iio_channel *adc; |
| 73 | }; | 88 | }; |
| 74 | 89 | ||
| @@ -125,6 +140,8 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip) | |||
| 125 | unsigned int stage, stage_new, stage_old; | 140 | unsigned int stage, stage_new, stage_old; |
| 126 | int ret; | 141 | int ret; |
| 127 | 142 | ||
| 143 | WARN_ON(!mutex_is_locked(&chip->lock)); | ||
| 144 | |||
| 128 | ret = qpnp_tm_get_temp_stage(chip); | 145 | ret = qpnp_tm_get_temp_stage(chip); |
| 129 | if (ret < 0) | 146 | if (ret < 0) |
| 130 | return ret; | 147 | return ret; |
| @@ -163,8 +180,15 @@ static int qpnp_tm_get_temp(void *data, int *temp) | |||
| 163 | if (!temp) | 180 | if (!temp) |
| 164 | return -EINVAL; | 181 | return -EINVAL; |
| 165 | 182 | ||
| 183 | if (!chip->initialized) { | ||
| 184 | *temp = DEFAULT_TEMP; | ||
| 185 | return 0; | ||
| 186 | } | ||
| 187 | |||
| 166 | if (!chip->adc) { | 188 | if (!chip->adc) { |
| 189 | mutex_lock(&chip->lock); | ||
| 167 | ret = qpnp_tm_update_temp_no_adc(chip); | 190 | ret = qpnp_tm_update_temp_no_adc(chip); |
| 191 | mutex_unlock(&chip->lock); | ||
| 168 | if (ret < 0) | 192 | if (ret < 0) |
| 169 | return ret; | 193 | return ret; |
| 170 | } else { | 194 | } else { |
| @@ -180,8 +204,72 @@ static int qpnp_tm_get_temp(void *data, int *temp) | |||
| 180 | return 0; | 204 | return 0; |
| 181 | } | 205 | } |
| 182 | 206 | ||
| 207 | static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip, | ||
| 208 | int temp) | ||
| 209 | { | ||
| 210 | u8 reg; | ||
| 211 | bool disable_s2_shutdown = false; | ||
| 212 | |||
| 213 | WARN_ON(!mutex_is_locked(&chip->lock)); | ||
| 214 | |||
| 215 | /* | ||
| 216 | * Default: S2 and S3 shutdown enabled, thresholds at | ||
| 217 | * 105C/125C/145C, monitoring at 25Hz | ||
| 218 | */ | ||
| 219 | reg = SHUTDOWN_CTRL1_RATE_25HZ; | ||
| 220 | |||
| 221 | if (temp == THERMAL_TEMP_INVALID || | ||
| 222 | temp < STAGE2_THRESHOLD_MIN) { | ||
| 223 | chip->thresh = THRESH_MIN; | ||
| 224 | goto skip; | ||
| 225 | } | ||
| 226 | |||
| 227 | if (temp <= STAGE2_THRESHOLD_MAX) { | ||
| 228 | chip->thresh = THRESH_MAX - | ||
| 229 | ((STAGE2_THRESHOLD_MAX - temp) / | ||
| 230 | TEMP_THRESH_STEP); | ||
| 231 | disable_s2_shutdown = true; | ||
| 232 | } else { | ||
| 233 | chip->thresh = THRESH_MAX; | ||
| 234 | |||
| 235 | if (chip->adc) | ||
| 236 | disable_s2_shutdown = true; | ||
| 237 | else | ||
| 238 | dev_warn(chip->dev, | ||
| 239 | "No ADC is configured and critical temperature is above the maximum stage 2 threshold of 140 C! Configuring stage 2 shutdown at 140 C.\n"); | ||
| 240 | } | ||
| 241 | |||
| 242 | skip: | ||
| 243 | reg |= chip->thresh; | ||
| 244 | if (disable_s2_shutdown) | ||
| 245 | reg |= SHUTDOWN_CTRL1_OVERRIDE_S2; | ||
| 246 | |||
| 247 | return qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg); | ||
| 248 | } | ||
| 249 | |||
| 250 | static int qpnp_tm_set_trip_temp(void *data, int trip, int temp) | ||
| 251 | { | ||
| 252 | struct qpnp_tm_chip *chip = data; | ||
| 253 | const struct thermal_trip *trip_points; | ||
| 254 | int ret; | ||
| 255 | |||
| 256 | trip_points = of_thermal_get_trip_points(chip->tz_dev); | ||
| 257 | if (!trip_points) | ||
| 258 | return -EINVAL; | ||
| 259 | |||
| 260 | if (trip_points[trip].type != THERMAL_TRIP_CRITICAL) | ||
| 261 | return 0; | ||
| 262 | |||
| 263 | mutex_lock(&chip->lock); | ||
| 264 | ret = qpnp_tm_update_critical_trip_temp(chip, temp); | ||
| 265 | mutex_unlock(&chip->lock); | ||
| 266 | |||
| 267 | return ret; | ||
| 268 | } | ||
| 269 | |||
| 183 | static const struct thermal_zone_of_device_ops qpnp_tm_sensor_ops = { | 270 | static const struct thermal_zone_of_device_ops qpnp_tm_sensor_ops = { |
| 184 | .get_temp = qpnp_tm_get_temp, | 271 | .get_temp = qpnp_tm_get_temp, |
| 272 | .set_trip_temp = qpnp_tm_set_trip_temp, | ||
| 185 | }; | 273 | }; |
| 186 | 274 | ||
| 187 | static irqreturn_t qpnp_tm_isr(int irq, void *data) | 275 | static irqreturn_t qpnp_tm_isr(int irq, void *data) |
| @@ -193,6 +281,29 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data) | |||
| 193 | return IRQ_HANDLED; | 281 | return IRQ_HANDLED; |
| 194 | } | 282 | } |
| 195 | 283 | ||
| 284 | static int qpnp_tm_get_critical_trip_temp(struct qpnp_tm_chip *chip) | ||
| 285 | { | ||
| 286 | int ntrips; | ||
| 287 | const struct thermal_trip *trips; | ||
| 288 | int i; | ||
| 289 | |||
| 290 | ntrips = of_thermal_get_ntrips(chip->tz_dev); | ||
| 291 | if (ntrips <= 0) | ||
| 292 | return THERMAL_TEMP_INVALID; | ||
| 293 | |||
| 294 | trips = of_thermal_get_trip_points(chip->tz_dev); | ||
| 295 | if (!trips) | ||
| 296 | return THERMAL_TEMP_INVALID; | ||
| 297 | |||
| 298 | for (i = 0; i < ntrips; i++) { | ||
| 299 | if (of_thermal_is_trip_valid(chip->tz_dev, i) && | ||
| 300 | trips[i].type == THERMAL_TRIP_CRITICAL) | ||
| 301 | return trips[i].temperature; | ||
| 302 | } | ||
| 303 | |||
| 304 | return THERMAL_TEMP_INVALID; | ||
| 305 | } | ||
| 306 | |||
| 196 | /* | 307 | /* |
| 197 | * This function initializes the internal temp value based on only the | 308 | * This function initializes the internal temp value based on only the |
| 198 | * current thermal stage and threshold. Setup threshold control and | 309 | * current thermal stage and threshold. Setup threshold control and |
| @@ -203,17 +314,20 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip) | |||
| 203 | unsigned int stage; | 314 | unsigned int stage; |
| 204 | int ret; | 315 | int ret; |
| 205 | u8 reg = 0; | 316 | u8 reg = 0; |
| 317 | int crit_temp; | ||
| 318 | |||
| 319 | mutex_lock(&chip->lock); | ||
| 206 | 320 | ||
| 207 | ret = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, ®); | 321 | ret = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, ®); |
| 208 | if (ret < 0) | 322 | if (ret < 0) |
| 209 | return ret; | 323 | goto out; |
| 210 | 324 | ||
| 211 | chip->thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK; | 325 | chip->thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK; |
| 212 | chip->temp = DEFAULT_TEMP; | 326 | chip->temp = DEFAULT_TEMP; |
| 213 | 327 | ||
| 214 | ret = qpnp_tm_get_temp_stage(chip); | 328 | ret = qpnp_tm_get_temp_stage(chip); |
| 215 | if (ret < 0) | 329 | if (ret < 0) |
| 216 | return ret; | 330 | goto out; |
| 217 | chip->stage = ret; | 331 | chip->stage = ret; |
| 218 | 332 | ||
| 219 | stage = chip->subtype == QPNP_TM_SUBTYPE_GEN1 | 333 | stage = chip->subtype == QPNP_TM_SUBTYPE_GEN1 |
| @@ -224,21 +338,19 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip) | |||
| 224 | (stage - 1) * TEMP_STAGE_STEP + | 338 | (stage - 1) * TEMP_STAGE_STEP + |
| 225 | TEMP_THRESH_MIN; | 339 | TEMP_THRESH_MIN; |
| 226 | 340 | ||
| 227 | /* | 341 | crit_temp = qpnp_tm_get_critical_trip_temp(chip); |
| 228 | * Set threshold and disable software override of stage 2 and 3 | 342 | ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp); |
| 229 | * shutdowns. | ||
| 230 | */ | ||
| 231 | chip->thresh = THRESH_MIN; | ||
| 232 | reg &= ~(SHUTDOWN_CTRL1_OVERRIDE_MASK | SHUTDOWN_CTRL1_THRESHOLD_MASK); | ||
| 233 | reg |= chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK; | ||
| 234 | ret = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg); | ||
| 235 | if (ret < 0) | 343 | if (ret < 0) |
| 236 | return ret; | 344 | goto out; |
| 237 | 345 | ||
| 238 | /* Enable the thermal alarm PMIC module in always-on mode. */ | 346 | /* Enable the thermal alarm PMIC module in always-on mode. */ |
| 239 | reg = ALARM_CTRL_FORCE_ENABLE; | 347 | reg = ALARM_CTRL_FORCE_ENABLE; |
| 240 | ret = qpnp_tm_write(chip, QPNP_TM_REG_ALARM_CTRL, reg); | 348 | ret = qpnp_tm_write(chip, QPNP_TM_REG_ALARM_CTRL, reg); |
| 241 | 349 | ||
| 350 | chip->initialized = true; | ||
| 351 | |||
| 352 | out: | ||
| 353 | mutex_unlock(&chip->lock); | ||
| 242 | return ret; | 354 | return ret; |
| 243 | } | 355 | } |
| 244 | 356 | ||
| @@ -257,6 +369,9 @@ static int qpnp_tm_probe(struct platform_device *pdev) | |||
| 257 | return -ENOMEM; | 369 | return -ENOMEM; |
| 258 | 370 | ||
| 259 | dev_set_drvdata(&pdev->dev, chip); | 371 | dev_set_drvdata(&pdev->dev, chip); |
| 372 | chip->dev = &pdev->dev; | ||
| 373 | |||
| 374 | mutex_init(&chip->lock); | ||
| 260 | 375 | ||
| 261 | chip->map = dev_get_regmap(pdev->dev.parent, NULL); | 376 | chip->map = dev_get_regmap(pdev->dev.parent, NULL); |
| 262 | if (!chip->map) | 377 | if (!chip->map) |
| @@ -302,6 +417,18 @@ static int qpnp_tm_probe(struct platform_device *pdev) | |||
| 302 | 417 | ||
| 303 | chip->subtype = subtype; | 418 | chip->subtype = subtype; |
| 304 | 419 | ||
| 420 | /* | ||
| 421 | * Register the sensor before initializing the hardware to be able to | ||
| 422 | * read the trip points. get_temp() returns the default temperature | ||
| 423 | * before the hardware initialization is completed. | ||
| 424 | */ | ||
| 425 | chip->tz_dev = devm_thermal_zone_of_sensor_register( | ||
| 426 | &pdev->dev, 0, chip, &qpnp_tm_sensor_ops); | ||
| 427 | if (IS_ERR(chip->tz_dev)) { | ||
| 428 | dev_err(&pdev->dev, "failed to register sensor\n"); | ||
| 429 | return PTR_ERR(chip->tz_dev); | ||
| 430 | } | ||
| 431 | |||
| 305 | ret = qpnp_tm_init(chip); | 432 | ret = qpnp_tm_init(chip); |
| 306 | if (ret < 0) { | 433 | if (ret < 0) { |
| 307 | dev_err(&pdev->dev, "init failed\n"); | 434 | dev_err(&pdev->dev, "init failed\n"); |
| @@ -313,12 +440,7 @@ static int qpnp_tm_probe(struct platform_device *pdev) | |||
| 313 | if (ret < 0) | 440 | if (ret < 0) |
| 314 | return ret; | 441 | return ret; |
| 315 | 442 | ||
| 316 | chip->tz_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, chip, | 443 | thermal_zone_device_update(chip->tz_dev, THERMAL_EVENT_UNSPECIFIED); |
| 317 | &qpnp_tm_sensor_ops); | ||
| 318 | if (IS_ERR(chip->tz_dev)) { | ||
| 319 | dev_err(&pdev->dev, "failed to register sensor\n"); | ||
| 320 | return PTR_ERR(chip->tz_dev); | ||
| 321 | } | ||
| 322 | 444 | ||
| 323 | return 0; | 445 | return 0; |
| 324 | } | 446 | } |
diff --git a/drivers/thermal/qcom/tsens-8916.c b/drivers/thermal/qcom/tsens-8916.c index fdf561b8b81d..c6dd620ac029 100644 --- a/drivers/thermal/qcom/tsens-8916.c +++ b/drivers/thermal/qcom/tsens-8916.c | |||
| @@ -1,15 +1,6 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | 3 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. |
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 and | ||
| 6 | * only version 2 as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | */ | 4 | */ |
| 14 | 5 | ||
| 15 | #include <linux/platform_device.h> | 6 | #include <linux/platform_device.h> |
| @@ -109,5 +100,6 @@ static const struct tsens_ops ops_8916 = { | |||
| 109 | const struct tsens_data data_8916 = { | 100 | const struct tsens_data data_8916 = { |
| 110 | .num_sensors = 5, | 101 | .num_sensors = 5, |
| 111 | .ops = &ops_8916, | 102 | .ops = &ops_8916, |
| 103 | .reg_offsets = { [SROT_CTRL_OFFSET] = 0x0 }, | ||
| 112 | .hw_ids = (unsigned int []){0, 1, 2, 4, 5 }, | 104 | .hw_ids = (unsigned int []){0, 1, 2, 4, 5 }, |
| 113 | }; | 105 | }; |
diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c index 0451277d3a8f..0f0adb302a7b 100644 --- a/drivers/thermal/qcom/tsens-8960.c +++ b/drivers/thermal/qcom/tsens-8960.c | |||
| @@ -1,15 +1,6 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | 3 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. |
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 and | ||
| 6 | * only version 2 as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | */ | 4 | */ |
| 14 | 5 | ||
| 15 | #include <linux/platform_device.h> | 6 | #include <linux/platform_device.h> |
| @@ -69,7 +60,7 @@ static int suspend_8960(struct tsens_device *tmdev) | |||
| 69 | { | 60 | { |
| 70 | int ret; | 61 | int ret; |
| 71 | unsigned int mask; | 62 | unsigned int mask; |
| 72 | struct regmap *map = tmdev->map; | 63 | struct regmap *map = tmdev->tm_map; |
| 73 | 64 | ||
| 74 | ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold); | 65 | ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold); |
| 75 | if (ret) | 66 | if (ret) |
| @@ -94,7 +85,7 @@ static int suspend_8960(struct tsens_device *tmdev) | |||
| 94 | static int resume_8960(struct tsens_device *tmdev) | 85 | static int resume_8960(struct tsens_device *tmdev) |
| 95 | { | 86 | { |
| 96 | int ret; | 87 | int ret; |
| 97 | struct regmap *map = tmdev->map; | 88 | struct regmap *map = tmdev->tm_map; |
| 98 | 89 | ||
| 99 | ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); | 90 | ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); |
| 100 | if (ret) | 91 | if (ret) |
| @@ -126,12 +117,12 @@ static int enable_8960(struct tsens_device *tmdev, int id) | |||
| 126 | int ret; | 117 | int ret; |
| 127 | u32 reg, mask; | 118 | u32 reg, mask; |
| 128 | 119 | ||
| 129 | ret = regmap_read(tmdev->map, CNTL_ADDR, ®); | 120 | ret = regmap_read(tmdev->tm_map, CNTL_ADDR, ®); |
| 130 | if (ret) | 121 | if (ret) |
| 131 | return ret; | 122 | return ret; |
| 132 | 123 | ||
| 133 | mask = BIT(id + SENSOR0_SHIFT); | 124 | mask = BIT(id + SENSOR0_SHIFT); |
| 134 | ret = regmap_write(tmdev->map, CNTL_ADDR, reg | SW_RST); | 125 | ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg | SW_RST); |
| 135 | if (ret) | 126 | if (ret) |
| 136 | return ret; | 127 | return ret; |
| 137 | 128 | ||
| @@ -140,7 +131,7 @@ static int enable_8960(struct tsens_device *tmdev, int id) | |||
| 140 | else | 131 | else |
| 141 | reg |= mask | SLP_CLK_ENA_8660 | EN; | 132 | reg |= mask | SLP_CLK_ENA_8660 | EN; |
| 142 | 133 | ||
| 143 | ret = regmap_write(tmdev->map, CNTL_ADDR, reg); | 134 | ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg); |
| 144 | if (ret) | 135 | if (ret) |
| 145 | return ret; | 136 | return ret; |
| 146 | 137 | ||
| @@ -157,7 +148,7 @@ static void disable_8960(struct tsens_device *tmdev) | |||
| 157 | mask <<= SENSOR0_SHIFT; | 148 | mask <<= SENSOR0_SHIFT; |
| 158 | mask |= EN; | 149 | mask |= EN; |
| 159 | 150 | ||
| 160 | ret = regmap_read(tmdev->map, CNTL_ADDR, ®_cntl); | 151 | ret = regmap_read(tmdev->tm_map, CNTL_ADDR, ®_cntl); |
| 161 | if (ret) | 152 | if (ret) |
| 162 | return; | 153 | return; |
| 163 | 154 | ||
| @@ -168,7 +159,7 @@ static void disable_8960(struct tsens_device *tmdev) | |||
| 168 | else | 159 | else |
| 169 | reg_cntl &= ~SLP_CLK_ENA_8660; | 160 | reg_cntl &= ~SLP_CLK_ENA_8660; |
| 170 | 161 | ||
| 171 | regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); | 162 | regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl); |
| 172 | } | 163 | } |
| 173 | 164 | ||
| 174 | static int init_8960(struct tsens_device *tmdev) | 165 | static int init_8960(struct tsens_device *tmdev) |
| @@ -176,8 +167,8 @@ static int init_8960(struct tsens_device *tmdev) | |||
| 176 | int ret, i; | 167 | int ret, i; |
| 177 | u32 reg_cntl; | 168 | u32 reg_cntl; |
| 178 | 169 | ||
| 179 | tmdev->map = dev_get_regmap(tmdev->dev, NULL); | 170 | tmdev->tm_map = dev_get_regmap(tmdev->dev, NULL); |
| 180 | if (!tmdev->map) | 171 | if (!tmdev->tm_map) |
| 181 | return -ENODEV; | 172 | return -ENODEV; |
| 182 | 173 | ||
| 183 | /* | 174 | /* |
| @@ -193,14 +184,14 @@ static int init_8960(struct tsens_device *tmdev) | |||
| 193 | } | 184 | } |
| 194 | 185 | ||
| 195 | reg_cntl = SW_RST; | 186 | reg_cntl = SW_RST; |
| 196 | ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl); | 187 | ret = regmap_update_bits(tmdev->tm_map, CNTL_ADDR, SW_RST, reg_cntl); |
| 197 | if (ret) | 188 | if (ret) |
| 198 | return ret; | 189 | return ret; |
| 199 | 190 | ||
| 200 | if (tmdev->num_sensors > 1) { | 191 | if (tmdev->num_sensors > 1) { |
| 201 | reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); | 192 | reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); |
| 202 | reg_cntl &= ~SW_RST; | 193 | reg_cntl &= ~SW_RST; |
| 203 | ret = regmap_update_bits(tmdev->map, CONFIG_ADDR, | 194 | ret = regmap_update_bits(tmdev->tm_map, CONFIG_ADDR, |
| 204 | CONFIG_MASK, CONFIG); | 195 | CONFIG_MASK, CONFIG); |
| 205 | } else { | 196 | } else { |
| 206 | reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16); | 197 | reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16); |
| @@ -209,12 +200,12 @@ static int init_8960(struct tsens_device *tmdev) | |||
| 209 | } | 200 | } |
| 210 | 201 | ||
| 211 | reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT; | 202 | reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT; |
| 212 | ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); | 203 | ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl); |
| 213 | if (ret) | 204 | if (ret) |
| 214 | return ret; | 205 | return ret; |
| 215 | 206 | ||
| 216 | reg_cntl |= EN; | 207 | reg_cntl |= EN; |
| 217 | ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); | 208 | ret = regmap_write(tmdev->tm_map, CNTL_ADDR, reg_cntl); |
| 218 | if (ret) | 209 | if (ret) |
| 219 | return ret; | 210 | return ret; |
| 220 | 211 | ||
| @@ -261,12 +252,12 @@ static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp) | |||
| 261 | 252 | ||
| 262 | timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); | 253 | timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); |
| 263 | do { | 254 | do { |
| 264 | ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy); | 255 | ret = regmap_read(tmdev->tm_map, INT_STATUS_ADDR, &trdy); |
| 265 | if (ret) | 256 | if (ret) |
| 266 | return ret; | 257 | return ret; |
| 267 | if (!(trdy & TRDY_MASK)) | 258 | if (!(trdy & TRDY_MASK)) |
| 268 | continue; | 259 | continue; |
| 269 | ret = regmap_read(tmdev->map, s->status, &code); | 260 | ret = regmap_read(tmdev->tm_map, s->status, &code); |
| 270 | if (ret) | 261 | if (ret) |
| 271 | return ret; | 262 | return ret; |
| 272 | *temp = code_to_mdegC(code, s); | 263 | *temp = code_to_mdegC(code, s); |
diff --git a/drivers/thermal/qcom/tsens-8974.c b/drivers/thermal/qcom/tsens-8974.c index 9baf77e8cbe3..3d3fda3d731b 100644 --- a/drivers/thermal/qcom/tsens-8974.c +++ b/drivers/thermal/qcom/tsens-8974.c | |||
| @@ -1,15 +1,6 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | 3 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. |
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 and | ||
| 6 | * only version 2 as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | */ | 4 | */ |
| 14 | 5 | ||
| 15 | #include <linux/platform_device.h> | 6 | #include <linux/platform_device.h> |
| @@ -241,4 +232,5 @@ static const struct tsens_ops ops_8974 = { | |||
| 241 | const struct tsens_data data_8974 = { | 232 | const struct tsens_data data_8974 = { |
| 242 | .num_sensors = 11, | 233 | .num_sensors = 11, |
| 243 | .ops = &ops_8974, | 234 | .ops = &ops_8974, |
| 235 | .reg_offsets = { [SROT_CTRL_OFFSET] = 0x0 }, | ||
| 244 | }; | 236 | }; |
diff --git a/drivers/thermal/qcom/tsens-common.c b/drivers/thermal/qcom/tsens-common.c index 6207d8d92351..3be4be2e0465 100644 --- a/drivers/thermal/qcom/tsens-common.c +++ b/drivers/thermal/qcom/tsens-common.c | |||
| @@ -1,15 +1,6 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | 3 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. |
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 and | ||
| 6 | * only version 2 as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | */ | 4 | */ |
| 14 | 5 | ||
| 15 | #include <linux/err.h> | 6 | #include <linux/err.h> |
| @@ -21,7 +12,11 @@ | |||
| 21 | #include <linux/regmap.h> | 12 | #include <linux/regmap.h> |
| 22 | #include "tsens.h" | 13 | #include "tsens.h" |
| 23 | 14 | ||
| 24 | #define S0_ST_ADDR 0x1030 | 15 | /* SROT */ |
| 16 | #define TSENS_EN BIT(0) | ||
| 17 | |||
| 18 | /* TM */ | ||
| 19 | #define STATUS_OFFSET 0x30 | ||
| 25 | #define SN_ADDR_OFFSET 0x4 | 20 | #define SN_ADDR_OFFSET 0x4 |
| 26 | #define SN_ST_TEMP_MASK 0x3ff | 21 | #define SN_ST_TEMP_MASK 0x3ff |
| 27 | #define CAL_DEGC_PT1 30 | 22 | #define CAL_DEGC_PT1 30 |
| @@ -107,8 +102,8 @@ int get_temp_common(struct tsens_device *tmdev, int id, int *temp) | |||
| 107 | unsigned int status_reg; | 102 | unsigned int status_reg; |
| 108 | int last_temp = 0, ret; | 103 | int last_temp = 0, ret; |
| 109 | 104 | ||
| 110 | status_reg = S0_ST_ADDR + s->hw_id * SN_ADDR_OFFSET; | 105 | status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * SN_ADDR_OFFSET; |
| 111 | ret = regmap_read(tmdev->map, status_reg, &code); | 106 | ret = regmap_read(tmdev->tm_map, status_reg, &code); |
| 112 | if (ret) | 107 | if (ret) |
| 113 | return ret; | 108 | return ret; |
| 114 | last_temp = code & SN_ST_TEMP_MASK; | 109 | last_temp = code & SN_ST_TEMP_MASK; |
| @@ -126,29 +121,52 @@ static const struct regmap_config tsens_config = { | |||
| 126 | 121 | ||
| 127 | int __init init_common(struct tsens_device *tmdev) | 122 | int __init init_common(struct tsens_device *tmdev) |
| 128 | { | 123 | { |
| 129 | void __iomem *base; | 124 | void __iomem *tm_base, *srot_base; |
| 130 | struct resource *res; | 125 | struct resource *res; |
| 126 | u32 code; | ||
| 127 | int ret; | ||
| 131 | struct platform_device *op = of_find_device_by_node(tmdev->dev->of_node); | 128 | struct platform_device *op = of_find_device_by_node(tmdev->dev->of_node); |
| 129 | u16 ctrl_offset = tmdev->reg_offsets[SROT_CTRL_OFFSET]; | ||
| 132 | 130 | ||
| 133 | if (!op) | 131 | if (!op) |
| 134 | return -EINVAL; | 132 | return -EINVAL; |
| 135 | 133 | ||
| 136 | /* The driver only uses the TM register address space for now */ | ||
| 137 | if (op->num_resources > 1) { | 134 | if (op->num_resources > 1) { |
| 135 | /* DT with separate SROT and TM address space */ | ||
| 138 | tmdev->tm_offset = 0; | 136 | tmdev->tm_offset = 0; |
| 137 | res = platform_get_resource(op, IORESOURCE_MEM, 1); | ||
| 138 | srot_base = devm_ioremap_resource(&op->dev, res); | ||
| 139 | if (IS_ERR(srot_base)) | ||
| 140 | return PTR_ERR(srot_base); | ||
| 141 | |||
| 142 | tmdev->srot_map = devm_regmap_init_mmio(tmdev->dev, | ||
| 143 | srot_base, &tsens_config); | ||
| 144 | if (IS_ERR(tmdev->srot_map)) | ||
| 145 | return PTR_ERR(tmdev->srot_map); | ||
| 146 | |||
| 139 | } else { | 147 | } else { |
| 140 | /* old DTs where SROT and TM were in a contiguous 2K block */ | 148 | /* old DTs where SROT and TM were in a contiguous 2K block */ |
| 141 | tmdev->tm_offset = 0x1000; | 149 | tmdev->tm_offset = 0x1000; |
| 142 | } | 150 | } |
| 143 | 151 | ||
| 144 | res = platform_get_resource(op, IORESOURCE_MEM, 0); | 152 | res = platform_get_resource(op, IORESOURCE_MEM, 0); |
| 145 | base = devm_ioremap_resource(&op->dev, res); | 153 | tm_base = devm_ioremap_resource(&op->dev, res); |
| 146 | if (IS_ERR(base)) | 154 | if (IS_ERR(tm_base)) |
| 147 | return PTR_ERR(base); | 155 | return PTR_ERR(tm_base); |
| 148 | 156 | ||
| 149 | tmdev->map = devm_regmap_init_mmio(tmdev->dev, base, &tsens_config); | 157 | tmdev->tm_map = devm_regmap_init_mmio(tmdev->dev, tm_base, &tsens_config); |
| 150 | if (IS_ERR(tmdev->map)) | 158 | if (IS_ERR(tmdev->tm_map)) |
| 151 | return PTR_ERR(tmdev->map); | 159 | return PTR_ERR(tmdev->tm_map); |
| 160 | |||
| 161 | if (tmdev->srot_map) { | ||
| 162 | ret = regmap_read(tmdev->srot_map, ctrl_offset, &code); | ||
| 163 | if (ret) | ||
| 164 | return ret; | ||
| 165 | if (!(code & TSENS_EN)) { | ||
| 166 | dev_err(tmdev->dev, "tsens device is not enabled\n"); | ||
| 167 | return -ENODEV; | ||
| 168 | } | ||
| 169 | } | ||
| 152 | 170 | ||
| 153 | return 0; | 171 | return 0; |
| 154 | } | 172 | } |
diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c index 44da02f594ac..381a212872bf 100644 --- a/drivers/thermal/qcom/tsens-v2.c +++ b/drivers/thermal/qcom/tsens-v2.c | |||
| @@ -21,7 +21,7 @@ static int get_temp_tsens_v2(struct tsens_device *tmdev, int id, int *temp) | |||
| 21 | int ret; | 21 | int ret; |
| 22 | 22 | ||
| 23 | status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * 4; | 23 | status_reg = tmdev->tm_offset + STATUS_OFFSET + s->hw_id * 4; |
| 24 | ret = regmap_read(tmdev->map, status_reg, &code); | 24 | ret = regmap_read(tmdev->tm_map, status_reg, &code); |
| 25 | if (ret) | 25 | if (ret) |
| 26 | return ret; | 26 | return ret; |
| 27 | last_temp = code & LAST_TEMP_MASK; | 27 | last_temp = code & LAST_TEMP_MASK; |
| @@ -29,7 +29,7 @@ static int get_temp_tsens_v2(struct tsens_device *tmdev, int id, int *temp) | |||
| 29 | goto done; | 29 | goto done; |
| 30 | 30 | ||
| 31 | /* Try a second time */ | 31 | /* Try a second time */ |
| 32 | ret = regmap_read(tmdev->map, status_reg, &code); | 32 | ret = regmap_read(tmdev->tm_map, status_reg, &code); |
| 33 | if (ret) | 33 | if (ret) |
| 34 | return ret; | 34 | return ret; |
| 35 | if (code & STATUS_VALID_BIT) { | 35 | if (code & STATUS_VALID_BIT) { |
| @@ -40,7 +40,7 @@ static int get_temp_tsens_v2(struct tsens_device *tmdev, int id, int *temp) | |||
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | /* Try a third/last time */ | 42 | /* Try a third/last time */ |
| 43 | ret = regmap_read(tmdev->map, status_reg, &code); | 43 | ret = regmap_read(tmdev->tm_map, status_reg, &code); |
| 44 | if (ret) | 44 | if (ret) |
| 45 | return ret; | 45 | return ret; |
| 46 | if (code & STATUS_VALID_BIT) { | 46 | if (code & STATUS_VALID_BIT) { |
| @@ -68,10 +68,12 @@ static const struct tsens_ops ops_generic_v2 = { | |||
| 68 | 68 | ||
| 69 | const struct tsens_data data_tsens_v2 = { | 69 | const struct tsens_data data_tsens_v2 = { |
| 70 | .ops = &ops_generic_v2, | 70 | .ops = &ops_generic_v2, |
| 71 | .reg_offsets = { [SROT_CTRL_OFFSET] = 0x4 }, | ||
| 71 | }; | 72 | }; |
| 72 | 73 | ||
| 73 | /* Kept around for backward compatibility with old msm8996.dtsi */ | 74 | /* Kept around for backward compatibility with old msm8996.dtsi */ |
| 74 | const struct tsens_data data_8996 = { | 75 | const struct tsens_data data_8996 = { |
| 75 | .num_sensors = 13, | 76 | .num_sensors = 13, |
| 76 | .ops = &ops_generic_v2, | 77 | .ops = &ops_generic_v2, |
| 78 | .reg_offsets = { [SROT_CTRL_OFFSET] = 0x4 }, | ||
| 77 | }; | 79 | }; |
diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c index a2c9bfae3d86..f1ec9bbe4717 100644 --- a/drivers/thermal/qcom/tsens.c +++ b/drivers/thermal/qcom/tsens.c | |||
| @@ -1,15 +1,6 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | 3 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. |
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 and | ||
| 6 | * only version 2 as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | */ | 4 | */ |
| 14 | 5 | ||
| 15 | #include <linux/err.h> | 6 | #include <linux/err.h> |
| @@ -89,11 +80,6 @@ static int tsens_register(struct tsens_device *tmdev) | |||
| 89 | { | 80 | { |
| 90 | int i; | 81 | int i; |
| 91 | struct thermal_zone_device *tzd; | 82 | struct thermal_zone_device *tzd; |
| 92 | u32 *hw_id, n = tmdev->num_sensors; | ||
| 93 | |||
| 94 | hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL); | ||
| 95 | if (!hw_id) | ||
| 96 | return -ENOMEM; | ||
| 97 | 83 | ||
| 98 | for (i = 0; i < tmdev->num_sensors; i++) { | 84 | for (i = 0; i < tmdev->num_sensors; i++) { |
| 99 | tmdev->sensor[i].tmdev = tmdev; | 85 | tmdev->sensor[i].tmdev = tmdev; |
| @@ -158,6 +144,9 @@ static int tsens_probe(struct platform_device *pdev) | |||
| 158 | else | 144 | else |
| 159 | tmdev->sensor[i].hw_id = i; | 145 | tmdev->sensor[i].hw_id = i; |
| 160 | } | 146 | } |
| 147 | for (i = 0; i < REG_ARRAY_SIZE; i++) { | ||
| 148 | tmdev->reg_offsets[i] = data->reg_offsets[i]; | ||
| 149 | } | ||
| 161 | 150 | ||
| 162 | if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp) | 151 | if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp) |
| 163 | return -EINVAL; | 152 | return -EINVAL; |
diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h index 14331eb45a86..7b7feee5dc46 100644 --- a/drivers/thermal/qcom/tsens.h +++ b/drivers/thermal/qcom/tsens.h | |||
| @@ -1,15 +1,8 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 1 | /* | 2 | /* |
| 2 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | 3 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. |
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | 4 | */ |
| 5 | |||
| 13 | #ifndef __QCOM_TSENS_H__ | 6 | #ifndef __QCOM_TSENS_H__ |
| 14 | #define __QCOM_TSENS_H__ | 7 | #define __QCOM_TSENS_H__ |
| 15 | 8 | ||
| @@ -55,15 +48,23 @@ struct tsens_ops { | |||
| 55 | int (*get_trend)(struct tsens_device *, int, enum thermal_trend *); | 48 | int (*get_trend)(struct tsens_device *, int, enum thermal_trend *); |
| 56 | }; | 49 | }; |
| 57 | 50 | ||
| 51 | enum reg_list { | ||
| 52 | SROT_CTRL_OFFSET, | ||
| 53 | |||
| 54 | REG_ARRAY_SIZE, | ||
| 55 | }; | ||
| 56 | |||
| 58 | /** | 57 | /** |
| 59 | * struct tsens_data - tsens instance specific data | 58 | * struct tsens_data - tsens instance specific data |
| 60 | * @num_sensors: Max number of sensors supported by platform | 59 | * @num_sensors: Max number of sensors supported by platform |
| 61 | * @ops: operations the tsens instance supports | 60 | * @ops: operations the tsens instance supports |
| 62 | * @hw_ids: Subset of sensors ids supported by platform, if not the first n | 61 | * @hw_ids: Subset of sensors ids supported by platform, if not the first n |
| 62 | * @reg_offsets: Register offsets for commonly used registers | ||
| 63 | */ | 63 | */ |
| 64 | struct tsens_data { | 64 | struct tsens_data { |
| 65 | const u32 num_sensors; | 65 | const u32 num_sensors; |
| 66 | const struct tsens_ops *ops; | 66 | const struct tsens_ops *ops; |
| 67 | const u16 reg_offsets[REG_ARRAY_SIZE]; | ||
| 67 | unsigned int *hw_ids; | 68 | unsigned int *hw_ids; |
| 68 | }; | 69 | }; |
| 69 | 70 | ||
| @@ -76,8 +77,10 @@ struct tsens_context { | |||
| 76 | struct tsens_device { | 77 | struct tsens_device { |
| 77 | struct device *dev; | 78 | struct device *dev; |
| 78 | u32 num_sensors; | 79 | u32 num_sensors; |
| 79 | struct regmap *map; | 80 | struct regmap *tm_map; |
| 81 | struct regmap *srot_map; | ||
| 80 | u32 tm_offset; | 82 | u32 tm_offset; |
| 83 | u16 reg_offsets[REG_ARRAY_SIZE]; | ||
| 81 | struct tsens_context ctx; | 84 | struct tsens_context ctx; |
| 82 | const struct tsens_ops *ops; | 85 | const struct tsens_ops *ops; |
| 83 | struct tsens_sensor sensor[0]; | 86 | struct tsens_sensor sensor[0]; |
diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 450ed66edf58..18c711b19514 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c | |||
| @@ -119,8 +119,8 @@ static int qoriq_tmu_get_sensor_id(void) | |||
| 119 | if (sensor_specs.args_count >= 1) { | 119 | if (sensor_specs.args_count >= 1) { |
| 120 | id = sensor_specs.args[0]; | 120 | id = sensor_specs.args[0]; |
| 121 | WARN(sensor_specs.args_count > 1, | 121 | WARN(sensor_specs.args_count > 1, |
| 122 | "%s: too many cells in sensor specifier %d\n", | 122 | "%pOFn: too many cells in sensor specifier %d\n", |
| 123 | sensor_specs.np->name, sensor_specs.args_count); | 123 | sensor_specs.np, sensor_specs.args_count); |
| 124 | } else { | 124 | } else { |
| 125 | id = 0; | 125 | id = 0; |
| 126 | } | 126 | } |
| @@ -294,6 +294,7 @@ static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, | |||
| 294 | 294 | ||
| 295 | static const struct of_device_id qoriq_tmu_match[] = { | 295 | static const struct of_device_id qoriq_tmu_match[] = { |
| 296 | { .compatible = "fsl,qoriq-tmu", }, | 296 | { .compatible = "fsl,qoriq-tmu", }, |
| 297 | { .compatible = "fsl,imx8mq-tmu", }, | ||
| 297 | {}, | 298 | {}, |
| 298 | }; | 299 | }; |
| 299 | MODULE_DEVICE_TABLE(of, qoriq_tmu_match); | 300 | MODULE_DEVICE_TABLE(of, qoriq_tmu_match); |
diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index 7aed5337bdd3..75786cc8e2f9 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c | |||
| @@ -318,9 +318,11 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc) | |||
| 318 | } | 318 | } |
| 319 | 319 | ||
| 320 | static const struct of_device_id rcar_gen3_thermal_dt_ids[] = { | 320 | static const struct of_device_id rcar_gen3_thermal_dt_ids[] = { |
| 321 | { .compatible = "renesas,r8a774a1-thermal", }, | ||
| 321 | { .compatible = "renesas,r8a7795-thermal", }, | 322 | { .compatible = "renesas,r8a7795-thermal", }, |
| 322 | { .compatible = "renesas,r8a7796-thermal", }, | 323 | { .compatible = "renesas,r8a7796-thermal", }, |
| 323 | { .compatible = "renesas,r8a77965-thermal", }, | 324 | { .compatible = "renesas,r8a77965-thermal", }, |
| 325 | { .compatible = "renesas,r8a77980-thermal", }, | ||
| 324 | {}, | 326 | {}, |
| 325 | }; | 327 | }; |
| 326 | MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids); | 328 | MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids); |
diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 78f932822d38..8014a207d8d9 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c | |||
| @@ -113,6 +113,10 @@ static const struct of_device_id rcar_thermal_dt_ids[] = { | |||
| 113 | .data = &rcar_gen2_thermal, | 113 | .data = &rcar_gen2_thermal, |
| 114 | }, | 114 | }, |
| 115 | { | 115 | { |
| 116 | .compatible = "renesas,thermal-r8a77970", | ||
| 117 | .data = &rcar_gen3_thermal, | ||
| 118 | }, | ||
| 119 | { | ||
| 116 | .compatible = "renesas,thermal-r8a77995", | 120 | .compatible = "renesas,thermal-r8a77995", |
| 117 | .data = &rcar_gen3_thermal, | 121 | .data = &rcar_gen3_thermal, |
| 118 | }, | 122 | }, |
| @@ -434,8 +438,8 @@ static irqreturn_t rcar_thermal_irq(int irq, void *data) | |||
| 434 | rcar_thermal_for_each_priv(priv, common) { | 438 | rcar_thermal_for_each_priv(priv, common) { |
| 435 | if (rcar_thermal_had_changed(priv, status)) { | 439 | if (rcar_thermal_had_changed(priv, status)) { |
| 436 | rcar_thermal_irq_disable(priv); | 440 | rcar_thermal_irq_disable(priv); |
| 437 | schedule_delayed_work(&priv->work, | 441 | queue_delayed_work(system_freezable_wq, &priv->work, |
| 438 | msecs_to_jiffies(300)); | 442 | msecs_to_jiffies(300)); |
| 439 | } | 443 | } |
| 440 | } | 444 | } |
| 441 | 445 | ||
| @@ -453,6 +457,7 @@ static int rcar_thermal_remove(struct platform_device *pdev) | |||
| 453 | 457 | ||
| 454 | rcar_thermal_for_each_priv(priv, common) { | 458 | rcar_thermal_for_each_priv(priv, common) { |
| 455 | rcar_thermal_irq_disable(priv); | 459 | rcar_thermal_irq_disable(priv); |
| 460 | cancel_delayed_work_sync(&priv->work); | ||
| 456 | if (priv->chip->use_of_thermal) | 461 | if (priv->chip->use_of_thermal) |
| 457 | thermal_remove_hwmon_sysfs(priv->zone); | 462 | thermal_remove_hwmon_sysfs(priv->zone); |
| 458 | else | 463 | else |
| @@ -492,7 +497,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) | |||
| 492 | pm_runtime_get_sync(dev); | 497 | pm_runtime_get_sync(dev); |
| 493 | 498 | ||
| 494 | for (i = 0; i < chip->nirqs; i++) { | 499 | for (i = 0; i < chip->nirqs; i++) { |
| 495 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | 500 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, i); |
| 496 | if (!irq) | 501 | if (!irq) |
| 497 | continue; | 502 | continue; |
| 498 | if (!common->base) { | 503 | if (!common->base) { |
diff --git a/drivers/thermal/st/Kconfig b/drivers/thermal/st/Kconfig index 490fdbe22eea..b80f9a9e4f8f 100644 --- a/drivers/thermal/st/Kconfig +++ b/drivers/thermal/st/Kconfig | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | # | ||
| 2 | # STMicroelectronics thermal drivers configuration | ||
| 3 | # | ||
| 4 | |||
| 1 | config ST_THERMAL | 5 | config ST_THERMAL |
| 2 | tristate "Thermal sensors on STMicroelectronics STi series of SoCs" | 6 | tristate "Thermal sensors on STMicroelectronics STi series of SoCs" |
| 3 | help | 7 | help |
| @@ -10,3 +14,13 @@ config ST_THERMAL_SYSCFG | |||
| 10 | config ST_THERMAL_MEMMAP | 14 | config ST_THERMAL_MEMMAP |
| 11 | select ST_THERMAL | 15 | select ST_THERMAL |
| 12 | tristate "STi series memory mapped access based thermal sensors" | 16 | tristate "STi series memory mapped access based thermal sensors" |
| 17 | |||
| 18 | config STM32_THERMAL | ||
| 19 | tristate "Thermal framework support on STMicroelectronics STM32 series of SoCs" | ||
| 20 | depends on MACH_STM32MP157 | ||
| 21 | default y | ||
| 22 | help | ||
| 23 | Support for thermal framework on STMicroelectronics STM32 series of | ||
| 24 | SoCs. This thermal driver allows to access to general thermal framework | ||
| 25 | functionalities and to acces to SoC sensor functionalities. This | ||
| 26 | configuration is fully dependent of MACH_STM32MP157. | ||
diff --git a/drivers/thermal/st/Makefile b/drivers/thermal/st/Makefile index b38878977bd8..b2b9e9b96296 100644 --- a/drivers/thermal/st/Makefile +++ b/drivers/thermal/st/Makefile | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | obj-$(CONFIG_ST_THERMAL) := st_thermal.o | 1 | obj-$(CONFIG_ST_THERMAL) := st_thermal.o |
| 2 | obj-$(CONFIG_ST_THERMAL_SYSCFG) += st_thermal_syscfg.o | 2 | obj-$(CONFIG_ST_THERMAL_SYSCFG) += st_thermal_syscfg.o |
| 3 | obj-$(CONFIG_ST_THERMAL_MEMMAP) += st_thermal_memmap.o | 3 | obj-$(CONFIG_ST_THERMAL_MEMMAP) += st_thermal_memmap.o |
| 4 | obj-$(CONFIG_STM32_THERMAL) := stm_thermal.o \ No newline at end of file | ||
diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c new file mode 100644 index 000000000000..47623da0f91b --- /dev/null +++ b/drivers/thermal/st/stm_thermal.c | |||
| @@ -0,0 +1,760 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (C) STMicroelectronics 2018 - All Rights Reserved | ||
| 4 | * Author: David Hernandez Sanchez <david.hernandezsanchez@st.com> for | ||
| 5 | * STMicroelectronics. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <linux/clk.h> | ||
| 9 | #include <linux/clk-provider.h> | ||
| 10 | #include <linux/delay.h> | ||
| 11 | #include <linux/err.h> | ||
| 12 | #include <linux/interrupt.h> | ||
| 13 | #include <linux/io.h> | ||
| 14 | #include <linux/iopoll.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/of.h> | ||
| 17 | #include <linux/of_address.h> | ||
| 18 | #include <linux/of_device.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | #include <linux/thermal.h> | ||
| 21 | |||
| 22 | #include "../thermal_core.h" | ||
| 23 | #include "../thermal_hwmon.h" | ||
| 24 | |||
| 25 | /* DTS register offsets */ | ||
| 26 | #define DTS_CFGR1_OFFSET 0x0 | ||
| 27 | #define DTS_T0VALR1_OFFSET 0x8 | ||
| 28 | #define DTS_RAMPVALR_OFFSET 0X10 | ||
| 29 | #define DTS_ITR1_OFFSET 0x14 | ||
| 30 | #define DTS_DR_OFFSET 0x1C | ||
| 31 | #define DTS_SR_OFFSET 0x20 | ||
| 32 | #define DTS_ITENR_OFFSET 0x24 | ||
| 33 | #define DTS_CIFR_OFFSET 0x28 | ||
| 34 | |||
| 35 | /* DTS_CFGR1 register mask definitions */ | ||
| 36 | #define HSREF_CLK_DIV_MASK GENMASK(30, 24) | ||
| 37 | #define TS1_SMP_TIME_MASK GENMASK(19, 16) | ||
| 38 | #define TS1_INTRIG_SEL_MASK GENMASK(11, 8) | ||
| 39 | |||
| 40 | /* DTS_T0VALR1 register mask definitions */ | ||
| 41 | #define TS1_T0_MASK GENMASK(17, 16) | ||
| 42 | #define TS1_FMT0_MASK GENMASK(15, 0) | ||
| 43 | |||
| 44 | /* DTS_RAMPVALR register mask definitions */ | ||
| 45 | #define TS1_RAMP_COEFF_MASK GENMASK(15, 0) | ||
| 46 | |||
| 47 | /* DTS_ITR1 register mask definitions */ | ||
| 48 | #define TS1_HITTHD_MASK GENMASK(31, 16) | ||
| 49 | #define TS1_LITTHD_MASK GENMASK(15, 0) | ||
| 50 | |||
| 51 | /* DTS_DR register mask definitions */ | ||
| 52 | #define TS1_MFREQ_MASK GENMASK(15, 0) | ||
| 53 | |||
| 54 | /* Less significant bit position definitions */ | ||
| 55 | #define TS1_T0_POS 16 | ||
| 56 | #define TS1_SMP_TIME_POS 16 | ||
| 57 | #define TS1_HITTHD_POS 16 | ||
| 58 | #define HSREF_CLK_DIV_POS 24 | ||
| 59 | |||
| 60 | /* DTS_CFGR1 bit definitions */ | ||
| 61 | #define TS1_EN BIT(0) | ||
| 62 | #define TS1_START BIT(4) | ||
| 63 | #define REFCLK_SEL BIT(20) | ||
| 64 | #define REFCLK_LSE REFCLK_SEL | ||
| 65 | #define Q_MEAS_OPT BIT(21) | ||
| 66 | #define CALIBRATION_CONTROL Q_MEAS_OPT | ||
| 67 | |||
| 68 | /* DTS_SR bit definitions */ | ||
| 69 | #define TS_RDY BIT(15) | ||
| 70 | /* Bit definitions below are common for DTS_SR, DTS_ITENR and DTS_CIFR */ | ||
| 71 | #define HIGH_THRESHOLD BIT(2) | ||
| 72 | #define LOW_THRESHOLD BIT(1) | ||
| 73 | |||
| 74 | /* Constants */ | ||
| 75 | #define ADJUST 100 | ||
| 76 | #define ONE_MHZ 1000000 | ||
| 77 | #define POLL_TIMEOUT 5000 | ||
| 78 | #define STARTUP_TIME 40 | ||
| 79 | #define TS1_T0_VAL0 30 | ||
| 80 | #define TS1_T0_VAL1 130 | ||
| 81 | #define NO_HW_TRIG 0 | ||
| 82 | |||
| 83 | /* The Thermal Framework expects millidegrees */ | ||
| 84 | #define mcelsius(temp) ((temp) * 1000) | ||
| 85 | |||
| 86 | /* The Sensor expects oC degrees */ | ||
| 87 | #define celsius(temp) ((temp) / 1000) | ||
| 88 | |||
| 89 | struct stm_thermal_sensor { | ||
| 90 | struct device *dev; | ||
| 91 | struct thermal_zone_device *th_dev; | ||
| 92 | enum thermal_device_mode mode; | ||
| 93 | struct clk *clk; | ||
| 94 | int high_temp; | ||
| 95 | int low_temp; | ||
| 96 | int temp_critical; | ||
| 97 | int temp_passive; | ||
| 98 | unsigned int low_temp_enabled; | ||
| 99 | int num_trips; | ||
| 100 | int irq; | ||
| 101 | unsigned int irq_enabled; | ||
| 102 | void __iomem *base; | ||
| 103 | int t0, fmt0, ramp_coeff; | ||
| 104 | }; | ||
| 105 | |||
| 106 | static irqreturn_t stm_thermal_alarm_irq(int irq, void *sdata) | ||
| 107 | { | ||
| 108 | struct stm_thermal_sensor *sensor = sdata; | ||
| 109 | |||
| 110 | disable_irq_nosync(irq); | ||
| 111 | sensor->irq_enabled = false; | ||
| 112 | |||
| 113 | return IRQ_WAKE_THREAD; | ||
| 114 | } | ||
| 115 | |||
| 116 | static irqreturn_t stm_thermal_alarm_irq_thread(int irq, void *sdata) | ||
| 117 | { | ||
| 118 | u32 value; | ||
| 119 | struct stm_thermal_sensor *sensor = sdata; | ||
| 120 | |||
| 121 | /* read IT reason in SR and clear flags */ | ||
| 122 | value = readl_relaxed(sensor->base + DTS_SR_OFFSET); | ||
| 123 | |||
| 124 | if ((value & LOW_THRESHOLD) == LOW_THRESHOLD) | ||
| 125 | writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); | ||
| 126 | |||
| 127 | if ((value & HIGH_THRESHOLD) == HIGH_THRESHOLD) | ||
| 128 | writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); | ||
| 129 | |||
| 130 | thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED); | ||
| 131 | |||
| 132 | return IRQ_HANDLED; | ||
| 133 | } | ||
| 134 | |||
| 135 | static int stm_sensor_power_on(struct stm_thermal_sensor *sensor) | ||
| 136 | { | ||
| 137 | int ret; | ||
| 138 | u32 value; | ||
| 139 | |||
| 140 | /* Enable sensor */ | ||
| 141 | value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET); | ||
| 142 | value |= TS1_EN; | ||
| 143 | writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET); | ||
| 144 | |||
| 145 | /* | ||
| 146 | * The DTS block can be enabled by setting TSx_EN bit in | ||
| 147 | * DTS_CFGRx register. It requires a startup time of | ||
| 148 | * 40μs. Use 5 ms as arbitrary timeout. | ||
| 149 | */ | ||
| 150 | ret = readl_poll_timeout(sensor->base + DTS_SR_OFFSET, | ||
| 151 | value, (value & TS_RDY), | ||
| 152 | STARTUP_TIME, POLL_TIMEOUT); | ||
| 153 | if (ret) | ||
| 154 | return ret; | ||
| 155 | |||
| 156 | /* Start continuous measuring */ | ||
| 157 | value = readl_relaxed(sensor->base + | ||
| 158 | DTS_CFGR1_OFFSET); | ||
| 159 | value |= TS1_START; | ||
| 160 | writel_relaxed(value, sensor->base + | ||
| 161 | DTS_CFGR1_OFFSET); | ||
| 162 | |||
| 163 | return 0; | ||
| 164 | } | ||
| 165 | |||
| 166 | static int stm_sensor_power_off(struct stm_thermal_sensor *sensor) | ||
| 167 | { | ||
| 168 | u32 value; | ||
| 169 | |||
| 170 | /* Stop measuring */ | ||
| 171 | value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET); | ||
| 172 | value &= ~TS1_START; | ||
| 173 | writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET); | ||
| 174 | |||
| 175 | /* Ensure stop is taken into account */ | ||
| 176 | usleep_range(STARTUP_TIME, POLL_TIMEOUT); | ||
| 177 | |||
| 178 | /* Disable sensor */ | ||
| 179 | value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET); | ||
| 180 | value &= ~TS1_EN; | ||
| 181 | writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET); | ||
| 182 | |||
| 183 | /* Ensure disable is taken into account */ | ||
| 184 | return readl_poll_timeout(sensor->base + DTS_SR_OFFSET, value, | ||
| 185 | !(value & TS_RDY), | ||
| 186 | STARTUP_TIME, POLL_TIMEOUT); | ||
| 187 | } | ||
| 188 | |||
| 189 | static int stm_thermal_calibration(struct stm_thermal_sensor *sensor) | ||
| 190 | { | ||
| 191 | u32 value, clk_freq; | ||
| 192 | u32 prescaler; | ||
| 193 | |||
| 194 | /* Figure out prescaler value for PCLK during calibration */ | ||
| 195 | clk_freq = clk_get_rate(sensor->clk); | ||
| 196 | if (!clk_freq) | ||
| 197 | return -EINVAL; | ||
| 198 | |||
| 199 | prescaler = 0; | ||
| 200 | clk_freq /= ONE_MHZ; | ||
| 201 | if (clk_freq) { | ||
| 202 | while (prescaler <= clk_freq) | ||
| 203 | prescaler++; | ||
| 204 | } | ||
| 205 | |||
| 206 | value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET); | ||
| 207 | |||
| 208 | /* Clear prescaler */ | ||
| 209 | value &= ~HSREF_CLK_DIV_MASK; | ||
| 210 | |||
| 211 | /* Set prescaler. pclk_freq/prescaler < 1MHz */ | ||
| 212 | value |= (prescaler << HSREF_CLK_DIV_POS); | ||
| 213 | |||
| 214 | /* Select PCLK as reference clock */ | ||
| 215 | value &= ~REFCLK_SEL; | ||
| 216 | |||
| 217 | /* Set maximal sampling time for better precision */ | ||
| 218 | value |= TS1_SMP_TIME_MASK; | ||
| 219 | |||
| 220 | /* Measure with calibration */ | ||
| 221 | value &= ~CALIBRATION_CONTROL; | ||
| 222 | |||
| 223 | /* select trigger */ | ||
| 224 | value &= ~TS1_INTRIG_SEL_MASK; | ||
| 225 | value |= NO_HW_TRIG; | ||
| 226 | |||
| 227 | writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET); | ||
| 228 | |||
| 229 | return 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | /* Fill in DTS structure with factory sensor values */ | ||
| 233 | static int stm_thermal_read_factory_settings(struct stm_thermal_sensor *sensor) | ||
| 234 | { | ||
| 235 | /* Retrieve engineering calibration temperature */ | ||
| 236 | sensor->t0 = readl_relaxed(sensor->base + DTS_T0VALR1_OFFSET) & | ||
| 237 | TS1_T0_MASK; | ||
| 238 | if (!sensor->t0) | ||
| 239 | sensor->t0 = TS1_T0_VAL0; | ||
| 240 | else | ||
| 241 | sensor->t0 = TS1_T0_VAL1; | ||
| 242 | |||
| 243 | /* Retrieve fmt0 and put it on Hz */ | ||
| 244 | sensor->fmt0 = ADJUST * readl_relaxed(sensor->base + DTS_T0VALR1_OFFSET) | ||
| 245 | & TS1_FMT0_MASK; | ||
| 246 | |||
| 247 | /* Retrieve ramp coefficient */ | ||
| 248 | sensor->ramp_coeff = readl_relaxed(sensor->base + DTS_RAMPVALR_OFFSET) & | ||
| 249 | TS1_RAMP_COEFF_MASK; | ||
| 250 | |||
| 251 | if (!sensor->fmt0 || !sensor->ramp_coeff) { | ||
| 252 | dev_err(sensor->dev, "%s: wrong setting\n", __func__); | ||
| 253 | return -EINVAL; | ||
| 254 | } | ||
| 255 | |||
| 256 | dev_dbg(sensor->dev, "%s: T0 = %doC, FMT0 = %dHz, RAMP_COEFF = %dHz/oC", | ||
| 257 | __func__, sensor->t0, sensor->fmt0, sensor->ramp_coeff); | ||
| 258 | |||
| 259 | return 0; | ||
| 260 | } | ||
| 261 | |||
| 262 | static int stm_thermal_calculate_threshold(struct stm_thermal_sensor *sensor, | ||
| 263 | int temp, u32 *th) | ||
| 264 | { | ||
| 265 | int freqM; | ||
| 266 | u32 sampling_time; | ||
| 267 | |||
| 268 | /* Retrieve the number of periods to sample */ | ||
| 269 | sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) & | ||
| 270 | TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS; | ||
| 271 | |||
| 272 | /* Figure out the CLK_PTAT frequency for a given temperature */ | ||
| 273 | freqM = ((temp - sensor->t0) * sensor->ramp_coeff) | ||
| 274 | + sensor->fmt0; | ||
| 275 | |||
| 276 | dev_dbg(sensor->dev, "%s: freqM for threshold = %d Hz", | ||
| 277 | __func__, freqM); | ||
| 278 | |||
| 279 | /* Figure out the threshold sample number */ | ||
| 280 | *th = clk_get_rate(sensor->clk); | ||
| 281 | if (!*th) | ||
| 282 | return -EINVAL; | ||
| 283 | |||
| 284 | *th = *th / freqM; | ||
| 285 | |||
| 286 | *th *= sampling_time; | ||
| 287 | |||
| 288 | return 0; | ||
| 289 | } | ||
| 290 | |||
| 291 | static int stm_thermal_set_threshold(struct stm_thermal_sensor *sensor) | ||
| 292 | { | ||
| 293 | u32 value, th; | ||
| 294 | int ret; | ||
| 295 | |||
| 296 | value = readl_relaxed(sensor->base + DTS_ITR1_OFFSET); | ||
| 297 | |||
| 298 | /* Erase threshold content */ | ||
| 299 | value &= ~(TS1_LITTHD_MASK | TS1_HITTHD_MASK); | ||
| 300 | |||
| 301 | /* Retrieve the sample threshold number th for a given temperature */ | ||
| 302 | ret = stm_thermal_calculate_threshold(sensor, sensor->high_temp, &th); | ||
| 303 | if (ret) | ||
| 304 | return ret; | ||
| 305 | |||
| 306 | value |= th & TS1_LITTHD_MASK; | ||
| 307 | |||
| 308 | if (sensor->low_temp_enabled) { | ||
| 309 | /* Retrieve the sample threshold */ | ||
| 310 | ret = stm_thermal_calculate_threshold(sensor, sensor->low_temp, | ||
| 311 | &th); | ||
| 312 | if (ret) | ||
| 313 | return ret; | ||
| 314 | |||
| 315 | value |= (TS1_HITTHD_MASK & (th << TS1_HITTHD_POS)); | ||
| 316 | } | ||
| 317 | |||
| 318 | /* Write value on the Low interrupt threshold */ | ||
| 319 | writel_relaxed(value, sensor->base + DTS_ITR1_OFFSET); | ||
| 320 | |||
| 321 | return 0; | ||
| 322 | } | ||
| 323 | |||
| 324 | /* Disable temperature interrupt */ | ||
| 325 | static int stm_disable_irq(struct stm_thermal_sensor *sensor) | ||
| 326 | { | ||
| 327 | u32 value; | ||
| 328 | |||
| 329 | /* Disable IT generation for low and high thresholds */ | ||
| 330 | value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET); | ||
| 331 | writel_relaxed(value & ~(LOW_THRESHOLD | HIGH_THRESHOLD), | ||
| 332 | sensor->base + DTS_ITENR_OFFSET); | ||
| 333 | |||
| 334 | dev_dbg(sensor->dev, "%s: IT disabled on sensor side", __func__); | ||
| 335 | |||
| 336 | return 0; | ||
| 337 | } | ||
| 338 | |||
| 339 | /* Enable temperature interrupt */ | ||
| 340 | static int stm_enable_irq(struct stm_thermal_sensor *sensor) | ||
| 341 | { | ||
| 342 | u32 value; | ||
| 343 | |||
| 344 | /* | ||
| 345 | * Code below enables High temperature threshold using a low threshold | ||
| 346 | * sampling value | ||
| 347 | */ | ||
| 348 | |||
| 349 | /* Make sure LOW_THRESHOLD IT is clear before enabling */ | ||
| 350 | writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); | ||
| 351 | |||
| 352 | /* Enable IT generation for low threshold */ | ||
| 353 | value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET); | ||
| 354 | value |= LOW_THRESHOLD; | ||
| 355 | |||
| 356 | /* Enable the low temperature threshold if needed */ | ||
| 357 | if (sensor->low_temp_enabled) { | ||
| 358 | /* Make sure HIGH_THRESHOLD IT is clear before enabling */ | ||
| 359 | writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); | ||
| 360 | |||
| 361 | /* Enable IT generation for high threshold */ | ||
| 362 | value |= HIGH_THRESHOLD; | ||
| 363 | } | ||
| 364 | |||
| 365 | /* Enable thresholds */ | ||
| 366 | writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET); | ||
| 367 | |||
| 368 | dev_dbg(sensor->dev, "%s: IT enabled on sensor side", __func__); | ||
| 369 | |||
| 370 | return 0; | ||
| 371 | } | ||
| 372 | |||
| 373 | static int stm_thermal_update_threshold(struct stm_thermal_sensor *sensor) | ||
| 374 | { | ||
| 375 | int ret; | ||
| 376 | |||
| 377 | sensor->mode = THERMAL_DEVICE_DISABLED; | ||
| 378 | |||
| 379 | ret = stm_sensor_power_off(sensor); | ||
| 380 | if (ret) | ||
| 381 | return ret; | ||
| 382 | |||
| 383 | ret = stm_disable_irq(sensor); | ||
| 384 | if (ret) | ||
| 385 | return ret; | ||
| 386 | |||
| 387 | ret = stm_thermal_set_threshold(sensor); | ||
| 388 | if (ret) | ||
| 389 | return ret; | ||
| 390 | |||
| 391 | ret = stm_enable_irq(sensor); | ||
| 392 | if (ret) | ||
| 393 | return ret; | ||
| 394 | |||
| 395 | ret = stm_sensor_power_on(sensor); | ||
| 396 | if (ret) | ||
| 397 | return ret; | ||
| 398 | |||
| 399 | sensor->mode = THERMAL_DEVICE_ENABLED; | ||
| 400 | |||
| 401 | return 0; | ||
| 402 | } | ||
| 403 | |||
| 404 | /* Callback to get temperature from HW */ | ||
| 405 | static int stm_thermal_get_temp(void *data, int *temp) | ||
| 406 | { | ||
| 407 | struct stm_thermal_sensor *sensor = data; | ||
| 408 | u32 sampling_time; | ||
| 409 | int freqM, ret; | ||
| 410 | |||
| 411 | if (sensor->mode != THERMAL_DEVICE_ENABLED) | ||
| 412 | return -EAGAIN; | ||
| 413 | |||
| 414 | /* Retrieve the number of samples */ | ||
| 415 | ret = readl_poll_timeout(sensor->base + DTS_DR_OFFSET, freqM, | ||
| 416 | (freqM & TS1_MFREQ_MASK), STARTUP_TIME, | ||
| 417 | POLL_TIMEOUT); | ||
| 418 | |||
| 419 | if (ret) | ||
| 420 | return ret; | ||
| 421 | |||
| 422 | if (!freqM) | ||
| 423 | return -ENODATA; | ||
| 424 | |||
| 425 | /* Retrieve the number of periods sampled */ | ||
| 426 | sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) & | ||
| 427 | TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS; | ||
| 428 | |||
| 429 | /* Figure out the number of samples per period */ | ||
| 430 | freqM /= sampling_time; | ||
| 431 | |||
| 432 | /* Figure out the CLK_PTAT frequency */ | ||
| 433 | freqM = clk_get_rate(sensor->clk) / freqM; | ||
| 434 | if (!freqM) | ||
| 435 | return -EINVAL; | ||
| 436 | |||
| 437 | dev_dbg(sensor->dev, "%s: freqM=%d\n", __func__, freqM); | ||
| 438 | |||
| 439 | /* Figure out the temperature in mili celsius */ | ||
| 440 | *temp = mcelsius(sensor->t0 + ((freqM - sensor->fmt0) / | ||
| 441 | sensor->ramp_coeff)); | ||
| 442 | |||
| 443 | dev_dbg(sensor->dev, "%s: temperature = %d millicelsius", | ||
| 444 | __func__, *temp); | ||
| 445 | |||
| 446 | /* Update thresholds */ | ||
| 447 | if (sensor->num_trips > 1) { | ||
| 448 | /* Update alarm threshold value to next higher trip point */ | ||
| 449 | if (sensor->high_temp == sensor->temp_passive && | ||
| 450 | celsius(*temp) >= sensor->temp_passive) { | ||
| 451 | sensor->high_temp = sensor->temp_critical; | ||
| 452 | sensor->low_temp = sensor->temp_passive; | ||
| 453 | sensor->low_temp_enabled = true; | ||
| 454 | ret = stm_thermal_update_threshold(sensor); | ||
| 455 | if (ret) | ||
| 456 | return ret; | ||
| 457 | } | ||
| 458 | |||
| 459 | if (sensor->high_temp == sensor->temp_critical && | ||
| 460 | celsius(*temp) < sensor->temp_passive) { | ||
| 461 | sensor->high_temp = sensor->temp_passive; | ||
| 462 | sensor->low_temp_enabled = false; | ||
| 463 | ret = stm_thermal_update_threshold(sensor); | ||
| 464 | if (ret) | ||
| 465 | return ret; | ||
| 466 | } | ||
| 467 | |||
| 468 | /* | ||
| 469 | * Re-enable alarm IRQ if temperature below critical | ||
| 470 | * temperature | ||
| 471 | */ | ||
| 472 | if (!sensor->irq_enabled && | ||
| 473 | (celsius(*temp) < sensor->temp_critical)) { | ||
| 474 | sensor->irq_enabled = true; | ||
| 475 | enable_irq(sensor->irq); | ||
| 476 | } | ||
| 477 | } | ||
| 478 | |||
| 479 | return 0; | ||
| 480 | } | ||
| 481 | |||
| 482 | /* Registers DTS irq to be visible by GIC */ | ||
| 483 | static int stm_register_irq(struct stm_thermal_sensor *sensor) | ||
| 484 | { | ||
| 485 | struct device *dev = sensor->dev; | ||
| 486 | struct platform_device *pdev = to_platform_device(dev); | ||
| 487 | int ret; | ||
| 488 | |||
| 489 | sensor->irq = platform_get_irq(pdev, 0); | ||
| 490 | if (sensor->irq < 0) { | ||
| 491 | dev_err(dev, "%s: Unable to find IRQ\n", __func__); | ||
| 492 | return sensor->irq; | ||
| 493 | } | ||
| 494 | |||
| 495 | ret = devm_request_threaded_irq(dev, sensor->irq, | ||
| 496 | stm_thermal_alarm_irq, | ||
| 497 | stm_thermal_alarm_irq_thread, | ||
| 498 | IRQF_ONESHOT, | ||
| 499 | dev->driver->name, sensor); | ||
| 500 | if (ret) { | ||
| 501 | dev_err(dev, "%s: Failed to register IRQ %d\n", __func__, | ||
| 502 | sensor->irq); | ||
| 503 | return ret; | ||
| 504 | } | ||
| 505 | |||
| 506 | sensor->irq_enabled = true; | ||
| 507 | |||
| 508 | dev_dbg(dev, "%s: thermal IRQ registered", __func__); | ||
| 509 | |||
| 510 | return 0; | ||
| 511 | } | ||
| 512 | |||
| 513 | static int stm_thermal_sensor_off(struct stm_thermal_sensor *sensor) | ||
| 514 | { | ||
| 515 | int ret; | ||
| 516 | |||
| 517 | ret = stm_sensor_power_off(sensor); | ||
| 518 | if (ret) | ||
| 519 | return ret; | ||
| 520 | |||
| 521 | clk_disable_unprepare(sensor->clk); | ||
| 522 | |||
| 523 | return 0; | ||
| 524 | } | ||
| 525 | |||
| 526 | static int stm_thermal_prepare(struct stm_thermal_sensor *sensor) | ||
| 527 | { | ||
| 528 | int ret; | ||
| 529 | struct device *dev = sensor->dev; | ||
| 530 | |||
| 531 | ret = clk_prepare_enable(sensor->clk); | ||
| 532 | if (ret) | ||
| 533 | return ret; | ||
| 534 | |||
| 535 | ret = stm_thermal_calibration(sensor); | ||
| 536 | if (ret) | ||
| 537 | goto thermal_unprepare; | ||
| 538 | |||
| 539 | /* Set threshold(s) for IRQ */ | ||
| 540 | ret = stm_thermal_set_threshold(sensor); | ||
| 541 | if (ret) | ||
| 542 | goto thermal_unprepare; | ||
| 543 | |||
| 544 | ret = stm_enable_irq(sensor); | ||
| 545 | if (ret) | ||
| 546 | goto thermal_unprepare; | ||
| 547 | |||
| 548 | ret = stm_sensor_power_on(sensor); | ||
| 549 | if (ret) { | ||
| 550 | dev_err(dev, "%s: failed to power on sensor\n", __func__); | ||
| 551 | goto irq_disable; | ||
| 552 | } | ||
| 553 | |||
| 554 | return 0; | ||
| 555 | |||
| 556 | irq_disable: | ||
| 557 | stm_disable_irq(sensor); | ||
| 558 | |||
| 559 | thermal_unprepare: | ||
| 560 | clk_disable_unprepare(sensor->clk); | ||
| 561 | |||
| 562 | return ret; | ||
| 563 | } | ||
| 564 | |||
| 565 | #ifdef CONFIG_PM_SLEEP | ||
| 566 | static int stm_thermal_suspend(struct device *dev) | ||
| 567 | { | ||
| 568 | int ret; | ||
| 569 | struct platform_device *pdev = to_platform_device(dev); | ||
| 570 | struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev); | ||
| 571 | |||
| 572 | ret = stm_thermal_sensor_off(sensor); | ||
| 573 | if (ret) | ||
| 574 | return ret; | ||
| 575 | |||
| 576 | sensor->mode = THERMAL_DEVICE_DISABLED; | ||
| 577 | |||
| 578 | return 0; | ||
| 579 | } | ||
| 580 | |||
| 581 | static int stm_thermal_resume(struct device *dev) | ||
| 582 | { | ||
| 583 | int ret; | ||
| 584 | struct platform_device *pdev = to_platform_device(dev); | ||
| 585 | struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev); | ||
| 586 | |||
| 587 | ret = stm_thermal_prepare(sensor); | ||
| 588 | if (ret) | ||
| 589 | return ret; | ||
| 590 | |||
| 591 | sensor->mode = THERMAL_DEVICE_ENABLED; | ||
| 592 | |||
| 593 | return 0; | ||
| 594 | } | ||
| 595 | #endif /* CONFIG_PM_SLEEP */ | ||
| 596 | |||
| 597 | SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops, stm_thermal_suspend, stm_thermal_resume); | ||
| 598 | |||
| 599 | static const struct thermal_zone_of_device_ops stm_tz_ops = { | ||
| 600 | .get_temp = stm_thermal_get_temp, | ||
| 601 | }; | ||
| 602 | |||
| 603 | static const struct of_device_id stm_thermal_of_match[] = { | ||
| 604 | { .compatible = "st,stm32-thermal"}, | ||
| 605 | { /* sentinel */ } | ||
| 606 | }; | ||
| 607 | MODULE_DEVICE_TABLE(of, stm_thermal_of_match); | ||
| 608 | |||
| 609 | static int stm_thermal_probe(struct platform_device *pdev) | ||
| 610 | { | ||
| 611 | struct stm_thermal_sensor *sensor; | ||
| 612 | struct resource *res; | ||
| 613 | const struct thermal_trip *trip; | ||
| 614 | void __iomem *base; | ||
| 615 | int ret, i; | ||
| 616 | |||
| 617 | if (!pdev->dev.of_node) { | ||
| 618 | dev_err(&pdev->dev, "%s: device tree node not found\n", | ||
| 619 | __func__); | ||
| 620 | return -EINVAL; | ||
| 621 | } | ||
| 622 | |||
| 623 | sensor = devm_kzalloc(&pdev->dev, sizeof(*sensor), GFP_KERNEL); | ||
| 624 | if (!sensor) | ||
| 625 | return -ENOMEM; | ||
| 626 | |||
| 627 | platform_set_drvdata(pdev, sensor); | ||
| 628 | |||
| 629 | sensor->dev = &pdev->dev; | ||
| 630 | |||
| 631 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 632 | base = devm_ioremap_resource(&pdev->dev, res); | ||
| 633 | if (IS_ERR(base)) | ||
| 634 | return PTR_ERR(base); | ||
| 635 | |||
| 636 | /* Populate sensor */ | ||
| 637 | sensor->base = base; | ||
| 638 | |||
| 639 | ret = stm_thermal_read_factory_settings(sensor); | ||
| 640 | if (ret) | ||
| 641 | return ret; | ||
| 642 | |||
| 643 | sensor->clk = devm_clk_get(&pdev->dev, "pclk"); | ||
| 644 | if (IS_ERR(sensor->clk)) { | ||
| 645 | dev_err(&pdev->dev, "%s: failed to fetch PCLK clock\n", | ||
| 646 | __func__); | ||
| 647 | return PTR_ERR(sensor->clk); | ||
| 648 | } | ||
| 649 | |||
| 650 | /* Register IRQ into GIC */ | ||
| 651 | ret = stm_register_irq(sensor); | ||
| 652 | if (ret) | ||
| 653 | return ret; | ||
| 654 | |||
| 655 | sensor->th_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, | ||
| 656 | sensor, | ||
| 657 | &stm_tz_ops); | ||
| 658 | |||
| 659 | if (IS_ERR(sensor->th_dev)) { | ||
| 660 | dev_err(&pdev->dev, "%s: thermal zone sensor registering KO\n", | ||
| 661 | __func__); | ||
| 662 | ret = PTR_ERR(sensor->th_dev); | ||
| 663 | return ret; | ||
| 664 | } | ||
| 665 | |||
| 666 | if (!sensor->th_dev->ops->get_crit_temp) { | ||
| 667 | /* Critical point must be provided */ | ||
| 668 | ret = -EINVAL; | ||
| 669 | goto err_tz; | ||
| 670 | } | ||
| 671 | |||
| 672 | ret = sensor->th_dev->ops->get_crit_temp(sensor->th_dev, | ||
| 673 | &sensor->temp_critical); | ||
| 674 | if (ret) { | ||
| 675 | dev_err(&pdev->dev, | ||
| 676 | "Not able to read critical_temp: %d\n", ret); | ||
| 677 | goto err_tz; | ||
| 678 | } | ||
| 679 | |||
| 680 | sensor->temp_critical = celsius(sensor->temp_critical); | ||
| 681 | |||
| 682 | /* Set thresholds for IRQ */ | ||
| 683 | sensor->high_temp = sensor->temp_critical; | ||
| 684 | |||
| 685 | trip = of_thermal_get_trip_points(sensor->th_dev); | ||
| 686 | sensor->num_trips = of_thermal_get_ntrips(sensor->th_dev); | ||
| 687 | |||
| 688 | /* Find out passive temperature if it exists */ | ||
| 689 | for (i = (sensor->num_trips - 1); i >= 0; i--) { | ||
| 690 | if (trip[i].type == THERMAL_TRIP_PASSIVE) { | ||
| 691 | sensor->temp_passive = celsius(trip[i].temperature); | ||
| 692 | /* Update high temperature threshold */ | ||
| 693 | sensor->high_temp = sensor->temp_passive; | ||
| 694 | } | ||
| 695 | } | ||
| 696 | |||
| 697 | /* | ||
| 698 | * Ensure low_temp_enabled flag is disabled. | ||
| 699 | * By disabling low_temp_enabled, low threshold IT will not be | ||
| 700 | * configured neither enabled because it is not needed as high | ||
| 701 | * threshold is set on the lowest temperature trip point after | ||
| 702 | * probe. | ||
| 703 | */ | ||
| 704 | sensor->low_temp_enabled = false; | ||
| 705 | |||
| 706 | /* Configure and enable HW sensor */ | ||
| 707 | ret = stm_thermal_prepare(sensor); | ||
| 708 | if (ret) { | ||
| 709 | dev_err(&pdev->dev, | ||
| 710 | "Not able to enable sensor: %d\n", ret); | ||
| 711 | goto err_tz; | ||
| 712 | } | ||
| 713 | |||
| 714 | /* | ||
| 715 | * Thermal_zone doesn't enable hwmon as default, | ||
| 716 | * enable it here | ||
| 717 | */ | ||
| 718 | sensor->th_dev->tzp->no_hwmon = false; | ||
| 719 | ret = thermal_add_hwmon_sysfs(sensor->th_dev); | ||
| 720 | if (ret) | ||
| 721 | goto err_tz; | ||
| 722 | |||
| 723 | sensor->mode = THERMAL_DEVICE_ENABLED; | ||
| 724 | |||
| 725 | dev_info(&pdev->dev, "%s: Driver initialized successfully\n", | ||
| 726 | __func__); | ||
| 727 | |||
| 728 | return 0; | ||
| 729 | |||
| 730 | err_tz: | ||
| 731 | thermal_zone_of_sensor_unregister(&pdev->dev, sensor->th_dev); | ||
| 732 | return ret; | ||
| 733 | } | ||
| 734 | |||
| 735 | static int stm_thermal_remove(struct platform_device *pdev) | ||
| 736 | { | ||
| 737 | struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev); | ||
| 738 | |||
| 739 | stm_thermal_sensor_off(sensor); | ||
| 740 | thermal_remove_hwmon_sysfs(sensor->th_dev); | ||
| 741 | thermal_zone_of_sensor_unregister(&pdev->dev, sensor->th_dev); | ||
| 742 | |||
| 743 | return 0; | ||
| 744 | } | ||
| 745 | |||
| 746 | static struct platform_driver stm_thermal_driver = { | ||
| 747 | .driver = { | ||
| 748 | .name = "stm_thermal", | ||
| 749 | .pm = &stm_thermal_pm_ops, | ||
| 750 | .of_match_table = stm_thermal_of_match, | ||
| 751 | }, | ||
| 752 | .probe = stm_thermal_probe, | ||
| 753 | .remove = stm_thermal_remove, | ||
| 754 | }; | ||
| 755 | module_platform_driver(stm_thermal_driver); | ||
| 756 | |||
| 757 | MODULE_DESCRIPTION("STMicroelectronics STM32 Thermal Sensor Driver"); | ||
| 758 | MODULE_AUTHOR("David Hernandez Sanchez <david.hernandezsanchez@st.com>"); | ||
| 759 | MODULE_LICENSE("GPL v2"); | ||
| 760 | MODULE_ALIAS("platform:stm_thermal"); | ||
