OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [w1/] [masters/] [ds1wm.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
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>
16
#include <linux/irq.h>
17
#include <linux/pm.h>
18
#include <linux/platform_device.h>
19
#include <linux/clk.h>
20
#include <linux/delay.h>
21
#include <linux/ds1wm.h>
22
 
23
#include <asm/io.h>
24
 
25
#include "../w1.h"
26
#include "../w1_int.h"
27
 
28
 
29
#define DS1WM_CMD       0x00    /* R/W 4 bits command */
30
#define DS1WM_DATA      0x01    /* R/W 8 bits, transmit/receive buffer */
31
#define DS1WM_INT       0x02    /* R/W interrupt status */
32
#define DS1WM_INT_EN    0x03    /* R/W interrupt enable */
33
#define DS1WM_CLKDIV    0x04    /* R/W 5 bits of divisor and pre-scale */
34
 
35
#define DS1WM_CMD_1W_RESET  (1 << 0)    /* force reset on 1-wire bus */
36
#define DS1WM_CMD_SRA       (1 << 1)    /* enable Search ROM accelerator mode */
37
#define DS1WM_CMD_DQ_OUTPUT (1 << 2)    /* write only - forces bus low */
38
#define DS1WM_CMD_DQ_INPUT  (1 << 3)    /* read only - reflects state of bus */
39
#define DS1WM_CMD_RST       (1 << 5)    /* software reset */
40
#define DS1WM_CMD_OD        (1 << 7)    /* overdrive */
41
 
42
#define DS1WM_INT_PD        (1 << 0)    /* presence detect */
43
#define DS1WM_INT_PDR       (1 << 1)    /* presence detect result */
44
#define DS1WM_INT_TBE       (1 << 2)    /* tx buffer empty */
45
#define DS1WM_INT_TSRE      (1 << 3)    /* tx shift register empty */
46
#define DS1WM_INT_RBF       (1 << 4)    /* rx buffer full */
47
#define DS1WM_INT_RSRF      (1 << 5)    /* rx shift register full */
48
 
49
#define DS1WM_INTEN_EPD     (1 << 0)    /* enable presence detect int */
50
#define DS1WM_INTEN_IAS     (1 << 1)    /* INTR active state */
51
#define DS1WM_INTEN_ETBE    (1 << 2)    /* enable tx buffer empty int */
52
#define DS1WM_INTEN_ETMT    (1 << 3)    /* enable tx shift register empty int */
53
#define DS1WM_INTEN_ERBF    (1 << 4)    /* enable rx buffer full int */
54
#define DS1WM_INTEN_ERSRF   (1 << 5)    /* enable rx shift register full int */
55
#define DS1WM_INTEN_DQO     (1 << 6)    /* enable direct bus driving ops */
56
 
57
 
58
#define DS1WM_TIMEOUT (HZ * 5)
59
 
60
static struct {
61
        unsigned long freq;
62
        unsigned long divisor;
63
} freq[] = {
64
        { 4000000, 0x8 },
65
        { 5000000, 0x2 },
66
        { 6000000, 0x5 },
67
        { 7000000, 0x3 },
68
        { 8000000, 0xc },
69
        { 10000000, 0x6 },
70
        { 12000000, 0x9 },
71
        { 14000000, 0x7 },
72
        { 16000000, 0x10 },
73
        { 20000000, 0xa },
74
        { 24000000, 0xd },
75
        { 28000000, 0xb },
76
        { 32000000, 0x14 },
77
        { 40000000, 0xe },
78
        { 48000000, 0x11 },
79
        { 56000000, 0xf },
80
        { 64000000, 0x18 },
81
        { 80000000, 0x12 },
82
        { 96000000, 0x15 },
83
        { 112000000, 0x13 },
84
        { 128000000, 0x1c },
85
};
86
 
87
struct ds1wm_data {
88
        void            __iomem *map;
89
        int             bus_shift; /* # of shifts to calc register offsets */
90
        struct platform_device *pdev;
91
        struct ds1wm_platform_data *pdata;
92
        int             irq;
93
        int             active_high;
94
        struct clk      *clk;
95
        int             slave_present;
96
        void            *reset_complete;
97
        void            *read_complete;
98
        void            *write_complete;
99
        u8              read_byte; /* last byte received */
100
};
101
 
102
static inline void ds1wm_write_register(struct ds1wm_data *ds1wm_data, u32 reg,
103
                                        u8 val)
104
{
105
        __raw_writeb(val, ds1wm_data->map + (reg << ds1wm_data->bus_shift));
106
}
107
 
108
static inline u8 ds1wm_read_register(struct ds1wm_data *ds1wm_data, u32 reg)
109
{
110
        return __raw_readb(ds1wm_data->map + (reg << ds1wm_data->bus_shift));
111
}
112
 
113
 
114
static irqreturn_t ds1wm_isr(int isr, void *data)
115
{
116
        struct ds1wm_data *ds1wm_data = data;
117
        u8 intr = ds1wm_read_register(ds1wm_data, DS1WM_INT);
118
 
119
        ds1wm_data->slave_present = (intr & DS1WM_INT_PDR) ? 0 : 1;
120
 
121
        if ((intr & DS1WM_INT_PD) && ds1wm_data->reset_complete)
122
                complete(ds1wm_data->reset_complete);
123
 
124
        if ((intr & DS1WM_INT_TSRE) && ds1wm_data->write_complete)
125
                complete(ds1wm_data->write_complete);
126
 
127
        if (intr & DS1WM_INT_RBF) {
128
                ds1wm_data->read_byte = ds1wm_read_register(ds1wm_data,
129
                                                            DS1WM_DATA);
130
                if (ds1wm_data->read_complete)
131
                        complete(ds1wm_data->read_complete);
132
        }
133
 
134
        return IRQ_HANDLED;
135
}
136
 
137
static int ds1wm_reset(struct ds1wm_data *ds1wm_data)
138
{
139
        unsigned long timeleft;
140
        DECLARE_COMPLETION_ONSTACK(reset_done);
141
 
142
        ds1wm_data->reset_complete = &reset_done;
143
 
144
        ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, DS1WM_INTEN_EPD |
145
                (ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0));
146
 
147
        ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_1W_RESET);
