blob: 78710fbc8e7f638cdd52b08286b7a6b1bfbffa71 [file] [log] [blame]
Boris Brezillon18d54e52018-10-19 09:48:54 +02001// SPDX-License-Identifier: GPL-2.0+
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * Normal mappings of chips in physical memory
4 *
5 * Copyright (C) 2003 MontaVista Software Inc.
6 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
7 *
8 * 031022 - [jsun] add run-time configure and partition setup
Boris Brezillon642b1e82018-10-19 09:49:04 +02009 *
10 * Device tree support:
11 * Copyright (C) 2006 MontaVista Software Inc.
12 * Author: Vitaly Wool <vwool@ru.mvista.com>
13 *
14 * Revised to handle newer style flash binding by:
15 * Copyright (C) 2007 David Gibson, IBM Corporation.
Boris Brezillonba32ce92018-10-19 09:49:07 +020016 *
17 * GPIO address extension:
18 * Handle the case where a flash device is mostly addressed using physical
19 * line and supplemented by GPIOs. This way you can hook up say a 8MiB flash
20 * to a 2MiB memory range and use the GPIOs to select a particular range.
21 *
22 * Copyright © 2000 Nicolas Pitre <nico@cam.org>
23 * Copyright © 2005-2009 Analog Devices Inc.
Linus Torvalds1da177e2005-04-16 15:20:36 -070024 */
25
26#include <linux/module.h>
27#include <linux/types.h>
28#include <linux/kernel.h>
29#include <linux/init.h>
30#include <linux/slab.h>
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010031#include <linux/device.h>
32#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/mtd/mtd.h>
34#include <linux/mtd/map.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <linux/mtd/partitions.h>
Adrian Bunk2b9175c2005-11-29 14:49:38 +000036#include <linux/mtd/physmap.h>
Stefan Roesedf66e712008-02-01 15:26:54 +010037#include <linux/mtd/concat.h>
Boris Brezillon642b1e82018-10-19 09:49:04 +020038#include <linux/mtd/cfi_endian.h>
Atsushi Nemoto3136e902008-11-26 10:26:29 +000039#include <linux/io.h>
Boris Brezillon642b1e82018-10-19 09:49:04 +020040#include <linux/of_device.h>
Geert Uytterhoeven0bc448b2019-12-09 14:48:23 +010041#include <linux/pm_runtime.h>
Boris Brezillonba32ce92018-10-19 09:49:07 +020042#include <linux/gpio/consumer.h>
Boris Brezillon642b1e82018-10-19 09:49:04 +020043
Serge Seminb3e79e72020-09-20 14:14:44 +030044#include "physmap-bt1-rom.h"
Boris Brezillon6ca15cf2018-10-19 09:49:05 +020045#include "physmap-gemini.h"
Linus Walleij2aba2f22019-10-21 01:00:42 +020046#include "physmap-ixp4xx.h"
Boris Brezillon6ca15cf2018-10-19 09:49:05 +020047#include "physmap-versatile.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010049struct physmap_flash_info {
Boris Brezillon51b436a2018-10-19 09:48:59 +020050 unsigned int nmaps;
51 struct mtd_info **mtds;
Stefan Roesedf66e712008-02-01 15:26:54 +010052 struct mtd_info *cmtd;
Boris Brezillon51b436a2018-10-19 09:48:59 +020053 struct map_info *maps;
Paul Parsons876fe762012-03-07 14:12:08 +000054 spinlock_t vpp_lock;
55 int vpp_refcnt;
Boris Brezillon642b1e82018-10-19 09:49:04 +020056 const char *probe_type;
57 const char * const *part_types;
58 unsigned int nparts;
59 const struct mtd_partition *parts;
Boris Brezillonba32ce92018-10-19 09:49:07 +020060 struct gpio_descs *gpios;
61 unsigned int gpio_values;
62 unsigned int win_order;
Linus Torvalds1da177e2005-04-16 15:20:36 -070063};
64
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010065static int physmap_flash_remove(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -070066{
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010067 struct physmap_flash_info *info;
68 struct physmap_flash_data *physmap_data;
Uwe Kleine-Königbb823652022-06-03 23:07:57 +020069 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010071 info = platform_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070072
H Hartley Sweeten8ce110a2009-10-20 12:23:33 -040073 if (info->cmtd) {
Uwe Kleine-Königbb823652022-06-03 23:07:57 +020074 WARN_ON(mtd_device_unregister(info->cmtd));
Boris Brezillon1e4f4202018-10-19 09:49:00 +020075
Boris Brezillon00142d62018-10-19 09:48:55 +020076 if (info->cmtd != info->mtds[0])
H Hartley Sweeten8ce110a2009-10-20 12:23:33 -040077 mtd_concat_destroy(info->cmtd);
H Hartley Sweeten8ce110a2009-10-20 12:23:33 -040078 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
Boris Brezillon51b436a2018-10-19 09:48:59 +020080 for (i = 0; i < info->nmaps; i++) {
Boris Brezilloncb946bf2018-10-19 09:49:02 +020081 if (info->mtds[i])
Boris Brezillon00142d62018-10-19 09:48:55 +020082 map_destroy(info->mtds[i]);
Stefan Roesedf66e712008-02-01 15:26:54 +010083 }
Marc Zyngierb7281ca2011-05-18 10:51:48 +010084
Boris Brezillon642b1e82018-10-19 09:49:04 +020085 physmap_data = dev_get_platdata(&dev->dev);
86 if (physmap_data && physmap_data->exit)
Marc Zyngierb7281ca2011-05-18 10:51:48 +010087 physmap_data->exit(dev);
88
Geert Uytterhoeven0bc448b2019-12-09 14:48:23 +010089 pm_runtime_put(&dev->dev);
90 pm_runtime_disable(&dev->dev);
Uwe Kleine-Königbb823652022-06-03 23:07:57 +020091 return 0;
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010092}
93
Marc Zyngier667f3902011-05-18 10:51:55 +010094static void physmap_set_vpp(struct map_info *map, int state)
95{
96 struct platform_device *pdev;
97 struct physmap_flash_data *physmap_data;
Paul Parsons876fe762012-03-07 14:12:08 +000098 struct physmap_flash_info *info;
99 unsigned long flags;
Marc Zyngier667f3902011-05-18 10:51:55 +0100100
101 pdev = (struct platform_device *)map->map_priv_1;
Jingoo Hand20d5a52013-07-30 17:18:06 +0900102 physmap_data = dev_get_platdata(&pdev->dev);
Marc Zyngier667f3902011-05-18 10:51:55 +0100103
Paul Parsons876fe762012-03-07 14:12:08 +0000104 if (!physmap_data->set_vpp)
105 return;
106
107 info = platform_get_drvdata(pdev);
108
109 spin_lock_irqsave(&info->vpp_lock, flags);
110 if (state) {
111 if (++info->vpp_refcnt == 1) /* first nested 'on' */
112 physmap_data->set_vpp(pdev, 1);
113 } else {
114 if (--info->vpp_refcnt == 0) /* last nested 'off' */
115 physmap_data->set_vpp(pdev, 0);
116 }
117 spin_unlock_irqrestore(&info->vpp_lock, flags);
Marc Zyngier667f3902011-05-18 10:51:55 +0100118}
119
Boris Brezillonba32ce92018-10-19 09:49:07 +0200120#if IS_ENABLED(CONFIG_MTD_PHYSMAP_GPIO_ADDR)
121static void physmap_set_addr_gpios(struct physmap_flash_info *info,
122 unsigned long ofs)
123{
124 unsigned int i;
125
126 ofs >>= info->win_order;
127 if (info->gpio_values == ofs)
128 return;
129
130 for (i = 0; i < info->gpios->ndescs; i++) {
131 if ((BIT(i) & ofs) == (BIT(i) & info->gpio_values))
132 continue;
133
134 gpiod_set_value(info->gpios->desc[i], !!(BIT(i) & ofs));
135 }
Chris Packham64d14c62019-03-29 15:13:21 +1300136
137 info->gpio_values = ofs;
Boris Brezillonba32ce92018-10-19 09:49:07 +0200138}
139
140#define win_mask(order) (BIT(order) - 1)
141
142static map_word physmap_addr_gpios_read(struct map_info *map,
143 unsigned long ofs)
144{
145 struct platform_device *pdev;
146 struct physmap_flash_info *info;
147 map_word mw;
148 u16 word;
149
150 pdev = (struct platform_device *)map->map_priv_1;
151 info = platform_get_drvdata(pdev);
152 physmap_set_addr_gpios(info, ofs);
153
154 word = readw(map->virt + (ofs & win_mask(info->win_order)));
155 mw.x[0] = word;
156 return mw;
157}
158
159static void physmap_addr_gpios_copy_from(struct map_info *map, void *buf,
160 unsigned long ofs, ssize_t len)
161{
162 struct platform_device *pdev;
163 struct physmap_flash_info *info;
164
165 pdev = (struct platform_device *)map->map_priv_1;
166 info = platform_get_drvdata(pdev);
167
168 while (len) {
169 unsigned int winofs = ofs & win_mask(info->win_order);
170 unsigned int chunklen = min_t(unsigned int, len,
171 BIT(info->win_order) - winofs);
172
173 physmap_set_addr_gpios(info, ofs);
174 memcpy_fromio(buf, map->virt + winofs, chunklen);
175 len -= chunklen;
176 buf += chunklen;
177 ofs += chunklen;
178 }
179}
180
181static void physmap_addr_gpios_write(struct map_info *map, map_word mw,
182 unsigned long ofs)
183{
184 struct platform_device *pdev;
185 struct physmap_flash_info *info;
186 u16 word;
187
188 pdev = (struct platform_device *)map->map_priv_1;
189 info = platform_get_drvdata(pdev);
190 physmap_set_addr_gpios(info, ofs);
191
192 word = mw.x[0];
193 writew(word, map->virt + (ofs & win_mask(info->win_order)));
194}
195
196static void physmap_addr_gpios_copy_to(struct map_info *map, unsigned long ofs,
197 const void *buf, ssize_t len)
198{
199 struct platform_device *pdev;
200 struct physmap_flash_info *info;
201
202 pdev = (struct platform_device *)map->map_priv_1;
203 info = platform_get_drvdata(pdev);
204
205 while (len) {
206 unsigned int winofs = ofs & win_mask(info->win_order);
207 unsigned int chunklen = min_t(unsigned int, len,
208 BIT(info->win_order) - winofs);
209
210 physmap_set_addr_gpios(info, ofs);
211 memcpy_toio(map->virt + winofs, buf, chunklen);
212 len -= chunklen;
213 buf += chunklen;
214 ofs += chunklen;
215 }
216}
217
218static int physmap_addr_gpios_map_init(struct map_info *map)
219{
220 map->phys = NO_XIP;
221 map->read = physmap_addr_gpios_read;
222 map->copy_from = physmap_addr_gpios_copy_from;
223 map->write = physmap_addr_gpios_write;
224 map->copy_to = physmap_addr_gpios_copy_to;
225
226 return 0;
227}
228#else
229static int physmap_addr_gpios_map_init(struct map_info *map)
230{
231 return -ENOTSUPP;
232}
233#endif
234
Boris Brezillon642b1e82018-10-19 09:49:04 +0200235#if IS_ENABLED(CONFIG_MTD_PHYSMAP_OF)
236static const struct of_device_id of_flash_match[] = {
237 {
238 .compatible = "cfi-flash",
239 .data = "cfi_probe",
240 },
241 {
242 /*
243 * FIXME: JEDEC chips can't be safely and reliably
244 * probed, although the mtd code gets it right in
245 * practice most of the time. We should use the
246 * vendor and device ids specified by the binding to
247 * bypass the heuristic probe code, but the mtd layer
248 * provides, at present, no interface for doing so
249 * :(.
250 */
251 .compatible = "jedec-flash",
252 .data = "jedec_probe",
253 },
254 {
255 .compatible = "mtd-ram",
256 .data = "map_ram",
257 },
258 {
259 .compatible = "mtd-rom",
260 .data = "map_rom",
261 },
262 {
263 .type = "rom",
264 .compatible = "direct-mapped"
265 },
266 { /* sentinel */ },
267};
268MODULE_DEVICE_TABLE(of, of_flash_match);
269
270static const char * const of_default_part_probes[] = {
271 "cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL
272};
273
274static const char * const *of_get_part_probes(struct platform_device *dev)
275{
276 struct device_node *dp = dev->dev.of_node;
277 const char **res;
278 int count;
279
280 count = of_property_count_strings(dp, "linux,part-probe");
281 if (count < 0)
282 return of_default_part_probes;
283
284 res = devm_kcalloc(&dev->dev, count + 1, sizeof(*res), GFP_KERNEL);
285 if (!res)
286 return NULL;
287
288 count = of_property_read_string_array(dp, "linux,part-probe", res,
289 count);
290 if (count < 0)
291 return NULL;
292
293 return res;
294}
295
296static const char *of_select_probe_type(struct platform_device *dev)
297{
298 struct device_node *dp = dev->dev.of_node;
299 const struct of_device_id *match;
300 const char *probe_type;
301
302 match = of_match_device(of_flash_match, &dev->dev);
Zeng Jingxiang8b740c02022-07-27 14:03:02 +0800303 if (!match)
304 return NULL;
305
Boris Brezillon642b1e82018-10-19 09:49:04 +0200306 probe_type = match->data;
307 if (probe_type)
308 return probe_type;
309
310 dev_warn(&dev->dev,
311 "Device tree uses obsolete \"direct-mapped\" flash binding\n");
312
313 of_property_read_string(dp, "probe-type", &probe_type);
314 if (!probe_type)
315 return NULL;
316
317 if (!strcmp(probe_type, "CFI")) {
318 probe_type = "cfi_probe";
319 } else if (!strcmp(probe_type, "JEDEC")) {
320 probe_type = "jedec_probe";
321 } else if (!strcmp(probe_type, "ROM")) {
322 probe_type = "map_rom";
323 } else {
324 dev_warn(&dev->dev,
325 "obsolete_probe: don't know probe type '%s', mapping as rom\n",
326 probe_type);
327 probe_type = "map_rom";
328 }
329
330 return probe_type;
331}
332
333static int physmap_flash_of_init(struct platform_device *dev)
334{
335 struct physmap_flash_info *info = platform_get_drvdata(dev);
336 struct device_node *dp = dev->dev.of_node;
337 const char *mtd_name = NULL;
338 int err, swap = 0;
339 bool map_indirect;
340 unsigned int i;
341 u32 bankwidth;
342
343 if (!dp)
344 return -EINVAL;
345
346 info->probe_type = of_select_probe_type(dev);
347
348 info->part_types = of_get_part_probes(dev);
349 if (!info->part_types)
350 return -ENOMEM;
351
352 of_property_read_string(dp, "linux,mtd-name", &mtd_name);
353
354 map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access");
355
356 err = of_property_read_u32(dp, "bank-width", &bankwidth);
357 if (err) {
358 dev_err(&dev->dev, "Can't get bank width from device tree\n");
359 return err;
360 }
361
362 if (of_property_read_bool(dp, "big-endian"))
363 swap = CFI_BIG_ENDIAN;
364 else if (of_property_read_bool(dp, "little-endian"))
365 swap = CFI_LITTLE_ENDIAN;
366
367 for (i = 0; i < info->nmaps; i++) {
368 info->maps[i].name = mtd_name;
369 info->maps[i].swap = swap;
370 info->maps[i].bankwidth = bankwidth;
371 info->maps[i].device_node = dp;
372
Serge Seminb3e79e72020-09-20 14:14:44 +0300373 err = of_flash_probe_bt1_rom(dev, dp, &info->maps[i]);
374 if (err)
375 return err;
376
Boris Brezillon642b1e82018-10-19 09:49:04 +0200377 err = of_flash_probe_gemini(dev, dp, &info->maps[i]);
378 if (err)
379 return err;
380
Linus Walleij2aba2f22019-10-21 01:00:42 +0200381 err = of_flash_probe_ixp4xx(dev, dp, &info->maps[i]);
382 if (err)
383 return err;
384
Boris Brezillon642b1e82018-10-19 09:49:04 +0200385 err = of_flash_probe_versatile(dev, dp, &info->maps[i]);
386 if (err)
387 return err;
388
389 /*
390 * On some platforms (e.g. MPC5200) a direct 1:1 mapping
391 * may cause problems with JFFS2 usage, as the local bus (LPB)
392 * doesn't support unaligned accesses as implemented in the
393 * JFFS2 code via memcpy(). By setting NO_XIP, the
394 * flash will not be exposed directly to the MTD users
395 * (e.g. JFFS2) any more.
396 */
397 if (map_indirect)
398 info->maps[i].phys = NO_XIP;
399 }
400
401 return 0;
402}
403#else /* IS_ENABLED(CONFIG_MTD_PHYSMAP_OF) */
404#define of_flash_match NULL
405
406static int physmap_flash_of_init(struct platform_device *dev)
407{
408 return -ENOTSUPP;
409}
410#endif /* IS_ENABLED(CONFIG_MTD_PHYSMAP_OF) */
411
Artem Bityutskiyf39cf6c2013-03-12 10:32:52 +0200412static const char * const rom_probe_types[] = {
Ricardo Ribalda Delgado99f732b2018-10-19 09:49:08 +0200413 "cfi_probe", "jedec_probe", "qinfo_probe", "map_rom",
Boris Brezilloncb946bf2018-10-19 09:49:02 +0200414};
Artem Bityutskiyf39cf6c2013-03-12 10:32:52 +0200415
416static const char * const part_probe_types[] = {
Boris Brezilloncb946bf2018-10-19 09:49:02 +0200417 "cmdlinepart", "RedBoot", "afs", NULL
418};
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100419
Boris Brezillon642b1e82018-10-19 09:49:04 +0200420static int physmap_flash_pdata_init(struct platform_device *dev)
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100421{
Boris Brezillon642b1e82018-10-19 09:49:04 +0200422 struct physmap_flash_info *info = platform_get_drvdata(dev);
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100423 struct physmap_flash_data *physmap_data;
Boris Brezillon642b1e82018-10-19 09:49:04 +0200424 unsigned int i;
425 int err;
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100426
Jingoo Hand20d5a52013-07-30 17:18:06 +0900427 physmap_data = dev_get_platdata(&dev->dev);
Boris Brezilloncb946bf2018-10-19 09:49:02 +0200428 if (!physmap_data)
Boris Brezillon642b1e82018-10-19 09:49:04 +0200429 return -EINVAL;
430
431 info->probe_type = physmap_data->probe_type;
432 info->part_types = physmap_data->part_probe_types ? : part_probe_types;
433 info->parts = physmap_data->parts;
434 info->nparts = physmap_data->nr_parts;
435
436 if (physmap_data->init) {
437 err = physmap_data->init(dev);
438 if (err)
439 return err;
440 }
441
442 for (i = 0; i < info->nmaps; i++) {
443 info->maps[i].bankwidth = physmap_data->width;
444 info->maps[i].pfow_base = physmap_data->pfow_base;
445 info->maps[i].set_vpp = physmap_set_vpp;
446 }
447
448 return 0;
449}
450
451static int physmap_flash_probe(struct platform_device *dev)
452{
453 struct physmap_flash_info *info;
Boris Brezillon642b1e82018-10-19 09:49:04 +0200454 int err = 0;
455 int i;
456
457 if (!dev->dev.of_node && !dev_get_platdata(&dev->dev))
458 return -EINVAL;
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100459
Boris Brezilloncb946bf2018-10-19 09:49:02 +0200460 info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL);
Boris Brezillon7abe5302018-10-19 09:49:01 +0200461 if (!info)
462 return -ENOMEM;
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100463
Boris Brezillon51b436a2018-10-19 09:48:59 +0200464 while (platform_get_resource(dev, IORESOURCE_MEM, info->nmaps))
465 info->nmaps++;
466
467 if (!info->nmaps)
468 return -ENODEV;
469
470 info->maps = devm_kzalloc(&dev->dev,
471 sizeof(*info->maps) * info->nmaps,
472 GFP_KERNEL);
473 if (!info->maps)
474 return -ENOMEM;
475
476 info->mtds = devm_kzalloc(&dev->dev,
477 sizeof(*info->mtds) * info->nmaps,
478 GFP_KERNEL);
479 if (!info->mtds)
480 return -ENOMEM;
481
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100482 platform_set_drvdata(dev, info);
483
Boris Brezillonba32ce92018-10-19 09:49:07 +0200484 info->gpios = devm_gpiod_get_array_optional(&dev->dev, "addr",
485 GPIOD_OUT_LOW);
486 if (IS_ERR(info->gpios))
487 return PTR_ERR(info->gpios);
488
489 if (info->gpios && info->nmaps > 1) {
490 dev_err(&dev->dev, "addr-gpios only supported for nmaps == 1\n");
491 return -EINVAL;
492 }
493
Geert Uytterhoeven0bc448b2019-12-09 14:48:23 +0100494 pm_runtime_enable(&dev->dev);
495 pm_runtime_get_sync(&dev->dev);
496
Boris Brezillon642b1e82018-10-19 09:49:04 +0200497 if (dev->dev.of_node)
498 err = physmap_flash_of_init(dev);
499 else
500 err = physmap_flash_pdata_init(dev);
501
Geert Uytterhoeven0bc448b2019-12-09 14:48:23 +0100502 if (err) {
503 pm_runtime_put(&dev->dev);
504 pm_runtime_disable(&dev->dev);
Boris Brezillon642b1e82018-10-19 09:49:04 +0200505 return err;
Geert Uytterhoeven0bc448b2019-12-09 14:48:23 +0100506 }
Boris Brezillon642b1e82018-10-19 09:49:04 +0200507
Boris Brezillon51b436a2018-10-19 09:48:59 +0200508 for (i = 0; i < info->nmaps; i++) {
Boris Brezillonc7f6dc62018-10-19 09:48:56 +0200509 struct resource *res;
Stefan Roesedf66e712008-02-01 15:26:54 +0100510
Yangtao Lie1666cf2023-07-07 12:06:22 +0800511 info->maps[i].virt = devm_platform_get_and_ioremap_resource(dev, i, &res);
Boris Brezillonea5bc542018-10-19 09:48:58 +0200512 if (IS_ERR(info->maps[i].virt)) {
513 err = PTR_ERR(info->maps[i].virt);
Stefan Roesedf66e712008-02-01 15:26:54 +0100514 goto err_out;
515 }
516
Boris Brezillonea5bc542018-10-19 09:48:58 +0200517 dev_notice(&dev->dev, "physmap platform flash device: %pR\n",
518 res);
519
Chris Packham9b210842020-08-24 14:57:44 +1200520 if (!info->maps[i].name)
521 info->maps[i].name = dev_name(&dev->dev);
Boris Brezillon642b1e82018-10-19 09:49:04 +0200522
523 if (!info->maps[i].phys)
524 info->maps[i].phys = res->start;
525
Boris Brezillonba32ce92018-10-19 09:49:07 +0200526 info->win_order = get_bitmask_order(resource_size(res)) - 1;
527 info->maps[i].size = BIT(info->win_order +
528 (info->gpios ?
529 info->gpios->ndescs : 0));
530
Boris Brezillon00142d62018-10-19 09:48:55 +0200531 info->maps[i].map_priv_1 = (unsigned long)dev;
Stefan Roesedf66e712008-02-01 15:26:54 +0100532
Boris Brezillonba32ce92018-10-19 09:49:07 +0200533 if (info->gpios) {
534 err = physmap_addr_gpios_map_init(&info->maps[i]);
535 if (err)
536 goto err_out;
Boris Brezillonba32ce92018-10-19 09:49:07 +0200537 }
Stefan Roesedf66e712008-02-01 15:26:54 +0100538
Linus Walleijb3dd9302018-11-27 21:53:57 +0100539#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
540 /*
541 * Only use the simple_map implementation if map hooks are not
542 * implemented. Since map->read() is mandatory checking for its
543 * presence is enough.
544 */
545 if (!info->maps[i].read)
546 simple_map_init(&info->maps[i]);
547#else
548 simple_map_init(&info->maps[i]);
549#endif
550
Ricardo Ribalda Delgado99f732b2018-10-19 09:49:08 +0200551 if (info->probe_type) {
Boris Brezillon642b1e82018-10-19 09:49:04 +0200552 info->mtds[i] = do_map_probe(info->probe_type,
Boris Brezilloncb946bf2018-10-19 09:49:02 +0200553 &info->maps[i]);
Ricardo Ribalda Delgado99f732b2018-10-19 09:49:08 +0200554 } else {
555 int j;
556
557 for (j = 0; j < ARRAY_SIZE(rom_probe_types); j++) {
558 info->mtds[i] = do_map_probe(rom_probe_types[j],
559 &info->maps[i]);
560 if (info->mtds[i])
561 break;
562 }
Boris Brezilloncb946bf2018-10-19 09:49:02 +0200563 }
Barry Song78ef7fa2010-01-15 15:50:14 +0800564
Boris Brezilloncb946bf2018-10-19 09:49:02 +0200565 if (!info->mtds[i]) {
Stefan Roesedf66e712008-02-01 15:26:54 +0100566 dev_err(&dev->dev, "map_probe failed\n");
567 err = -ENXIO;
568 goto err_out;
Stefan Roesedf66e712008-02-01 15:26:54 +0100569 }
Boris Brezillon00142d62018-10-19 09:48:55 +0200570 info->mtds[i]->dev.parent = &dev->dev;
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100571 }
572
Boris Brezillon51b436a2018-10-19 09:48:59 +0200573 if (info->nmaps == 1) {
Boris Brezillon00142d62018-10-19 09:48:55 +0200574 info->cmtd = info->mtds[0];
Boris Brezillonc7f6dc62018-10-19 09:48:56 +0200575 } else {
Stefan Roesedf66e712008-02-01 15:26:54 +0100576 /*
577 * We detected multiple devices. Concatenate them together.
578 */
Boris Brezillon51b436a2018-10-19 09:48:59 +0200579 info->cmtd = mtd_concat_create(info->mtds, info->nmaps,
580 dev_name(&dev->dev));
Boris Brezilloncb946bf2018-10-19 09:49:02 +0200581 if (!info->cmtd)
Stefan Roesedf66e712008-02-01 15:26:54 +0100582 err = -ENXIO;
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100583 }
Stefan Roesedf66e712008-02-01 15:26:54 +0100584 if (err)
585 goto err_out;
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100586
Paul Parsons876fe762012-03-07 14:12:08 +0000587 spin_lock_init(&info->vpp_lock);
588
Boris Brezillon642b1e82018-10-19 09:49:04 +0200589 mtd_set_of_node(info->cmtd, dev->dev.of_node);
590 err = mtd_device_parse_register(info->cmtd, info->part_types, NULL,
591 info->parts, info->nparts);
Boris Brezillon1e4f4202018-10-19 09:49:00 +0200592 if (err)
593 goto err_out;
594
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100595 return 0;
596
597err_out:
598 physmap_flash_remove(dev);
599 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600}
601
Lennert Buytenhek17c2dae2006-09-21 23:16:48 +0200602#ifdef CONFIG_PM
Lennert Buytenhek17c2dae2006-09-21 23:16:48 +0200603static void physmap_flash_shutdown(struct platform_device *dev)
604{
605 struct physmap_flash_info *info = platform_get_drvdata(dev);
Stefan Roesedf66e712008-02-01 15:26:54 +0100606 int i;
607
Boris Brezillon51b436a2018-10-19 09:48:59 +0200608 for (i = 0; i < info->nmaps && info->mtds[i]; i++)
Boris Brezillon00142d62018-10-19 09:48:55 +0200609 if (mtd_suspend(info->mtds[i]) == 0)
610 mtd_resume(info->mtds[i]);
Lennert Buytenhek17c2dae2006-09-21 23:16:48 +0200611}
akpm@linux-foundation.orgd5476682008-02-03 12:56:03 -0800612#else
akpm@linux-foundation.orgd5476682008-02-03 12:56:03 -0800613#define physmap_flash_shutdown NULL
Lennert Buytenhek17c2dae2006-09-21 23:16:48 +0200614#endif
615
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100616static struct platform_driver physmap_flash_driver = {
617 .probe = physmap_flash_probe,
618 .remove = physmap_flash_remove,
Lennert Buytenhek17c2dae2006-09-21 23:16:48 +0200619 .shutdown = physmap_flash_shutdown,
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100620 .driver = {
621 .name = "physmap-flash",
Boris Brezillon642b1e82018-10-19 09:49:04 +0200622 .of_match_table = of_flash_match,
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100623 },
624};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
Mike Frysingerdcb3e132008-12-01 14:23:40 -0800626#ifdef CONFIG_MTD_PHYSMAP_COMPAT
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100627static struct physmap_flash_data physmap_flash_data = {
628 .width = CONFIG_MTD_PHYSMAP_BANKWIDTH,
629};
630
631static struct resource physmap_flash_resource = {
632 .start = CONFIG_MTD_PHYSMAP_START,
Sascha Hauer6d4f8222006-06-27 14:38:15 +0100633 .end = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1,
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100634 .flags = IORESOURCE_MEM,
635};
636
637static struct platform_device physmap_flash = {
638 .name = "physmap-flash",
639 .id = 0,
640 .dev = {
641 .platform_data = &physmap_flash_data,
642 },
643 .num_resources = 1,
644 .resource = &physmap_flash_resource,
645};
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100646#endif
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100647
648static int __init physmap_init(void)
649{
650 int err;
651
652 err = platform_driver_register(&physmap_flash_driver);
Mike Frysingerdcb3e132008-12-01 14:23:40 -0800653#ifdef CONFIG_MTD_PHYSMAP_COMPAT
H Hartley Sweeten1ca5d2f2010-04-02 17:46:30 -0500654 if (err == 0) {
655 err = platform_device_register(&physmap_flash);
656 if (err)
657 platform_driver_unregister(&physmap_flash_driver);
658 }
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100659#endif
660
661 return err;
662}
663
664static void __exit physmap_exit(void)
665{
Mike Frysingerdcb3e132008-12-01 14:23:40 -0800666#ifdef CONFIG_MTD_PHYSMAP_COMPAT
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100667 platform_device_unregister(&physmap_flash);
668#endif
669 platform_driver_unregister(&physmap_flash_driver);
670}
671
672module_init(physmap_init);
673module_exit(physmap_exit);
674
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675MODULE_LICENSE("GPL");
676MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
Boris Brezillon642b1e82018-10-19 09:49:04 +0200677MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>");
Boris Brezillonba32ce92018-10-19 09:49:07 +0200678MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679MODULE_DESCRIPTION("Generic configurable MTD map driver");
Kay Sievers41d867c2008-04-18 13:44:26 -0700680
681/* legacy platform drivers can't hotplug or coldplg */
Mike Frysingerdcb3e132008-12-01 14:23:40 -0800682#ifndef CONFIG_MTD_PHYSMAP_COMPAT
Kay Sievers41d867c2008-04-18 13:44:26 -0700683/* work with hotplug and coldplug */
684MODULE_ALIAS("platform:physmap-flash");
685#endif