Date: | 2011-08-02 10:49:28 (12 years 7 months ago) |
---|---|
Author: | Maarten ter Huurne |
Commit: | 010bfe2843c058bf0454a3d4e11d6436fbeff943 |
Message: | MTD: NAND: JZ4740: Multi-bank support with autodetection The platform data can now specify which external memory banks to probe for NAND chips, and in which order. Banks that contain a NAND are used and the other banks are freed. Squashed version of development done in jz-2.6.38 branch. Original patch by Lars-Peter Clausen with some bug fixes from me. Thanks to Paul Cercueil for the initial autodetection patch. |
Files: |
arch/mips/include/asm/mach-jz4740/jz4740_nand.h (2 diffs) arch/mips/jz4740/platform.c (1 diff) drivers/mtd/nand/jz4740_nand.c (12 diffs) |
Change Details
arch/mips/include/asm/mach-jz4740/jz4740_nand.h | ||
---|---|---|
19 | 19 | #include <linux/mtd/nand.h> |
20 | 20 | #include <linux/mtd/partitions.h> |
21 | 21 | |
22 | #define JZ_NAND_NUM_BANKS 4 | |
23 | ||
22 | 24 | struct jz_nand_platform_data { |
23 | 25 | int num_partitions; |
24 | 26 | struct mtd_partition *partitions; |
... | ... | |
27 | 29 | |
28 | 30 | unsigned int busy_gpio; |
29 | 31 | |
32 | unsigned char banks[JZ_NAND_NUM_BANKS]; | |
33 | ||
30 | 34 | void (*ident_callback)(struct platform_device *, struct nand_chip *, |
31 | 35 | struct mtd_partition **, int *num_partitions); |
32 | 36 | }; |
arch/mips/jz4740/platform.c | ||
---|---|---|
157 | 157 | .flags = IORESOURCE_MEM, |
158 | 158 | }, |
159 | 159 | { |
160 | .name = "bank", | |
160 | .name = "bank1", | |
161 | 161 | .start = 0x18000000, |
162 | 162 | .end = 0x180C0000 - 1, |
163 | 163 | .flags = IORESOURCE_MEM, |
164 | 164 | }, |
165 | { | |
166 | .name = "bank2", | |
167 | .start = 0x14000000, | |
168 | .end = 0x140C0000 - 1, | |
169 | .flags = IORESOURCE_MEM, | |
170 | }, | |
171 | { | |
172 | .name = "bank3", | |
173 | .start = 0x0C000000, | |
174 | .end = 0x0C0C0000 - 1, | |
175 | .flags = IORESOURCE_MEM, | |
176 | }, | |
177 | { | |
178 | .name = "bank4", | |
179 | .start = 0x08000000, | |
180 | .end = 0x080C0000 - 1, | |
181 | .flags = IORESOURCE_MEM, | |
182 | }, | |
165 | 183 | }; |
166 | 184 | |
167 | 185 | struct platform_device jz4740_nand_device = { |
drivers/mtd/nand/jz4740_nand.c | ||
---|---|---|
52 | 52 | |
53 | 53 | #define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) |
54 | 54 | #define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) |
55 | #define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa | |
55 | 56 | |
56 | #define JZ_NAND_MEM_ADDR_OFFSET 0x10000 | |
57 | 57 | #define JZ_NAND_MEM_CMD_OFFSET 0x08000 |
58 | #define JZ_NAND_MEM_ADDR_OFFSET 0x10000 | |
58 | 59 | |
59 | 60 | struct jz_nand { |
60 | 61 | struct mtd_info mtd; |
... | ... | |
62 | 63 | void __iomem *base; |
63 | 64 | struct resource *mem; |
64 | 65 | |
65 | void __iomem *bank_base; | |
66 | struct resource *bank_mem; | |
66 | unsigned char banks[JZ_NAND_NUM_BANKS]; | |
67 | void __iomem *bank_base[JZ_NAND_NUM_BANKS]; | |
68 | struct resource *bank_mem[JZ_NAND_NUM_BANKS]; | |
69 | ||
70 | int selected_bank; | |
67 | 71 | |
68 | 72 | struct jz_nand_platform_data *pdata; |
69 | 73 | bool is_reading; |
... | ... | |
74 | 78 | return container_of(mtd, struct jz_nand, mtd); |
75 | 79 | } |
76 | 80 | |
81 | static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr) | |
82 | { | |
83 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | |
84 | struct nand_chip *chip = mtd->priv; | |
85 | uint32_t ctrl; | |
86 | int banknr; | |
87 | ||
88 | ctrl = readl(nand->base + JZ_REG_NAND_CTRL); | |
89 | ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK; | |
90 | ||
91 | if (chipnr == -1) { | |
92 | banknr = -1; | |
93 | } else { | |
94 | banknr = nand->banks[chipnr] - 1; | |
95 | chip->IO_ADDR_R = nand->bank_base[banknr]; | |
96 | chip->IO_ADDR_W = nand->bank_base[banknr]; | |
97 | } | |
98 | writel(ctrl, nand->base + JZ_REG_NAND_CTRL); | |
99 | ||
100 | nand->selected_bank = banknr; | |
101 | } | |
102 | ||
77 | 103 | static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) |
78 | 104 | { |
79 | 105 | struct jz_nand *nand = mtd_to_jz_nand(mtd); |
80 | 106 | struct nand_chip *chip = mtd->priv; |
81 | 107 | uint32_t reg; |
108 | void __iomem *bank_base = nand->bank_base[nand->selected_bank]; | |
109 | ||
110 | BUG_ON(nand->selected_bank < 0); | |
82 | 111 | |
83 | 112 | if (ctrl & NAND_CTRL_CHANGE) { |
84 | 113 | BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); |
85 | 114 | if (ctrl & NAND_ALE) |
86 | chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET; | |
115 | bank_base += JZ_NAND_MEM_ADDR_OFFSET; | |
87 | 116 | else if (ctrl & NAND_CLE) |
88 | chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET; | |
89 | else | |
90 | chip->IO_ADDR_W = nand->bank_base; | |
117 | bank_base += JZ_NAND_MEM_CMD_OFFSET; | |
118 | chip->IO_ADDR_W = bank_base; | |
91 | 119 | |
92 | 120 | reg = readl(nand->base + JZ_REG_NAND_CTRL); |
93 | 121 | if (ctrl & NAND_NCE) |
94 | reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); | |
122 | reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); | |
95 | 123 | else |
96 | reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); | |
124 | reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); | |
97 | 125 | writel(reg, nand->base + JZ_REG_NAND_CTRL); |
98 | 126 | } |
99 | 127 | if (dat != NAND_CMD_NONE) |
... | ... | |
256 | 284 | #endif |
257 | 285 | |
258 | 286 | static int jz_nand_ioremap_resource(struct platform_device *pdev, |
259 | const char *name, struct resource **res, void __iomem **base) | |
287 | const char *name, struct resource **res, void *__iomem *base) | |
260 | 288 | { |
261 | 289 | int ret; |
262 | 290 | |
... | ... | |
292 | 320 | return ret; |
293 | 321 | } |
294 | 322 | |
323 | static inline void jz_nand_iounmap_resource(struct resource *res, void __iomem *base) | |
324 | { | |
325 | iounmap(base); | |
326 | release_mem_region(res->start, resource_size(res)); | |
327 | } | |
328 | ||
329 | static int __devinit jz_nand_detect_bank(struct platform_device *pdev, struct jz_nand *nand, unsigned char bank, size_t chipnr, uint8_t *nand_maf_id, uint8_t *nand_dev_id) { | |
330 | int ret; | |
331 | int gpio; | |
332 | char gpio_name[9]; | |
333 | char res_name[6]; | |
334 | uint32_t ctrl; | |
335 | struct mtd_info *mtd = &nand->mtd; | |
336 | struct nand_chip *chip = &nand->chip; | |
337 | ||
338 | /* Request GPIO port. */ | |
339 | gpio = JZ_GPIO_MEM_CS0 + bank - 1; | |
340 | sprintf(gpio_name, "NAND CS%d", bank); | |
341 | ret = gpio_request(gpio, gpio_name); | |
342 | if (ret) { | |
343 | dev_warn(&pdev->dev, | |
344 | "Failed to request %s gpio %d: %d\n", | |
345 | gpio_name, gpio, ret); | |
346 | goto notfound_gpio; | |
347 | } | |
348 | ||
349 | /* Request I/O resource. */ | |
350 | sprintf(res_name, "bank%d", bank); | |
351 | ret = jz_nand_ioremap_resource(pdev, res_name, | |
352 | &nand->bank_mem[bank - 1], | |
353 | &nand->bank_base[bank - 1]); | |
354 | if (ret) | |
355 | goto notfound_resource; | |
356 | ||
357 | /* Enable chip in bank. */ | |
358 | jz_gpio_set_function(gpio, JZ_GPIO_FUNC_MEM_CS0); | |
359 | ctrl = readl(nand->base + JZ_REG_NAND_CTRL); | |
360 | ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1); | |
361 | writel(ctrl, nand->base + JZ_REG_NAND_CTRL); | |
362 | ||
363 | if (chipnr == 0) { | |
364 | /* Detect first chip. */ | |
365 | ret = nand_scan_ident(mtd, 1, NULL); | |
366 | if (ret) | |
367 | goto notfound_id; | |
368 | ||
369 | /* Retrieve the IDs from the first chip. */ | |
370 | chip->select_chip(mtd, 0); | |
371 | chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); | |
372 | chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); | |
373 | *nand_maf_id = chip->read_byte(mtd); | |
374 | *nand_dev_id = chip->read_byte(mtd); | |
375 | } else { | |
376 | /* Detect additional chip. */ | |
377 | chip->select_chip(mtd, chipnr); | |
378 | chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); | |
379 | chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); | |
380 | if (*nand_maf_id != chip->read_byte(mtd) | |
381 | || *nand_dev_id != chip->read_byte(mtd)) { | |
382 | ret = -ENODEV; | |
383 | goto notfound_id; | |
384 | } | |
385 | ||
386 | /* Update size of the MTD. */ | |
387 | chip->numchips++; | |
388 | mtd->size += chip->chipsize; | |
389 | } | |
390 | ||
391 | dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank); | |
392 | return 0; | |
393 | ||
394 | notfound_id: | |
395 | dev_info(&pdev->dev, "No chip found on bank %i\n", bank); | |
396 | ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1)); | |
397 | writel(ctrl, nand->base + JZ_REG_NAND_CTRL); | |
398 | jz_gpio_set_function(gpio, JZ_GPIO_FUNC_NONE); | |
399 | jz_nand_iounmap_resource(nand->bank_mem[bank - 1], | |
400 | nand->bank_base[bank - 1]); | |
401 | notfound_resource: | |
402 | gpio_free(gpio); | |
403 | notfound_gpio: | |
404 | return ret; | |
405 | } | |
406 | ||
295 | 407 | static int __devinit jz_nand_probe(struct platform_device *pdev) |
296 | 408 | { |
297 | 409 | int ret; |
... | ... | |
301 | 413 | struct jz_nand_platform_data *pdata = pdev->dev.platform_data; |
302 | 414 | struct mtd_partition *partition_info; |
303 | 415 | int num_partitions = 0; |
416 | size_t chipnr, bank_idx; | |
417 | uint8_t nand_maf_id = 0, nand_dev_id = 0; | |
304 | 418 | |
305 | 419 | nand = kzalloc(sizeof(*nand), GFP_KERNEL); |
306 | 420 | if (!nand) { |
... | ... | |
311 | 425 | ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); |
312 | 426 | if (ret) |
313 | 427 | goto err_free; |
314 | ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem, | |
315 | &nand->bank_base); | |
316 | if (ret) | |
317 | goto err_iounmap_mmio; | |
318 | 428 | |
319 | 429 | if (pdata && gpio_is_valid(pdata->busy_gpio)) { |
320 | 430 | ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); |
... | ... | |
322 | 432 | dev_err(&pdev->dev, |
323 | 433 | "Failed to request busy gpio %d: %d\n", |
324 | 434 | pdata->busy_gpio, ret); |
325 | goto err_iounmap_mem; | |
435 | goto err_iounmap_mmio; | |
326 | 436 | } |
327 | 437 | } |
328 | 438 | |
... | ... | |
344 | 454 | |
345 | 455 | chip->chip_delay = 50; |
346 | 456 | chip->cmd_ctrl = jz_nand_cmd_ctrl; |
457 | chip->select_chip = jz_nand_select_chip; | |
347 | 458 | |
348 | 459 | if (pdata && gpio_is_valid(pdata->busy_gpio)) |
349 | 460 | chip->dev_ready = jz_nand_dev_ready; |
350 | 461 | |
351 | chip->IO_ADDR_R = nand->bank_base; | |
352 | chip->IO_ADDR_W = nand->bank_base; | |
353 | ||
354 | 462 | nand->pdata = pdata; |
355 | 463 | platform_set_drvdata(pdev, nand); |
356 | 464 | |
357 | writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL); | |
358 | ||
359 | ret = nand_scan_ident(mtd, 1, NULL); | |
360 | if (ret) { | |
361 | dev_err(&pdev->dev, "Failed to scan nand\n"); | |
362 | goto err_gpio_free; | |
465 | /* We are going to autodetect NAND chips in the banks specified in the | |
466 | * platform data. Although nand_scan_ident() can detect multiple chips, | |
467 | * it requires those chips to be numbered consecuitively, which is not | |
468 | * always the case for external memory banks. And a fixed chip-to-bank | |
469 | * mapping is not practical either, since for example Dingoo units | |
470 | * produced at different times have NAND chips in different banks. | |
471 | */ | |
472 | chipnr = 0; | |
473 | for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) { | |
474 | unsigned char bank; | |
475 | ||
476 | /* If there is no platform data, look for NAND in bank 1, | |
477 | * which is the most likely bank since it is the only one | |
478 | * that can be booted from. | |
479 | */ | |
480 | bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1; | |
481 | if (bank == 0) | |
482 | break; | |
483 | if (bank > JZ_NAND_NUM_BANKS) { | |
484 | dev_warn(&pdev->dev, | |
485 | "Skipping non-existing bank: %d\n", bank); | |
486 | continue; | |
487 | } | |
488 | /* The detection routine will directly or indirectly call | |
489 | * jz_nand_select_chip(), so nand->banks has to contain the | |
490 | * bank we're checking. | |
491 | */ | |
492 | nand->banks[chipnr] = bank; | |
493 | if (jz_nand_detect_bank(pdev, nand, bank, chipnr, | |
494 | &nand_maf_id, &nand_dev_id) == 0) | |
495 | chipnr++; | |
496 | else | |
497 | nand->banks[chipnr] = 0; | |
498 | } | |
499 | if (chipnr == 0) { | |
500 | dev_err(&pdev->dev, "No NAND chips found\n"); | |
501 | goto err_gpio_busy; | |
363 | 502 | } |
364 | 503 | |
365 | 504 | if (pdata && pdata->ident_callback) { |
... | ... | |
369 | 508 | |
370 | 509 | ret = nand_scan_tail(mtd); |
371 | 510 | if (ret) { |
372 | dev_err(&pdev->dev, "Failed to scan nand\n"); | |
373 | goto err_gpio_free; | |
511 | dev_err(&pdev->dev, "Failed to scan NAND\n"); | |
512 | goto err_unclaim_banks; | |
374 | 513 | } |
375 | 514 | |
376 | 515 | #ifdef CONFIG_MTD_CMDLINE_PARTS |
... | ... | |
393 | 532 | return 0; |
394 | 533 | |
395 | 534 | err_nand_release: |
396 | nand_release(&nand->mtd); | |
397 | err_gpio_free: | |
535 | nand_release(mtd); | |
536 | err_unclaim_banks: | |
537 | while (chipnr--) { | |
538 | unsigned char bank = nand->banks[chipnr]; | |
539 | gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); | |
540 | jz_nand_iounmap_resource(nand->bank_mem[bank - 1], | |
541 | nand->bank_base[bank - 1]); | |
542 | } | |
543 | writel(0, nand->base + JZ_REG_NAND_CTRL); | |
544 | err_gpio_busy: | |
545 | if (pdata && gpio_is_valid(pdata->busy_gpio)) | |
546 | gpio_free(pdata->busy_gpio); | |
398 | 547 | platform_set_drvdata(pdev, NULL); |
399 | gpio_free(pdata->busy_gpio); | |
400 | err_iounmap_mem: | |
401 | iounmap(nand->bank_base); | |
402 | 548 | err_iounmap_mmio: |
403 | iounmap(nand->base); | |
549 | jz_nand_iounmap_resource(nand->mem, nand->base); | |
404 | 550 | err_free: |
405 | 551 | kfree(nand); |
406 | 552 | return ret; |
... | ... | |
409 | 555 | static int __devexit jz_nand_remove(struct platform_device *pdev) |
410 | 556 | { |
411 | 557 | struct jz_nand *nand = platform_get_drvdata(pdev); |
558 | struct jz_nand_platform_data *pdata = pdev->dev.platform_data; | |
559 | size_t i; | |
412 | 560 | |
413 | 561 | nand_release(&nand->mtd); |
414 | 562 | |
415 | 563 | /* Deassert and disable all chips */ |
416 | 564 | writel(0, nand->base + JZ_REG_NAND_CTRL); |
417 | 565 | |
418 | iounmap(nand->bank_base); | |
419 | release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem)); | |
420 | iounmap(nand->base); | |
421 | release_mem_region(nand->mem->start, resource_size(nand->mem)); | |
566 | for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) { | |
567 | unsigned char bank = nand->banks[i]; | |
568 | if (bank != 0) { | |
569 | jz_nand_iounmap_resource(nand->bank_mem[bank - 1], | |
570 | nand->bank_base[bank - 1]); | |
571 | gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); | |
572 | } | |
573 | } | |
574 | if (pdata && gpio_is_valid(pdata->busy_gpio)) | |
575 | gpio_free(pdata->busy_gpio); | |
576 | ||
577 | jz_nand_iounmap_resource(nand->mem, nand->base); | |
422 | 578 | |
423 | 579 | platform_set_drvdata(pdev, NULL); |
424 | 580 | kfree(nand); |
Branches:
ben-wpan
ben-wpan-stefan
5396a9238205f20f811ea57898980d3ca82df0b6
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9