Commit | Line | Data |
---|---|---|
f19b121e | 1 | /* |
2 | * 1-wire busmaster driver for DS1WM and ASICs with embedded DS1WMs | |
3 | * such as HP iPAQs (including h5xxx, h2200, and devices with ASIC3 | |
4 | * like hx4700). | |
5 | * | |
6 | * Copyright (c) 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> | |
7 | * Copyright (c) 2004-2007, Matt Reimer <mreimer@vpop.net> | |
8 | * | |
9 | * Use consistent with the GNU GPL is permitted, | |
10 | * provided that this copyright notice is | |
11 | * preserved in its entirety in all copies and derived works. | |
12 | */ | |
13 | ||
14 | #include <linux/module.h> | |
15 | #include <linux/interrupt.h> | |
eea2172e | 16 | #include <linux/io.h> |
f19b121e | 17 | #include <linux/irq.h> |
18 | #include <linux/pm.h> | |
19 | #include <linux/platform_device.h> | |
fbc357df | 20 | #include <linux/err.h> |
f19b121e | 21 | #include <linux/delay.h> |
a23a1757 PZ |
22 | #include <linux/mfd/core.h> |
23 | #include <linux/mfd/ds1wm.h> | |
5a0e3ad6 | 24 | #include <linux/slab.h> |
f19b121e | 25 | |
26 | #include <asm/io.h> | |
27 | ||
28 | #include "../w1.h" | |
29 | #include "../w1_int.h" | |
30 | ||
31 | ||
32 | #define DS1WM_CMD 0x00 /* R/W 4 bits command */ | |
33 | #define DS1WM_DATA 0x01 /* R/W 8 bits, transmit/receive buffer */ | |
34 | #define DS1WM_INT 0x02 /* R/W interrupt status */ | |
35 | #define DS1WM_INT_EN 0x03 /* R/W interrupt enable */ | |
36 | #define DS1WM_CLKDIV 0x04 /* R/W 5 bits of divisor and pre-scale */ | |
26a6afb9 | 37 | #define DS1WM_CNTRL 0x05 /* R/W master control register (not used yet) */ |
f19b121e | 38 | |
39 | #define DS1WM_CMD_1W_RESET (1 << 0) /* force reset on 1-wire bus */ | |
40 | #define DS1WM_CMD_SRA (1 << 1) /* enable Search ROM accelerator mode */ | |
41 | #define DS1WM_CMD_DQ_OUTPUT (1 << 2) /* write only - forces bus low */ | |
42 | #define DS1WM_CMD_DQ_INPUT (1 << 3) /* read only - reflects state of bus */ | |
43 | #define DS1WM_CMD_RST (1 << 5) /* software reset */ | |
44 | #define DS1WM_CMD_OD (1 << 7) /* overdrive */ | |
45 | ||
46 | #define DS1WM_INT_PD (1 << 0) /* presence detect */ | |
47 | #define DS1WM_INT_PDR (1 << 1) /* presence detect result */ | |
48 | #define DS1WM_INT_TBE (1 << 2) /* tx buffer empty */ | |
49 | #define DS1WM_INT_TSRE (1 << 3) /* tx shift register empty */ | |
50 | #define DS1WM_INT_RBF (1 << 4) /* rx buffer full */ | |
51 | #define DS1WM_INT_RSRF (1 << 5) /* rx shift register full */ | |
52 | ||
53 | #define DS1WM_INTEN_EPD (1 << 0) /* enable presence detect int */ | |
54 | #define DS1WM_INTEN_IAS (1 << 1) /* INTR active state */ | |
55 | #define DS1WM_INTEN_ETBE (1 << 2) /* enable tx buffer empty int */ | |
56 | #define DS1WM_INTEN_ETMT (1 << 3) /* enable tx shift register empty int */ | |
57 | #define DS1WM_INTEN_ERBF (1 << 4) /* enable rx buffer full int */ | |
58 | #define DS1WM_INTEN_ERSRF (1 << 5) /* enable rx shift register full int */ | |
59 | #define DS1WM_INTEN_DQO (1 << 6) /* enable direct bus driving ops */ | |
60 | ||
26a6afb9 | 61 | #define DS1WM_INTEN_NOT_IAS (~DS1WM_INTEN_IAS) /* all but INTR active state */ |
f19b121e | 62 | |
63 | #define DS1WM_TIMEOUT (HZ * 5) | |
64 | ||
65 | static struct { | |
66 | unsigned long freq; | |
67 | unsigned long divisor; | |
68 | } freq[] = { | |
26a6afb9 JFD |
69 | { 1000000, 0x80 }, |
70 | { 2000000, 0x84 }, | |
71 | { 3000000, 0x81 }, | |
72 | { 4000000, 0x88 }, | |
73 | { 5000000, 0x82 }, | |
74 | { 6000000, 0x85 }, | |
75 | { 7000000, 0x83 }, | |
76 | { 8000000, 0x8c }, | |
77 | { 10000000, 0x86 }, | |
78 | { 12000000, 0x89 }, | |
79 | { 14000000, 0x87 }, | |
80 | { 16000000, 0x90 }, | |
81 | { 20000000, 0x8a }, | |
82 | { 24000000, 0x8d }, | |
83 | { 28000000, 0x8b }, | |
84 | { 32000000, 0x94 }, | |
85 | { 40000000, 0x8e }, | |
86 | { 48000000, 0x91 }, | |
87 | { 56000000, 0x8f }, | |
88 | { 64000000, 0x98 }, | |
89 | { 80000000, 0x92 }, | |
90 | { 96000000, 0x95 }, | |
91 | { 112000000, 0x93 }, | |
92 | { 128000000, 0x9c }, | |
93 | /* you can continue this table, consult the OPERATION - CLOCK DIVISOR | |
94 | section of the ds1wm spec sheet. */ | |
f19b121e | 95 | }; |
96 | ||
97 | struct ds1wm_data { | |
26a6afb9 JFD |
98 | void __iomem *map; |
99 | int bus_shift; /* # of shifts to calc register offsets */ | |
f19b121e | 100 | struct platform_device *pdev; |
26a6afb9 JFD |
101 | const struct mfd_cell *cell; |
102 | int irq; | |
103 | int slave_present; | |
104 | void *reset_complete; | |
105 | void *read_complete; | |
106 | void *write_complete; | |
107 | int read_error; | |
108 | /* last byte received */ | |
109 | u8 read_byte; | |
110 | /* byte to write that makes all intr disabled, */ | |
111 | /* considering active_state (IAS) (optimization) */ | |
112 | u8 int_en_reg_none; | |
f607e7fc | 113 | unsigned int reset_recover_delay; /* see ds1wm.h */ |
f19b121e | 114 | }; |
115 | ||
116 | static inline void ds1wm_write_register(struct ds1wm_data *ds1wm_data, u32 reg, | |
117 | u8 val) | |
118 | { | |
daa49ff5 | 119 | __raw_writeb(val, ds1wm_data->map + (reg << ds1wm_data->bus_shift)); |
f19b121e | 120 | } |
121 | ||
122 | static inline u8 ds1wm_read_register(struct ds1wm_data *ds1wm_data, u32 reg) | |
123 | { | |
daa49ff5 | 124 | return __raw_readb(ds1wm_data->map + (reg << ds1wm_data->bus_shift)); |
f19b121e | 125 | } |
126 | ||
127 | ||
128 | static irqreturn_t ds1wm_isr(int isr, void *data) | |
129 | { | |
130 | struct ds1wm_data *ds1wm_data = data; | |
26a6afb9 JFD |
131 | u8 intr; |
132 | u8 inten = ds1wm_read_register(ds1wm_data, DS1WM_INT_EN); | |
133 | /* if no bits are set in int enable register (except the IAS) | |
134 | than go no further, reading the regs below has side effects */ | |
135 | if (!(inten & DS1WM_INTEN_NOT_IAS)) | |
136 | return IRQ_NONE; | |
f19b121e | 137 | |
26a6afb9 JFD |
138 | ds1wm_write_register(ds1wm_data, |
139 | DS1WM_INT_EN, ds1wm_data->int_en_reg_none); | |
f19b121e | 140 | |
26a6afb9 JFD |
141 | /* this read action clears the INTR and certain flags in ds1wm */ |
142 | intr = ds1wm_read_register(ds1wm_data, DS1WM_INT); | |
f19b121e | 143 | |
26a6afb9 | 144 | ds1wm_data->slave_present = (intr & DS1WM_INT_PDR) ? 0 : 1; |
f19b121e | 145 | |
26a6afb9 JFD |
146 | if ((intr & DS1WM_INT_TSRE) && ds1wm_data->write_complete) { |
147 | inten &= ~DS1WM_INTEN_ETMT; | |
148 | complete(ds1wm_data->write_complete); | |
149 | } | |
f19b121e | 150 | if (intr & DS1WM_INT_RBF) { |
26a6afb9 | 151 | /* this read clears the RBF flag */ |
f19b121e | 152 | ds1wm_data->read_byte = ds1wm_read_register(ds1wm_data, |
26a6afb9 JFD |
153 | DS1WM_DATA); |
154 | inten &= ~DS1WM_INTEN_ERBF; | |
f19b121e | 155 | if (ds1wm_data->read_complete) |
156 | complete(ds1wm_data->read_complete); | |
157 | } | |
26a6afb9 JFD |
158 | if ((intr & DS1WM_INT_PD) && ds1wm_data->reset_complete) { |
159 | inten &= ~DS1WM_INTEN_EPD; | |
160 | complete(ds1wm_data->reset_complete); | |
161 | } | |
f19b121e | 162 | |
26a6afb9 | 163 | ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, inten); |
f19b121e | 164 | return IRQ_HANDLED; |
165 | } | |
166 | ||
167 | static int ds1wm_reset(struct ds1wm_data *ds1wm_data) | |
168 | { | |
169 | unsigned long timeleft; | |
170 | DECLARE_COMPLETION_ONSTACK(reset_done); | |
171 | ||
172 | ds1wm_data->reset_complete = &reset_done; | |
173 | ||
26a6afb9 | 174 | /* enable Presence detect only */ |
f19b121e | 175 | ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, DS1WM_INTEN_EPD | |
26a6afb9 | 176 | ds1wm_data->int_en_reg_none); |
f19b121e | 177 | |
178 | ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_1W_RESET); | |
179 | ||
180 | timeleft = wait_for_completion_timeout(&reset_done, DS1WM_TIMEOUT); | |
181 | ds1wm_data->reset_complete = NULL; | |
182 | if (!timeleft) { | |
26a6afb9 | 183 | dev_err(&ds1wm_data->pdev->dev, "reset failed, timed out\n"); |
daa49ff5 | 184 | return 1; |
f19b121e | 185 | } |
186 | ||
f19b121e | 187 | if (!ds1wm_data->slave_present) { |
daa49ff5 AV |
188 | dev_dbg(&ds1wm_data->pdev->dev, "reset: no devices found\n"); |
189 | return 1; | |
190 | } | |
f19b121e | 191 | |
f607e7fc JFD |
192 | if (ds1wm_data->reset_recover_delay) |
193 | msleep(ds1wm_data->reset_recover_delay); | |
194 | ||
daa49ff5 | 195 | return 0; |
f19b121e | 196 | } |
197 | ||
198 | static int ds1wm_write(struct ds1wm_data *ds1wm_data, u8 data) | |
199 | { | |
26a6afb9 | 200 | unsigned long timeleft; |
f19b121e | 201 | DECLARE_COMPLETION_ONSTACK(write_done); |
202 | ds1wm_data->write_complete = &write_done; | |
203 | ||
26a6afb9 JFD |
204 | ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, |
205 | ds1wm_data->int_en_reg_none | DS1WM_INTEN_ETMT); | |
206 | ||
f19b121e | 207 | ds1wm_write_register(ds1wm_data, DS1WM_DATA, data); |
208 | ||
26a6afb9 JFD |
209 | timeleft = wait_for_completion_timeout(&write_done, DS1WM_TIMEOUT); |
210 | ||
f19b121e | 211 | ds1wm_data->write_complete = NULL; |
26a6afb9 JFD |
212 | if (!timeleft) { |
213 | dev_err(&ds1wm_data->pdev->dev, "write failed, timed out\n"); | |
214 | return -ETIMEDOUT; | |
215 | } | |
f19b121e | 216 | |
217 | return 0; | |
218 | } | |
219 | ||
26a6afb9 | 220 | static u8 ds1wm_read(struct ds1wm_data *ds1wm_data, unsigned char write_data) |
f19b121e | 221 | { |
26a6afb9 JFD |
222 | unsigned long timeleft; |
223 | u8 intEnable = DS1WM_INTEN_ERBF | ds1wm_data->int_en_reg_none; | |
f19b121e | 224 | DECLARE_COMPLETION_ONSTACK(read_done); |
26a6afb9 JFD |
225 | |
226 | ds1wm_read_register(ds1wm_data, DS1WM_DATA); | |
227 | ||
f19b121e | 228 | ds1wm_data->read_complete = &read_done; |
26a6afb9 | 229 | ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, intEnable); |
f19b121e | 230 | |
26a6afb9 JFD |
231 | ds1wm_write_register(ds1wm_data, DS1WM_DATA, write_data); |
232 | timeleft = wait_for_completion_timeout(&read_done, DS1WM_TIMEOUT); | |
f19b121e | 233 | |
26a6afb9 JFD |
234 | ds1wm_data->read_complete = NULL; |
235 | if (!timeleft) { | |
236 | dev_err(&ds1wm_data->pdev->dev, "read failed, timed out\n"); | |
237 | ds1wm_data->read_error = -ETIMEDOUT; | |
238 | return 0xFF; | |
239 | } | |
240 | ds1wm_data->read_error = 0; | |
f19b121e | 241 | return ds1wm_data->read_byte; |
242 | } | |
243 | ||
244 | static int ds1wm_find_divisor(int gclk) | |
245 | { | |
246 | int i; | |
247 | ||
26a6afb9 JFD |
248 | for (i = ARRAY_SIZE(freq)-1; i >= 0; --i) |
249 | if (gclk >= freq[i].freq) | |
f19b121e | 250 | return freq[i].divisor; |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | static void ds1wm_up(struct ds1wm_data *ds1wm_data) | |
256 | { | |
7d33ccbe | 257 | int divisor; |
121ea573 | 258 | struct ds1wm_driver_data *plat = ds1wm_data->pdev->dev.platform_data; |
f19b121e | 259 | |
a23a1757 PZ |
260 | if (ds1wm_data->cell->enable) |
261 | ds1wm_data->cell->enable(ds1wm_data->pdev); | |
f19b121e | 262 | |
7d33ccbe | 263 | divisor = ds1wm_find_divisor(plat->clock_rate); |
26a6afb9 JFD |
264 | dev_dbg(&ds1wm_data->pdev->dev, |
265 | "found divisor 0x%x for clock %d\n", divisor, plat->clock_rate); | |
f19b121e | 266 | if (divisor == 0) { |
267 | dev_err(&ds1wm_data->pdev->dev, | |
7d33ccbe PZ |
268 | "no suitable divisor for %dHz clock\n", |
269 | plat->clock_rate); | |
f19b121e | 270 | return; |
271 | } | |
272 | ds1wm_write_register(ds1wm_data, DS1WM_CLKDIV, divisor); | |
273 | ||
274 | /* Let the w1 clock stabilize. */ | |
275 | msleep(1); | |
276 | ||
277 | ds1wm_reset(ds1wm_data); | |
278 | } | |
279 | ||
280 | static void ds1wm_down(struct ds1wm_data *ds1wm_data) | |
281 | { | |
282 | ds1wm_reset(ds1wm_data); | |
283 | ||
284 | /* Disable interrupts. */ | |
285 | ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, | |
26a6afb9 | 286 | ds1wm_data->int_en_reg_none); |
f19b121e | 287 | |
a23a1757 PZ |
288 | if (ds1wm_data->cell->disable) |
289 | ds1wm_data->cell->disable(ds1wm_data->pdev); | |
f19b121e | 290 | } |
291 | ||
292 | /* --------------------------------------------------------------------- */ | |
293 | /* w1 methods */ | |
294 | ||
295 | static u8 ds1wm_read_byte(void *data) | |
296 | { | |
297 | struct ds1wm_data *ds1wm_data = data; | |
298 | ||
299 | return ds1wm_read(ds1wm_data, 0xff); | |
300 | } | |
301 | ||
302 | static void ds1wm_write_byte(void *data, u8 byte) | |
303 | { | |
304 | struct ds1wm_data *ds1wm_data = data; | |
305 | ||
306 | ds1wm_write(ds1wm_data, byte); | |
307 | } | |
308 | ||
309 | static u8 ds1wm_reset_bus(void *data) | |
310 | { | |
311 | struct ds1wm_data *ds1wm_data = data; | |
312 | ||
313 | ds1wm_reset(ds1wm_data); | |
314 | ||
315 | return 0; | |
316 | } | |
317 | ||
c30c9b15 DF |
318 | static void ds1wm_search(void *data, struct w1_master *master_dev, |
319 | u8 search_type, w1_slave_found_callback slave_found) | |
f19b121e | 320 | { |
321 | struct ds1wm_data *ds1wm_data = data; | |
322 | int i; | |
26a6afb9 JFD |
323 | int ms_discrep_bit = -1; |
324 | u64 r = 0; /* holds the progress of the search */ | |
325 | u64 r_prime, d; | |
326 | unsigned slaves_found = 0; | |
327 | unsigned int pass = 0; | |
328 | ||
329 | dev_dbg(&ds1wm_data->pdev->dev, "search begin\n"); | |
330 | while (true) { | |
331 | ++pass; | |
332 | if (pass > 100) { | |
333 | dev_dbg(&ds1wm_data->pdev->dev, | |
334 | "too many attempts (100), search aborted\n"); | |
335 | return; | |
336 | } | |
337 | ||
b02f8bed | 338 | mutex_lock(&master_dev->bus_mutex); |
26a6afb9 | 339 | if (ds1wm_reset(ds1wm_data)) { |
b02f8bed | 340 | mutex_unlock(&master_dev->bus_mutex); |
26a6afb9 JFD |
341 | dev_dbg(&ds1wm_data->pdev->dev, |
342 | "pass: %d reset error (or no slaves)\n", pass); | |
343 | break; | |
344 | } | |
345 | ||
346 | dev_dbg(&ds1wm_data->pdev->dev, | |
347 | "pass: %d r : %0#18llx writing SEARCH_ROM\n", pass, r); | |
348 | ds1wm_write(ds1wm_data, search_type); | |
349 | dev_dbg(&ds1wm_data->pdev->dev, | |
350 | "pass: %d entering ASM\n", pass); | |
351 | ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA); | |
352 | dev_dbg(&ds1wm_data->pdev->dev, | |
4907cb7b | 353 | "pass: %d beginning nibble loop\n", pass); |
26a6afb9 JFD |
354 | |
355 | r_prime = 0; | |
356 | d = 0; | |
357 | /* we work one nibble at a time */ | |
358 | /* each nibble is interleaved to form a byte */ | |
359 | for (i = 0; i < 16; i++) { | |
360 | ||
361 | unsigned char resp, _r, _r_prime, _d; | |
362 | ||
363 | _r = (r >> (4*i)) & 0xf; | |
364 | _r = ((_r & 0x1) << 1) | | |
365 | ((_r & 0x2) << 2) | | |
366 | ((_r & 0x4) << 3) | | |
367 | ((_r & 0x8) << 4); | |
368 | ||
369 | /* writes _r, then reads back: */ | |
370 | resp = ds1wm_read(ds1wm_data, _r); | |
371 | ||
372 | if (ds1wm_data->read_error) { | |
373 | dev_err(&ds1wm_data->pdev->dev, | |
374 | "pass: %d nibble: %d read error\n", pass, i); | |
375 | break; | |
376 | } | |
377 | ||
378 | _r_prime = ((resp & 0x02) >> 1) | | |
379 | ((resp & 0x08) >> 2) | | |
380 | ((resp & 0x20) >> 3) | | |
381 | ((resp & 0x80) >> 4); | |
382 | ||
383 | _d = ((resp & 0x01) >> 0) | | |
384 | ((resp & 0x04) >> 1) | | |
385 | ((resp & 0x10) >> 2) | | |
386 | ((resp & 0x40) >> 3); | |
387 | ||
388 | r_prime |= (unsigned long long) _r_prime << (i * 4); | |
389 | d |= (unsigned long long) _d << (i * 4); | |
390 | ||
391 | } | |
392 | if (ds1wm_data->read_error) { | |
b02f8bed | 393 | mutex_unlock(&master_dev->bus_mutex); |
26a6afb9 JFD |
394 | dev_err(&ds1wm_data->pdev->dev, |
395 | "pass: %d read error, retrying\n", pass); | |
396 | break; | |
397 | } | |
398 | dev_dbg(&ds1wm_data->pdev->dev, | |
399 | "pass: %d r\': %0#18llx d:%0#18llx\n", | |
400 | pass, r_prime, d); | |
401 | dev_dbg(&ds1wm_data->pdev->dev, | |
402 | "pass: %d nibble loop complete, exiting ASM\n", pass); | |
403 | ds1wm_write_register(ds1wm_data, DS1WM_CMD, ~DS1WM_CMD_SRA); | |
404 | dev_dbg(&ds1wm_data->pdev->dev, | |
405 | "pass: %d resetting bus\n", pass); | |
406 | ds1wm_reset(ds1wm_data); | |
b02f8bed | 407 | mutex_unlock(&master_dev->bus_mutex); |
26a6afb9 JFD |
408 | if ((r_prime & ((u64)1 << 63)) && (d & ((u64)1 << 63))) { |
409 | dev_err(&ds1wm_data->pdev->dev, | |
410 | "pass: %d bus error, retrying\n", pass); | |
411 | continue; /* start over */ | |
412 | } | |
413 | ||
414 | ||
415 | dev_dbg(&ds1wm_data->pdev->dev, | |
416 | "pass: %d found %0#18llx\n", pass, r_prime); | |
417 | slave_found(master_dev, r_prime); | |
418 | ++slaves_found; | |
419 | dev_dbg(&ds1wm_data->pdev->dev, | |
420 | "pass: %d complete, preparing next pass\n", pass); | |
421 | ||
422 | /* any discrepency found which we already choose the | |
423 | '1' branch is now is now irrelevant we reveal the | |
424 | next branch with this: */ | |
425 | d &= ~r; | |
426 | /* find last bit set, i.e. the most signif. bit set */ | |
427 | ms_discrep_bit = fls64(d) - 1; | |
428 | dev_dbg(&ds1wm_data->pdev->dev, | |
429 | "pass: %d new d:%0#18llx MS discrep bit:%d\n", | |
430 | pass, d, ms_discrep_bit); | |
431 | ||
432 | /* prev_ms_discrep_bit = ms_discrep_bit; | |
433 | prepare for next ROM search: */ | |
434 | if (ms_discrep_bit == -1) | |
435 | break; | |
436 | ||
437 | r = (r & ~(~0ull << (ms_discrep_bit))) | 1 << ms_discrep_bit; | |
438 | } /* end while true */ | |
439 | dev_dbg(&ds1wm_data->pdev->dev, | |
440 | "pass: %d total: %d search done ms d bit pos: %d\n", pass, | |
441 | slaves_found, ms_discrep_bit); | |
f19b121e | 442 | } |
443 | ||
444 | /* --------------------------------------------------------------------- */ | |
445 | ||
446 | static struct w1_bus_master ds1wm_master = { | |
447 | .read_byte = ds1wm_read_byte, | |
448 | .write_byte = ds1wm_write_byte, | |
449 | .reset_bus = ds1wm_reset_bus, | |
450 | .search = ds1wm_search, | |
451 | }; | |
452 | ||
453 | static int ds1wm_probe(struct platform_device *pdev) | |
454 | { | |
455 | struct ds1wm_data *ds1wm_data; | |
a23a1757 | 456 | struct ds1wm_driver_data *plat; |
f19b121e | 457 | struct resource *res; |
458 | int ret; | |
459 | ||
460 | if (!pdev) | |
461 | return -ENODEV; | |
462 | ||
eea2172e | 463 | ds1wm_data = devm_kzalloc(&pdev->dev, sizeof(*ds1wm_data), GFP_KERNEL); |
f19b121e | 464 | if (!ds1wm_data) |
465 | return -ENOMEM; | |
466 | ||
467 | platform_set_drvdata(pdev, ds1wm_data); | |
468 | ||
469 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
eea2172e JL |
470 | if (!res) |
471 | return -ENXIO; | |
472 | ds1wm_data->map = devm_ioremap(&pdev->dev, res->start, | |
473 | resource_size(res)); | |
474 | if (!ds1wm_data->map) | |
475 | return -ENOMEM; | |
a23a1757 PZ |
476 | |
477 | /* calculate bus shift from mem resource */ | |
478 | ds1wm_data->bus_shift = resource_size(res) >> 3; | |
479 | ||
f19b121e | 480 | ds1wm_data->pdev = pdev; |
7e5dc1f7 | 481 | ds1wm_data->cell = mfd_get_cell(pdev); |
eea2172e JL |
482 | if (!ds1wm_data->cell) |
483 | return -ENODEV; | |
121ea573 | 484 | plat = pdev->dev.platform_data; |
eea2172e JL |
485 | if (!plat) |
486 | return -ENODEV; | |
f19b121e | 487 | |
488 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | |
eea2172e JL |
489 | if (!res) |
490 | return -ENXIO; | |
f19b121e | 491 | ds1wm_data->irq = res->start; |
26a6afb9 | 492 | ds1wm_data->int_en_reg_none = (plat->active_high ? DS1WM_INTEN_IAS : 0); |
f607e7fc | 493 | ds1wm_data->reset_recover_delay = plat->reset_recover_delay; |
f19b121e | 494 | |
4aa323bd | 495 | if (res->flags & IORESOURCE_IRQ_HIGHEDGE) |
dced35ae | 496 | irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_RISING); |
4aa323bd | 497 | if (res->flags & IORESOURCE_IRQ_LOWEDGE) |
dced35ae | 498 | irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING); |
f19b121e | 499 | |
eea2172e | 500 | ret = devm_request_irq(&pdev->dev, ds1wm_data->irq, ds1wm_isr, |
26a6afb9 | 501 | IRQF_DISABLED | IRQF_SHARED, "ds1wm", ds1wm_data); |
f19b121e | 502 | if (ret) |
eea2172e | 503 | return ret; |
f19b121e | 504 | |
f19b121e | 505 | ds1wm_up(ds1wm_data); |
506 | ||
507 | ds1wm_master.data = (void *)ds1wm_data; | |
508 | ||
509 | ret = w1_add_master_device(&ds1wm_master); | |
510 | if (ret) | |
eea2172e | 511 | goto err; |
f19b121e | 512 | |
513 | return 0; | |
514 | ||
eea2172e | 515 | err: |
7d33ccbe | 516 | ds1wm_down(ds1wm_data); |
f19b121e | 517 | |
518 | return ret; | |
519 | } | |
520 | ||
521 | #ifdef CONFIG_PM | |
522 | static int ds1wm_suspend(struct platform_device *pdev, pm_message_t state) | |
523 | { | |
524 | struct ds1wm_data *ds1wm_data = platform_get_drvdata(pdev); | |
525 | ||
526 | ds1wm_down(ds1wm_data); | |
527 | ||
528 | return 0; | |
529 | } | |
530 | ||
531 | static int ds1wm_resume(struct platform_device *pdev) | |
532 | { | |
533 | struct ds1wm_data *ds1wm_data = platform_get_drvdata(pdev); | |
534 | ||
535 | ds1wm_up(ds1wm_data); | |
536 | ||
537 | return 0; | |
538 | } | |
539 | #else | |
540 | #define ds1wm_suspend NULL | |
541 | #define ds1wm_resume NULL | |
542 | #endif | |
543 | ||
544 | static int ds1wm_remove(struct platform_device *pdev) | |
545 | { | |
546 | struct ds1wm_data *ds1wm_data = platform_get_drvdata(pdev); | |
547 | ||
548 | w1_remove_master_device(&ds1wm_master); | |
549 | ds1wm_down(ds1wm_data); | |
f19b121e | 550 | |
551 | return 0; | |
552 | } | |
553 | ||
554 | static struct platform_driver ds1wm_driver = { | |
555 | .driver = { | |
556 | .name = "ds1wm", | |
557 | }, | |
558 | .probe = ds1wm_probe, | |
559 | .remove = ds1wm_remove, | |
560 | .suspend = ds1wm_suspend, | |
561 | .resume = ds1wm_resume | |
562 | }; | |
563 | ||
564 | static int __init ds1wm_init(void) | |
565 | { | |
566 | printk("DS1WM w1 busmaster driver - (c) 2004 Szabolcs Gyurko\n"); | |
567 | return platform_driver_register(&ds1wm_driver); | |
568 | } | |
569 | ||
570 | static void __exit ds1wm_exit(void) | |
571 | { | |
572 | platform_driver_unregister(&ds1wm_driver); | |
573 | } | |
574 | ||
575 | module_init(ds1wm_init); | |
576 | module_exit(ds1wm_exit); | |
577 | ||
578 | MODULE_LICENSE("GPL"); | |
579 | MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, " | |
26a6afb9 JFD |
580 | "Matt Reimer <mreimer@vpop.net>," |
581 | "Jean-Francois Dagenais <dagenaisj@sonatest.com>"); | |
f19b121e | 582 | MODULE_DESCRIPTION("DS1WM w1 busmaster driver"); |