aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>2012-07-20 20:53:48 -0400
committerZhang Rui <rui.zhang@intel.com>2012-09-24 02:44:37 -0400
commit1e426ffddf2f158367eb6c7b8eb563c814a43283 (patch)
tree7965a5e2d0dea0191ee259d6f1fd671abb8f2528 /drivers
parent79a49168b595f5400ed3108cd57e90f5bbe144ca (diff)
thermal: add Renesas R-Car thermal sensor support
This patch add basic Renesas R-Car thermal sensor support. It was tested on R-Car H1 Marzen board. Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Cc: Len Brown <len.brown@intel.com> Cc: Joe Perches <joe@perches.com> Cc: Jean Delvare <khali@linux-fr.org> Cc: Guenter Roeck <guenter.roeck@ericsson.com> Cc: Magnus Damm <magnus.damm@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/thermal/Kconfig8
-rw-r--r--drivers/thermal/Makefile3
-rw-r--r--drivers/thermal/rcar_thermal.c260
3 files changed, 270 insertions, 1 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 3ab2bd540b54..7dd8c34552e1 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -27,3 +27,11 @@ config SPEAR_THERMAL
27 help 27 help
28 Enable this to plug the SPEAr thermal sensor driver into the Linux 28 Enable this to plug the SPEAr thermal sensor driver into the Linux
29 thermal framework 29 thermal framework
30
31config RCAR_THERMAL
32 tristate "Renesas R-Car thermal driver"
33 depends on THERMAL
34 depends on ARCH_SHMOBILE
35 help
36 Enable this to plug the R-Car thermal sensor driver into the Linux
37 thermal framework
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index a9fff0bf4b14..fd9369aeb96f 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -3,4 +3,5 @@
3# 3#
4 4
5obj-$(CONFIG_THERMAL) += thermal_sys.o 5obj-$(CONFIG_THERMAL) += thermal_sys.o
6obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o \ No newline at end of file 6obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
7obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c
new file mode 100644
index 000000000000..d4452716aaab
--- /dev/null
+++ b/drivers/thermal/rcar_thermal.c
@@ -0,0 +1,260 @@
1/*
2 * R-Car THS/TSC thermal sensor driver
3 *
4 * Copyright (C) 2012 Renesas Solutions Corp.
5 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 */
20#include <linux/delay.h>
21#include <linux/err.h>
22#include <linux/io.h>
23#include <linux/module.h>
24#include <linux/platform_device.h>
25#include <linux/slab.h>
26#include <linux/spinlock.h>
27#include <linux/thermal.h>
28
29#define THSCR 0x2c
30#define THSSR 0x30
31
32/* THSCR */
33#define CPTAP 0xf
34
35/* THSSR */
36#define CTEMP 0x3f
37
38
39struct rcar_thermal_priv {
40 void __iomem *base;
41 struct device *dev;
42 spinlock_t lock;
43 u32 comp;
44};
45
46/*
47 * basic functions
48 */
49static u32 rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg)
50{
51 unsigned long flags;
52 u32 ret;
53
54 spin_lock_irqsave(&priv->lock, flags);
55
56 ret = ioread32(priv->base + reg);
57
58 spin_unlock_irqrestore(&priv->lock, flags);
59
60 return ret;
61}
62
63#if 0 /* no user at this point */
64static void rcar_thermal_write(struct rcar_thermal_priv *priv,
65 u32 reg, u32 data)
66{
67 unsigned long flags;
68
69 spin_lock_irqsave(&priv->lock, flags);
70
71 iowrite32(data, priv->base + reg);
72
73 spin_unlock_irqrestore(&priv->lock, flags);
74}
75#endif
76
77static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg,
78 u32 mask, u32 data)
79{
80 unsigned long flags;
81 u32 val;
82
83 spin_lock_irqsave(&priv->lock, flags);
84
85 val = ioread32(priv->base + reg);
86 val &= ~mask;
87 val |= (data & mask);
88 iowrite32(val, priv->base + reg);
89
90 spin_unlock_irqrestore(&priv->lock, flags);
91}
92
93/*
94 * zone device functions
95 */
96static int rcar_thermal_get_temp(struct thermal_zone_device *zone,
97 unsigned long *temp)
98{
99 struct rcar_thermal_priv *priv = zone->devdata;
100 int val, min, max, tmp;
101
102 tmp = -200; /* default */
103 while (1) {
104 if (priv->comp < 1 || priv->comp > 12) {
105 dev_err(priv->dev,
106 "THSSR invalid data (%d)\n", priv->comp);
107 priv->comp = 4; /* for next thermal */
108 return -EINVAL;
109 }
110
111 /*
112 * THS comparator offset and the reference temperature
113 *
114 * Comparator | reference | Temperature field
115 * offset | temperature | measurement
116 * | (degrees C) | (degrees C)
117 * -------------+---------------+-------------------
118 * 1 | -45 | -45 to -30
119 * 2 | -30 | -30 to -15
120 * 3 | -15 | -15 to 0
121 * 4 | 0 | 0 to +15
122 * 5 | +15 | +15 to +30
123 * 6 | +30 | +30 to +45
124 * 7 | +45 | +45 to +60
125 * 8 | +60 | +60 to +75
126 * 9 | +75 | +75 to +90
127 * 10 | +90 | +90 to +105
128 * 11 | +105 | +105 to +120
129 * 12 | +120 | +120 to +135
130 */
131
132 /* calculate thermal limitation */
133 min = (priv->comp * 15) - 60;
134 max = min + 15;
135
136 /*
137 * we need to wait 300us after changing comparator offset
138 * to get stable temperature.
139 * see "Usage Notes" on datasheet
140 */
141 rcar_thermal_bset(priv, THSCR, CPTAP, priv->comp);
142 udelay(300);
143
144 /* calculate current temperature */
145 val = rcar_thermal_read(priv, THSSR) & CTEMP;
146 val = (val * 5) - 65;
147
148 dev_dbg(priv->dev, "comp/min/max/val = %d/%d/%d/%d\n",
149 priv->comp, min, max, val);
150
151 /*
152 * If val is same as min/max, then,
153 * it should try again on next comparator.
154 * But the val might be correct temperature.
155 * Keep it on "tmp" and compare with next val.
156 */
157 if (tmp == val)
158 break;
159
160 if (val <= min) {
161 tmp = min;
162 priv->comp--; /* try again */
163 } else if (val >= max) {
164 tmp = max;
165 priv->comp++; /* try again */
166 } else {
167 tmp = val;
168 break;
169 }
170 }
171
172 *temp = tmp;
173 return 0;
174}
175
176static struct thermal_zone_device_ops rcar_thermal_zone_ops = {
177 .get_temp = rcar_thermal_get_temp,
178};
179
180/*
181 * platform functions
182 */
183static int rcar_thermal_probe(struct platform_device *pdev)
184{
185 struct thermal_zone_device *zone;
186 struct rcar_thermal_priv *priv;
187 struct resource *res;
188 int ret;
189
190 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
191 if (!res) {
192 dev_err(&pdev->dev, "Could not get platform resource\n");
193 return -ENODEV;
194 }
195
196 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
197 if (!priv) {
198 dev_err(&pdev->dev, "Could not allocate priv\n");
199 return -ENOMEM;
200 }
201
202 priv->comp = 4; /* basic setup */
203 priv->dev = &pdev->dev;
204 spin_lock_init(&priv->lock);
205 priv->base = devm_ioremap_nocache(&pdev->dev,
206 res->start, resource_size(res));
207 if (!priv->base) {
208 dev_err(&pdev->dev, "Unable to ioremap thermal register\n");
209 ret = -ENOMEM;
210 goto error_free_priv;
211 }
212
213 zone = thermal_zone_device_register("rcar_thermal", 0, priv,
214 &rcar_thermal_zone_ops, 0, 0);
215 if (IS_ERR(zone)) {
216 dev_err(&pdev->dev, "thermal zone device is NULL\n");
217 ret = PTR_ERR(zone);
218 goto error_iounmap;
219 }
220
221 platform_set_drvdata(pdev, zone);
222
223 dev_info(&pdev->dev, "proved\n");
224
225 return 0;
226
227error_iounmap:
228 devm_iounmap(&pdev->dev, priv->base);
229error_free_priv:
230 devm_kfree(&pdev->dev, priv);
231
232 return ret;
233}
234
235static int rcar_thermal_remove(struct platform_device *pdev)
236{
237 struct thermal_zone_device *zone = platform_get_drvdata(pdev);
238 struct rcar_thermal_priv *priv = zone->devdata;
239
240 thermal_zone_device_unregister(zone);
241 platform_set_drvdata(pdev, NULL);
242
243 devm_iounmap(&pdev->dev, priv->base);
244 devm_kfree(&pdev->dev, priv);
245
246 return 0;
247}
248
249static struct platform_driver rcar_thermal_driver = {
250 .driver = {
251 .name = "rcar_thermal",
252 },
253 .probe = rcar_thermal_probe,
254 .remove = rcar_thermal_remove,
255};
256module_platform_driver(rcar_thermal_driver);
257
258MODULE_LICENSE("GPL");
259MODULE_DESCRIPTION("R-Car THS/TSC thermal sensor driver");
260MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");