Merge branch '2020-05-18-reduce-size-of-common.h'
[oweals/u-boot.git] / drivers / mtd / mtdcore.c
index 2f2172b98781ccbbb047fc68801b65d53c400f59..1a4dec34d93ebb85c5a169408d966b0943393488 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Core registration and callback routines for MTD
  * drivers and users.
@@ -5,11 +6,11 @@
  * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
  * Copyright © 2006      Red Hat UK Limited 
  *
- * SPDX-License-Identifier:    GPL-2.0+
- *
  */
 
 #ifndef __UBOOT__
+#include <log.h>
+#include <dm/devres.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/ptrace.h>
 #include <linux/gfp.h>
 #include <linux/slab.h>
 #else
-#include <linux/compat.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
 #include <linux/err.h>
 #include <ubi_uboot.h>
 #endif
 
+#include <linux/log2.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 
@@ -77,8 +80,6 @@ static struct class mtd_class = {
        .resume = mtd_cls_resume,
 };
 #else
-struct mtd_info *mtd_table[MAX_MTD_DEVICES];
-
 #define MAX_IDR_ID     64
 
 struct idr_layer {
@@ -88,14 +89,17 @@ struct idr_layer {
 
 struct idr {
        struct idr_layer id[MAX_IDR_ID];
+       bool updated;
 };
 
 #define DEFINE_IDR(name)       struct idr name;
 
 void idr_remove(struct idr *idp, int id)
 {
-       if (idp->id[id].used)
+       if (idp->id[id].used) {
                idp->id[id].used = 0;
+               idp->updated = true;
+       }
 
        return;
 }
@@ -135,6 +139,7 @@ int idr_alloc(struct idr *idp, void *ptr, int start, int end, gfp_t gfp_mask)
                if (idl->used == 0) {
                        idl->used = 1;
                        idl->ptr = ptr;
+                       idp->updated = true;
                        return i;
                }
                i++;
@@ -156,6 +161,16 @@ struct mtd_info *__mtd_next_device(int i)
 }
 EXPORT_SYMBOL_GPL(__mtd_next_device);
 
+bool mtd_dev_list_updated(void)
+{
+       if (mtd_idr.updated) {
+               mtd_idr.updated = false;
+               return true;
+       }
+
+       return false;
+}
+
 #ifndef __UBOOT__
 static LIST_HEAD(mtd_notifiers);
 
@@ -427,6 +442,8 @@ int add_mtd_device(struct mtd_info *mtd)
        mtd->index = i;
        mtd->usecount = 0;
 
+       INIT_LIST_HEAD(&mtd->partitions);
+
        /* default value if not set by driver */
        if (mtd->bitflip_threshold == 0)
                mtd->bitflip_threshold = mtd->ecc_strength;
@@ -513,6 +530,13 @@ int del_mtd_device(struct mtd_info *mtd)
        struct mtd_notifier *not;
 #endif
 
+       ret = del_mtd_partitions(mtd);
+       if (ret) {
+               debug("Failed to delete MTD partitions attached to %s (err %d)\n",
+                     mtd->name, ret);
+               return ret;
+       }
+
        mutex_lock(&mtd_table_mutex);
 
        if (idr_find(&mtd_idr, mtd->index) != mtd) {
@@ -938,7 +962,20 @@ int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
         * representing the maximum number of bitflips that were corrected on
         * any one ecc region (if applicable; zero otherwise).
         */
-       ret_code = mtd->_read(mtd, from, len, retlen, buf);
+       if (mtd->_read) {
+               ret_code = mtd->_read(mtd, from, len, retlen, buf);
+       } else if (mtd->_read_oob) {
+               struct mtd_oob_ops ops = {
+                       .len = len,
+                       .datbuf = buf,
+               };
+
+               ret_code = mtd->_read_oob(mtd, from, &ops);
+               *retlen = ops.retlen;
+       } else {
+               return -ENOTSUPP;
+       }
+
        if (unlikely(ret_code < 0))
                return ret_code;
        if (mtd->ecc_strength == 0)
@@ -953,10 +990,24 @@ int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
        *retlen = 0;
        if (to < 0 || to > mtd->size || len > mtd->size - to)
                return -EINVAL;
-       if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE))
+       if ((!mtd->_write && !mtd->_write_oob) ||
+           !(mtd->flags & MTD_WRITEABLE))
                return -EROFS;
        if (!len)
                return 0;
+
+       if (!mtd->_write) {
+               struct mtd_oob_ops ops = {
+                       .len = len,
+                       .datbuf = (u8 *)buf,
+               };
+               int ret;
+
+               ret = mtd->_write_oob(mtd, to, &ops);
+               *retlen = ops.retlen;
+               return ret;
+       }
+
        return mtd->_write(mtd, to, len, retlen, buf);
 }
 EXPORT_SYMBOL_GPL(mtd_write);
