diff options
author | Jon Hunter <jon-hunter@ti.com> | 2013-02-08 17:46:13 -0500 |
---|---|---|
committer | Jon Hunter <jon-hunter@ti.com> | 2013-04-03 21:13:38 -0400 |
commit | cdd6928c589a2dcf084bd62fa5a2b7db1516187b (patch) | |
tree | 3ed13fd349ced1dd80c587c719cb7972236b8b86 | |
parent | d36b4cd46d23dd3c283c2e11de540e4cb875255d (diff) |
ARM: OMAP2+: Add device-tree support for NOR flash
NOR flash is not currently supported when booting with device-tree
on OMAP2+ devices. Add support to detect and configure NOR devices
when booting with device-tree.
Add documentation for the TI GPMC NOR binding.
Signed-off-by: Jon Hunter <jon-hunter@ti.com>
Tested-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
-rw-r--r-- | Documentation/devicetree/bindings/mtd/gpmc-nor.txt | 98 | ||||
-rw-r--r-- | arch/arm/mach-omap2/gpmc.c | 115 |
2 files changed, 213 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/mtd/gpmc-nor.txt b/Documentation/devicetree/bindings/mtd/gpmc-nor.txt new file mode 100644 index 000000000000..420b3ab18890 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/gpmc-nor.txt | |||
@@ -0,0 +1,98 @@ | |||
1 | Device tree bindings for NOR flash connect to TI GPMC | ||
2 | |||
3 | NOR flash connected to the TI GPMC (found on OMAP boards) are represented as | ||
4 | child nodes of the GPMC controller with a name of "nor". | ||
5 | |||
6 | All timing relevant properties as well as generic GPMC child properties are | ||
7 | explained in a separate documents. Please refer to | ||
8 | Documentation/devicetree/bindings/bus/ti-gpmc.txt | ||
9 | |||
10 | Required properties: | ||
11 | - bank-width: Width of NOR flash in bytes. GPMC supports 8-bit and | ||
12 | 16-bit devices and so must be either 1 or 2 bytes. | ||
13 | - compatible: Documentation/devicetree/bindings/mtd/mtd-physmap.txt | ||
14 | - gpmc,cs-on-ns: Chip-select assertion time | ||
15 | - gpmc,cs-rd-off-ns: Chip-select de-assertion time for reads | ||
16 | - gpmc,cs-wr-off-ns: Chip-select de-assertion time for writes | ||
17 | - gpmc,oe-on-ns: Output-enable assertion time | ||
18 | - gpmc,oe-off-ns: Output-enable de-assertion time | ||
19 | - gpmc,we-on-ns Write-enable assertion time | ||
20 | - gpmc,we-off-ns: Write-enable de-assertion time | ||
21 | - gpmc,access-ns: Start cycle to first data capture (read access) | ||
22 | - gpmc,rd-cycle-ns: Total read cycle time | ||
23 | - gpmc,wr-cycle-ns: Total write cycle time | ||
24 | - linux,mtd-name: Documentation/devicetree/bindings/mtd/mtd-physmap.txt | ||
25 | - reg: Chip-select, base address (relative to chip-select) | ||
26 | and size of NOR flash. Note that base address will be | ||
27 | typically 0 as this is the start of the chip-select. | ||
28 | |||
29 | Optional properties: | ||
30 | - gpmc,XXX Additional GPMC timings and settings parameters. See | ||
31 | Documentation/devicetree/bindings/bus/ti-gpmc.txt | ||
32 | |||
33 | Optional properties for partiton table parsing: | ||
34 | - #address-cells: should be set to 1 | ||
35 | - #size-cells: should be set to 1 | ||
36 | |||
37 | Example: | ||
38 | |||
39 | gpmc: gpmc@6e000000 { | ||
40 | compatible = "ti,omap3430-gpmc", "simple-bus"; | ||
41 | ti,hwmods = "gpmc"; | ||
42 | reg = <0x6e000000 0x1000>; | ||
43 | interrupts = <20>; | ||
44 | gpmc,num-cs = <8>; | ||
45 | gpmc,num-waitpins = <4>; | ||
46 | #address-cells = <2>; | ||
47 | #size-cells = <1>; | ||
48 | |||
49 | ranges = <0 0 0x10000000 0x08000000>; | ||
50 | |||
51 | nor@0,0 { | ||
52 | compatible = "cfi-flash"; | ||
53 | linux,mtd-name= "intel,pf48f6000m0y1be"; | ||
54 | #address-cells = <1>; | ||
55 | #size-cells = <1>; | ||
56 | reg = <0 0 0x08000000>; | ||
57 | bank-width = <2>; | ||
58 | |||
59 | gpmc,mux-add-data; | ||
60 | gpmc,cs-on-ns = <0>; | ||
61 | gpmc,cs-rd-off-ns = <186>; | ||
62 | gpmc,cs-wr-off-ns = <186>; | ||
63 | gpmc,adv-on-ns = <12>; | ||
64 | gpmc,adv-rd-off-ns = <48>; | ||
65 | gpmc,adv-wr-off-ns = <48>; | ||
66 | gpmc,oe-on-ns = <54>; | ||
67 | gpmc,oe-off-ns = <168>; | ||
68 | gpmc,we-on-ns = <54>; | ||
69 | gpmc,we-off-ns = <168>; | ||
70 | gpmc,rd-cycle-ns = <186>; | ||
71 | gpmc,wr-cycle-ns = <186>; | ||
72 | gpmc,access-ns = <114>; | ||
73 | gpmc,page-burst-access-ns = <6>; | ||
74 | gpmc,bus-turnaround-ns = <12>; | ||
75 | gpmc,cycle2cycle-delay-ns = <18>; | ||
76 | gpmc,wr-data-mux-bus-ns = <90>; | ||
77 | gpmc,wr-access-ns = <186>; | ||
78 | gpmc,cycle2cycle-samecsen; | ||
79 | gpmc,cycle2cycle-diffcsen; | ||
80 | |||
81 | partition@0 { | ||
82 | label = "bootloader-nor"; | ||
83 | reg = <0 0x40000>; | ||
84 | }; | ||
85 | partition@0x40000 { | ||
86 | label = "params-nor"; | ||
87 | reg = <0x40000 0x40000>; | ||
88 | }; | ||
89 | partition@0x80000 { | ||
90 | label = "kernel-nor"; | ||
91 | reg = <0x80000 0x200000>; | ||
92 | }; | ||
93 | partition@0x280000 { | ||
94 | label = "filesystem-nor"; | ||
95 | reg = <0x240000 0x7d80000>; | ||
96 | }; | ||
97 | }; | ||
98 | }; | ||
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index ab658eb1419b..c7bf6ddf04ed 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
27 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
28 | #include <linux/of.h> | 28 | #include <linux/of.h> |
29 | #include <linux/of_address.h> | ||
29 | #include <linux/of_mtd.h> | 30 | #include <linux/of_mtd.h> |
30 | #include <linux/of_device.h> | 31 | #include <linux/of_device.h> |
31 | #include <linux/mtd/nand.h> | 32 | #include <linux/mtd/nand.h> |
@@ -499,6 +500,37 @@ static int gpmc_cs_delete_mem(int cs) | |||
499 | return r; | 500 | return r; |
500 | } | 501 | } |
501 | 502 | ||
503 | /** | ||
504 | * gpmc_cs_remap - remaps a chip-select physical base address | ||
505 | * @cs: chip-select to remap | ||
506 | * @base: physical base address to re-map chip-select to | ||
507 | * | ||
508 | * Re-maps a chip-select to a new physical base address specified by | ||
509 | * "base". Returns 0 on success and appropriate negative error code | ||
510 | * on failure. | ||
511 | */ | ||
512 | static int gpmc_cs_remap(int cs, u32 base) | ||
513 | { | ||
514 | int ret; | ||
515 | u32 old_base, size; | ||
516 | |||
517 | if (cs > GPMC_CS_NUM) | ||
518 | return -ENODEV; | ||
519 | gpmc_cs_get_memconf(cs, &old_base, &size); | ||
520 | if (base == old_base) | ||
521 | return 0; | ||
522 | gpmc_cs_disable_mem(cs); | ||
523 | ret = gpmc_cs_delete_mem(cs); | ||
524 | if (ret < 0) | ||
525 | return ret; | ||
526 | ret = gpmc_cs_insert_mem(cs, base, size); | ||
527 | if (ret < 0) | ||
528 | return ret; | ||
529 | gpmc_cs_enable_mem(cs, base, size); | ||
530 | |||
531 | return 0; | ||
532 | } | ||
533 | |||
502 | int gpmc_cs_request(int cs, unsigned long size, unsigned long *base) | 534 | int gpmc_cs_request(int cs, unsigned long size, unsigned long *base) |
503 | { | 535 | { |
504 | struct resource *res = &gpmc_cs_mem[cs]; | 536 | struct resource *res = &gpmc_cs_mem[cs]; |
@@ -1386,6 +1418,80 @@ static int gpmc_probe_onenand_child(struct platform_device *pdev, | |||
1386 | } | 1418 | } |
1387 | #endif | 1419 | #endif |
1388 | 1420 | ||
1421 | /** | ||
1422 | * gpmc_probe_nor_child - configures the gpmc for a nor device | ||
1423 | * @pdev: pointer to gpmc platform device | ||
1424 | * @child: pointer to device-tree node for nor device | ||
1425 | * | ||
1426 | * Allocates and configures a GPMC chip-select for a NOR flash device. | ||
1427 | * Returns 0 on success and appropriate negative error code on failure. | ||
1428 | */ | ||
1429 | static int gpmc_probe_nor_child(struct platform_device *pdev, | ||
1430 | struct device_node *child) | ||
1431 | { | ||
1432 | struct gpmc_settings gpmc_s; | ||
1433 | struct gpmc_timings gpmc_t; | ||
1434 | struct resource res; | ||
1435 | unsigned long base; | ||
1436 | int ret, cs; | ||
1437 | |||
1438 | if (of_property_read_u32(child, "reg", &cs) < 0) { | ||
1439 | dev_err(&pdev->dev, "%s has no 'reg' property\n", | ||
1440 | child->full_name); | ||
1441 | return -ENODEV; | ||
1442 | } | ||
1443 | |||
1444 | if (of_address_to_resource(child, 0, &res) < 0) { | ||
1445 | dev_err(&pdev->dev, "%s has malformed 'reg' property\n", | ||
1446 | child->full_name); | ||
1447 | return -ENODEV; | ||
1448 | } | ||
1449 | |||
1450 | ret = gpmc_cs_request(cs, resource_size(&res), &base); | ||
1451 | if (ret < 0) { | ||
1452 | dev_err(&pdev->dev, "cannot request GPMC CS %d\n", cs); | ||
1453 | return ret; | ||
1454 | } | ||
1455 | |||
1456 | /* | ||
1457 | * FIXME: gpmc_cs_request() will map the CS to an arbitary | ||
1458 | * location in the gpmc address space. When booting with | ||
1459 | * device-tree we want the NOR flash to be mapped to the | ||
1460 | * location specified in the device-tree blob. So remap the | ||
1461 | * CS to this location. Once DT migration is complete should | ||
1462 | * just make gpmc_cs_request() map a specific address. | ||
1463 | */ | ||
1464 | ret = gpmc_cs_remap(cs, res.start); | ||
1465 | if (ret < 0) { | ||
1466 | dev_err(&pdev->dev, "cannot remap GPMC CS %d to 0x%x\n", | ||
1467 | cs, res.start); | ||
1468 | goto err; | ||
1469 | } | ||
1470 | |||
1471 | gpmc_read_settings_dt(child, &gpmc_s); | ||
1472 | |||
1473 | ret = of_property_read_u32(child, "bank-width", &gpmc_s.device_width); | ||
1474 | if (ret < 0) | ||
1475 | goto err; | ||
1476 | |||
1477 | ret = gpmc_cs_program_settings(cs, &gpmc_s); | ||
1478 | if (ret < 0) | ||
1479 | goto err; | ||
1480 | |||
1481 | gpmc_read_timings_dt(child, &gpmc_t); | ||
1482 | gpmc_cs_set_timings(cs, &gpmc_t); | ||
1483 | |||
1484 | if (of_platform_device_create(child, NULL, &pdev->dev)) | ||
1485 | return 0; | ||
1486 | |||
1487 | dev_err(&pdev->dev, "failed to create gpmc child %s\n", child->name); | ||
1488 | |||
1489 | err: | ||
1490 | gpmc_cs_free(cs); | ||
1491 | |||
1492 | return ret; | ||
1493 | } | ||
1494 | |||
1389 | static int gpmc_probe_dt(struct platform_device *pdev) | 1495 | static int gpmc_probe_dt(struct platform_device *pdev) |
1390 | { | 1496 | { |
1391 | int ret; | 1497 | int ret; |
@@ -1418,6 +1524,15 @@ static int gpmc_probe_dt(struct platform_device *pdev) | |||
1418 | return ret; | 1524 | return ret; |
1419 | } | 1525 | } |
1420 | } | 1526 | } |
1527 | |||
1528 | for_each_node_by_name(child, "nor") { | ||
1529 | ret = gpmc_probe_nor_child(pdev, child); | ||
1530 | if (ret < 0) { | ||
1531 | of_node_put(child); | ||
1532 | return ret; | ||
1533 | } | ||
1534 | } | ||
1535 | |||
1421 | return 0; | 1536 | return 0; |
1422 | } | 1537 | } |
1423 | #else | 1538 | #else |