aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/thermal/armada-thermal.txt22
-rw-r--r--drivers/thermal/Kconfig8
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/armada_thermal.c232
4 files changed, 263 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/thermal/armada-thermal.txt b/Documentation/devicetree/bindings/thermal/armada-thermal.txt
new file mode 100644
index 000000000000..fff93d5f92de
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/armada-thermal.txt
@@ -0,0 +1,22 @@
1* Marvell Armada 370/XP thermal management
2
3Required properties:
4
5- compatible: Should be set to one of the following:
6 marvell,armada370-thermal
7 marvell,armadaxp-thermal
8
9- reg: Device's register space.
10 Two entries are expected, see the examples below.
11 The first one is required for the sensor register;
12 the second one is required for the control register
13 to be used for sensor initialization (a.k.a. calibration).
14
15Example:
16
17 thermal@d0018300 {
18 compatible = "marvell,armada370-thermal";
19 reg = <0xd0018300 0x4
20 0xd0018304 0x4>;
21 status = "okay";
22 };
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index a764f165b589..9eddf744c94f 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -144,6 +144,14 @@ config DB8500_THERMAL
144 created. Cooling devices can be bound to the trip points to cool this 144 created. Cooling devices can be bound to the trip points to cool this
145 thermal zone if trip points reached. 145 thermal zone if trip points reached.
146 146
147config ARMADA_THERMAL
148 tristate "Armada 370/XP thermal management"
149 depends on ARCH_MVEBU
150 depends on OF
151 help
152 Enable this option if you want to have support for thermal management
153 controller present in Armada 370 and Armada XP SoC.
154
147config DB8500_CPUFREQ_COOLING 155config DB8500_CPUFREQ_COOLING
148 tristate "DB8500 cpufreq cooling" 156 tristate "DB8500 cpufreq cooling"
149 depends on ARCH_U8500 157 depends on ARCH_U8500
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index d3a2b38c31e8..7f6509a97c14 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o
19obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o 19obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
20obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o 20obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
21obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o 21obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
22obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
22obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o 23obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
23obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o 24obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
24 25
diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c
new file mode 100644
index 000000000000..5b4d75fd7b49
--- /dev/null
+++ b/drivers/thermal/armada_thermal.c
@@ -0,0 +1,232 @@
1/*
2 * Marvell Armada 370/XP thermal sensor driver
3 *
4 * Copyright (C) 2013 Marvell
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16#include <linux/device.h>
17#include <linux/err.h>
18#include <linux/io.h>
19#include <linux/kernel.h>
20#include <linux/of.h>
21#include <linux/module.h>
22#include <linux/delay.h>
23#include <linux/platform_device.h>
24#include <linux/of_device.h>
25#include <linux/thermal.h>
26
27#define THERMAL_VALID_OFFSET 9
28#define THERMAL_VALID_MASK 0x1
29#define THERMAL_TEMP_OFFSET 10
30#define THERMAL_TEMP_MASK 0x1ff
31
32/* Thermal Manager Control and Status Register */
33#define PMU_TDC0_SW_RST_MASK (0x1 << 1)
34#define PMU_TM_DISABLE_OFFS 0
35#define PMU_TM_DISABLE_MASK (0x1 << PMU_TM_DISABLE_OFFS)
36#define PMU_TDC0_REF_CAL_CNT_OFFS 11
37#define PMU_TDC0_REF_CAL_CNT_MASK (0x1ff << PMU_TDC0_REF_CAL_CNT_OFFS)
38#define PMU_TDC0_OTF_CAL_MASK (0x1 << 30)
39#define PMU_TDC0_START_CAL_MASK (0x1 << 25)
40
41struct armada_thermal_ops;
42
43/* Marvell EBU Thermal Sensor Dev Structure */
44struct armada_thermal_priv {
45 void __iomem *sensor;
46 void __iomem *control;
47 struct armada_thermal_ops *ops;
48};
49
50struct armada_thermal_ops {
51 /* Initialize the sensor */
52 void (*init_sensor)(struct armada_thermal_priv *);
53
54 /* Test for a valid sensor value (optional) */
55 bool (*is_valid)(struct armada_thermal_priv *);
56};
57
58static void armadaxp_init_sensor(struct armada_thermal_priv *priv)
59{
60 unsigned long reg;
61
62 reg = readl_relaxed(priv->control);
63 reg |= PMU_TDC0_OTF_CAL_MASK;
64 writel(reg, priv->control);
65
66 /* Reference calibration value */
67 reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;
68 reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);
69 writel(reg, priv->control);
70
71 /* Reset the sensor */
72 reg = readl_relaxed(priv->control);
73 writel((reg | PMU_TDC0_SW_RST_MASK), priv->control);
74
75 writel(reg, priv->control);
76
77 /* Enable the sensor */
78 reg = readl_relaxed(priv->sensor);
79 reg &= ~PMU_TM_DISABLE_MASK;
80 writel(reg, priv->sensor);
81}
82
83static void armada370_init_sensor(struct armada_thermal_priv *priv)
84{
85 unsigned long reg;
86
87 reg = readl_relaxed(priv->control);
88 reg |= PMU_TDC0_OTF_CAL_MASK;
89 writel(reg, priv->control);
90
91 /* Reference calibration value */
92 reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;
93 reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);
94 writel(reg, priv->control);
95
96 reg &= ~PMU_TDC0_START_CAL_MASK;
97 writel(reg, priv->control);
98
99 mdelay(10);
100}
101
102static bool armada_is_valid(struct armada_thermal_priv *priv)
103{
104 unsigned long reg = readl_relaxed(priv->sensor);
105
106 return (reg >> THERMAL_VALID_OFFSET) & THERMAL_VALID_MASK;
107}
108
109static int armada_get_temp(struct thermal_zone_device *thermal,
110 unsigned long *temp)
111{
112 struct armada_thermal_priv *priv = thermal->devdata;
113 unsigned long reg;
114
115 /* Valid check */
116 if (priv->ops->is_valid && !priv->ops->is_valid(priv)) {
117 dev_err(&thermal->device,
118 "Temperature sensor reading not valid\n");
119 return -EIO;
120 }
121
122 reg = readl_relaxed(priv->sensor);
123 reg = (reg >> THERMAL_TEMP_OFFSET) & THERMAL_TEMP_MASK;
124 *temp = (3153000000UL - (10000000UL*reg)) / 13825;
125 return 0;
126}
127
128static struct thermal_zone_device_ops ops = {
129 .get_temp = armada_get_temp,
130};
131
132static const struct armada_thermal_ops armadaxp_ops = {
133 .init_sensor = armadaxp_init_sensor,
134};
135
136static const struct armada_thermal_ops armada370_ops = {
137 .is_valid = armada_is_valid,
138 .init_sensor = armada370_init_sensor,
139};
140
141static const struct of_device_id armada_thermal_id_table[] = {
142 {
143 .compatible = "marvell,armadaxp-thermal",
144 .data = &armadaxp_ops,
145 },
146 {
147 .compatible = "marvell,armada370-thermal",
148 .data = &armada370_ops,
149 },
150 {
151 /* sentinel */
152 },
153};
154MODULE_DEVICE_TABLE(of, armada_thermal_id_table);
155
156static int armada_thermal_probe(struct platform_device *pdev)
157{
158 struct thermal_zone_device *thermal;
159 const struct of_device_id *match;
160 struct armada_thermal_priv *priv;
161 struct resource *res;
162
163 match = of_match_device(armada_thermal_id_table, &pdev->dev);
164 if (!match)
165 return -ENODEV;
166
167 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
168 if (!priv)
169 return -ENOMEM;
170
171 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
172 if (!res) {
173 dev_err(&pdev->dev, "Failed to get platform resource\n");
174 return -ENODEV;
175 }
176
177 priv->sensor = devm_ioremap_resource(&pdev->dev, res);
178 if (IS_ERR(priv->sensor))
179 return PTR_ERR(priv->sensor);
180
181 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
182 if (!res) {
183 dev_err(&pdev->dev, "Failed to get platform resource\n");
184 return -ENODEV;
185 }
186
187 priv->control = devm_ioremap_resource(&pdev->dev, res);
188 if (IS_ERR(priv->control))
189 return PTR_ERR(priv->control);
190
191 priv->ops = (struct armada_thermal_ops *)match->data;
192 priv->ops->init_sensor(priv);
193
194 thermal = thermal_zone_device_register("armada_thermal", 0, 0,
195 priv, &ops, NULL, 0, 0);
196 if (IS_ERR(thermal)) {
197 dev_err(&pdev->dev,
198 "Failed to register thermal zone device\n");
199 return PTR_ERR(thermal);
200 }
201
202 platform_set_drvdata(pdev, thermal);
203
204 return 0;
205}
206
207static int armada_thermal_exit(struct platform_device *pdev)
208{
209 struct thermal_zone_device *armada_thermal =
210 platform_get_drvdata(pdev);
211
212 thermal_zone_device_unregister(armada_thermal);
213 platform_set_drvdata(pdev, NULL);
214
215 return 0;
216}
217
218static struct platform_driver armada_thermal_driver = {
219 .probe = armada_thermal_probe,
220 .remove = armada_thermal_exit,
221 .driver = {
222 .name = "armada_thermal",
223 .owner = THIS_MODULE,
224 .of_match_table = of_match_ptr(armada_thermal_id_table),
225 },
226};
227
228module_platform_driver(armada_thermal_driver);
229
230MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@free-electrons.com>");
231MODULE_DESCRIPTION("Armada 370/XP thermal driver");
232MODULE_LICENSE("GPL v2");