Merge branch 'kconfig' of git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / w1 / slaves / w1_therm.c
1 /*
2  *      w1_therm.c
3  *
4  * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>
5  *
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the therms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21
22 #include <asm/types.h>
23
24 #include <linux/kernel.h>
25 #include <linux/module.h>
26 #include <linux/moduleparam.h>
27 #include <linux/sched.h>
28 #include <linux/device.h>
29 #include <linux/types.h>
30 #include <linux/delay.h>
31
32 #include "../w1.h"
33 #include "../w1_int.h"
34 #include "../w1_family.h"
35
36 MODULE_LICENSE("GPL");
37 MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
38 MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family.");
39 MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18S20));
40 MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1822));
41 MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18B20));
42 MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1825));
43 MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS28EA00));
44
45 /* Allow the strong pullup to be disabled, but default to enabled.
46  * If it was disabled a parasite powered device might not get the require
47  * current to do a temperature conversion.  If it is enabled parasite powered
48  * devices have a better chance of getting the current required.
49  * In case the parasite power-detection is not working (seems to be the case
50  * for some DS18S20) the strong pullup can also be forced, regardless of the
51  * power state of the devices.
52  *
53  * Summary of options:
54  * - strong_pullup = 0  Disable strong pullup completely
55  * - strong_pullup = 1  Enable automatic strong pullup detection
56  * - strong_pullup = 2  Force strong pullup
57  */
58 static int w1_strong_pullup = 1;
59 module_param_named(strong_pullup, w1_strong_pullup, int, 0);
60
61
62 static ssize_t w1_slave_show(struct device *device,
63         struct device_attribute *attr, char *buf);
64
65 static DEVICE_ATTR_RO(w1_slave);
66
67 static struct attribute *w1_therm_attrs[] = {
68         &dev_attr_w1_slave.attr,
69         NULL,
70 };
71 ATTRIBUTE_GROUPS(w1_therm);
72
73 static struct w1_family_ops w1_therm_fops = {
74         .groups         = w1_therm_groups,
75 };
76
77 static struct w1_family w1_therm_family_DS18S20 = {
78         .fid = W1_THERM_DS18S20,
79         .fops = &w1_therm_fops,
80 };
81
82 static struct w1_family w1_therm_family_DS18B20 = {
83         .fid = W1_THERM_DS18B20,
84         .fops = &w1_therm_fops,
85 };
86
87 static struct w1_family w1_therm_family_DS1822 = {
88         .fid = W1_THERM_DS1822,
89         .fops = &w1_therm_fops,
90 };
91
92 static struct w1_family w1_therm_family_DS28EA00 = {
93         .fid = W1_THERM_DS28EA00,
94         .fops = &w1_therm_fops,
95 };
96
97 static struct w1_family w1_therm_family_DS1825 = {
98         .fid = W1_THERM_DS1825,
99         .fops = &w1_therm_fops,
100 };
101
102 struct w1_therm_family_converter
103 {
104         u8                      broken;
105         u16                     reserved;
106         struct w1_family        *f;
107         int                     (*convert)(u8 rom[9]);
108 };
109
110 /* The return value is millidegrees Centigrade. */
111 static inline int w1_DS18B20_convert_temp(u8 rom[9]);
112 static inline int w1_DS18S20_convert_temp(u8 rom[9]);
113
114 static struct w1_therm_family_converter w1_therm_families[] = {
115         {
116                 .f              = &w1_therm_family_DS18S20,
117                 .convert        = w1_DS18S20_convert_temp
118         },
119         {
120                 .f              = &w1_therm_family_DS1822,
121                 .convert        = w1_DS18B20_convert_temp
122         },
123         {
124                 .f              = &w1_therm_family_DS18B20,
125                 .convert        = w1_DS18B20_convert_temp
126         },
127         {
128                 .f              = &w1_therm_family_DS28EA00,
129                 .convert        = w1_DS18B20_convert_temp
130         },
131         {
132                 .f              = &w1_therm_family_DS1825,
133                 .convert        = w1_DS18B20_convert_temp
134         }
135 };
136
137 static inline int w1_DS18B20_convert_temp(u8 rom[9])
138 {
139         s16 t = le16_to_cpup((__le16 *)rom);
140         return t*1000/16;
141 }
142
143 static inline int w1_DS18S20_convert_temp(u8 rom[9])
144 {
145         int t, h;
146
147         if (!rom[7])
148                 return 0;
149
150         if (rom[1] == 0)
151                 t = ((s32)rom[0] >> 1)*1000;
152         else
153                 t = 1000*(-1*(s32)(0x100-rom[0]) >> 1);
154
155         t -= 250;
156         h = 1000*((s32)rom[7] - (s32)rom[6]);
157         h /= (s32)rom[7];
158         t += h;
159
160         return t;
161 }
162
163 static inline int w1_convert_temp(u8 rom[9], u8 fid)
164 {
165         int i;
166
167         for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
168                 if (w1_therm_families[i].f->fid == fid)
169                         return w1_therm_families[i].convert(rom);
170
171         return 0;
172 }
173
174
175 static ssize_t w1_slave_show(struct device *device,
176         struct device_attribute *attr, char *buf)
177 {
178         struct w1_slave *sl = dev_to_w1_slave(device);
179         struct w1_master *dev = sl->master;
180         u8 rom[9], crc, verdict, external_power;
181         int i, max_trying = 10;
182         ssize_t c = PAGE_SIZE;
183
184         i = mutex_lock_interruptible(&dev->bus_mutex);
185         if (i != 0)
186                 return i;
187
188         memset(rom, 0, sizeof(rom));
189
190         while (max_trying--) {
191
192                 verdict = 0;
193                 crc = 0;
194
195                 if (!w1_reset_select_slave(sl)) {
196                         int count = 0;
197                         unsigned int tm = 750;
198                         unsigned long sleep_rem;
199
200                         w1_write_8(dev, W1_READ_PSUPPLY);
201                         external_power = w1_read_8(dev);
202
203                         if (w1_reset_select_slave(sl))
204                                 continue;
205
206                         /* 750ms strong pullup (or delay) after the convert */
207                         if (w1_strong_pullup == 2 ||
208                                         (!external_power && w1_strong_pullup))
209                                 w1_next_pullup(dev, tm);
210
211                         w1_write_8(dev, W1_CONVERT_TEMP);
212
213                         if (external_power) {
214                                 mutex_unlock(&dev->bus_mutex);
215
216                                 sleep_rem = msleep_interruptible(tm);
217                                 if (sleep_rem != 0)
218                                         return -EINTR;
219
220                                 i = mutex_lock_interruptible(&dev->bus_mutex);
221                                 if (i != 0)
222                                         return i;
223                         } else if (!w1_strong_pullup) {
224                                 sleep_rem = msleep_interruptible(tm);
225                                 if (sleep_rem != 0) {
226                                         mutex_unlock(&dev->bus_mutex);
227                                         return -EINTR;
228                                 }
229                         }
230
231                         if (!w1_reset_select_slave(sl)) {
232
233                                 w1_write_8(dev, W1_READ_SCRATCHPAD);
234                                 if ((count = w1_read_block(dev, rom, 9)) != 9) {
235                                         dev_warn(device, "w1_read_block() "
236                                                 "returned %u instead of 9.\n",
237                                                 count);
238                                 }
239
240                                 crc = w1_calc_crc8(rom, 8);
241
242                                 if (rom[8] == crc)
243                                         verdict = 1;
244                         }
245                 }
246
247                 if (verdict)
248                         break;
249         }
250
251         for (i = 0; i < 9; ++i)
252                 c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", rom[i]);
253         c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n",
254                            crc, (verdict) ? "YES" : "NO");
255         if (verdict)
256                 memcpy(sl->rom, rom, sizeof(sl->rom));
257         else
258                 dev_warn(device, "Read failed CRC check\n");
259
260         for (i = 0; i < 9; ++i)
261                 c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", sl->rom[i]);
262
263         c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n",
264                 w1_convert_temp(rom, sl->family->fid));
265         mutex_unlock(&dev->bus_mutex);
266
267         return PAGE_SIZE - c;
268 }
269
270 static int __init w1_therm_init(void)
271 {
272         int err, i;
273
274         for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i) {
275                 err = w1_register_family(w1_therm_families[i].f);
276                 if (err)
277                         w1_therm_families[i].broken = 1;
278         }
279
280         return 0;
281 }
282
283 static void __exit w1_therm_fini(void)
284 {
285         int i;
286
287         for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
288                 if (!w1_therm_families[i].broken)
289                         w1_unregister_family(w1_therm_families[i].f);
290 }
291
292 module_init(w1_therm_init);
293 module_exit(w1_therm_fini);