aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/eeprom
diff options
context:
space:
mode:
authorAnatolij Gustschin <agust@denx.de>2011-07-25 20:13:27 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-07-25 23:57:16 -0400
commit06b4501e88ad10f02849a3f9d7408ed6ae15a53f (patch)
tree641a1c6044582b393ad4d877f6a9924cc495bfc0 /drivers/misc/eeprom
parent6e60c02e9d9427f59842192bdb123cbeaf8bc9a0 (diff)
misc/eeprom: add driver for microwire 93xx46 EEPROMs
Add EEPROM driver for 93xx46 chips. It can also be used with spi_gpio driver to access 93xx46 EEPROMs connected over GPIO lines. This driver supports read/write/erase access to the EEPROM chips over sysfs files. [rdunlap@xenotime.net: fix printk format] Signed-off-by: Anatolij Gustschin <agust@denx.de> Cc: Jonathan Cameron <jic23@cam.ac.uk> Cc: Grant Likely <grant.likely@secretlab.ca> Signed-off-by: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/misc/eeprom')
-rw-r--r--drivers/misc/eeprom/Kconfig13
-rw-r--r--drivers/misc/eeprom/Makefile1
-rw-r--r--drivers/misc/eeprom/eeprom_93xx46.c410
3 files changed, 424 insertions, 0 deletions
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 9118613af321..620de283b40b 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -70,4 +70,17 @@ config EEPROM_93CX6
70 70
71 If unsure, say N. 71 If unsure, say N.
72 72
73config EEPROM_93XX46
74 tristate "Microwire EEPROM 93XX46 support"
75 depends on SPI && SYSFS
76 help
77 Driver for the microwire EEPROM chipsets 93xx46x. The driver
78 supports both read and write commands and also the command to
79 erase the whole EEPROM.
80
81 This driver can also be built as a module. If so, the module
82 will be called eeprom_93xx46.
83
84 If unsure, say N.
85
73endmenu 86endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index df3d68ffa9d1..ec55aad24cfb 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_EEPROM_AT25) += at25.o
3obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o 3obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
4obj-$(CONFIG_EEPROM_MAX6875) += max6875.o 4obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
5obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o 5obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
6obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o
diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c
new file mode 100644
index 000000000000..0c7ebb1e19e5
--- /dev/null
+++ b/drivers/misc/eeprom/eeprom_93xx46.c
@@ -0,0 +1,410 @@
1/*
2 * Driver for 93xx46 EEPROMs
3 *
4 * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/delay.h>
12#include <linux/device.h>
13#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/mutex.h>
17#include <linux/slab.h>
18#include <linux/spi/spi.h>
19#include <linux/sysfs.h>
20#include <linux/eeprom_93xx46.h>
21
22#define OP_START 0x4
23#define OP_WRITE (OP_START | 0x1)
24#define OP_READ (OP_START | 0x2)
25#define ADDR_EWDS 0x00
26#define ADDR_ERAL 0x20
27#define ADDR_EWEN 0x30
28
29struct eeprom_93xx46_dev {
30 struct spi_device *spi;
31 struct eeprom_93xx46_platform_data *pdata;
32 struct bin_attribute bin;
33 struct mutex lock;
34 int addrlen;
35};
36
37static ssize_t
38eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj,
39 struct bin_attribute *bin_attr,
40 char *buf, loff_t off, size_t count)
41{
42 struct eeprom_93xx46_dev *edev;
43 struct device *dev;
44 struct spi_message m;
45 struct spi_transfer t[2];
46 int bits, ret;
47 u16 cmd_addr;
48
49 dev = container_of(kobj, struct device, kobj);
50 edev = dev_get_drvdata(dev);
51
52 if (unlikely(off >= edev->bin.size))
53 return 0;
54 if ((off + count) > edev->bin.size)
55 count = edev->bin.size - off;
56 if (unlikely(!count))
57 return count;
58
59 cmd_addr = OP_READ << edev->addrlen;
60
61 if (edev->addrlen == 7) {
62 cmd_addr |= off & 0x7f;
63 bits = 10;
64 } else {
65 cmd_addr |= off & 0x3f;
66 bits = 9;
67 }
68
69 dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n",
70 cmd_addr, edev->spi->max_speed_hz);
71
72 spi_message_init(&m);
73 memset(t, 0, sizeof(t));
74
75 t[0].tx_buf = (char *)&cmd_addr;
76 t[0].len = 2;
77 t[0].bits_per_word = bits;
78 spi_message_add_tail(&t[0], &m);
79
80 t[1].rx_buf = buf;
81 t[1].len = count;
82 t[1].bits_per_word = 8;
83 spi_message_add_tail(&t[1], &m);
84
85 mutex_lock(&edev->lock);
86
87 if (edev->pdata->prepare)
88 edev->pdata->prepare(edev);
89
90 ret = spi_sync(edev->spi, &m);
91 /* have to wait at least Tcsl ns */
92 ndelay(250);
93 if (ret) {
94 dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n",
95 count, (int)off, ret);
96 }
97
98 if (edev->pdata->finish)
99 edev->pdata->finish(edev);
100
101 mutex_unlock(&edev->lock);
102 return ret ? : count;
103}
104
105static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on)
106{
107 struct spi_message m;
108 struct spi_transfer t;
109 int bits, ret;
110 u16 cmd_addr;
111
112 cmd_addr = OP_START << edev->addrlen;
113 if (edev->addrlen == 7) {
114 cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS) << 1;
115 bits = 10;
116 } else {
117 cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS);
118 bits = 9;
119 }
120
121 dev_dbg(&edev->spi->dev, "ew cmd 0x%04x\n", cmd_addr);
122
123 spi_message_init(&m);
124 memset(&t, 0, sizeof(t));
125
126 t.tx_buf = &cmd_addr;
127 t.len = 2;
128 t.bits_per_word = bits;
129 spi_message_add_tail(&t, &m);
130
131 mutex_lock(&edev->lock);
132
133 if (edev->pdata->prepare)
134 edev->pdata->prepare(edev);
135
136 ret = spi_sync(edev->spi, &m);
137 /* have to wait at least Tcsl ns */
138 ndelay(250);
139 if (ret)
140 dev_err(&edev->spi->dev, "erase/write %sable error %d\n",
141 is_on ? "en" : "dis", ret);
142
143 if (edev->pdata->finish)
144 edev->pdata->finish(edev);
145
146 mutex_unlock(&edev->lock);
147 return ret;
148}
149
150static ssize_t
151eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev,
152 const char *buf, unsigned off)
153{
154 struct spi_message m;
155 struct spi_transfer t[2];
156 int bits, data_len, ret;
157 u16 cmd_addr;
158
159 cmd_addr = OP_WRITE << edev->addrlen;
160
161 if (edev->addrlen == 7) {
162 cmd_addr |= off & 0x7f;
163 bits = 10;
164 data_len = 1;
165 } else {
166 cmd_addr |= off & 0x3f;
167 bits = 9;
168 data_len = 2;
169 }
170
171 dev_dbg(&edev->spi->dev, "write cmd 0x%x\n", cmd_addr);
172
173 spi_message_init(&m);
174 memset(t, 0, sizeof(t));
175
176 t[0].tx_buf = (char *)&cmd_addr;
177 t[0].len = 2;
178 t[0].bits_per_word = bits;
179 spi_message_add_tail(&t[0], &m);
180
181 t[1].tx_buf = buf;
182 t[1].len = data_len;
183 t[1].bits_per_word = 8;
184 spi_message_add_tail(&t[1], &m);
185
186 ret = spi_sync(edev->spi, &m);
187 /* have to wait program cycle time Twc ms */
188 mdelay(6);
189 return ret;
190}
191
192static ssize_t
193eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj,
194 struct bin_attribute *bin_attr,
195 char *buf, loff_t off, size_t count)
196{
197 struct eeprom_93xx46_dev *edev;
198 struct device *dev;
199 int i, ret, step = 1;
200
201 dev = container_of(kobj, struct device, kobj);
202 edev = dev_get_drvdata(dev);
203
204 if (unlikely(off >= edev->bin.size))
205 return 0;
206 if ((off + count) > edev->bin.size)
207 count = edev->bin.size - off;
208 if (unlikely(!count))
209 return count;
210
211 /* only write even number of bytes on 16-bit devices */
212 if (edev->addrlen == 6) {
213 step = 2;
214 count &= ~1;
215 }
216
217 /* erase/write enable */
218 ret = eeprom_93xx46_ew(edev, 1);
219 if (ret)
220 return ret;
221
222 mutex_lock(&edev->lock);
223
224 if (edev->pdata->prepare)
225 edev->pdata->prepare(edev);
226
227 for (i = 0; i < count; i += step) {
228 ret = eeprom_93xx46_write_word(edev, &buf[i], off + i);
229 if (ret) {
230 dev_err(&edev->spi->dev, "write failed at %d: %d\n",
231 (int)off + i, ret);
232 break;
233 }
234 }
235
236 if (edev->pdata->finish)
237 edev->pdata->finish(edev);
238
239 mutex_unlock(&edev->lock);
240
241 /* erase/write disable */
242 eeprom_93xx46_ew(edev, 0);
243 return ret ? : count;
244}
245
246static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev)
247{
248 struct eeprom_93xx46_platform_data *pd = edev->pdata;
249 struct spi_message m;
250 struct spi_transfer t;
251 int bits, ret;
252 u16 cmd_addr;
253
254 cmd_addr = OP_START << edev->addrlen;
255 if (edev->addrlen == 7) {
256 cmd_addr |= ADDR_ERAL << 1;
257 bits = 10;
258 } else {
259 cmd_addr |= ADDR_ERAL;
260 bits = 9;
261 }
262
263 spi_message_init(&m);
264 memset(&t, 0, sizeof(t));
265
266 t.tx_buf = &cmd_addr;
267 t.len = 2;
268 t.bits_per_word = bits;
269 spi_message_add_tail(&t, &m);
270
271 mutex_lock(&edev->lock);
272
273 if (edev->pdata->prepare)
274 edev->pdata->prepare(edev);
275
276 ret = spi_sync(edev->spi, &m);
277 if (ret)
278 dev_err(&edev->spi->dev, "erase error %d\n", ret);
279 /* have to wait erase cycle time Tec ms */
280 mdelay(6);
281
282 if (pd->finish)
283 pd->finish(edev);
284
285 mutex_unlock(&edev->lock);
286 return ret;
287}
288
289static ssize_t eeprom_93xx46_store_erase(struct device *dev,
290 struct device_attribute *attr,
291 const char *buf, size_t count)
292{
293 struct eeprom_93xx46_dev *edev = dev_get_drvdata(dev);
294 int erase = 0, ret;
295
296 sscanf(buf, "%d", &erase);
297 if (erase) {
298 ret = eeprom_93xx46_ew(edev, 1);
299 if (ret)
300 return ret;
301 ret = eeprom_93xx46_eral(edev);
302 if (ret)
303 return ret;
304 ret = eeprom_93xx46_ew(edev, 0);
305 if (ret)
306 return ret;
307 }
308 return count;
309}
310static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase);
311
312static int __devinit eeprom_93xx46_probe(struct spi_device *spi)
313{
314 struct eeprom_93xx46_platform_data *pd;
315 struct eeprom_93xx46_dev *edev;
316 int err;
317
318 pd = spi->dev.platform_data;
319 if (!pd) {
320 dev_err(&spi->dev, "missing platform data\n");
321 return -ENODEV;
322 }
323
324 edev = kzalloc(sizeof(*edev), GFP_KERNEL);
325 if (!edev)
326 return -ENOMEM;
327
328 if (pd->flags & EE_ADDR8)
329 edev->addrlen = 7;
330 else if (pd->flags & EE_ADDR16)
331 edev->addrlen = 6;
332 else {
333 dev_err(&spi->dev, "unspecified address type\n");
334 err = -EINVAL;
335 goto fail;
336 }
337
338 mutex_init(&edev->lock);
339
340 edev->spi = spi_dev_get(spi);
341 edev->pdata = pd;
342
343 sysfs_bin_attr_init(&edev->bin);
344 edev->bin.attr.name = "eeprom";
345 edev->bin.attr.mode = S_IRUSR;
346 edev->bin.read = eeprom_93xx46_bin_read;
347 edev->bin.size = 128;
348 if (!(pd->flags & EE_READONLY)) {
349 edev->bin.write = eeprom_93xx46_bin_write;
350 edev->bin.attr.mode |= S_IWUSR;
351 }
352
353 err = sysfs_create_bin_file(&spi->dev.kobj, &edev->bin);
354 if (err)
355 goto fail;
356
357 dev_info(&spi->dev, "%d-bit eeprom %s\n",
358 (pd->flags & EE_ADDR8) ? 8 : 16,
359 (pd->flags & EE_READONLY) ? "(readonly)" : "");
360
361 if (!(pd->flags & EE_READONLY)) {
362 if (device_create_file(&spi->dev, &dev_attr_erase))
363 dev_err(&spi->dev, "can't create erase interface\n");
364 }
365
366 dev_set_drvdata(&spi->dev, edev);
367 return 0;
368fail:
369 kfree(edev);
370 return err;
371}
372
373static int __devexit eeprom_93xx46_remove(struct spi_device *spi)
374{
375 struct eeprom_93xx46_dev *edev = dev_get_drvdata(&spi->dev);
376
377 if (!(edev->pdata->flags & EE_READONLY))
378 device_remove_file(&spi->dev, &dev_attr_erase);
379
380 sysfs_remove_bin_file(&spi->dev.kobj, &edev->bin);
381 dev_set_drvdata(&spi->dev, NULL);
382 kfree(edev);
383 return 0;
384}
385
386static struct spi_driver eeprom_93xx46_driver = {
387 .driver = {
388 .name = "93xx46",
389 .owner = THIS_MODULE,
390 },
391 .probe = eeprom_93xx46_probe,
392 .remove = __devexit_p(eeprom_93xx46_remove),
393};
394
395static int __init eeprom_93xx46_init(void)
396{
397 return spi_register_driver(&eeprom_93xx46_driver);
398}
399module_init(eeprom_93xx46_init);
400
401static void __exit eeprom_93xx46_exit(void)
402{
403 spi_unregister_driver(&eeprom_93xx46_driver);
404}
405module_exit(eeprom_93xx46_exit);
406
407MODULE_LICENSE("GPL");
408MODULE_DESCRIPTION("Driver for 93xx46 EEPROMs");
409MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
410MODULE_ALIAS("spi:93xx46");