diff options
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/onenand/omap-onenand.c | 271 |
1 files changed, 89 insertions, 182 deletions
diff --git a/drivers/mtd/onenand/omap-onenand.c b/drivers/mtd/onenand/omap-onenand.c index 7c89549f7f58..57e69f184d33 100644 --- a/drivers/mtd/onenand/omap-onenand.c +++ b/drivers/mtd/onenand/omap-onenand.c | |||
@@ -4,236 +4,143 @@ | |||
4 | * Copyright (c) 2005 Samsung Electronics | 4 | * Copyright (c) 2005 Samsung Electronics |
5 | * Kyungmin Park <kyungmin.park@samsung.com> | 5 | * Kyungmin Park <kyungmin.park@samsung.com> |
6 | * | 6 | * |
7 | * Derived from linux/drivers/mtd/nand/omap-nand-flash.c | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
11 | * published by the Free Software Foundation. | 9 | * published by the Free Software Foundation. |
12 | * | 10 | * |
13 | * Overview: | 11 | * Overview: |
14 | * This is a device driver for the OneNAND flash device for TI OMAP boards. | 12 | * This is a device driver for the OneNAND flash for OMAP boards. |
15 | */ | 13 | */ |
16 | 14 | ||
17 | #include <linux/slab.h> | 15 | #include <linux/device.h> |
18 | #include <linux/init.h> | ||
19 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/init.h> | ||
20 | #include <linux/mtd/mtd.h> | 18 | #include <linux/mtd/mtd.h> |
21 | #include <linux/mtd/onenand.h> | 19 | #include <linux/mtd/onenand.h> |
22 | #include <linux/mtd/partitions.h> | 20 | #include <linux/mtd/partitions.h> |
23 | 21 | ||
24 | #include <asm/io.h> | 22 | #include <asm/io.h> |
25 | #include <asm/arch/hardware.h> | 23 | #include <asm/mach/flash.h> |
26 | #include <asm/arch/tc.h> | ||
27 | #include <asm/sizes.h> | ||
28 | #include <asm/mach-types.h> | ||
29 | 24 | ||
30 | #define OMAP_ONENAND_FLASH_START1 OMAP_CS2A_PHYS | 25 | #define DRIVER_NAME "onenand" |
31 | #define OMAP_ONENAND_FLASH_START2 omap_cs3_phys() | ||
32 | /* | ||
33 | * MTD structure for OMAP board | ||
34 | */ | ||
35 | static struct mtd_info *omap_onenand_mtd = NULL; | ||
36 | 26 | ||
37 | /* | ||
38 | * Define partitions for flash devices | ||
39 | */ | ||
40 | 27 | ||
41 | #ifdef CONFIG_MTD_PARTITIONS | 28 | #ifdef CONFIG_MTD_PARTITIONS |
42 | static struct mtd_partition static_partition[] = { | ||
43 | { | ||
44 | .name = "X-Loader + U-Boot", | ||
45 | .offset = 0, | ||
46 | .size = SZ_128K, | ||
47 | .mask_flags = MTD_WRITEABLE /* force read-only */ | ||
48 | }, | ||
49 | { | ||
50 | .name = "U-Boot Environment", | ||
51 | .offset = MTDPART_OFS_APPEND, | ||
52 | .size = SZ_128K, | ||
53 | .mask_flags = MTD_WRITEABLE /* force read-only */ | ||
54 | }, | ||
55 | { | ||
56 | .name = "kernel", | ||
57 | .offset = MTDPART_OFS_APPEND, | ||
58 | .size = 2 * SZ_1M | ||
59 | }, | ||
60 | { | ||
61 | .name = "filesystem0", | ||
62 | .offset = MTDPART_OFS_APPEND, | ||
63 | .size = SZ_16M, | ||
64 | }, | ||
65 | { | ||
66 | .name = "filesystem1", | ||
67 | .offset = MTDPART_OFS_APPEND, | ||
68 | .size = MTDPART_SIZ_FULL, | ||
69 | }, | ||
70 | }; | ||
71 | |||
72 | static const char *part_probes[] = { "cmdlinepart", NULL, }; | 29 | static const char *part_probes[] = { "cmdlinepart", NULL, }; |
73 | |||
74 | #endif | 30 | #endif |
75 | 31 | ||
76 | #ifdef CONFIG_MTD_ONENAND_SYNC_READ | 32 | struct omap_onenand_info { |
77 | static unsigned int omap_emifs_cs; | 33 | struct mtd_info mtd; |
34 | struct mtd_partition *parts; | ||
35 | struct onenand_chip onenand; | ||
36 | }; | ||
78 | 37 | ||
79 | static void omap_find_emifs_cs(unsigned int addr) | 38 | static int __devinit omap_onenand_probe(struct device *dev) |
80 | { | 39 | { |
81 | /* Check CS3 */ | 40 | struct omap_onenand_info *info; |
82 | if (OMAP_EMIFS_CONFIG_REG & OMAP_EMIFS_CONFIG_BM && addr == 0x0) { | 41 | struct platform_device *pdev = to_platform_device(dev); |
83 | omap_emifs_cs = 3; | 42 | struct onenand_platform_data *pdata = pdev->dev.platform_data; |
84 | } else { | 43 | struct resource *res = pdev->resource; |
85 | omap_emifs_cs = (addr >> 26); | 44 | unsigned long size = res->end - res->start + 1; |
45 | int err; | ||
46 | |||
47 | info = kmalloc(sizeof(struct omap_onenand_info), GFP_KERNEL); | ||
48 | if (!info) | ||
49 | return -ENOMEM; | ||
50 | |||
51 | memset(info, 0, sizeof(struct omap_onenand_info)); | ||
52 | |||
53 | if (!request_mem_region(res->start, size, dev->driver->name)) { | ||
54 | err = -EBUSY; | ||
55 | goto out_free_info; | ||
86 | } | 56 | } |
87 | } | ||
88 | 57 | ||
89 | /** | 58 | info->onenand.base = ioremap(res->start, size); |
90 | * omap_onenand_mmcontrol - Control OMAP EMIFS | 59 | if (!info->onenand.base) { |
91 | */ | 60 | err = -ENOMEM; |
92 | static void omap_onenand_mmcontrol(struct mtd_info *mtd, int sync_read) | 61 | goto out_release_mem_region; |
93 | { | ||
94 | struct onenand_chip *this = mtd->priv; | ||
95 | static unsigned long omap_emifs_ccs, omap_emifs_acs; | ||
96 | static unsigned long onenand_sys_cfg1; | ||
97 | int config, emifs_ccs, emifs_acs; | ||
98 | |||
99 | if (sync_read) { | ||
100 | /* | ||
101 | * Note: BRL and RDWST is equal | ||
102 | */ | ||
103 | omap_emifs_ccs = EMIFS_CCS(omap_emifs_cs); | ||
104 | omap_emifs_acs = EMIFS_ACS(omap_emifs_cs); | ||
105 | |||
106 | emifs_ccs = 0x41141; | ||
107 | emifs_acs = 0x1; | ||
108 | |||
109 | /* OneNAND System Configuration 1 */ | ||
110 | onenand_sys_cfg1 = this->read_word(this->base + ONENAND_REG_SYS_CFG1); | ||
111 | config = (onenand_sys_cfg1 | ||
112 | & ~(0x3f << ONENAND_SYS_CFG1_BL_SHIFT)) | ||
113 | | ONENAND_SYS_CFG1_SYNC_READ | ||
114 | | ONENAND_SYS_CFG1_BRL_4 | ||
115 | | ONENAND_SYS_CFG1_BL_8; | ||
116 | } else { | ||
117 | emifs_ccs = omap_emifs_ccs; | ||
118 | emifs_acs = omap_emifs_acs; | ||
119 | config = onenand_sys_cfg1; | ||
120 | } | 62 | } |
121 | 63 | ||
122 | this->write_word(config, this->base + ONENAND_REG_SYS_CFG1); | 64 | info->onenand.mmcontrol = pdata->mmcontrol; |
123 | EMIFS_CCS(omap_emifs_cs) = emifs_ccs; | ||
124 | EMIFS_ACS(omap_emifs_cs) = emifs_acs; | ||
125 | } | ||
126 | #else | ||
127 | #define omap_find_emifs_cs(x) do { } while (0) | ||
128 | #define omap_onenand_mmcontrol NULL | ||
129 | #endif | ||
130 | 65 | ||
66 | info->mtd.name = pdev->dev.bus_id; | ||
67 | info->mtd.priv = &info->onenand; | ||
68 | info->mtd.owner = THIS_MODULE; | ||
131 | 69 | ||
132 | /* Scan to find existance of the device at base. | 70 | if (onenand_scan(&info->mtd, 1)) { |
133 | This also allocates oob and data internal buffers */ | 71 | err = -ENXIO; |
134 | static char onenand_name[] = "onenand"; | 72 | goto out_iounmap; |
135 | |||
136 | /* | ||
137 | * Main initialization routine | ||
138 | */ | ||
139 | static int __init omap_onenand_init (void) | ||
140 | { | ||
141 | struct onenand_chip *this; | ||
142 | struct mtd_partition *dynamic_partition = 0; | ||
143 | int err = 0; | ||
144 | |||
145 | /* Allocate memory for MTD device structure and private data */ | ||
146 | omap_onenand_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct onenand_chip), | ||
147 | GFP_KERNEL); | ||
148 | if (!omap_onenand_mtd) { | ||
149 | printk (KERN_WARNING "Unable to allocate OneNAND MTD device structure.\n"); | ||
150 | err = -ENOMEM; | ||
151 | goto out; | ||
152 | } | 73 | } |
153 | 74 | ||
154 | /* Get pointer to private data */ | 75 | #ifdef CONFIG_MTD_PARTITIONS |
155 | this = (struct onenand_chip *) (&omap_onenand_mtd[1]); | 76 | err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); |
77 | if (err > 0) | ||
78 | add_mtd_partitions(&info->mtd, info->parts, err); | ||
79 | else if (err < 0 && pdata->parts) | ||
80 | add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); | ||
81 | else | ||
82 | #endif | ||
83 | err = add_mtd_device(&info->mtd); | ||
156 | 84 | ||
157 | /* Initialize structures */ | 85 | dev_set_drvdata(&pdev->dev, info); |
158 | memset((char *) omap_onenand_mtd, 0, sizeof(struct mtd_info) + sizeof(struct onenand_chip)); | ||
159 | 86 | ||
160 | /* Link the private data with the MTD structure */ | 87 | return 0; |
161 | omap_onenand_mtd->priv = this; | ||
162 | this->mmcontrol = omap_onenand_mmcontrol; | ||
163 | 88 | ||
164 | /* try the first address */ | 89 | out_iounmap: |
165 | this->base = ioremap(OMAP_ONENAND_FLASH_START1, SZ_128K); | 90 | iounmap(info->onenand.base); |
166 | omap_find_emifs_cs(OMAP_ONENAND_FLASH_START1); | 91 | out_release_mem_region: |
92 | release_mem_region(res->start, size); | ||
93 | out_free_info: | ||
94 | kfree(info); | ||
167 | 95 | ||
168 | omap_onenand_mtd->name = onenand_name; | 96 | return err; |
169 | if (onenand_scan(omap_onenand_mtd, 1)){ | 97 | } |
170 | /* try the second address */ | ||
171 | iounmap(this->base); | ||
172 | this->base = ioremap(OMAP_ONENAND_FLASH_START2, SZ_128K); | ||
173 | omap_find_emifs_cs(OMAP_ONENAND_FLASH_START2); | ||
174 | 98 | ||
175 | if (onenand_scan(omap_onenand_mtd, 1)) { | 99 | static int __devexit omap_onenand_remove(struct device *dev) |
176 | iounmap(this->base); | 100 | { |
177 | err = -ENXIO; | 101 | struct platform_device *pdev = to_platform_device(dev); |
178 | goto out_mtd; | 102 | struct omap_onenand_info *info = dev_get_drvdata(&pdev->dev); |
179 | } | 103 | struct resource *res = pdev->resource; |
180 | } | 104 | unsigned long size = res->end - res->start + 1; |
181 | 105 | ||
182 | /* Register the partitions */ | 106 | dev_set_drvdata(&pdev->dev, NULL); |
183 | switch (omap_onenand_mtd->size) { | 107 | |
184 | case SZ_128M: | 108 | if (info) { |
185 | case SZ_64M: | 109 | if (info->parts) |
186 | case SZ_32M: | 110 | del_mtd_partitions(&info->mtd); |
187 | #ifdef CONFIG_MTD_PARTITIONS | ||
188 | err = parse_mtd_partitions(omap_onenand_mtd, part_probes, | ||
189 | &dynamic_partition, 0); | ||
190 | if (err > 0) | ||
191 | err = add_mtd_partitions(omap_onenand_mtd, | ||
192 | dynamic_partition, err); | ||
193 | else if (1) | ||
194 | err = add_mtd_partitions(omap_onenand_mtd, | ||
195 | static_partition, | ||
196 | ARRAY_SIZE(static_partition)); | ||
197 | else | 111 | else |
198 | #endif | 112 | del_mtd_device(&info->mtd); |
199 | err = add_mtd_device(omap_onenand_mtd); | ||
200 | if (err) | ||
201 | goto out_buf; | ||
202 | break; | ||
203 | 113 | ||
204 | default: | 114 | onenand_release(&info->mtd); |
205 | printk(KERN_WARNING "Unsupported OneNAND device\n"); | 115 | release_mem_region(res->start, size); |
206 | err = -ENXIO; | 116 | iounmap(info->onenand.base); |
207 | goto out_buf; | 117 | kfree(info); |
208 | } | 118 | } |
209 | 119 | ||
210 | return 0; | 120 | return 0; |
211 | |||
212 | out_buf: | ||
213 | onenand_release(omap_onenand_mtd); | ||
214 | iounmap(this->base); | ||
215 | out_mtd: | ||
216 | kfree(omap_onenand_mtd); | ||
217 | out: | ||
218 | return err; | ||
219 | } | 121 | } |
220 | 122 | ||
221 | /* | 123 | static struct device_driver omap_onenand_driver = { |
222 | * Clean up routine | 124 | .name = DRIVER_NAME, |
223 | */ | 125 | .bus = &platform_bus_type, |
224 | static void __exit omap_onenand_cleanup (void) | 126 | .probe = omap_onenand_probe, |
127 | .remove = __devexit_p(omap_onenand_remove), | ||
128 | }; | ||
129 | |||
130 | MODULE_ALIAS(DRIVER_NAME); | ||
131 | |||
132 | static int __init omap_onenand_init(void) | ||
225 | { | 133 | { |
226 | struct onenand_chip *this = omap_onenand_mtd->priv; | 134 | return driver_register(&omap_onenand_driver); |
135 | } | ||
227 | 136 | ||
228 | /* onenand_release frees MTD partitions, MTD structure | 137 | static void __exit omap_onenand_exit(void) |
229 | and onenand internal buffers */ | 138 | { |
230 | onenand_release(omap_onenand_mtd); | 139 | driver_unregister(&omap_onenand_driver); |
231 | iounmap(this->base); | ||
232 | kfree(omap_onenand_mtd); | ||
233 | } | 140 | } |
234 | 141 | ||
235 | module_init(omap_onenand_init); | 142 | module_init(omap_onenand_init); |
236 | module_exit(omap_onenand_cleanup); | 143 | module_exit(omap_onenand_exit); |
237 | 144 | ||
238 | MODULE_LICENSE("GPL"); | 145 | MODULE_LICENSE("GPL"); |
239 | MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); | 146 | MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); |