Commit | Line | Data |
---|---|---|
42cb1403 AV |
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 | ||
693ef66d | 85 | #ifdef CONFIG_MTD_PARTITIONS |
52f83014 | 86 | static const char *part_probes[] = { "cmdlinepart", NULL }; |
693ef66d AV |
87 | #endif |
88 | ||
42cb1403 AV |
89 | /* |
90 | * Probe for the NAND device. | |
91 | */ | |
92 | static int __init at91_nand_probe(struct platform_device *pdev) | |
93 | { | |
94 | struct at91_nand_host *host; | |
95 | struct mtd_info *mtd; | |
96 | struct nand_chip *nand_chip; | |
97 | int res; | |
98 | ||
99 | #ifdef CONFIG_MTD_PARTITIONS | |
100 | struct mtd_partition *partitions = NULL; | |
101 | int num_partitions = 0; | |
102 | #endif | |
103 | ||
104 | /* Allocate memory for the device structure (and zero it) */ | |
105 | host = kzalloc(sizeof(struct at91_nand_host), GFP_KERNEL); | |
106 | if (!host) { | |
107 | printk(KERN_ERR "at91_nand: failed to allocate device structure.\n"); | |
108 | return -ENOMEM; | |
109 | } | |
110 | ||
111 | host->io_base = ioremap(pdev->resource[0].start, | |
112 | pdev->resource[0].end - pdev->resource[0].start + 1); | |
113 | if (host->io_base == NULL) { | |
114 | printk(KERN_ERR "at91_nand: ioremap failed\n"); | |
115 | kfree(host); | |
116 | return -EIO; | |
117 | } | |
118 | ||
119 | mtd = &host->mtd; | |
120 | nand_chip = &host->nand_chip; | |
121 | host->board = pdev->dev.platform_data; | |
122 | ||
123 | nand_chip->priv = host; /* link the private data structures */ | |
124 | mtd->priv = nand_chip; | |
125 | mtd->owner = THIS_MODULE; | |
126 | ||
127 | /* Set address of NAND IO lines */ | |
128 | nand_chip->IO_ADDR_R = host->io_base; | |
129 | nand_chip->IO_ADDR_W = host->io_base; | |
130 | nand_chip->cmd_ctrl = at91_nand_cmd_ctrl; | |
a4265f8d IK |
131 | |
132 | if (host->board->rdy_pin) | |
133 | nand_chip->dev_ready = at91_nand_device_ready; | |
134 | ||
42cb1403 AV |
135 | nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ |
136 | nand_chip->chip_delay = 20; /* 20us command delay time */ | |
137 | ||
dd11b8cd AV |
138 | if (host->board->bus_width_16) /* 16-bit bus width */ |
139 | nand_chip->options |= NAND_BUSWIDTH_16; | |
140 | ||
42cb1403 AV |
141 | platform_set_drvdata(pdev, host); |
142 | at91_nand_enable(host); | |
143 | ||
144 | if (host->board->det_pin) { | |
145 | if (at91_get_gpio_value(host->board->det_pin)) { | |
146 | printk ("No SmartMedia card inserted.\n"); | |
147 | res = ENXIO; | |
148 | goto out; | |
149 | } | |
150 | } | |
151 | ||
152 | /* Scan to find existance of the device */ | |
153 | if (nand_scan(mtd, 1)) { | |
154 | res = -ENXIO; | |
155 | goto out; | |
156 | } | |
157 | ||
158 | #ifdef CONFIG_MTD_PARTITIONS | |
693ef66d | 159 | #ifdef CONFIG_MTD_CMDLINE_PARTS |
842b1a10 AN |
160 | mtd->name = "at91_nand"; |
161 | num_partitions = parse_mtd_partitions(mtd, part_probes, | |
162 | &partitions, 0); | |
693ef66d | 163 | #endif |
842b1a10 AN |
164 | if (num_partitions <= 0 && host->board->partition_info) |
165 | partitions = host->board->partition_info(mtd->size, | |
166 | &num_partitions); | |
42cb1403 AV |
167 | |
168 | if ((!partitions) || (num_partitions == 0)) { | |
169 | printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n"); | |
170 | res = ENXIO; | |
171 | goto release; | |
172 | } | |
173 | ||
174 | res = add_mtd_partitions(mtd, partitions, num_partitions); | |
175 | #else | |
176 | res = add_mtd_device(mtd); | |
177 | #endif | |
178 | ||
179 | if (!res) | |
180 | return res; | |
181 | ||
182 | release: | |
183 | nand_release(mtd); | |
184 | out: | |
185 | at91_nand_disable(host); | |
186 | platform_set_drvdata(pdev, NULL); | |
187 | iounmap(host->io_base); | |
188 | kfree(host); | |
189 | return res; | |
190 | } | |
191 | ||
192 | /* | |
193 | * Remove a NAND device. | |
194 | */ | |
195 | static int __devexit at91_nand_remove(struct platform_device *pdev) | |
196 | { | |
197 | struct at91_nand_host *host = platform_get_drvdata(pdev); | |
198 | struct mtd_info *mtd = &host->mtd; | |
199 | ||
200 | nand_release(mtd); | |
201 | ||
202 | at91_nand_disable(host); | |
203 | ||
204 | iounmap(host->io_base); | |
205 | kfree(host); | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
210 | static struct platform_driver at91_nand_driver = { | |
211 | .probe = at91_nand_probe, | |
212 | .remove = at91_nand_remove, | |
213 | .driver = { | |
214 | .name = "at91_nand", | |
215 | .owner = THIS_MODULE, | |
216 | }, | |
217 | }; | |
218 | ||
219 | static int __init at91_nand_init(void) | |
220 | { | |
221 | return platform_driver_register(&at91_nand_driver); | |
222 | } | |
223 | ||
224 | ||
225 | static void __exit at91_nand_exit(void) | |
226 | { | |
227 | platform_driver_unregister(&at91_nand_driver); | |
228 | } | |
229 | ||
230 | ||
231 | module_init(at91_nand_init); | |
232 | module_exit(at91_nand_exit); | |
233 | ||
234 | MODULE_LICENSE("GPL"); | |
235 | MODULE_AUTHOR("Rick Bronson"); | |
236 | MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200"); | |
1ff18422 | 237 | MODULE_ALIAS("platform:at91_nand"); |