diff options
author | Hans-Jürgen Koch <hjk@linutronix.de> | 2007-04-17 13:42:56 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-04-17 13:42:56 -0400 |
commit | 340ea370c2ce89d1c15fbf785460f2f74314ce58 (patch) | |
tree | 2533e894517139917d573a01ae7f1fcd38233dd0 /drivers/mtd/devices | |
parent | 408b483d9cc2d839ecbc9134958c42814865081c (diff) |
[MTD] Driver for AT26Fxxx dataflash devices
Add support for AT26Fxxx dataflash devices. These devices have a quite different
commandset than the AT45xxx chips, which are handled by at91_dataflash.c, so a
combined driver turned out to be more ugly than useful.
Tested only on AT26F004.
Signed-off-by: Hans-Jürgen Koch <hjk@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'drivers/mtd/devices')
-rw-r--r-- | drivers/mtd/devices/Kconfig | 8 | ||||
-rw-r--r-- | drivers/mtd/devices/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/devices/at91_dataflash26.c | 485 |
3 files changed, 494 insertions, 0 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 440f6851da69..bef0f0d2c28e 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig | |||
@@ -60,6 +60,14 @@ config MTD_DATAFLASH | |||
60 | Sometimes DataFlash chips are packaged inside MMC-format | 60 | Sometimes DataFlash chips are packaged inside MMC-format |
61 | cards; at this writing, the MMC stack won't handle those. | 61 | cards; at this writing, the MMC stack won't handle those. |
62 | 62 | ||
63 | config MTD_DATAFLASH26 | ||
64 | tristate "AT91RM9200 DataFlash AT26xxx" | ||
65 | depends on MTD && ARCH_AT91RM9200 && AT91_SPI | ||
66 | help | ||
67 | This enables access to the DataFlash chip (AT26xxx) on an | ||
68 | AT91RM9200-based board. | ||
69 | If you have such a board and such a DataFlash, say 'Y'. | ||
70 | |||
63 | config MTD_M25P80 | 71 | config MTD_M25P80 |
64 | tristate "Support for M25 SPI Flash" | 72 | tristate "Support for M25 SPI Flash" |
65 | depends on MTD && SPI_MASTER && EXPERIMENTAL | 73 | depends on MTD && SPI_MASTER && EXPERIMENTAL |
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 0f788d5c4bf8..8ab568b3f533 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile | |||
@@ -16,4 +16,5 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o | |||
16 | obj-$(CONFIG_MTD_LART) += lart.o | 16 | obj-$(CONFIG_MTD_LART) += lart.o |
17 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o | 17 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o |
18 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o | 18 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o |
19 | obj-$(CONFIG_MTD_DATAFLASH26) += at91_dataflash26.o | ||
19 | obj-$(CONFIG_MTD_M25P80) += m25p80.o | 20 | obj-$(CONFIG_MTD_M25P80) += m25p80.o |
diff --git a/drivers/mtd/devices/at91_dataflash26.c b/drivers/mtd/devices/at91_dataflash26.c new file mode 100644 index 000000000000..64ce37f986fc --- /dev/null +++ b/drivers/mtd/devices/at91_dataflash26.c | |||
@@ -0,0 +1,485 @@ | |||
1 | /* | ||
2 | * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder) | ||
3 | * This is a largely modified version of at91_dataflash.c that | ||
4 | * supports AT26xxx dataflash chips. The original driver supports | ||
5 | * AT45xxx chips. | ||
6 | * | ||
7 | * Note: This driver was only tested with an AT26F004. It should be | ||
8 | * easy to make it work with other AT26xxx dataflash devices, though. | ||
9 | * | ||
10 | * Copyright (C) 2007 Hans J. Koch <hjk@linutronix.de> | ||
11 | * original Copyright (C) SAN People (Pty) Ltd | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * version 2 as published by the Free Software Foundation. | ||
16 | */ | ||
17 | |||
18 | #include <linux/config.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/mtd/mtd.h> | ||
22 | |||
23 | #include <asm/arch/at91_spi.h> | ||
24 | |||
25 | #define DATAFLASH_MAX_DEVICES 4 /* max number of dataflash devices */ | ||
26 | |||
27 | #define MANUFACTURER_ID_ATMEL 0x1F | ||
28 | |||
29 | /* command codes */ | ||
30 | |||
31 | #define AT26_OP_READ_STATUS 0x05 | ||
32 | #define AT26_OP_READ_DEV_ID 0x9F | ||
33 | #define AT26_OP_ERASE_PAGE_4K 0x20 | ||
34 | #define AT26_OP_READ_ARRAY_FAST 0x0B | ||
35 | #define AT26_OP_SEQUENTIAL_WRITE 0xAF | ||
36 | #define AT26_OP_WRITE_ENABLE 0x06 | ||
37 | #define AT26_OP_WRITE_DISABLE 0x04 | ||
38 | #define AT26_OP_SECTOR_PROTECT 0x36 | ||
39 | #define AT26_OP_SECTOR_UNPROTECT 0x39 | ||
40 | |||
41 | /* status register bits */ | ||
42 | |||
43 | #define AT26_STATUS_BUSY 0x01 | ||
44 | #define AT26_STATUS_WRITE_ENABLE 0x02 | ||
45 | |||
46 | struct dataflash_local | ||
47 | { | ||
48 | int spi; /* SPI chip-select number */ | ||
49 | unsigned int page_size; /* number of bytes per page */ | ||
50 | }; | ||
51 | |||
52 | |||
53 | /* Detected DataFlash devices */ | ||
54 | static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES]; | ||
55 | static int nr_devices = 0; | ||
56 | |||
57 | /* Allocate a single SPI transfer descriptor. We're assuming that if multiple | ||
58 | SPI transfers occur at the same time, spi_access_bus() will serialize them. | ||
59 | If this is not valid, then either (i) each dataflash 'priv' structure | ||
60 | needs it's own transfer descriptor, (ii) we lock this one, or (iii) use | ||
61 | another mechanism. */ | ||
62 | static struct spi_transfer_list* spi_transfer_desc; | ||
63 | |||
64 | /* | ||
65 | * Perform a SPI transfer to access the DataFlash device. | ||
66 | */ | ||
67 | static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len, | ||
68 | char* txnext, int txnext_len, char* rxnext, int rxnext_len) | ||
69 | { | ||
70 | struct spi_transfer_list* list = spi_transfer_desc; | ||
71 | |||
72 | list->tx[0] = tx; list->txlen[0] = tx_len; | ||
73 | list->rx[0] = rx; list->rxlen[0] = rx_len; | ||
74 | |||
75 | list->tx[1] = txnext; list->txlen[1] = txnext_len; | ||
76 | list->rx[1] = rxnext; list->rxlen[1] = rxnext_len; | ||
77 | |||
78 | list->nr_transfers = nr; | ||
79 | /* Note: spi_transfer() always returns 0, there are no error checks */ | ||
80 | return spi_transfer(list); | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Return the status of the DataFlash device. | ||
85 | */ | ||
86 | static unsigned char at91_dataflash26_status(void) | ||
87 | { | ||
88 | unsigned char command[2]; | ||
89 | |||
90 | command[0] = AT26_OP_READ_STATUS; | ||
91 | command[1] = 0; | ||
92 | |||
93 | do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0); | ||
94 | |||
95 | return command[1]; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * Poll the DataFlash device until it is READY. | ||
100 | */ | ||
101 | static unsigned char at91_dataflash26_waitready(void) | ||
102 | { | ||
103 | unsigned char status; | ||
104 | |||
105 | while (1) { | ||
106 | status = at91_dataflash26_status(); | ||
107 | if (!(status & AT26_STATUS_BUSY)) | ||
108 | return status; | ||
109 | } | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * Enable/disable write access | ||
114 | */ | ||
115 | static void at91_dataflash26_write_enable(int enable) | ||
116 | { | ||
117 | unsigned char cmd[2]; | ||
118 | |||
119 | DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable); | ||
120 | |||
121 | if (enable) | ||
122 | cmd[0] = AT26_OP_WRITE_ENABLE; | ||
123 | else | ||
124 | cmd[0] = AT26_OP_WRITE_DISABLE; | ||
125 | cmd[1] = 0; | ||
126 | |||
127 | do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * Protect/unprotect sector | ||
132 | */ | ||
133 | static void at91_dataflash26_sector_protect(loff_t addr, int protect) | ||
134 | { | ||
135 | unsigned char cmd[4]; | ||
136 | |||
137 | DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n", | ||
138 | addr, protect); | ||
139 | |||
140 | if (protect) | ||
141 | cmd[0] = AT26_OP_SECTOR_PROTECT; | ||
142 | else | ||
143 | cmd[0] = AT26_OP_SECTOR_UNPROTECT; | ||
144 | cmd[1] = (addr & 0x00FF0000) >> 16; | ||
145 | cmd[2] = (addr & 0x0000FF00) >> 8; | ||
146 | cmd[3] = (addr & 0x000000FF); | ||
147 | |||
148 | do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Erase blocks of flash. | ||
153 | */ | ||
154 | static int at91_dataflash26_erase(struct mtd_info *mtd, | ||
155 | struct erase_info *instr) | ||
156 | { | ||
157 | struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; | ||
158 | unsigned char cmd[4]; | ||
159 | |||
160 | DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n", | ||
161 | instr->addr, instr->len); | ||
162 | |||
163 | /* Sanity checks */ | ||
164 | if (priv->page_size != 4096) | ||
165 | return -EINVAL; /* Can't handle other sizes at the moment */ | ||
166 | |||
167 | if ( ((instr->len % mtd->erasesize) != 0) | ||
168 | || ((instr->len % priv->page_size) != 0) | ||
169 | || ((instr->addr % priv->page_size) != 0) | ||
170 | || ((instr->addr + instr->len) > mtd->size)) | ||
171 | return -EINVAL; | ||
172 | |||
173 | spi_access_bus(priv->spi); | ||
174 | |||
175 | while (instr->len > 0) { | ||
176 | at91_dataflash26_write_enable(1); | ||
177 | at91_dataflash26_sector_protect(instr->addr, 0); | ||
178 | at91_dataflash26_write_enable(1); | ||
179 | cmd[0] = AT26_OP_ERASE_PAGE_4K; | ||
180 | cmd[1] = (instr->addr & 0x00FF0000) >> 16; | ||
181 | cmd[2] = (instr->addr & 0x0000FF00) >> 8; | ||
182 | cmd[3] = (instr->addr & 0x000000FF); | ||
183 | |||
184 | DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x" | ||
185 | "0x%02x\n", | ||
186 | cmd[0], cmd[1], cmd[2], cmd[3]); | ||
187 | |||
188 | do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); | ||
189 | at91_dataflash26_waitready(); | ||
190 | |||
191 | instr->addr += priv->page_size; /* next page */ | ||
192 | instr->len -= priv->page_size; | ||
193 | } | ||
194 | |||
195 | at91_dataflash26_write_enable(0); | ||
196 | spi_release_bus(priv->spi); | ||
197 | |||
198 | /* Inform MTD subsystem that erase is complete */ | ||
199 | instr->state = MTD_ERASE_DONE; | ||
200 | if (instr->callback) | ||
201 | instr->callback(instr); | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * Read from the DataFlash device. | ||
208 | * from : Start offset in flash device | ||
209 | * len : Number of bytes to read | ||
210 | * retlen : Number of bytes actually read | ||
211 | * buf : Buffer that will receive data | ||
212 | */ | ||
213 | static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
214 | size_t *retlen, u_char *buf) | ||
215 | { | ||
216 | struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; | ||
217 | unsigned char cmd[5]; | ||
218 | |||
219 | DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n", | ||
220 | from, from+len); | ||
221 | |||
222 | *retlen = 0; | ||
223 | |||
224 | /* Sanity checks */ | ||
225 | if (!len) | ||
226 | return 0; | ||
227 | if (from + len > mtd->size) | ||
228 | return -EINVAL; | ||
229 | |||
230 | cmd[0] = AT26_OP_READ_ARRAY_FAST; | ||
231 | cmd[1] = (from & 0x00FF0000) >> 16; | ||
232 | cmd[2] = (from & 0x0000FF00) >> 8; | ||
233 | cmd[3] = (from & 0x000000FF); | ||
234 | /* cmd[4] is a "Don't care" byte */ | ||
235 | |||
236 | DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n", | ||
237 | cmd[0], cmd[1], cmd[2], cmd[3]); | ||
238 | |||
239 | spi_access_bus(priv->spi); | ||
240 | do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len); | ||
241 | spi_release_bus(priv->spi); | ||
242 | |||
243 | *retlen = len; | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * Write to the DataFlash device. | ||
249 | * to : Start offset in flash device | ||
250 | * len : Number of bytes to write | ||
251 | * retlen : Number of bytes actually written | ||
252 | * buf : Buffer containing the data | ||
253 | */ | ||
254 | static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
255 | size_t *retlen, const u_char *buf) | ||
256 | { | ||
257 | struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; | ||
258 | unsigned int addr, buf_index = 0; | ||
259 | int ret = -EIO, sector, last_sector; | ||
260 | unsigned char status, cmd[5]; | ||
261 | |||
262 | DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len); | ||
263 | |||
264 | *retlen = 0; | ||
265 | |||
266 | /* Sanity checks */ | ||
267 | if (!len) | ||
268 | return 0; | ||
269 | if (to + len > mtd->size) | ||
270 | return -EINVAL; | ||
271 | |||
272 | spi_access_bus(priv->spi); | ||
273 | |||
274 | addr = to; | ||
275 | last_sector = -1; | ||
276 | |||
277 | while (buf_index < len) { | ||
278 | sector = addr / priv->page_size; | ||
279 | /* Write first byte if a new sector begins */ | ||
280 | if (sector != last_sector) { | ||
281 | at91_dataflash26_write_enable(1); | ||
282 | at91_dataflash26_sector_protect(addr, 0); | ||
283 | at91_dataflash26_write_enable(1); | ||
284 | |||
285 | /* Program first byte of a new sector */ | ||
286 | cmd[0] = AT26_OP_SEQUENTIAL_WRITE; | ||
287 | cmd[1] = (addr & 0x00FF0000) >> 16; | ||
288 | cmd[2] = (addr & 0x0000FF00) >> 8; | ||
289 | cmd[3] = (addr & 0x000000FF); | ||
290 | cmd[4] = buf[buf_index++]; | ||
291 | do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); | ||
292 | status = at91_dataflash26_waitready(); | ||
293 | addr++; | ||
294 | /* On write errors, the chip resets the write enable | ||
295 | flag. This also happens after the last byte of a | ||
296 | sector is successfully programmed. */ | ||
297 | if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) | ||
298 | && ((addr % priv->page_size) != 0) ) { | ||
299 | DEBUG(MTD_DEBUG_LEVEL1, | ||
300 | "write error1: addr=0x%06x, " | ||
301 | "status=0x%02x\n", addr, status); | ||
302 | goto write_err; | ||
303 | } | ||
304 | (*retlen)++; | ||
305 | last_sector = sector; | ||
306 | } | ||
307 | |||
308 | /* Write subsequent bytes in the same sector */ | ||
309 | cmd[0] = AT26_OP_SEQUENTIAL_WRITE; | ||
310 | cmd[1] = buf[buf_index++]; | ||
311 | do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); | ||
312 | status = at91_dataflash26_waitready(); | ||
313 | addr++; | ||
314 | |||
315 | if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) | ||
316 | && ((addr % priv->page_size) != 0) ) { | ||
317 | DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, " | ||
318 | "status=0x%02x\n", addr, status); | ||
319 | goto write_err; | ||
320 | } | ||
321 | |||
322 | (*retlen)++; | ||
323 | } | ||
324 | |||
325 | ret = 0; | ||
326 | at91_dataflash26_write_enable(0); | ||
327 | write_err: | ||
328 | spi_release_bus(priv->spi); | ||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | /* | ||
333 | * Initialize and register DataFlash device with MTD subsystem. | ||
334 | */ | ||
335 | static int __init add_dataflash(int channel, char *name, int nr_pages, | ||
336 | int pagesize) | ||
337 | { | ||
338 | struct mtd_info *device; | ||
339 | struct dataflash_local *priv; | ||
340 | |||
341 | if (nr_devices >= DATAFLASH_MAX_DEVICES) { | ||
342 | printk(KERN_ERR "at91_dataflash26: Too many devices " | ||
343 | "detected\n"); | ||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8, | ||
348 | GFP_KERNEL); | ||
349 | if (!device) | ||
350 | return -ENOMEM; | ||
351 | |||
352 | device->name = (char *)&device[1]; | ||
353 | sprintf(device->name, "%s.spi%d", name, channel); | ||
354 | device->size = nr_pages * pagesize; | ||
355 | device->erasesize = pagesize; | ||
356 | device->owner = THIS_MODULE; | ||
357 | device->type = MTD_DATAFLASH; | ||
358 | device->flags = MTD_CAP_NORFLASH; | ||
359 | device->erase = at91_dataflash26_erase; | ||
360 | device->read = at91_dataflash26_read; | ||
361 | device->write = at91_dataflash26_write; | ||
362 | |||
363 | priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local), | ||
364 | GFP_KERNEL); | ||
365 | if (!priv) { | ||
366 | kfree(device); | ||
367 | return -ENOMEM; | ||
368 | } | ||
369 | |||
370 | priv->spi = channel; | ||
371 | priv->page_size = pagesize; | ||
372 | device->priv = priv; | ||
373 | |||
374 | mtd_devices[nr_devices] = device; | ||
375 | nr_devices++; | ||
376 | printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n", | ||
377 | name, channel, device->size); | ||
378 | |||
379 | return add_mtd_device(device); | ||
380 | } | ||
381 | |||
382 | /* | ||
383 | * Detect and initialize DataFlash device connected to specified SPI channel. | ||
384 | * | ||
385 | */ | ||
386 | |||
387 | struct dataflash26_types { | ||
388 | unsigned char id0; | ||
389 | unsigned char id1; | ||
390 | char *name; | ||
391 | int pagesize; | ||
392 | int nr_pages; | ||
393 | }; | ||
394 | |||
395 | struct dataflash26_types df26_types[] = { | ||
396 | { | ||
397 | .id0 = 0x04, | ||
398 | .id1 = 0x00, | ||
399 | .name = "AT26F004", | ||
400 | .pagesize = 4096, | ||
401 | .nr_pages = 128, | ||
402 | }, | ||
403 | { | ||
404 | .id0 = 0x45, | ||
405 | .id1 = 0x01, | ||
406 | .name = "AT26DF081A", /* Not tested ! */ | ||
407 | .pagesize = 4096, | ||
408 | .nr_pages = 256, | ||
409 | }, | ||
410 | }; | ||
411 | |||
412 | static int __init at91_dataflash26_detect(int channel) | ||
413 | { | ||
414 | unsigned char status, cmd[5]; | ||
415 | int i; | ||
416 | |||
417 | spi_access_bus(channel); | ||
418 | status = at91_dataflash26_status(); | ||
419 | |||
420 | if (status == 0 || status == 0xff) { | ||
421 | printk(KERN_ERR "at91_dataflash26_detect: status error %d\n", | ||
422 | status); | ||
423 | spi_release_bus(channel); | ||
424 | return -ENODEV; | ||
425 | } | ||
426 | |||
427 | cmd[0] = AT26_OP_READ_DEV_ID; | ||
428 | do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); | ||
429 | spi_release_bus(channel); | ||
430 | |||
431 | if (cmd[1] != MANUFACTURER_ID_ATMEL) | ||
432 | return -ENODEV; | ||
433 | |||
434 | for (i = 0; i < ARRAY_SIZE(df26_types); i++) { | ||
435 | if ( cmd[2] == df26_types[i].id0 | ||
436 | && cmd[3] == df26_types[i].id1) | ||
437 | return add_dataflash(channel, | ||
438 | df26_types[i].name, | ||
439 | df26_types[i].nr_pages, | ||
440 | df26_types[i].pagesize); | ||
441 | } | ||
442 | |||
443 | printk(KERN_ERR "at91_dataflash26_detect: Unsupported device " | ||
444 | "(0x%02x/0x%02x)\n", cmd[2], cmd[3]); | ||
445 | return -ENODEV; | ||
446 | } | ||
447 | |||
448 | static int __init at91_dataflash26_init(void) | ||
449 | { | ||
450 | spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list), | ||
451 | GFP_KERNEL); | ||
452 | if (!spi_transfer_desc) | ||
453 | return -ENOMEM; | ||
454 | |||
455 | /* DataFlash (SPI chip select 0) */ | ||
456 | at91_dataflash26_detect(0); | ||
457 | |||
458 | #ifdef CONFIG_MTD_AT91_DATAFLASH_CARD | ||
459 | /* DataFlash card (SPI chip select 3) */ | ||
460 | at91_dataflash26_detect(3); | ||
461 | #endif | ||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | static void __exit at91_dataflash26_exit(void) | ||
466 | { | ||
467 | int i; | ||
468 | |||
469 | for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) { | ||
470 | if (mtd_devices[i]) { | ||
471 | del_mtd_device(mtd_devices[i]); | ||
472 | kfree(mtd_devices[i]->priv); | ||
473 | kfree(mtd_devices[i]); | ||
474 | } | ||
475 | } | ||
476 | nr_devices = 0; | ||
477 | kfree(spi_transfer_desc); | ||
478 | } | ||
479 | |||
480 | module_init(at91_dataflash26_init); | ||
481 | module_exit(at91_dataflash26_exit); | ||
482 | |||
483 | MODULE_LICENSE("GPL"); | ||
484 | MODULE_AUTHOR("Hans J. Koch"); | ||
485 | MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200"); | ||