148
 
149
        timeleft = wait_for_completion_timeout(&reset_done, DS1WM_TIMEOUT);
150
        ds1wm_data->reset_complete = NULL;
151
        if (!timeleft) {
152
                dev_dbg(&ds1wm_data->pdev->dev, "reset failed\n");
153
                return 1;
154
        }
155
 
156
        /* Wait for the end of the reset. According to the specs, the time
157
         * from when the interrupt is asserted to the end of the reset is:
158
         *     tRSTH  - tPDH  - tPDL - tPDI
159
         *     625 us - 60 us - 240 us - 100 ns = 324.9 us
160
         *
161
         * We'll wait a bit longer just to be sure.
162
         */
163
        udelay(500);
164
 
165
        ds1wm_write_register(ds1wm_data, DS1WM_INT_EN,
166
                DS1WM_INTEN_ERBF | DS1WM_INTEN_ETMT | DS1WM_INTEN_EPD |
167
                (ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0));
168
 
169
        if (!ds1wm_data->slave_present) {
170
                dev_dbg(&ds1wm_data->pdev->dev, "reset: no devices found\n");
171
                return 1;
172
        }
173
 
174
        return 0;
175
}
176
 
177
static int ds1wm_write(struct ds1wm_data *ds1wm_data, u8 data)
178
{
179
        DECLARE_COMPLETION_ONSTACK(write_done);
180
        ds1wm_data->write_complete = &write_done;
181
 
182
        ds1wm_write_register(ds1wm_data, DS1WM_DATA, data);
183
 
184
        wait_for_completion_timeout(&write_done, DS1WM_TIMEOUT);
185
        ds1wm_data->write_complete = NULL;
186
 
187
        return 0;
188
}
189
 
190
static int ds1wm_read(struct ds1wm_data *ds1wm_data, unsigned char write_data)
191
{
192
        DECLARE_COMPLETION_ONSTACK(read_done);
193
        ds1wm_data->read_complete = &read_done;
194
 
195
        ds1wm_write(ds1wm_data, write_data);
196
        wait_for_completion_timeout(&read_done, DS1WM_TIMEOUT);
197
        ds1wm_data->read_complete = NULL;
198
 
199
        return ds1wm_data->read_byte;
200
}
201
 
202
static int ds1wm_find_divisor(int gclk)
203
{
204
        int i;
205
 
206
        for (i = 0; i < ARRAY_SIZE(freq); i++)
207
                if (gclk <= freq[i].freq)
208
                        return freq[i].divisor;
209
 
210
        return 0;
211
}
212
 
