[PATCH] w1: Added DS2433 driver.
[powerpc.git] / drivers / w1 / w1_ds2433.c
1 /*
2  *      w1_ds2433.c - w1 family 23 (DS2433) driver
3  *
4  * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the smems of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  */
10
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/moduleparam.h>
14 #include <linux/device.h>
15 #include <linux/types.h>
16 #include <linux/delay.h>
17
18 #include "w1.h"
19 #include "w1_io.h"
20 #include "w1_int.h"
21 #include "w1_family.h"
22
23 MODULE_LICENSE("GPL");
24 MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
25 MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM");
26
27 #define W1_EEPROM_SIZE          512
28 #define W1_PAGE_SIZE            32
29 #define W1_PAGE_BITS            5
30 #define W1_PAGE_MASK            0x1F
31
32 #define W1_F23_READ_EEPROM      0xF0
33 #define W1_F23_WRITE_SCRATCH    0x0F
34 #define W1_F23_READ_SCRATCH     0xAA
35 #define W1_F23_COPY_SCRATCH     0x55
36
37 /**
38  * Check the file size bounds and adjusts count as needed.
39  * This may not be needed if the sysfs layer checks bounds.
40  */
41 static inline size_t w1_f23_fix_count(loff_t off, size_t count, size_t size)
42 {
43         if (off > size)
44                 return 0;
45
46         if ((off + count) > size)
47                 return (size - off);
48
49         return count;
50 }
51
52 static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off, size_t count)
53 {
54         struct w1_slave *sl = kobj_to_w1_slave(kobj);
55         u8 wrbuf[3];
56
57         if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
58                 return 0;
59
60         atomic_inc(&sl->refcnt);
61         if (down_interruptible(&sl->master->mutex)) {
62                 count = 0;
63                 goto out_dec;
64         }
65
66         /* read directly from the EEPROM */
67         if (w1_reset_select_slave(sl)) {
68                 count = -EIO;
69                 goto out_up;
70         }
71
72         wrbuf[0] = W1_F23_READ_EEPROM;
73         wrbuf[1] = off & 0xff;
74         wrbuf[2] = off >> 8;
75         w1_write_block(sl->master, wrbuf, 3);
76         w1_read_block(sl->master, buf, count);
77
78 out_up:
79         up(&sl->master->mutex);
80 out_dec:
81         atomic_dec(&sl->refcnt);
82
83         return count;
84 }
85
86 /**
87  * Writes to the scratchpad and reads it back for verification.
88  * The master must be locked.
89  *
90  * @param sl    The slave structure
91  * @param addr  Address for the write
92  * @param len   length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK))
93  * @param data  The data to write
94  * @return      0=Success -1=failure
95  */
96 static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data)
97 {
98         u8 wrbuf[4];
99         u8 rdbuf[W1_PAGE_SIZE + 3];
100         u8 es = (addr + len - 1) & 0x1f;
101
102         /* Write the data to the scratchpad */
103         if (w1_reset_select_slave(sl))
104                 return -1;
105
106         wrbuf[0] = W1_F23_WRITE_SCRATCH;
107         wrbuf[1] = addr & 0xff;
108         wrbuf[2] = addr >> 8;
109
110         w1_write_block(sl->master, wrbuf, 3);
111         w1_write_block(sl->master, data, len);
112
113         /* Read the scratchpad and verify */
114         if (w1_reset_select_slave(sl))
115                 return -1;
116
117         w1_write_8(sl->master, W1_F23_READ_SCRATCH);
118         w1_read_block(sl->master, rdbuf, len + 3);
119
120         /* Compare what was read against the data written */
121         if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) ||
122             (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0))
123                 return -1;
124
125         /* Copy the scratchpad to EEPROM */
126         if (w1_reset_select_slave(sl))
127                 return -1;
128
129         wrbuf[0] = W1_F23_COPY_SCRATCH;
130         wrbuf[3] = es;
131         w1_write_block(sl->master, wrbuf, 4);
132
133         /* Sleep for 5 ms to wait for the write to complete */
134         msleep(5);
135
136         /* Reset the bus to wake up the EEPROM (this may not be needed) */
137         w1_reset_bus(sl->master);
138
139         return 0;
140 }
141
142 static ssize_t w1_f23_write_bin(struct kobject *kobj, char *buf, loff_t off,
143                                 size_t count)
144 {
145         struct w1_slave *sl = kobj_to_w1_slave(kobj);
146         int addr, len, idx;
147
148         if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
149                 return 0;
150
151         atomic_inc(&sl->refcnt);
152         if (down_interruptible(&sl->master->mutex)) {
153                 count = 0;
154                 goto out_dec;
155         }
156
157         /* Can only write data to one page at a time */
158         idx = 0;
159         while (idx < count) {
160                 addr = off + idx;
161                 len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK);
162                 if (len > (count - idx))
163                         len = count - idx;
164
165                 if (w1_f23_write(sl, addr, len, &buf[idx]) < 0) {
166                         count = -EIO;
167                         goto out_up;
168                 }
169                 idx += len;
170         }
171
172 out_up:
173         up(&sl->master->mutex);
174 out_dec:
175         atomic_dec(&sl->refcnt);
176
177         return count;
178 }
179
180 static struct bin_attribute w1_f23_bin_attr = {
181         .attr = {
182                 .name = "eeprom",
183                 .mode = S_IRUGO | S_IWUSR,
184                 .owner = THIS_MODULE,
185         },
186         .size = W1_EEPROM_SIZE,
187         .read = w1_f23_read_bin,
188         .write = w1_f23_write_bin,
189 };
190
191 static int w1_f23_add_slave(struct w1_slave *sl)
192 {
193         return sysfs_create_bin_file(&sl->dev.kobj, &w1_f23_bin_attr);
194 }
195
196 static void w1_f23_remove_slave(struct w1_slave *sl)
197 {
198         sysfs_remove_bin_file(&sl->dev.kobj, &w1_f23_bin_attr);
199 }
200
201 static struct w1_family_ops w1_f23_fops = {
202         .add_slave      = w1_f23_add_slave,
203         .remove_slave   = w1_f23_remove_slave,
204 };
205
206 static struct w1_family w1_family_23 = {
207         .fid = W1_EEPROM_DS2433,
208         .fops = &w1_f23_fops,
209 };
210
211 static int __init w1_f23_init(void)
212 {
213         return w1_register_family(&w1_family_23);
214 }
215
216 static void __exit w1_f23_fini(void)
217 {
218         w1_unregister_family(&w1_family_23);
219 }
220
221 module_init(w1_f23_init);
222 module_exit(w1_f23_fini);