]>
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 | ||
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"); |