213
static void ds1wm_up(struct ds1wm_data *ds1wm_data)
214
{
215
        int gclk, divisor;
216
 
217
        if (ds1wm_data->pdata->enable)
218
                ds1wm_data->pdata->enable(ds1wm_data->pdev);
219
 
220
        gclk = clk_get_rate(ds1wm_data->clk);
221
        clk_enable(ds1wm_data->clk);
222
        divisor = ds1wm_find_divisor(gclk);
223
        if (divisor == 0) {
224
                dev_err(&ds1wm_data->pdev->dev,
225
                        "no suitable divisor for %dHz clock\n", gclk);
226
                return;
227
        }
228
        ds1wm_write_register(ds1wm_data, DS1WM_CLKDIV, divisor);
229
 
230
        /* Let the w1 clock stabilize. */
231
        msleep(1);
232
 
233
        ds1wm_reset(ds1wm_data);
234
}
235
 
236
static void ds1wm_down(struct ds1wm_data *ds1wm_data)
237
{
238
        ds1wm_reset(ds1wm_data);
239
 
240
        /* Disable interrupts. */
241
        ds1wm_write_register(ds1wm_data, DS1WM_INT_EN,
242
                             ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0);
243
 
244
        if (ds1wm_data->pdata->disable)
245
                ds1wm_data->pdata->disable(ds1wm_data->pdev);
246
 
247
        clk_disable(ds1wm_data->clk);
248
}
249
 
250
/* --------------------------------------------------------------------- */
251
/* w1 methods */
252
 
253
static u8 ds1wm_read_byte(void *data)
254
{
255
        struct ds1wm_data *ds1wm_data = data;
256
 
257
        return ds1wm_read(ds1wm_data, 0xff);
258
}
259
 
260
static void ds1wm_write_byte(void *data, u8 byte)
261
{
262
        struct ds1wm_data *ds1wm_data = data;
263
 
264
        ds1wm_write(ds1wm_data, byte);
265
}
266
 
267
static u8 ds1wm_reset_bus(void *data)
268
{
269
        struct ds1wm_data *ds1wm_data = data;
270
 
271
        ds1wm_reset(ds1wm_data);
272
 
273
        return 0;
274
}
275
 
276
static void ds1wm_search(void *data, u8 search_type,
277
                         w1_slave_found_callback slave_found)
278
{
279
        struct ds1wm_data *ds1wm_data = data;
280
        int i;
281
        unsigned long long rom_id;
282
 
283
        /* XXX We need to iterate for multiple devices per the DS1WM docs.
284
         * See http://www.maxim-ic.com/appnotes.cfm/appnote_number/120. */
285
        if (ds1wm_reset(ds1wm_data))
286
                return;
287
 
288
        ds1wm_write(ds1wm_data, search_type);
289
        ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA);
290
 
291
        for (rom_id = 0, i = 0; i < 16; i++) {
292
 
293
                unsigned char resp, r, d;
294
 
295
                resp = ds1wm_read(ds1wm_data, 0x00);
296
 
297
                r = ((resp & 0x02) >> 1) |
298
                    ((resp & 0x08) >> 2) |
299
                    ((resp & 0x20) >> 3) |
300
                    ((resp & 0x80) >> 4);
301
 
302
                d = ((resp & 0x01) >> 0) |
303
                    ((resp & 0x04) >> 1) |
304
                    ((resp & 0x10) >> 2) |
305
                    ((resp & 0x40) >> 3);
306
 
307
                rom_id |= (unsigned long long) r << (i * 4);
308
 
309
        }
310
        dev_dbg(&ds1wm_data->pdev->dev, "found 0x%08llX\n", rom_id);
311
 
312
        ds1wm_write_register(ds1wm_data, DS1WM_CMD, ~DS1WM_CMD_SRA);
313
        ds1wm_reset(ds1wm_data);
314
 
315
        slave_found(ds1wm_data, rom_id);
316
}
317
 
318
/* --------------------------------------------------------------------- */
319
 
320
static struct w1_bus_master ds1wm_master = {
321
        .read_byte  = ds1wm_read_byte,
322
        .write_byte = ds1wm_write_byte,
323
        .reset_bus  = ds1wm_reset_bus,
324
        .search     = ds1wm_search,
325
};
326
 
