1fd534e9bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Core registration and callback routines for MTD
41da177e4SLinus Torvalds * drivers and users.
51da177e4SLinus Torvalds *
6a1452a37SDavid Woodhouse * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
7a1452a37SDavid Woodhouse * Copyright © 2006 Red Hat UK Limited
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds
101da177e4SLinus Torvalds #include <linux/module.h>
111da177e4SLinus Torvalds #include <linux/kernel.h>
121da177e4SLinus Torvalds #include <linux/ptrace.h>
13447d9bd8SAlexey Dobriyan #include <linux/seq_file.h>
141da177e4SLinus Torvalds #include <linux/string.h>
151da177e4SLinus Torvalds #include <linux/timer.h>
161da177e4SLinus Torvalds #include <linux/major.h>
171da177e4SLinus Torvalds #include <linux/fs.h>
187799308fSArtem Bityutskiy #include <linux/err.h>
191da177e4SLinus Torvalds #include <linux/ioctl.h>
201da177e4SLinus Torvalds #include <linux/init.h>
21215a02fdSBrian Norris #include <linux/of.h>
221da177e4SLinus Torvalds #include <linux/proc_fs.h>
23b520e412SBen Hutchings #include <linux/idr.h>
24a33eb6b9SJörn Engel #include <linux/backing-dev.h>
2505d71b46STejun Heo #include <linux/gfp.h>
263b270facSLinus Walleij #include <linux/random.h>
270d01ff25SDavid Howells #include <linux/slab.h>
283efe41beSBrian Norris #include <linux/reboot.h>
29fea728c0SEzequiel Garcia #include <linux/leds.h>
30e8e3edb9SMario Rugiero #include <linux/debugfs.h>
31c4dfa25aSAlban Bedel #include <linux/nvmem-provider.h>
3226422ac7SRafał Miłecki #include <linux/root_dev.h>
331da177e4SLinus Torvalds
341da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
35f5671ab3SJamie Iles #include <linux/mtd/partitions.h>
361da177e4SLinus Torvalds
37356d70f1SBen Dooks #include "mtdcore.h"
38660685d9SArtem Bityutskiy
39fa06052dSJan Kara struct backing_dev_info *mtd_bdi;
40356d70f1SBen Dooks
4157b8045dSLars-Peter Clausen #ifdef CONFIG_PM_SLEEP
4257b8045dSLars-Peter Clausen
mtd_cls_suspend(struct device * dev)4357b8045dSLars-Peter Clausen static int mtd_cls_suspend(struct device *dev)
4457b8045dSLars-Peter Clausen {
4557b8045dSLars-Peter Clausen struct mtd_info *mtd = dev_get_drvdata(dev);
4657b8045dSLars-Peter Clausen
4757b8045dSLars-Peter Clausen return mtd ? mtd_suspend(mtd) : 0;
4857b8045dSLars-Peter Clausen }
4957b8045dSLars-Peter Clausen
mtd_cls_resume(struct device * dev)5057b8045dSLars-Peter Clausen static int mtd_cls_resume(struct device *dev)
5157b8045dSLars-Peter Clausen {
5257b8045dSLars-Peter Clausen struct mtd_info *mtd = dev_get_drvdata(dev);
5357b8045dSLars-Peter Clausen
5457b8045dSLars-Peter Clausen if (mtd)
5557b8045dSLars-Peter Clausen mtd_resume(mtd);
5657b8045dSLars-Peter Clausen return 0;
5757b8045dSLars-Peter Clausen }
5857b8045dSLars-Peter Clausen
5957b8045dSLars-Peter Clausen static SIMPLE_DEV_PM_OPS(mtd_cls_pm_ops, mtd_cls_suspend, mtd_cls_resume);
6057b8045dSLars-Peter Clausen #define MTD_CLS_PM_OPS (&mtd_cls_pm_ops)
6157b8045dSLars-Peter Clausen #else
6257b8045dSLars-Peter Clausen #define MTD_CLS_PM_OPS NULL
6357b8045dSLars-Peter Clausen #endif
641f24b5a8SDavid Brownell
6515bce40cSDavid Woodhouse static struct class mtd_class = {
6615bce40cSDavid Woodhouse .name = "mtd",
6757b8045dSLars-Peter Clausen .pm = MTD_CLS_PM_OPS,
6815bce40cSDavid Woodhouse };
691f24b5a8SDavid Brownell
70b520e412SBen Hutchings static DEFINE_IDR(mtd_idr);
71b520e412SBen Hutchings
721da177e4SLinus Torvalds /* These are exported solely for the purpose of mtd_blkdevs.c. You
731da177e4SLinus Torvalds should not use them for _anything_ else */
7448b19268SIngo Molnar DEFINE_MUTEX(mtd_table_mutex);
751da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(mtd_table_mutex);
76b520e412SBen Hutchings
__mtd_next_device(int i)77b520e412SBen Hutchings struct mtd_info *__mtd_next_device(int i)
78b520e412SBen Hutchings {
79b520e412SBen Hutchings return idr_get_next(&mtd_idr, &i);
80b520e412SBen Hutchings }
81b520e412SBen Hutchings EXPORT_SYMBOL_GPL(__mtd_next_device);
821da177e4SLinus Torvalds
831da177e4SLinus Torvalds static LIST_HEAD(mtd_notifiers);
841da177e4SLinus Torvalds
851f24b5a8SDavid Brownell
861f24b5a8SDavid Brownell #define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2)
871f24b5a8SDavid Brownell
881f24b5a8SDavid Brownell /* REVISIT once MTD uses the driver model better, whoever allocates
891f24b5a8SDavid Brownell * the mtd_info will probably want to use the release() hook...
901f24b5a8SDavid Brownell */
mtd_release(struct device * dev)911f24b5a8SDavid Brownell static void mtd_release(struct device *dev)
921f24b5a8SDavid Brownell {
935e472128SBrian Norris struct mtd_info *mtd = dev_get_drvdata(dev);
94d5de20a9SArtem Bityutskiy dev_t index = MTD_DEVT(mtd->index);
951f24b5a8SDavid Brownell
96e9714c22SAlexander Usyskin idr_remove(&mtd_idr, mtd->index);
97e9714c22SAlexander Usyskin of_node_put(mtd_get_of_node(mtd));
98e9714c22SAlexander Usyskin
9919bfa9ebSTomas Winkler if (mtd_is_partition(mtd))
10019bfa9ebSTomas Winkler release_mtd_partition(mtd);
10119bfa9ebSTomas Winkler
1025e472128SBrian Norris /* remove /dev/mtdXro node */
10315bce40cSDavid Woodhouse device_destroy(&mtd_class, index + 1);
10415bce40cSDavid Woodhouse }
10515bce40cSDavid Woodhouse
mtd_device_release(struct kref * kref)10619bfa9ebSTomas Winkler static void mtd_device_release(struct kref *kref)
10719bfa9ebSTomas Winkler {
10819bfa9ebSTomas Winkler struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt);
109e9714c22SAlexander Usyskin bool is_partition = mtd_is_partition(mtd);
11019bfa9ebSTomas Winkler
11119bfa9ebSTomas Winkler debugfs_remove_recursive(mtd->dbg.dfs_dir);
11219bfa9ebSTomas Winkler
11319bfa9ebSTomas Winkler /* Try to remove the NVMEM provider */
11419bfa9ebSTomas Winkler nvmem_unregister(mtd->nvmem);
11519bfa9ebSTomas Winkler
11619bfa9ebSTomas Winkler device_unregister(&mtd->dev);
11719bfa9ebSTomas Winkler
118e9714c22SAlexander Usyskin /*
119e9714c22SAlexander Usyskin * Clear dev so mtd can be safely re-registered later if desired.
120e9714c22SAlexander Usyskin * Should not be done for partition,
121e9714c22SAlexander Usyskin * as it was already destroyed in device_unregister().
122e9714c22SAlexander Usyskin */
123e9714c22SAlexander Usyskin if (!is_partition)
12419bfa9ebSTomas Winkler memset(&mtd->dev, 0, sizeof(mtd->dev));
12519bfa9ebSTomas Winkler
12619bfa9ebSTomas Winkler module_put(THIS_MODULE);
12719bfa9ebSTomas Winkler }
12819bfa9ebSTomas Winkler
129b4e24863SZhen Lei #define MTD_DEVICE_ATTR_RO(name) \
130b4e24863SZhen Lei static DEVICE_ATTR(name, 0444, mtd_##name##_show, NULL)
131b4e24863SZhen Lei
132b4e24863SZhen Lei #define MTD_DEVICE_ATTR_RW(name) \
133b4e24863SZhen Lei static DEVICE_ATTR(name, 0644, mtd_##name##_show, mtd_##name##_store)
134b4e24863SZhen Lei
mtd_type_show(struct device * dev,struct device_attribute * attr,char * buf)1351f24b5a8SDavid Brownell static ssize_t mtd_type_show(struct device *dev,
1361f24b5a8SDavid Brownell struct device_attribute *attr, char *buf)
1371f24b5a8SDavid Brownell {
138d5de20a9SArtem Bityutskiy struct mtd_info *mtd = dev_get_drvdata(dev);
1391f24b5a8SDavid Brownell char *type;
1401f24b5a8SDavid Brownell
1411f24b5a8SDavid Brownell switch (mtd->type) {
1421f24b5a8SDavid Brownell case MTD_ABSENT:
1431f24b5a8SDavid Brownell type = "absent";
1441f24b5a8SDavid Brownell break;
1451f24b5a8SDavid Brownell case MTD_RAM:
1461f24b5a8SDavid Brownell type = "ram";
1471f24b5a8SDavid Brownell break;
1481f24b5a8SDavid Brownell case MTD_ROM:
1491f24b5a8SDavid Brownell type = "rom";
1501f24b5a8SDavid Brownell break;
1511f24b5a8SDavid Brownell case MTD_NORFLASH:
1521f24b5a8SDavid Brownell type = "nor";
1531f24b5a8SDavid Brownell break;
1541f24b5a8SDavid Brownell case MTD_NANDFLASH:
1551f24b5a8SDavid Brownell type = "nand";
1561f24b5a8SDavid Brownell break;
1571f24b5a8SDavid Brownell case MTD_DATAFLASH:
1581f24b5a8SDavid Brownell type = "dataflash";
1591f24b5a8SDavid Brownell break;
1601f24b5a8SDavid Brownell case MTD_UBIVOLUME:
1611f24b5a8SDavid Brownell type = "ubi";
1621f24b5a8SDavid Brownell break;
163f4837246SHuang Shijie case MTD_MLCNANDFLASH:
164f4837246SHuang Shijie type = "mlc-nand";
165f4837246SHuang Shijie break;
1661f24b5a8SDavid Brownell default:
1671f24b5a8SDavid Brownell type = "unknown";
1681f24b5a8SDavid Brownell }
1691f24b5a8SDavid Brownell
1705b2fbe0cSTian Tao return sysfs_emit(buf, "%s\n", type);
1711f24b5a8SDavid Brownell }
172a17da115SZhen Lei MTD_DEVICE_ATTR_RO(type);
173694bb7fcSKevin Cernekee
mtd_flags_show(struct device * dev,struct device_attribute * attr,char * buf)174694bb7fcSKevin Cernekee static ssize_t mtd_flags_show(struct device *dev,
175694bb7fcSKevin Cernekee struct device_attribute *attr, char *buf)
176694bb7fcSKevin Cernekee {
177d5de20a9SArtem Bityutskiy struct mtd_info *mtd = dev_get_drvdata(dev);
178694bb7fcSKevin Cernekee
1795b2fbe0cSTian Tao return sysfs_emit(buf, "0x%lx\n", (unsigned long)mtd->flags);
180694bb7fcSKevin Cernekee }
181a17da115SZhen Lei MTD_DEVICE_ATTR_RO(flags);
182694bb7fcSKevin Cernekee
mtd_size_show(struct device * dev,struct device_attribute * attr,char * buf)183694bb7fcSKevin Cernekee static ssize_t mtd_size_show(struct device *dev,
184694bb7fcSKevin Cernekee struct device_attribute *attr, char *buf)
185694bb7fcSKevin Cernekee {
186d5de20a9SArtem Bityutskiy struct mtd_info *mtd = dev_get_drvdata(dev);
187694bb7fcSKevin Cernekee
1885b2fbe0cSTian Tao return sysfs_emit(buf, "%llu\n", (unsigned long long)mtd->size);
189694bb7fcSKevin Cernekee }
190a17da115SZhen Lei MTD_DEVICE_ATTR_RO(size);
191694bb7fcSKevin Cernekee
mtd_erasesize_show(struct device * dev,struct device_attribute * attr,char * buf)192694bb7fcSKevin Cernekee static ssize_t mtd_erasesize_show(struct device *dev,
193694bb7fcSKevin Cernekee struct device_attribute *attr, char *buf)
194694bb7fcSKevin Cernekee {
195d5de20a9SArtem Bityutskiy struct mtd_info *mtd = dev_get_drvdata(dev);
196694bb7fcSKevin Cernekee
1975b2fbe0cSTian Tao return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->erasesize);
198694bb7fcSKevin Cernekee }
199a17da115SZhen Lei MTD_DEVICE_ATTR_RO(erasesize);
200694bb7fcSKevin Cernekee
mtd_writesize_show(struct device * dev,struct device_attribute * attr,char * buf)201694bb7fcSKevin Cernekee static ssize_t mtd_writesize_show(struct device *dev,
202694bb7fcSKevin Cernekee struct device_attribute *attr, char *buf)
203694bb7fcSKevin Cernekee {
204d5de20a9SArtem Bityutskiy struct mtd_info *mtd = dev_get_drvdata(dev);
205694bb7fcSKevin Cernekee
2065b2fbe0cSTian Tao return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->writesize);
207694bb7fcSKevin Cernekee }
208a17da115SZhen Lei MTD_DEVICE_ATTR_RO(writesize);
209694bb7fcSKevin Cernekee
mtd_subpagesize_show(struct device * dev,struct device_attribute * attr,char * buf)210e7693548SArtem Bityutskiy static ssize_t mtd_subpagesize_show(struct device *dev,
211e7693548SArtem Bityutskiy struct device_attribute *attr, char *buf)
212e7693548SArtem Bityutskiy {
213d5de20a9SArtem Bityutskiy struct mtd_info *mtd = dev_get_drvdata(dev);
214e7693548SArtem Bityutskiy unsigned int subpagesize = mtd->writesize >> mtd->subpage_sft;
215e7693548SArtem Bityutskiy
2165b2fbe0cSTian Tao return sysfs_emit(buf, "%u\n", subpagesize);
217e7693548SArtem Bityutskiy }
218a17da115SZhen Lei MTD_DEVICE_ATTR_RO(subpagesize);
219e7693548SArtem Bityutskiy
mtd_oobsize_show(struct device * dev,struct device_attribute * attr,char * buf)220694bb7fcSKevin Cernekee static ssize_t mtd_oobsize_show(struct device *dev,
221694bb7fcSKevin Cernekee struct device_attribute *attr, char *buf)
222694bb7fcSKevin Cernekee {
223d5de20a9SArtem Bityutskiy struct mtd_info *mtd = dev_get_drvdata(dev);
224694bb7fcSKevin Cernekee
2255b2fbe0cSTian Tao return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->oobsize);
226694bb7fcSKevin Cernekee }
227a17da115SZhen Lei MTD_DEVICE_ATTR_RO(oobsize);
228694bb7fcSKevin Cernekee
mtd_oobavail_show(struct device * dev,struct device_attribute * attr,char * buf)2297cc9aa66SXiaolei Li static ssize_t mtd_oobavail_show(struct device *dev,
2307cc9aa66SXiaolei Li struct device_attribute *attr, char *buf)
2317cc9aa66SXiaolei Li {
2327cc9aa66SXiaolei Li struct mtd_info *mtd = dev_get_drvdata(dev);
2337cc9aa66SXiaolei Li
2345b2fbe0cSTian Tao return sysfs_emit(buf, "%u\n", mtd->oobavail);
2357cc9aa66SXiaolei Li }
236a17da115SZhen Lei MTD_DEVICE_ATTR_RO(oobavail);
2377cc9aa66SXiaolei Li
mtd_numeraseregions_show(struct device * dev,struct device_attribute * attr,char * buf)238694bb7fcSKevin Cernekee static ssize_t mtd_numeraseregions_show(struct device *dev,
239694bb7fcSKevin Cernekee struct device_attribute *attr, char *buf)
240694bb7fcSKevin Cernekee {
241d5de20a9SArtem Bityutskiy struct mtd_info *mtd = dev_get_drvdata(dev);
242694bb7fcSKevin Cernekee
2435b2fbe0cSTian Tao return sysfs_emit(buf, "%u\n", mtd->numeraseregions);
244694bb7fcSKevin Cernekee }
245a17da115SZhen Lei MTD_DEVICE_ATTR_RO(numeraseregions);
246694bb7fcSKevin Cernekee
mtd_name_show(struct device * dev,struct device_attribute * attr,char * buf)247694bb7fcSKevin Cernekee static ssize_t mtd_name_show(struct device *dev,
248694bb7fcSKevin Cernekee struct device_attribute *attr, char *buf)
249694bb7fcSKevin Cernekee {
250d5de20a9SArtem Bityutskiy struct mtd_info *mtd = dev_get_drvdata(dev);
251694bb7fcSKevin Cernekee
2525b2fbe0cSTian Tao return sysfs_emit(buf, "%s\n", mtd->name);
253694bb7fcSKevin Cernekee }
254a17da115SZhen Lei MTD_DEVICE_ATTR_RO(name);
2551f24b5a8SDavid Brownell
mtd_ecc_strength_show(struct device * dev,struct device_attribute * attr,char * buf)256a9b672e8SMike Dunn static ssize_t mtd_ecc_strength_show(struct device *dev,
257a9b672e8SMike Dunn struct device_attribute *attr, char *buf)
258a9b672e8SMike Dunn {
259a9b672e8SMike Dunn struct mtd_info *mtd = dev_get_drvdata(dev);
260a9b672e8SMike Dunn
2615b2fbe0cSTian Tao return sysfs_emit(buf, "%u\n", mtd->ecc_strength);
262a9b672e8SMike Dunn }
263a17da115SZhen Lei MTD_DEVICE_ATTR_RO(ecc_strength);
264a9b672e8SMike Dunn
mtd_bitflip_threshold_show(struct device * dev,struct device_attribute * attr,char * buf)265d062d4edSMike Dunn static ssize_t mtd_bitflip_threshold_show(struct device *dev,
266d062d4edSMike Dunn struct device_attribute *attr,
267d062d4edSMike Dunn char *buf)
268d062d4edSMike Dunn {
269d062d4edSMike Dunn struct mtd_info *mtd = dev_get_drvdata(dev);
270d062d4edSMike Dunn
2715b2fbe0cSTian Tao return sysfs_emit(buf, "%u\n", mtd->bitflip_threshold);
272d062d4edSMike Dunn }
273d062d4edSMike Dunn
mtd_bitflip_threshold_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)274d062d4edSMike Dunn static ssize_t mtd_bitflip_threshold_store(struct device *dev,
275d062d4edSMike Dunn struct device_attribute *attr,
276d062d4edSMike Dunn const char *buf, size_t count)
277d062d4edSMike Dunn {
278d062d4edSMike Dunn struct mtd_info *mtd = dev_get_drvdata(dev);
279d062d4edSMike Dunn unsigned int bitflip_threshold;
280d062d4edSMike Dunn int retval;
281d062d4edSMike Dunn
282d062d4edSMike Dunn retval = kstrtouint(buf, 0, &bitflip_threshold);
283d062d4edSMike Dunn if (retval)
284d062d4edSMike Dunn return retval;
285d062d4edSMike Dunn
286d062d4edSMike Dunn mtd->bitflip_threshold = bitflip_threshold;
287d062d4edSMike Dunn return count;
288d062d4edSMike Dunn }
289a17da115SZhen Lei MTD_DEVICE_ATTR_RW(bitflip_threshold);
290d062d4edSMike Dunn
mtd_ecc_step_size_show(struct device * dev,struct device_attribute * attr,char * buf)291bf977e3fSHuang Shijie static ssize_t mtd_ecc_step_size_show(struct device *dev,
292bf977e3fSHuang Shijie struct device_attribute *attr, char *buf)
293bf977e3fSHuang Shijie {
294bf977e3fSHuang Shijie struct mtd_info *mtd = dev_get_drvdata(dev);
295bf977e3fSHuang Shijie
2965b2fbe0cSTian Tao return sysfs_emit(buf, "%u\n", mtd->ecc_step_size);
297bf977e3fSHuang Shijie
298bf977e3fSHuang Shijie }
299a17da115SZhen Lei MTD_DEVICE_ATTR_RO(ecc_step_size);
300bf977e3fSHuang Shijie
mtd_corrected_bits_show(struct device * dev,struct device_attribute * attr,char * buf)301a17da115SZhen Lei static ssize_t mtd_corrected_bits_show(struct device *dev,
302990a3af0SEzequiel Garcia struct device_attribute *attr, char *buf)
303990a3af0SEzequiel Garcia {
304990a3af0SEzequiel Garcia struct mtd_info *mtd = dev_get_drvdata(dev);
305990a3af0SEzequiel Garcia struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats;
306990a3af0SEzequiel Garcia
3075b2fbe0cSTian Tao return sysfs_emit(buf, "%u\n", ecc_stats->corrected);
308990a3af0SEzequiel Garcia }
309a17da115SZhen Lei MTD_DEVICE_ATTR_RO(corrected_bits); /* ecc stats corrected */
310990a3af0SEzequiel Garcia
mtd_ecc_failures_show(struct device * dev,struct device_attribute * attr,char * buf)311a17da115SZhen Lei static ssize_t mtd_ecc_failures_show(struct device *dev,
312990a3af0SEzequiel Garcia struct device_attribute *attr, char *buf)
313990a3af0SEzequiel Garcia {
314990a3af0SEzequiel Garcia struct mtd_info *mtd = dev_get_drvdata(dev);
315990a3af0SEzequiel Garcia struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats;
316990a3af0SEzequiel Garcia
3175b2fbe0cSTian Tao return sysfs_emit(buf, "%u\n", ecc_stats->failed);
318990a3af0SEzequiel Garcia }
319a17da115SZhen Lei MTD_DEVICE_ATTR_RO(ecc_failures); /* ecc stats errors */
320990a3af0SEzequiel Garcia
mtd_bad_blocks_show(struct device * dev,struct device_attribute * attr,char * buf)321a17da115SZhen Lei static ssize_t mtd_bad_blocks_show(struct device *dev,
322990a3af0SEzequiel Garcia struct device_attribute *attr, char *buf)
323990a3af0SEzequiel Garcia {
324990a3af0SEzequiel Garcia struct mtd_info *mtd = dev_get_drvdata(dev);
325990a3af0SEzequiel Garcia struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats;
326990a3af0SEzequiel Garcia
3275b2fbe0cSTian Tao return sysfs_emit(buf, "%u\n", ecc_stats->badblocks);
328990a3af0SEzequiel Garcia }
329a17da115SZhen Lei MTD_DEVICE_ATTR_RO(bad_blocks);
330990a3af0SEzequiel Garcia
mtd_bbt_blocks_show(struct device * dev,struct device_attribute * attr,char * buf)331a17da115SZhen Lei static ssize_t mtd_bbt_blocks_show(struct device *dev,
332990a3af0SEzequiel Garcia struct device_attribute *attr, char *buf)
333990a3af0SEzequiel Garcia {
334990a3af0SEzequiel Garcia struct mtd_info *mtd = dev_get_drvdata(dev);
335990a3af0SEzequiel Garcia struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats;
336990a3af0SEzequiel Garcia
3375b2fbe0cSTian Tao return sysfs_emit(buf, "%u\n", ecc_stats->bbtblocks);
338990a3af0SEzequiel Garcia }
339a17da115SZhen Lei MTD_DEVICE_ATTR_RO(bbt_blocks);
340990a3af0SEzequiel Garcia
3411f24b5a8SDavid Brownell static struct attribute *mtd_attrs[] = {
342694bb7fcSKevin Cernekee &dev_attr_type.attr,
343694bb7fcSKevin Cernekee &dev_attr_flags.attr,
344694bb7fcSKevin Cernekee &dev_attr_size.attr,
345694bb7fcSKevin Cernekee &dev_attr_erasesize.attr,
346694bb7fcSKevin Cernekee &dev_attr_writesize.attr,
347e7693548SArtem Bityutskiy &dev_attr_subpagesize.attr,
348694bb7fcSKevin Cernekee &dev_attr_oobsize.attr,
3497cc9aa66SXiaolei Li &dev_attr_oobavail.attr,
350694bb7fcSKevin Cernekee &dev_attr_numeraseregions.attr,
351694bb7fcSKevin Cernekee &dev_attr_name.attr,
352a9b672e8SMike Dunn &dev_attr_ecc_strength.attr,
353bf977e3fSHuang Shijie &dev_attr_ecc_step_size.attr,
354990a3af0SEzequiel Garcia &dev_attr_corrected_bits.attr,
355990a3af0SEzequiel Garcia &dev_attr_ecc_failures.attr,
356990a3af0SEzequiel Garcia &dev_attr_bad_blocks.attr,
357990a3af0SEzequiel Garcia &dev_attr_bbt_blocks.attr,
358d062d4edSMike Dunn &dev_attr_bitflip_threshold.attr,
3591f24b5a8SDavid Brownell NULL,
3601f24b5a8SDavid Brownell };
36154c738f6SAxel Lin ATTRIBUTE_GROUPS(mtd);
3621f24b5a8SDavid Brownell
36375864b30SBhumika Goyal static const struct device_type mtd_devtype = {
3641f24b5a8SDavid Brownell .name = "mtd",
3651f24b5a8SDavid Brownell .groups = mtd_groups,
3661f24b5a8SDavid Brownell .release = mtd_release,
3671f24b5a8SDavid Brownell };
3681f24b5a8SDavid Brownell
369ad5e35f5SMiquel Raynal static bool mtd_expert_analysis_mode;
370ad5e35f5SMiquel Raynal
371ad5e35f5SMiquel Raynal #ifdef CONFIG_DEBUG_FS
mtd_check_expert_analysis_mode(void)372ad5e35f5SMiquel Raynal bool mtd_check_expert_analysis_mode(void)
3731018c94bSZhuohao Lee {
374ad5e35f5SMiquel Raynal const char *mtd_expert_analysis_warning =
375ad5e35f5SMiquel Raynal "Bad block checks have been entirely disabled.\n"
376ad5e35f5SMiquel Raynal "This is only reserved for post-mortem forensics and debug purposes.\n"
377ad5e35f5SMiquel Raynal "Never enable this mode if you do not know what you are doing!\n";
3781018c94bSZhuohao Lee
379ad5e35f5SMiquel Raynal return WARN_ONCE(mtd_expert_analysis_mode, mtd_expert_analysis_warning);
3801018c94bSZhuohao Lee }
381ad5e35f5SMiquel Raynal EXPORT_SYMBOL_GPL(mtd_check_expert_analysis_mode);
382ad5e35f5SMiquel Raynal #endif
3831018c94bSZhuohao Lee
3841018c94bSZhuohao Lee static struct dentry *dfs_dir_mtd;
3851018c94bSZhuohao Lee
mtd_debugfs_populate(struct mtd_info * mtd)3861018c94bSZhuohao Lee static void mtd_debugfs_populate(struct mtd_info *mtd)
3871018c94bSZhuohao Lee {
3881018c94bSZhuohao Lee struct device *dev = &mtd->dev;
3891018c94bSZhuohao Lee
3901018c94bSZhuohao Lee if (IS_ERR_OR_NULL(dfs_dir_mtd))
3911018c94bSZhuohao Lee return;
3921018c94bSZhuohao Lee
393ec090a03STudor Ambarus mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(dev), dfs_dir_mtd);
3941018c94bSZhuohao Lee }
3951018c94bSZhuohao Lee
396b4caecd4SChristoph Hellwig #ifndef CONFIG_MMU
mtd_mmap_capabilities(struct mtd_info * mtd)397b4caecd4SChristoph Hellwig unsigned mtd_mmap_capabilities(struct mtd_info *mtd)
398b4caecd4SChristoph Hellwig {
399b4caecd4SChristoph Hellwig switch (mtd->type) {
400b4caecd4SChristoph Hellwig case MTD_RAM:
401b4caecd4SChristoph Hellwig return NOMMU_MAP_COPY | NOMMU_MAP_DIRECT | NOMMU_MAP_EXEC |
402b4caecd4SChristoph Hellwig NOMMU_MAP_READ | NOMMU_MAP_WRITE;
403b4caecd4SChristoph Hellwig case MTD_ROM:
404b4caecd4SChristoph Hellwig return NOMMU_MAP_COPY | NOMMU_MAP_DIRECT | NOMMU_MAP_EXEC |
405b4caecd4SChristoph Hellwig NOMMU_MAP_READ;
406b4caecd4SChristoph Hellwig default:
407b4caecd4SChristoph Hellwig return NOMMU_MAP_COPY;
408b4caecd4SChristoph Hellwig }
409b4caecd4SChristoph Hellwig }
410706a4e5aSArnd Bergmann EXPORT_SYMBOL_GPL(mtd_mmap_capabilities);
411b4caecd4SChristoph Hellwig #endif
412b4caecd4SChristoph Hellwig
mtd_reboot_notifier(struct notifier_block * n,unsigned long state,void * cmd)4133efe41beSBrian Norris static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state,
4143efe41beSBrian Norris void *cmd)
4153efe41beSBrian Norris {
4163efe41beSBrian Norris struct mtd_info *mtd;
4173efe41beSBrian Norris
4183efe41beSBrian Norris mtd = container_of(n, struct mtd_info, reboot_notifier);
4193efe41beSBrian Norris mtd->_reboot(mtd);
4203efe41beSBrian Norris
4213efe41beSBrian Norris return NOTIFY_DONE;
4223efe41beSBrian Norris }
4233efe41beSBrian Norris
4241da177e4SLinus Torvalds /**
425477b0229SBoris Brezillon * mtd_wunit_to_pairing_info - get pairing information of a wunit
426477b0229SBoris Brezillon * @mtd: pointer to new MTD device info structure
427477b0229SBoris Brezillon * @wunit: write unit we are interested in
428477b0229SBoris Brezillon * @info: returned pairing information
429477b0229SBoris Brezillon *
430477b0229SBoris Brezillon * Retrieve pairing information associated to the wunit.
431477b0229SBoris Brezillon * This is mainly useful when dealing with MLC/TLC NANDs where pages can be
432477b0229SBoris Brezillon * paired together, and where programming a page may influence the page it is
433477b0229SBoris Brezillon * paired with.
434477b0229SBoris Brezillon * The notion of page is replaced by the term wunit (write-unit) to stay
435477b0229SBoris Brezillon * consistent with the ->writesize field.
436477b0229SBoris Brezillon *
437477b0229SBoris Brezillon * The @wunit argument can be extracted from an absolute offset using
438477b0229SBoris Brezillon * mtd_offset_to_wunit(). @info is filled with the pairing information attached
439477b0229SBoris Brezillon * to @wunit.
440477b0229SBoris Brezillon *
441477b0229SBoris Brezillon * From the pairing info the MTD user can find all the wunits paired with
442477b0229SBoris Brezillon * @wunit using the following loop:
443477b0229SBoris Brezillon *
444477b0229SBoris Brezillon * for (i = 0; i < mtd_pairing_groups(mtd); i++) {
445477b0229SBoris Brezillon * info.pair = i;
446477b0229SBoris Brezillon * mtd_pairing_info_to_wunit(mtd, &info);
447477b0229SBoris Brezillon * ...
448477b0229SBoris Brezillon * }
449477b0229SBoris Brezillon */
mtd_wunit_to_pairing_info(struct mtd_info * mtd,int wunit,struct mtd_pairing_info * info)450477b0229SBoris Brezillon int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
451477b0229SBoris Brezillon struct mtd_pairing_info *info)
452477b0229SBoris Brezillon {
45346b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
45446b5889cSMiquel Raynal int npairs = mtd_wunit_per_eb(master) / mtd_pairing_groups(master);
455477b0229SBoris Brezillon
456477b0229SBoris Brezillon if (wunit < 0 || wunit >= npairs)
457477b0229SBoris Brezillon return -EINVAL;
458477b0229SBoris Brezillon
45946b5889cSMiquel Raynal if (master->pairing && master->pairing->get_info)
46046b5889cSMiquel Raynal return master->pairing->get_info(master, wunit, info);
461477b0229SBoris Brezillon
462477b0229SBoris Brezillon info->group = 0;
463477b0229SBoris Brezillon info->pair = wunit;
464477b0229SBoris Brezillon
465477b0229SBoris Brezillon return 0;
466477b0229SBoris Brezillon }
467477b0229SBoris Brezillon EXPORT_SYMBOL_GPL(mtd_wunit_to_pairing_info);
468477b0229SBoris Brezillon
469477b0229SBoris Brezillon /**
470c77a9312SXiaolei Li * mtd_pairing_info_to_wunit - get wunit from pairing information
471477b0229SBoris Brezillon * @mtd: pointer to new MTD device info structure
472477b0229SBoris Brezillon * @info: pairing information struct
473477b0229SBoris Brezillon *
474477b0229SBoris Brezillon * Returns a positive number representing the wunit associated to the info
475477b0229SBoris Brezillon * struct, or a negative error code.
476477b0229SBoris Brezillon *
477477b0229SBoris Brezillon * This is the reverse of mtd_wunit_to_pairing_info(), and can help one to
478477b0229SBoris Brezillon * iterate over all wunits of a given pair (see mtd_wunit_to_pairing_info()
479477b0229SBoris Brezillon * doc).
480477b0229SBoris Brezillon *
481477b0229SBoris Brezillon * It can also be used to only program the first page of each pair (i.e.
482477b0229SBoris Brezillon * page attached to group 0), which allows one to use an MLC NAND in
483477b0229SBoris Brezillon * software-emulated SLC mode:
484477b0229SBoris Brezillon *
485477b0229SBoris Brezillon * info.group = 0;
486477b0229SBoris Brezillon * npairs = mtd_wunit_per_eb(mtd) / mtd_pairing_groups(mtd);
487477b0229SBoris Brezillon * for (info.pair = 0; info.pair < npairs; info.pair++) {
488477b0229SBoris Brezillon * wunit = mtd_pairing_info_to_wunit(mtd, &info);
489477b0229SBoris Brezillon * mtd_write(mtd, mtd_wunit_to_offset(mtd, blkoffs, wunit),
490477b0229SBoris Brezillon * mtd->writesize, &retlen, buf + (i * mtd->writesize));
491477b0229SBoris Brezillon * }
492477b0229SBoris Brezillon */
mtd_pairing_info_to_wunit(struct mtd_info * mtd,const struct mtd_pairing_info * info)493477b0229SBoris Brezillon int mtd_pairing_info_to_wunit(struct mtd_info *mtd,
494477b0229SBoris Brezillon const struct mtd_pairing_info *info)
495477b0229SBoris Brezillon {
49646b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
49746b5889cSMiquel Raynal int ngroups = mtd_pairing_groups(master);
49846b5889cSMiquel Raynal int npairs = mtd_wunit_per_eb(master) / ngroups;
499477b0229SBoris Brezillon
500477b0229SBoris Brezillon if (!info || info->pair < 0 || info->pair >= npairs ||
501477b0229SBoris Brezillon info->group < 0 || info->group >= ngroups)
502477b0229SBoris Brezillon return -EINVAL;
503477b0229SBoris Brezillon
50446b5889cSMiquel Raynal if (master->pairing && master->pairing->get_wunit)
50546b5889cSMiquel Raynal return mtd->pairing->get_wunit(master, info);
506477b0229SBoris Brezillon
507477b0229SBoris Brezillon return info->pair;
508477b0229SBoris Brezillon }
509477b0229SBoris Brezillon EXPORT_SYMBOL_GPL(mtd_pairing_info_to_wunit);
510477b0229SBoris Brezillon
511477b0229SBoris Brezillon /**
512477b0229SBoris Brezillon * mtd_pairing_groups - get the number of pairing groups
513477b0229SBoris Brezillon * @mtd: pointer to new MTD device info structure
514477b0229SBoris Brezillon *
515477b0229SBoris Brezillon * Returns the number of pairing groups.
516477b0229SBoris Brezillon *
517477b0229SBoris Brezillon * This number is usually equal to the number of bits exposed by a single
518477b0229SBoris Brezillon * cell, and can be used in conjunction with mtd_pairing_info_to_wunit()
519477b0229SBoris Brezillon * to iterate over all pages of a given pair.
520477b0229SBoris Brezillon */
mtd_pairing_groups(struct mtd_info * mtd)521477b0229SBoris Brezillon int mtd_pairing_groups(struct mtd_info *mtd)
522477b0229SBoris Brezillon {
52346b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
52446b5889cSMiquel Raynal
52546b5889cSMiquel Raynal if (!master->pairing || !master->pairing->ngroups)
526477b0229SBoris Brezillon return 1;
527477b0229SBoris Brezillon
52846b5889cSMiquel Raynal return master->pairing->ngroups;
529477b0229SBoris Brezillon }
530477b0229SBoris Brezillon EXPORT_SYMBOL_GPL(mtd_pairing_groups);
531477b0229SBoris Brezillon
mtd_nvmem_reg_read(void * priv,unsigned int offset,void * val,size_t bytes)532c4dfa25aSAlban Bedel static int mtd_nvmem_reg_read(void *priv, unsigned int offset,
533c4dfa25aSAlban Bedel void *val, size_t bytes)
534c4dfa25aSAlban Bedel {
535c4dfa25aSAlban Bedel struct mtd_info *mtd = priv;
536c4dfa25aSAlban Bedel size_t retlen;
537c4dfa25aSAlban Bedel int err;
538c4dfa25aSAlban Bedel
539c4dfa25aSAlban Bedel err = mtd_read(mtd, offset, bytes, &retlen, val);
540c4dfa25aSAlban Bedel if (err && err != -EUCLEAN)
541c4dfa25aSAlban Bedel return err;
542c4dfa25aSAlban Bedel
543c4dfa25aSAlban Bedel return retlen == bytes ? 0 : -EIO;
544c4dfa25aSAlban Bedel }
545c4dfa25aSAlban Bedel
mtd_nvmem_add(struct mtd_info * mtd)546c4dfa25aSAlban Bedel static int mtd_nvmem_add(struct mtd_info *mtd)
547c4dfa25aSAlban Bedel {
548658c4448SAnsuel Smith struct device_node *node = mtd_get_of_node(mtd);
549c4dfa25aSAlban Bedel struct nvmem_config config = {};
550c4dfa25aSAlban Bedel
55175f32f4bSMiquel Raynal config.id = NVMEM_DEVID_NONE;
552c4dfa25aSAlban Bedel config.dev = &mtd->dev;
5537b01b723SRicardo Ribalda Delgado config.name = dev_name(&mtd->dev);
554c4dfa25aSAlban Bedel config.owner = THIS_MODULE;
55526e2fe4cSRafał Miłecki config.add_legacy_fixed_of_cells = of_device_is_compatible(node, "nvmem-cells");
556c4dfa25aSAlban Bedel config.reg_read = mtd_nvmem_reg_read;
557c4dfa25aSAlban Bedel config.size = mtd->size;
558c4dfa25aSAlban Bedel config.word_size = 1;
559c4dfa25aSAlban Bedel config.stride = 1;
560c4dfa25aSAlban Bedel config.read_only = true;
561c4dfa25aSAlban Bedel config.root_only = true;
5626c762189SChristophe Kerello config.ignore_wp = true;
563658c4448SAnsuel Smith config.no_of_node = !of_device_is_compatible(node, "nvmem-cells");
564c4dfa25aSAlban Bedel config.priv = mtd;
565c4dfa25aSAlban Bedel
566c4dfa25aSAlban Bedel mtd->nvmem = nvmem_register(&config);
567c4dfa25aSAlban Bedel if (IS_ERR(mtd->nvmem)) {
568c4dfa25aSAlban Bedel /* Just ignore if there is no NVMEM support in the kernel */
5695cab0615SMiquel Raynal if (PTR_ERR(mtd->nvmem) == -EOPNOTSUPP)
570c4dfa25aSAlban Bedel mtd->nvmem = NULL;
5715cab0615SMiquel Raynal else
5725cab0615SMiquel Raynal return dev_err_probe(&mtd->dev, PTR_ERR(mtd->nvmem),
5735cab0615SMiquel Raynal "Failed to register NVMEM device\n");
574c4dfa25aSAlban Bedel }
575c4dfa25aSAlban Bedel
576c4dfa25aSAlban Bedel return 0;
577c4dfa25aSAlban Bedel }
578c4dfa25aSAlban Bedel
mtd_check_of_node(struct mtd_info * mtd)579ad9b10d1SChristian Marangi static void mtd_check_of_node(struct mtd_info *mtd)
580ad9b10d1SChristian Marangi {
581ad9b10d1SChristian Marangi struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
582ad9b10d1SChristian Marangi const char *pname, *prefix = "partition-";
583ad9b10d1SChristian Marangi int plen, mtd_name_len, offset, prefix_len;
584ad9b10d1SChristian Marangi
585ad9b10d1SChristian Marangi /* Check if MTD already has a device node */
586c5f5d0cdSRafał Miłecki if (mtd_get_of_node(mtd))
587ad9b10d1SChristian Marangi return;
588ad9b10d1SChristian Marangi
5897ec4cdb3STetsuo Handa if (!mtd_is_partition(mtd))
5907ec4cdb3STetsuo Handa return;
591c5f5d0cdSRafał Miłecki
592c5f5d0cdSRafał Miłecki parent_dn = of_node_get(mtd_get_of_node(mtd->parent));
593ad9b10d1SChristian Marangi if (!parent_dn)
594ad9b10d1SChristian Marangi return;
595ad9b10d1SChristian Marangi
5962df11f00SRafał Miłecki if (mtd_is_partition(mtd->parent))
5972df11f00SRafał Miłecki partitions = of_node_get(parent_dn);
5982df11f00SRafał Miłecki else
599ad9b10d1SChristian Marangi partitions = of_get_child_by_name(parent_dn, "partitions");
600ad9b10d1SChristian Marangi if (!partitions)
601ad9b10d1SChristian Marangi goto exit_parent;
602ad9b10d1SChristian Marangi
603ad9b10d1SChristian Marangi prefix_len = strlen(prefix);
604ad9b10d1SChristian Marangi mtd_name_len = strlen(mtd->name);
605ad9b10d1SChristian Marangi
606ad9b10d1SChristian Marangi /* Search if a partition is defined with the same name */
607ad9b10d1SChristian Marangi for_each_child_of_node(partitions, mtd_dn) {
608ad9b10d1SChristian Marangi /* Skip partition with no/wrong prefix */
609c5f5d0cdSRafał Miłecki if (!of_node_name_prefix(mtd_dn, prefix))
610ad9b10d1SChristian Marangi continue;
611ad9b10d1SChristian Marangi
612ad9b10d1SChristian Marangi /* Label have priority. Check that first */
613c5f5d0cdSRafał Miłecki if (!of_property_read_string(mtd_dn, "label", &pname)) {
614c5f5d0cdSRafał Miłecki offset = 0;
615c5f5d0cdSRafał Miłecki } else {
616c5f5d0cdSRafał Miłecki pname = mtd_dn->name;
617ad9b10d1SChristian Marangi offset = prefix_len;
618ad9b10d1SChristian Marangi }
619ad9b10d1SChristian Marangi
620ad9b10d1SChristian Marangi plen = strlen(pname) - offset;
621ad9b10d1SChristian Marangi if (plen == mtd_name_len &&
622ad9b10d1SChristian Marangi !strncmp(mtd->name, pname + offset, plen)) {
6232df11f00SRafał Miłecki mtd_set_of_node(mtd, mtd_dn);
624ad9b10d1SChristian Marangi break;
625ad9b10d1SChristian Marangi }
626ad9b10d1SChristian Marangi }
627ad9b10d1SChristian Marangi
628ad9b10d1SChristian Marangi of_node_put(partitions);
629ad9b10d1SChristian Marangi exit_parent:
630ad9b10d1SChristian Marangi of_node_put(parent_dn);
631ad9b10d1SChristian Marangi }
632ad9b10d1SChristian Marangi
633477b0229SBoris Brezillon /**
6341da177e4SLinus Torvalds * add_mtd_device - register an MTD device
6351da177e4SLinus Torvalds * @mtd: pointer to new MTD device info structure
6361da177e4SLinus Torvalds *
6371da177e4SLinus Torvalds * Add a device to the list of MTD devices present in the system, and
6381da177e4SLinus Torvalds * notify each currently active MTD 'user' of its arrival. Returns
63957dd990cSBrian Norris * zero on success or non-zero on failure.
6401da177e4SLinus Torvalds */
6411da177e4SLinus Torvalds
add_mtd_device(struct mtd_info * mtd)6421da177e4SLinus Torvalds int add_mtd_device(struct mtd_info *mtd)
6431da177e4SLinus Torvalds {
64482e214f6SVincent Whitchurch struct device_node *np = mtd_get_of_node(mtd);
64546b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
646b520e412SBen Hutchings struct mtd_notifier *not;
64782e214f6SVincent Whitchurch int i, error, ofidx;
6481da177e4SLinus Torvalds
649be0dbff8SBrian Norris /*
650be0dbff8SBrian Norris * May occur, for instance, on buggy drivers which call
651be0dbff8SBrian Norris * mtd_device_parse_register() multiple times on the same master MTD,
652be0dbff8SBrian Norris * especially with CONFIG_MTD_PARTITIONED_MASTER=y.
653be0dbff8SBrian Norris */
654fa06052dSJan Kara if (WARN_ONCE(mtd->dev.type, "MTD already registered\n"))
655be0dbff8SBrian Norris return -EEXIST;
656be0dbff8SBrian Norris
657783ed81fSArtem B. Bityutskiy BUG_ON(mtd->writesize == 0);
65833f45c44SBoris Brezillon
6592431c4f5SBoris Brezillon /*
6602431c4f5SBoris Brezillon * MTD drivers should implement ->_{write,read}() or
6612431c4f5SBoris Brezillon * ->_{write,read}_oob(), but not both.
6622431c4f5SBoris Brezillon */
6632431c4f5SBoris Brezillon if (WARN_ON((mtd->_write && mtd->_write_oob) ||
6642431c4f5SBoris Brezillon (mtd->_read && mtd->_read_oob)))
6652431c4f5SBoris Brezillon return -EINVAL;
6662431c4f5SBoris Brezillon
66746b5889cSMiquel Raynal if (WARN_ON((!mtd->erasesize || !master->_erase) &&
66833f45c44SBoris Brezillon !(mtd->flags & MTD_NO_ERASE)))
66933f45c44SBoris Brezillon return -EINVAL;
67033f45c44SBoris Brezillon
6719e3307a1SBoris Brezillon /*
6729e3307a1SBoris Brezillon * MTD_SLC_ON_MLC_EMULATION can only be set on partitions, when the
6739e3307a1SBoris Brezillon * master is an MLC NAND and has a proper pairing scheme defined.
6749e3307a1SBoris Brezillon * We also reject masters that implement ->_writev() for now, because
6759e3307a1SBoris Brezillon * NAND controller drivers don't implement this hook, and adding the
6769e3307a1SBoris Brezillon * SLC -> MLC address/length conversion to this path is useless if we
6779e3307a1SBoris Brezillon * don't have a user.
6789e3307a1SBoris Brezillon */
6799e3307a1SBoris Brezillon if (mtd->flags & MTD_SLC_ON_MLC_EMULATION &&
6809e3307a1SBoris Brezillon (!mtd_is_partition(mtd) || master->type != MTD_MLCNANDFLASH ||
6819e3307a1SBoris Brezillon !master->pairing || master->_writev))
6829e3307a1SBoris Brezillon return -EINVAL;
6839e3307a1SBoris Brezillon
68448b19268SIngo Molnar mutex_lock(&mtd_table_mutex);
6851da177e4SLinus Torvalds
68682e214f6SVincent Whitchurch ofidx = -1;
68782e214f6SVincent Whitchurch if (np)
68882e214f6SVincent Whitchurch ofidx = of_alias_get_id(np, "mtd");
68982e214f6SVincent Whitchurch if (ofidx >= 0)
69082e214f6SVincent Whitchurch i = idr_alloc(&mtd_idr, mtd, ofidx, ofidx + 1, GFP_KERNEL);
69182e214f6SVincent Whitchurch else
692589e9c4dSTejun Heo i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
69357dd990cSBrian Norris if (i < 0) {
69457dd990cSBrian Norris error = i;
695b520e412SBen Hutchings goto fail_locked;
69657dd990cSBrian Norris }
697b520e412SBen Hutchings
6981da177e4SLinus Torvalds mtd->index = i;
69919bfa9ebSTomas Winkler kref_init(&mtd->refcnt);
7001da177e4SLinus Torvalds
701d062d4edSMike Dunn /* default value if not set by driver */
702d062d4edSMike Dunn if (mtd->bitflip_threshold == 0)
703d062d4edSMike Dunn mtd->bitflip_threshold = mtd->ecc_strength;
704d062d4edSMike Dunn
7059e3307a1SBoris Brezillon if (mtd->flags & MTD_SLC_ON_MLC_EMULATION) {
7069e3307a1SBoris Brezillon int ngroups = mtd_pairing_groups(master);
7079e3307a1SBoris Brezillon
7089e3307a1SBoris Brezillon mtd->erasesize /= ngroups;
7099e3307a1SBoris Brezillon mtd->size = (u64)mtd_div_by_eb(mtd->size, master) *
7109e3307a1SBoris Brezillon mtd->erasesize;
7119e3307a1SBoris Brezillon }
7129e3307a1SBoris Brezillon
71369423d99SAdrian Hunter if (is_power_of_2(mtd->erasesize))
71469423d99SAdrian Hunter mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
71569423d99SAdrian Hunter else
71669423d99SAdrian Hunter mtd->erasesize_shift = 0;
71769423d99SAdrian Hunter
71869423d99SAdrian Hunter if (is_power_of_2(mtd->writesize))
71969423d99SAdrian Hunter mtd->writesize_shift = ffs(mtd->writesize) - 1;
72069423d99SAdrian Hunter else
72169423d99SAdrian Hunter mtd->writesize_shift = 0;
72269423d99SAdrian Hunter
72369423d99SAdrian Hunter mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
72469423d99SAdrian Hunter mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
72569423d99SAdrian Hunter
726187ef152SHåvard Skinnemoen /* Some chips always power up locked. Unlock them now */
72738134565SArtem Bityutskiy if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
72838134565SArtem Bityutskiy error = mtd_unlock(mtd, 0, mtd->size);
72938134565SArtem Bityutskiy if (error && error != -EOPNOTSUPP)
730187ef152SHåvard Skinnemoen printk(KERN_WARNING
731b520e412SBen Hutchings "%s: unlock failed, writes may not work\n",
732187ef152SHåvard Skinnemoen mtd->name);
73357dd990cSBrian Norris /* Ignore unlock failures? */
73457dd990cSBrian Norris error = 0;
735187ef152SHåvard Skinnemoen }
736187ef152SHåvard Skinnemoen
7371f24b5a8SDavid Brownell /* Caller should have set dev.parent to match the
738260e89a6SFrans Klaver * physical device, if appropriate.
7391f24b5a8SDavid Brownell */
7401f24b5a8SDavid Brownell mtd->dev.type = &mtd_devtype;
74115bce40cSDavid Woodhouse mtd->dev.class = &mtd_class;
7421f24b5a8SDavid Brownell mtd->dev.devt = MTD_DEVT(i);
7431f24b5a8SDavid Brownell dev_set_name(&mtd->dev, "mtd%d", i);
7446afc4fdbSSaeed Bishara dev_set_drvdata(&mtd->dev, mtd);
745ad9b10d1SChristian Marangi mtd_check_of_node(mtd);
746215a02fdSBrian Norris of_node_get(mtd_get_of_node(mtd));
74757dd990cSBrian Norris error = device_register(&mtd->dev);
748895d68a3SZhang Xiaoxu if (error) {
749895d68a3SZhang Xiaoxu put_device(&mtd->dev);
750b520e412SBen Hutchings goto fail_added;
751895d68a3SZhang Xiaoxu }
7521f24b5a8SDavid Brownell
753c4dfa25aSAlban Bedel /* Add the nvmem provider */
754c4dfa25aSAlban Bedel error = mtd_nvmem_add(mtd);
755c4dfa25aSAlban Bedel if (error)
756c4dfa25aSAlban Bedel goto fail_nvmem_add;
757c4dfa25aSAlban Bedel
7581018c94bSZhuohao Lee mtd_debugfs_populate(mtd);
759e8e3edb9SMario Rugiero
7605e472128SBrian Norris device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
7615e472128SBrian Norris "mtd%dro", i);
7621f24b5a8SDavid Brownell
763289c0522SBrian Norris pr_debug("mtd: Giving out device %d to %s\n", i, mtd->name);
7641da177e4SLinus Torvalds /* No need to get a refcount on the module containing
7651da177e4SLinus Torvalds the notifier, since we hold the mtd_table_mutex */
76671a928c0SChris Malley list_for_each_entry(not, &mtd_notifiers, list)
7671da177e4SLinus Torvalds not->add(mtd);
7681da177e4SLinus Torvalds
76948b19268SIngo Molnar mutex_unlock(&mtd_table_mutex);
77026422ac7SRafał Miłecki
77157150c40SRob Herring if (of_property_read_bool(mtd_get_of_node(mtd), "linux,rootfs")) {
77226422ac7SRafał Miłecki if (IS_BUILTIN(CONFIG_MTD)) {
77326422ac7SRafał Miłecki pr_info("mtd: setting mtd%d (%s) as root device\n", mtd->index, mtd->name);
77426422ac7SRafał Miłecki ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
77526422ac7SRafał Miłecki } else {
77626422ac7SRafał Miłecki pr_warn("mtd: can't set mtd%d (%s) as root device - mtd must be builtin\n",
77726422ac7SRafał Miłecki mtd->index, mtd->name);
77826422ac7SRafał Miłecki }
77926422ac7SRafał Miłecki }
78026422ac7SRafał Miłecki
7811da177e4SLinus Torvalds /* We _know_ we aren't being removed, because
7821da177e4SLinus Torvalds our caller is still holding us here. So none
7831da177e4SLinus Torvalds of this try_ nonsense, and no bitching about it
7841da177e4SLinus Torvalds either. :) */
7851da177e4SLinus Torvalds __module_get(THIS_MODULE);
7861da177e4SLinus Torvalds return 0;
7871da177e4SLinus Torvalds
788c4dfa25aSAlban Bedel fail_nvmem_add:
789c4dfa25aSAlban Bedel device_unregister(&mtd->dev);
790b520e412SBen Hutchings fail_added:
791215a02fdSBrian Norris of_node_put(mtd_get_of_node(mtd));
792b520e412SBen Hutchings idr_remove(&mtd_idr, i);
793b520e412SBen Hutchings fail_locked:
79448b19268SIngo Molnar mutex_unlock(&mtd_table_mutex);
79557dd990cSBrian Norris return error;
7961da177e4SLinus Torvalds }
7971da177e4SLinus Torvalds
7981da177e4SLinus Torvalds /**
7991da177e4SLinus Torvalds * del_mtd_device - unregister an MTD device
8001da177e4SLinus Torvalds * @mtd: pointer to MTD device info structure
8011da177e4SLinus Torvalds *
8021da177e4SLinus Torvalds * Remove a device from the list of MTD devices present in the system,
8031da177e4SLinus Torvalds * and notify each currently active MTD 'user' of its departure.
8041da177e4SLinus Torvalds * Returns zero on success or 1 on failure, which currently will happen
8051da177e4SLinus Torvalds * if the requested device does not appear to be present in the list.
8061da177e4SLinus Torvalds */
8071da177e4SLinus Torvalds
del_mtd_device(struct mtd_info * mtd)8081da177e4SLinus Torvalds int del_mtd_device(struct mtd_info *mtd)
8091da177e4SLinus Torvalds {
8101da177e4SLinus Torvalds int ret;
81175c0b84dSMaxim Levitsky struct mtd_notifier *not;
8121da177e4SLinus Torvalds
81348b19268SIngo Molnar mutex_lock(&mtd_table_mutex);
8141da177e4SLinus Torvalds
815b520e412SBen Hutchings if (idr_find(&mtd_idr, mtd->index) != mtd) {
8161da177e4SLinus Torvalds ret = -ENODEV;
81775c0b84dSMaxim Levitsky goto out_error;
81875c0b84dSMaxim Levitsky }
819694bb7fcSKevin Cernekee
8201da177e4SLinus Torvalds /* No need to get a refcount on the module containing
8211da177e4SLinus Torvalds the notifier, since we hold the mtd_table_mutex */
82271a928c0SChris Malley list_for_each_entry(not, &mtd_notifiers, list)
8231da177e4SLinus Torvalds not->remove(mtd);
8241da177e4SLinus Torvalds
82519bfa9ebSTomas Winkler kref_put(&mtd->refcnt, mtd_device_release);
8261da177e4SLinus Torvalds ret = 0;
8271da177e4SLinus Torvalds
82875c0b84dSMaxim Levitsky out_error:
82948b19268SIngo Molnar mutex_unlock(&mtd_table_mutex);
8301da177e4SLinus Torvalds return ret;
8311da177e4SLinus Torvalds }
8321da177e4SLinus Torvalds
833472b444eSBrian Norris /*
834472b444eSBrian Norris * Set a few defaults based on the parent devices, if not provided by the
835472b444eSBrian Norris * driver
836472b444eSBrian Norris */
mtd_set_dev_defaults(struct mtd_info * mtd)837472b444eSBrian Norris static void mtd_set_dev_defaults(struct mtd_info *mtd)
838472b444eSBrian Norris {
839472b444eSBrian Norris if (mtd->dev.parent) {
840472b444eSBrian Norris if (!mtd->owner && mtd->dev.parent->driver)
841472b444eSBrian Norris mtd->owner = mtd->dev.parent->driver->owner;
842472b444eSBrian Norris if (!mtd->name)
843472b444eSBrian Norris mtd->name = dev_name(mtd->dev.parent);
844472b444eSBrian Norris } else {
845472b444eSBrian Norris pr_debug("mtd device won't show a device symlink in sysfs\n");
846472b444eSBrian Norris }
8471186af45SRafał Miłecki
84846b5889cSMiquel Raynal INIT_LIST_HEAD(&mtd->partitions);
84946b5889cSMiquel Raynal mutex_init(&mtd->master.partitions_lock);
8501ad55288SAlexander Sverdlin mutex_init(&mtd->master.chrdev_lock);
851472b444eSBrian Norris }
852727dc612SDan Ehrenberg
mtd_otp_size(struct mtd_info * mtd,bool is_user)8534b361cfaSMichael Walle static ssize_t mtd_otp_size(struct mtd_info *mtd, bool is_user)
8544b361cfaSMichael Walle {
855c3c8c051SDan Carpenter struct otp_info *info;
8564b361cfaSMichael Walle ssize_t size = 0;
8574b361cfaSMichael Walle unsigned int i;
8584b361cfaSMichael Walle size_t retlen;
8594b361cfaSMichael Walle int ret;
8604b361cfaSMichael Walle
861c3c8c051SDan Carpenter info = kmalloc(PAGE_SIZE, GFP_KERNEL);
862c3c8c051SDan Carpenter if (!info)
863c3c8c051SDan Carpenter return -ENOMEM;
864c3c8c051SDan Carpenter
8654b361cfaSMichael Walle if (is_user)
8664b361cfaSMichael Walle ret = mtd_get_user_prot_info(mtd, PAGE_SIZE, &retlen, info);
8674b361cfaSMichael Walle else
8684b361cfaSMichael Walle ret = mtd_get_fact_prot_info(mtd, PAGE_SIZE, &retlen, info);
8694b361cfaSMichael Walle if (ret)
8704b361cfaSMichael Walle goto err;
8714b361cfaSMichael Walle
872bc8e157fSJon Hunter for (i = 0; i < retlen / sizeof(*info); i++)
873bc8e157fSJon Hunter size += info[i].length;
8744b361cfaSMichael Walle
8754b361cfaSMichael Walle kfree(info);
8764b361cfaSMichael Walle return size;
8774b361cfaSMichael Walle
8784b361cfaSMichael Walle err:
8794b361cfaSMichael Walle kfree(info);
88045bb1faaSMichael Walle
88145bb1faaSMichael Walle /* ENODATA means there is no OTP region. */
88245bb1faaSMichael Walle return ret == -ENODATA ? 0 : ret;
8834b361cfaSMichael Walle }
8844b361cfaSMichael Walle
mtd_otp_nvmem_register(struct mtd_info * mtd,const char * compatible,int size,nvmem_reg_read_t reg_read)8854b361cfaSMichael Walle static struct nvmem_device *mtd_otp_nvmem_register(struct mtd_info *mtd,
8864b361cfaSMichael Walle const char *compatible,
8874b361cfaSMichael Walle int size,
8884b361cfaSMichael Walle nvmem_reg_read_t reg_read)
8894b361cfaSMichael Walle {
8904b361cfaSMichael Walle struct nvmem_device *nvmem = NULL;
8914b361cfaSMichael Walle struct nvmem_config config = {};
8924b361cfaSMichael Walle struct device_node *np;
8934b361cfaSMichael Walle
8944b361cfaSMichael Walle /* DT binding is optional */
8954b361cfaSMichael Walle np = of_get_compatible_child(mtd->dev.of_node, compatible);
8964b361cfaSMichael Walle
8974b361cfaSMichael Walle /* OTP nvmem will be registered on the physical device */
8984b361cfaSMichael Walle config.dev = mtd->dev.parent;
8991cd9ceaaSMichael Walle config.name = compatible;
9001cd9ceaaSMichael Walle config.id = NVMEM_DEVID_AUTO;
9014b361cfaSMichael Walle config.owner = THIS_MODULE;
902cce311f8SChristian Marangi config.add_legacy_fixed_of_cells = !mtd_type_is_nand(mtd);
9034b361cfaSMichael Walle config.type = NVMEM_TYPE_OTP;
9044b361cfaSMichael Walle config.root_only = true;
9056c762189SChristophe Kerello config.ignore_wp = true;
9064b361cfaSMichael Walle config.reg_read = reg_read;
9074b361cfaSMichael Walle config.size = size;
9084b361cfaSMichael Walle config.of_node = np;
9094b361cfaSMichael Walle config.priv = mtd;
9104b361cfaSMichael Walle
9114b361cfaSMichael Walle nvmem = nvmem_register(&config);
9124b361cfaSMichael Walle /* Just ignore if there is no NVMEM support in the kernel */
9134b361cfaSMichael Walle if (IS_ERR(nvmem) && PTR_ERR(nvmem) == -EOPNOTSUPP)
9144b361cfaSMichael Walle nvmem = NULL;
9154b361cfaSMichael Walle
9164b361cfaSMichael Walle of_node_put(np);
9174b361cfaSMichael Walle
9184b361cfaSMichael Walle return nvmem;
9194b361cfaSMichael Walle }
9204b361cfaSMichael Walle
mtd_nvmem_user_otp_reg_read(void * priv,unsigned int offset,void * val,size_t bytes)9214b361cfaSMichael Walle static int mtd_nvmem_user_otp_reg_read(void *priv, unsigned int offset,
9224b361cfaSMichael Walle void *val, size_t bytes)
9234b361cfaSMichael Walle {
9244b361cfaSMichael Walle struct mtd_info *mtd = priv;
9254b361cfaSMichael Walle size_t retlen;
9264b361cfaSMichael Walle int ret;
9274b361cfaSMichael Walle
9284b361cfaSMichael Walle ret = mtd_read_user_prot_reg(mtd, offset, bytes, &retlen, val);
9294b361cfaSMichael Walle if (ret)
9304b361cfaSMichael Walle return ret;
9314b361cfaSMichael Walle
9324b361cfaSMichael Walle return retlen == bytes ? 0 : -EIO;
9334b361cfaSMichael Walle }
9344b361cfaSMichael Walle
mtd_nvmem_fact_otp_reg_read(void * priv,unsigned int offset,void * val,size_t bytes)9354b361cfaSMichael Walle static int mtd_nvmem_fact_otp_reg_read(void *priv, unsigned int offset,
9364b361cfaSMichael Walle void *val, size_t bytes)
9374b361cfaSMichael Walle {
9384b361cfaSMichael Walle struct mtd_info *mtd = priv;
9394b361cfaSMichael Walle size_t retlen;
9404b361cfaSMichael Walle int ret;
9414b361cfaSMichael Walle
9424b361cfaSMichael Walle ret = mtd_read_fact_prot_reg(mtd, offset, bytes, &retlen, val);
9434b361cfaSMichael Walle if (ret)
9444b361cfaSMichael Walle return ret;
9454b361cfaSMichael Walle
9464b361cfaSMichael Walle return retlen == bytes ? 0 : -EIO;
9474b361cfaSMichael Walle }
9484b361cfaSMichael Walle
mtd_otp_nvmem_add(struct mtd_info * mtd)9494b361cfaSMichael Walle static int mtd_otp_nvmem_add(struct mtd_info *mtd)
9504b361cfaSMichael Walle {
9518bd1d24eSMichael Walle struct device *dev = mtd->dev.parent;
9524b361cfaSMichael Walle struct nvmem_device *nvmem;
9534b361cfaSMichael Walle ssize_t size;
9544b361cfaSMichael Walle int err;
9554b361cfaSMichael Walle
9564b361cfaSMichael Walle if (mtd->_get_user_prot_info && mtd->_read_user_prot_reg) {
9574b361cfaSMichael Walle size = mtd_otp_size(mtd, true);
958*adbd5da0SAapo Vienamo if (size < 0) {
959*adbd5da0SAapo Vienamo err = size;
960*adbd5da0SAapo Vienamo goto err;
961*adbd5da0SAapo Vienamo }
9624b361cfaSMichael Walle
9634b361cfaSMichael Walle if (size > 0) {
9644b361cfaSMichael Walle nvmem = mtd_otp_nvmem_register(mtd, "user-otp", size,
9654b361cfaSMichael Walle mtd_nvmem_user_otp_reg_read);
9664b361cfaSMichael Walle if (IS_ERR(nvmem)) {
967281f7a6cSMichael Walle err = PTR_ERR(nvmem);
968281f7a6cSMichael Walle goto err;
9694b361cfaSMichael Walle }
9704b361cfaSMichael Walle mtd->otp_user_nvmem = nvmem;
9714b361cfaSMichael Walle }
9724b361cfaSMichael Walle }
9734b361cfaSMichael Walle
9744b361cfaSMichael Walle if (mtd->_get_fact_prot_info && mtd->_read_fact_prot_reg) {
9754b361cfaSMichael Walle size = mtd_otp_size(mtd, false);
9764b361cfaSMichael Walle if (size < 0) {
9774b361cfaSMichael Walle err = size;
9784b361cfaSMichael Walle goto err;
9794b361cfaSMichael Walle }
9804b361cfaSMichael Walle
9814b361cfaSMichael Walle if (size > 0) {
9823b270facSLinus Walleij /*
9833b270facSLinus Walleij * The factory OTP contains thing such as a unique serial
9843b270facSLinus Walleij * number and is small, so let's read it out and put it
9853b270facSLinus Walleij * into the entropy pool.
9863b270facSLinus Walleij */
9873b270facSLinus Walleij void *otp;
9883b270facSLinus Walleij
9893b270facSLinus Walleij otp = kmalloc(size, GFP_KERNEL);
990cefa1aaaSDan Carpenter if (!otp) {
991cefa1aaaSDan Carpenter err = -ENOMEM;
992cefa1aaaSDan Carpenter goto err;
993cefa1aaaSDan Carpenter }
9943b270facSLinus Walleij err = mtd_nvmem_fact_otp_reg_read(mtd, 0, otp, size);
9953b270facSLinus Walleij if (err < 0) {
9963b270facSLinus Walleij kfree(otp);
997cefa1aaaSDan Carpenter goto err;
9983b270facSLinus Walleij }
9993b270facSLinus Walleij add_device_randomness(otp, err);
10003b270facSLinus Walleij kfree(otp);
10013b270facSLinus Walleij
10024b361cfaSMichael Walle nvmem = mtd_otp_nvmem_register(mtd, "factory-otp", size,
10034b361cfaSMichael Walle mtd_nvmem_fact_otp_reg_read);
10044b361cfaSMichael Walle if (IS_ERR(nvmem)) {
10054b361cfaSMichael Walle err = PTR_ERR(nvmem);
10064b361cfaSMichael Walle goto err;
10074b361cfaSMichael Walle }
10084b361cfaSMichael Walle mtd->otp_factory_nvmem = nvmem;
10094b361cfaSMichael Walle }
10104b361cfaSMichael Walle }
10114b361cfaSMichael Walle
10124b361cfaSMichael Walle return 0;
10134b361cfaSMichael Walle
10144b361cfaSMichael Walle err:
10154b361cfaSMichael Walle nvmem_unregister(mtd->otp_user_nvmem);
1016281f7a6cSMichael Walle return dev_err_probe(dev, err, "Failed to register OTP NVMEM device\n");
10174b361cfaSMichael Walle }
10184b361cfaSMichael Walle
10191da177e4SLinus Torvalds /**
10201c4c215cSDmitry Eremin-Solenikov * mtd_device_parse_register - parse partitions and register an MTD device.
10211c4c215cSDmitry Eremin-Solenikov *
10221c4c215cSDmitry Eremin-Solenikov * @mtd: the MTD device to register
10231c4c215cSDmitry Eremin-Solenikov * @types: the list of MTD partition probes to try, see
10241c4c215cSDmitry Eremin-Solenikov * 'parse_mtd_partitions()' for more information
1025c7975330SDmitry Eremin-Solenikov * @parser_data: MTD partition parser-specific data
10261c4c215cSDmitry Eremin-Solenikov * @parts: fallback partition information to register, if parsing fails;
10271c4c215cSDmitry Eremin-Solenikov * only valid if %nr_parts > %0
10281c4c215cSDmitry Eremin-Solenikov * @nr_parts: the number of partitions in parts, if zero then the full
10291c4c215cSDmitry Eremin-Solenikov * MTD device is registered if no partition info is found
10301c4c215cSDmitry Eremin-Solenikov *
10311c4c215cSDmitry Eremin-Solenikov * This function aggregates MTD partitions parsing (done by
10321c4c215cSDmitry Eremin-Solenikov * 'parse_mtd_partitions()') and MTD device and partitions registering. It
10331c4c215cSDmitry Eremin-Solenikov * basically follows the most common pattern found in many MTD drivers:
10341c4c215cSDmitry Eremin-Solenikov *
103555a999a0SRafał Miłecki * * If the MTD_PARTITIONED_MASTER option is set, then the device as a whole is
103655a999a0SRafał Miłecki * registered first.
103755a999a0SRafał Miłecki * * Then It tries to probe partitions on MTD device @mtd using parsers
10381c4c215cSDmitry Eremin-Solenikov * specified in @types (if @types is %NULL, then the default list of parsers
10391c4c215cSDmitry Eremin-Solenikov * is used, see 'parse_mtd_partitions()' for more information). If none are
10401c4c215cSDmitry Eremin-Solenikov * found this functions tries to fallback to information specified in
10411c4c215cSDmitry Eremin-Solenikov * @parts/@nr_parts.
10421c4c215cSDmitry Eremin-Solenikov * * If no partitions were found this function just registers the MTD device
10431c4c215cSDmitry Eremin-Solenikov * @mtd and exits.
10441c4c215cSDmitry Eremin-Solenikov *
10451c4c215cSDmitry Eremin-Solenikov * Returns zero in case of success and a negative error code in case of failure.
10461c4c215cSDmitry Eremin-Solenikov */
mtd_device_parse_register(struct mtd_info * mtd,const char * const * types,struct mtd_part_parser_data * parser_data,const struct mtd_partition * parts,int nr_parts)104726a47346SArtem Bityutskiy int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
1048c7975330SDmitry Eremin-Solenikov struct mtd_part_parser_data *parser_data,
10491c4c215cSDmitry Eremin-Solenikov const struct mtd_partition *parts,
10501c4c215cSDmitry Eremin-Solenikov int nr_parts)
10511c4c215cSDmitry Eremin-Solenikov {
1052727dc612SDan Ehrenberg int ret;
10531c4c215cSDmitry Eremin-Solenikov
1054472b444eSBrian Norris mtd_set_dev_defaults(mtd);
1055472b444eSBrian Norris
1056e0489f6eSMichael Walle ret = mtd_otp_nvmem_add(mtd);
1057e0489f6eSMichael Walle if (ret)
1058e0489f6eSMichael Walle goto out;
1059e0489f6eSMichael Walle
10602c77c57dSRafał Miłecki if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
10612c77c57dSRafał Miłecki ret = add_mtd_device(mtd);
10622c77c57dSRafał Miłecki if (ret)
1063e0489f6eSMichael Walle goto out;
10642c77c57dSRafał Miłecki }
10652c77c57dSRafał Miłecki
10660dbe4ea7SRafał Miłecki /* Prefer parsed partitions over driver-provided fallback */
10675ac67ce3SRafał Miłecki ret = parse_mtd_partitions(mtd, types, parser_data);
106808608adbSManivannan Sadhasivam if (ret == -EPROBE_DEFER)
106908608adbSManivannan Sadhasivam goto out;
107008608adbSManivannan Sadhasivam
10715ac67ce3SRafał Miłecki if (ret > 0)
10725ac67ce3SRafał Miłecki ret = 0;
10735ac67ce3SRafał Miłecki else if (nr_parts)
10740dbe4ea7SRafał Miłecki ret = add_mtd_partitions(mtd, parts, nr_parts);
10750dbe4ea7SRafał Miłecki else if (!device_is_registered(&mtd->dev))
10760dbe4ea7SRafał Miłecki ret = add_mtd_device(mtd);
10770dbe4ea7SRafał Miłecki else
10780dbe4ea7SRafał Miłecki ret = 0;
10790dbe4ea7SRafał Miłecki
10803e00ed0eSBrian Norris if (ret)
10813e00ed0eSBrian Norris goto out;
10821c4c215cSDmitry Eremin-Solenikov
1083e1dd8641SNiklas Cassel /*
1084e1dd8641SNiklas Cassel * FIXME: some drivers unfortunately call this function more than once.
1085e1dd8641SNiklas Cassel * So we have to check if we've already assigned the reboot notifier.
1086e1dd8641SNiklas Cassel *
1087e1dd8641SNiklas Cassel * Generally, we can make multiple calls work for most cases, but it
1088e1dd8641SNiklas Cassel * does cause problems with parse_mtd_partitions() above (e.g.,
1089e1dd8641SNiklas Cassel * cmdlineparts will register partitions more than once).
1090e1dd8641SNiklas Cassel */
1091f8479dd6SBrian Norris WARN_ONCE(mtd->_reboot && mtd->reboot_notifier.notifier_call,
1092f8479dd6SBrian Norris "MTD already registered\n");
1093e1dd8641SNiklas Cassel if (mtd->_reboot && !mtd->reboot_notifier.notifier_call) {
10943efe41beSBrian Norris mtd->reboot_notifier.notifier_call = mtd_reboot_notifier;
10953efe41beSBrian Norris register_reboot_notifier(&mtd->reboot_notifier);
10963efe41beSBrian Norris }
10973efe41beSBrian Norris
10983e00ed0eSBrian Norris out:
1099e0489f6eSMichael Walle if (ret) {
1100e0489f6eSMichael Walle nvmem_unregister(mtd->otp_user_nvmem);
1101e0489f6eSMichael Walle nvmem_unregister(mtd->otp_factory_nvmem);
1102e0489f6eSMichael Walle }
1103e0489f6eSMichael Walle
11042c77c57dSRafał Miłecki if (ret && device_is_registered(&mtd->dev))
11052c77c57dSRafał Miłecki del_mtd_device(mtd);
11062c77c57dSRafał Miłecki
1107727dc612SDan Ehrenberg return ret;
11081c4c215cSDmitry Eremin-Solenikov }
11091c4c215cSDmitry Eremin-Solenikov EXPORT_SYMBOL_GPL(mtd_device_parse_register);
11101c4c215cSDmitry Eremin-Solenikov
11111c4c215cSDmitry Eremin-Solenikov /**
1112f5671ab3SJamie Iles * mtd_device_unregister - unregister an existing MTD device.
1113f5671ab3SJamie Iles *
1114f5671ab3SJamie Iles * @master: the MTD device to unregister. This will unregister both the master
1115f5671ab3SJamie Iles * and any partitions if registered.
1116f5671ab3SJamie Iles */
mtd_device_unregister(struct mtd_info * master)1117f5671ab3SJamie Iles int mtd_device_unregister(struct mtd_info *master)
1118f5671ab3SJamie Iles {
1119f5671ab3SJamie Iles int err;
1120f5671ab3SJamie Iles
112100596576SZev Weiss if (master->_reboot) {
11223efe41beSBrian Norris unregister_reboot_notifier(&master->reboot_notifier);
112300596576SZev Weiss memset(&master->reboot_notifier, 0, sizeof(master->reboot_notifier));
112400596576SZev Weiss }
11253efe41beSBrian Norris
11264b361cfaSMichael Walle nvmem_unregister(master->otp_user_nvmem);
11274b361cfaSMichael Walle nvmem_unregister(master->otp_factory_nvmem);
11284b361cfaSMichael Walle
1129f5671ab3SJamie Iles err = del_mtd_partitions(master);
1130f5671ab3SJamie Iles if (err)
1131f5671ab3SJamie Iles return err;
1132f5671ab3SJamie Iles
1133f5671ab3SJamie Iles if (!device_is_registered(&master->dev))
1134f5671ab3SJamie Iles return 0;
1135f5671ab3SJamie Iles
1136f5671ab3SJamie Iles return del_mtd_device(master);
1137f5671ab3SJamie Iles }
1138f5671ab3SJamie Iles EXPORT_SYMBOL_GPL(mtd_device_unregister);
1139f5671ab3SJamie Iles
1140f5671ab3SJamie Iles /**
11411da177e4SLinus Torvalds * register_mtd_user - register a 'user' of MTD devices.
11421da177e4SLinus Torvalds * @new: pointer to notifier info structure
11431da177e4SLinus Torvalds *
11441da177e4SLinus Torvalds * Registers a pair of callbacks function to be called upon addition
11451da177e4SLinus Torvalds * or removal of MTD devices. Causes the 'add' callback to be immediately
11461da177e4SLinus Torvalds * invoked for each MTD device currently present in the system.
11471da177e4SLinus Torvalds */
register_mtd_user(struct mtd_notifier * new)11481da177e4SLinus Torvalds void register_mtd_user (struct mtd_notifier *new)
11491da177e4SLinus Torvalds {
1150f1332ba2SBen Hutchings struct mtd_info *mtd;
11511da177e4SLinus Torvalds
115248b19268SIngo Molnar mutex_lock(&mtd_table_mutex);
11531da177e4SLinus Torvalds
11541da177e4SLinus Torvalds list_add(&new->list, &mtd_notifiers);
11551da177e4SLinus Torvalds
11561da177e4SLinus Torvalds __module_get(THIS_MODULE);
11571da177e4SLinus Torvalds
1158f1332ba2SBen Hutchings mtd_for_each_device(mtd)
1159f1332ba2SBen Hutchings new->add(mtd);
11601da177e4SLinus Torvalds
116148b19268SIngo Molnar mutex_unlock(&mtd_table_mutex);
11621da177e4SLinus Torvalds }
116333c87b4aSArtem Bityutskiy EXPORT_SYMBOL_GPL(register_mtd_user);
11641da177e4SLinus Torvalds
11651da177e4SLinus Torvalds /**
116649450795SArtem B. Bityuckiy * unregister_mtd_user - unregister a 'user' of MTD devices.
116749450795SArtem B. Bityuckiy * @old: pointer to notifier info structure
11681da177e4SLinus Torvalds *
11691da177e4SLinus Torvalds * Removes a callback function pair from the list of 'users' to be
11701da177e4SLinus Torvalds * notified upon addition or removal of MTD devices. Causes the
11711da177e4SLinus Torvalds * 'remove' callback to be immediately invoked for each MTD device
11721da177e4SLinus Torvalds * currently present in the system.
11731da177e4SLinus Torvalds */
unregister_mtd_user(struct mtd_notifier * old)11741da177e4SLinus Torvalds int unregister_mtd_user (struct mtd_notifier *old)
11751da177e4SLinus Torvalds {
1176f1332ba2SBen Hutchings struct mtd_info *mtd;
11771da177e4SLinus Torvalds
117848b19268SIngo Molnar mutex_lock(&mtd_table_mutex);
11791da177e4SLinus Torvalds
11801da177e4SLinus Torvalds module_put(THIS_MODULE);
11811da177e4SLinus Torvalds
1182f1332ba2SBen Hutchings mtd_for_each_device(mtd)
1183f1332ba2SBen Hutchings old->remove(mtd);
11841da177e4SLinus Torvalds
11851da177e4SLinus Torvalds list_del(&old->list);
118648b19268SIngo Molnar mutex_unlock(&mtd_table_mutex);
11871da177e4SLinus Torvalds return 0;
11881da177e4SLinus Torvalds }
118933c87b4aSArtem Bityutskiy EXPORT_SYMBOL_GPL(unregister_mtd_user);
11901da177e4SLinus Torvalds
11911da177e4SLinus Torvalds /**
11921da177e4SLinus Torvalds * get_mtd_device - obtain a validated handle for an MTD device
11931da177e4SLinus Torvalds * @mtd: last known address of the required MTD device
11941da177e4SLinus Torvalds * @num: internal device number of the required MTD device
11951da177e4SLinus Torvalds *
11961da177e4SLinus Torvalds * Given a number and NULL address, return the num'th entry in the device
11971da177e4SLinus Torvalds * table, if any. Given an address and num == -1, search the device table
11981da177e4SLinus Torvalds * for a device with that address and return if it's still present. Given
11999c74034fSArtem Bityutskiy * both, return the num'th driver only if its address matches. Return
12009c74034fSArtem Bityutskiy * error code if not.
12011da177e4SLinus Torvalds */
get_mtd_device(struct mtd_info * mtd,int num)12021da177e4SLinus Torvalds struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
12031da177e4SLinus Torvalds {
1204f1332ba2SBen Hutchings struct mtd_info *ret = NULL, *other;
1205f1332ba2SBen Hutchings int err = -ENODEV;
12061da177e4SLinus Torvalds
120748b19268SIngo Molnar mutex_lock(&mtd_table_mutex);
12081da177e4SLinus Torvalds
12091da177e4SLinus Torvalds if (num == -1) {
1210f1332ba2SBen Hutchings mtd_for_each_device(other) {
1211f1332ba2SBen Hutchings if (other == mtd) {
1212f1332ba2SBen Hutchings ret = mtd;
1213f1332ba2SBen Hutchings break;
1214f1332ba2SBen Hutchings }
1215f1332ba2SBen Hutchings }
1216b520e412SBen Hutchings } else if (num >= 0) {
1217b520e412SBen Hutchings ret = idr_find(&mtd_idr, num);
12181da177e4SLinus Torvalds if (mtd && mtd != ret)
12191da177e4SLinus Torvalds ret = NULL;
12201da177e4SLinus Torvalds }
12211da177e4SLinus Torvalds
12223bd45657SMaxim Levitsky if (!ret) {
12233bd45657SMaxim Levitsky ret = ERR_PTR(err);
12243bd45657SMaxim Levitsky goto out;
12259fe912ceSArtem Bityutskiy }
12269fe912ceSArtem Bityutskiy
12273bd45657SMaxim Levitsky err = __get_mtd_device(ret);
12283bd45657SMaxim Levitsky if (err)
12293bd45657SMaxim Levitsky ret = ERR_PTR(err);
12303bd45657SMaxim Levitsky out:
123148b19268SIngo Molnar mutex_unlock(&mtd_table_mutex);
12321da177e4SLinus Torvalds return ret;
12333bd45657SMaxim Levitsky }
123433c87b4aSArtem Bityutskiy EXPORT_SYMBOL_GPL(get_mtd_device);
12359c74034fSArtem Bityutskiy
12363bd45657SMaxim Levitsky
__get_mtd_device(struct mtd_info * mtd)12373bd45657SMaxim Levitsky int __get_mtd_device(struct mtd_info *mtd)
12383bd45657SMaxim Levitsky {
123946b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
12403bd45657SMaxim Levitsky int err;
12413bd45657SMaxim Levitsky
124246b5889cSMiquel Raynal if (master->_get_device) {
124346b5889cSMiquel Raynal err = master->_get_device(mtd);
124479c4a562SAlexander Usyskin if (err)
12453bd45657SMaxim Levitsky return err;
12463bd45657SMaxim Levitsky }
124779c4a562SAlexander Usyskin
124879c4a562SAlexander Usyskin if (!try_module_get(master->owner)) {
124979c4a562SAlexander Usyskin if (master->_put_device)
125079c4a562SAlexander Usyskin master->_put_device(master);
125179c4a562SAlexander Usyskin return -ENODEV;
12523bd45657SMaxim Levitsky }
125346b5889cSMiquel Raynal
1254264725e3SMiquel Raynal while (mtd) {
1255264725e3SMiquel Raynal if (mtd != master)
125679c4a562SAlexander Usyskin kref_get(&mtd->refcnt);
125746b5889cSMiquel Raynal mtd = mtd->parent;
125846b5889cSMiquel Raynal }
125946b5889cSMiquel Raynal
1260264725e3SMiquel Raynal if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
1261264725e3SMiquel Raynal kref_get(&master->refcnt);
1262264725e3SMiquel Raynal
12633bd45657SMaxim Levitsky return 0;
12641da177e4SLinus Torvalds }
126533c87b4aSArtem Bityutskiy EXPORT_SYMBOL_GPL(__get_mtd_device);
12661da177e4SLinus Torvalds
12677799308fSArtem Bityutskiy /**
12684a575865SRafał Miłecki * of_get_mtd_device_by_node - obtain an MTD device associated with a given node
12694a575865SRafał Miłecki *
12704a575865SRafał Miłecki * @np: device tree node
12714a575865SRafał Miłecki */
of_get_mtd_device_by_node(struct device_node * np)12724a575865SRafał Miłecki struct mtd_info *of_get_mtd_device_by_node(struct device_node *np)
12734a575865SRafał Miłecki {
12744a575865SRafał Miłecki struct mtd_info *mtd = NULL;
12754a575865SRafał Miłecki struct mtd_info *tmp;
12764a575865SRafał Miłecki int err;
12774a575865SRafał Miłecki
12784a575865SRafał Miłecki mutex_lock(&mtd_table_mutex);
12794a575865SRafał Miłecki
12804a575865SRafał Miłecki err = -EPROBE_DEFER;
12814a575865SRafał Miłecki mtd_for_each_device(tmp) {
12824a575865SRafał Miłecki if (mtd_get_of_node(tmp) == np) {
12834a575865SRafał Miłecki mtd = tmp;
12844a575865SRafał Miłecki err = __get_mtd_device(mtd);
12854a575865SRafał Miłecki break;
12864a575865SRafał Miłecki }
12874a575865SRafał Miłecki }
12884a575865SRafał Miłecki
12894a575865SRafał Miłecki mutex_unlock(&mtd_table_mutex);
12904a575865SRafał Miłecki
12914a575865SRafał Miłecki return err ? ERR_PTR(err) : mtd;
12924a575865SRafał Miłecki }
12934a575865SRafał Miłecki EXPORT_SYMBOL_GPL(of_get_mtd_device_by_node);
12944a575865SRafał Miłecki
12954a575865SRafał Miłecki /**
12967799308fSArtem Bityutskiy * get_mtd_device_nm - obtain a validated handle for an MTD device by
12977799308fSArtem Bityutskiy * device name
12987799308fSArtem Bityutskiy * @name: MTD device name to open
12997799308fSArtem Bityutskiy *
13007799308fSArtem Bityutskiy * This function returns MTD device description structure in case of
13017799308fSArtem Bityutskiy * success and an error code in case of failure.
13027799308fSArtem Bityutskiy */
get_mtd_device_nm(const char * name)13037799308fSArtem Bityutskiy struct mtd_info *get_mtd_device_nm(const char *name)
13047799308fSArtem Bityutskiy {
1305f1332ba2SBen Hutchings int err = -ENODEV;
1306f1332ba2SBen Hutchings struct mtd_info *mtd = NULL, *other;
13077799308fSArtem Bityutskiy
13087799308fSArtem Bityutskiy mutex_lock(&mtd_table_mutex);
13097799308fSArtem Bityutskiy
1310f1332ba2SBen Hutchings mtd_for_each_device(other) {
1311f1332ba2SBen Hutchings if (!strcmp(name, other->name)) {
1312f1332ba2SBen Hutchings mtd = other;
13137799308fSArtem Bityutskiy break;
13147799308fSArtem Bityutskiy }
13157799308fSArtem Bityutskiy }
13167799308fSArtem Bityutskiy
13179fe912ceSArtem Bityutskiy if (!mtd)
13187799308fSArtem Bityutskiy goto out_unlock;
13197799308fSArtem Bityutskiy
132052534f2dSWanlong Gao err = __get_mtd_device(mtd);
132152534f2dSWanlong Gao if (err)
13227799308fSArtem Bityutskiy goto out_unlock;
13237799308fSArtem Bityutskiy
13247799308fSArtem Bityutskiy mutex_unlock(&mtd_table_mutex);
13257799308fSArtem Bityutskiy return mtd;
13269fe912ceSArtem Bityutskiy
13279fe912ceSArtem Bityutskiy out_unlock:
13289fe912ceSArtem Bityutskiy mutex_unlock(&mtd_table_mutex);
13299fe912ceSArtem Bityutskiy return ERR_PTR(err);
13307799308fSArtem Bityutskiy }
133133c87b4aSArtem Bityutskiy EXPORT_SYMBOL_GPL(get_mtd_device_nm);
13327799308fSArtem Bityutskiy
put_mtd_device(struct mtd_info * mtd)13331da177e4SLinus Torvalds void put_mtd_device(struct mtd_info *mtd)
13341da177e4SLinus Torvalds {
133548b19268SIngo Molnar mutex_lock(&mtd_table_mutex);
13363bd45657SMaxim Levitsky __put_mtd_device(mtd);
13373bd45657SMaxim Levitsky mutex_unlock(&mtd_table_mutex);
13383bd45657SMaxim Levitsky
13393bd45657SMaxim Levitsky }
134033c87b4aSArtem Bityutskiy EXPORT_SYMBOL_GPL(put_mtd_device);
13413bd45657SMaxim Levitsky
__put_mtd_device(struct mtd_info * mtd)13423bd45657SMaxim Levitsky void __put_mtd_device(struct mtd_info *mtd)
13433bd45657SMaxim Levitsky {
134446b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
134546b5889cSMiquel Raynal
1346264725e3SMiquel Raynal while (mtd) {
1347264725e3SMiquel Raynal /* kref_put() can relese mtd, so keep a reference mtd->parent */
134819bfa9ebSTomas Winkler struct mtd_info *parent = mtd->parent;
13493bd45657SMaxim Levitsky
1350264725e3SMiquel Raynal if (mtd != master)
135119bfa9ebSTomas Winkler kref_put(&mtd->refcnt, mtd_device_release);
135219bfa9ebSTomas Winkler mtd = parent;
135319bfa9ebSTomas Winkler }
13541ca71415SRichard Weinberger
135579c4a562SAlexander Usyskin if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
135679c4a562SAlexander Usyskin kref_put(&master->refcnt, mtd_device_release);
13571da177e4SLinus Torvalds
135846b5889cSMiquel Raynal module_put(master->owner);
135919bfa9ebSTomas Winkler
136079c4a562SAlexander Usyskin /* must be the last as master can be freed in the _put_device */
136179c4a562SAlexander Usyskin if (master->_put_device)
136279c4a562SAlexander Usyskin master->_put_device(master);
13631da177e4SLinus Torvalds }
136433c87b4aSArtem Bityutskiy EXPORT_SYMBOL_GPL(__put_mtd_device);
13651da177e4SLinus Torvalds
136652b02031SArtem Bityutskiy /*
1367884cfd90SBoris Brezillon * Erase is an synchronous operation. Device drivers are epected to return a
1368884cfd90SBoris Brezillon * negative error code if the operation failed and update instr->fail_addr
1369884cfd90SBoris Brezillon * to point the portion that was not properly erased.
13708273a0c9SArtem Bityutskiy */
mtd_erase(struct mtd_info * mtd,struct erase_info * instr)13718273a0c9SArtem Bityutskiy int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
13728273a0c9SArtem Bityutskiy {
137346b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
137446b5889cSMiquel Raynal u64 mst_ofs = mtd_get_master_ofs(mtd, 0);
13759e3307a1SBoris Brezillon struct erase_info adjinstr;
137646b5889cSMiquel Raynal int ret;
137746b5889cSMiquel Raynal
1378c585da9fSBoris Brezillon instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
13799e3307a1SBoris Brezillon adjinstr = *instr;
1380c585da9fSBoris Brezillon
138146b5889cSMiquel Raynal if (!mtd->erasesize || !master->_erase)
1382e6e620f0SBoris Brezillon return -ENOTSUPP;
1383e6e620f0SBoris Brezillon
13840c2b4e21SBrian Norris if (instr->addr >= mtd->size || instr->len > mtd->size - instr->addr)
13858273a0c9SArtem Bityutskiy return -EINVAL;
1386664addc2SArtem Bityutskiy if (!(mtd->flags & MTD_WRITEABLE))
1387664addc2SArtem Bityutskiy return -EROFS;
1388e6e620f0SBoris Brezillon
1389e7bfb3fdSBoris Brezillon if (!instr->len)
1390bcb1d238SArtem Bityutskiy return 0;
1391e7bfb3fdSBoris Brezillon
1392fea728c0SEzequiel Garcia ledtrig_mtd_activity();
139346b5889cSMiquel Raynal
13949e3307a1SBoris Brezillon if (mtd->flags & MTD_SLC_ON_MLC_EMULATION) {
13959e3307a1SBoris Brezillon adjinstr.addr = (loff_t)mtd_div_by_eb(instr->addr, mtd) *
13969e3307a1SBoris Brezillon master->erasesize;
13979e3307a1SBoris Brezillon adjinstr.len = ((u64)mtd_div_by_eb(instr->addr + instr->len, mtd) *
13989e3307a1SBoris Brezillon master->erasesize) -
13999e3307a1SBoris Brezillon adjinstr.addr;
14009e3307a1SBoris Brezillon }
140146b5889cSMiquel Raynal
14029e3307a1SBoris Brezillon adjinstr.addr += mst_ofs;
14039e3307a1SBoris Brezillon
14049e3307a1SBoris Brezillon ret = master->_erase(master, &adjinstr);
14059e3307a1SBoris Brezillon
14069e3307a1SBoris Brezillon if (adjinstr.fail_addr != MTD_FAIL_ADDR_UNKNOWN) {
14079e3307a1SBoris Brezillon instr->fail_addr = adjinstr.fail_addr - mst_ofs;
14089e3307a1SBoris Brezillon if (mtd->flags & MTD_SLC_ON_MLC_EMULATION) {
14099e3307a1SBoris Brezillon instr->fail_addr = mtd_div_by_eb(instr->fail_addr,
14109e3307a1SBoris Brezillon master);
14119e3307a1SBoris Brezillon instr->fail_addr *= mtd->erasesize;
14129e3307a1SBoris Brezillon }
14139e3307a1SBoris Brezillon }
14149e3307a1SBoris Brezillon
141546b5889cSMiquel Raynal return ret;
14168273a0c9SArtem Bityutskiy }
14178273a0c9SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_erase);
14188273a0c9SArtem Bityutskiy
14198273a0c9SArtem Bityutskiy /*
14208273a0c9SArtem Bityutskiy * This stuff for eXecute-In-Place. phys is optional and may be set to NULL.
14218273a0c9SArtem Bityutskiy */
mtd_point(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,void ** virt,resource_size_t * phys)14228273a0c9SArtem Bityutskiy int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
14238273a0c9SArtem Bityutskiy void **virt, resource_size_t *phys)
14248273a0c9SArtem Bityutskiy {
142546b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
142646b5889cSMiquel Raynal
14278273a0c9SArtem Bityutskiy *retlen = 0;
14280dd5235fSArtem Bityutskiy *virt = NULL;
14290dd5235fSArtem Bityutskiy if (phys)
14300dd5235fSArtem Bityutskiy *phys = 0;
143146b5889cSMiquel Raynal if (!master->_point)
14328273a0c9SArtem Bityutskiy return -EOPNOTSUPP;
14330c2b4e21SBrian Norris if (from < 0 || from >= mtd->size || len > mtd->size - from)
14348273a0c9SArtem Bityutskiy return -EINVAL;
1435bcb1d238SArtem Bityutskiy if (!len)
1436bcb1d238SArtem Bityutskiy return 0;
143746b5889cSMiquel Raynal
143846b5889cSMiquel Raynal from = mtd_get_master_ofs(mtd, from);
143946b5889cSMiquel Raynal return master->_point(master, from, len, retlen, virt, phys);
14408273a0c9SArtem Bityutskiy }
14418273a0c9SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_point);
14428273a0c9SArtem Bityutskiy
14438273a0c9SArtem Bityutskiy /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
mtd_unpoint(struct mtd_info * mtd,loff_t from,size_t len)14448273a0c9SArtem Bityutskiy int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
14458273a0c9SArtem Bityutskiy {
144646b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
144746b5889cSMiquel Raynal
144846b5889cSMiquel Raynal if (!master->_unpoint)
14498273a0c9SArtem Bityutskiy return -EOPNOTSUPP;
14500c2b4e21SBrian Norris if (from < 0 || from >= mtd->size || len > mtd->size - from)
14518273a0c9SArtem Bityutskiy return -EINVAL;
1452bcb1d238SArtem Bityutskiy if (!len)
1453bcb1d238SArtem Bityutskiy return 0;
145446b5889cSMiquel Raynal return master->_unpoint(master, mtd_get_master_ofs(mtd, from), len);
14558273a0c9SArtem Bityutskiy }
14568273a0c9SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_unpoint);
14578273a0c9SArtem Bityutskiy
14588273a0c9SArtem Bityutskiy /*
14598273a0c9SArtem Bityutskiy * Allow NOMMU mmap() to directly map the device (if not NULL)
14608273a0c9SArtem Bityutskiy * - return the address to which the offset maps
14618273a0c9SArtem Bityutskiy * - return -ENOSYS to indicate refusal to do the mapping
14628273a0c9SArtem Bityutskiy */
mtd_get_unmapped_area(struct mtd_info * mtd,unsigned long len,unsigned long offset,unsigned long flags)14638273a0c9SArtem Bityutskiy unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len,
14648273a0c9SArtem Bityutskiy unsigned long offset, unsigned long flags)
14658273a0c9SArtem Bityutskiy {
14669eaa903cSNicolas Pitre size_t retlen;
14679eaa903cSNicolas Pitre void *virt;
14689eaa903cSNicolas Pitre int ret;
14699eaa903cSNicolas Pitre
14709eaa903cSNicolas Pitre ret = mtd_point(mtd, offset, len, &retlen, &virt, NULL);
14719eaa903cSNicolas Pitre if (ret)
14729eaa903cSNicolas Pitre return ret;
14739eaa903cSNicolas Pitre if (retlen != len) {
14749eaa903cSNicolas Pitre mtd_unpoint(mtd, offset, retlen);
14759eaa903cSNicolas Pitre return -ENOSYS;
14769eaa903cSNicolas Pitre }
14779eaa903cSNicolas Pitre return (unsigned long)virt;
14788273a0c9SArtem Bityutskiy }
14798273a0c9SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_get_unmapped_area);
14808273a0c9SArtem Bityutskiy
mtd_update_ecc_stats(struct mtd_info * mtd,struct mtd_info * master,const struct mtd_ecc_stats * old_stats)148146b5889cSMiquel Raynal static void mtd_update_ecc_stats(struct mtd_info *mtd, struct mtd_info *master,
148246b5889cSMiquel Raynal const struct mtd_ecc_stats *old_stats)
148346b5889cSMiquel Raynal {
148446b5889cSMiquel Raynal struct mtd_ecc_stats diff;
148546b5889cSMiquel Raynal
148646b5889cSMiquel Raynal if (master == mtd)
148746b5889cSMiquel Raynal return;
148846b5889cSMiquel Raynal
148946b5889cSMiquel Raynal diff = master->ecc_stats;
149046b5889cSMiquel Raynal diff.failed -= old_stats->failed;
149146b5889cSMiquel Raynal diff.corrected -= old_stats->corrected;
149246b5889cSMiquel Raynal
149346b5889cSMiquel Raynal while (mtd->parent) {
149446b5889cSMiquel Raynal mtd->ecc_stats.failed += diff.failed;
149546b5889cSMiquel Raynal mtd->ecc_stats.corrected += diff.corrected;
149646b5889cSMiquel Raynal mtd = mtd->parent;
149746b5889cSMiquel Raynal }
149846b5889cSMiquel Raynal }
149946b5889cSMiquel Raynal
mtd_read(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)15008273a0c9SArtem Bityutskiy int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
15018273a0c9SArtem Bityutskiy u_char *buf)
15028273a0c9SArtem Bityutskiy {
150324ff1292SBoris Brezillon struct mtd_oob_ops ops = {
150424ff1292SBoris Brezillon .len = len,
150524ff1292SBoris Brezillon .datbuf = buf,
150624ff1292SBoris Brezillon };
15072431c4f5SBoris Brezillon int ret;
150824ff1292SBoris Brezillon
15092431c4f5SBoris Brezillon ret = mtd_read_oob(mtd, from, &ops);
151024ff1292SBoris Brezillon *retlen = ops.retlen;
151124ff1292SBoris Brezillon
15122431c4f5SBoris Brezillon return ret;
15138273a0c9SArtem Bityutskiy }
15148273a0c9SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_read);
15158273a0c9SArtem Bityutskiy
mtd_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u_char * buf)15168273a0c9SArtem Bityutskiy int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
15178273a0c9SArtem Bityutskiy const u_char *buf)
15188273a0c9SArtem Bityutskiy {
151924ff1292SBoris Brezillon struct mtd_oob_ops ops = {
152024ff1292SBoris Brezillon .len = len,
152124ff1292SBoris Brezillon .datbuf = (u8 *)buf,
152224ff1292SBoris Brezillon };
152324ff1292SBoris Brezillon int ret;
152424ff1292SBoris Brezillon
15252431c4f5SBoris Brezillon ret = mtd_write_oob(mtd, to, &ops);
152624ff1292SBoris Brezillon *retlen = ops.retlen;
152724ff1292SBoris Brezillon
15282431c4f5SBoris Brezillon return ret;
15298273a0c9SArtem Bityutskiy }
15308273a0c9SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_write);
15318273a0c9SArtem Bityutskiy
15328273a0c9SArtem Bityutskiy /*
15338273a0c9SArtem Bityutskiy * In blackbox flight recorder like scenarios we want to make successful writes
15348273a0c9SArtem Bityutskiy * in interrupt context. panic_write() is only intended to be called when its
15358273a0c9SArtem Bityutskiy * known the kernel is about to panic and we need the write to succeed. Since
15368273a0c9SArtem Bityutskiy * the kernel is not going to be running for much longer, this function can
15378273a0c9SArtem Bityutskiy * break locks and delay to ensure the write succeeds (but not sleep).
15388273a0c9SArtem Bityutskiy */
mtd_panic_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u_char * buf)15398273a0c9SArtem Bityutskiy int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
15408273a0c9SArtem Bityutskiy const u_char *buf)
15418273a0c9SArtem Bityutskiy {
154246b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
154346b5889cSMiquel Raynal
15448273a0c9SArtem Bityutskiy *retlen = 0;
154546b5889cSMiquel Raynal if (!master->_panic_write)
15468273a0c9SArtem Bityutskiy return -EOPNOTSUPP;
15470c2b4e21SBrian Norris if (to < 0 || to >= mtd->size || len > mtd->size - to)
15488273a0c9SArtem Bityutskiy return -EINVAL;
1549664addc2SArtem Bityutskiy if (!(mtd->flags & MTD_WRITEABLE))
1550664addc2SArtem Bityutskiy return -EROFS;
1551bcb1d238SArtem Bityutskiy if (!len)
1552bcb1d238SArtem Bityutskiy return 0;
1553630e8d55SKamal Dasu if (!master->oops_panic_write)
1554630e8d55SKamal Dasu master->oops_panic_write = true;
15559f897bfdSKamal Dasu
155646b5889cSMiquel Raynal return master->_panic_write(master, mtd_get_master_ofs(mtd, to), len,
155746b5889cSMiquel Raynal retlen, buf);
15588273a0c9SArtem Bityutskiy }
15598273a0c9SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_panic_write);
15608273a0c9SArtem Bityutskiy
mtd_check_oob_ops(struct mtd_info * mtd,loff_t offs,struct mtd_oob_ops * ops)15615cdd929dSBoris Brezillon static int mtd_check_oob_ops(struct mtd_info *mtd, loff_t offs,
15625cdd929dSBoris Brezillon struct mtd_oob_ops *ops)
15635cdd929dSBoris Brezillon {
15645cdd929dSBoris Brezillon /*
15655cdd929dSBoris Brezillon * Some users are setting ->datbuf or ->oobbuf to NULL, but are leaving
15665cdd929dSBoris Brezillon * ->len or ->ooblen uninitialized. Force ->len and ->ooblen to 0 in
15675cdd929dSBoris Brezillon * this case.
15685cdd929dSBoris Brezillon */
15695cdd929dSBoris Brezillon if (!ops->datbuf)
15705cdd929dSBoris Brezillon ops->len = 0;
15715cdd929dSBoris Brezillon
15725cdd929dSBoris Brezillon if (!ops->oobbuf)
15735cdd929dSBoris Brezillon ops->ooblen = 0;
15745cdd929dSBoris Brezillon
1575d82c3682SMiquel Raynal if (offs < 0 || offs + ops->len > mtd->size)
15765cdd929dSBoris Brezillon return -EINVAL;
15775cdd929dSBoris Brezillon
15785cdd929dSBoris Brezillon if (ops->ooblen) {
157989f706dbSMiquel Raynal size_t maxooblen;
15805cdd929dSBoris Brezillon
15815cdd929dSBoris Brezillon if (ops->ooboffs >= mtd_oobavail(mtd, ops))
15825cdd929dSBoris Brezillon return -EINVAL;
15835cdd929dSBoris Brezillon
158489f706dbSMiquel Raynal maxooblen = ((size_t)(mtd_div_by_ws(mtd->size, mtd) -
15855cdd929dSBoris Brezillon mtd_div_by_ws(offs, mtd)) *
15865cdd929dSBoris Brezillon mtd_oobavail(mtd, ops)) - ops->ooboffs;
15875cdd929dSBoris Brezillon if (ops->ooblen > maxooblen)
15885cdd929dSBoris Brezillon return -EINVAL;
15895cdd929dSBoris Brezillon }
15905cdd929dSBoris Brezillon
15915cdd929dSBoris Brezillon return 0;
15925cdd929dSBoris Brezillon }
15935cdd929dSBoris Brezillon
mtd_read_oob_std(struct mtd_info * mtd,loff_t from,struct mtd_oob_ops * ops)15949e3307a1SBoris Brezillon static int mtd_read_oob_std(struct mtd_info *mtd, loff_t from,
15959e3307a1SBoris Brezillon struct mtd_oob_ops *ops)
15969e3307a1SBoris Brezillon {
15979e3307a1SBoris Brezillon struct mtd_info *master = mtd_get_master(mtd);
15989e3307a1SBoris Brezillon int ret;
15999e3307a1SBoris Brezillon
16009e3307a1SBoris Brezillon from = mtd_get_master_ofs(mtd, from);
16019e3307a1SBoris Brezillon if (master->_read_oob)
16029e3307a1SBoris Brezillon ret = master->_read_oob(master, from, ops);
16039e3307a1SBoris Brezillon else
16049e3307a1SBoris Brezillon ret = master->_read(master, from, ops->len, &ops->retlen,
16059e3307a1SBoris Brezillon ops->datbuf);
16069e3307a1SBoris Brezillon
16079e3307a1SBoris Brezillon return ret;
16089e3307a1SBoris Brezillon }
16099e3307a1SBoris Brezillon
mtd_write_oob_std(struct mtd_info * mtd,loff_t to,struct mtd_oob_ops * ops)16109e3307a1SBoris Brezillon static int mtd_write_oob_std(struct mtd_info *mtd, loff_t to,
16119e3307a1SBoris Brezillon struct mtd_oob_ops *ops)
16129e3307a1SBoris Brezillon {
16139e3307a1SBoris Brezillon struct mtd_info *master = mtd_get_master(mtd);
16149e3307a1SBoris Brezillon int ret;
16159e3307a1SBoris Brezillon
16169e3307a1SBoris Brezillon to = mtd_get_master_ofs(mtd, to);
16179e3307a1SBoris Brezillon if (master->_write_oob)
16189e3307a1SBoris Brezillon ret = master->_write_oob(master, to, ops);
16199e3307a1SBoris Brezillon else
16209e3307a1SBoris Brezillon ret = master->_write(master, to, ops->len, &ops->retlen,
16219e3307a1SBoris Brezillon ops->datbuf);
16229e3307a1SBoris Brezillon
16239e3307a1SBoris Brezillon return ret;
16249e3307a1SBoris Brezillon }
16259e3307a1SBoris Brezillon
mtd_io_emulated_slc(struct mtd_info * mtd,loff_t start,bool read,struct mtd_oob_ops * ops)16269e3307a1SBoris Brezillon static int mtd_io_emulated_slc(struct mtd_info *mtd, loff_t start, bool read,
16279e3307a1SBoris Brezillon struct mtd_oob_ops *ops)
16289e3307a1SBoris Brezillon {
16299e3307a1SBoris Brezillon struct mtd_info *master = mtd_get_master(mtd);
16309e3307a1SBoris Brezillon int ngroups = mtd_pairing_groups(master);
16319e3307a1SBoris Brezillon int npairs = mtd_wunit_per_eb(master) / ngroups;
16329e3307a1SBoris Brezillon struct mtd_oob_ops adjops = *ops;
16339e3307a1SBoris Brezillon unsigned int wunit, oobavail;
16349e3307a1SBoris Brezillon struct mtd_pairing_info info;
16359e3307a1SBoris Brezillon int max_bitflips = 0;
16369e3307a1SBoris Brezillon u32 ebofs, pageofs;
16379e3307a1SBoris Brezillon loff_t base, pos;
16389e3307a1SBoris Brezillon
16399e3307a1SBoris Brezillon ebofs = mtd_mod_by_eb(start, mtd);
16409e3307a1SBoris Brezillon base = (loff_t)mtd_div_by_eb(start, mtd) * master->erasesize;
16419e3307a1SBoris Brezillon info.group = 0;
16429e3307a1SBoris Brezillon info.pair = mtd_div_by_ws(ebofs, mtd);
16439e3307a1SBoris Brezillon pageofs = mtd_mod_by_ws(ebofs, mtd);
16449e3307a1SBoris Brezillon oobavail = mtd_oobavail(mtd, ops);
16459e3307a1SBoris Brezillon
16469e3307a1SBoris Brezillon while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) {
16479e3307a1SBoris Brezillon int ret;
16489e3307a1SBoris Brezillon
16499e3307a1SBoris Brezillon if (info.pair >= npairs) {
16509e3307a1SBoris Brezillon info.pair = 0;
16519e3307a1SBoris Brezillon base += master->erasesize;
16529e3307a1SBoris Brezillon }
16539e3307a1SBoris Brezillon
16549e3307a1SBoris Brezillon wunit = mtd_pairing_info_to_wunit(master, &info);
16559e3307a1SBoris Brezillon pos = mtd_wunit_to_offset(mtd, base, wunit);
16569e3307a1SBoris Brezillon
16579e3307a1SBoris Brezillon adjops.len = ops->len - ops->retlen;
16589e3307a1SBoris Brezillon if (adjops.len > mtd->writesize - pageofs)
16599e3307a1SBoris Brezillon adjops.len = mtd->writesize - pageofs;
16609e3307a1SBoris Brezillon
16619e3307a1SBoris Brezillon adjops.ooblen = ops->ooblen - ops->oobretlen;
16629e3307a1SBoris Brezillon if (adjops.ooblen > oobavail - adjops.ooboffs)
16639e3307a1SBoris Brezillon adjops.ooblen = oobavail - adjops.ooboffs;
16649e3307a1SBoris Brezillon
16659e3307a1SBoris Brezillon if (read) {
16669e3307a1SBoris Brezillon ret = mtd_read_oob_std(mtd, pos + pageofs, &adjops);
16679e3307a1SBoris Brezillon if (ret > 0)
16689e3307a1SBoris Brezillon max_bitflips = max(max_bitflips, ret);
16699e3307a1SBoris Brezillon } else {
16709e3307a1SBoris Brezillon ret = mtd_write_oob_std(mtd, pos + pageofs, &adjops);
16719e3307a1SBoris Brezillon }
16729e3307a1SBoris Brezillon
16739e3307a1SBoris Brezillon if (ret < 0)
16749e3307a1SBoris Brezillon return ret;
16759e3307a1SBoris Brezillon
16769e3307a1SBoris Brezillon max_bitflips = max(max_bitflips, ret);
16779e3307a1SBoris Brezillon ops->retlen += adjops.retlen;
16789e3307a1SBoris Brezillon ops->oobretlen += adjops.oobretlen;
16799e3307a1SBoris Brezillon adjops.datbuf += adjops.retlen;
16809e3307a1SBoris Brezillon adjops.oobbuf += adjops.oobretlen;
16819e3307a1SBoris Brezillon adjops.ooboffs = 0;
16829e3307a1SBoris Brezillon pageofs = 0;
16839e3307a1SBoris Brezillon info.pair++;
16849e3307a1SBoris Brezillon }
16859e3307a1SBoris Brezillon
16869e3307a1SBoris Brezillon return max_bitflips;
16879e3307a1SBoris Brezillon }
16889e3307a1SBoris Brezillon
mtd_read_oob(struct mtd_info * mtd,loff_t from,struct mtd_oob_ops * ops)1689d2d48480SBrian Norris int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
1690d2d48480SBrian Norris {
169146b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
169246b5889cSMiquel Raynal struct mtd_ecc_stats old_stats = master->ecc_stats;
1693e47f6858SBrian Norris int ret_code;
169446b5889cSMiquel Raynal
1695d2d48480SBrian Norris ops->retlen = ops->oobretlen = 0;
1696fea728c0SEzequiel Garcia
16975cdd929dSBoris Brezillon ret_code = mtd_check_oob_ops(mtd, from, ops);
16985cdd929dSBoris Brezillon if (ret_code)
16995cdd929dSBoris Brezillon return ret_code;
17005cdd929dSBoris Brezillon
1701fea728c0SEzequiel Garcia ledtrig_mtd_activity();
170289fd23efSMiquel Raynal
170389fd23efSMiquel Raynal /* Check the validity of a potential fallback on mtd->_read */
170446b5889cSMiquel Raynal if (!master->_read_oob && (!master->_read || ops->oobbuf))
170589fd23efSMiquel Raynal return -EOPNOTSUPP;
170689fd23efSMiquel Raynal
170765394169SMichał Kępień if (ops->stats)
170865394169SMichał Kępień memset(ops->stats, 0, sizeof(*ops->stats));
170965394169SMichał Kępień
17109e3307a1SBoris Brezillon if (mtd->flags & MTD_SLC_ON_MLC_EMULATION)
17119e3307a1SBoris Brezillon ret_code = mtd_io_emulated_slc(mtd, from, true, ops);
171289fd23efSMiquel Raynal else
17139e3307a1SBoris Brezillon ret_code = mtd_read_oob_std(mtd, from, ops);
171489fd23efSMiquel Raynal
171546b5889cSMiquel Raynal mtd_update_ecc_stats(mtd, master, &old_stats);
171646b5889cSMiquel Raynal
1717e47f6858SBrian Norris /*
1718e47f6858SBrian Norris * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics
1719e47f6858SBrian Norris * similar to mtd->_read(), returning a non-negative integer
1720e47f6858SBrian Norris * representing max bitflips. In other cases, mtd->_read_oob() may
1721e47f6858SBrian Norris * return -EUCLEAN. In all cases, perform similar logic to mtd_read().
1722e47f6858SBrian Norris */
1723e47f6858SBrian Norris if (unlikely(ret_code < 0))
1724e47f6858SBrian Norris return ret_code;
1725e47f6858SBrian Norris if (mtd->ecc_strength == 0)
1726e47f6858SBrian Norris return 0; /* device lacks ecc */
172765394169SMichał Kępień if (ops->stats)
172865394169SMichał Kępień ops->stats->max_bitflips = ret_code;
1729e47f6858SBrian Norris return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
1730d2d48480SBrian Norris }
1731d2d48480SBrian Norris EXPORT_SYMBOL_GPL(mtd_read_oob);
1732d2d48480SBrian Norris
mtd_write_oob(struct mtd_info * mtd,loff_t to,struct mtd_oob_ops * ops)17330c034fe3SEzequiel Garcia int mtd_write_oob(struct mtd_info *mtd, loff_t to,
17340c034fe3SEzequiel Garcia struct mtd_oob_ops *ops)
17350c034fe3SEzequiel Garcia {
173646b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
17375cdd929dSBoris Brezillon int ret;
17385cdd929dSBoris Brezillon
17390c034fe3SEzequiel Garcia ops->retlen = ops->oobretlen = 0;
174089fd23efSMiquel Raynal
17410c034fe3SEzequiel Garcia if (!(mtd->flags & MTD_WRITEABLE))
17420c034fe3SEzequiel Garcia return -EROFS;
17435cdd929dSBoris Brezillon
17445cdd929dSBoris Brezillon ret = mtd_check_oob_ops(mtd, to, ops);
17455cdd929dSBoris Brezillon if (ret)
17465cdd929dSBoris Brezillon return ret;
17475cdd929dSBoris Brezillon
1748fea728c0SEzequiel Garcia ledtrig_mtd_activity();
174989fd23efSMiquel Raynal
175089fd23efSMiquel Raynal /* Check the validity of a potential fallback on mtd->_write */
175146b5889cSMiquel Raynal if (!master->_write_oob && (!master->_write || ops->oobbuf))
175289fd23efSMiquel Raynal return -EOPNOTSUPP;
175389fd23efSMiquel Raynal
17549e3307a1SBoris Brezillon if (mtd->flags & MTD_SLC_ON_MLC_EMULATION)
17559e3307a1SBoris Brezillon return mtd_io_emulated_slc(mtd, to, false, ops);
175646b5889cSMiquel Raynal
17579e3307a1SBoris Brezillon return mtd_write_oob_std(mtd, to, ops);
17580c034fe3SEzequiel Garcia }
17590c034fe3SEzequiel Garcia EXPORT_SYMBOL_GPL(mtd_write_oob);
17600c034fe3SEzequiel Garcia
176175eb2cecSBoris Brezillon /**
176275eb2cecSBoris Brezillon * mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section
176375eb2cecSBoris Brezillon * @mtd: MTD device structure
176475eb2cecSBoris Brezillon * @section: ECC section. Depending on the layout you may have all the ECC
176575eb2cecSBoris Brezillon * bytes stored in a single contiguous section, or one section
176675eb2cecSBoris Brezillon * per ECC chunk (and sometime several sections for a single ECC
176775eb2cecSBoris Brezillon * ECC chunk)
176875eb2cecSBoris Brezillon * @oobecc: OOB region struct filled with the appropriate ECC position
176975eb2cecSBoris Brezillon * information
177075eb2cecSBoris Brezillon *
17717da0fffbSMasahiro Yamada * This function returns ECC section information in the OOB area. If you want
177275eb2cecSBoris Brezillon * to get all the ECC bytes information, then you should call
177375eb2cecSBoris Brezillon * mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE.
177475eb2cecSBoris Brezillon *
177575eb2cecSBoris Brezillon * Returns zero on success, a negative error code otherwise.
177675eb2cecSBoris Brezillon */
mtd_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * oobecc)177775eb2cecSBoris Brezillon int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
177875eb2cecSBoris Brezillon struct mtd_oob_region *oobecc)
177975eb2cecSBoris Brezillon {
178046b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
178146b5889cSMiquel Raynal
178275eb2cecSBoris Brezillon memset(oobecc, 0, sizeof(*oobecc));
178375eb2cecSBoris Brezillon
178446b5889cSMiquel Raynal if (!master || section < 0)
178575eb2cecSBoris Brezillon return -EINVAL;
178675eb2cecSBoris Brezillon
178746b5889cSMiquel Raynal if (!master->ooblayout || !master->ooblayout->ecc)
178875eb2cecSBoris Brezillon return -ENOTSUPP;
178975eb2cecSBoris Brezillon
179046b5889cSMiquel Raynal return master->ooblayout->ecc(master, section, oobecc);
179175eb2cecSBoris Brezillon }
179275eb2cecSBoris Brezillon EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc);
179375eb2cecSBoris Brezillon
179475eb2cecSBoris Brezillon /**
179575eb2cecSBoris Brezillon * mtd_ooblayout_free - Get the OOB region definition of a specific free
179675eb2cecSBoris Brezillon * section
179775eb2cecSBoris Brezillon * @mtd: MTD device structure
179875eb2cecSBoris Brezillon * @section: Free section you are interested in. Depending on the layout
179975eb2cecSBoris Brezillon * you may have all the free bytes stored in a single contiguous
180075eb2cecSBoris Brezillon * section, or one section per ECC chunk plus an extra section
180175eb2cecSBoris Brezillon * for the remaining bytes (or other funky layout).
180275eb2cecSBoris Brezillon * @oobfree: OOB region struct filled with the appropriate free position
180375eb2cecSBoris Brezillon * information
180475eb2cecSBoris Brezillon *
18057da0fffbSMasahiro Yamada * This function returns free bytes position in the OOB area. If you want
180675eb2cecSBoris Brezillon * to get all the free bytes information, then you should call
180775eb2cecSBoris Brezillon * mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE.
180875eb2cecSBoris Brezillon *
180975eb2cecSBoris Brezillon * Returns zero on success, a negative error code otherwise.
181075eb2cecSBoris Brezillon */
mtd_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * oobfree)181175eb2cecSBoris Brezillon int mtd_ooblayout_free(struct mtd_info *mtd, int section,
181275eb2cecSBoris Brezillon struct mtd_oob_region *oobfree)
181375eb2cecSBoris Brezillon {
181446b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
181546b5889cSMiquel Raynal
181675eb2cecSBoris Brezillon memset(oobfree, 0, sizeof(*oobfree));
181775eb2cecSBoris Brezillon
181846b5889cSMiquel Raynal if (!master || section < 0)
181975eb2cecSBoris Brezillon return -EINVAL;
182075eb2cecSBoris Brezillon
182146b5889cSMiquel Raynal if (!master->ooblayout || !master->ooblayout->free)
182275eb2cecSBoris Brezillon return -ENOTSUPP;
182375eb2cecSBoris Brezillon
182446b5889cSMiquel Raynal return master->ooblayout->free(master, section, oobfree);
182575eb2cecSBoris Brezillon }
182675eb2cecSBoris Brezillon EXPORT_SYMBOL_GPL(mtd_ooblayout_free);
182775eb2cecSBoris Brezillon
182875eb2cecSBoris Brezillon /**
182975eb2cecSBoris Brezillon * mtd_ooblayout_find_region - Find the region attached to a specific byte
183075eb2cecSBoris Brezillon * @mtd: mtd info structure
183175eb2cecSBoris Brezillon * @byte: the byte we are searching for
183275eb2cecSBoris Brezillon * @sectionp: pointer where the section id will be stored
183375eb2cecSBoris Brezillon * @oobregion: used to retrieve the ECC position
183475eb2cecSBoris Brezillon * @iter: iterator function. Should be either mtd_ooblayout_free or
183575eb2cecSBoris Brezillon * mtd_ooblayout_ecc depending on the region type you're searching for
183675eb2cecSBoris Brezillon *
18377da0fffbSMasahiro Yamada * This function returns the section id and oobregion information of a
183875eb2cecSBoris Brezillon * specific byte. For example, say you want to know where the 4th ECC byte is
183975eb2cecSBoris Brezillon * stored, you'll use:
184075eb2cecSBoris Brezillon *
184175eb2cecSBoris Brezillon * mtd_ooblayout_find_region(mtd, 3, §ion, &oobregion, mtd_ooblayout_ecc);
184275eb2cecSBoris Brezillon *
184375eb2cecSBoris Brezillon * Returns zero on success, a negative error code otherwise.
184475eb2cecSBoris Brezillon */
mtd_ooblayout_find_region(struct mtd_info * mtd,int byte,int * sectionp,struct mtd_oob_region * oobregion,int (* iter)(struct mtd_info *,int section,struct mtd_oob_region * oobregion))184575eb2cecSBoris Brezillon static int mtd_ooblayout_find_region(struct mtd_info *mtd, int byte,
184675eb2cecSBoris Brezillon int *sectionp, struct mtd_oob_region *oobregion,
184775eb2cecSBoris Brezillon int (*iter)(struct mtd_info *,
184875eb2cecSBoris Brezillon int section,
184975eb2cecSBoris Brezillon struct mtd_oob_region *oobregion))
185075eb2cecSBoris Brezillon {
185175eb2cecSBoris Brezillon int pos = 0, ret, section = 0;
185275eb2cecSBoris Brezillon
185375eb2cecSBoris Brezillon memset(oobregion, 0, sizeof(*oobregion));
185475eb2cecSBoris Brezillon
185575eb2cecSBoris Brezillon while (1) {
185675eb2cecSBoris Brezillon ret = iter(mtd, section, oobregion);
185775eb2cecSBoris Brezillon if (ret)
185875eb2cecSBoris Brezillon return ret;
185975eb2cecSBoris Brezillon
186075eb2cecSBoris Brezillon if (pos + oobregion->length > byte)
186175eb2cecSBoris Brezillon break;
186275eb2cecSBoris Brezillon
186375eb2cecSBoris Brezillon pos += oobregion->length;
186475eb2cecSBoris Brezillon section++;
186575eb2cecSBoris Brezillon }
186675eb2cecSBoris Brezillon
186775eb2cecSBoris Brezillon /*
186875eb2cecSBoris Brezillon * Adjust region info to make it start at the beginning at the
186975eb2cecSBoris Brezillon * 'start' ECC byte.
187075eb2cecSBoris Brezillon */
187175eb2cecSBoris Brezillon oobregion->offset += byte - pos;
187275eb2cecSBoris Brezillon oobregion->length -= byte - pos;
187375eb2cecSBoris Brezillon *sectionp = section;
187475eb2cecSBoris Brezillon
187575eb2cecSBoris Brezillon return 0;
187675eb2cecSBoris Brezillon }
187775eb2cecSBoris Brezillon
187875eb2cecSBoris Brezillon /**
187975eb2cecSBoris Brezillon * mtd_ooblayout_find_eccregion - Find the ECC region attached to a specific
188075eb2cecSBoris Brezillon * ECC byte
188175eb2cecSBoris Brezillon * @mtd: mtd info structure
188275eb2cecSBoris Brezillon * @eccbyte: the byte we are searching for
18836361f536SLee Jones * @section: pointer where the section id will be stored
188475eb2cecSBoris Brezillon * @oobregion: OOB region information
188575eb2cecSBoris Brezillon *
188675eb2cecSBoris Brezillon * Works like mtd_ooblayout_find_region() except it searches for a specific ECC
188775eb2cecSBoris Brezillon * byte.
188875eb2cecSBoris Brezillon *
188975eb2cecSBoris Brezillon * Returns zero on success, a negative error code otherwise.
189075eb2cecSBoris Brezillon */
mtd_ooblayout_find_eccregion(struct mtd_info * mtd,int eccbyte,int * section,struct mtd_oob_region * oobregion)189175eb2cecSBoris Brezillon int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
189275eb2cecSBoris Brezillon int *section,
189375eb2cecSBoris Brezillon struct mtd_oob_region *oobregion)
189475eb2cecSBoris Brezillon {
189575eb2cecSBoris Brezillon return mtd_ooblayout_find_region(mtd, eccbyte, section, oobregion,
189675eb2cecSBoris Brezillon mtd_ooblayout_ecc);
189775eb2cecSBoris Brezillon }
189875eb2cecSBoris Brezillon EXPORT_SYMBOL_GPL(mtd_ooblayout_find_eccregion);
189975eb2cecSBoris Brezillon
190075eb2cecSBoris Brezillon /**
190175eb2cecSBoris Brezillon * mtd_ooblayout_get_bytes - Extract OOB bytes from the oob buffer
190275eb2cecSBoris Brezillon * @mtd: mtd info structure
190375eb2cecSBoris Brezillon * @buf: destination buffer to store OOB bytes
190475eb2cecSBoris Brezillon * @oobbuf: OOB buffer
190575eb2cecSBoris Brezillon * @start: first byte to retrieve
190675eb2cecSBoris Brezillon * @nbytes: number of bytes to retrieve
190775eb2cecSBoris Brezillon * @iter: section iterator
190875eb2cecSBoris Brezillon *
190975eb2cecSBoris Brezillon * Extract bytes attached to a specific category (ECC or free)
191075eb2cecSBoris Brezillon * from the OOB buffer and copy them into buf.
191175eb2cecSBoris Brezillon *
191275eb2cecSBoris Brezillon * Returns zero on success, a negative error code otherwise.
191375eb2cecSBoris Brezillon */
mtd_ooblayout_get_bytes(struct mtd_info * mtd,u8 * buf,const u8 * oobbuf,int start,int nbytes,int (* iter)(struct mtd_info *,int section,struct mtd_oob_region * oobregion))191475eb2cecSBoris Brezillon static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf,
191575eb2cecSBoris Brezillon const u8 *oobbuf, int start, int nbytes,
191675eb2cecSBoris Brezillon int (*iter)(struct mtd_info *,
191775eb2cecSBoris Brezillon int section,
191875eb2cecSBoris Brezillon struct mtd_oob_region *oobregion))
191975eb2cecSBoris Brezillon {
19208e8fd4d1SMasahiro Yamada struct mtd_oob_region oobregion;
19218e8fd4d1SMasahiro Yamada int section, ret;
192275eb2cecSBoris Brezillon
192375eb2cecSBoris Brezillon ret = mtd_ooblayout_find_region(mtd, start, §ion,
192475eb2cecSBoris Brezillon &oobregion, iter);
192575eb2cecSBoris Brezillon
192675eb2cecSBoris Brezillon while (!ret) {
192775eb2cecSBoris Brezillon int cnt;
192875eb2cecSBoris Brezillon
19297c295ef9SMasahiro Yamada cnt = min_t(int, nbytes, oobregion.length);
193075eb2cecSBoris Brezillon memcpy(buf, oobbuf + oobregion.offset, cnt);
193175eb2cecSBoris Brezillon buf += cnt;
193275eb2cecSBoris Brezillon nbytes -= cnt;
193375eb2cecSBoris Brezillon
193475eb2cecSBoris Brezillon if (!nbytes)
193575eb2cecSBoris Brezillon break;
193675eb2cecSBoris Brezillon
193775eb2cecSBoris Brezillon ret = iter(mtd, ++section, &oobregion);
193875eb2cecSBoris Brezillon }
193975eb2cecSBoris Brezillon
194075eb2cecSBoris Brezillon return ret;
194175eb2cecSBoris Brezillon }
194275eb2cecSBoris Brezillon
194375eb2cecSBoris Brezillon /**
194475eb2cecSBoris Brezillon * mtd_ooblayout_set_bytes - put OOB bytes into the oob buffer
194575eb2cecSBoris Brezillon * @mtd: mtd info structure
194675eb2cecSBoris Brezillon * @buf: source buffer to get OOB bytes from
194775eb2cecSBoris Brezillon * @oobbuf: OOB buffer
194875eb2cecSBoris Brezillon * @start: first OOB byte to set
194975eb2cecSBoris Brezillon * @nbytes: number of OOB bytes to set
195075eb2cecSBoris Brezillon * @iter: section iterator
195175eb2cecSBoris Brezillon *
195275eb2cecSBoris Brezillon * Fill the OOB buffer with data provided in buf. The category (ECC or free)
195375eb2cecSBoris Brezillon * is selected by passing the appropriate iterator.
195475eb2cecSBoris Brezillon *
195575eb2cecSBoris Brezillon * Returns zero on success, a negative error code otherwise.
195675eb2cecSBoris Brezillon */
mtd_ooblayout_set_bytes(struct mtd_info * mtd,const u8 * buf,u8 * oobbuf,int start,int nbytes,int (* iter)(struct mtd_info *,int section,struct mtd_oob_region * oobregion))195775eb2cecSBoris Brezillon static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf,
195875eb2cecSBoris Brezillon u8 *oobbuf, int start, int nbytes,
195975eb2cecSBoris Brezillon int (*iter)(struct mtd_info *,
196075eb2cecSBoris Brezillon int section,
196175eb2cecSBoris Brezillon struct mtd_oob_region *oobregion))
196275eb2cecSBoris Brezillon {
19638e8fd4d1SMasahiro Yamada struct mtd_oob_region oobregion;
19648e8fd4d1SMasahiro Yamada int section, ret;
196575eb2cecSBoris Brezillon
196675eb2cecSBoris Brezillon ret = mtd_ooblayout_find_region(mtd, start, §ion,
196775eb2cecSBoris Brezillon &oobregion, iter);
196875eb2cecSBoris Brezillon
196975eb2cecSBoris Brezillon while (!ret) {
197075eb2cecSBoris Brezillon int cnt;
197175eb2cecSBoris Brezillon
19727c295ef9SMasahiro Yamada cnt = min_t(int, nbytes, oobregion.length);
197375eb2cecSBoris Brezillon memcpy(oobbuf + oobregion.offset, buf, cnt);
197475eb2cecSBoris Brezillon buf += cnt;
197575eb2cecSBoris Brezillon nbytes -= cnt;
197675eb2cecSBoris Brezillon
197775eb2cecSBoris Brezillon if (!nbytes)
197875eb2cecSBoris Brezillon break;
197975eb2cecSBoris Brezillon
198075eb2cecSBoris Brezillon ret = iter(mtd, ++section, &oobregion);
198175eb2cecSBoris Brezillon }
198275eb2cecSBoris Brezillon
198375eb2cecSBoris Brezillon return ret;
198475eb2cecSBoris Brezillon }
198575eb2cecSBoris Brezillon
198675eb2cecSBoris Brezillon /**
198775eb2cecSBoris Brezillon * mtd_ooblayout_count_bytes - count the number of bytes in a OOB category
198875eb2cecSBoris Brezillon * @mtd: mtd info structure
198975eb2cecSBoris Brezillon * @iter: category iterator
199075eb2cecSBoris Brezillon *
199175eb2cecSBoris Brezillon * Count the number of bytes in a given category.
199275eb2cecSBoris Brezillon *
199375eb2cecSBoris Brezillon * Returns a positive value on success, a negative error code otherwise.
199475eb2cecSBoris Brezillon */
mtd_ooblayout_count_bytes(struct mtd_info * mtd,int (* iter)(struct mtd_info *,int section,struct mtd_oob_region * oobregion))199575eb2cecSBoris Brezillon static int mtd_ooblayout_count_bytes(struct mtd_info *mtd,
199675eb2cecSBoris Brezillon int (*iter)(struct mtd_info *,
199775eb2cecSBoris Brezillon int section,
199875eb2cecSBoris Brezillon struct mtd_oob_region *oobregion))
199975eb2cecSBoris Brezillon {
20004d6aecfbSMasahiro Yamada struct mtd_oob_region oobregion;
200175eb2cecSBoris Brezillon int section = 0, ret, nbytes = 0;
200275eb2cecSBoris Brezillon
200375eb2cecSBoris Brezillon while (1) {
200475eb2cecSBoris Brezillon ret = iter(mtd, section++, &oobregion);
200575eb2cecSBoris Brezillon if (ret) {
200675eb2cecSBoris Brezillon if (ret == -ERANGE)
200775eb2cecSBoris Brezillon ret = nbytes;
200875eb2cecSBoris Brezillon break;
200975eb2cecSBoris Brezillon }
201075eb2cecSBoris Brezillon
201175eb2cecSBoris Brezillon nbytes += oobregion.length;
201275eb2cecSBoris Brezillon }
201375eb2cecSBoris Brezillon
201475eb2cecSBoris Brezillon return ret;
201575eb2cecSBoris Brezillon }
201675eb2cecSBoris Brezillon
201775eb2cecSBoris Brezillon /**
201875eb2cecSBoris Brezillon * mtd_ooblayout_get_eccbytes - extract ECC bytes from the oob buffer
201975eb2cecSBoris Brezillon * @mtd: mtd info structure
202075eb2cecSBoris Brezillon * @eccbuf: destination buffer to store ECC bytes
202175eb2cecSBoris Brezillon * @oobbuf: OOB buffer
202275eb2cecSBoris Brezillon * @start: first ECC byte to retrieve
202375eb2cecSBoris Brezillon * @nbytes: number of ECC bytes to retrieve
202475eb2cecSBoris Brezillon *
202575eb2cecSBoris Brezillon * Works like mtd_ooblayout_get_bytes(), except it acts on ECC bytes.
202675eb2cecSBoris Brezillon *
202775eb2cecSBoris Brezillon * Returns zero on success, a negative error code otherwise.
202875eb2cecSBoris Brezillon */
mtd_ooblayout_get_eccbytes(struct mtd_info * mtd,u8 * eccbuf,const u8 * oobbuf,int start,int nbytes)202975eb2cecSBoris Brezillon int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf,
203075eb2cecSBoris Brezillon const u8 *oobbuf, int start, int nbytes)
203175eb2cecSBoris Brezillon {
203275eb2cecSBoris Brezillon return mtd_ooblayout_get_bytes(mtd, eccbuf, oobbuf, start, nbytes,
203375eb2cecSBoris Brezillon mtd_ooblayout_ecc);
203475eb2cecSBoris Brezillon }
203575eb2cecSBoris Brezillon EXPORT_SYMBOL_GPL(mtd_ooblayout_get_eccbytes);
203675eb2cecSBoris Brezillon
203775eb2cecSBoris Brezillon /**
203875eb2cecSBoris Brezillon * mtd_ooblayout_set_eccbytes - set ECC bytes into the oob buffer
203975eb2cecSBoris Brezillon * @mtd: mtd info structure
204075eb2cecSBoris Brezillon * @eccbuf: source buffer to get ECC bytes from
204175eb2cecSBoris Brezillon * @oobbuf: OOB buffer
204275eb2cecSBoris Brezillon * @start: first ECC byte to set
204375eb2cecSBoris Brezillon * @nbytes: number of ECC bytes to set
204475eb2cecSBoris Brezillon *
204575eb2cecSBoris Brezillon * Works like mtd_ooblayout_set_bytes(), except it acts on ECC bytes.
204675eb2cecSBoris Brezillon *
204775eb2cecSBoris Brezillon * Returns zero on success, a negative error code otherwise.
204875eb2cecSBoris Brezillon */
mtd_ooblayout_set_eccbytes(struct mtd_info * mtd,const u8 * eccbuf,u8 * oobbuf,int start,int nbytes)204975eb2cecSBoris Brezillon int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf,
205075eb2cecSBoris Brezillon u8 *oobbuf, int start, int nbytes)
205175eb2cecSBoris Brezillon {
205275eb2cecSBoris Brezillon return mtd_ooblayout_set_bytes(mtd, eccbuf, oobbuf, start, nbytes,
205375eb2cecSBoris Brezillon mtd_ooblayout_ecc);
205475eb2cecSBoris Brezillon }
205575eb2cecSBoris Brezillon EXPORT_SYMBOL_GPL(mtd_ooblayout_set_eccbytes);
205675eb2cecSBoris Brezillon
205775eb2cecSBoris Brezillon /**
205875eb2cecSBoris Brezillon * mtd_ooblayout_get_databytes - extract data bytes from the oob buffer
205975eb2cecSBoris Brezillon * @mtd: mtd info structure
206075eb2cecSBoris Brezillon * @databuf: destination buffer to store ECC bytes
206175eb2cecSBoris Brezillon * @oobbuf: OOB buffer
206275eb2cecSBoris Brezillon * @start: first ECC byte to retrieve
206375eb2cecSBoris Brezillon * @nbytes: number of ECC bytes to retrieve
206475eb2cecSBoris Brezillon *
206575eb2cecSBoris Brezillon * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes.
206675eb2cecSBoris Brezillon *
206775eb2cecSBoris Brezillon * Returns zero on success, a negative error code otherwise.
206875eb2cecSBoris Brezillon */
mtd_ooblayout_get_databytes(struct mtd_info * mtd,u8 * databuf,const u8 * oobbuf,int start,int nbytes)206975eb2cecSBoris Brezillon int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf,
207075eb2cecSBoris Brezillon const u8 *oobbuf, int start, int nbytes)
207175eb2cecSBoris Brezillon {
207275eb2cecSBoris Brezillon return mtd_ooblayout_get_bytes(mtd, databuf, oobbuf, start, nbytes,
207375eb2cecSBoris Brezillon mtd_ooblayout_free);
207475eb2cecSBoris Brezillon }
207575eb2cecSBoris Brezillon EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes);
207675eb2cecSBoris Brezillon
207775eb2cecSBoris Brezillon /**
2078c77a9312SXiaolei Li * mtd_ooblayout_set_databytes - set data bytes into the oob buffer
207975eb2cecSBoris Brezillon * @mtd: mtd info structure
2080c77a9312SXiaolei Li * @databuf: source buffer to get data bytes from
208175eb2cecSBoris Brezillon * @oobbuf: OOB buffer
208275eb2cecSBoris Brezillon * @start: first ECC byte to set
208375eb2cecSBoris Brezillon * @nbytes: number of ECC bytes to set
208475eb2cecSBoris Brezillon *
2085519494a9SMiquel Raynal * Works like mtd_ooblayout_set_bytes(), except it acts on free bytes.
208675eb2cecSBoris Brezillon *
208775eb2cecSBoris Brezillon * Returns zero on success, a negative error code otherwise.
208875eb2cecSBoris Brezillon */
mtd_ooblayout_set_databytes(struct mtd_info * mtd,const u8 * databuf,u8 * oobbuf,int start,int nbytes)208975eb2cecSBoris Brezillon int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
209075eb2cecSBoris Brezillon u8 *oobbuf, int start, int nbytes)
209175eb2cecSBoris Brezillon {
209275eb2cecSBoris Brezillon return mtd_ooblayout_set_bytes(mtd, databuf, oobbuf, start, nbytes,
209375eb2cecSBoris Brezillon mtd_ooblayout_free);
209475eb2cecSBoris Brezillon }
209575eb2cecSBoris Brezillon EXPORT_SYMBOL_GPL(mtd_ooblayout_set_databytes);
209675eb2cecSBoris Brezillon
209775eb2cecSBoris Brezillon /**
209875eb2cecSBoris Brezillon * mtd_ooblayout_count_freebytes - count the number of free bytes in OOB
209975eb2cecSBoris Brezillon * @mtd: mtd info structure
210075eb2cecSBoris Brezillon *
210175eb2cecSBoris Brezillon * Works like mtd_ooblayout_count_bytes(), except it count free bytes.
210275eb2cecSBoris Brezillon *
210375eb2cecSBoris Brezillon * Returns zero on success, a negative error code otherwise.
210475eb2cecSBoris Brezillon */
mtd_ooblayout_count_freebytes(struct mtd_info * mtd)210575eb2cecSBoris Brezillon int mtd_ooblayout_count_freebytes(struct mtd_info *mtd)
210675eb2cecSBoris Brezillon {
210775eb2cecSBoris Brezillon return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_free);
210875eb2cecSBoris Brezillon }
210975eb2cecSBoris Brezillon EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes);
211075eb2cecSBoris Brezillon
211175eb2cecSBoris Brezillon /**
2112c77a9312SXiaolei Li * mtd_ooblayout_count_eccbytes - count the number of ECC bytes in OOB
211375eb2cecSBoris Brezillon * @mtd: mtd info structure
211475eb2cecSBoris Brezillon *
211575eb2cecSBoris Brezillon * Works like mtd_ooblayout_count_bytes(), except it count ECC bytes.
211675eb2cecSBoris Brezillon *
211775eb2cecSBoris Brezillon * Returns zero on success, a negative error code otherwise.
211875eb2cecSBoris Brezillon */
mtd_ooblayout_count_eccbytes(struct mtd_info * mtd)211975eb2cecSBoris Brezillon int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd)
212075eb2cecSBoris Brezillon {
212175eb2cecSBoris Brezillon return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_ecc);
212275eb2cecSBoris Brezillon }
212375eb2cecSBoris Brezillon EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes);
212475eb2cecSBoris Brezillon
2125de3cac93SArtem Bityutskiy /*
2126de3cac93SArtem Bityutskiy * Method to access the protection register area, present in some flash
2127de3cac93SArtem Bityutskiy * devices. The user data is one time programmable but the factory data is read
2128de3cac93SArtem Bityutskiy * only.
2129de3cac93SArtem Bityutskiy */
mtd_get_fact_prot_info(struct mtd_info * mtd,size_t len,size_t * retlen,struct otp_info * buf)21304b78fc42SChristian Riesch int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
21314b78fc42SChristian Riesch struct otp_info *buf)
2132de3cac93SArtem Bityutskiy {
213346b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
213446b5889cSMiquel Raynal
213546b5889cSMiquel Raynal if (!master->_get_fact_prot_info)
2136de3cac93SArtem Bityutskiy return -EOPNOTSUPP;
2137de3cac93SArtem Bityutskiy if (!len)
2138de3cac93SArtem Bityutskiy return 0;
213946b5889cSMiquel Raynal return master->_get_fact_prot_info(master, len, retlen, buf);
2140de3cac93SArtem Bityutskiy }
2141de3cac93SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info);
2142de3cac93SArtem Bityutskiy
mtd_read_fact_prot_reg(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)2143de3cac93SArtem Bityutskiy int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
2144de3cac93SArtem Bityutskiy size_t *retlen, u_char *buf)
2145de3cac93SArtem Bityutskiy {
214646b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
214746b5889cSMiquel Raynal
2148de3cac93SArtem Bityutskiy *retlen = 0;
214946b5889cSMiquel Raynal if (!master->_read_fact_prot_reg)
2150de3cac93SArtem Bityutskiy return -EOPNOTSUPP;
2151de3cac93SArtem Bityutskiy if (!len)
2152de3cac93SArtem Bityutskiy return 0;
215346b5889cSMiquel Raynal return master->_read_fact_prot_reg(master, from, len, retlen, buf);
2154de3cac93SArtem Bityutskiy }
2155de3cac93SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg);
2156de3cac93SArtem Bityutskiy
mtd_get_user_prot_info(struct mtd_info * mtd,size_t len,size_t * retlen,struct otp_info * buf)21574b78fc42SChristian Riesch int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
21584b78fc42SChristian Riesch struct otp_info *buf)
2159de3cac93SArtem Bityutskiy {
216046b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
216146b5889cSMiquel Raynal
216246b5889cSMiquel Raynal if (!master->_get_user_prot_info)
2163de3cac93SArtem Bityutskiy return -EOPNOTSUPP;
2164de3cac93SArtem Bityutskiy if (!len)
2165de3cac93SArtem Bityutskiy return 0;
216646b5889cSMiquel Raynal return master->_get_user_prot_info(master, len, retlen, buf);
2167de3cac93SArtem Bityutskiy }
2168de3cac93SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_get_user_prot_info);
2169de3cac93SArtem Bityutskiy
mtd_read_user_prot_reg(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)2170de3cac93SArtem Bityutskiy int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
2171de3cac93SArtem Bityutskiy size_t *retlen, u_char *buf)
2172de3cac93SArtem Bityutskiy {
217346b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
217446b5889cSMiquel Raynal
2175de3cac93SArtem Bityutskiy *retlen = 0;
217646b5889cSMiquel Raynal if (!master->_read_user_prot_reg)
2177de3cac93SArtem Bityutskiy return -EOPNOTSUPP;
2178de3cac93SArtem Bityutskiy if (!len)
2179de3cac93SArtem Bityutskiy return 0;
218046b5889cSMiquel Raynal return master->_read_user_prot_reg(master, from, len, retlen, buf);
2181de3cac93SArtem Bityutskiy }
2182de3cac93SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg);
2183de3cac93SArtem Bityutskiy
mtd_write_user_prot_reg(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u_char * buf)2184de3cac93SArtem Bityutskiy int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
21851df1fc8cSTudor Ambarus size_t *retlen, const u_char *buf)
2186de3cac93SArtem Bityutskiy {
218746b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
21889a78bc83SChristian Riesch int ret;
21899a78bc83SChristian Riesch
2190de3cac93SArtem Bityutskiy *retlen = 0;
219146b5889cSMiquel Raynal if (!master->_write_user_prot_reg)
2192de3cac93SArtem Bityutskiy return -EOPNOTSUPP;
2193de3cac93SArtem Bityutskiy if (!len)
2194de3cac93SArtem Bityutskiy return 0;
219546b5889cSMiquel Raynal ret = master->_write_user_prot_reg(master, to, len, retlen, buf);
21969a78bc83SChristian Riesch if (ret)
21979a78bc83SChristian Riesch return ret;
21989a78bc83SChristian Riesch
21999a78bc83SChristian Riesch /*
22009a78bc83SChristian Riesch * If no data could be written at all, we are out of memory and
22019a78bc83SChristian Riesch * must return -ENOSPC.
22029a78bc83SChristian Riesch */
22039a78bc83SChristian Riesch return (*retlen) ? 0 : -ENOSPC;
2204de3cac93SArtem Bityutskiy }
2205de3cac93SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg);
2206de3cac93SArtem Bityutskiy
mtd_lock_user_prot_reg(struct mtd_info * mtd,loff_t from,size_t len)2207de3cac93SArtem Bityutskiy int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len)
2208de3cac93SArtem Bityutskiy {
220946b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
221046b5889cSMiquel Raynal
221146b5889cSMiquel Raynal if (!master->_lock_user_prot_reg)
2212de3cac93SArtem Bityutskiy return -EOPNOTSUPP;
2213de3cac93SArtem Bityutskiy if (!len)
2214de3cac93SArtem Bityutskiy return 0;
221546b5889cSMiquel Raynal return master->_lock_user_prot_reg(master, from, len);
2216de3cac93SArtem Bityutskiy }
2217de3cac93SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_lock_user_prot_reg);
2218de3cac93SArtem Bityutskiy
mtd_erase_user_prot_reg(struct mtd_info * mtd,loff_t from,size_t len)2219e3c1f1c9SMichael Walle int mtd_erase_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len)
2220e3c1f1c9SMichael Walle {
2221e3c1f1c9SMichael Walle struct mtd_info *master = mtd_get_master(mtd);
2222e3c1f1c9SMichael Walle
2223e3c1f1c9SMichael Walle if (!master->_erase_user_prot_reg)
2224e3c1f1c9SMichael Walle return -EOPNOTSUPP;
2225e3c1f1c9SMichael Walle if (!len)
2226e3c1f1c9SMichael Walle return 0;
2227e3c1f1c9SMichael Walle return master->_erase_user_prot_reg(master, from, len);
2228e3c1f1c9SMichael Walle }
2229e3c1f1c9SMichael Walle EXPORT_SYMBOL_GPL(mtd_erase_user_prot_reg);
2230e3c1f1c9SMichael Walle
22318273a0c9SArtem Bityutskiy /* Chip-supported device locking */
mtd_lock(struct mtd_info * mtd,loff_t ofs,uint64_t len)22328273a0c9SArtem Bityutskiy int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
22338273a0c9SArtem Bityutskiy {
223446b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
223546b5889cSMiquel Raynal
223646b5889cSMiquel Raynal if (!master->_lock)
22378273a0c9SArtem Bityutskiy return -EOPNOTSUPP;
22380c2b4e21SBrian Norris if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs)
22398273a0c9SArtem Bityutskiy return -EINVAL;
2240bcb1d238SArtem Bityutskiy if (!len)
2241bcb1d238SArtem Bityutskiy return 0;
22429e3307a1SBoris Brezillon
22439e3307a1SBoris Brezillon if (mtd->flags & MTD_SLC_ON_MLC_EMULATION) {
22449e3307a1SBoris Brezillon ofs = (loff_t)mtd_div_by_eb(ofs, mtd) * master->erasesize;
22459e3307a1SBoris Brezillon len = (u64)mtd_div_by_eb(len, mtd) * master->erasesize;
22469e3307a1SBoris Brezillon }
22479e3307a1SBoris Brezillon
224846b5889cSMiquel Raynal return master->_lock(master, mtd_get_master_ofs(mtd, ofs), len);
22498273a0c9SArtem Bityutskiy }
22508273a0c9SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_lock);
22518273a0c9SArtem Bityutskiy
mtd_unlock(struct mtd_info * mtd,loff_t ofs,uint64_t len)22528273a0c9SArtem Bityutskiy int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
22538273a0c9SArtem Bityutskiy {
225446b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
225546b5889cSMiquel Raynal
225646b5889cSMiquel Raynal if (!master->_unlock)
22578273a0c9SArtem Bityutskiy return -EOPNOTSUPP;
22580c2b4e21SBrian Norris if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs)
22598273a0c9SArtem Bityutskiy return -EINVAL;
2260bcb1d238SArtem Bityutskiy if (!len)
2261bcb1d238SArtem Bityutskiy return 0;
22629e3307a1SBoris Brezillon
22639e3307a1SBoris Brezillon if (mtd->flags & MTD_SLC_ON_MLC_EMULATION) {
22649e3307a1SBoris Brezillon ofs = (loff_t)mtd_div_by_eb(ofs, mtd) * master->erasesize;
22659e3307a1SBoris Brezillon len = (u64)mtd_div_by_eb(len, mtd) * master->erasesize;
22669e3307a1SBoris Brezillon }
22679e3307a1SBoris Brezillon
226846b5889cSMiquel Raynal return master->_unlock(master, mtd_get_master_ofs(mtd, ofs), len);
22698273a0c9SArtem Bityutskiy }
22708273a0c9SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_unlock);
22718273a0c9SArtem Bityutskiy
mtd_is_locked(struct mtd_info * mtd,loff_t ofs,uint64_t len)22728273a0c9SArtem Bityutskiy int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
22738273a0c9SArtem Bityutskiy {
227446b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
227546b5889cSMiquel Raynal
227646b5889cSMiquel Raynal if (!master->_is_locked)
22778273a0c9SArtem Bityutskiy return -EOPNOTSUPP;
22780c2b4e21SBrian Norris if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs)
22798273a0c9SArtem Bityutskiy return -EINVAL;
2280bcb1d238SArtem Bityutskiy if (!len)
2281bcb1d238SArtem Bityutskiy return 0;
22829e3307a1SBoris Brezillon
22839e3307a1SBoris Brezillon if (mtd->flags & MTD_SLC_ON_MLC_EMULATION) {
22849e3307a1SBoris Brezillon ofs = (loff_t)mtd_div_by_eb(ofs, mtd) * master->erasesize;
22859e3307a1SBoris Brezillon len = (u64)mtd_div_by_eb(len, mtd) * master->erasesize;
22869e3307a1SBoris Brezillon }
22879e3307a1SBoris Brezillon
228846b5889cSMiquel Raynal return master->_is_locked(master, mtd_get_master_ofs(mtd, ofs), len);
22898273a0c9SArtem Bityutskiy }
22908273a0c9SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_is_locked);
22918273a0c9SArtem Bityutskiy
mtd_block_isreserved(struct mtd_info * mtd,loff_t ofs)22928471bb73SEzequiel Garcia int mtd_block_isreserved(struct mtd_info *mtd, loff_t ofs)
22938273a0c9SArtem Bityutskiy {
229446b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
229546b5889cSMiquel Raynal
22960c2b4e21SBrian Norris if (ofs < 0 || ofs >= mtd->size)
22978273a0c9SArtem Bityutskiy return -EINVAL;
229846b5889cSMiquel Raynal if (!master->_block_isreserved)
22998471bb73SEzequiel Garcia return 0;
23009e3307a1SBoris Brezillon
23019e3307a1SBoris Brezillon if (mtd->flags & MTD_SLC_ON_MLC_EMULATION)
23029e3307a1SBoris Brezillon ofs = (loff_t)mtd_div_by_eb(ofs, mtd) * master->erasesize;
23039e3307a1SBoris Brezillon
230446b5889cSMiquel Raynal return master->_block_isreserved(master, mtd_get_master_ofs(mtd, ofs));
23058471bb73SEzequiel Garcia }
23068471bb73SEzequiel Garcia EXPORT_SYMBOL_GPL(mtd_block_isreserved);
23078471bb73SEzequiel Garcia
mtd_block_isbad(struct mtd_info * mtd,loff_t ofs)23088471bb73SEzequiel Garcia int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs)
23098471bb73SEzequiel Garcia {
231046b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
231146b5889cSMiquel Raynal
23120c2b4e21SBrian Norris if (ofs < 0 || ofs >= mtd->size)
23138471bb73SEzequiel Garcia return -EINVAL;
231446b5889cSMiquel Raynal if (!master->_block_isbad)
23158471bb73SEzequiel Garcia return 0;
23169e3307a1SBoris Brezillon
23179e3307a1SBoris Brezillon if (mtd->flags & MTD_SLC_ON_MLC_EMULATION)
23189e3307a1SBoris Brezillon ofs = (loff_t)mtd_div_by_eb(ofs, mtd) * master->erasesize;
23199e3307a1SBoris Brezillon
232046b5889cSMiquel Raynal return master->_block_isbad(master, mtd_get_master_ofs(mtd, ofs));
23218273a0c9SArtem Bityutskiy }
23228273a0c9SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_block_isbad);
23238273a0c9SArtem Bityutskiy
mtd_block_markbad(struct mtd_info * mtd,loff_t ofs)23248273a0c9SArtem Bityutskiy int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
23258273a0c9SArtem Bityutskiy {
232646b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
232746b5889cSMiquel Raynal int ret;
232846b5889cSMiquel Raynal
232946b5889cSMiquel Raynal if (!master->_block_markbad)
23308273a0c9SArtem Bityutskiy return -EOPNOTSUPP;
23310c2b4e21SBrian Norris if (ofs < 0 || ofs >= mtd->size)
23328273a0c9SArtem Bityutskiy return -EINVAL;
2333664addc2SArtem Bityutskiy if (!(mtd->flags & MTD_WRITEABLE))
2334664addc2SArtem Bityutskiy return -EROFS;
233546b5889cSMiquel Raynal
23369e3307a1SBoris Brezillon if (mtd->flags & MTD_SLC_ON_MLC_EMULATION)
23379e3307a1SBoris Brezillon ofs = (loff_t)mtd_div_by_eb(ofs, mtd) * master->erasesize;
23389e3307a1SBoris Brezillon
233946b5889cSMiquel Raynal ret = master->_block_markbad(master, mtd_get_master_ofs(mtd, ofs));
234046b5889cSMiquel Raynal if (ret)
234146b5889cSMiquel Raynal return ret;
234246b5889cSMiquel Raynal
234346b5889cSMiquel Raynal while (mtd->parent) {
234446b5889cSMiquel Raynal mtd->ecc_stats.badblocks++;
234546b5889cSMiquel Raynal mtd = mtd->parent;
234646b5889cSMiquel Raynal }
234746b5889cSMiquel Raynal
234846b5889cSMiquel Raynal return 0;
23498273a0c9SArtem Bityutskiy }
23508273a0c9SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_block_markbad);
23518273a0c9SArtem Bityutskiy
23528273a0c9SArtem Bityutskiy /*
235352b02031SArtem Bityutskiy * default_mtd_writev - the default writev method
235452b02031SArtem Bityutskiy * @mtd: mtd device description object pointer
235552b02031SArtem Bityutskiy * @vecs: the vectors to write
235652b02031SArtem Bityutskiy * @count: count of vectors in @vecs
235752b02031SArtem Bityutskiy * @to: the MTD device offset to write to
235852b02031SArtem Bityutskiy * @retlen: on exit contains the count of bytes written to the MTD device.
235952b02031SArtem Bityutskiy *
236052b02031SArtem Bityutskiy * This function returns zero in case of success and a negative error code in
236152b02031SArtem Bityutskiy * case of failure.
23621da177e4SLinus Torvalds */
default_mtd_writev(struct mtd_info * mtd,const struct kvec * vecs,unsigned long count,loff_t to,size_t * retlen)23631dbebd32SArtem Bityutskiy static int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
23641da177e4SLinus Torvalds unsigned long count, loff_t to, size_t *retlen)
23651da177e4SLinus Torvalds {
23661da177e4SLinus Torvalds unsigned long i;
23671da177e4SLinus Torvalds size_t totlen = 0, thislen;
23681da177e4SLinus Torvalds int ret = 0;
23691da177e4SLinus Torvalds
23701da177e4SLinus Torvalds for (i = 0; i < count; i++) {
23711da177e4SLinus Torvalds if (!vecs[i].iov_len)
23721da177e4SLinus Torvalds continue;
2373eda95cbfSArtem Bityutskiy ret = mtd_write(mtd, to, vecs[i].iov_len, &thislen,
2374eda95cbfSArtem Bityutskiy vecs[i].iov_base);
23751da177e4SLinus Torvalds totlen += thislen;
23761da177e4SLinus Torvalds if (ret || thislen != vecs[i].iov_len)
23771da177e4SLinus Torvalds break;
23781da177e4SLinus Torvalds to += vecs[i].iov_len;
23791da177e4SLinus Torvalds }
23801da177e4SLinus Torvalds *retlen = totlen;
23811da177e4SLinus Torvalds return ret;
23821da177e4SLinus Torvalds }
23831dbebd32SArtem Bityutskiy
23841dbebd32SArtem Bityutskiy /*
23851dbebd32SArtem Bityutskiy * mtd_writev - the vector-based MTD write method
23861dbebd32SArtem Bityutskiy * @mtd: mtd device description object pointer
23871dbebd32SArtem Bityutskiy * @vecs: the vectors to write
23881dbebd32SArtem Bityutskiy * @count: count of vectors in @vecs
23891dbebd32SArtem Bityutskiy * @to: the MTD device offset to write to
23901dbebd32SArtem Bityutskiy * @retlen: on exit contains the count of bytes written to the MTD device.
23911dbebd32SArtem Bityutskiy *
23921dbebd32SArtem Bityutskiy * This function returns zero in case of success and a negative error code in
23931dbebd32SArtem Bityutskiy * case of failure.
23941dbebd32SArtem Bityutskiy */
mtd_writev(struct mtd_info * mtd,const struct kvec * vecs,unsigned long count,loff_t to,size_t * retlen)23951dbebd32SArtem Bityutskiy int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
23961dbebd32SArtem Bityutskiy unsigned long count, loff_t to, size_t *retlen)
23971dbebd32SArtem Bityutskiy {
239846b5889cSMiquel Raynal struct mtd_info *master = mtd_get_master(mtd);
239946b5889cSMiquel Raynal
24001dbebd32SArtem Bityutskiy *retlen = 0;
2401664addc2SArtem Bityutskiy if (!(mtd->flags & MTD_WRITEABLE))
2402664addc2SArtem Bityutskiy return -EROFS;
240346b5889cSMiquel Raynal
240446b5889cSMiquel Raynal if (!master->_writev)
24051dbebd32SArtem Bityutskiy return default_mtd_writev(mtd, vecs, count, to, retlen);
240646b5889cSMiquel Raynal
240746b5889cSMiquel Raynal return master->_writev(master, vecs, count,
240846b5889cSMiquel Raynal mtd_get_master_ofs(mtd, to), retlen);
24091dbebd32SArtem Bityutskiy }
24101dbebd32SArtem Bityutskiy EXPORT_SYMBOL_GPL(mtd_writev);
24111da177e4SLinus Torvalds
241233b53716SGrant Erickson /**
241333b53716SGrant Erickson * mtd_kmalloc_up_to - allocate a contiguous buffer up to the specified size
241452b02031SArtem Bityutskiy * @mtd: mtd device description object pointer
241552b02031SArtem Bityutskiy * @size: a pointer to the ideal or maximum size of the allocation, points
241633b53716SGrant Erickson * to the actual allocation size on success.
241733b53716SGrant Erickson *
241833b53716SGrant Erickson * This routine attempts to allocate a contiguous kernel buffer up to
241933b53716SGrant Erickson * the specified size, backing off the size of the request exponentially
242033b53716SGrant Erickson * until the request succeeds or until the allocation size falls below
242133b53716SGrant Erickson * the system page size. This attempts to make sure it does not adversely
242233b53716SGrant Erickson * impact system performance, so when allocating more than one page, we
2423caf49191SLinus Torvalds * ask the memory allocator to avoid re-trying, swapping, writing back
2424caf49191SLinus Torvalds * or performing I/O.
242533b53716SGrant Erickson *
242633b53716SGrant Erickson * Note, this function also makes sure that the allocated buffer is aligned to
242733b53716SGrant Erickson * the MTD device's min. I/O unit, i.e. the "mtd->writesize" value.
242833b53716SGrant Erickson *
242933b53716SGrant Erickson * This is called, for example by mtd_{read,write} and jffs2_scan_medium,
243033b53716SGrant Erickson * to handle smaller (i.e. degraded) buffer allocations under low- or
243133b53716SGrant Erickson * fragmented-memory situations where such reduced allocations, from a
243233b53716SGrant Erickson * requested ideal, are allowed.
243333b53716SGrant Erickson *
243433b53716SGrant Erickson * Returns a pointer to the allocated buffer on success; otherwise, NULL.
243533b53716SGrant Erickson */
mtd_kmalloc_up_to(const struct mtd_info * mtd,size_t * size)243633b53716SGrant Erickson void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size)
243733b53716SGrant Erickson {
2438d0164adcSMel Gorman gfp_t flags = __GFP_NOWARN | __GFP_DIRECT_RECLAIM | __GFP_NORETRY;
243933b53716SGrant Erickson size_t min_alloc = max_t(size_t, mtd->writesize, PAGE_SIZE);
244033b53716SGrant Erickson void *kbuf;
244133b53716SGrant Erickson
244233b53716SGrant Erickson *size = min_t(size_t, *size, KMALLOC_MAX_SIZE);
244333b53716SGrant Erickson
244433b53716SGrant Erickson while (*size > min_alloc) {
244533b53716SGrant Erickson kbuf = kmalloc(*size, flags);
244633b53716SGrant Erickson if (kbuf)
244733b53716SGrant Erickson return kbuf;
244833b53716SGrant Erickson
244933b53716SGrant Erickson *size >>= 1;
245033b53716SGrant Erickson *size = ALIGN(*size, mtd->writesize);
245133b53716SGrant Erickson }
245233b53716SGrant Erickson
245333b53716SGrant Erickson /*
245433b53716SGrant Erickson * For the last resort allocation allow 'kmalloc()' to do all sorts of
245533b53716SGrant Erickson * things (write-back, dropping caches, etc) by using GFP_KERNEL.
245633b53716SGrant Erickson */
245733b53716SGrant Erickson return kmalloc(*size, GFP_KERNEL);
245833b53716SGrant Erickson }
245933b53716SGrant Erickson EXPORT_SYMBOL_GPL(mtd_kmalloc_up_to);
24601da177e4SLinus Torvalds
24612d2dce0eSPavel Machek #ifdef CONFIG_PROC_FS
24622d2dce0eSPavel Machek
24631da177e4SLinus Torvalds /*====================================================================*/
24641da177e4SLinus Torvalds /* Support for /proc/mtd */
24651da177e4SLinus Torvalds
mtd_proc_show(struct seq_file * m,void * v)2466447d9bd8SAlexey Dobriyan static int mtd_proc_show(struct seq_file *m, void *v)
24671da177e4SLinus Torvalds {
2468f1332ba2SBen Hutchings struct mtd_info *mtd;
24691da177e4SLinus Torvalds
2470447d9bd8SAlexey Dobriyan seq_puts(m, "dev: size erasesize name\n");
247148b19268SIngo Molnar mutex_lock(&mtd_table_mutex);
2472f1332ba2SBen Hutchings mtd_for_each_device(mtd) {
2473447d9bd8SAlexey Dobriyan seq_printf(m, "mtd%d: %8.8llx %8.8x \"%s\"\n",
2474447d9bd8SAlexey Dobriyan mtd->index, (unsigned long long)mtd->size,
2475447d9bd8SAlexey Dobriyan mtd->erasesize, mtd->name);
24761da177e4SLinus Torvalds }
247748b19268SIngo Molnar mutex_unlock(&mtd_table_mutex);
24781da177e4SLinus Torvalds return 0;
24791da177e4SLinus Torvalds }
248045b09076SKevin Cernekee #endif /* CONFIG_PROC_FS */
248145b09076SKevin Cernekee
24821da177e4SLinus Torvalds /*====================================================================*/
24831da177e4SLinus Torvalds /* Init code */
24841da177e4SLinus Torvalds
mtd_bdi_init(const char * name)2485462d69a2STomas Winkler static struct backing_dev_info * __init mtd_bdi_init(const char *name)
24860661b1acSJens Axboe {
2487445caaa2SSteve Longerbeam struct backing_dev_info *bdi;
24880661b1acSJens Axboe int ret;
24890661b1acSJens Axboe
2490aef33c2fSChristoph Hellwig bdi = bdi_alloc(NUMA_NO_NODE);
2491445caaa2SSteve Longerbeam if (!bdi)
2492445caaa2SSteve Longerbeam return ERR_PTR(-ENOMEM);
249355b2598eSChristoph Hellwig bdi->ra_pages = 0;
249455b2598eSChristoph Hellwig bdi->io_pages = 0;
24950661b1acSJens Axboe
2496fa06052dSJan Kara /*
2497fa06052dSJan Kara * We put '-0' suffix to the name to get the same name format as we
2498fa06052dSJan Kara * used to get. Since this is called only once, we get a unique name.
2499fa06052dSJan Kara */
25007c4cc300SJan Kara ret = bdi_register(bdi, "%.28s-0", name);
25010661b1acSJens Axboe if (ret)
2502fa06052dSJan Kara bdi_put(bdi);
25030661b1acSJens Axboe
2504445caaa2SSteve Longerbeam return ret ? ERR_PTR(ret) : bdi;
25050661b1acSJens Axboe }
25060661b1acSJens Axboe
250793e56214SArtem Bityutskiy static struct proc_dir_entry *proc_mtd;
250893e56214SArtem Bityutskiy
init_mtd(void)25091da177e4SLinus Torvalds static int __init init_mtd(void)
25101da177e4SLinus Torvalds {
251115bce40cSDavid Woodhouse int ret;
2512694bb7fcSKevin Cernekee
25130661b1acSJens Axboe ret = class_register(&mtd_class);
25140661b1acSJens Axboe if (ret)
25150661b1acSJens Axboe goto err_reg;
25160661b1acSJens Axboe
2517445caaa2SSteve Longerbeam mtd_bdi = mtd_bdi_init("mtd");
2518445caaa2SSteve Longerbeam if (IS_ERR(mtd_bdi)) {
2519445caaa2SSteve Longerbeam ret = PTR_ERR(mtd_bdi);
2520b4caecd4SChristoph Hellwig goto err_bdi;
2521445caaa2SSteve Longerbeam }
25220661b1acSJens Axboe
25233f3942acSChristoph Hellwig proc_mtd = proc_create_single("mtd", 0, NULL, mtd_proc_show);
252493e56214SArtem Bityutskiy
2525660685d9SArtem Bityutskiy ret = init_mtdchar();
2526660685d9SArtem Bityutskiy if (ret)
2527660685d9SArtem Bityutskiy goto out_procfs;
2528660685d9SArtem Bityutskiy
2529e8e3edb9SMario Rugiero dfs_dir_mtd = debugfs_create_dir("mtd", NULL);
253067b967ddSMiquel Raynal debugfs_create_bool("expert_analysis_mode", 0600, dfs_dir_mtd,
253167b967ddSMiquel Raynal &mtd_expert_analysis_mode);
2532e8e3edb9SMario Rugiero
25331da177e4SLinus Torvalds return 0;
25340661b1acSJens Axboe
2535660685d9SArtem Bityutskiy out_procfs:
2536660685d9SArtem Bityutskiy if (proc_mtd)
2537660685d9SArtem Bityutskiy remove_proc_entry("mtd", NULL);
25381aadf01eSGaosheng Cui bdi_unregister(mtd_bdi);
2539fa06052dSJan Kara bdi_put(mtd_bdi);
2540b4caecd4SChristoph Hellwig err_bdi:
25410661b1acSJens Axboe class_unregister(&mtd_class);
25420661b1acSJens Axboe err_reg:
25430661b1acSJens Axboe pr_err("Error registering mtd class or bdi: %d\n", ret);
25440661b1acSJens Axboe return ret;
25451da177e4SLinus Torvalds }
25461da177e4SLinus Torvalds
cleanup_mtd(void)25471da177e4SLinus Torvalds static void __exit cleanup_mtd(void)
25481da177e4SLinus Torvalds {
2549e8e3edb9SMario Rugiero debugfs_remove_recursive(dfs_dir_mtd);
2550660685d9SArtem Bityutskiy cleanup_mtdchar();
25511da177e4SLinus Torvalds if (proc_mtd)
25521da177e4SLinus Torvalds remove_proc_entry("mtd", NULL);
255315bce40cSDavid Woodhouse class_unregister(&mtd_class);
25549718c59cSChristoph Hellwig bdi_unregister(mtd_bdi);
2555fa06052dSJan Kara bdi_put(mtd_bdi);
255635667b99SJohannes Thumshirn idr_destroy(&mtd_idr);
25571da177e4SLinus Torvalds }
25581da177e4SLinus Torvalds
25591da177e4SLinus Torvalds module_init(init_mtd);
25601da177e4SLinus Torvalds module_exit(cleanup_mtd);
25611da177e4SLinus Torvalds
25621da177e4SLinus Torvalds MODULE_LICENSE("GPL");
25631da177e4SLinus Torvalds MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
25641da177e4SLinus Torvalds MODULE_DESCRIPTION("Core MTD registration and access routines");
2565