summaryrefslogtreecommitdiffstats
path: root/drivers/thermal
diff options
context:
space:
mode:
authorBaoyou Xie <baoyou.xie@linaro.org>2017-02-06 19:56:41 -0500
committerEduardo Valentin <edubezval@gmail.com>2017-02-18 19:36:12 -0500
commit50fdd36f336a03b9486f1cd4b0d85fcb361baf60 (patch)
tree0e237ed828a37a8410cefbc8ef85a659e8440c99 /drivers/thermal
parente0fa56489f21e319d0aa9654834209faa3eb481d (diff)
thermal: zx2967: add thermal driver for ZTE's zx2967 family
This patch adds thermal driver for ZTE's zx2967 family. Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org> Reviewed-by: Mathieu Poirier <mathieu.poirier@linaro.org> Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
Diffstat (limited to 'drivers/thermal')
-rw-r--r--drivers/thermal/Kconfig8
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/zx2967_thermal.c258
3 files changed, 267 insertions, 0 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 3912d24a07b1..776b34396144 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -445,4 +445,12 @@ depends on (ARCH_QCOM && OF) || COMPILE_TEST
445source "drivers/thermal/qcom/Kconfig" 445source "drivers/thermal/qcom/Kconfig"
446endmenu 446endmenu
447 447
448config ZX2967_THERMAL
449 tristate "Thermal sensors on zx2967 SoC"
450 depends on ARCH_ZX || COMPILE_TEST
451 help
452 Enable the zx2967 thermal sensors driver, which supports
453 the primitive temperature sensor embedded in zx2967 SoCs.
454 This sensor generates the real time die temperature.
455
448endif 456endif
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index adcf3cc90859..7adae2029355 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -57,3 +57,4 @@ obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/
57obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o 57obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
58obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o 58obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
59obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o 59obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
60obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o
diff --git a/drivers/thermal/zx2967_thermal.c b/drivers/thermal/zx2967_thermal.c
new file mode 100644
index 000000000000..a5670ad2cfc8
--- /dev/null
+++ b/drivers/thermal/zx2967_thermal.c
@@ -0,0 +1,258 @@
1/*
2 * ZTE's zx2967 family thermal sensor driver
3 *
4 * Copyright (C) 2017 ZTE Ltd.
5 *
6 * Author: Baoyou Xie <baoyou.xie@linaro.org>
7 *
8 * License terms: GNU General Public License (GPL) version 2
9 */
10
11#include <linux/clk.h>
12#include <linux/device.h>
13#include <linux/err.h>
14#include <linux/iopoll.h>
15#include <linux/module.h>
16#include <linux/platform_device.h>
17#include <linux/thermal.h>
18
19/* Power Mode: 0->low 1->high */
20#define ZX2967_THERMAL_POWER_MODE 0
21#define ZX2967_POWER_MODE_LOW 0
22#define ZX2967_POWER_MODE_HIGH 1
23
24/* DCF Control Register */
25#define ZX2967_THERMAL_DCF 0x4
26#define ZX2967_DCF_EN BIT(1)
27#define ZX2967_DCF_FREEZE BIT(0)
28
29/* Selection Register */
30#define ZX2967_THERMAL_SEL 0x8
31
32/* Control Register */
33#define ZX2967_THERMAL_CTRL 0x10
34
35#define ZX2967_THERMAL_READY BIT(12)
36#define ZX2967_THERMAL_TEMP_MASK GENMASK(11, 0)
37#define ZX2967_THERMAL_ID_MASK 0x18
38#define ZX2967_THERMAL_ID 0x10
39
40#define ZX2967_GET_TEMP_TIMEOUT_US (100 * 1024)
41
42/**
43 * struct zx2967_thermal_priv - zx2967 thermal sensor private structure
44 * @tzd: struct thermal_zone_device where the sensor is registered
45 * @lock: prevents read sensor in parallel
46 * @clk_topcrm: topcrm clk structure
47 * @clk_apb: apb clk structure
48 * @regs: pointer to base address of the thermal sensor
49 */
50
51struct zx2967_thermal_priv {
52 struct thermal_zone_device *tzd;
53 struct mutex lock;
54 struct clk *clk_topcrm;
55 struct clk *clk_apb;
56 void __iomem *regs;
57 struct device *dev;
58};
59
60static int zx2967_thermal_get_temp(void *data, int *temp)
61{
62 void __iomem *regs;
63 struct zx2967_thermal_priv *priv = data;
64 u32 val;
65 int ret;
66
67 if (!priv->tzd)
68 return -EAGAIN;
69
70 regs = priv->regs;
71 mutex_lock(&priv->lock);
72 writel_relaxed(ZX2967_POWER_MODE_LOW,
73 regs + ZX2967_THERMAL_POWER_MODE);
74 writel_relaxed(ZX2967_DCF_EN, regs + ZX2967_THERMAL_DCF);
75
76 val = readl_relaxed(regs + ZX2967_THERMAL_SEL);
77 val &= ~ZX2967_THERMAL_ID_MASK;
78 val |= ZX2967_THERMAL_ID;
79 writel_relaxed(val, regs + ZX2967_THERMAL_SEL);
80
81 /*
82 * Must wait for a while, surely it's a bit odd.
83 * otherwise temperature value we got has a few deviation, even if
84 * the THERMAL_READY bit is set.
85 */
86 usleep_range(100, 300);
87 ret = readx_poll_timeout(readl, regs + ZX2967_THERMAL_CTRL,
88 val, val & ZX2967_THERMAL_READY, 300,
89 ZX2967_GET_TEMP_TIMEOUT_US);
90 if (ret) {
91 dev_err(priv->dev, "Thermal sensor data timeout\n");
92 goto unlock;
93 }
94
95 writel_relaxed(ZX2967_DCF_FREEZE | ZX2967_DCF_EN,
96 regs + ZX2967_THERMAL_DCF);
97 val = readl_relaxed(regs + ZX2967_THERMAL_CTRL)
98 & ZX2967_THERMAL_TEMP_MASK;
99 writel_relaxed(ZX2967_POWER_MODE_HIGH,
100 regs + ZX2967_THERMAL_POWER_MODE);
101
102 /*
103 * Calculate temperature
104 * In dts, slope is multiplied by 1000.
105 */
106 *temp = DIV_ROUND_CLOSEST(((s32)val + priv->tzd->tzp->offset) * 1000,
107 priv->tzd->tzp->slope);
108
109unlock:
110 mutex_unlock(&priv->lock);
111 return ret;
112}
113
114static struct thermal_zone_of_device_ops zx2967_of_thermal_ops = {
115 .get_temp = zx2967_thermal_get_temp,
116};
117
118static int zx2967_thermal_probe(struct platform_device *pdev)
119{
120 struct zx2967_thermal_priv *priv;
121 struct resource *res;
122 int ret;
123
124 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
125 if (!priv)
126 return -ENOMEM;
127
128 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
129 priv->regs = devm_ioremap_resource(&pdev->dev, res);
130 if (IS_ERR(priv->regs))
131 return PTR_ERR(priv->regs);
132
133 priv->clk_topcrm = devm_clk_get(&pdev->dev, "topcrm");
134 if (IS_ERR(priv->clk_topcrm)) {
135 ret = PTR_ERR(priv->clk_topcrm);
136 dev_err(&pdev->dev, "failed to get topcrm clock: %d\n", ret);
137 return ret;
138 }
139
140 ret = clk_prepare_enable(priv->clk_topcrm);
141 if (ret) {
142 dev_err(&pdev->dev, "failed to enable topcrm clock: %d\n",
143 ret);
144 return ret;
145 }
146
147 priv->clk_apb = devm_clk_get(&pdev->dev, "apb");
148 if (IS_ERR(priv->clk_apb)) {
149 ret = PTR_ERR(priv->clk_apb);
150 dev_err(&pdev->dev, "failed to get apb clock: %d\n", ret);
151 goto disable_clk_topcrm;
152 }
153
154 ret = clk_prepare_enable(priv->clk_apb);
155 if (ret) {
156 dev_err(&pdev->dev, "failed to enable apb clock: %d\n",
157 ret);
158 goto disable_clk_topcrm;
159 }
160
161 mutex_init(&priv->lock);
162 priv->tzd = thermal_zone_of_sensor_register(&pdev->dev,
163 0, priv, &zx2967_of_thermal_ops);
164
165 if (IS_ERR(priv->tzd)) {
166 ret = PTR_ERR(priv->tzd);
167 dev_err(&pdev->dev, "failed to register sensor: %d\n", ret);
168 goto disable_clk_all;
169 }
170
171 if (priv->tzd->tzp->slope == 0) {
172 thermal_zone_of_sensor_unregister(&pdev->dev, priv->tzd);
173 dev_err(&pdev->dev, "coefficients of sensor is invalid\n");
174 ret = -EINVAL;
175 goto disable_clk_all;
176 }
177
178 priv->dev = &pdev->dev;
179 platform_set_drvdata(pdev, priv);
180
181 return 0;
182
183disable_clk_all:
184 clk_disable_unprepare(priv->clk_apb);
185disable_clk_topcrm:
186 clk_disable_unprepare(priv->clk_topcrm);
187 return ret;
188}
189
190static int zx2967_thermal_exit(struct platform_device *pdev)
191{
192 struct zx2967_thermal_priv *priv = platform_get_drvdata(pdev);
193
194 thermal_zone_of_sensor_unregister(&pdev->dev, priv->tzd);
195 clk_disable_unprepare(priv->clk_topcrm);
196 clk_disable_unprepare(priv->clk_apb);
197
198 return 0;
199}
200
201static const struct of_device_id zx2967_thermal_id_table[] = {
202 { .compatible = "zte,zx296718-thermal" },
203 {}
204};
205MODULE_DEVICE_TABLE(of, zx2967_thermal_id_table);
206
207#ifdef CONFIG_PM_SLEEP
208static int zx2967_thermal_suspend(struct device *dev)
209{
210 struct platform_device *pdev = to_platform_device(dev);
211 struct zx2967_thermal_priv *priv = platform_get_drvdata(pdev);
212
213 if (priv && priv->clk_topcrm)
214 clk_disable_unprepare(priv->clk_topcrm);
215
216 if (priv && priv->clk_apb)
217 clk_disable_unprepare(priv->clk_apb);
218
219 return 0;
220}
221
222static int zx2967_thermal_resume(struct device *dev)
223{
224 struct platform_device *pdev = to_platform_device(dev);
225 struct zx2967_thermal_priv *priv = platform_get_drvdata(pdev);
226 int error;
227
228 error = clk_prepare_enable(priv->clk_topcrm);
229 if (error)
230 return error;
231
232 error = clk_prepare_enable(priv->clk_apb);
233 if (error) {
234 clk_disable_unprepare(priv->clk_topcrm);
235 return error;
236 }
237
238 return 0;
239}
240#endif
241
242static SIMPLE_DEV_PM_OPS(zx2967_thermal_pm_ops,
243 zx2967_thermal_suspend, zx2967_thermal_resume);
244
245static struct platform_driver zx2967_thermal_driver = {
246 .probe = zx2967_thermal_probe,
247 .remove = zx2967_thermal_exit,
248 .driver = {
249 .name = "zx2967_thermal",
250 .of_match_table = zx2967_thermal_id_table,
251 .pm = &zx2967_thermal_pm_ops,
252 },
253};
254module_platform_driver(zx2967_thermal_driver);
255
256MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
257MODULE_DESCRIPTION("ZTE zx2967 thermal driver");
258MODULE_LICENSE("GPL v2");