diff options
Diffstat (limited to 'drivers/mtd/nand/ams-delta.c')
-rw-r--r-- | drivers/mtd/nand/ams-delta.c | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c new file mode 100644 index 000000000000..d7897dc6b3c8 --- /dev/null +++ b/drivers/mtd/nand/ams-delta.c | |||
@@ -0,0 +1,237 @@ | |||
1 | /* | ||
2 | * drivers/mtd/nand/ams-delta.c | ||
3 | * | ||
4 | * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li> | ||
5 | * | ||
6 | * Derived from drivers/mtd/toto.c | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Overview: | ||
13 | * This is a device driver for the NAND flash device found on the | ||
14 | * Amstrad E3 (Delta). | ||
15 | */ | ||
16 | |||
17 | #include <linux/slab.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/mtd/mtd.h> | ||
22 | #include <linux/mtd/nand.h> | ||
23 | #include <linux/mtd/partitions.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/arch/hardware.h> | ||
26 | #include <asm/sizes.h> | ||
27 | #include <asm/arch/gpio.h> | ||
28 | #include <asm/arch/board-ams-delta.h> | ||
29 | |||
30 | /* | ||
31 | * MTD structure for E3 (Delta) | ||
32 | */ | ||
33 | static struct mtd_info *ams_delta_mtd = NULL; | ||
34 | |||
35 | #define NAND_MASK (AMS_DELTA_LATCH2_NAND_NRE | AMS_DELTA_LATCH2_NAND_NWE | AMS_DELTA_LATCH2_NAND_CLE | AMS_DELTA_LATCH2_NAND_ALE | AMS_DELTA_LATCH2_NAND_NCE | AMS_DELTA_LATCH2_NAND_NWP) | ||
36 | |||
37 | /* | ||
38 | * Define partitions for flash devices | ||
39 | */ | ||
40 | |||
41 | static struct mtd_partition partition_info[] = { | ||
42 | { .name = "Kernel", | ||
43 | .offset = 0, | ||
44 | .size = 3 * SZ_1M + SZ_512K }, | ||
45 | { .name = "u-boot", | ||
46 | .offset = 3 * SZ_1M + SZ_512K, | ||
47 | .size = SZ_256K }, | ||
48 | { .name = "u-boot params", | ||
49 | .offset = 3 * SZ_1M + SZ_512K + SZ_256K, | ||
50 | .size = SZ_256K }, | ||
51 | { .name = "Amstrad LDR", | ||
52 | .offset = 4 * SZ_1M, | ||
53 | .size = SZ_256K }, | ||
54 | { .name = "File system", | ||
55 | .offset = 4 * SZ_1M + 1 * SZ_256K, | ||
56 | .size = 27 * SZ_1M }, | ||
57 | { .name = "PBL reserved", | ||
58 | .offset = 32 * SZ_1M - 3 * SZ_256K, | ||
59 | .size = 3 * SZ_256K }, | ||
60 | }; | ||
61 | |||
62 | static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte) | ||
63 | { | ||
64 | struct nand_chip *this = mtd->priv; | ||
65 | |||
66 | omap_writew(0, (OMAP_MPUIO_BASE + OMAP_MPUIO_IO_CNTL)); | ||
67 | omap_writew(byte, this->IO_ADDR_W); | ||
68 | ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE, 0); | ||
69 | ndelay(40); | ||
70 | ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE, | ||
71 | AMS_DELTA_LATCH2_NAND_NWE); | ||
72 | } | ||
73 | |||
74 | static u_char ams_delta_read_byte(struct mtd_info *mtd) | ||
75 | { | ||
76 | u_char res; | ||
77 | struct nand_chip *this = mtd->priv; | ||
78 | |||
79 | ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE, 0); | ||
80 | ndelay(40); | ||
81 | omap_writew(~0, (OMAP_MPUIO_BASE + OMAP_MPUIO_IO_CNTL)); | ||
82 | res = omap_readw(this->IO_ADDR_R); | ||
83 | ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE, | ||
84 | AMS_DELTA_LATCH2_NAND_NRE); | ||
85 | |||
86 | return res; | ||
87 | } | ||
88 | |||
89 | static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf, | ||
90 | int len) | ||
91 | { | ||
92 | int i; | ||
93 | |||
94 | for (i=0; i<len; i++) | ||
95 | ams_delta_write_byte(mtd, buf[i]); | ||
96 | } | ||
97 | |||
98 | static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len) | ||
99 | { | ||
100 | int i; | ||
101 | |||
102 | for (i=0; i<len; i++) | ||
103 | buf[i] = ams_delta_read_byte(mtd); | ||
104 | } | ||
105 | |||
106 | static int ams_delta_verify_buf(struct mtd_info *mtd, const u_char *buf, | ||
107 | int len) | ||
108 | { | ||
109 | int i; | ||
110 | |||
111 | for (i=0; i<len; i++) | ||
112 | if (buf[i] != ams_delta_read_byte(mtd)) | ||
113 | return -EFAULT; | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * Command control function | ||
120 | * | ||
121 | * ctrl: | ||
122 | * NAND_NCE: bit 0 -> bit 2 | ||
123 | * NAND_CLE: bit 1 -> bit 7 | ||
124 | * NAND_ALE: bit 2 -> bit 6 | ||
125 | */ | ||
126 | static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd, | ||
127 | unsigned int ctrl) | ||
128 | { | ||
129 | |||
130 | if (ctrl & NAND_CTRL_CHANGE) { | ||
131 | unsigned long bits; | ||
132 | |||
133 | bits = (~ctrl & NAND_NCE) << 2; | ||
134 | bits |= (ctrl & NAND_CLE) << 7; | ||
135 | bits |= (ctrl & NAND_ALE) << 6; | ||
136 | |||
137 | ams_delta_latch2_write(0xC2, bits); | ||
138 | } | ||
139 | |||
140 | if (cmd != NAND_CMD_NONE) | ||
141 | ams_delta_write_byte(mtd, cmd); | ||
142 | } | ||
143 | |||
144 | static int ams_delta_nand_ready(struct mtd_info *mtd) | ||
145 | { | ||
146 | return omap_get_gpio_datain(AMS_DELTA_GPIO_PIN_NAND_RB); | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * Main initialization routine | ||
151 | */ | ||
152 | static int __init ams_delta_init(void) | ||
153 | { | ||
154 | struct nand_chip *this; | ||
155 | int err = 0; | ||
156 | |||
157 | /* Allocate memory for MTD device structure and private data */ | ||
158 | ams_delta_mtd = kmalloc(sizeof(struct mtd_info) + | ||
159 | sizeof(struct nand_chip), GFP_KERNEL); | ||
160 | if (!ams_delta_mtd) { | ||
161 | printk (KERN_WARNING "Unable to allocate E3 NAND MTD device structure.\n"); | ||
162 | err = -ENOMEM; | ||
163 | goto out; | ||
164 | } | ||
165 | |||
166 | ams_delta_mtd->owner = THIS_MODULE; | ||
167 | |||
168 | /* Get pointer to private data */ | ||
169 | this = (struct nand_chip *) (&ams_delta_mtd[1]); | ||
170 | |||
171 | /* Initialize structures */ | ||
172 | memset(ams_delta_mtd, 0, sizeof(struct mtd_info)); | ||
173 | memset(this, 0, sizeof(struct nand_chip)); | ||
174 | |||
175 | /* Link the private data with the MTD structure */ | ||
176 | ams_delta_mtd->priv = this; | ||
177 | |||
178 | /* Set address of NAND IO lines */ | ||
179 | this->IO_ADDR_R = (OMAP_MPUIO_BASE + OMAP_MPUIO_INPUT_LATCH); | ||
180 | this->IO_ADDR_W = (OMAP_MPUIO_BASE + OMAP_MPUIO_OUTPUT); | ||
181 | this->read_byte = ams_delta_read_byte; | ||
182 | this->write_buf = ams_delta_write_buf; | ||
183 | this->read_buf = ams_delta_read_buf; | ||
184 | this->verify_buf = ams_delta_verify_buf; | ||
185 | this->cmd_ctrl = ams_delta_hwcontrol; | ||
186 | if (!omap_request_gpio(AMS_DELTA_GPIO_PIN_NAND_RB)) { | ||
187 | this->dev_ready = ams_delta_nand_ready; | ||
188 | } else { | ||
189 | this->dev_ready = NULL; | ||
190 | printk(KERN_NOTICE "Couldn't request gpio for Delta NAND ready.\n"); | ||
191 | } | ||
192 | /* 25 us command delay time */ | ||
193 | this->chip_delay = 30; | ||
194 | this->ecc.mode = NAND_ECC_SOFT; | ||
195 | |||
196 | /* Set chip enabled, but */ | ||
197 | ams_delta_latch2_write(NAND_MASK, AMS_DELTA_LATCH2_NAND_NRE | | ||
198 | AMS_DELTA_LATCH2_NAND_NWE | | ||
199 | AMS_DELTA_LATCH2_NAND_NCE | | ||
200 | AMS_DELTA_LATCH2_NAND_NWP); | ||
201 | |||
202 | /* Scan to find existance of the device */ | ||
203 | if (nand_scan(ams_delta_mtd, 1)) { | ||
204 | err = -ENXIO; | ||
205 | goto out_mtd; | ||
206 | } | ||
207 | |||
208 | /* Register the partitions */ | ||
209 | add_mtd_partitions(ams_delta_mtd, partition_info, | ||
210 | ARRAY_SIZE(partition_info)); | ||
211 | |||
212 | goto out; | ||
213 | |||
214 | out_mtd: | ||
215 | kfree(ams_delta_mtd); | ||
216 | out: | ||
217 | return err; | ||
218 | } | ||
219 | |||
220 | module_init(ams_delta_init); | ||
221 | |||
222 | /* | ||
223 | * Clean up routine | ||
224 | */ | ||
225 | static void __exit ams_delta_cleanup(void) | ||
226 | { | ||
227 | /* Release resources, unregister device */ | ||
228 | nand_release(ams_delta_mtd); | ||
229 | |||
230 | /* Free the MTD device structure */ | ||
231 | kfree(ams_delta_mtd); | ||
232 | } | ||
233 | module_exit(ams_delta_cleanup); | ||
234 | |||
235 | MODULE_LICENSE("GPL"); | ||
236 | MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>"); | ||
237 | MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)"); | ||