diff options
author | Catalin Marinas <catalin.marinas@arm.com> | 2009-04-23 12:41:10 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2009-05-29 09:00:21 -0400 |
commit | 9fd1e8f92ad17b3bc94245fee9b4e4bfea0dba0e (patch) | |
tree | 6e44bd5e4046b2161a404e8786633ddf34172e19 /drivers | |
parent | 8d42b524f4323cdf49872fe5e3a0e824f32feb51 (diff) |
mtd: Add armflash support for multiple blocks of flash
This patch adds MTD concatenation support to integrator-flash.c for
platforms with more than one block of flash memory (e.g. RealView
PB11MPCore). The implementation is based on the sa1100-flash.c one.
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/maps/integrator-flash.c | 226 |
1 files changed, 164 insertions, 62 deletions
diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index c9681a339a59..b08a798ee254 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c | |||
@@ -36,27 +36,33 @@ | |||
36 | #include <linux/mtd/mtd.h> | 36 | #include <linux/mtd/mtd.h> |
37 | #include <linux/mtd/map.h> | 37 | #include <linux/mtd/map.h> |
38 | #include <linux/mtd/partitions.h> | 38 | #include <linux/mtd/partitions.h> |
39 | #include <linux/mtd/concat.h> | ||
39 | 40 | ||
40 | #include <asm/mach/flash.h> | 41 | #include <asm/mach/flash.h> |
41 | #include <mach/hardware.h> | 42 | #include <mach/hardware.h> |
42 | #include <asm/system.h> | 43 | #include <asm/system.h> |
43 | 44 | ||
44 | #ifdef CONFIG_ARCH_P720T | 45 | #define SUBDEV_NAME_SIZE (BUS_ID_SIZE + 2) |
45 | #define FLASH_BASE (0x04000000) | ||
46 | #define FLASH_SIZE (64*1024*1024) | ||
47 | #endif | ||
48 | 46 | ||
49 | struct armflash_info { | 47 | struct armflash_subdev_info { |
48 | char name[SUBDEV_NAME_SIZE]; | ||
49 | struct mtd_info *mtd; | ||
50 | struct map_info map; | ||
50 | struct flash_platform_data *plat; | 51 | struct flash_platform_data *plat; |
52 | }; | ||
53 | |||
54 | struct armflash_info { | ||
51 | struct resource *res; | 55 | struct resource *res; |
52 | struct mtd_partition *parts; | 56 | struct mtd_partition *parts; |
53 | struct mtd_info *mtd; | 57 | struct mtd_info *mtd; |
54 | struct map_info map; | 58 | int nr_subdev; |
59 | struct armflash_subdev_info subdev[0]; | ||
55 | }; | 60 | }; |
56 | 61 | ||
57 | static void armflash_set_vpp(struct map_info *map, int on) | 62 | static void armflash_set_vpp(struct map_info *map, int on) |
58 | { | 63 | { |
59 | struct armflash_info *info = container_of(map, struct armflash_info, map); | 64 | struct armflash_subdev_info *info = |
65 | container_of(map, struct armflash_subdev_info, map); | ||
60 | 66 | ||
61 | if (info->plat && info->plat->set_vpp) | 67 | if (info->plat && info->plat->set_vpp) |
62 | info->plat->set_vpp(on); | 68 | info->plat->set_vpp(on); |
@@ -64,32 +70,17 @@ static void armflash_set_vpp(struct map_info *map, int on) | |||
64 | 70 | ||
65 | static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; | 71 | static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; |
66 | 72 | ||
67 | static int armflash_probe(struct platform_device *dev) | 73 | static int armflash_subdev_probe(struct armflash_subdev_info *subdev, |
74 | struct resource *res) | ||
68 | { | 75 | { |
69 | struct flash_platform_data *plat = dev->dev.platform_data; | 76 | struct flash_platform_data *plat = subdev->plat; |
70 | struct resource *res = dev->resource; | 77 | resource_size_t size = res->end - res->start + 1; |
71 | unsigned int size = res->end - res->start + 1; | ||
72 | struct armflash_info *info; | ||
73 | int err; | ||
74 | void __iomem *base; | 78 | void __iomem *base; |
79 | int err = 0; | ||
75 | 80 | ||
76 | info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL); | 81 | if (!request_mem_region(res->start, size, subdev->name)) { |
77 | if (!info) { | ||
78 | err = -ENOMEM; | ||
79 | goto out; | ||
80 | } | ||
81 | |||
82 | info->plat = plat; | ||
83 | if (plat && plat->init) { | ||
84 | err = plat->init(); | ||
85 | if (err) | ||
86 | goto no_resource; | ||
87 | } | ||
88 | |||
89 | info->res = request_mem_region(res->start, size, "armflash"); | ||
90 | if (!info->res) { | ||
91 | err = -EBUSY; | 82 | err = -EBUSY; |
92 | goto no_resource; | 83 | goto out; |
93 | } | 84 | } |
94 | 85 | ||
95 | base = ioremap(res->start, size); | 86 | base = ioremap(res->start, size); |
@@ -101,27 +92,132 @@ static int armflash_probe(struct platform_device *dev) | |||
101 | /* | 92 | /* |
102 | * look for CFI based flash parts fitted to this board | 93 | * look for CFI based flash parts fitted to this board |
103 | */ | 94 | */ |
104 | info->map.size = size; | 95 | subdev->map.size = size; |
105 | info->map.bankwidth = plat->width; | 96 | subdev->map.bankwidth = plat->width; |
106 | info->map.phys = res->start; | 97 | subdev->map.phys = res->start; |
107 | info->map.virt = base; | 98 | subdev->map.virt = base; |
108 | info->map.name = dev_name(&dev->dev); | 99 | subdev->map.name = subdev->name; |
109 | info->map.set_vpp = armflash_set_vpp; | 100 | subdev->map.set_vpp = armflash_set_vpp; |
110 | 101 | ||
111 | simple_map_init(&info->map); | 102 | simple_map_init(&subdev->map); |
112 | 103 | ||
113 | /* | 104 | /* |
114 | * Also, the CFI layer automatically works out what size | 105 | * Also, the CFI layer automatically works out what size |
115 | * of chips we have, and does the necessary identification | 106 | * of chips we have, and does the necessary identification |
116 | * for us automatically. | 107 | * for us automatically. |
117 | */ | 108 | */ |
118 | info->mtd = do_map_probe(plat->map_name, &info->map); | 109 | subdev->mtd = do_map_probe(plat->map_name, &subdev->map); |
119 | if (!info->mtd) { | 110 | if (!subdev->mtd) { |
120 | err = -ENXIO; | 111 | err = -ENXIO; |
121 | goto no_device; | 112 | goto no_device; |
122 | } | 113 | } |
123 | 114 | ||
124 | info->mtd->owner = THIS_MODULE; | 115 | subdev->mtd->owner = THIS_MODULE; |
116 | |||
117 | /* Successful? */ | ||
118 | if (err == 0) | ||
119 | return err; | ||
120 | |||
121 | if (subdev->mtd) | ||
122 | map_destroy(subdev->mtd); | ||
123 | no_device: | ||
124 | iounmap(base); | ||
125 | no_mem: | ||
126 | release_mem_region(res->start, size); | ||
127 | out: | ||
128 | return err; | ||
129 | } | ||
130 | |||
131 | static void armflash_subdev_remove(struct armflash_subdev_info *subdev) | ||
132 | { | ||
133 | if (subdev->mtd) | ||
134 | map_destroy(subdev->mtd); | ||
135 | if (subdev->map.virt) | ||
136 | iounmap(subdev->map.virt); | ||
137 | release_mem_region(subdev->map.phys, subdev->map.size); | ||
138 | } | ||
139 | |||
140 | static int armflash_probe(struct platform_device *dev) | ||
141 | { | ||
142 | struct flash_platform_data *plat = dev->dev.platform_data; | ||
143 | unsigned int size; | ||
144 | struct armflash_info *info; | ||
145 | int i, nr, err; | ||
146 | |||
147 | /* Count the number of devices */ | ||
148 | for (nr = 0; ; nr++) | ||
149 | if (!platform_get_resource(dev, IORESOURCE_MEM, nr)) | ||
150 | break; | ||
151 | if (nr == 0) { | ||
152 | err = -ENODEV; | ||
153 | goto out; | ||
154 | } | ||
155 | |||
156 | size = sizeof(struct armflash_info) + | ||
157 | sizeof(struct armflash_subdev_info) * nr; | ||
158 | info = kzalloc(size, GFP_KERNEL); | ||
159 | if (!info) { | ||
160 | err = -ENOMEM; | ||
161 | goto out; | ||
162 | } | ||
163 | |||
164 | if (plat && plat->init) { | ||
165 | err = plat->init(); | ||
166 | if (err) | ||
167 | goto no_resource; | ||
168 | } | ||
169 | |||
170 | for (i = 0; i < nr; i++) { | ||
171 | struct armflash_subdev_info *subdev = &info->subdev[i]; | ||
172 | struct resource *res; | ||
173 | |||
174 | res = platform_get_resource(dev, IORESOURCE_MEM, i); | ||
175 | if (!res) | ||
176 | break; | ||
177 | |||
178 | if (nr == 1) | ||
179 | /* No MTD concatenation, just use the default name */ | ||
180 | snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s", | ||
181 | dev_name(&dev->dev)); | ||
182 | else | ||
183 | snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s-%d", | ||
184 | dev_name(&dev->dev), i); | ||
185 | subdev->plat = plat; | ||
186 | |||
187 | err = armflash_subdev_probe(subdev, res); | ||
188 | if (err) | ||
189 | break; | ||
190 | } | ||
191 | info->nr_subdev = i; | ||
192 | |||
193 | if (err) | ||
194 | goto subdev_err; | ||
195 | |||
196 | if (info->nr_subdev == 1) | ||
197 | info->mtd = info->subdev[0].mtd; | ||
198 | else if (info->nr_subdev > 1) { | ||
199 | #ifdef CONFIG_MTD_CONCAT | ||
200 | struct mtd_info *cdev[info->nr_subdev]; | ||
201 | |||
202 | /* | ||
203 | * We detected multiple devices. Concatenate them together. | ||
204 | */ | ||
205 | for (i = 0; i < info->nr_subdev; i++) | ||
206 | cdev[i] = info->subdev[i].mtd; | ||
207 | |||
208 | info->mtd = mtd_concat_create(cdev, info->nr_subdev, | ||
209 | dev_name(&dev->dev)); | ||
210 | if (info->mtd == NULL) | ||
211 | err = -ENXIO; | ||
212 | #else | ||
213 | printk(KERN_ERR "armflash: multiple devices found but " | ||
214 | "MTD concat support disabled.\n"); | ||
215 | err = -ENXIO; | ||
216 | #endif | ||
217 | } | ||
218 | |||
219 | if (err < 0) | ||
220 | goto cleanup; | ||
125 | 221 | ||
126 | err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); | 222 | err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); |
127 | if (err > 0) { | 223 | if (err > 0) { |
@@ -131,28 +227,30 @@ static int armflash_probe(struct platform_device *dev) | |||
131 | "mtd partition registration failed: %d\n", err); | 227 | "mtd partition registration failed: %d\n", err); |
132 | } | 228 | } |
133 | 229 | ||
134 | if (err == 0) | 230 | if (err == 0) { |
135 | platform_set_drvdata(dev, info); | 231 | platform_set_drvdata(dev, info); |
232 | return err; | ||
233 | } | ||
136 | 234 | ||
137 | /* | 235 | /* |
138 | * If we got an error, free all resources. | 236 | * We got an error, free all resources. |
139 | */ | 237 | */ |
140 | if (err < 0) { | 238 | cleanup: |
141 | if (info->mtd) { | 239 | if (info->mtd) { |
142 | del_mtd_partitions(info->mtd); | 240 | del_mtd_partitions(info->mtd); |
143 | map_destroy(info->mtd); | 241 | #ifdef CONFIG_MTD_CONCAT |
144 | } | 242 | if (info->mtd != info->subdev[0].mtd) |
145 | kfree(info->parts); | 243 | mtd_concat_destroy(info->mtd); |
146 | 244 | #endif | |
147 | no_device: | ||
148 | iounmap(base); | ||
149 | no_mem: | ||
150 | release_mem_region(res->start, size); | ||
151 | no_resource: | ||
152 | if (plat && plat->exit) | ||
153 | plat->exit(); | ||
154 | kfree(info); | ||
155 | } | 245 | } |
246 | kfree(info->parts); | ||
247 | subdev_err: | ||
248 | for (i = info->nr_subdev - 1; i >= 0; i--) | ||
249 | armflash_subdev_remove(&info->subdev[i]); | ||
250 | no_resource: | ||
251 | if (plat && plat->exit) | ||
252 | plat->exit(); | ||
253 | kfree(info); | ||
156 | out: | 254 | out: |
157 | return err; | 255 | return err; |
158 | } | 256 | } |
@@ -160,22 +258,26 @@ static int armflash_probe(struct platform_device *dev) | |||
160 | static int armflash_remove(struct platform_device *dev) | 258 | static int armflash_remove(struct platform_device *dev) |
161 | { | 259 | { |
162 | struct armflash_info *info = platform_get_drvdata(dev); | 260 | struct armflash_info *info = platform_get_drvdata(dev); |
261 | struct flash_platform_data *plat = dev->dev.platform_data; | ||
262 | int i; | ||
163 | 263 | ||
164 | platform_set_drvdata(dev, NULL); | 264 | platform_set_drvdata(dev, NULL); |
165 | 265 | ||
166 | if (info) { | 266 | if (info) { |
167 | if (info->mtd) { | 267 | if (info->mtd) { |
168 | del_mtd_partitions(info->mtd); | 268 | del_mtd_partitions(info->mtd); |
169 | map_destroy(info->mtd); | 269 | #ifdef CONFIG_MTD_CONCAT |
270 | if (info->mtd != info->subdev[0].mtd) | ||
271 | mtd_concat_destroy(info->mtd); | ||
272 | #endif | ||
170 | } | 273 | } |
171 | kfree(info->parts); | 274 | kfree(info->parts); |
172 | 275 | ||
173 | iounmap(info->map.virt); | 276 | for (i = info->nr_subdev - 1; i >= 0; i--) |
174 | release_resource(info->res); | 277 | armflash_subdev_remove(&info->subdev[i]); |
175 | kfree(info->res); | ||
176 | 278 | ||
177 | if (info->plat && info->plat->exit) | 279 | if (plat && plat->exit) |
178 | info->plat->exit(); | 280 | plat->exit(); |
179 | 281 | ||
180 | kfree(info); | 282 | kfree(info); |
181 | } | 283 | } |