diff options
author | Andrew Victor <andrew@sanpeople.com> | 2006-10-19 12:24:35 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2006-10-21 11:28:26 -0400 |
commit | 42cb1403af8a755b3dfebeb9d2a5f73bc48832a1 (patch) | |
tree | 3a0c15e9f71da1f83ff4914d2c3815be7f89bbaf /drivers/mtd/nand/at91_nand.c | |
parent | c7cf0c68ea4e2020db7204eb0ed4412b2cbb167d (diff) |
[MTD] NAND: AT91 NAND driver
This version only differs from version posted by Savin Zlobec (20 Jun
2006) in that the AT91RM9200-specific chip-select / bus setup code has
been moved from the at91_nand.c driver into the processor-specific file.
From: Savin Zlobec <savin@epico.si>
Signed-off-by: Andrew Victor <andrew@sanpeople.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'drivers/mtd/nand/at91_nand.c')
-rw-r--r-- | drivers/mtd/nand/at91_nand.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/at91_nand.c new file mode 100644 index 000000000000..a58ed3763086 --- /dev/null +++ b/drivers/mtd/nand/at91_nand.c | |||
@@ -0,0 +1,220 @@ | |||
1 | /* | ||
2 | * drivers/mtd/nand/at91_nand.c | ||
3 | * | ||
4 | * Copyright (C) 2003 Rick Bronson | ||
5 | * | ||
6 | * Derived from drivers/mtd/nand/autcpu12.c | ||
7 | * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) | ||
8 | * | ||
9 | * Derived from drivers/mtd/spia.c | ||
10 | * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) | ||
11 | * | ||
12 | * This program 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/slab.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/mtd/mtd.h> | ||
22 | #include <linux/mtd/nand.h> | ||
23 | #include <linux/mtd/partitions.h> | ||
24 | |||
25 | #include <asm/io.h> | ||
26 | #include <asm/sizes.h> | ||
27 | |||
28 | #include <asm/hardware.h> | ||
29 | #include <asm/arch/board.h> | ||
30 | #include <asm/arch/gpio.h> | ||
31 | |||
32 | struct at91_nand_host { | ||
33 | struct nand_chip nand_chip; | ||
34 | struct mtd_info mtd; | ||
35 | void __iomem *io_base; | ||
36 | struct at91_nand_data *board; | ||
37 | }; | ||
38 | |||
39 | /* | ||
40 | * Hardware specific access to control-lines | ||
41 | */ | ||
42 | static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) | ||
43 | { | ||
44 | struct nand_chip *nand_chip = mtd->priv; | ||
45 | struct at91_nand_host *host = nand_chip->priv; | ||
46 | |||
47 | if (cmd == NAND_CMD_NONE) | ||
48 | return; | ||
49 | |||
50 | if (ctrl & NAND_CLE) | ||
51 | writeb(cmd, host->io_base + (1 << host->board->cle)); | ||
52 | else | ||
53 | writeb(cmd, host->io_base + (1 << host->board->ale)); | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Read the Device Ready pin. | ||
58 | */ | ||
59 | static int at91_nand_device_ready(struct mtd_info *mtd) | ||
60 | { | ||
61 | struct nand_chip *nand_chip = mtd->priv; | ||
62 | struct at91_nand_host *host = nand_chip->priv; | ||
63 | |||
64 | return at91_get_gpio_value(host->board->rdy_pin); | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * Enable NAND. | ||
69 | */ | ||
70 | static void at91_nand_enable(struct at91_nand_host *host) | ||
71 | { | ||
72 | if (host->board->enable_pin) | ||
73 | at91_set_gpio_value(host->board->enable_pin, 0); | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * Disable NAND. | ||
78 | */ | ||
79 | static void at91_nand_disable(struct at91_nand_host *host) | ||
80 | { | ||
81 | if (host->board->enable_pin) | ||
82 | at91_set_gpio_value(host->board->enable_pin, 1); | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * Probe for the NAND device. | ||
87 | */ | ||
88 | static int __init at91_nand_probe(struct platform_device *pdev) | ||
89 | { | ||
90 | struct at91_nand_host *host; | ||
91 | struct mtd_info *mtd; | ||
92 | struct nand_chip *nand_chip; | ||
93 | int res; | ||
94 | |||
95 | #ifdef CONFIG_MTD_PARTITIONS | ||
96 | struct mtd_partition *partitions = NULL; | ||
97 | int num_partitions = 0; | ||
98 | #endif | ||
99 | |||
100 | /* Allocate memory for the device structure (and zero it) */ | ||
101 | host = kzalloc(sizeof(struct at91_nand_host), GFP_KERNEL); | ||
102 | if (!host) { | ||
103 | printk(KERN_ERR "at91_nand: failed to allocate device structure.\n"); | ||
104 | return -ENOMEM; | ||
105 | } | ||
106 | |||
107 | host->io_base = ioremap(pdev->resource[0].start, | ||
108 | pdev->resource[0].end - pdev->resource[0].start + 1); | ||
109 | if (host->io_base == NULL) { | ||
110 | printk(KERN_ERR "at91_nand: ioremap failed\n"); | ||
111 | kfree(host); | ||
112 | return -EIO; | ||
113 | } | ||
114 | |||
115 | mtd = &host->mtd; | ||
116 | nand_chip = &host->nand_chip; | ||
117 | host->board = pdev->dev.platform_data; | ||
118 | |||
119 | nand_chip->priv = host; /* link the private data structures */ | ||
120 | mtd->priv = nand_chip; | ||
121 | mtd->owner = THIS_MODULE; | ||
122 | |||
123 | /* Set address of NAND IO lines */ | ||
124 | nand_chip->IO_ADDR_R = host->io_base; | ||
125 | nand_chip->IO_ADDR_W = host->io_base; | ||
126 | nand_chip->cmd_ctrl = at91_nand_cmd_ctrl; | ||
127 | nand_chip->dev_ready = at91_nand_device_ready; | ||
128 | nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ | ||
129 | nand_chip->chip_delay = 20; /* 20us command delay time */ | ||
130 | |||
131 | platform_set_drvdata(pdev, host); | ||
132 | at91_nand_enable(host); | ||
133 | |||
134 | if (host->board->det_pin) { | ||
135 | if (at91_get_gpio_value(host->board->det_pin)) { | ||
136 | printk ("No SmartMedia card inserted.\n"); | ||
137 | res = ENXIO; | ||
138 | goto out; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | /* Scan to find existance of the device */ | ||
143 | if (nand_scan(mtd, 1)) { | ||
144 | res = -ENXIO; | ||
145 | goto out; | ||
146 | } | ||
147 | |||
148 | #ifdef CONFIG_MTD_PARTITIONS | ||
149 | if (host->board->partition_info) | ||
150 | partitions = host->board->partition_info(mtd->size, &num_partitions); | ||
151 | |||
152 | if ((!partitions) || (num_partitions == 0)) { | ||
153 | printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n"); | ||
154 | res = ENXIO; | ||
155 | goto release; | ||
156 | } | ||
157 | |||
158 | res = add_mtd_partitions(mtd, partitions, num_partitions); | ||
159 | #else | ||
160 | res = add_mtd_device(mtd); | ||
161 | #endif | ||
162 | |||
163 | if (!res) | ||
164 | return res; | ||
165 | |||
166 | release: | ||
167 | nand_release(mtd); | ||
168 | out: | ||
169 | at91_nand_disable(host); | ||
170 | platform_set_drvdata(pdev, NULL); | ||
171 | iounmap(host->io_base); | ||
172 | kfree(host); | ||
173 | return res; | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * Remove a NAND device. | ||
178 | */ | ||
179 | static int __devexit at91_nand_remove(struct platform_device *pdev) | ||
180 | { | ||
181 | struct at91_nand_host *host = platform_get_drvdata(pdev); | ||
182 | struct mtd_info *mtd = &host->mtd; | ||
183 | |||
184 | nand_release(mtd); | ||
185 | |||
186 | at91_nand_disable(host); | ||
187 | |||
188 | iounmap(host->io_base); | ||
189 | kfree(host); | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static struct platform_driver at91_nand_driver = { | ||
195 | .probe = at91_nand_probe, | ||
196 | .remove = at91_nand_remove, | ||
197 | .driver = { | ||
198 | .name = "at91_nand", | ||
199 | .owner = THIS_MODULE, | ||
200 | }, | ||
201 | }; | ||
202 | |||
203 | static int __init at91_nand_init(void) | ||
204 | { | ||
205 | return platform_driver_register(&at91_nand_driver); | ||
206 | } | ||
207 | |||
208 | |||
209 | static void __exit at91_nand_exit(void) | ||
210 | { | ||
211 | platform_driver_unregister(&at91_nand_driver); | ||
212 | } | ||
213 | |||
214 | |||
215 | module_init(at91_nand_init); | ||
216 | module_exit(at91_nand_exit); | ||
217 | |||
218 | MODULE_LICENSE("GPL"); | ||
219 | MODULE_AUTHOR("Rick Bronson"); | ||
220 | MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200"); | ||