183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2e29c22f5SKyungmin Park /*
3e29c22f5SKyungmin Park * Simple MTD partitioning layer
4e29c22f5SKyungmin Park *
5ff94bc40SHeiko Schocher * Copyright © 2000 Nicolas Pitre <nico@fluxnic.net>
6ff94bc40SHeiko Schocher * Copyright © 2002 Thomas Gleixner <gleixner@linutronix.de>
7ff94bc40SHeiko Schocher * Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org>
8e29c22f5SKyungmin Park *
9e29c22f5SKyungmin Park */
10e29c22f5SKyungmin Park
11ff94bc40SHeiko Schocher #ifndef __UBOOT__
12ff94bc40SHeiko Schocher #include <linux/module.h>
13ff94bc40SHeiko Schocher #include <linux/types.h>
14ff94bc40SHeiko Schocher #include <linux/kernel.h>
15ff94bc40SHeiko Schocher #include <linux/slab.h>
16ff94bc40SHeiko Schocher #include <linux/list.h>
17ff94bc40SHeiko Schocher #include <linux/kmod.h>
18ff94bc40SHeiko Schocher #endif
19ff94bc40SHeiko Schocher
20e29c22f5SKyungmin Park #include <common.h>
21e29c22f5SKyungmin Park #include <malloc.h>
221221ce45SMasahiro Yamada #include <linux/errno.h>
23ff94bc40SHeiko Schocher #include <linux/compat.h>
24ff94bc40SHeiko Schocher #include <ubi_uboot.h>
25e29c22f5SKyungmin Park
26e29c22f5SKyungmin Park #include <linux/mtd/mtd.h>
27e29c22f5SKyungmin Park #include <linux/mtd/partitions.h>
28ff94bc40SHeiko Schocher #include <linux/err.h>
2921cc1fb5SMiquel Raynal #include <linux/sizes.h>
30ff94bc40SHeiko Schocher
31ff94bc40SHeiko Schocher #include "mtdcore.h"
32e29c22f5SKyungmin Park
33ff94bc40SHeiko Schocher #ifndef __UBOOT__
34ff94bc40SHeiko Schocher static DEFINE_MUTEX(mtd_partitions_mutex);
35ff94bc40SHeiko Schocher #else
36ff94bc40SHeiko Schocher DEFINE_MUTEX(mtd_partitions_mutex);
37ff94bc40SHeiko Schocher #endif
38e29c22f5SKyungmin Park
39ff94bc40SHeiko Schocher #ifdef __UBOOT__
40ff94bc40SHeiko Schocher /* from mm/util.c */
41ff94bc40SHeiko Schocher
42ff94bc40SHeiko Schocher /**
43ff94bc40SHeiko Schocher * kstrdup - allocate space for and copy an existing string
44ff94bc40SHeiko Schocher * @s: the string to duplicate
45ff94bc40SHeiko Schocher * @gfp: the GFP mask used in the kmalloc() call when allocating memory
46ff94bc40SHeiko Schocher */
kstrdup(const char * s,gfp_t gfp)47ff94bc40SHeiko Schocher char *kstrdup(const char *s, gfp_t gfp)
48ff94bc40SHeiko Schocher {
49ff94bc40SHeiko Schocher size_t len;
50ff94bc40SHeiko Schocher char *buf;
51ff94bc40SHeiko Schocher
52ff94bc40SHeiko Schocher if (!s)
53ff94bc40SHeiko Schocher return NULL;
54ff94bc40SHeiko Schocher
55ff94bc40SHeiko Schocher len = strlen(s) + 1;
56ff94bc40SHeiko Schocher buf = kmalloc(len, gfp);
57ff94bc40SHeiko Schocher if (buf)
58ff94bc40SHeiko Schocher memcpy(buf, s, len);
59ff94bc40SHeiko Schocher return buf;
60ff94bc40SHeiko Schocher }
61ff94bc40SHeiko Schocher #endif
62ff94bc40SHeiko Schocher
6321cc1fb5SMiquel Raynal #define MTD_SIZE_REMAINING (~0LLU)
6421cc1fb5SMiquel Raynal #define MTD_OFFSET_NOT_SPECIFIED (~0LLU)
6521cc1fb5SMiquel Raynal
mtd_partitions_used(struct mtd_info * master)66*4a5594faSBoris Brezillon bool mtd_partitions_used(struct mtd_info *master)
67*4a5594faSBoris Brezillon {
68*4a5594faSBoris Brezillon struct mtd_info *slave;
69*4a5594faSBoris Brezillon
70*4a5594faSBoris Brezillon list_for_each_entry(slave, &master->partitions, node) {
71*4a5594faSBoris Brezillon if (slave->usecount)
72*4a5594faSBoris Brezillon return true;
73*4a5594faSBoris Brezillon }
74*4a5594faSBoris Brezillon
75*4a5594faSBoris Brezillon return false;
76*4a5594faSBoris Brezillon }
77*4a5594faSBoris Brezillon
7821cc1fb5SMiquel Raynal /**
7921cc1fb5SMiquel Raynal * mtd_parse_partition - Parse @mtdparts partition definition, fill @partition
8021cc1fb5SMiquel Raynal * with it and update the @mtdparts string pointer.
8121cc1fb5SMiquel Raynal *
8221cc1fb5SMiquel Raynal * The partition name is allocated and must be freed by the caller.
8321cc1fb5SMiquel Raynal *
8421cc1fb5SMiquel Raynal * This function is widely inspired from part_parse (mtdparts.c).
8521cc1fb5SMiquel Raynal *
8621cc1fb5SMiquel Raynal * @mtdparts: String describing the partition with mtdparts command syntax
8721cc1fb5SMiquel Raynal * @partition: MTD partition structure to fill
8821cc1fb5SMiquel Raynal *
8921cc1fb5SMiquel Raynal * @return 0 on success, an error otherwise.
9021cc1fb5SMiquel Raynal */
mtd_parse_partition(const char ** _mtdparts,struct mtd_partition * partition)9121cc1fb5SMiquel Raynal static int mtd_parse_partition(const char **_mtdparts,
9221cc1fb5SMiquel Raynal struct mtd_partition *partition)
9321cc1fb5SMiquel Raynal {
9421cc1fb5SMiquel Raynal const char *mtdparts = *_mtdparts;
9521cc1fb5SMiquel Raynal const char *name = NULL;
9621cc1fb5SMiquel Raynal int name_len;
9721cc1fb5SMiquel Raynal char *buf;
9821cc1fb5SMiquel Raynal
9921cc1fb5SMiquel Raynal /* Ensure the partition structure is empty */
10021cc1fb5SMiquel Raynal memset(partition, 0, sizeof(struct mtd_partition));
10121cc1fb5SMiquel Raynal
10221cc1fb5SMiquel Raynal /* Fetch the partition size */
10321cc1fb5SMiquel Raynal if (*mtdparts == '-') {
10421cc1fb5SMiquel Raynal /* Assign all remaining space to this partition */
10521cc1fb5SMiquel Raynal partition->size = MTD_SIZE_REMAINING;
10621cc1fb5SMiquel Raynal mtdparts++;
10721cc1fb5SMiquel Raynal } else {
10821cc1fb5SMiquel Raynal partition->size = ustrtoull(mtdparts, (char **)&mtdparts, 0);
10921cc1fb5SMiquel Raynal if (partition->size < SZ_4K) {
11021cc1fb5SMiquel Raynal printf("Minimum partition size 4kiB, %lldB requested\n",
11121cc1fb5SMiquel Raynal partition->size);
11221cc1fb5SMiquel Raynal return -EINVAL;
11321cc1fb5SMiquel Raynal }
11421cc1fb5SMiquel Raynal }
11521cc1fb5SMiquel Raynal
11621cc1fb5SMiquel Raynal /* Check for the offset */
11721cc1fb5SMiquel Raynal partition->offset = MTD_OFFSET_NOT_SPECIFIED;
11821cc1fb5SMiquel Raynal if (*mtdparts == '@') {
11921cc1fb5SMiquel Raynal mtdparts++;
12021cc1fb5SMiquel Raynal partition->offset = ustrtoull(mtdparts, (char **)&mtdparts, 0);
12121cc1fb5SMiquel Raynal }
12221cc1fb5SMiquel Raynal
12321cc1fb5SMiquel Raynal /* Now look for the name */
12421cc1fb5SMiquel Raynal if (*mtdparts == '(') {
12521cc1fb5SMiquel Raynal name = ++mtdparts;
12621cc1fb5SMiquel Raynal mtdparts = strchr(name, ')');
12721cc1fb5SMiquel Raynal if (!mtdparts) {
12821cc1fb5SMiquel Raynal printf("No closing ')' found in partition name\n");
12921cc1fb5SMiquel Raynal return -EINVAL;
13021cc1fb5SMiquel Raynal }
13121cc1fb5SMiquel Raynal name_len = mtdparts - name + 1;
13221cc1fb5SMiquel Raynal if ((name_len - 1) == 0) {
13321cc1fb5SMiquel Raynal printf("Empty partition name\n");
13421cc1fb5SMiquel Raynal return -EINVAL;
13521cc1fb5SMiquel Raynal }
13621cc1fb5SMiquel Raynal mtdparts++;
13721cc1fb5SMiquel Raynal } else {
13821cc1fb5SMiquel Raynal /* Name will be of the form size@offset */
13921cc1fb5SMiquel Raynal name_len = 22;
14021cc1fb5SMiquel Raynal }
14121cc1fb5SMiquel Raynal
14221cc1fb5SMiquel Raynal /* Check if the partition is read-only */
14321cc1fb5SMiquel Raynal if (strncmp(mtdparts, "ro", 2) == 0) {
14421cc1fb5SMiquel Raynal partition->mask_flags |= MTD_WRITEABLE;
14521cc1fb5SMiquel Raynal mtdparts += 2;
14621cc1fb5SMiquel Raynal }
14721cc1fb5SMiquel Raynal
14821cc1fb5SMiquel Raynal /* Check for a potential next partition definition */
14921cc1fb5SMiquel Raynal if (*mtdparts == ',') {
15021cc1fb5SMiquel Raynal if (partition->size == MTD_SIZE_REMAINING) {
15121cc1fb5SMiquel Raynal printf("No partitions allowed after a fill-up\n");
15221cc1fb5SMiquel Raynal return -EINVAL;
15321cc1fb5SMiquel Raynal }
15421cc1fb5SMiquel Raynal ++mtdparts;
15521cc1fb5SMiquel Raynal } else if ((*mtdparts == ';') || (*mtdparts == '\0')) {
15621cc1fb5SMiquel Raynal /* NOP */
15721cc1fb5SMiquel Raynal } else {
15821cc1fb5SMiquel Raynal printf("Unexpected character '%c' in mtdparts\n", *mtdparts);
15921cc1fb5SMiquel Raynal return -EINVAL;
16021cc1fb5SMiquel Raynal }
16121cc1fb5SMiquel Raynal
16221cc1fb5SMiquel Raynal /*
16321cc1fb5SMiquel Raynal * Allocate a buffer for the name and either copy the provided name or
16421cc1fb5SMiquel Raynal * auto-generate it with the form 'size@offset'.
16521cc1fb5SMiquel Raynal */
16621cc1fb5SMiquel Raynal buf = malloc(name_len);
16721cc1fb5SMiquel Raynal if (!buf)
16821cc1fb5SMiquel Raynal return -ENOMEM;
16921cc1fb5SMiquel Raynal
17021cc1fb5SMiquel Raynal if (name)
17121cc1fb5SMiquel Raynal strncpy(buf, name, name_len - 1);
17221cc1fb5SMiquel Raynal else
17321cc1fb5SMiquel Raynal snprintf(buf, name_len, "0x%08llx@0x%08llx",
17421cc1fb5SMiquel Raynal partition->size, partition->offset);
17521cc1fb5SMiquel Raynal
17621cc1fb5SMiquel Raynal buf[name_len - 1] = '\0';
17721cc1fb5SMiquel Raynal partition->name = buf;
17821cc1fb5SMiquel Raynal
17921cc1fb5SMiquel Raynal *_mtdparts = mtdparts;
18021cc1fb5SMiquel Raynal
18121cc1fb5SMiquel Raynal return 0;
18221cc1fb5SMiquel Raynal }
18321cc1fb5SMiquel Raynal
18421cc1fb5SMiquel Raynal /**
18521cc1fb5SMiquel Raynal * mtd_parse_partitions - Create a partition array from an mtdparts definition
18621cc1fb5SMiquel Raynal *
18721cc1fb5SMiquel Raynal * Stateless function that takes a @parent MTD device, a string @_mtdparts
18821cc1fb5SMiquel Raynal * describing the partitions (with the "mtdparts" command syntax) and creates
18921cc1fb5SMiquel Raynal * the corresponding MTD partition structure array @_parts. Both the name and
19021cc1fb5SMiquel Raynal * the structure partition itself must be freed freed, the caller may use
19121cc1fb5SMiquel Raynal * @mtd_free_parsed_partitions() for this purpose.
19221cc1fb5SMiquel Raynal *
19321cc1fb5SMiquel Raynal * @parent: MTD device which contains the partitions
19421cc1fb5SMiquel Raynal * @_mtdparts: Pointer to a string describing the partitions with "mtdparts"
19521cc1fb5SMiquel Raynal * command syntax.
19621cc1fb5SMiquel Raynal * @_parts: Allocated array containing the partitions, must be freed by the
19721cc1fb5SMiquel Raynal * caller.
19821cc1fb5SMiquel Raynal * @_nparts: Size of @_parts array.
19921cc1fb5SMiquel Raynal *
20021cc1fb5SMiquel Raynal * @return 0 on success, an error otherwise.
20121cc1fb5SMiquel Raynal */
mtd_parse_partitions(struct mtd_info * parent,const char ** _mtdparts,struct mtd_partition ** _parts,int * _nparts)20221cc1fb5SMiquel Raynal int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts,
20321cc1fb5SMiquel Raynal struct mtd_partition **_parts, int *_nparts)
20421cc1fb5SMiquel Raynal {
20521cc1fb5SMiquel Raynal struct mtd_partition partition = {}, *parts;
20621cc1fb5SMiquel Raynal const char *mtdparts = *_mtdparts;
20721cc1fb5SMiquel Raynal int cur_off = 0, cur_sz = 0;
20821cc1fb5SMiquel Raynal int nparts = 0;
20921cc1fb5SMiquel Raynal int ret, idx;
21021cc1fb5SMiquel Raynal u64 sz;
21121cc1fb5SMiquel Raynal
21221cc1fb5SMiquel Raynal /* First, iterate over the partitions until we know their number */
21321cc1fb5SMiquel Raynal while (mtdparts[0] != '\0' && mtdparts[0] != ';') {
21421cc1fb5SMiquel Raynal ret = mtd_parse_partition(&mtdparts, &partition);
21521cc1fb5SMiquel Raynal if (ret)
21621cc1fb5SMiquel Raynal return ret;
21721cc1fb5SMiquel Raynal
21821cc1fb5SMiquel Raynal free((char *)partition.name);
21921cc1fb5SMiquel Raynal nparts++;
22021cc1fb5SMiquel Raynal }
22121cc1fb5SMiquel Raynal
22221cc1fb5SMiquel Raynal /* Allocate an array of partitions to give back to the caller */
22321cc1fb5SMiquel Raynal parts = malloc(sizeof(*parts) * nparts);
22421cc1fb5SMiquel Raynal if (!parts) {
22521cc1fb5SMiquel Raynal printf("Not enough space to save partitions meta-data\n");
22621cc1fb5SMiquel Raynal return -ENOMEM;
22721cc1fb5SMiquel Raynal }
22821cc1fb5SMiquel Raynal
22921cc1fb5SMiquel Raynal /* Iterate again over each partition to save the data in our array */
23021cc1fb5SMiquel Raynal for (idx = 0; idx < nparts; idx++) {
23121cc1fb5SMiquel Raynal ret = mtd_parse_partition(_mtdparts, &parts[idx]);
23221cc1fb5SMiquel Raynal if (ret)
23321cc1fb5SMiquel Raynal return ret;
23421cc1fb5SMiquel Raynal
23521cc1fb5SMiquel Raynal if (parts[idx].size == MTD_SIZE_REMAINING)
23621cc1fb5SMiquel Raynal parts[idx].size = parent->size - cur_sz;
23721cc1fb5SMiquel Raynal cur_sz += parts[idx].size;
23821cc1fb5SMiquel Raynal
23921cc1fb5SMiquel Raynal sz = parts[idx].size;
24021cc1fb5SMiquel Raynal if (sz < parent->writesize || do_div(sz, parent->writesize)) {
24121cc1fb5SMiquel Raynal printf("Partition size must be a multiple of %d\n",
24221cc1fb5SMiquel Raynal parent->writesize);
24321cc1fb5SMiquel Raynal return -EINVAL;
24421cc1fb5SMiquel Raynal }
24521cc1fb5SMiquel Raynal
24621cc1fb5SMiquel Raynal if (parts[idx].offset == MTD_OFFSET_NOT_SPECIFIED)
24721cc1fb5SMiquel Raynal parts[idx].offset = cur_off;
24821cc1fb5SMiquel Raynal cur_off += parts[idx].size;
24921cc1fb5SMiquel Raynal
25021cc1fb5SMiquel Raynal parts[idx].ecclayout = parent->ecclayout;
25121cc1fb5SMiquel Raynal }
25221cc1fb5SMiquel Raynal
25321cc1fb5SMiquel Raynal /* Offset by one mtdparts to point to the next device if any */
25421cc1fb5SMiquel Raynal if (*_mtdparts[0] == ';')
25521cc1fb5SMiquel Raynal (*_mtdparts)++;
25621cc1fb5SMiquel Raynal
25721cc1fb5SMiquel Raynal *_parts = parts;
25821cc1fb5SMiquel Raynal *_nparts = nparts;
25921cc1fb5SMiquel Raynal
26021cc1fb5SMiquel Raynal return 0;
26121cc1fb5SMiquel Raynal }
26221cc1fb5SMiquel Raynal
26321cc1fb5SMiquel Raynal /**
26421cc1fb5SMiquel Raynal * mtd_free_parsed_partitions - Free dynamically allocated partitions
26521cc1fb5SMiquel Raynal *
26621cc1fb5SMiquel Raynal * Each successful call to @mtd_parse_partitions must be followed by a call to
26721cc1fb5SMiquel Raynal * @mtd_free_parsed_partitions to free any allocated array during the parsing
26821cc1fb5SMiquel Raynal * process.
26921cc1fb5SMiquel Raynal *
27021cc1fb5SMiquel Raynal * @parts: Array containing the partitions that will be freed.
27121cc1fb5SMiquel Raynal * @nparts: Size of @parts array.
27221cc1fb5SMiquel Raynal */
mtd_free_parsed_partitions(struct mtd_partition * parts,unsigned int nparts)27321cc1fb5SMiquel Raynal void mtd_free_parsed_partitions(struct mtd_partition *parts,
27421cc1fb5SMiquel Raynal unsigned int nparts)
27521cc1fb5SMiquel Raynal {
27621cc1fb5SMiquel Raynal int i;
27721cc1fb5SMiquel Raynal
27821cc1fb5SMiquel Raynal for (i = 0; i < nparts; i++)
27921cc1fb5SMiquel Raynal free((char *)parts[i].name);
28021cc1fb5SMiquel Raynal
28121cc1fb5SMiquel Raynal free(parts);
28221cc1fb5SMiquel Raynal }
28321cc1fb5SMiquel Raynal
284e29c22f5SKyungmin Park /*
285e29c22f5SKyungmin Park * MTD methods which simply translate the effective address and pass through
286e29c22f5SKyungmin Park * to the _real_ device.
287e29c22f5SKyungmin Park */
288e29c22f5SKyungmin Park
part_read(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)289e29c22f5SKyungmin Park static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
290e29c22f5SKyungmin Park size_t *retlen, u_char *buf)
291e29c22f5SKyungmin Park {
2928d2effeaSStefan Roese struct mtd_ecc_stats stats;
293e29c22f5SKyungmin Park int res;
294e29c22f5SKyungmin Park
2952a74930dSMiquel Raynal stats = mtd->parent->ecc_stats;
2962a74930dSMiquel Raynal res = mtd->parent->_read(mtd->parent, from + mtd->offset, len,
297ff94bc40SHeiko Schocher retlen, buf);
29840462e54SPaul Burton if (unlikely(mtd_is_eccerr(res)))
29940462e54SPaul Burton mtd->ecc_stats.failed +=
3002a74930dSMiquel Raynal mtd->parent->ecc_stats.failed - stats.failed;
30140462e54SPaul Burton else
30240462e54SPaul Burton mtd->ecc_stats.corrected +=
3032a74930dSMiquel Raynal mtd->parent->ecc_stats.corrected - stats.corrected;
304e29c22f5SKyungmin Park return res;
305e29c22f5SKyungmin Park }
306e29c22f5SKyungmin Park
307ff94bc40SHeiko Schocher #ifndef __UBOOT__
part_point(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,void ** virt,resource_size_t * phys)308ff94bc40SHeiko Schocher static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
309ff94bc40SHeiko Schocher size_t *retlen, void **virt, resource_size_t *phys)
310ff94bc40SHeiko Schocher {
3112a74930dSMiquel Raynal return mtd->parent->_point(mtd->parent, from + mtd->offset, len,
312ff94bc40SHeiko Schocher retlen, virt, phys);
313ff94bc40SHeiko Schocher }
314ff94bc40SHeiko Schocher
part_unpoint(struct mtd_info * mtd,loff_t from,size_t len)315ff94bc40SHeiko Schocher static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
316ff94bc40SHeiko Schocher {
3172a74930dSMiquel Raynal return mtd->parent->_unpoint(mtd->parent, from + mtd->offset, len);
318ff94bc40SHeiko Schocher }
319ff94bc40SHeiko Schocher #endif
320ff94bc40SHeiko Schocher
part_get_unmapped_area(struct mtd_info * mtd,unsigned long len,unsigned long offset,unsigned long flags)321ff94bc40SHeiko Schocher static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
322ff94bc40SHeiko Schocher unsigned long len,
323ff94bc40SHeiko Schocher unsigned long offset,
324ff94bc40SHeiko Schocher unsigned long flags)
325ff94bc40SHeiko Schocher {
3262a74930dSMiquel Raynal offset += mtd->offset;
3272a74930dSMiquel Raynal return mtd->parent->_get_unmapped_area(mtd->parent, len, offset, flags);
328ff94bc40SHeiko Schocher }
329ff94bc40SHeiko Schocher
part_read_oob(struct mtd_info * mtd,loff_t from,struct mtd_oob_ops * ops)330e29c22f5SKyungmin Park static int part_read_oob(struct mtd_info *mtd, loff_t from,
331e29c22f5SKyungmin Park struct mtd_oob_ops *ops)
332e29c22f5SKyungmin Park {
333e29c22f5SKyungmin Park int res;
334e29c22f5SKyungmin Park
335e29c22f5SKyungmin Park if (from >= mtd->size)
336e29c22f5SKyungmin Park return -EINVAL;
337e29c22f5SKyungmin Park if (ops->datbuf && from + ops->len > mtd->size)
338e29c22f5SKyungmin Park return -EINVAL;
339e29c22f5SKyungmin Park
340ff94bc40SHeiko Schocher /*
341ff94bc40SHeiko Schocher * If OOB is also requested, make sure that we do not read past the end
342ff94bc40SHeiko Schocher * of this partition.
343ff94bc40SHeiko Schocher */
344ff94bc40SHeiko Schocher if (ops->oobbuf) {
345ff94bc40SHeiko Schocher size_t len, pages;
346ff94bc40SHeiko Schocher
347ff94bc40SHeiko Schocher if (ops->mode == MTD_OPS_AUTO_OOB)
348ff94bc40SHeiko Schocher len = mtd->oobavail;
349ff94bc40SHeiko Schocher else
350ff94bc40SHeiko Schocher len = mtd->oobsize;
351ff94bc40SHeiko Schocher pages = mtd_div_by_ws(mtd->size, mtd);
352ff94bc40SHeiko Schocher pages -= mtd_div_by_ws(from, mtd);
353ff94bc40SHeiko Schocher if (ops->ooboffs + ops->ooblen > pages * len)
354ff94bc40SHeiko Schocher return -EINVAL;
355ff94bc40SHeiko Schocher }
356ff94bc40SHeiko Schocher
3572a74930dSMiquel Raynal res = mtd->parent->_read_oob(mtd->parent, from + mtd->offset, ops);
358e29c22f5SKyungmin Park if (unlikely(res)) {
359dfe64e2cSSergey Lapin if (mtd_is_bitflip(res))
360e29c22f5SKyungmin Park mtd->ecc_stats.corrected++;
361dfe64e2cSSergey Lapin if (mtd_is_eccerr(res))
362e29c22f5SKyungmin Park mtd->ecc_stats.failed++;
363e29c22f5SKyungmin Park }
364e29c22f5SKyungmin Park return res;
365e29c22f5SKyungmin Park }
366e29c22f5SKyungmin Park
part_read_user_prot_reg(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)3678d2effeaSStefan Roese static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
3688d2effeaSStefan Roese size_t len, size_t *retlen, u_char *buf)
369e29c22f5SKyungmin Park {
3702a74930dSMiquel Raynal return mtd->parent->_read_user_prot_reg(mtd->parent, from, len,
371ff94bc40SHeiko Schocher retlen, buf);
372e29c22f5SKyungmin Park }
373e29c22f5SKyungmin Park
part_get_user_prot_info(struct mtd_info * mtd,size_t len,size_t * retlen,struct otp_info * buf)3744e67c571SHeiko Schocher static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
3754e67c571SHeiko Schocher size_t *retlen, struct otp_info *buf)
376e29c22f5SKyungmin Park {
3772a74930dSMiquel Raynal return mtd->parent->_get_user_prot_info(mtd->parent, len, retlen,
3784e67c571SHeiko Schocher buf);
379e29c22f5SKyungmin Park }
380e29c22f5SKyungmin Park
part_read_fact_prot_reg(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)3818d2effeaSStefan Roese static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
3828d2effeaSStefan Roese size_t len, size_t *retlen, u_char *buf)
383e29c22f5SKyungmin Park {
3842a74930dSMiquel Raynal return mtd->parent->_read_fact_prot_reg(mtd->parent, from, len,
385ff94bc40SHeiko Schocher retlen, buf);
386e29c22f5SKyungmin Park }
387e29c22f5SKyungmin Park
part_get_fact_prot_info(struct mtd_info * mtd,size_t len,size_t * retlen,struct otp_info * buf)3884e67c571SHeiko Schocher static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
3894e67c571SHeiko Schocher size_t *retlen, struct otp_info *buf)
390e29c22f5SKyungmin Park {
3912a74930dSMiquel Raynal return mtd->parent->_get_fact_prot_info(mtd->parent, len, retlen,
3924e67c571SHeiko Schocher buf);
393e29c22f5SKyungmin Park }
394e29c22f5SKyungmin Park
part_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u_char * buf)395e29c22f5SKyungmin Park static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
396e29c22f5SKyungmin Park size_t *retlen, const u_char *buf)
397e29c22f5SKyungmin Park {
3982a74930dSMiquel Raynal return mtd->parent->_write(mtd->parent, to + mtd->offset, len,
399ff94bc40SHeiko Schocher retlen, buf);
400ff94bc40SHeiko Schocher }
401ff94bc40SHeiko Schocher
part_panic_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u_char * buf)402ff94bc40SHeiko Schocher static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
403ff94bc40SHeiko Schocher size_t *retlen, const u_char *buf)
404ff94bc40SHeiko Schocher {
4052a74930dSMiquel Raynal return mtd->parent->_panic_write(mtd->parent, to + mtd->offset, len,
406ff94bc40SHeiko Schocher retlen, buf);
407e29c22f5SKyungmin Park }
408e29c22f5SKyungmin Park
part_write_oob(struct mtd_info * mtd,loff_t to,struct mtd_oob_ops * ops)409e29c22f5SKyungmin Park static int part_write_oob(struct mtd_info *mtd, loff_t to,
410e29c22f5SKyungmin Park struct mtd_oob_ops *ops)
411e29c22f5SKyungmin Park {
412e29c22f5SKyungmin Park if (to >= mtd->size)
413e29c22f5SKyungmin Park return -EINVAL;
414e29c22f5SKyungmin Park if (ops->datbuf && to + ops->len > mtd->size)
415e29c22f5SKyungmin Park return -EINVAL;
4162a74930dSMiquel Raynal return mtd->parent->_write_oob(mtd->parent, to + mtd->offset, ops);
417e29c22f5SKyungmin Park }
418e29c22f5SKyungmin Park
part_write_user_prot_reg(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)4198d2effeaSStefan Roese static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
4208d2effeaSStefan Roese size_t len, size_t *retlen, u_char *buf)
421e29c22f5SKyungmin Park {
4222a74930dSMiquel Raynal return mtd->parent->_write_user_prot_reg(mtd->parent, from, len,
423ff94bc40SHeiko Schocher retlen, buf);
424e29c22f5SKyungmin Park }
425e29c22f5SKyungmin Park
part_lock_user_prot_reg(struct mtd_info * mtd,loff_t from,size_t len)4268d2effeaSStefan Roese static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
4278d2effeaSStefan Roese size_t len)
428e29c22f5SKyungmin Park {
4292a74930dSMiquel Raynal return mtd->parent->_lock_user_prot_reg(mtd->parent, from, len);
430e29c22f5SKyungmin Park }
431e29c22f5SKyungmin Park
432ff94bc40SHeiko Schocher #ifndef __UBOOT__
part_writev(struct mtd_info * mtd,const struct kvec * vecs,unsigned long count,loff_t to,size_t * retlen)433ff94bc40SHeiko Schocher static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
434ff94bc40SHeiko Schocher unsigned long count, loff_t to, size_t *retlen)
435ff94bc40SHeiko Schocher {
4362a74930dSMiquel Raynal return mtd->parent->_writev(mtd->parent, vecs, count,
4372a74930dSMiquel Raynal to + mtd->offset, retlen);
438ff94bc40SHeiko Schocher }
439ff94bc40SHeiko Schocher #endif
440ff94bc40SHeiko Schocher
part_erase(struct mtd_info * mtd,struct erase_info * instr)441e29c22f5SKyungmin Park static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
442e29c22f5SKyungmin Park {
443e29c22f5SKyungmin Park int ret;
444dfe64e2cSSergey Lapin
4452a74930dSMiquel Raynal instr->addr += mtd->offset;
4462a74930dSMiquel Raynal ret = mtd->parent->_erase(mtd->parent, instr);
447e29c22f5SKyungmin Park if (ret) {
4488d2effeaSStefan Roese if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
4492a74930dSMiquel Raynal instr->fail_addr -= mtd->offset;
4502a74930dSMiquel Raynal instr->addr -= mtd->offset;
451e29c22f5SKyungmin Park }
452e29c22f5SKyungmin Park return ret;
453e29c22f5SKyungmin Park }
454e29c22f5SKyungmin Park
mtd_erase_callback(struct erase_info * instr)455e29c22f5SKyungmin Park void mtd_erase_callback(struct erase_info *instr)
456e29c22f5SKyungmin Park {
457dfe64e2cSSergey Lapin if (instr->mtd->_erase == part_erase) {
4588d2effeaSStefan Roese if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
4592a74930dSMiquel Raynal instr->fail_addr -= instr->mtd->offset;
4602a74930dSMiquel Raynal instr->addr -= instr->mtd->offset;
461e29c22f5SKyungmin Park }
462e29c22f5SKyungmin Park if (instr->callback)
463e29c22f5SKyungmin Park instr->callback(instr);
464e29c22f5SKyungmin Park }
465ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_erase_callback);
466e29c22f5SKyungmin Park
part_lock(struct mtd_info * mtd,loff_t ofs,uint64_t len)4678d2effeaSStefan Roese static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
468e29c22f5SKyungmin Park {
4692a74930dSMiquel Raynal return mtd->parent->_lock(mtd->parent, ofs + mtd->offset, len);
470e29c22f5SKyungmin Park }
471e29c22f5SKyungmin Park
part_unlock(struct mtd_info * mtd,loff_t ofs,uint64_t len)4728d2effeaSStefan Roese static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
473e29c22f5SKyungmin Park {
4742a74930dSMiquel Raynal return mtd->parent->_unlock(mtd->parent, ofs + mtd->offset, len);
475ff94bc40SHeiko Schocher }
476ff94bc40SHeiko Schocher
part_is_locked(struct mtd_info * mtd,loff_t ofs,uint64_t len)477ff94bc40SHeiko Schocher static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
478ff94bc40SHeiko Schocher {
4792a74930dSMiquel Raynal return mtd->parent->_is_locked(mtd->parent, ofs + mtd->offset, len);
480e29c22f5SKyungmin Park }
481e29c22f5SKyungmin Park
part_sync(struct mtd_info * mtd)482e29c22f5SKyungmin Park static void part_sync(struct mtd_info *mtd)
483e29c22f5SKyungmin Park {
4842a74930dSMiquel Raynal mtd->parent->_sync(mtd->parent);
485e29c22f5SKyungmin Park }
486e29c22f5SKyungmin Park
487ff94bc40SHeiko Schocher #ifndef __UBOOT__
part_suspend(struct mtd_info * mtd)488ff94bc40SHeiko Schocher static int part_suspend(struct mtd_info *mtd)
489ff94bc40SHeiko Schocher {
4902a74930dSMiquel Raynal return mtd->parent->_suspend(mtd->parent);
491ff94bc40SHeiko Schocher }
492ff94bc40SHeiko Schocher
part_resume(struct mtd_info * mtd)493ff94bc40SHeiko Schocher static void part_resume(struct mtd_info *mtd)
494ff94bc40SHeiko Schocher {
4952a74930dSMiquel Raynal mtd->parent->_resume(mtd->parent);
496ff94bc40SHeiko Schocher }
497ff94bc40SHeiko Schocher #endif
498ff94bc40SHeiko Schocher
part_block_isreserved(struct mtd_info * mtd,loff_t ofs)49986a720aaSEzequiel Garcia static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs)
50086a720aaSEzequiel Garcia {
5012a74930dSMiquel Raynal ofs += mtd->offset;
5022a74930dSMiquel Raynal return mtd->parent->_block_isreserved(mtd->parent, ofs);
50386a720aaSEzequiel Garcia }
50486a720aaSEzequiel Garcia
part_block_isbad(struct mtd_info * mtd,loff_t ofs)505e29c22f5SKyungmin Park static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
506e29c22f5SKyungmin Park {
5072a74930dSMiquel Raynal ofs += mtd->offset;
5082a74930dSMiquel Raynal return mtd->parent->_block_isbad(mtd->parent, ofs);
509e29c22f5SKyungmin Park }
510e29c22f5SKyungmin Park
part_block_markbad(struct mtd_info * mtd,loff_t ofs)511e29c22f5SKyungmin Park static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
512e29c22f5SKyungmin Park {
513e29c22f5SKyungmin Park int res;
514e29c22f5SKyungmin Park
5152a74930dSMiquel Raynal ofs += mtd->offset;
5162a74930dSMiquel Raynal res = mtd->parent->_block_markbad(mtd->parent, ofs);
517e29c22f5SKyungmin Park if (!res)
518e29c22f5SKyungmin Park mtd->ecc_stats.badblocks++;
519e29c22f5SKyungmin Park return res;
520e29c22f5SKyungmin Park }
521e29c22f5SKyungmin Park
free_partition(struct mtd_info * p)5222a74930dSMiquel Raynal static inline void free_partition(struct mtd_info *p)
523ff94bc40SHeiko Schocher {
5242a74930dSMiquel Raynal kfree(p->name);
525ff94bc40SHeiko Schocher kfree(p);
526ff94bc40SHeiko Schocher }
527ff94bc40SHeiko Schocher
528e29c22f5SKyungmin Park /*
529e29c22f5SKyungmin Park * This function unregisters and destroy all slave MTD objects which are
5302a74930dSMiquel Raynal * attached to the given master MTD object, recursively.
531e29c22f5SKyungmin Park */
do_del_mtd_partitions(struct mtd_info * master)5322a74930dSMiquel Raynal static int do_del_mtd_partitions(struct mtd_info *master)
533e29c22f5SKyungmin Park {
5342a74930dSMiquel Raynal struct mtd_info *slave, *next;
535ff94bc40SHeiko Schocher int ret, err = 0;
536e29c22f5SKyungmin Park
5372a74930dSMiquel Raynal list_for_each_entry_safe(slave, next, &master->partitions, node) {
5382a74930dSMiquel Raynal if (mtd_has_partitions(slave))
5392a74930dSMiquel Raynal del_mtd_partitions(slave);
540b0036f70SMiquel Raynal
5412a74930dSMiquel Raynal debug("Deleting %s MTD partition\n", slave->name);
5422a74930dSMiquel Raynal ret = del_mtd_device(slave);
543ff94bc40SHeiko Schocher if (ret < 0) {
5442a74930dSMiquel Raynal printf("Error when deleting partition \"%s\" (%d)\n",
5452a74930dSMiquel Raynal slave->name, ret);
546ff94bc40SHeiko Schocher err = ret;
547ff94bc40SHeiko Schocher continue;
548ff94bc40SHeiko Schocher }
5492a74930dSMiquel Raynal
5502a74930dSMiquel Raynal list_del(&slave->node);
551ff94bc40SHeiko Schocher free_partition(slave);
552ff94bc40SHeiko Schocher }
553ff94bc40SHeiko Schocher
554ff94bc40SHeiko Schocher return err;
555e29c22f5SKyungmin Park }
556e29c22f5SKyungmin Park
del_mtd_partitions(struct mtd_info * master)5572a74930dSMiquel Raynal int del_mtd_partitions(struct mtd_info *master)
558e29c22f5SKyungmin Park {
5592a74930dSMiquel Raynal int ret;
5602a74930dSMiquel Raynal
5612a74930dSMiquel Raynal debug("Deleting MTD partitions on \"%s\":\n", master->name);
5622a74930dSMiquel Raynal
5632a74930dSMiquel Raynal mutex_lock(&mtd_partitions_mutex);
5642a74930dSMiquel Raynal ret = do_del_mtd_partitions(master);
5652a74930dSMiquel Raynal mutex_unlock(&mtd_partitions_mutex);
5662a74930dSMiquel Raynal
5672a74930dSMiquel Raynal return ret;
5682a74930dSMiquel Raynal }
5692a74930dSMiquel Raynal
allocate_partition(struct mtd_info * master,const struct mtd_partition * part,int partno,uint64_t cur_offset)5702a74930dSMiquel Raynal static struct mtd_info *allocate_partition(struct mtd_info *master,
5712a74930dSMiquel Raynal const struct mtd_partition *part,
5722a74930dSMiquel Raynal int partno, uint64_t cur_offset)
5732a74930dSMiquel Raynal {
5742a74930dSMiquel Raynal struct mtd_info *slave;
575ff94bc40SHeiko Schocher char *name;
576e29c22f5SKyungmin Park
577e29c22f5SKyungmin Park /* allocate the partition structure */
578e29c22f5SKyungmin Park slave = kzalloc(sizeof(*slave), GFP_KERNEL);
579ff94bc40SHeiko Schocher name = kstrdup(part->name, GFP_KERNEL);
580ff94bc40SHeiko Schocher if (!name || !slave) {
5818d2effeaSStefan Roese printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
582e29c22f5SKyungmin Park master->name);
583ff94bc40SHeiko Schocher kfree(name);
584ff94bc40SHeiko Schocher kfree(slave);
585ff94bc40SHeiko Schocher return ERR_PTR(-ENOMEM);
586e29c22f5SKyungmin Park }
587e29c22f5SKyungmin Park
588e29c22f5SKyungmin Park /* set up the MTD object for this partition */
5892a74930dSMiquel Raynal slave->type = master->type;
5902a74930dSMiquel Raynal slave->flags = master->flags & ~part->mask_flags;
5912a74930dSMiquel Raynal slave->size = part->size;
5922a74930dSMiquel Raynal slave->writesize = master->writesize;
5932a74930dSMiquel Raynal slave->writebufsize = master->writebufsize;
5942a74930dSMiquel Raynal slave->oobsize = master->oobsize;
5952a74930dSMiquel Raynal slave->oobavail = master->oobavail;
5962a74930dSMiquel Raynal slave->subpage_sft = master->subpage_sft;
597e29c22f5SKyungmin Park
5982a74930dSMiquel Raynal slave->name = name;
5992a74930dSMiquel Raynal slave->owner = master->owner;
600ff94bc40SHeiko Schocher #ifndef __UBOOT__
6012a74930dSMiquel Raynal slave->backing_dev_info = master->backing_dev_info;
602ff94bc40SHeiko Schocher
603ff94bc40SHeiko Schocher /* NOTE: we don't arrange MTDs as a tree; it'd be error-prone
604ff94bc40SHeiko Schocher * to have the same data be in two different partitions.
605ff94bc40SHeiko Schocher */
6062a74930dSMiquel Raynal slave->dev.parent = master->dev.parent;
607ff94bc40SHeiko Schocher #endif
608e29c22f5SKyungmin Park
609596cf083SBoris Brezillon if (master->_read)
6102a74930dSMiquel Raynal slave->_read = part_read;
611596cf083SBoris Brezillon if (master->_write)
6122a74930dSMiquel Raynal slave->_write = part_write;
613e29c22f5SKyungmin Park
614ff94bc40SHeiko Schocher if (master->_panic_write)
6152a74930dSMiquel Raynal slave->_panic_write = part_panic_write;
616ff94bc40SHeiko Schocher
617ff94bc40SHeiko Schocher #ifndef __UBOOT__
618ff94bc40SHeiko Schocher if (master->_point && master->_unpoint) {
6192a74930dSMiquel Raynal slave->_point = part_point;
6202a74930dSMiquel Raynal slave->_unpoint = part_unpoint;
621ff94bc40SHeiko Schocher }
622ff94bc40SHeiko Schocher #endif
623ff94bc40SHeiko Schocher
624ff94bc40SHeiko Schocher if (master->_get_unmapped_area)
6252a74930dSMiquel Raynal slave->_get_unmapped_area = part_get_unmapped_area;
626dfe64e2cSSergey Lapin if (master->_read_oob)
6272a74930dSMiquel Raynal slave->_read_oob = part_read_oob;
628dfe64e2cSSergey Lapin if (master->_write_oob)
6292a74930dSMiquel Raynal slave->_write_oob = part_write_oob;
630dfe64e2cSSergey Lapin if (master->_read_user_prot_reg)
6312a74930dSMiquel Raynal slave->_read_user_prot_reg = part_read_user_prot_reg;
632dfe64e2cSSergey Lapin if (master->_read_fact_prot_reg)
6332a74930dSMiquel Raynal slave->_read_fact_prot_reg = part_read_fact_prot_reg;
634dfe64e2cSSergey Lapin if (master->_write_user_prot_reg)
6352a74930dSMiquel Raynal slave->_write_user_prot_reg = part_write_user_prot_reg;
636dfe64e2cSSergey Lapin if (master->_lock_user_prot_reg)
6372a74930dSMiquel Raynal slave->_lock_user_prot_reg = part_lock_user_prot_reg;
638dfe64e2cSSergey Lapin if (master->_get_user_prot_info)
6392a74930dSMiquel Raynal slave->_get_user_prot_info = part_get_user_prot_info;
640dfe64e2cSSergey Lapin if (master->_get_fact_prot_info)
6412a74930dSMiquel Raynal slave->_get_fact_prot_info = part_get_fact_prot_info;
642dfe64e2cSSergey Lapin if (master->_sync)
6432a74930dSMiquel Raynal slave->_sync = part_sync;
644ff94bc40SHeiko Schocher #ifndef __UBOOT__
645ff94bc40SHeiko Schocher if (!partno && !master->dev.class && master->_suspend &&
646ff94bc40SHeiko Schocher master->_resume) {
6472a74930dSMiquel Raynal slave->_suspend = part_suspend;
6482a74930dSMiquel Raynal slave->_resume = part_resume;
649ff94bc40SHeiko Schocher }
650ff94bc40SHeiko Schocher if (master->_writev)
6512a74930dSMiquel Raynal slave->_writev = part_writev;
652ff94bc40SHeiko Schocher #endif
653dfe64e2cSSergey Lapin if (master->_lock)
6542a74930dSMiquel Raynal slave->_lock = part_lock;
655dfe64e2cSSergey Lapin if (master->_unlock)
6562a74930dSMiquel Raynal slave->_unlock = part_unlock;
657ff94bc40SHeiko Schocher if (master->_is_locked)
6582a74930dSMiquel Raynal slave->_is_locked = part_is_locked;
65986a720aaSEzequiel Garcia if (master->_block_isreserved)
6602a74930dSMiquel Raynal slave->_block_isreserved = part_block_isreserved;
661dfe64e2cSSergey Lapin if (master->_block_isbad)
6622a74930dSMiquel Raynal slave->_block_isbad = part_block_isbad;
663dfe64e2cSSergey Lapin if (master->_block_markbad)
6642a74930dSMiquel Raynal slave->_block_markbad = part_block_markbad;
6652a74930dSMiquel Raynal slave->_erase = part_erase;
6662a74930dSMiquel Raynal slave->parent = master;
6678d2effeaSStefan Roese slave->offset = part->offset;
6682a74930dSMiquel Raynal INIT_LIST_HEAD(&slave->partitions);
6692a74930dSMiquel Raynal INIT_LIST_HEAD(&slave->node);
670e29c22f5SKyungmin Park
671e29c22f5SKyungmin Park if (slave->offset == MTDPART_OFS_APPEND)
672e29c22f5SKyungmin Park slave->offset = cur_offset;
673e29c22f5SKyungmin Park if (slave->offset == MTDPART_OFS_NXTBLK) {
674e29c22f5SKyungmin Park slave->offset = cur_offset;
6758d2effeaSStefan Roese if (mtd_mod_by_eb(cur_offset, master) != 0) {
676e29c22f5SKyungmin Park /* Round up to next erasesize */
6778d2effeaSStefan Roese slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
678ff94bc40SHeiko Schocher debug("Moving partition %d: "
679ff94bc40SHeiko Schocher "0x%012llx -> 0x%012llx\n", partno,
680ff94bc40SHeiko Schocher (unsigned long long)cur_offset, (unsigned long long)slave->offset);
681ff94bc40SHeiko Schocher }
682ff94bc40SHeiko Schocher }
683ff94bc40SHeiko Schocher if (slave->offset == MTDPART_OFS_RETAIN) {
684ff94bc40SHeiko Schocher slave->offset = cur_offset;
6852a74930dSMiquel Raynal if (master->size - slave->offset >= slave->size) {
6862a74930dSMiquel Raynal slave->size = master->size - slave->offset
6872a74930dSMiquel Raynal - slave->size;
688ff94bc40SHeiko Schocher } else {
689ff94bc40SHeiko Schocher debug("mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
690ff94bc40SHeiko Schocher part->name, master->size - slave->offset,
6912a74930dSMiquel Raynal slave->size);
692ff94bc40SHeiko Schocher /* register to preserve ordering */
693ff94bc40SHeiko Schocher goto out_register;
694e29c22f5SKyungmin Park }
695e29c22f5SKyungmin Park }
6962a74930dSMiquel Raynal if (slave->size == MTDPART_SIZ_FULL)
6972a74930dSMiquel Raynal slave->size = master->size - slave->offset;
698e29c22f5SKyungmin Park
699ff94bc40SHeiko Schocher debug("0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
7002a74930dSMiquel Raynal (unsigned long long)(slave->offset + slave->size), slave->name);
701e29c22f5SKyungmin Park
702e29c22f5SKyungmin Park /* let's do some sanity checks */
703e29c22f5SKyungmin Park if (slave->offset >= master->size) {
704e29c22f5SKyungmin Park /* let's register it anyway to preserve ordering */
705e29c22f5SKyungmin Park slave->offset = 0;
7062a74930dSMiquel Raynal slave->size = 0;
7078d2effeaSStefan Roese printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
7088d2effeaSStefan Roese part->name);
7098d2effeaSStefan Roese goto out_register;
710e29c22f5SKyungmin Park }
7112a74930dSMiquel Raynal if (slave->offset + slave->size > master->size) {
7122a74930dSMiquel Raynal slave->size = master->size - slave->offset;
7138d2effeaSStefan Roese printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
7142a74930dSMiquel Raynal part->name, master->name, slave->size);
715e29c22f5SKyungmin Park }
716e29c22f5SKyungmin Park if (master->numeraseregions > 1) {
717e29c22f5SKyungmin Park /* Deal with variable erase size stuff */
7188d2effeaSStefan Roese int i, max = master->numeraseregions;
7192a74930dSMiquel Raynal u64 end = slave->offset + slave->size;
720e29c22f5SKyungmin Park struct mtd_erase_region_info *regions = master->eraseregions;
721e29c22f5SKyungmin Park
7228d2effeaSStefan Roese /* Find the first erase regions which is part of this
7238d2effeaSStefan Roese * partition. */
7248d2effeaSStefan Roese for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
725e29c22f5SKyungmin Park ;
7268d2effeaSStefan Roese /* The loop searched for the region _behind_ the first one */
727ff94bc40SHeiko Schocher if (i > 0)
7288d2effeaSStefan Roese i--;
729e29c22f5SKyungmin Park
7308d2effeaSStefan Roese /* Pick biggest erasesize */
7318d2effeaSStefan Roese for (; i < max && regions[i].offset < end; i++) {
7322a74930dSMiquel Raynal if (slave->erasesize < regions[i].erasesize)
7332a74930dSMiquel Raynal slave->erasesize = regions[i].erasesize;
734e29c22f5SKyungmin Park }
7352a74930dSMiquel Raynal WARN_ON(slave->erasesize == 0);
736e29c22f5SKyungmin Park } else {
737e29c22f5SKyungmin Park /* Single erase size */
7382a74930dSMiquel Raynal slave->erasesize = master->erasesize;
739e29c22f5SKyungmin Park }
740e29c22f5SKyungmin Park
7412a74930dSMiquel Raynal if ((slave->flags & MTD_WRITEABLE) &&
7422a74930dSMiquel Raynal mtd_mod_by_eb(slave->offset, slave)) {
743e29c22f5SKyungmin Park /* Doesn't start on a boundary of major erase size */
7448d2effeaSStefan Roese /* FIXME: Let it be writable if it is on a boundary of
7458d2effeaSStefan Roese * _minor_ erase size though */
7462a74930dSMiquel Raynal slave->flags &= ~MTD_WRITEABLE;
7478d2effeaSStefan Roese printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
7488d2effeaSStefan Roese part->name);
749e29c22f5SKyungmin Park }
7502a74930dSMiquel Raynal if ((slave->flags & MTD_WRITEABLE) &&
7512a74930dSMiquel Raynal mtd_mod_by_eb(slave->size, slave)) {
7522a74930dSMiquel Raynal slave->flags &= ~MTD_WRITEABLE;
7538d2effeaSStefan Roese printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
7548d2effeaSStefan Roese part->name);
755e29c22f5SKyungmin Park }
756e29c22f5SKyungmin Park
7572a74930dSMiquel Raynal slave->ecclayout = master->ecclayout;
7582a74930dSMiquel Raynal slave->ecc_step_size = master->ecc_step_size;
7592a74930dSMiquel Raynal slave->ecc_strength = master->ecc_strength;
7602a74930dSMiquel Raynal slave->bitflip_threshold = master->bitflip_threshold;
761ff94bc40SHeiko Schocher
762dfe64e2cSSergey Lapin if (master->_block_isbad) {
7638d2effeaSStefan Roese uint64_t offs = 0;
764e29c22f5SKyungmin Park
7652a74930dSMiquel Raynal while (offs < slave->size) {
766dfe64e2cSSergey Lapin if (mtd_block_isbad(master, offs + slave->offset))
7672a74930dSMiquel Raynal slave->ecc_stats.badblocks++;
7682a74930dSMiquel Raynal offs += slave->erasesize;
769e29c22f5SKyungmin Park }
770e29c22f5SKyungmin Park }
771e29c22f5SKyungmin Park
7728d2effeaSStefan Roese out_register:
7738d2effeaSStefan Roese return slave;
7748d2effeaSStefan Roese }
7758d2effeaSStefan Roese
776ddf7bcfaSHeiko Schocher #ifndef __UBOOT__
mtd_add_partition(struct mtd_info * master,const char * name,long long offset,long long length)777ff94bc40SHeiko Schocher int mtd_add_partition(struct mtd_info *master, const char *name,
778ff94bc40SHeiko Schocher long long offset, long long length)
779ff94bc40SHeiko Schocher {
780ff94bc40SHeiko Schocher struct mtd_partition part;
7812a74930dSMiquel Raynal struct mtd_info *p, *new;
782ff94bc40SHeiko Schocher uint64_t start, end;
783ff94bc40SHeiko Schocher int ret = 0;
784ff94bc40SHeiko Schocher
785ff94bc40SHeiko Schocher /* the direct offset is expected */
786ff94bc40SHeiko Schocher if (offset == MTDPART_OFS_APPEND ||
787ff94bc40SHeiko Schocher offset == MTDPART_OFS_NXTBLK)
788ff94bc40SHeiko Schocher return -EINVAL;
789ff94bc40SHeiko Schocher
790ff94bc40SHeiko Schocher if (length == MTDPART_SIZ_FULL)
791ff94bc40SHeiko Schocher length = master->size - offset;
792ff94bc40SHeiko Schocher
793ff94bc40SHeiko Schocher if (length <= 0)
794ff94bc40SHeiko Schocher return -EINVAL;
795ff94bc40SHeiko Schocher
796ff94bc40SHeiko Schocher part.name = name;
797ff94bc40SHeiko Schocher part.size = length;
798ff94bc40SHeiko Schocher part.offset = offset;
799ff94bc40SHeiko Schocher part.mask_flags = 0;
800ff94bc40SHeiko Schocher part.ecclayout = NULL;
801ff94bc40SHeiko Schocher
802ff94bc40SHeiko Schocher new = allocate_partition(master, &part, -1, offset);
803ff94bc40SHeiko Schocher if (IS_ERR(new))
804ff94bc40SHeiko Schocher return PTR_ERR(new);
805ff94bc40SHeiko Schocher
806ff94bc40SHeiko Schocher start = offset;
807ff94bc40SHeiko Schocher end = offset + length;
808ff94bc40SHeiko Schocher
809ff94bc40SHeiko Schocher mutex_lock(&mtd_partitions_mutex);
8102a74930dSMiquel Raynal list_for_each_entry(p, &master->partitions, node) {
8112a74930dSMiquel Raynal if (start >= p->offset &&
8122a74930dSMiquel Raynal (start < (p->offset + p->size)))
813ff94bc40SHeiko Schocher goto err_inv;
814ff94bc40SHeiko Schocher
8152a74930dSMiquel Raynal if (end >= p->offset &&
8162a74930dSMiquel Raynal (end < (p->offset + p->size)))
817ff94bc40SHeiko Schocher goto err_inv;
818ff94bc40SHeiko Schocher }
819ff94bc40SHeiko Schocher
8202a74930dSMiquel Raynal list_add_tail(&new->node, &master->partitions);
821ff94bc40SHeiko Schocher mutex_unlock(&mtd_partitions_mutex);
822ff94bc40SHeiko Schocher
8232a74930dSMiquel Raynal add_mtd_device(new);
824ff94bc40SHeiko Schocher
825ff94bc40SHeiko Schocher return ret;
826ff94bc40SHeiko Schocher err_inv:
827ff94bc40SHeiko Schocher mutex_unlock(&mtd_partitions_mutex);
828ff94bc40SHeiko Schocher free_partition(new);
829ff94bc40SHeiko Schocher return -EINVAL;
830ff94bc40SHeiko Schocher }
831ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_add_partition);
832ff94bc40SHeiko Schocher
mtd_del_partition(struct mtd_info * master,int partno)833ff94bc40SHeiko Schocher int mtd_del_partition(struct mtd_info *master, int partno)
834ff94bc40SHeiko Schocher {
8352a74930dSMiquel Raynal struct mtd_info *slave, *next;
836ff94bc40SHeiko Schocher int ret = -EINVAL;
837ff94bc40SHeiko Schocher
838ff94bc40SHeiko Schocher mutex_lock(&mtd_partitions_mutex);
8392a74930dSMiquel Raynal list_for_each_entry_safe(slave, next, &master->partitions, node)
8402a74930dSMiquel Raynal if (slave->index == partno) {
8412a74930dSMiquel Raynal ret = del_mtd_device(slave);
842ff94bc40SHeiko Schocher if (ret < 0)
843ff94bc40SHeiko Schocher break;
844ff94bc40SHeiko Schocher
8452a74930dSMiquel Raynal list_del(&slave->node);
846ff94bc40SHeiko Schocher free_partition(slave);
847ff94bc40SHeiko Schocher break;
848ff94bc40SHeiko Schocher }
849ff94bc40SHeiko Schocher mutex_unlock(&mtd_partitions_mutex);
850ff94bc40SHeiko Schocher
851ff94bc40SHeiko Schocher return ret;
852ff94bc40SHeiko Schocher }
853ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_del_partition);
854ddf7bcfaSHeiko Schocher #endif
855ff94bc40SHeiko Schocher
8568d2effeaSStefan Roese /*
8578d2effeaSStefan Roese * This function, given a master MTD object and a partition table, creates
8588d2effeaSStefan Roese * and registers slave MTD objects which are bound to the master according to
8598d2effeaSStefan Roese * the partition definitions.
8608d2effeaSStefan Roese *
8618d2effeaSStefan Roese * We don't register the master, or expect the caller to have done so,
8628d2effeaSStefan Roese * for reasons of data integrity.
8638d2effeaSStefan Roese */
8648d2effeaSStefan Roese
add_mtd_partitions(struct mtd_info * master,const struct mtd_partition * parts,int nbparts)8658d2effeaSStefan Roese int add_mtd_partitions(struct mtd_info *master,
8668d2effeaSStefan Roese const struct mtd_partition *parts,
8678d2effeaSStefan Roese int nbparts)
8688d2effeaSStefan Roese {
8692a74930dSMiquel Raynal struct mtd_info *slave;
8708d2effeaSStefan Roese uint64_t cur_offset = 0;
8718d2effeaSStefan Roese int i;
8728d2effeaSStefan Roese
873147162daSJoe Hershberger debug("Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
8748d2effeaSStefan Roese
8758d2effeaSStefan Roese for (i = 0; i < nbparts; i++) {
876ff94bc40SHeiko Schocher slave = allocate_partition(master, parts + i, i, cur_offset);
877ff94bc40SHeiko Schocher if (IS_ERR(slave))
878ff94bc40SHeiko Schocher return PTR_ERR(slave);
879ff94bc40SHeiko Schocher
880ff94bc40SHeiko Schocher mutex_lock(&mtd_partitions_mutex);
8812a74930dSMiquel Raynal list_add_tail(&slave->node, &master->partitions);
882ff94bc40SHeiko Schocher mutex_unlock(&mtd_partitions_mutex);
883ff94bc40SHeiko Schocher
8842a74930dSMiquel Raynal add_mtd_device(slave);
885ff94bc40SHeiko Schocher
8862a74930dSMiquel Raynal cur_offset = slave->offset + slave->size;
887e29c22f5SKyungmin Park }
888e29c22f5SKyungmin Park
889e29c22f5SKyungmin Park return 0;
890e29c22f5SKyungmin Park }
891ff94bc40SHeiko Schocher
892ff94bc40SHeiko Schocher #ifndef __UBOOT__
893ff94bc40SHeiko Schocher static DEFINE_SPINLOCK(part_parser_lock);
894ff94bc40SHeiko Schocher static LIST_HEAD(part_parsers);
895ff94bc40SHeiko Schocher
get_partition_parser(const char * name)896ff94bc40SHeiko Schocher static struct mtd_part_parser *get_partition_parser(const char *name)
897ff94bc40SHeiko Schocher {
898ff94bc40SHeiko Schocher struct mtd_part_parser *p, *ret = NULL;
899ff94bc40SHeiko Schocher
900ff94bc40SHeiko Schocher spin_lock(&part_parser_lock);
901ff94bc40SHeiko Schocher
902ff94bc40SHeiko Schocher list_for_each_entry(p, &part_parsers, list)
903ff94bc40SHeiko Schocher if (!strcmp(p->name, name) && try_module_get(p->owner)) {
904ff94bc40SHeiko Schocher ret = p;
905ff94bc40SHeiko Schocher break;
906ff94bc40SHeiko Schocher }
907ff94bc40SHeiko Schocher
908ff94bc40SHeiko Schocher spin_unlock(&part_parser_lock);
909ff94bc40SHeiko Schocher
910ff94bc40SHeiko Schocher return ret;
911ff94bc40SHeiko Schocher }
912ff94bc40SHeiko Schocher
913ff94bc40SHeiko Schocher #define put_partition_parser(p) do { module_put((p)->owner); } while (0)
914ff94bc40SHeiko Schocher
register_mtd_parser(struct mtd_part_parser * p)915ff94bc40SHeiko Schocher void register_mtd_parser(struct mtd_part_parser *p)
916ff94bc40SHeiko Schocher {
917ff94bc40SHeiko Schocher spin_lock(&part_parser_lock);
918ff94bc40SHeiko Schocher list_add(&p->list, &part_parsers);
919ff94bc40SHeiko Schocher spin_unlock(&part_parser_lock);
920ff94bc40SHeiko Schocher }
921ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(register_mtd_parser);
922ff94bc40SHeiko Schocher
deregister_mtd_parser(struct mtd_part_parser * p)923ff94bc40SHeiko Schocher void deregister_mtd_parser(struct mtd_part_parser *p)
924ff94bc40SHeiko Schocher {
925ff94bc40SHeiko Schocher spin_lock(&part_parser_lock);
926ff94bc40SHeiko Schocher list_del(&p->list);
927ff94bc40SHeiko Schocher spin_unlock(&part_parser_lock);
928ff94bc40SHeiko Schocher }
929ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(deregister_mtd_parser);
930ff94bc40SHeiko Schocher
931ff94bc40SHeiko Schocher /*
932ff94bc40SHeiko Schocher * Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you
933ff94bc40SHeiko Schocher * are changing this array!
934ff94bc40SHeiko Schocher */
935ff94bc40SHeiko Schocher static const char * const default_mtd_part_types[] = {
936ff94bc40SHeiko Schocher "cmdlinepart",
937ff94bc40SHeiko Schocher "ofpart",
938ff94bc40SHeiko Schocher NULL
939ff94bc40SHeiko Schocher };
940ff94bc40SHeiko Schocher
941ff94bc40SHeiko Schocher /**
942ff94bc40SHeiko Schocher * parse_mtd_partitions - parse MTD partitions
943ff94bc40SHeiko Schocher * @master: the master partition (describes whole MTD device)
944ff94bc40SHeiko Schocher * @types: names of partition parsers to try or %NULL
945ff94bc40SHeiko Schocher * @pparts: array of partitions found is returned here
946ff94bc40SHeiko Schocher * @data: MTD partition parser-specific data
947ff94bc40SHeiko Schocher *
948ff94bc40SHeiko Schocher * This function tries to find partition on MTD device @master. It uses MTD
949ff94bc40SHeiko Schocher * partition parsers, specified in @types. However, if @types is %NULL, then
950ff94bc40SHeiko Schocher * the default list of parsers is used. The default list contains only the
951ff94bc40SHeiko Schocher * "cmdlinepart" and "ofpart" parsers ATM.
952ff94bc40SHeiko Schocher * Note: If there are more then one parser in @types, the kernel only takes the
953ff94bc40SHeiko Schocher * partitions parsed out by the first parser.
954ff94bc40SHeiko Schocher *
955ff94bc40SHeiko Schocher * This function may return:
956ff94bc40SHeiko Schocher * o a negative error code in case of failure
957ff94bc40SHeiko Schocher * o zero if no partitions were found
958ff94bc40SHeiko Schocher * o a positive number of found partitions, in which case on exit @pparts will
959ff94bc40SHeiko Schocher * point to an array containing this number of &struct mtd_info objects.
960ff94bc40SHeiko Schocher */
parse_mtd_partitions(struct mtd_info * master,const char * const * types,struct mtd_partition ** pparts,struct mtd_part_parser_data * data)961ff94bc40SHeiko Schocher int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
962ff94bc40SHeiko Schocher struct mtd_partition **pparts,
963ff94bc40SHeiko Schocher struct mtd_part_parser_data *data)
964ff94bc40SHeiko Schocher {
965ff94bc40SHeiko Schocher struct mtd_part_parser *parser;
966ff94bc40SHeiko Schocher int ret = 0;
967ff94bc40SHeiko Schocher
968ff94bc40SHeiko Schocher if (!types)
969ff94bc40SHeiko Schocher types = default_mtd_part_types;
970ff94bc40SHeiko Schocher
971ff94bc40SHeiko Schocher for ( ; ret <= 0 && *types; types++) {
972ff94bc40SHeiko Schocher parser = get_partition_parser(*types);
973ff94bc40SHeiko Schocher if (!parser && !request_module("%s", *types))
974ff94bc40SHeiko Schocher parser = get_partition_parser(*types);
975ff94bc40SHeiko Schocher if (!parser)
976ff94bc40SHeiko Schocher continue;
977ff94bc40SHeiko Schocher ret = (*parser->parse_fn)(master, pparts, data);
978ff94bc40SHeiko Schocher put_partition_parser(parser);
979ff94bc40SHeiko Schocher if (ret > 0) {
980ff94bc40SHeiko Schocher printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
981ff94bc40SHeiko Schocher ret, parser->name, master->name);
982ff94bc40SHeiko Schocher break;
983ff94bc40SHeiko Schocher }
984ff94bc40SHeiko Schocher }
985ff94bc40SHeiko Schocher return ret;
986ff94bc40SHeiko Schocher }
987ff94bc40SHeiko Schocher #endif
988ff94bc40SHeiko Schocher
989ff94bc40SHeiko Schocher /* Returns the size of the entire flash chip */
mtd_get_device_size(const struct mtd_info * mtd)990ff94bc40SHeiko Schocher uint64_t mtd_get_device_size(const struct mtd_info *mtd)
991ff94bc40SHeiko Schocher {
9922a74930dSMiquel Raynal if (mtd_is_partition(mtd))
9932a74930dSMiquel Raynal return mtd->parent->size;
994ff94bc40SHeiko Schocher
9952a74930dSMiquel Raynal return mtd->size;
996ff94bc40SHeiko Schocher }
997ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_get_device_size);
998