@@ -984,19 +1035,64 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
 }
 EXPORT_SYMBOL_GPL(mtd_panic_write);
 
+static int mtd_check_oob_ops(struct mtd_info *mtd, loff_t offs,
+                            struct mtd_oob_ops *ops)
+{
+       /*
+        * Some users are setting ->datbuf or ->oobbuf to NULL, but are leaving
+        * ->len or ->ooblen uninitialized. Force ->len and ->ooblen to 0 in
+        *  this case.
+        */
+       if (!ops->datbuf)
+               ops->len = 0;
+
+       if (!ops->oobbuf)
+               ops->ooblen = 0;
+
+       if (offs < 0 || offs + ops->len > mtd->size)
+               return -EINVAL;
+
+       if (ops->ooblen) {
+               size_t maxooblen;
+
+               if (ops->ooboffs >= mtd_oobavail(mtd, ops))
+                       return -EINVAL;
+
+               maxooblen = ((size_t)(mtd_div_by_ws(mtd->size, mtd) -
+                                     mtd_div_by_ws(offs, mtd)) *
+                            mtd_oobavail(mtd, ops)) - ops->ooboffs;
+               if (ops->ooblen > maxooblen)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
 int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
 {
        int ret_code;
        ops->retlen = ops->oobretlen = 0;
-       if (!mtd->_read_oob)
+
+       ret_code = mtd_check_oob_ops(mtd, from, ops);
+       if (ret_code)
+               return ret_code;
+
+       /* Check the validity of a potential fallback on mtd->_read */
+       if (!mtd->_read_oob && (!mtd->_read || ops->oobbuf))
                return -EOPNOTSUPP;
+
+       if (mtd->_read_oob)
+               ret_code = mtd->_read_oob(mtd, from, ops);
+       else
+               ret_code = mtd->_read(mtd, from, ops->len, &ops->retlen,
+                                     ops->datbuf);
+
        /*
         * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics
         * similar to mtd->_read(), returning a non-negative integer
         * representing max bitflips. In other cases, mtd->_read_oob() may
         * return -EUCLEAN. In all cases, perform similar logic to mtd_read().
         */
-       ret_code = mtd->_read_oob(mtd, from, ops);
        if (unlikely(ret_code < 0))
                return ret_code;
        if (mtd->ecc_strength == 0)
@@ -1005,6 +1101,392 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
 }
 EXPORT_SYMBOL_GPL(mtd_read_oob);
 
+int mtd_write_oob(struct mtd_info *mtd, loff_t to,
+                               struct mtd_oob_ops *ops)
+{
+       int ret;
+
+       ops->retlen = ops->oobretlen = 0;
+
+       if (!(mtd->flags & MTD_WRITEABLE))
+               return -EROFS;
+
+       ret = mtd_check_oob_ops(mtd, to, ops);
+       if (ret)
+               return ret;
+
+       /* Check the validity of a potential fallback on mtd->_write */
+       if (!mtd->_write_oob && (!mtd->_write || ops->oobbuf))
+               return -EOPNOTSUPP;
+
+       if (mtd->_write_oob)
+               return mtd->_write_oob(mtd, to, ops);
+       else
+               return mtd->_write(mtd, to, ops->len, &ops->retlen,
+                                  ops->datbuf);
+}
+EXPORT_SYMBOL_GPL(mtd_write_oob);
+
+/**
+ * mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section
+ * @mtd: MTD device structure
+ * @section: ECC section. Depending on the layout you may have all the ECC
+ *          bytes stored in a single contiguous section, or one section
+ *          per ECC chunk (and sometime several sections for a single ECC
+ *          ECC chunk)
+ * @oobecc: OOB region struct filled with the appropriate ECC position
+ *         information
+ *
+ * This function returns ECC section information in the OOB area. If you want
+ * to get all the ECC bytes information, then you should call
+ * mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
+                     struct mtd_oob_region *oobecc)
+{
+       memset(oobecc, 0, sizeof(*oobecc));
+
+       if (!mtd || section < 0)
+               return -EINVAL;
+
+       if (!mtd->ooblayout || !mtd->ooblayout->ecc)
+               return -ENOTSUPP;
+
+       return mtd->ooblayout->ecc(mtd, section, oobecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc);
+
+/**
+ * mtd_ooblayout_free - Get the OOB region definition of a specific free
+ *                     section
+ * @mtd: MTD device structure
+ * @section: Free section you are interested in. Depending on the layout
+ *          you may have all the free bytes stored in a single contiguous
+ *          section, or one section per ECC chunk plus an extra section
+ *          for the remaining bytes (or other funky layout).
+ * @oobfree: OOB region struct filled with the appropriate free position
+ *          information
+ *
+ * This function returns free bytes position in the OOB area. If you want
+ * to get all the free bytes information, then you should call
+ * mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_free(struct mtd_info *mtd, int section,
+                      struct mtd_oob_region *oobfree)
+{
+       memset(oobfree, 0, sizeof(*oobfree));
+
+       if (!mtd || section < 0)
+               return -EINVAL;
+
+       if (!mtd->ooblayout || !mtd->ooblayout->rfree)
+               return -ENOTSUPP;
+
+       return mtd->ooblayout->rfree(mtd, section, oobfree);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_free);
+
+/**
+ * mtd_ooblayout_find_region - Find the region attached to a specific byte
+ * @mtd: mtd info structure
+ * @byte: the byte we are searching for
+ * @sectionp: pointer where the section id will be stored
+ * @oobregion: used to retrieve the ECC position
+ * @iter: iterator function. Should be either mtd_ooblayout_free or
+ *       mtd_ooblayout_ecc depending on the region type you're searching for
+ *
+ * This function returns the section id and oobregion information of a
+ * specific byte. For example, say you want to know where the 4th ECC byte is
+ * stored, you'll use:
+ *
+ * mtd_ooblayout_find_region(mtd, 3, &section, &oobregion, mtd_ooblayout_ecc);
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_find_region(struct mtd_info *mtd, int byte,
+                               int *sectionp, struct mtd_oob_region *oobregion,
+                               int (*iter)(struct mtd_info *,
+                                           int section,
+                                           struct mtd_oob_region *oobregion))
+{
+       int pos = 0, ret, section = 0;
+
+       memset(oobregion, 0, sizeof(*oobregion));
+
+       while (1) {
+               ret = iter(mtd, section, oobregion);
+               if (ret)
+                       return ret;
+
+               if (pos + oobregion->length > byte)
+                       break;
+
+               pos += oobregion->length;
+               section++;
+       }
+
+       /*
+        * Adjust region info to make it start at the beginning at the
+        * 'start' ECC byte.
+        */
+       oobregion->offset += byte - pos;
+       oobregion->length -= byte - pos;
+       *sectionp = section;
+
+       return 0;
+}
+
+/**
+ * mtd_ooblayout_find_eccregion - Find the ECC region attached to a specific
+ *                               ECC byte
+ * @mtd: mtd info structure
+ * @eccbyte: the byte we are searching for
+ * @sectionp: pointer where the section id will be stored
+ * @oobregion: OOB region information
+ *
+ * Works like mtd_ooblayout_find_region() except it searches for a specific ECC
+ * byte.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
+                                int *section,
+                                struct mtd_oob_region *oobregion)
+{
+       return mtd_ooblayout_find_region(mtd, eccbyte, section, oobregion,
+                                        mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_find_eccregion);
+
+/**
+ * mtd_ooblayout_get_bytes - Extract OOB bytes from the oob buffer
+ * @mtd: mtd info structure
+ * @buf: destination buffer to store OOB bytes
+ * @oobbuf: OOB buffer
+ * @start: first byte to retrieve
+ * @nbytes: number of bytes to retrieve
+ * @iter: section iterator
+ *
+ * Extract bytes attached to a specific category (ECC or free)
+ * from the OOB buffer and copy them into buf.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf,
+                               const u8 *oobbuf, int start, int nbytes,
+                               int (*iter)(struct mtd_info *,
+                                           int section,
+                                           struct mtd_oob_region *oobregion))
+{
+       struct mtd_oob_region oobregion;
+       int section, ret;
+
+       ret = mtd_ooblayout_find_region(mtd, start, &section,
+                                       &oobregion, iter);
+
+       while (!ret) {
+               int cnt;
+
+               cnt = min_t(int, nbytes, oobregion.length);
+               memcpy(buf, oobbuf + oobregion.offset, cnt);
+               buf += cnt;
+               nbytes -= cnt;
+
+               if (!nbytes)
+                       break;
+
+               ret = iter(mtd, ++section, &oobregion);
+       }
+
+       return ret;
+}
+
+/**
+ * mtd_ooblayout_set_bytes - put OOB bytes into the oob buffer
+ * @mtd: mtd info structure
+ * @buf: source buffer to get OOB bytes from
+ * @oobbuf: OOB buffer
+ * @start: first OOB byte to set
+ * @nbytes: number of OOB bytes to set
+ * @iter: section iterator
+ *
+ * Fill the OOB buffer with data provided in buf. The category (ECC or free)
+ * is selected by passing the appropriate iterator.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf,
+                               u8 *oobbuf, int start, int nbytes,
+                               int (*iter)(struct mtd_info *,
+                                           int section,
+                                           struct mtd_oob_region *oobregion))
+{
+       struct mtd_oob_region oobregion;
+       int section, ret;
+
+       ret = mtd_ooblayout_find_region(mtd, start, &section,
+                                       &oobregion, iter);
+
+       while (!ret) {
+               int cnt;
+
+               cnt = min_t(int, nbytes, oobregion.length);
+               memcpy(oobbuf + oobregion.offset, buf, cnt);
+               buf += cnt;
+               nbytes -= cnt;
+
+               if (!nbytes)
+                       break;
+
+               ret = iter(mtd, ++section, &oobregion);
+       }
+
+       return ret;
+}
+
+/**
+ * mtd_ooblayout_count_bytes - count the number of bytes in a OOB category
+ * @mtd: mtd info structure
+ * @iter: category iterator
+ *
+ * Count the number of bytes in a given category.
+ *
+ * Returns a positive value on success, a negative error code otherwise.
+ */
+static int mtd_ooblayout_count_bytes(struct mtd_info *mtd,
+                               int (*iter)(struct mtd_info *,
+                                           int section,
+                                           struct mtd_oob_region *oobregion))
+{
+       struct mtd_oob_region oobregion;
+       int section = 0, ret, nbytes = 0;
+
+       while (1) {
+               ret = iter(mtd, section++, &oobregion);
+               if (ret) {
+                       if (ret == -ERANGE)
+                               ret = nbytes;
+                       break;
+               }
+
+               nbytes += oobregion.length;
+       }
+
+       return ret;
+}
+
+/**
+ * mtd_ooblayout_get_eccbytes - extract ECC bytes from the oob buffer
+ * @mtd: mtd info structure
+ * @eccbuf: destination buffer to store ECC bytes
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to retrieve
+ * @nbytes: number of ECC bytes to retrieve
+ *
+ * Works like mtd_ooblayout_get_bytes(), except it acts on ECC bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf,
+                              const u8 *oobbuf, int start, int nbytes)
+{
+       return mtd_ooblayout_get_bytes(mtd, eccbuf, oobbuf, start, nbytes,
+                                      mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_get_eccbytes);
+
+/**
+ * mtd_ooblayout_set_eccbytes - set ECC bytes into the oob buffer
+ * @mtd: mtd info structure
+ * @eccbuf: source buffer to get ECC bytes from
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to set
+ * @nbytes: number of ECC bytes to set
+ *
+ * Works like mtd_ooblayout_set_bytes(), except it acts on ECC bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf,
+                              u8 *oobbuf, int start, int nbytes)
+{
+       return mtd_ooblayout_set_bytes(mtd, eccbuf, oobbuf, start, nbytes,
+                                      mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_set_eccbytes);
+
+/**
+ * mtd_ooblayout_get_databytes - extract data bytes from the oob buffer
+ * @mtd: mtd info structure
+ * @databuf: destination buffer to store ECC bytes
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to retrieve
+ * @nbytes: number of ECC bytes to retrieve
+ *
+ * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf,
+                               const u8 *oobbuf, int start, int nbytes)
+{
+       return mtd_ooblayout_get_bytes(mtd, databuf, oobbuf, start, nbytes,
+                                      mtd_ooblayout_free);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes);
+
+/**
+ * mtd_ooblayout_get_eccbytes - set data bytes into the oob buffer
+ * @mtd: mtd info structure
+ * @eccbuf: source buffer to get data bytes from
+ * @oobbuf: OOB buffer
+ * @start: first ECC byte to set
+ * @nbytes: number of ECC bytes to set
+ *
+ * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
+                               u8 *oobbuf, int start, int nbytes)
+{
+       return mtd_ooblayout_set_bytes(mtd, databuf, oobbuf, start, nbytes,
+                                      mtd_ooblayout_free);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_set_databytes);
+
+/**
+ * mtd_ooblayout_count_freebytes - count the number of free bytes in OOB
+ * @mtd: mtd info structure
+ *
+ * Works like mtd_ooblayout_count_bytes(), except it count free bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_count_freebytes(struct mtd_info *mtd)
+{
+       return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_free);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes);
+
+/**
+ * mtd_ooblayout_count_freebytes - count the number of ECC bytes in OOB
+ * @mtd: mtd info structure
+ *
+ * Works like mtd_ooblayout_count_bytes(), except it count ECC bytes.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ */
+int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd)
+{
+       return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_ecc);
+}
+EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes);
+
 /*
  * Method to access the protection register area, present in some flash
  * devices. The user data is one time programmable but the factory data is read