1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
36MODULE_LICENSE("GPL");
37MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
38MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family.");
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53static int w1_strong_pullup = 1;
54module_param_named(strong_pullup, w1_strong_pullup, int, 0);
55
56
57static ssize_t w1_therm_read(struct device *device,
58 struct device_attribute *attr, char *buf);
59
60static struct device_attribute w1_therm_attr =
61 __ATTR(w1_slave, S_IRUGO, w1_therm_read, NULL);
62
63static int w1_therm_add_slave(struct w1_slave *sl)
64{
65 return device_create_file(&sl->dev, &w1_therm_attr);
66}
67
68static void w1_therm_remove_slave(struct w1_slave *sl)
69{
70 device_remove_file(&sl->dev, &w1_therm_attr);
71}
72
73static struct w1_family_ops w1_therm_fops = {
74 .add_slave = w1_therm_add_slave,
75 .remove_slave = w1_therm_remove_slave,
76};
77
78static struct w1_family w1_therm_family_DS18S20 = {
79 .fid = W1_THERM_DS18S20,
80 .fops = &w1_therm_fops,
81};
82
83static struct w1_family w1_therm_family_DS18B20 = {
84 .fid = W1_THERM_DS18B20,
85 .fops = &w1_therm_fops,
86};
87
88static struct w1_family w1_therm_family_DS1822 = {
89 .fid = W1_THERM_DS1822,
90 .fops = &w1_therm_fops,
91};
92
93static struct w1_family w1_therm_family_DS28EA00 = {
94 .fid = W1_THERM_DS28EA00,
95 .fops = &w1_therm_fops,
96};
97
98static struct w1_family w1_therm_family_DS1825 = {
99 .fid = W1_THERM_DS1825,
100 .fops = &w1_therm_fops,
101};
102
103struct w1_therm_family_converter
104{
105 u8 broken;
106 u16 reserved;
107 struct w1_family *f;
108 int (*convert)(u8 rom[9]);
109};
110
111
112static inline int w1_DS18B20_convert_temp(u8 rom[9]);
113static inline int w1_DS18S20_convert_temp(u8 rom[9]);
114
115static struct w1_therm_family_converter w1_therm_families[] = {
116 {
117 .f = &w1_therm_family_DS18S20,
118 .convert = w1_DS18S20_convert_temp
119 },
120 {
121 .f = &w1_therm_family_DS1822,
122 .convert = w1_DS18B20_convert_temp
123 },
124 {
125 .f = &w1_therm_family_DS18B20,
126 .convert = w1_DS18B20_convert_temp
127 },
128 {
129 .f = &w1_therm_family_DS28EA00,
130 .convert = w1_DS18B20_convert_temp
131 },
132 {
133 .f = &w1_therm_family_DS1825,
134 .convert = w1_DS18B20_convert_temp
135 }
136};
137
138static inline int w1_DS18B20_convert_temp(u8 rom[9])
139{
140 s16 t = le16_to_cpup((__le16 *)rom);
141 return t*1000/16;
142}
143
144static inline int w1_DS18S20_convert_temp(u8 rom[9])
145{
146 int t, h;
147
148 if (!rom[7])
149 return 0;
150
151 if (rom[1] == 0)
152 t = ((s32)rom[0] >> 1)*1000;
153 else
154 t = 1000*(-1*(s32)(0x100-rom[0]) >> 1);
155
156 t -= 250;
157 h = 1000*((s32)rom[7] - (s32)rom[6]);
158 h /= (s32)rom[7];
159 t += h;
160
161 return t;
162}
163
164static inline int w1_convert_temp(u8 rom[9], u8 fid)
165{
166 int i;
167
168 for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
169 if (w1_therm_families[i].f->fid == fid)
170 return w1_therm_families[i].convert(rom);
171
172 return 0;
173}
174
175
176static ssize_t w1_therm_read(struct device *device,
177 struct device_attribute *attr, char *buf)
178{
179 struct w1_slave *sl = dev_to_w1_slave(device);
180 struct w1_master *dev = sl->master;
181 u8 rom[9], crc, verdict, external_power;
182 int i, max_trying = 10;
183 ssize_t c = PAGE_SIZE;
184
185 i = mutex_lock_interruptible(&dev->bus_mutex);
186 if (i != 0)
187 return i;
188
189 memset(rom, 0, sizeof(rom));
190
191 while (max_trying--) {
192
193 verdict = 0;
194 crc = 0;
195
196 if (!w1_reset_select_slave(sl)) {
197 int count = 0;
198 unsigned int tm = 750;
199 unsigned long sleep_rem;
200
201 w1_write_8(dev, W1_READ_PSUPPLY);
202 external_power = w1_read_8(dev);
203
204 if (w1_reset_select_slave(sl))
205 continue;
206
207
208 if (w1_strong_pullup == 2 ||
209 (!external_power && w1_strong_pullup))
210 w1_next_pullup(dev, tm);
211
212 w1_write_8(dev, W1_CONVERT_TEMP);
213
214 if (external_power) {
215 mutex_unlock(&dev->bus_mutex);
216
217 sleep_rem = msleep_interruptible(tm);
218 if (sleep_rem != 0)
219 return -EINTR;
220
221 i = mutex_lock_interruptible(&dev->bus_mutex);
222 if (i != 0)
223 return i;
224 } else if (!w1_strong_pullup) {
225 sleep_rem = msleep_interruptible(tm);
226 if (sleep_rem != 0) {
227 mutex_unlock(&dev->bus_mutex);
228 return -EINTR;
229 }
230 }
231
232 if (!w1_reset_select_slave(sl)) {
233
234 w1_write_8(dev, W1_READ_SCRATCHPAD);
235 if ((count = w1_read_block(dev, rom, 9)) != 9) {
236 dev_warn(device, "w1_read_block() "
237 "returned %u instead of 9.\n",
238 count);
239 }
240
241 crc = w1_calc_crc8(rom, 8);
242
243 if (rom[8] == crc)
244 verdict = 1;
245 }
246 }
247
248 if (verdict)
249 break;
250 }
251
252 for (i = 0; i < 9; ++i)
253 c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", rom[i]);
254 c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n",
255 crc, (verdict) ? "YES" : "NO");
256 if (verdict)
257 memcpy(sl->rom, rom, sizeof(sl->rom));
258 else
259 dev_warn(device, "Read failed CRC check\n");
260
261 for (i = 0; i < 9; ++i)
262 c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", sl->rom[i]);
263
264 c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n",
265 w1_convert_temp(rom, sl->family->fid));
266 mutex_unlock(&dev->bus_mutex);
267
268 return PAGE_SIZE - c;
269}
270
271static int __init w1_therm_init(void)
272{
273 int err, i;
274
275 for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i) {
276 err = w1_register_family(w1_therm_families[i].f);
277 if (err)
278 w1_therm_families[i].broken = 1;
279 }
280
281 return 0;
282}
283
284static void __exit w1_therm_fini(void)
285{
286 int i;
287
288 for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
289 if (!w1_therm_families[i].broken)
290 w1_unregister_family(w1_therm_families[i].f);
291}
292
293module_init(w1_therm_init);
294module_exit(w1_therm_fini);
295