.. | .. |
---|
| 1 | +/* SPDX-License-Identifier: GPL-2.0-or-later */ |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> et al. |
---|
3 | | - * |
---|
4 | | - * This program is free software; you can redistribute it and/or modify |
---|
5 | | - * it under the terms of the GNU General Public License as published by |
---|
6 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
7 | | - * (at your option) any later version. |
---|
8 | | - * |
---|
9 | | - * This program is distributed in the hope that it will be useful, |
---|
10 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
12 | | - * GNU General Public License for more details. |
---|
13 | | - * |
---|
14 | | - * You should have received a copy of the GNU General Public License |
---|
15 | | - * along with this program; if not, write to the Free Software |
---|
16 | | - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
---|
17 | | - * |
---|
18 | 4 | */ |
---|
19 | 5 | |
---|
20 | 6 | #ifndef __MTD_MTD_H__ |
---|
.. | .. |
---|
22 | 8 | |
---|
23 | 9 | #include <linux/types.h> |
---|
24 | 10 | #include <linux/uio.h> |
---|
| 11 | +#include <linux/list.h> |
---|
25 | 12 | #include <linux/notifier.h> |
---|
26 | 13 | #include <linux/device.h> |
---|
27 | 14 | #include <linux/of.h> |
---|
| 15 | +#include <linux/nvmem-provider.h> |
---|
28 | 16 | |
---|
29 | 17 | #include <mtd/mtd-abi.h> |
---|
30 | 18 | |
---|
.. | .. |
---|
202 | 190 | */ |
---|
203 | 191 | struct mtd_debug_info { |
---|
204 | 192 | struct dentry *dfs_dir; |
---|
| 193 | + |
---|
| 194 | + const char *partname; |
---|
| 195 | + const char *partid; |
---|
| 196 | +}; |
---|
| 197 | + |
---|
| 198 | +/** |
---|
| 199 | + * struct mtd_part - MTD partition specific fields |
---|
| 200 | + * |
---|
| 201 | + * @node: list node used to add an MTD partition to the parent partition list |
---|
| 202 | + * @offset: offset of the partition relatively to the parent offset |
---|
| 203 | + * @size: partition size. Should be equal to mtd->size unless |
---|
| 204 | + * MTD_SLC_ON_MLC_EMULATION is set |
---|
| 205 | + * @flags: original flags (before the mtdpart logic decided to tweak them based |
---|
| 206 | + * on flash constraints, like eraseblock/pagesize alignment) |
---|
| 207 | + * |
---|
| 208 | + * This struct is embedded in mtd_info and contains partition-specific |
---|
| 209 | + * properties/fields. |
---|
| 210 | + */ |
---|
| 211 | +struct mtd_part { |
---|
| 212 | + struct list_head node; |
---|
| 213 | + u64 offset; |
---|
| 214 | + u64 size; |
---|
| 215 | + u32 flags; |
---|
| 216 | +}; |
---|
| 217 | + |
---|
| 218 | +/** |
---|
| 219 | + * struct mtd_master - MTD master specific fields |
---|
| 220 | + * |
---|
| 221 | + * @partitions_lock: lock protecting accesses to the partition list. Protects |
---|
| 222 | + * not only the master partition list, but also all |
---|
| 223 | + * sub-partitions. |
---|
| 224 | + * @suspended: et to 1 when the device is suspended, 0 otherwise |
---|
| 225 | + * |
---|
| 226 | + * This struct is embedded in mtd_info and contains master-specific |
---|
| 227 | + * properties/fields. The master is the root MTD device from the MTD partition |
---|
| 228 | + * point of view. |
---|
| 229 | + */ |
---|
| 230 | +struct mtd_master { |
---|
| 231 | + struct mutex partitions_lock; |
---|
| 232 | + unsigned int suspended : 1; |
---|
205 | 233 | }; |
---|
206 | 234 | |
---|
207 | 235 | struct mtd_info { |
---|
.. | .. |
---|
328 | 356 | int (*_get_device) (struct mtd_info *mtd); |
---|
329 | 357 | void (*_put_device) (struct mtd_info *mtd); |
---|
330 | 358 | |
---|
| 359 | + /* |
---|
| 360 | + * flag indicates a panic write, low level drivers can take appropriate |
---|
| 361 | + * action if required to ensure writes go through |
---|
| 362 | + */ |
---|
| 363 | + bool oops_panic_write; |
---|
| 364 | + |
---|
331 | 365 | struct notifier_block reboot_notifier; /* default mode before reboot */ |
---|
332 | 366 | |
---|
333 | 367 | /* ECC status information */ |
---|
.. | .. |
---|
341 | 375 | struct device dev; |
---|
342 | 376 | int usecount; |
---|
343 | 377 | struct mtd_debug_info dbg; |
---|
| 378 | + struct nvmem_device *nvmem; |
---|
| 379 | + |
---|
| 380 | + /* |
---|
| 381 | + * Parent device from the MTD partition point of view. |
---|
| 382 | + * |
---|
| 383 | + * MTD masters do not have any parent, MTD partitions do. The parent |
---|
| 384 | + * MTD device can itself be a partition. |
---|
| 385 | + */ |
---|
| 386 | + struct mtd_info *parent; |
---|
| 387 | + |
---|
| 388 | + /* List of partitions attached to this MTD device */ |
---|
| 389 | + struct list_head partitions; |
---|
| 390 | + |
---|
| 391 | + struct mtd_part part; |
---|
| 392 | + struct mtd_master master; |
---|
344 | 393 | }; |
---|
| 394 | + |
---|
| 395 | +static inline struct mtd_info *mtd_get_master(struct mtd_info *mtd) |
---|
| 396 | +{ |
---|
| 397 | + while (mtd->parent) |
---|
| 398 | + mtd = mtd->parent; |
---|
| 399 | + |
---|
| 400 | + return mtd; |
---|
| 401 | +} |
---|
| 402 | + |
---|
| 403 | +static inline u64 mtd_get_master_ofs(struct mtd_info *mtd, u64 ofs) |
---|
| 404 | +{ |
---|
| 405 | + while (mtd->parent) { |
---|
| 406 | + ofs += mtd->part.offset; |
---|
| 407 | + mtd = mtd->parent; |
---|
| 408 | + } |
---|
| 409 | + |
---|
| 410 | + return ofs; |
---|
| 411 | +} |
---|
| 412 | + |
---|
| 413 | +static inline bool mtd_is_partition(const struct mtd_info *mtd) |
---|
| 414 | +{ |
---|
| 415 | + return mtd->parent; |
---|
| 416 | +} |
---|
| 417 | + |
---|
| 418 | +static inline bool mtd_has_partitions(const struct mtd_info *mtd) |
---|
| 419 | +{ |
---|
| 420 | + return !list_empty(&mtd->partitions); |
---|
| 421 | +} |
---|
345 | 422 | |
---|
346 | 423 | int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, |
---|
347 | 424 | struct mtd_oob_region *oobecc); |
---|
.. | .. |
---|
394 | 471 | static inline int mtd_max_bad_blocks(struct mtd_info *mtd, |
---|
395 | 472 | loff_t ofs, size_t len) |
---|
396 | 473 | { |
---|
397 | | - if (!mtd->_max_bad_blocks) |
---|
| 474 | + struct mtd_info *master = mtd_get_master(mtd); |
---|
| 475 | + |
---|
| 476 | + if (!master->_max_bad_blocks) |
---|
398 | 477 | return -ENOTSUPP; |
---|
399 | 478 | |
---|
400 | 479 | if (mtd->size < (len + ofs) || ofs < 0) |
---|
401 | 480 | return -EINVAL; |
---|
402 | 481 | |
---|
403 | | - return mtd->_max_bad_blocks(mtd, ofs, len); |
---|
| 482 | + return master->_max_bad_blocks(master, mtd_get_master_ofs(mtd, ofs), |
---|
| 483 | + len); |
---|
404 | 484 | } |
---|
405 | 485 | |
---|
406 | 486 | int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit, |
---|
.. | .. |
---|
441 | 521 | |
---|
442 | 522 | static inline void mtd_sync(struct mtd_info *mtd) |
---|
443 | 523 | { |
---|
444 | | - if (mtd->_sync) |
---|
445 | | - mtd->_sync(mtd); |
---|
| 524 | + struct mtd_info *master = mtd_get_master(mtd); |
---|
| 525 | + |
---|
| 526 | + if (master->_sync) |
---|
| 527 | + master->_sync(master); |
---|
446 | 528 | } |
---|
447 | 529 | |
---|
448 | 530 | int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); |
---|
.. | .. |
---|
454 | 536 | |
---|
455 | 537 | static inline int mtd_suspend(struct mtd_info *mtd) |
---|
456 | 538 | { |
---|
457 | | - return mtd->_suspend ? mtd->_suspend(mtd) : 0; |
---|
| 539 | + struct mtd_info *master = mtd_get_master(mtd); |
---|
| 540 | + int ret; |
---|
| 541 | + |
---|
| 542 | + if (master->master.suspended) |
---|
| 543 | + return 0; |
---|
| 544 | + |
---|
| 545 | + ret = master->_suspend ? master->_suspend(master) : 0; |
---|
| 546 | + if (ret) |
---|
| 547 | + return ret; |
---|
| 548 | + |
---|
| 549 | + master->master.suspended = 1; |
---|
| 550 | + return 0; |
---|
458 | 551 | } |
---|
459 | 552 | |
---|
460 | 553 | static inline void mtd_resume(struct mtd_info *mtd) |
---|
461 | 554 | { |
---|
462 | | - if (mtd->_resume) |
---|
463 | | - mtd->_resume(mtd); |
---|
| 555 | + struct mtd_info *master = mtd_get_master(mtd); |
---|
| 556 | + |
---|
| 557 | + if (!master->master.suspended) |
---|
| 558 | + return; |
---|
| 559 | + |
---|
| 560 | + if (master->_resume) |
---|
| 561 | + master->_resume(master); |
---|
| 562 | + |
---|
| 563 | + master->master.suspended = 0; |
---|
464 | 564 | } |
---|
465 | 565 | |
---|
466 | 566 | static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd) |
---|
.. | .. |
---|
523 | 623 | |
---|
524 | 624 | static inline int mtd_wunit_per_eb(struct mtd_info *mtd) |
---|
525 | 625 | { |
---|
526 | | - return mtd->erasesize / mtd->writesize; |
---|
| 626 | + struct mtd_info *master = mtd_get_master(mtd); |
---|
| 627 | + |
---|
| 628 | + return master->erasesize / mtd->writesize; |
---|
527 | 629 | } |
---|
528 | 630 | |
---|
529 | 631 | static inline int mtd_offset_to_wunit(struct mtd_info *mtd, loff_t offs) |
---|
.. | .. |
---|
540 | 642 | |
---|
541 | 643 | static inline int mtd_has_oob(const struct mtd_info *mtd) |
---|
542 | 644 | { |
---|
543 | | - return mtd->_read_oob && mtd->_write_oob; |
---|
| 645 | + struct mtd_info *master = mtd_get_master((struct mtd_info *)mtd); |
---|
| 646 | + |
---|
| 647 | + return master->_read_oob && master->_write_oob; |
---|
544 | 648 | } |
---|
545 | 649 | |
---|
546 | 650 | static inline int mtd_type_is_nand(const struct mtd_info *mtd) |
---|
.. | .. |
---|
550 | 654 | |
---|
551 | 655 | static inline int mtd_can_have_bb(const struct mtd_info *mtd) |
---|
552 | 656 | { |
---|
553 | | - return !!mtd->_block_isbad; |
---|
| 657 | + struct mtd_info *master = mtd_get_master((struct mtd_info *)mtd); |
---|
| 658 | + |
---|
| 659 | + return !!master->_block_isbad; |
---|
554 | 660 | } |
---|
555 | 661 | |
---|
556 | 662 | /* Kernel-side ioctl definitions */ |
---|