blob: 88094b81e7a2e27ce18a3d185ba63b708c2deb45 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Kyungmin Parkf6d5e252008-11-19 16:20:36 +01002/*
3 * Simple MTD partitioning layer
4 *
Heiko Schocherf5895d12014-06-24 10:10:04 +02005 * Copyright © 2000 Nicolas Pitre <nico@fluxnic.net>
6 * Copyright © 2002 Thomas Gleixner <gleixner@linutronix.de>
7 * Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org>
Kyungmin Parkf6d5e252008-11-19 16:20:36 +01008 *
Kyungmin Parkf6d5e252008-11-19 16:20:36 +01009 */
10
Heiko Schocherf5895d12014-06-24 10:10:04 +020011#ifndef __UBOOT__
Simon Glass0f2af882020-05-10 11:40:05 -060012#include <log.h>
Simon Glassd66c5f72020-02-03 07:36:15 -070013#include <dm/devres.h>
Heiko Schocherf5895d12014-06-24 10:10:04 +020014#include <linux/module.h>
15#include <linux/types.h>
16#include <linux/kernel.h>
17#include <linux/slab.h>
18#include <linux/list.h>
19#include <linux/kmod.h>
20#endif
21
Kyungmin Parkf6d5e252008-11-19 16:20:36 +010022#include <malloc.h>
Alexey Romanovd5119672024-07-18 08:46:04 +030023#include <memalign.h>
24#include <part.h>
Simon Glassc06c1be2020-05-10 11:40:08 -060025#include <linux/bug.h>
Masahiro Yamada56a931c2016-09-21 11:28:55 +090026#include <linux/errno.h>
Heiko Schocherf5895d12014-06-24 10:10:04 +020027#include <linux/compat.h>
28#include <ubi_uboot.h>
Kyungmin Parkf6d5e252008-11-19 16:20:36 +010029
Kyungmin Parkf6d5e252008-11-19 16:20:36 +010030#include <linux/mtd/mtd.h>
31#include <linux/mtd/partitions.h>
Heiko Schocherf5895d12014-06-24 10:10:04 +020032#include <linux/err.h>
Miquel Raynal6f381d52018-09-29 12:58:25 +020033#include <linux/sizes.h>
Heiko Schocherf5895d12014-06-24 10:10:04 +020034
35#include "mtdcore.h"
Kyungmin Parkf6d5e252008-11-19 16:20:36 +010036
Heiko Schocherf5895d12014-06-24 10:10:04 +020037#ifndef __UBOOT__
38static DEFINE_MUTEX(mtd_partitions_mutex);
39#else
40DEFINE_MUTEX(mtd_partitions_mutex);
41#endif
Kyungmin Parkf6d5e252008-11-19 16:20:36 +010042
Heiko Schocherf5895d12014-06-24 10:10:04 +020043#ifdef __UBOOT__
44/* from mm/util.c */
45
46/**
47 * kstrdup - allocate space for and copy an existing string
48 * @s: the string to duplicate
49 * @gfp: the GFP mask used in the kmalloc() call when allocating memory
50 */
51char *kstrdup(const char *s, gfp_t gfp)
52{
53 size_t len;
54 char *buf;
55
56 if (!s)
57 return NULL;
58
59 len = strlen(s) + 1;
60 buf = kmalloc(len, gfp);
61 if (buf)
62 memcpy(buf, s, len);
63 return buf;
64}
65#endif
66
Miquel Raynal6f381d52018-09-29 12:58:25 +020067#define MTD_SIZE_REMAINING (~0LLU)
68#define MTD_OFFSET_NOT_SPECIFIED (~0LLU)
69
Boris Brezillon7dfad312018-12-02 10:54:30 +010070bool mtd_partitions_used(struct mtd_info *master)
71{
72 struct mtd_info *slave;
73
74 list_for_each_entry(slave, &master->partitions, node) {
75 if (slave->usecount)
76 return true;
77 }
78
79 return false;
80}
81
Miquel Raynal6f381d52018-09-29 12:58:25 +020082/**
83 * mtd_parse_partition - Parse @mtdparts partition definition, fill @partition
84 * with it and update the @mtdparts string pointer.
85 *
86 * The partition name is allocated and must be freed by the caller.
87 *
88 * This function is widely inspired from part_parse (mtdparts.c).
89 *
90 * @mtdparts: String describing the partition with mtdparts command syntax
91 * @partition: MTD partition structure to fill
92 *
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +010093 * Return: 0 on success, an error otherwise.
Miquel Raynal6f381d52018-09-29 12:58:25 +020094 */
95static int mtd_parse_partition(const char **_mtdparts,
96 struct mtd_partition *partition)
97{
98 const char *mtdparts = *_mtdparts;
99 const char *name = NULL;
100 int name_len;
101 char *buf;
102
103 /* Ensure the partition structure is empty */
104 memset(partition, 0, sizeof(struct mtd_partition));
105
106 /* Fetch the partition size */
107 if (*mtdparts == '-') {
108 /* Assign all remaining space to this partition */
109 partition->size = MTD_SIZE_REMAINING;
110 mtdparts++;
111 } else {
112 partition->size = ustrtoull(mtdparts, (char **)&mtdparts, 0);
113 if (partition->size < SZ_4K) {
114 printf("Minimum partition size 4kiB, %lldB requested\n",
115 partition->size);
116 return -EINVAL;
117 }
118 }
119
120 /* Check for the offset */
121 partition->offset = MTD_OFFSET_NOT_SPECIFIED;
122 if (*mtdparts == '@') {
123 mtdparts++;
124 partition->offset = ustrtoull(mtdparts, (char **)&mtdparts, 0);
125 }
126
127 /* Now look for the name */
128 if (*mtdparts == '(') {
129 name = ++mtdparts;
130 mtdparts = strchr(name, ')');
131 if (!mtdparts) {
132 printf("No closing ')' found in partition name\n");
133 return -EINVAL;
134 }
135 name_len = mtdparts - name + 1;
136 if ((name_len - 1) == 0) {
137 printf("Empty partition name\n");
138 return -EINVAL;
139 }
140 mtdparts++;
141 } else {
142 /* Name will be of the form size@offset */
143 name_len = 22;
144 }
145
146 /* Check if the partition is read-only */
147 if (strncmp(mtdparts, "ro", 2) == 0) {
148 partition->mask_flags |= MTD_WRITEABLE;
149 mtdparts += 2;
150 }
151
152 /* Check for a potential next partition definition */
153 if (*mtdparts == ',') {
154 if (partition->size == MTD_SIZE_REMAINING) {
155 printf("No partitions allowed after a fill-up\n");
156 return -EINVAL;
157 }
158 ++mtdparts;
159 } else if ((*mtdparts == ';') || (*mtdparts == '\0')) {
160 /* NOP */
161 } else {
162 printf("Unexpected character '%c' in mtdparts\n", *mtdparts);
163 return -EINVAL;
164 }
165
166 /*
167 * Allocate a buffer for the name and either copy the provided name or
168 * auto-generate it with the form 'size@offset'.
169 */
170 buf = malloc(name_len);
171 if (!buf)
172 return -ENOMEM;
173
174 if (name)
175 strncpy(buf, name, name_len - 1);
176 else
177 snprintf(buf, name_len, "0x%08llx@0x%08llx",
178 partition->size, partition->offset);
179
180 buf[name_len - 1] = '\0';
181 partition->name = buf;
182
183 *_mtdparts = mtdparts;
184
185 return 0;
186}
187
188/**
189 * mtd_parse_partitions - Create a partition array from an mtdparts definition
190 *
191 * Stateless function that takes a @parent MTD device, a string @_mtdparts
192 * describing the partitions (with the "mtdparts" command syntax) and creates
193 * the corresponding MTD partition structure array @_parts. Both the name and
194 * the structure partition itself must be freed freed, the caller may use
195 * @mtd_free_parsed_partitions() for this purpose.
196 *
197 * @parent: MTD device which contains the partitions
198 * @_mtdparts: Pointer to a string describing the partitions with "mtdparts"
199 * command syntax.
200 * @_parts: Allocated array containing the partitions, must be freed by the
201 * caller.
202 * @_nparts: Size of @_parts array.
203 *
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +0100204 * Return: 0 on success, an error otherwise.
Miquel Raynal6f381d52018-09-29 12:58:25 +0200205 */
206int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts,
207 struct mtd_partition **_parts, int *_nparts)
208{
209 struct mtd_partition partition = {}, *parts;
210 const char *mtdparts = *_mtdparts;
Martin Kaistra982c4602020-07-13 14:40:02 +0200211 uint64_t cur_off = 0, cur_sz = 0;
Miquel Raynal6f381d52018-09-29 12:58:25 +0200212 int nparts = 0;
213 int ret, idx;
214 u64 sz;
215
216 /* First, iterate over the partitions until we know their number */
217 while (mtdparts[0] != '\0' && mtdparts[0] != ';') {
218 ret = mtd_parse_partition(&mtdparts, &partition);
219 if (ret)
220 return ret;
221
222 free((char *)partition.name);
223 nparts++;
224 }
225
226 /* Allocate an array of partitions to give back to the caller */
227 parts = malloc(sizeof(*parts) * nparts);
228 if (!parts) {
229 printf("Not enough space to save partitions meta-data\n");
230 return -ENOMEM;
231 }
232
233 /* Iterate again over each partition to save the data in our array */
234 for (idx = 0; idx < nparts; idx++) {
235 ret = mtd_parse_partition(_mtdparts, &parts[idx]);
236 if (ret)
237 return ret;
238
239 if (parts[idx].size == MTD_SIZE_REMAINING)
240 parts[idx].size = parent->size - cur_sz;
241 cur_sz += parts[idx].size;
242
243 sz = parts[idx].size;
244 if (sz < parent->writesize || do_div(sz, parent->writesize)) {
245 printf("Partition size must be a multiple of %d\n",
246 parent->writesize);
247 return -EINVAL;
248 }
249
250 if (parts[idx].offset == MTD_OFFSET_NOT_SPECIFIED)
251 parts[idx].offset = cur_off;
252 cur_off += parts[idx].size;
253
254 parts[idx].ecclayout = parent->ecclayout;
255 }
256
257 /* Offset by one mtdparts to point to the next device if any */
258 if (*_mtdparts[0] == ';')
259 (*_mtdparts)++;
260
261 *_parts = parts;
262 *_nparts = nparts;
263
264 return 0;
265}
266
267/**
268 * mtd_free_parsed_partitions - Free dynamically allocated partitions
269 *
270 * Each successful call to @mtd_parse_partitions must be followed by a call to
271 * @mtd_free_parsed_partitions to free any allocated array during the parsing
272 * process.
273 *
274 * @parts: Array containing the partitions that will be freed.
275 * @nparts: Size of @parts array.
276 */
277void mtd_free_parsed_partitions(struct mtd_partition *parts,
278 unsigned int nparts)
279{
280 int i;
281
282 for (i = 0; i < nparts; i++)
283 free((char *)parts[i].name);
284
285 free(parts);
286}
287
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100288/*
289 * MTD methods which simply translate the effective address and pass through
290 * to the _real_ device.
291 */
292
Stefan Roese586b3a62009-05-11 16:03:55 +0200293static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
294 size_t *retlen, u_char *buf)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100295{
Stefan Roese586b3a62009-05-11 16:03:55 +0200296 struct mtd_ecc_stats stats;
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100297 int res;
298
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200299 stats = mtd->parent->ecc_stats;
300 res = mtd->parent->_read(mtd->parent, from + mtd->offset, len,
301 retlen, buf);
Paul Burton700a76c2013-09-04 15:16:56 +0100302 if (unlikely(mtd_is_eccerr(res)))
303 mtd->ecc_stats.failed +=
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200304 mtd->parent->ecc_stats.failed - stats.failed;
Paul Burton700a76c2013-09-04 15:16:56 +0100305 else
306 mtd->ecc_stats.corrected +=
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200307 mtd->parent->ecc_stats.corrected - stats.corrected;
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100308 return res;
309}
Heiko Schocherf5895d12014-06-24 10:10:04 +0200310
311#ifndef __UBOOT__
312static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
313 size_t *retlen, void **virt, resource_size_t *phys)
314{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200315 return mtd->parent->_point(mtd->parent, from + mtd->offset, len,
316 retlen, virt, phys);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200317}
318
319static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
320{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200321 return mtd->parent->_unpoint(mtd->parent, from + mtd->offset, len);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200322}
323#endif
324
325static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
326 unsigned long len,
327 unsigned long offset,
328 unsigned long flags)
329{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200330 offset += mtd->offset;
331 return mtd->parent->_get_unmapped_area(mtd->parent, len, offset, flags);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200332}
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100333
334static int part_read_oob(struct mtd_info *mtd, loff_t from,
Stefan Roese586b3a62009-05-11 16:03:55 +0200335 struct mtd_oob_ops *ops)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100336{
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100337 int res;
338
339 if (from >= mtd->size)
340 return -EINVAL;
341 if (ops->datbuf && from + ops->len > mtd->size)
342 return -EINVAL;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200343
344 /*
345 * If OOB is also requested, make sure that we do not read past the end
346 * of this partition.
347 */
348 if (ops->oobbuf) {
349 size_t len, pages;
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100350
Heiko Schocherf5895d12014-06-24 10:10:04 +0200351 if (ops->mode == MTD_OPS_AUTO_OOB)
352 len = mtd->oobavail;
353 else
354 len = mtd->oobsize;
355 pages = mtd_div_by_ws(mtd->size, mtd);
356 pages -= mtd_div_by_ws(from, mtd);
357 if (ops->ooboffs + ops->ooblen > pages * len)
358 return -EINVAL;
359 }
360
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200361 res = mtd->parent->_read_oob(mtd->parent, from + mtd->offset, ops);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100362 if (unlikely(res)) {
Sergey Lapin3a38a552013-01-14 03:46:50 +0000363 if (mtd_is_bitflip(res))
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100364 mtd->ecc_stats.corrected++;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000365 if (mtd_is_eccerr(res))
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100366 mtd->ecc_stats.failed++;
367 }
368 return res;
369}
370
Stefan Roese586b3a62009-05-11 16:03:55 +0200371static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
372 size_t len, size_t *retlen, u_char *buf)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100373{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200374 return mtd->parent->_read_user_prot_reg(mtd->parent, from, len,
375 retlen, buf);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100376}
377
Heiko Schocher081fe9e2014-07-15 16:08:43 +0200378static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
379 size_t *retlen, struct otp_info *buf)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100380{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200381 return mtd->parent->_get_user_prot_info(mtd->parent, len, retlen,
382 buf);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100383}
384
Stefan Roese586b3a62009-05-11 16:03:55 +0200385static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
386 size_t len, size_t *retlen, u_char *buf)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100387{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200388 return mtd->parent->_read_fact_prot_reg(mtd->parent, from, len,
389 retlen, buf);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100390}
391
Heiko Schocher081fe9e2014-07-15 16:08:43 +0200392static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
393 size_t *retlen, struct otp_info *buf)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100394{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200395 return mtd->parent->_get_fact_prot_info(mtd->parent, len, retlen,
396 buf);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100397}
398
Stefan Roese586b3a62009-05-11 16:03:55 +0200399static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
400 size_t *retlen, const u_char *buf)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100401{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200402 return mtd->parent->_write(mtd->parent, to + mtd->offset, len,
403 retlen, buf);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200404}
405
406static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
407 size_t *retlen, const u_char *buf)
408{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200409 return mtd->parent->_panic_write(mtd->parent, to + mtd->offset, len,
410 retlen, buf);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100411}
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100412
413static int part_write_oob(struct mtd_info *mtd, loff_t to,
Stefan Roese586b3a62009-05-11 16:03:55 +0200414 struct mtd_oob_ops *ops)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100415{
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100416 if (to >= mtd->size)
417 return -EINVAL;
418 if (ops->datbuf && to + ops->len > mtd->size)
419 return -EINVAL;
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200420 return mtd->parent->_write_oob(mtd->parent, to + mtd->offset, ops);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100421}
422
Stefan Roese586b3a62009-05-11 16:03:55 +0200423static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
424 size_t len, size_t *retlen, u_char *buf)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100425{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200426 return mtd->parent->_write_user_prot_reg(mtd->parent, from, len,
427 retlen, buf);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100428}
429
Stefan Roese586b3a62009-05-11 16:03:55 +0200430static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
431 size_t len)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100432{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200433 return mtd->parent->_lock_user_prot_reg(mtd->parent, from, len);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200434}
435
436#ifndef __UBOOT__
437static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
438 unsigned long count, loff_t to, size_t *retlen)
439{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200440 return mtd->parent->_writev(mtd->parent, vecs, count,
441 to + mtd->offset, retlen);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100442}
Heiko Schocherf5895d12014-06-24 10:10:04 +0200443#endif
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100444
Stefan Roese586b3a62009-05-11 16:03:55 +0200445static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100446{
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100447 int ret;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000448
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200449 instr->addr += mtd->offset;
Marek Behún2e7d7622021-10-05 15:56:05 +0200450
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200451 ret = mtd->parent->_erase(mtd->parent, instr);
Marek Behún2e7d7622021-10-05 15:56:05 +0200452 if (ret && instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
453 instr->fail_addr -= mtd->offset;
454
455 instr->addr -= mtd->offset;
456
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100457 return ret;
458}
459
Stefan Roese586b3a62009-05-11 16:03:55 +0200460static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100461{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200462 return mtd->parent->_lock(mtd->parent, ofs + mtd->offset, len);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100463}
464
Stefan Roese586b3a62009-05-11 16:03:55 +0200465static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100466{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200467 return mtd->parent->_unlock(mtd->parent, ofs + mtd->offset, len);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100468}
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100469
Heiko Schocherf5895d12014-06-24 10:10:04 +0200470static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
471{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200472 return mtd->parent->_is_locked(mtd->parent, ofs + mtd->offset, len);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200473}
474
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100475static void part_sync(struct mtd_info *mtd)
476{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200477 mtd->parent->_sync(mtd->parent);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200478}
479
480#ifndef __UBOOT__
481static int part_suspend(struct mtd_info *mtd)
482{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200483 return mtd->parent->_suspend(mtd->parent);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200484}
485
486static void part_resume(struct mtd_info *mtd)
487{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200488 mtd->parent->_resume(mtd->parent);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100489}
Heiko Schocherf5895d12014-06-24 10:10:04 +0200490#endif
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100491
Ezequiel Garciafc9d57c2014-05-21 19:06:12 -0300492static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs)
493{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200494 ofs += mtd->offset;
495 return mtd->parent->_block_isreserved(mtd->parent, ofs);
Ezequiel Garciafc9d57c2014-05-21 19:06:12 -0300496}
497
Stefan Roese586b3a62009-05-11 16:03:55 +0200498static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100499{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200500 ofs += mtd->offset;
501 return mtd->parent->_block_isbad(mtd->parent, ofs);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100502}
503
Stefan Roese586b3a62009-05-11 16:03:55 +0200504static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100505{
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100506 int res;
507
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200508 ofs += mtd->offset;
509 res = mtd->parent->_block_markbad(mtd->parent, ofs);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100510 if (!res)
511 mtd->ecc_stats.badblocks++;
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100512 return res;
513}
514
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200515static inline void free_partition(struct mtd_info *p)
Heiko Schocherf5895d12014-06-24 10:10:04 +0200516{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200517 kfree(p->name);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200518 kfree(p);
519}
520
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100521/*
522 * This function unregisters and destroy all slave MTD objects which are
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200523 * attached to the given master MTD object, recursively.
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100524 */
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200525static int do_del_mtd_partitions(struct mtd_info *master)
526{
527 struct mtd_info *slave, *next;
528 int ret, err = 0;
529
530 list_for_each_entry_safe(slave, next, &master->partitions, node) {
531 if (mtd_has_partitions(slave))
532 del_mtd_partitions(slave);
533
534 debug("Deleting %s MTD partition\n", slave->name);
535 ret = del_mtd_device(slave);
536 if (ret < 0) {
537 printf("Error when deleting partition \"%s\" (%d)\n",
538 slave->name, ret);
539 err = ret;
540 continue;
541 }
542
543 list_del(&slave->node);
544 free_partition(slave);
545 }
546
547 return err;
548}
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100549
550int del_mtd_partitions(struct mtd_info *master)
551{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200552 int ret;
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100553
Miquel Raynalf78a12b2018-08-16 17:30:19 +0200554 debug("Deleting MTD partitions on \"%s\":\n", master->name);
555
Heiko Schocherf5895d12014-06-24 10:10:04 +0200556 mutex_lock(&mtd_partitions_mutex);
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200557 ret = do_del_mtd_partitions(master);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200558 mutex_unlock(&mtd_partitions_mutex);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100559
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200560 return ret;
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100561}
562
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200563static struct mtd_info *allocate_partition(struct mtd_info *master,
564 const struct mtd_partition *part,
565 int partno, uint64_t cur_offset)
Stefan Roese586b3a62009-05-11 16:03:55 +0200566{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200567 struct mtd_info *slave;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200568 char *name;
Stefan Roese586b3a62009-05-11 16:03:55 +0200569
570 /* allocate the partition structure */
571 slave = kzalloc(sizeof(*slave), GFP_KERNEL);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200572 name = kstrdup(part->name, GFP_KERNEL);
573 if (!name || !slave) {
Stefan Roese586b3a62009-05-11 16:03:55 +0200574 printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
Heiko Schocherf5895d12014-06-24 10:10:04 +0200575 master->name);
576 kfree(name);
577 kfree(slave);
578 return ERR_PTR(-ENOMEM);
Stefan Roese586b3a62009-05-11 16:03:55 +0200579 }
Stefan Roese586b3a62009-05-11 16:03:55 +0200580
581 /* set up the MTD object for this partition */
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200582 slave->type = master->type;
583 slave->flags = master->flags & ~part->mask_flags;
584 slave->size = part->size;
585 slave->writesize = master->writesize;
586 slave->writebufsize = master->writebufsize;
587 slave->oobsize = master->oobsize;
588 slave->oobavail = master->oobavail;
589 slave->subpage_sft = master->subpage_sft;
Stefan Roese586b3a62009-05-11 16:03:55 +0200590
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200591 slave->name = name;
592 slave->owner = master->owner;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200593#ifndef __UBOOT__
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200594 slave->backing_dev_info = master->backing_dev_info;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200595
596 /* NOTE: we don't arrange MTDs as a tree; it'd be error-prone
597 * to have the same data be in two different partitions.
598 */
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200599 slave->dev.parent = master->dev.parent;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200600#endif
Stefan Roese586b3a62009-05-11 16:03:55 +0200601
Boris Brezillon6c20df72018-08-16 17:29:59 +0200602 if (master->_read)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200603 slave->_read = part_read;
Boris Brezillon6c20df72018-08-16 17:29:59 +0200604 if (master->_write)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200605 slave->_write = part_write;
Stefan Roese586b3a62009-05-11 16:03:55 +0200606
Heiko Schocherf5895d12014-06-24 10:10:04 +0200607 if (master->_panic_write)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200608 slave->_panic_write = part_panic_write;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200609
610#ifndef __UBOOT__
611 if (master->_point && master->_unpoint) {
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200612 slave->_point = part_point;
613 slave->_unpoint = part_unpoint;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200614 }
615#endif
616
617 if (master->_get_unmapped_area)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200618 slave->_get_unmapped_area = part_get_unmapped_area;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000619 if (master->_read_oob)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200620 slave->_read_oob = part_read_oob;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000621 if (master->_write_oob)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200622 slave->_write_oob = part_write_oob;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000623 if (master->_read_user_prot_reg)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200624 slave->_read_user_prot_reg = part_read_user_prot_reg;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000625 if (master->_read_fact_prot_reg)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200626 slave->_read_fact_prot_reg = part_read_fact_prot_reg;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000627 if (master->_write_user_prot_reg)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200628 slave->_write_user_prot_reg = part_write_user_prot_reg;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000629 if (master->_lock_user_prot_reg)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200630 slave->_lock_user_prot_reg = part_lock_user_prot_reg;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000631 if (master->_get_user_prot_info)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200632 slave->_get_user_prot_info = part_get_user_prot_info;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000633 if (master->_get_fact_prot_info)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200634 slave->_get_fact_prot_info = part_get_fact_prot_info;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000635 if (master->_sync)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200636 slave->_sync = part_sync;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200637#ifndef __UBOOT__
638 if (!partno && !master->dev.class && master->_suspend &&
639 master->_resume) {
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200640 slave->_suspend = part_suspend;
641 slave->_resume = part_resume;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200642 }
643 if (master->_writev)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200644 slave->_writev = part_writev;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200645#endif
Sergey Lapin3a38a552013-01-14 03:46:50 +0000646 if (master->_lock)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200647 slave->_lock = part_lock;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000648 if (master->_unlock)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200649 slave->_unlock = part_unlock;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200650 if (master->_is_locked)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200651 slave->_is_locked = part_is_locked;
Ezequiel Garciafc9d57c2014-05-21 19:06:12 -0300652 if (master->_block_isreserved)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200653 slave->_block_isreserved = part_block_isreserved;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000654 if (master->_block_isbad)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200655 slave->_block_isbad = part_block_isbad;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000656 if (master->_block_markbad)
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200657 slave->_block_markbad = part_block_markbad;
658 slave->_erase = part_erase;
659 slave->parent = master;
Stefan Roese586b3a62009-05-11 16:03:55 +0200660 slave->offset = part->offset;
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200661 INIT_LIST_HEAD(&slave->partitions);
662 INIT_LIST_HEAD(&slave->node);
Stefan Roese586b3a62009-05-11 16:03:55 +0200663
664 if (slave->offset == MTDPART_OFS_APPEND)
665 slave->offset = cur_offset;
666 if (slave->offset == MTDPART_OFS_NXTBLK) {
667 slave->offset = cur_offset;
668 if (mtd_mod_by_eb(cur_offset, master) != 0) {
669 /* Round up to next erasesize */
670 slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200671 debug("Moving partition %d: "
672 "0x%012llx -> 0x%012llx\n", partno,
673 (unsigned long long)cur_offset, (unsigned long long)slave->offset);
674 }
675 }
676 if (slave->offset == MTDPART_OFS_RETAIN) {
677 slave->offset = cur_offset;
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200678 if (master->size - slave->offset >= slave->size) {
679 slave->size = master->size - slave->offset
680 - slave->size;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200681 } else {
682 debug("mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
683 part->name, master->size - slave->offset,
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200684 slave->size);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200685 /* register to preserve ordering */
686 goto out_register;
Stefan Roese586b3a62009-05-11 16:03:55 +0200687 }
688 }
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200689 if (slave->size == MTDPART_SIZ_FULL)
690 slave->size = master->size - slave->offset;
Stefan Roese586b3a62009-05-11 16:03:55 +0200691
Heiko Schocherf5895d12014-06-24 10:10:04 +0200692 debug("0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200693 (unsigned long long)(slave->offset + slave->size), slave->name);
Stefan Roese586b3a62009-05-11 16:03:55 +0200694
695 /* let's do some sanity checks */
696 if (slave->offset >= master->size) {
697 /* let's register it anyway to preserve ordering */
698 slave->offset = 0;
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200699 slave->size = 0;
Stefan Roese586b3a62009-05-11 16:03:55 +0200700 printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
701 part->name);
702 goto out_register;
703 }
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200704 if (slave->offset + slave->size > master->size) {
705 slave->size = master->size - slave->offset;
Stefan Roese586b3a62009-05-11 16:03:55 +0200706 printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200707 part->name, master->name, slave->size);
Stefan Roese586b3a62009-05-11 16:03:55 +0200708 }
709 if (master->numeraseregions > 1) {
710 /* Deal with variable erase size stuff */
711 int i, max = master->numeraseregions;
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200712 u64 end = slave->offset + slave->size;
Stefan Roese586b3a62009-05-11 16:03:55 +0200713 struct mtd_erase_region_info *regions = master->eraseregions;
714
715 /* Find the first erase regions which is part of this
716 * partition. */
717 for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
718 ;
719 /* The loop searched for the region _behind_ the first one */
Heiko Schocherf5895d12014-06-24 10:10:04 +0200720 if (i > 0)
721 i--;
Stefan Roese586b3a62009-05-11 16:03:55 +0200722
723 /* Pick biggest erasesize */
724 for (; i < max && regions[i].offset < end; i++) {
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200725 if (slave->erasesize < regions[i].erasesize)
726 slave->erasesize = regions[i].erasesize;
Stefan Roese586b3a62009-05-11 16:03:55 +0200727 }
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200728 WARN_ON(slave->erasesize == 0);
Stefan Roese586b3a62009-05-11 16:03:55 +0200729 } else {
730 /* Single erase size */
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200731 slave->erasesize = master->erasesize;
Stefan Roese586b3a62009-05-11 16:03:55 +0200732 }
733
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200734 if ((slave->flags & MTD_WRITEABLE) &&
735 mtd_mod_by_eb(slave->offset, slave)) {
Stefan Roese586b3a62009-05-11 16:03:55 +0200736 /* Doesn't start on a boundary of major erase size */
737 /* FIXME: Let it be writable if it is on a boundary of
738 * _minor_ erase size though */
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200739 slave->flags &= ~MTD_WRITEABLE;
Stefan Roese586b3a62009-05-11 16:03:55 +0200740 printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
741 part->name);
742 }
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200743 if ((slave->flags & MTD_WRITEABLE) &&
744 mtd_mod_by_eb(slave->size, slave)) {
745 slave->flags &= ~MTD_WRITEABLE;
Stefan Roese586b3a62009-05-11 16:03:55 +0200746 printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
747 part->name);
748 }
749
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200750 slave->ecclayout = master->ecclayout;
751 slave->ecc_step_size = master->ecc_step_size;
752 slave->ecc_strength = master->ecc_strength;
753 slave->bitflip_threshold = master->bitflip_threshold;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200754
Sergey Lapin3a38a552013-01-14 03:46:50 +0000755 if (master->_block_isbad) {
Stefan Roese586b3a62009-05-11 16:03:55 +0200756 uint64_t offs = 0;
757
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200758 while (offs < slave->size) {
Sergey Lapin3a38a552013-01-14 03:46:50 +0000759 if (mtd_block_isbad(master, offs + slave->offset))
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200760 slave->ecc_stats.badblocks++;
761 offs += slave->erasesize;
Stefan Roese586b3a62009-05-11 16:03:55 +0200762 }
763 }
764
765out_register:
Stefan Roese586b3a62009-05-11 16:03:55 +0200766 return slave;
767}
768
Heiko Schocherb24c4272014-07-15 16:08:42 +0200769#ifndef __UBOOT__
Heiko Schocherf5895d12014-06-24 10:10:04 +0200770int mtd_add_partition(struct mtd_info *master, const char *name,
771 long long offset, long long length)
772{
773 struct mtd_partition part;
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200774 struct mtd_info *p, *new;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200775 uint64_t start, end;
776 int ret = 0;
777
778 /* the direct offset is expected */
779 if (offset == MTDPART_OFS_APPEND ||
780 offset == MTDPART_OFS_NXTBLK)
781 return -EINVAL;
782
783 if (length == MTDPART_SIZ_FULL)
784 length = master->size - offset;
785
786 if (length <= 0)
787 return -EINVAL;
788
789 part.name = name;
790 part.size = length;
791 part.offset = offset;
792 part.mask_flags = 0;
793 part.ecclayout = NULL;
794
795 new = allocate_partition(master, &part, -1, offset);
796 if (IS_ERR(new))
797 return PTR_ERR(new);
798
799 start = offset;
800 end = offset + length;
801
802 mutex_lock(&mtd_partitions_mutex);
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200803 list_for_each_entry(p, &master->partitions, node) {
804 if (start >= p->offset &&
805 (start < (p->offset + p->size)))
806 goto err_inv;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200807
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200808 if (end >= p->offset &&
809 (end < (p->offset + p->size)))
810 goto err_inv;
811 }
Heiko Schocherf5895d12014-06-24 10:10:04 +0200812
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200813 list_add_tail(&new->node, &master->partitions);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200814 mutex_unlock(&mtd_partitions_mutex);
815
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200816 add_mtd_device(new);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200817
818 return ret;
819err_inv:
820 mutex_unlock(&mtd_partitions_mutex);
821 free_partition(new);
822 return -EINVAL;
823}
824EXPORT_SYMBOL_GPL(mtd_add_partition);
825
826int mtd_del_partition(struct mtd_info *master, int partno)
827{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200828 struct mtd_info *slave, *next;
Heiko Schocherf5895d12014-06-24 10:10:04 +0200829 int ret = -EINVAL;
830
831 mutex_lock(&mtd_partitions_mutex);
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200832 list_for_each_entry_safe(slave, next, &master->partitions, node)
833 if (slave->index == partno) {
834 ret = del_mtd_device(slave);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200835 if (ret < 0)
836 break;
837
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200838 list_del(&slave->node);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200839 free_partition(slave);
840 break;
841 }
842 mutex_unlock(&mtd_partitions_mutex);
843
844 return ret;
845}
846EXPORT_SYMBOL_GPL(mtd_del_partition);
Heiko Schocherb24c4272014-07-15 16:08:42 +0200847#endif
Heiko Schocherf5895d12014-06-24 10:10:04 +0200848
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100849/*
850 * This function, given a master MTD object and a partition table, creates
851 * and registers slave MTD objects which are bound to the master according to
852 * the partition definitions.
Stefan Roese586b3a62009-05-11 16:03:55 +0200853 *
854 * We don't register the master, or expect the caller to have done so,
855 * for reasons of data integrity.
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100856 */
857
858int add_mtd_partitions(struct mtd_info *master,
859 const struct mtd_partition *parts,
860 int nbparts)
861{
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200862 struct mtd_info *slave;
Stefan Roese586b3a62009-05-11 16:03:55 +0200863 uint64_t cur_offset = 0;
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100864 int i;
865
Joe Hershberger47550fc2013-04-08 10:32:49 +0000866 debug("Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100867
868 for (i = 0; i < nbparts; i++) {
Heiko Schocherf5895d12014-06-24 10:10:04 +0200869 slave = allocate_partition(master, parts + i, i, cur_offset);
870 if (IS_ERR(slave))
871 return PTR_ERR(slave);
872
873 mutex_lock(&mtd_partitions_mutex);
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200874 list_add_tail(&slave->node, &master->partitions);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200875 mutex_unlock(&mtd_partitions_mutex);
876
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200877 add_mtd_device(slave);
Heiko Schocherf5895d12014-06-24 10:10:04 +0200878
Miquel Raynal6382e0c2018-09-29 12:58:27 +0200879 cur_offset = slave->offset + slave->size;
Kyungmin Parkf6d5e252008-11-19 16:20:36 +0100880 }
881
882 return 0;
883}
Heiko Schocherf5895d12014-06-24 10:10:04 +0200884
Marek Behún544a8c92021-05-26 14:08:19 +0200885#if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(OF_CONTROL)
886int add_mtd_partitions_of(struct mtd_info *master)
887{
888 ofnode parts, child;
889 int i = 0;
890
Patrice Chotardc1315f02022-03-21 09:13:37 +0100891 if (!master->dev && !ofnode_valid(master->flash_node))
Marek Behún544a8c92021-05-26 14:08:19 +0200892 return 0;
893
Patrice Chotardc1315f02022-03-21 09:13:37 +0100894 if (master->dev)
895 parts = ofnode_find_subnode(mtd_get_ofnode(master), "partitions");
896 else
897 parts = ofnode_find_subnode(master->flash_node, "partitions");
898
Simon Glass2e4938b2022-09-06 20:27:17 -0600899 if (!ofnode_valid(parts) || !ofnode_is_enabled(parts) ||
Marek Behún544a8c92021-05-26 14:08:19 +0200900 !ofnode_device_is_compatible(parts, "fixed-partitions"))
901 return 0;
902
903 ofnode_for_each_subnode(child, parts) {
904 struct mtd_partition part = { 0 };
905 struct mtd_info *slave;
Pali Rohár004ab832022-05-13 22:24:51 +0200906 fdt_addr_t offset;
907 fdt_size_t size;
Marek Behún544a8c92021-05-26 14:08:19 +0200908
Simon Glass2e4938b2022-09-06 20:27:17 -0600909 if (!ofnode_is_enabled(child))
Marek Behún544a8c92021-05-26 14:08:19 +0200910 continue;
911
912 offset = ofnode_get_addr_size_index_notrans(child, 0, &size);
913 if (offset == FDT_ADDR_T_NONE || !size) {
914 debug("Missing partition offset/size on \"%s\" partition\n",
915 master->name);
916 continue;
917 }
918
919 part.name = ofnode_read_string(child, "label");
920 if (!part.name)
921 part.name = ofnode_read_string(child, "name");
922
923 /*
924 * .mask_flags is used to remove flags in allocate_partition(),
925 * so when "read-only" is present, we add MTD_WRITABLE to the
926 * mask, and so MTD_WRITABLE will be removed on partition
927 * allocation
928 */
929 if (ofnode_read_bool(child, "read-only"))
930 part.mask_flags |= MTD_WRITEABLE;
931 if (ofnode_read_bool(child, "lock"))
932 part.mask_flags |= MTD_POWERUP_LOCK;
933
934 part.offset = offset;
935 part.size = size;
936 part.ecclayout = master->ecclayout;
937
938 slave = allocate_partition(master, &part, i++, 0);
939 if (IS_ERR(slave))
940 return PTR_ERR(slave);
941
942 mutex_lock(&mtd_partitions_mutex);
943 list_add_tail(&slave->node, &master->partitions);
944 mutex_unlock(&mtd_partitions_mutex);
945
946 add_mtd_device(slave);
947 }
948
949 return 0;
950}
951#endif /* CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(OF_CONTROL) */
952
Heiko Schocherf5895d12014-06-24 10:10:04 +0200953#ifndef __UBOOT__
954static DEFINE_SPINLOCK(part_parser_lock);
955static LIST_HEAD(part_parsers);
956
957static struct mtd_part_parser *get_partition_parser(const char *name)
958{
959 struct mtd_part_parser *p, *ret = NULL;
960
961 spin_lock(&part_parser_lock);
962
963 list_for_each_entry(p, &part_parsers, list)
964 if (!strcmp(p->name, name) && try_module_get(p->owner)) {
965 ret = p;
966 break;
967 }
968
969 spin_unlock(&part_parser_lock);
970
971 return ret;
972}
973
974#define put_partition_parser(p) do { module_put((p)->owner); } while (0)
975
976void register_mtd_parser(struct mtd_part_parser *p)
977{
978 spin_lock(&part_parser_lock);
979 list_add(&p->list, &part_parsers);
980 spin_unlock(&part_parser_lock);
981}
982EXPORT_SYMBOL_GPL(register_mtd_parser);
983
984void deregister_mtd_parser(struct mtd_part_parser *p)
985{
986 spin_lock(&part_parser_lock);
987 list_del(&p->list);
988 spin_unlock(&part_parser_lock);
989}
990EXPORT_SYMBOL_GPL(deregister_mtd_parser);
991
992/*
993 * Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you
994 * are changing this array!
995 */
996static const char * const default_mtd_part_types[] = {
997 "cmdlinepart",
998 "ofpart",
999 NULL
1000};
1001
1002/**
1003 * parse_mtd_partitions - parse MTD partitions
1004 * @master: the master partition (describes whole MTD device)
1005 * @types: names of partition parsers to try or %NULL
1006 * @pparts: array of partitions found is returned here
1007 * @data: MTD partition parser-specific data
1008 *
1009 * This function tries to find partition on MTD device @master. It uses MTD
1010 * partition parsers, specified in @types. However, if @types is %NULL, then
1011 * the default list of parsers is used. The default list contains only the
1012 * "cmdlinepart" and "ofpart" parsers ATM.
1013 * Note: If there are more then one parser in @types, the kernel only takes the
1014 * partitions parsed out by the first parser.
1015 *
1016 * This function may return:
1017 * o a negative error code in case of failure
1018 * o zero if no partitions were found
1019 * o a positive number of found partitions, in which case on exit @pparts will
1020 * point to an array containing this number of &struct mtd_info objects.
1021 */
1022int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
1023 struct mtd_partition **pparts,
1024 struct mtd_part_parser_data *data)
1025{
1026 struct mtd_part_parser *parser;
1027 int ret = 0;
1028
1029 if (!types)
1030 types = default_mtd_part_types;
1031
1032 for ( ; ret <= 0 && *types; types++) {
1033 parser = get_partition_parser(*types);
1034 if (!parser && !request_module("%s", *types))
1035 parser = get_partition_parser(*types);
1036 if (!parser)
1037 continue;
1038 ret = (*parser->parse_fn)(master, pparts, data);
1039 put_partition_parser(parser);
1040 if (ret > 0) {
1041 printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
1042 ret, parser->name, master->name);
1043 break;
1044 }
1045 }
1046 return ret;
1047}
1048#endif
1049
Heiko Schocherf5895d12014-06-24 10:10:04 +02001050/* Returns the size of the entire flash chip */
1051uint64_t mtd_get_device_size(const struct mtd_info *mtd)
1052{
Miquel Raynal6382e0c2018-09-29 12:58:27 +02001053 if (mtd_is_partition(mtd))
1054 return mtd->parent->size;
Heiko Schocherf5895d12014-06-24 10:10:04 +02001055
Miquel Raynal6382e0c2018-09-29 12:58:27 +02001056 return mtd->size;
Heiko Schocherf5895d12014-06-24 10:10:04 +02001057}
1058EXPORT_SYMBOL_GPL(mtd_get_device_size);
Alexey Romanovd5119672024-07-18 08:46:04 +03001059
1060static struct mtd_info *mtd_get_partition_by_index(struct mtd_info *mtd, int index)
1061{
1062 struct mtd_info *part;
1063 int i = 0;
1064
1065 list_for_each_entry(part, &mtd->partitions, node)
1066 if (i++ == index)
1067 return part;
1068
1069 debug("Partition with idx=%d not found on MTD device %s\n", index, mtd->name);
1070 return NULL;
1071}
1072
1073static int __maybe_unused part_get_info_mtd(struct blk_desc *dev_desc, int part_idx,
1074 struct disk_partition *info)
1075{
1076 struct mtd_info *master = blk_desc_to_mtd(dev_desc);
1077 struct mtd_info *part;
1078
1079 if (!master) {
1080 debug("MTD device is NULL\n");
1081 return -EINVAL;
1082 }
1083
1084 part = mtd_get_partition_by_index(master, part_idx);
1085 if (!part) {
1086 debug("Failed to find partition with idx=%d\n", part_idx);
1087 return -EINVAL;
1088 }
1089
1090 snprintf(info->name, PART_NAME_LEN, part->name);
1091 info->start = part->offset / dev_desc->blksz;
1092 info->size = part->size / dev_desc->blksz;
1093 info->blksz = dev_desc->blksz;
1094
1095 return 0;
1096}
1097
1098static void __maybe_unused part_print_mtd(struct blk_desc *dev_desc)
1099{
1100 struct mtd_info *master = blk_desc_to_mtd(dev_desc);
1101 struct mtd_info *part;
1102
1103 if (!master)
1104 return;
1105
1106 list_for_each_entry(part, &master->partitions, node)
1107 printf("- 0x%012llx-0x%012llx : \"%s\"\n",
1108 part->offset, part->offset + part->size, part->name);
1109}
1110
1111static int part_test_mtd(struct blk_desc *dev_desc)
1112{
1113 struct mtd_info *master = blk_desc_to_mtd(dev_desc);
1114 ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
1115
1116 if (!master)
1117 return -1;
1118
1119 if (blk_dread(dev_desc, 0, 1, (ulong *)buffer) != 1)
1120 return -1;
1121
1122 return 0;
1123}
1124
1125U_BOOT_PART_TYPE(mtd) = {
1126 .name = "MTD",
1127 .part_type = PART_TYPE_MTD,
1128 .max_entries = MTD_ENTRY_NUMBERS,
1129 .get_info = part_get_info_ptr(part_get_info_mtd),
1130 .print = part_print_ptr(part_print_mtd),
1131 .test = part_test_mtd,
1132};