327
static int ds1wm_probe(struct platform_device *pdev)
328
{
329
        struct ds1wm_data *ds1wm_data;
330
        struct ds1wm_platform_data *plat;
331
        struct resource *res;
332
        int ret;
333
 
334
        if (!pdev)
335
                return -ENODEV;
336
 
337
        ds1wm_data = kzalloc(sizeof (*ds1wm_data), GFP_KERNEL);
338
        if (!ds1wm_data)
339
                return -ENOMEM;
340
 
341
        platform_set_drvdata(pdev, ds1wm_data);
342
 
343
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
344
        if (!res) {
345
                ret = -ENXIO;
346
                goto err0;
347
        }
348
        ds1wm_data->map = ioremap(res->start, res->end - res->start + 1);
349
        if (!ds1wm_data->map) {
350
                ret = -ENOMEM;
351
                goto err0;
352
        }
353
        plat = pdev->dev.platform_data;
354
        ds1wm_data->bus_shift = plat->bus_shift;
355
        ds1wm_data->pdev = pdev;
356
        ds1wm_data->pdata = plat;
357
 
358
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
359
        if (!res) {
360
                ret = -ENXIO;
361
                goto err1;
362
        }
363
        ds1wm_data->irq = res->start;
364
        ds1wm_data->active_high = (res->flags & IORESOURCE_IRQ_HIGHEDGE) ?
365
                1 : 0;
366
 
367
        set_irq_type(ds1wm_data->irq, ds1wm_data->active_high ?
368
                        IRQ_TYPE_EDGE_RISING : IRQ_TYPE_EDGE_FALLING);
369
 
370
        ret = request_irq(ds1wm_data->irq, ds1wm_isr, IRQF_DISABLED,
371
                          "ds1wm", ds1wm_data);
372
        if (ret)
373
                goto err1;
374
 
375
        ds1wm_data->clk = clk_get(&pdev->dev, "ds1wm");
376
        if (!ds1wm_data->clk) {
377
                ret = -ENOENT;
378
                goto err2;
379
        }
380
 
381
        ds1wm_up(ds1wm_data);
382
 
383
        ds1wm_master.data = (void *)ds1wm_data;
384
 
385
        ret = w1_add_master_device(&ds1wm_master);
386
        if (ret)
387
                goto err3;
388
 
389
        return 0;
390
 
391
err3:
392
        ds1wm_down(ds1wm_data);
393
        clk_put(ds1wm_data->clk);
394
err2:
395
        free_irq(ds1wm_data->irq, ds1wm_data);
396
err1:
397
        iounmap(ds1wm_data->map);
398
err0:
399
        kfree(ds1wm_data);
400
 
401
        return ret;
402
}
403
 
404
#ifdef CONFIG_PM
405
static int ds1wm_suspend(struct platform_device *pdev, pm_message_t state)
406
{
407
        struct ds1wm_data *ds1wm_data = platform_get_drvdata(pdev);
408
 
409
        ds1wm_down(ds1wm_data);
410
 
411
        return 0;
412
}
413
 
414
static int ds1wm_resume(struct platform_device *pdev)
415
{
416
        struct ds1wm_data *ds1wm_data = platform_get_drvdata(pdev);
417
 
418
        ds1wm_up(ds1wm_data);
419
 
420
        return 0;
421
}
422
#else
423
#define ds1wm_suspend NULL
424
#define ds1wm_resume NULL
425
#endif
426
 
427
static int ds1wm_remove(struct platform_device *pdev)
428
{
429
        struct ds1wm_data *ds1wm_data = platform_get_drvdata(pdev);
430
 
431
        w1_remove_master_device(&ds1wm_master);
432
        ds1wm_down(ds1wm_data);
433
        clk_put(ds1wm_data->clk);
434
        free_irq(ds1wm_data->irq, ds1wm_data);
435
        iounmap(ds1wm_data->map);
436
        kfree(ds1wm_data);
437
 
438
        return 0;
439
}
440
 
441
static struct platform_driver ds1wm_driver = {
442
        .driver   = {
443
                .name = "ds1wm",
444
        },
445
        .probe    = ds1wm_probe,
446
        .remove   = ds1wm_remove,
447
        .suspend  = ds1wm_suspend,
448
        .resume   = ds1wm_resume
449
};
450
 
451
static int __init ds1wm_init(void)
452
{
453
        printk("DS1WM w1 busmaster driver - (c) 2004 Szabolcs Gyurko\n");
454
        return platform_driver_register(&ds1wm_driver);
455
}
456
 
457
static void __exit ds1wm_exit(void)
458
{
459
        platform_driver_unregister(&ds1wm_driver);
460
}
461
 
462
module_init(ds1wm_init);
463
module_exit(ds1wm_exit);
464
 
465
MODULE_LICENSE("GPL");
466
MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, "
467
              "Matt Reimer <mreimer@vpop.net>");
468
MODULE_DESCRIPTION("DS1WM w1 busmaster driver");

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.