aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2007-02-12 03:52:48 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-12 12:48:31 -0500
commitb587b13a4f670ebae79ae6259cf44328455e4e69 (patch)
tree36e614fae95d8a2c888190d162e67ec95d144304 /drivers
parent3925a5ce44330767f7f0de5c58c6a797009f0f75 (diff)
[PATCH] SPI eeprom driver
This is adds a simple SPI EEPROM driver, providing access to the EEPROM through sysfs much like the I2C "eeprom" driver ... except this driver supports write access, and multiple EEPROM sizes. From: "Tuppa, Walter" <walter.tuppa@siemens.com> Since I have EEPROMs on SPI with different address sizing, I made some changes to your at25.c to support them. Works perfectly. (Also includes a small bugfix for the "what size address" test.) Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Walter Tuppa <walter.tuppa@siemens.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/spi/Kconfig10
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/at25.c381
3 files changed, 392 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b217a65453f5..9052f4c3493b 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -135,6 +135,16 @@ config SPI_S3C24XX_GPIO
135comment "SPI Protocol Masters" 135comment "SPI Protocol Masters"
136 depends on SPI_MASTER 136 depends on SPI_MASTER
137 137
138config SPI_AT25
139 tristate "SPI EEPROMs from most vendors"
140 depends on SPI_MASTER && SYSFS
141 help
142 Enable this driver to get read/write support to most SPI EEPROMs,
143 after you configure the board init code to know about each eeprom
144 on your target board.
145
146 This driver can also be built as a module. If so, the module
147 will be called at25.
138 148
139# 149#
140# Add new SPI protocol masters in alphabetical order above this line 150# Add new SPI protocol masters in alphabetical order above this line
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index e01104d1ebf8..bf271fe4e536 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
22# ... add above this line ... 22# ... add above this line ...
23 23
24# SPI protocol drivers (device/link on bus) 24# SPI protocol drivers (device/link on bus)
25obj-$(CONFIG_SPI_AT25) += at25.o
25# ... add above this line ... 26# ... add above this line ...
26 27
27# SPI slave controller drivers (upstream link) 28# SPI slave controller drivers (upstream link)
diff --git a/drivers/spi/at25.c b/drivers/spi/at25.c
new file mode 100644
index 000000000000..48e4f48e779f
--- /dev/null
+++ b/drivers/spi/at25.c
@@ -0,0 +1,381 @@
1/*
2 * at25.c -- support most SPI EEPROMs, such as Atmel AT25 models
3 *
4 * Copyright (C) 2006 David Brownell
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 as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12#include <linux/kernel.h>
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/slab.h>
16#include <linux/delay.h>
17#include <linux/device.h>
18#include <linux/sched.h>
19
20#include <linux/spi/spi.h>
21#include <linux/spi/eeprom.h>
22
23
24struct at25_data {
25 struct spi_device *spi;
26 struct mutex lock;
27 struct spi_eeprom chip;
28 struct bin_attribute bin;
29 unsigned addrlen;
30};
31
32#define AT25_WREN 0x06 /* latch the write enable */
33#define AT25_WRDI 0x04 /* reset the write enable */
34#define AT25_RDSR 0x05 /* read status register */
35#define AT25_WRSR 0x01 /* write status register */
36#define AT25_READ 0x03 /* read byte(s) */
37#define AT25_WRITE 0x02 /* write byte(s)/sector */
38
39#define AT25_SR_nRDY 0x01 /* nRDY = write-in-progress */
40#define AT25_SR_WEN 0x02 /* write enable (latched) */
41#define AT25_SR_BP0 0x04 /* BP for software writeprotect */
42#define AT25_SR_BP1 0x08
43#define AT25_SR_WPEN 0x80 /* writeprotect enable */
44
45
46#define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */
47
48/* Specs often allow 5 msec for a page write, sometimes 20 msec;
49 * it's important to recover from write timeouts.
50 */
51#define EE_TIMEOUT 25
52
53/*-------------------------------------------------------------------------*/
54
55#define io_limit PAGE_SIZE /* bytes */
56
57static ssize_t
58at25_ee_read(
59 struct at25_data *at25,
60 char *buf,
61 unsigned offset,
62 size_t count
63)
64{
65 u8 command[EE_MAXADDRLEN + 1];
66 u8 *cp;
67 ssize_t status;
68 struct spi_transfer t[2];
69 struct spi_message m;
70
71 cp = command;
72 *cp++ = AT25_READ;
73
74 /* 8/16/24-bit address is written MSB first */
75 switch (at25->addrlen) {
76 default: /* case 3 */
77 *cp++ = offset >> 16;
78 case 2:
79 *cp++ = offset >> 8;
80 case 1:
81 case 0: /* can't happen: for better codegen */
82 *cp++ = offset >> 0;
83 }
84
85 spi_message_init(&m);
86 memset(t, 0, sizeof t);
87
88 t[0].tx_buf = command;
89 t[0].len = at25->addrlen + 1;
90 spi_message_add_tail(&t[0], &m);
91
92 t[1].rx_buf = buf;
93 t[1].len = count;
94 spi_message_add_tail(&t[1], &m);
95
96 mutex_lock(&at25->lock);
97
98 /* Read it all at once.
99 *
100 * REVISIT that's potentially a problem with large chips, if
101 * other devices on the bus need to be accessed regularly or
102 * this chip is clocked very slowly
103 */
104 status = spi_sync(at25->spi, &m);
105 dev_dbg(&at25->spi->dev,
106 "read %Zd bytes at %d --> %d\n",
107 count, offset, (int) status);
108
109 mutex_unlock(&at25->lock);
110 return status ? status : count;
111}
112
113static ssize_t
114at25_bin_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
115{
116 struct device *dev;
117 struct at25_data *at25;
118
119 dev = container_of(kobj, struct device, kobj);
120 at25 = dev_get_drvdata(dev);
121
122 if (unlikely(off >= at25->bin.size))
123 return 0;
124 if ((off + count) > at25->bin.size)
125 count = at25->bin.size - off;
126 if (unlikely(!count))
127 return count;
128
129 return at25_ee_read(at25, buf, off, count);
130}
131
132
133static ssize_t
134at25_ee_write(struct at25_data *at25, char *buf, loff_t off, size_t count)
135{
136 ssize_t status = 0;
137 unsigned written = 0;
138 unsigned buf_size;
139 u8 *bounce;
140
141 /* Temp buffer starts with command and address */
142 buf_size = at25->chip.page_size;
143 if (buf_size > io_limit)
144 buf_size = io_limit;
145 bounce = kmalloc(buf_size + at25->addrlen + 1, GFP_KERNEL);
146 if (!bounce)
147 return -ENOMEM;
148
149 /* For write, rollover is within the page ... so we write at
150 * most one page, then manually roll over to the next page.
151 */
152 bounce[0] = AT25_WRITE;
153 mutex_lock(&at25->lock);
154 do {
155 unsigned long timeout, retries;
156 unsigned segment;
157 unsigned offset = (unsigned) off;
158 u8 *cp = bounce + 1;
159
160 *cp = AT25_WREN;
161 status = spi_write(at25->spi, cp, 1);
162 if (status < 0) {
163 dev_dbg(&at25->spi->dev, "WREN --> %d\n",
164 (int) status);
165 break;
166 }
167
168 /* 8/16/24-bit address is written MSB first */
169 switch (at25->addrlen) {
170 default: /* case 3 */
171 *cp++ = offset >> 16;
172 case 2:
173 *cp++ = offset >> 8;
174 case 1:
175 case 0: /* can't happen: for better codegen */
176 *cp++ = offset >> 0;
177 }
178
179 /* Write as much of a page as we can */
180 segment = buf_size - (offset % buf_size);
181 if (segment > count)
182 segment = count;
183 memcpy(cp, buf, segment);
184 status = spi_write(at25->spi, bounce,
185 segment + at25->addrlen + 1);
186 dev_dbg(&at25->spi->dev,
187 "write %u bytes at %u --> %d\n",
188 segment, offset, (int) status);
189 if (status < 0)
190 break;
191
192 /* REVISIT this should detect (or prevent) failed writes
193 * to readonly sections of the EEPROM...
194 */
195
196 /* Wait for non-busy status */
197 timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT);
198 retries = 0;
199 do {
200 int sr;
201
202 sr = spi_w8r8(at25->spi, AT25_RDSR);
203 if (sr < 0 || (sr & AT25_SR_nRDY)) {
204 dev_dbg(&at25->spi->dev,
205 "rdsr --> %d (%02x)\n", sr, sr);
206 /* at HZ=100, this is sloooow */
207 msleep(1);
208 continue;
209 }
210 if (!(sr & AT25_SR_nRDY))
211 break;
212 } while (retries++ < 3 || time_before_eq(jiffies, timeout));
213
214 if (time_after(jiffies, timeout)) {
215 dev_err(&at25->spi->dev,
216 "write %d bytes offset %d, "
217 "timeout after %u msecs\n",
218 segment, offset,
219 jiffies_to_msecs(jiffies -
220 (timeout - EE_TIMEOUT)));
221 status = -ETIMEDOUT;
222 break;
223 }
224
225 off += segment;
226 buf += segment;
227 count -= segment;
228 written += segment;
229
230 } while (count > 0);
231
232 mutex_unlock(&at25->lock);
233
234 kfree(bounce);
235 return written ? written : status;
236}
237
238static ssize_t
239at25_bin_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
240{
241 struct device *dev;
242 struct at25_data *at25;
243
244 dev = container_of(kobj, struct device, kobj);
245 at25 = dev_get_drvdata(dev);
246
247 if (unlikely(off >= at25->bin.size))
248 return -EFBIG;
249 if ((off + count) > at25->bin.size)
250 count = at25->bin.size - off;
251 if (unlikely(!count))
252 return count;
253
254 return at25_ee_write(at25, buf, off, count);
255}
256
257/*-------------------------------------------------------------------------*/
258
259static int at25_probe(struct spi_device *spi)
260{
261 struct at25_data *at25 = NULL;
262 const struct spi_eeprom *chip;
263 int err;
264 int sr;
265 int addrlen;
266
267 /* Chip description */
268 chip = spi->dev.platform_data;
269 if (!chip) {
270 dev_dbg(&spi->dev, "no chip description\n");
271 err = -ENODEV;
272 goto fail;
273 }
274
275 /* For now we only support 8/16/24 bit addressing */
276 if (chip->flags & EE_ADDR1)
277 addrlen = 1;
278 else if (chip->flags & EE_ADDR2)
279 addrlen = 2;
280 else if (chip->flags & EE_ADDR3)
281 addrlen = 3;
282 else {
283 dev_dbg(&spi->dev, "unsupported address type\n");
284 err = -EINVAL;
285 goto fail;
286 }
287
288 /* Ping the chip ... the status register is pretty portable,
289 * unlike probing manufacturer IDs. We do expect that system
290 * firmware didn't write it in the past few milliseconds!
291 */
292 sr = spi_w8r8(spi, AT25_RDSR);
293 if (sr < 0 || sr & AT25_SR_nRDY) {
294 dev_dbg(&at25->spi->dev, "rdsr --> %d (%02x)\n", sr, sr);
295 err = -ENXIO;
296 goto fail;
297 }
298
299 if (!(at25 = kzalloc(sizeof *at25, GFP_KERNEL))) {
300 err = -ENOMEM;
301 goto fail;
302 }
303
304 mutex_init(&at25->lock);
305 at25->chip = *chip;
306 at25->spi = spi_dev_get(spi);
307 dev_set_drvdata(&spi->dev, at25);
308 at25->addrlen = addrlen;
309
310 /* Export the EEPROM bytes through sysfs, since that's convenient.
311 * Default to root-only access to the data; EEPROMs often hold data
312 * that's sensitive for read and/or write, like ethernet addresses,
313 * security codes, board-specific manufacturing calibrations, etc.
314 */
315 at25->bin.attr.name = "eeprom";
316 at25->bin.attr.mode = S_IRUSR;
317 at25->bin.attr.owner = THIS_MODULE;
318 at25->bin.read = at25_bin_read;
319
320 at25->bin.size = at25->chip.byte_len;
321 if (!(chip->flags & EE_READONLY)) {
322 at25->bin.write = at25_bin_write;
323 at25->bin.attr.mode |= S_IWUSR;
324 }
325
326 err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin);
327 if (err)
328 goto fail;
329
330 dev_info(&spi->dev, "%Zd %s %s eeprom%s, pagesize %u\n",
331 (at25->bin.size < 1024)
332 ? at25->bin.size
333 : (at25->bin.size / 1024),
334 (at25->bin.size < 1024) ? "Byte" : "KByte",
335 at25->chip.name,
336 (chip->flags & EE_READONLY) ? " (readonly)" : "",
337 at25->chip.page_size);
338 return 0;
339fail:
340 dev_dbg(&spi->dev, "probe err %d\n", err);
341 kfree(at25);
342 return err;
343}
344
345static int __devexit at25_remove(struct spi_device *spi)
346{
347 struct at25_data *at25;
348
349 at25 = dev_get_drvdata(&spi->dev);
350 sysfs_remove_bin_file(&spi->dev.kobj, &at25->bin);
351 kfree(at25);
352 return 0;
353}
354
355/*-------------------------------------------------------------------------*/
356
357static struct spi_driver at25_driver = {
358 .driver = {
359 .name = "at25",
360 .owner = THIS_MODULE,
361 },
362 .probe = at25_probe,
363 .remove = __devexit_p(at25_remove),
364};
365
366static int __init at25_init(void)
367{
368 return spi_register_driver(&at25_driver);
369}
370module_init(at25_init);
371
372static void __exit at25_exit(void)
373{
374 spi_unregister_driver(&at25_driver);
375}
376module_exit(at25_exit);
377
378MODULE_DESCRIPTION("Driver for most SPI EEPROMs");
379MODULE_AUTHOR("David Brownell");
380MODULE_LICENSE("GPL");
381