aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyan Mallon <ryan@bluewatersys.com>2009-09-18 15:51:40 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2009-09-19 13:54:45 -0400
commitec77e21b91f0393a5201cfd4571a82ab7d64fd29 (patch)
treed50c9d344ceffc48e2e7ac4cb3c030021bd162bf
parent91e0955b57377578f7555b5d0f2a21040691004b (diff)
mtd: SST25L (non JEDEC) SPI Flash driver
Add support for the non JEDEC SST25L SPI Flash devices. [dwmw2: Some cleanups] Signed-off-by: Andre Renaud <andre@bluewatersys.com> Signed-off-by: Ryan Mallon <ryan@bluewatersys.com> Acked-by: Linus Walleij <linus.walleij@stericsson.com> Cc: Anton Vorontsov <avorontsov@ru.mvista.com> Cc: "H Hartley Sweeten" <hartleys@visionengravers.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r--drivers/mtd/devices/Kconfig10
-rw-r--r--drivers/mtd/devices/Makefile1
-rw-r--r--drivers/mtd/devices/sst25l.c510
3 files changed, 521 insertions, 0 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 325fab92a62c..c222514bb70d 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -104,6 +104,16 @@ config M25PXX_USE_FAST_READ
104 help 104 help
105 This option enables FAST_READ access supported by ST M25Pxx. 105 This option enables FAST_READ access supported by ST M25Pxx.
106 106
107config MTD_SST25L
108 tristate "Support SST25L (non JEDEC) SPI Flash chips"
109 depends on SPI_MASTER
110 help
111 This enables access to the non JEDEC SST25L SPI flash chips, used
112 for program and data storage.
113
114 Set up your spi devices with the right board-specific platform data,
115 if you want to specify device partitioning.
116
107config MTD_SLRAM 117config MTD_SLRAM
108 tristate "Uncached system RAM" 118 tristate "Uncached system RAM"
109 help 119 help
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 0993d5cf3923..ab5c9b92ac82 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o
16obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o 16obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
17obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o 17obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
18obj-$(CONFIG_MTD_M25P80) += m25p80.o 18obj-$(CONFIG_MTD_M25P80) += m25p80.o
19obj-$(CONFIG_MTD_SST25L) += sst25l.o
diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c
new file mode 100644
index 000000000000..ac7d52b420cc
--- /dev/null
+++ b/drivers/mtd/devices/sst25l.c
@@ -0,0 +1,510 @@
1/*
2 * sst25l.c
3 *
4 * Driver for SST25L SPI Flash chips
5 *
6 * Copyright © 2009 Bluewater Systems Ltd
7 * Author: Andre Renaud <andre@bluewatersys.com>
8 * Author: Ryan Mallon <ryan@bluewatersys.com>
9 *
10 * Based on m25p80.c
11 *
12 * This code is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License version 2 as
14 * published by the Free Software Foundation.
15 *
16 */
17
18#include <linux/init.h>
19#include <linux/module.h>
20#include <linux/device.h>
21#include <linux/mutex.h>
22#include <linux/interrupt.h>
23
24#include <linux/mtd/mtd.h>
25#include <linux/mtd/partitions.h>
26
27#include <linux/spi/spi.h>
28#include <linux/spi/flash.h>
29
30/* Erases can take up to 3 seconds! */
31#define MAX_READY_WAIT_JIFFIES msecs_to_jiffies(3000)
32
33#define SST25L_CMD_WRSR 0x01 /* Write status register */
34#define SST25L_CMD_WRDI 0x04 /* Write disable */
35#define SST25L_CMD_RDSR 0x05 /* Read status register */
36#define SST25L_CMD_WREN 0x06 /* Write enable */
37#define SST25L_CMD_READ 0x03 /* High speed read */
38
39#define SST25L_CMD_EWSR 0x50 /* Enable write status register */
40#define SST25L_CMD_SECTOR_ERASE 0x20 /* Erase sector */
41#define SST25L_CMD_READ_ID 0x90 /* Read device ID */
42#define SST25L_CMD_AAI_PROGRAM 0xaf /* Auto address increment */
43
44#define SST25L_STATUS_BUSY (1 << 0) /* Chip is busy */
45#define SST25L_STATUS_WREN (1 << 1) /* Write enabled */
46#define SST25L_STATUS_BP0 (1 << 2) /* Block protection 0 */
47#define SST25L_STATUS_BP1 (1 << 3) /* Block protection 1 */
48
49struct sst25l_flash {
50 struct spi_device *spi;
51 struct mutex lock;
52 struct mtd_info mtd;
53
54 int partitioned;
55};
56
57struct flash_info {
58 const char *name;
59 uint16_t device_id;
60 unsigned page_size;
61 unsigned nr_pages;
62 unsigned erase_size;
63};
64
65#define to_sst25l_flash(x) container_of(x, struct sst25l_flash, mtd)
66
67static struct flash_info __initdata sst25l_flash_info[] = {
68 {"sst25lf020a", 0xbf43, 256, 1024, 4096},
69 {"sst25lf040a", 0xbf44, 256, 2048, 4096},
70};
71
72static int sst25l_status(struct sst25l_flash *flash, int *status)
73{
74 unsigned char command, response;
75 int err;
76
77 command = SST25L_CMD_RDSR;
78 err = spi_write_then_read(flash->spi, &command, 1, &response, 1);
79 if (err < 0)
80 return err;
81
82 *status = response;
83 return 0;
84}
85
86static int sst25l_write_enable(struct sst25l_flash *flash, int enable)
87{
88 unsigned char command[2];
89 int status, err;
90
91 command[0] = enable ? SST25L_CMD_WREN : SST25L_CMD_WRDI;
92 err = spi_write(flash->spi, command, 1);
93 if (err)
94 return err;
95
96 command[0] = SST25L_CMD_EWSR;
97 err = spi_write(flash->spi, command, 1);
98 if (err)
99 return err;
100
101 command[0] = SST25L_CMD_WRSR;
102 command[1] = enable ? 0 : SST25L_STATUS_BP0 | SST25L_STATUS_BP1;
103 err = spi_write(flash->spi, command, 2);
104 if (err)
105 return err;
106
107 if (enable) {
108 err = sst25l_status(flash, &status);
109 if (err)
110 return err;
111 if (!(status & SST25L_STATUS_WREN))
112 return -EROFS;
113 }
114
115 return 0;
116}
117
118static int sst25l_wait_till_ready(struct sst25l_flash *flash)
119{
120 unsigned long deadline;
121 int status, err;
122
123 deadline = jiffies + MAX_READY_WAIT_JIFFIES;
124 do {
125 err = sst25l_status(flash, &status);
126 if (err)
127 return err;
128 if (!(status & SST25L_STATUS_BUSY))
129 return 0;
130
131 cond_resched();
132 } while (!time_after_eq(jiffies, deadline));
133
134 return -ETIMEDOUT;
135}
136
137static int sst25l_erase_sector(struct sst25l_flash *flash, uint32_t offset)
138{
139 unsigned char command[4];
140 int err;
141
142 err = sst25l_write_enable(flash, 1);
143 if (err)
144 return err;
145
146 command[0] = SST25L_CMD_SECTOR_ERASE;
147 command[1] = offset >> 16;
148 command[2] = offset >> 8;
149 command[3] = offset;
150 err = spi_write(flash->spi, command, 4);
151 if (err)
152 return err;
153
154 err = sst25l_wait_till_ready(flash);
155 if (err)
156 return err;
157
158 return sst25l_write_enable(flash, 0);
159}
160
161static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
162{
163 struct sst25l_flash *flash = to_sst25l_flash(mtd);
164 uint32_t addr, end;
165 int err;
166
167 /* Sanity checks */
168 if (instr->addr + instr->len > flash->mtd.size)
169 return -EINVAL;
170
171 if ((uint32_t)instr->len % mtd->erasesize)
172 return -EINVAL;
173
174 if ((uint32_t)instr->addr % mtd->erasesize)
175 return -EINVAL;
176
177 addr = instr->addr;
178 end = addr + instr->len;
179
180 mutex_lock(&flash->lock);
181
182 err = sst25l_wait_till_ready(flash);
183 if (err)
184 return err;
185
186 while (addr < end) {
187 err = sst25l_erase_sector(flash, addr);
188 if (err) {
189 mutex_unlock(&flash->lock);
190 instr->state = MTD_ERASE_FAILED;
191 dev_err(&flash->spi->dev, "Erase failed\n");
192 return err;
193 }
194
195 addr += mtd->erasesize;
196 }
197
198 mutex_unlock(&flash->lock);
199
200 instr->state = MTD_ERASE_DONE;
201 mtd_erase_callback(instr);
202 return 0;
203}
204
205static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len,
206 size_t *retlen, unsigned char *buf)
207{
208 struct sst25l_flash *flash = to_sst25l_flash(mtd);
209 struct spi_transfer transfer[2];
210 struct spi_message message;
211 unsigned char command[4];
212 int ret;
213
214 /* Sanity checking */
215 if (len == 0)
216 return 0;
217
218 if (from + len > flash->mtd.size)
219 return -EINVAL;
220
221 if (retlen)
222 *retlen = 0;
223
224 spi_message_init(&message);
225 memset(&transfer, 0, sizeof(transfer));
226
227 command[0] = SST25L_CMD_READ;
228 command[1] = from >> 16;
229 command[2] = from >> 8;
230 command[3] = from;
231
232 transfer[0].tx_buf = command;
233 transfer[0].len = sizeof(command);
234 spi_message_add_tail(&transfer[0], &message);
235
236 transfer[1].rx_buf = buf;
237 transfer[1].len = len;
238 spi_message_add_tail(&transfer[1], &message);
239
240 mutex_lock(&flash->lock);
241
242 /* Wait for previous write/erase to complete */
243 ret = sst25l_wait_till_ready(flash);
244 if (ret) {
245 mutex_unlock(&flash->lock);
246 return ret;
247 }
248
249 spi_sync(flash->spi, &message);
250
251 if (retlen && message.actual_length > sizeof(command))
252 *retlen += message.actual_length - sizeof(command);
253
254 mutex_unlock(&flash->lock);
255 return 0;
256}
257
258static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len,
259 size_t *retlen, const unsigned char *buf)
260{
261 struct sst25l_flash *flash = to_sst25l_flash(mtd);
262 int i, j, ret, bytes, copied = 0;
263 unsigned char command[5];
264
265 /* Sanity checks */
266 if (!len)
267 return 0;
268
269 if (to + len > flash->mtd.size)
270 return -EINVAL;
271
272 if ((uint32_t)to % mtd->writesize)
273 return -EINVAL;
274
275 mutex_lock(&flash->lock);
276
277 ret = sst25l_write_enable(flash, 1);
278 if (ret)
279 goto out;
280
281 for (i = 0; i < len; i += mtd->writesize) {
282 ret = sst25l_wait_till_ready(flash);
283 if (ret)
284 goto out;
285
286 /* Write the first byte of the page */
287 command[0] = SST25L_CMD_AAI_PROGRAM;
288 command[1] = (to + i) >> 16;
289 command[2] = (to + i) >> 8;
290 command[3] = (to + i);
291 command[4] = buf[i];
292 ret = spi_write(flash->spi, command, 5);
293 if (ret < 0)
294 goto out;
295 copied++;
296
297 /*
298 * Write the remaining bytes using auto address
299 * increment mode
300 */
301 bytes = min_t(uint32_t, mtd->writesize, len - i);
302 for (j = 1; j < bytes; j++, copied++) {
303 ret = sst25l_wait_till_ready(flash);
304 if (ret)
305 goto out;
306
307 command[1] = buf[i + j];
308 ret = spi_write(flash->spi, command, 2);
309 if (ret)
310 goto out;
311 }
312 }
313
314out:
315 ret = sst25l_write_enable(flash, 0);
316
317 if (retlen)
318 *retlen = copied;
319
320 mutex_unlock(&flash->lock);
321 return ret;
322}
323
324static struct flash_info *__init sst25l_match_device(struct spi_device *spi)
325{
326 struct flash_info *flash_info = NULL;
327 unsigned char command[4], response;
328 int i, err;
329 uint16_t id;
330
331 command[0] = SST25L_CMD_READ_ID;
332 command[1] = 0;
333 command[2] = 0;
334 command[3] = 0;
335 err = spi_write_then_read(spi, command, sizeof(command), &response, 1);
336 if (err < 0) {
337 dev_err(&spi->dev, "error reading device id msb\n");
338 return NULL;
339 }
340
341 id = response << 8;
342
343 command[0] = SST25L_CMD_READ_ID;
344 command[1] = 0;
345 command[2] = 0;
346 command[3] = 1;
347 err = spi_write_then_read(spi, command, sizeof(command), &response, 1);
348 if (err < 0) {
349 dev_err(&spi->dev, "error reading device id lsb\n");
350 return NULL;
351 }
352
353 id |= response;
354
355 for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); i++)
356 if (sst25l_flash_info[i].device_id == id)
357 flash_info = &sst25l_flash_info[i];
358
359 if (!flash_info)
360 dev_err(&spi->dev, "unknown id %.4x\n", id);
361
362 return flash_info;
363}
364
365static int __init sst25l_probe(struct spi_device *spi)
366{
367 struct flash_info *flash_info;
368 struct sst25l_flash *flash;
369 struct flash_platform_data *data;
370 int ret, i;
371
372 flash_info = sst25l_match_device(spi);
373 if (!flash_info)
374 return -ENODEV;
375
376 flash = kzalloc(sizeof(struct sst25l_flash), GFP_KERNEL);
377 if (!flash)
378 return -ENOMEM;
379
380 flash->spi = spi;
381 mutex_init(&flash->lock);
382 dev_set_drvdata(&spi->dev, flash);
383
384 data = spi->dev.platform_data;
385 if (data && data->name)
386 flash->mtd.name = data->name;
387 else
388 flash->mtd.name = dev_name(&spi->dev);
389
390 flash->mtd.type = MTD_NORFLASH;
391 flash->mtd.flags = MTD_CAP_NORFLASH;
392 flash->mtd.erasesize = flash_info->erase_size;
393 flash->mtd.writesize = flash_info->page_size;
394 flash->mtd.size = flash_info->page_size * flash_info->nr_pages;
395 flash->mtd.erase = sst25l_erase;
396 flash->mtd.read = sst25l_read;
397 flash->mtd.write = sst25l_write;
398
399 dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name,
400 (long long)flash->mtd.size >> 10);
401
402 DEBUG(MTD_DEBUG_LEVEL2,
403 "mtd .name = %s, .size = 0x%llx (%lldMiB) "
404 ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
405 flash->mtd.name,
406 (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20),
407 flash->mtd.erasesize, flash->mtd.erasesize / 1024,
408 flash->mtd.numeraseregions);
409
410 if (flash->mtd.numeraseregions)
411 for (i = 0; i < flash->mtd.numeraseregions; i++)
412 DEBUG(MTD_DEBUG_LEVEL2,
413 "mtd.eraseregions[%d] = { .offset = 0x%llx, "
414 ".erasesize = 0x%.8x (%uKiB), "
415 ".numblocks = %d }\n",
416 i, (long long)flash->mtd.eraseregions[i].offset,
417 flash->mtd.eraseregions[i].erasesize,
418 flash->mtd.eraseregions[i].erasesize / 1024,
419 flash->mtd.eraseregions[i].numblocks);
420
421 if (mtd_has_partitions()) {
422 struct mtd_partition *parts = NULL;
423 int nr_parts = 0;
424
425 if (mtd_has_cmdlinepart()) {
426 static const char *part_probes[] =
427 {"cmdlinepart", NULL};
428
429 nr_parts = parse_mtd_partitions(&flash->mtd,
430 part_probes,
431 &parts, 0);
432 }
433
434 if (nr_parts <= 0 && data && data->parts) {
435 parts = data->parts;
436 nr_parts = data->nr_parts;
437 }
438
439 if (nr_parts > 0) {
440 for (i = 0; i < nr_parts; i++) {
441 DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
442 "{.name = %s, .offset = 0x%llx, "
443 ".size = 0x%llx (%lldKiB) }\n",
444 i, parts[i].name,
445 (long long)parts[i].offset,
446 (long long)parts[i].size,
447 (long long)(parts[i].size >> 10));
448 }
449
450 flash->partitioned = 1;
451 return add_mtd_partitions(&flash->mtd,
452 parts, nr_parts);
453 }
454
455 } else if (data->nr_parts) {
456 dev_warn(&spi->dev, "ignoring %d default partitions on %s\n",
457 data->nr_parts, data->name);
458 }
459
460 ret = add_mtd_device(&flash->mtd);
461 if (ret == 1) {
462 kfree(flash);
463 dev_set_drvdata(&spi->dev, NULL);
464 return -ENODEV;
465 }
466
467 return 0;
468}
469
470static int __exit sst25l_remove(struct spi_device *spi)
471{
472 struct sst25l_flash *flash = dev_get_drvdata(&spi->dev);
473 int ret;
474
475 if (mtd_has_partitions() && flash->partitioned)
476 ret = del_mtd_partitions(&flash->mtd);
477 else
478 ret = del_mtd_device(&flash->mtd);
479 if (ret == 0)
480 kfree(flash);
481 return ret;
482}
483
484static struct spi_driver sst25l_driver = {
485 .driver = {
486 .name = "sst25l",
487 .bus = &spi_bus_type,
488 .owner = THIS_MODULE,
489 },
490 .probe = sst25l_probe,
491 .remove = __exit_p(sst25l_remove),
492};
493
494static int __init sst25l_init(void)
495{
496 return spi_register_driver(&sst25l_driver);
497}
498
499static void __exit sst25l_exit(void)
500{
501 spi_unregister_driver(&sst25l_driver);
502}
503
504module_init(sst25l_init);
505module_exit(sst25l_exit);
506
507MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips");
508MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, "
509 "Ryan Mallon <ryan@bluewatersys.com>");
510MODULE_LICENSE("GPL");