doc: replace @return by Return:
[platform/kernel/u-boot.git] / drivers / mtd / mtdcore.c
index ad61406..aa78d41 100644 (file)
@@ -4,7 +4,7 @@
  * drivers and users.
  *
  * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
- * Copyright © 2006      Red Hat UK Limited 
+ * Copyright © 2006      Red Hat UK Limited
  *
  */
 
@@ -26,6 +26,8 @@
 #include <linux/gfp.h>
 #include <linux/slab.h>
 #else
+#include <linux/bitops.h>
+#include <linux/bug.h>
 #include <linux/err.h>
 #include <ubi_uboot.h>
 #endif
@@ -76,8 +78,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 {
@@ -87,14 +87,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;
 }
@@ -120,7 +123,7 @@ void *idr_get_next(struct idr *idp, int *next)
        } else {
                *next = 0;
        }
-       
+
        return ret;
 }
 
@@ -134,6 +137,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++;
@@ -155,6 +159,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);
 
@@ -426,6 +440,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;
@@ -512,6 +528,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) {
@@ -745,13 +768,39 @@ int __get_mtd_device(struct mtd_info *mtd)
 }
 EXPORT_SYMBOL_GPL(__get_mtd_device);
 
+#if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(OF_CONTROL)
+static bool mtd_device_matches_name(struct mtd_info *mtd, const char *name)
+{
+       struct udevice *dev = NULL;
+       bool is_part;
+
+       /*
+        * If the first character of mtd name is '/', try interpreting as OF
+        * path. Otherwise try comparing by mtd->name and mtd->dev->name.
+        */
+       if (*name == '/')
+               device_get_global_by_ofnode(ofnode_path(name), &dev);
+
+       is_part = mtd_is_partition(mtd);
+
+       return (!is_part && dev && mtd->dev == dev) ||
+              !strcmp(name, mtd->name) ||
+              (is_part && mtd->dev && !strcmp(name, mtd->dev->name));
+}
+#else
+static bool mtd_device_matches_name(struct mtd_info *mtd, const char *name)
+{
+       return !strcmp(name, mtd->name);
+}
+#endif
+
 /**
  *     get_mtd_device_nm - obtain a validated handle for an MTD device by
  *     device name
  *     @name: MTD device name to open
  *
- *     This function returns MTD device description structure in case of
- *     success and an error code in case of failure.
+ *     This function returns MTD device description structure in case of
+ *     success and an error code in case of failure.
  */
 struct mtd_info *get_mtd_device_nm(const char *name)
 {
@@ -761,10 +810,19 @@ struct mtd_info *get_mtd_device_nm(const char *name)
        mutex_lock(&mtd_table_mutex);
 
        mtd_for_each_device(other) {
+#ifdef __UBOOT__
+               if (mtd_device_matches_name(other, name)) {
+                       if (mtd)
+                               printf("\nWarning: MTD name \"%s\" is not unique!\n\n",
+                                      name);
+                       mtd = other;
+               }
+#else /* !__UBOOT__ */
                if (!strcmp(name, other->name)) {
                        mtd = other;
                        break;
                }
+#endif /* !__UBOOT__ */
        }
 
        if (!mtd)
@@ -792,7 +850,7 @@ EXPORT_SYMBOL_GPL(get_mtd_device_nm);
  * @param mtd an MTD device
  * @param offset offset in flash
  * @param length image length
- * @return image length including bad blocks in *len_incl_bad and whether or not
+ * Return: image length including bad blocks in *len_incl_bad and whether or not
  *         the length returned was truncated in *truncated
  */
 void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset,
@@ -848,13 +906,6 @@ void __put_mtd_device(struct mtd_info *mtd)
 }
 EXPORT_SYMBOL_GPL(__put_mtd_device);
 
-/*
- * Erase is an asynchronous operation.  Device drivers are supposed
- * to call instr->callback() whenever the operation completes, even
- * if it completes with a failure.
- * Callers are supposed to pass a callback function and wait for it
- * to be called before writing to the block.
- */
 int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
        if (instr->addr > mtd->size || instr->len > mtd->size - instr->addr)
@@ -864,7 +915,6 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
        instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
        if (!instr->len) {
                instr->state = MTD_ERASE_DONE;
-               mtd_erase_callback(instr);
                return 0;
        }
        return mtd->_erase(mtd, instr);
@@ -1010,19 +1060,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)
@@ -1034,12 +1129,26 @@ 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->_write_oob)
-               return -EOPNOTSUPP;
+
        if (!(mtd->flags & MTD_WRITEABLE))
                return -EROFS;
-       return mtd->_write_oob(mtd, to, ops);
+
+       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);
 
@@ -1099,10 +1208,10 @@ int mtd_ooblayout_free(struct mtd_info *mtd, int section,
        if (!mtd || section < 0)
                return -EINVAL;
 
-       if (!mtd->ooblayout || !mtd->ooblayout->free)
+       if (!mtd->ooblayout || !mtd->ooblayout->rfree)
                return -ENOTSUPP;
 
-       return mtd->ooblayout->free(mtd, section, oobfree);
+       return mtd->ooblayout->rfree(mtd, section, oobfree);
 }
 EXPORT_SYMBOL_GPL(mtd_ooblayout_free);