| .. | .. |
|---|
| 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 */ |
|---|