2675 lines · 68.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Core registration and callback routines for MTD * drivers and users. * * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> * Copyright © 2006      Red Hat UK Limited  */ #include <linux/module.h>#include <linux/kernel.h>#include <linux/ptrace.h>#include <linux/seq_file.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/major.h>#include <linux/fs.h>#include <linux/err.h>#include <linux/ioctl.h>#include <linux/init.h>#include <linux/of.h>#include <linux/proc_fs.h>#include <linux/idr.h>#include <linux/backing-dev.h>#include <linux/gfp.h>#include <linux/random.h>#include <linux/slab.h>#include <linux/reboot.h>#include <linux/leds.h>#include <linux/debugfs.h>#include <linux/nvmem-provider.h>#include <linux/root_dev.h>#include <linux/error-injection.h> #include <linux/mtd/mtd.h>#include <linux/mtd/partitions.h>#include <linux/mtd/concat.h> #include "mtdcore.h" struct backing_dev_info *mtd_bdi; #ifdef CONFIG_PM_SLEEP static int mtd_cls_suspend(struct device *dev){	struct mtd_info *mtd = dev_get_drvdata(dev); 	return mtd ? mtd_suspend(mtd) : 0;} static int mtd_cls_resume(struct device *dev){	struct mtd_info *mtd = dev_get_drvdata(dev); 	if (mtd)		mtd_resume(mtd);	return 0;} static SIMPLE_DEV_PM_OPS(mtd_cls_pm_ops, mtd_cls_suspend, mtd_cls_resume);#define MTD_CLS_PM_OPS (&mtd_cls_pm_ops)#else#define MTD_CLS_PM_OPS NULL#endif static struct class mtd_class = {	.name = "mtd",	.pm = MTD_CLS_PM_OPS,}; static DEFINE_IDR(mtd_idr); /* These are exported solely for the purpose of mtd_blkdevs.c. You   should not use them for _anything_ else */DEFINE_MUTEX(mtd_table_mutex);EXPORT_SYMBOL_GPL(mtd_table_mutex); struct mtd_info *__mtd_next_device(int i){	return idr_get_next(&mtd_idr, &i);}EXPORT_SYMBOL_GPL(__mtd_next_device); static LIST_HEAD(mtd_notifiers);  #define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2) /* REVISIT once MTD uses the driver model better, whoever allocates * the mtd_info will probably want to use the release() hook... */static void mtd_release(struct device *dev){	struct mtd_info *mtd = dev_get_drvdata(dev);	dev_t index = MTD_DEVT(mtd->index); 	idr_remove(&mtd_idr, mtd->index);	of_node_put(mtd_get_of_node(mtd)); 	if (mtd_is_partition(mtd))		release_mtd_partition(mtd); 	/* remove /dev/mtdXro node */	device_destroy(&mtd_class, index + 1);} /* * No-op device release used in add_mtd_device() error paths. * Prevents mtd_release() from being called via device_release(), * which would free the mtd_info that the caller still manages. */static void mtd_dev_release_nop(struct device *dev){} static void mtd_device_release(struct kref *kref){	struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt);	bool is_partition = mtd_is_partition(mtd); 	debugfs_remove_recursive(mtd->dbg.dfs_dir); 	/* Try to remove the NVMEM provider */	nvmem_unregister(mtd->nvmem); 	device_unregister(&mtd->dev); 	/*	 *  Clear dev so mtd can be safely re-registered later if desired.	 *  Should not be done for partition,	 *  as it was already destroyed in device_unregister().	 */	if (!is_partition)		memset(&mtd->dev, 0, sizeof(mtd->dev)); 	module_put(THIS_MODULE);} #define MTD_DEVICE_ATTR_RO(name) \static DEVICE_ATTR(name, 0444, mtd_##name##_show, NULL) #define MTD_DEVICE_ATTR_RW(name) \static DEVICE_ATTR(name, 0644, mtd_##name##_show, mtd_##name##_store) static ssize_t mtd_type_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev);	char *type; 	switch (mtd->type) {	case MTD_ABSENT:		type = "absent";		break;	case MTD_RAM:		type = "ram";		break;	case MTD_ROM:		type = "rom";		break;	case MTD_NORFLASH:		type = "nor";		break;	case MTD_NANDFLASH:		type = "nand";		break;	case MTD_DATAFLASH:		type = "dataflash";		break;	case MTD_UBIVOLUME:		type = "ubi";		break;	case MTD_MLCNANDFLASH:		type = "mlc-nand";		break;	default:		type = "unknown";	} 	return sysfs_emit(buf, "%s\n", type);}MTD_DEVICE_ATTR_RO(type); static ssize_t mtd_flags_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev); 	return sysfs_emit(buf, "0x%lx\n", (unsigned long)mtd->flags);}MTD_DEVICE_ATTR_RO(flags); static ssize_t mtd_size_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%llu\n", (unsigned long long)mtd->size);}MTD_DEVICE_ATTR_RO(size); static ssize_t mtd_erasesize_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->erasesize);}MTD_DEVICE_ATTR_RO(erasesize); static ssize_t mtd_writesize_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->writesize);}MTD_DEVICE_ATTR_RO(writesize); static ssize_t mtd_subpagesize_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev);	unsigned int subpagesize = mtd->writesize >> mtd->subpage_sft; 	return sysfs_emit(buf, "%u\n", subpagesize);}MTD_DEVICE_ATTR_RO(subpagesize); static ssize_t mtd_oobsize_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->oobsize);}MTD_DEVICE_ATTR_RO(oobsize); static ssize_t mtd_oobavail_show(struct device *dev,				 struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%u\n", mtd->oobavail);}MTD_DEVICE_ATTR_RO(oobavail); static ssize_t mtd_numeraseregions_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%u\n", mtd->numeraseregions);}MTD_DEVICE_ATTR_RO(numeraseregions); static ssize_t mtd_name_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%s\n", mtd->name);}MTD_DEVICE_ATTR_RO(name); static ssize_t mtd_ecc_strength_show(struct device *dev,				     struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%u\n", mtd->ecc_strength);}MTD_DEVICE_ATTR_RO(ecc_strength); static ssize_t mtd_bitflip_threshold_show(struct device *dev,					  struct device_attribute *attr,					  char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%u\n", mtd->bitflip_threshold);} static ssize_t mtd_bitflip_threshold_store(struct device *dev,					   struct device_attribute *attr,					   const char *buf, size_t count){	struct mtd_info *mtd = dev_get_drvdata(dev);	unsigned int bitflip_threshold;	int retval; 	retval = kstrtouint(buf, 0, &bitflip_threshold);	if (retval)		return retval; 	mtd->bitflip_threshold = bitflip_threshold;	return count;}MTD_DEVICE_ATTR_RW(bitflip_threshold); static ssize_t mtd_ecc_step_size_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev); 	return sysfs_emit(buf, "%u\n", mtd->ecc_step_size); }MTD_DEVICE_ATTR_RO(ecc_step_size); static ssize_t mtd_corrected_bits_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev);	struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats; 	return sysfs_emit(buf, "%u\n", ecc_stats->corrected);}MTD_DEVICE_ATTR_RO(corrected_bits);	/* ecc stats corrected */ static ssize_t mtd_ecc_failures_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev);	struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats; 	return sysfs_emit(buf, "%u\n", ecc_stats->failed);}MTD_DEVICE_ATTR_RO(ecc_failures);	/* ecc stats errors */ static ssize_t mtd_bad_blocks_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev);	struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats; 	return sysfs_emit(buf, "%u\n", ecc_stats->badblocks);}MTD_DEVICE_ATTR_RO(bad_blocks); static ssize_t mtd_bbt_blocks_show(struct device *dev,		struct device_attribute *attr, char *buf){	struct mtd_info *mtd = dev_get_drvdata(dev);	struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats; 	return sysfs_emit(buf, "%u\n", ecc_stats->bbtblocks);}MTD_DEVICE_ATTR_RO(bbt_blocks); static struct attribute *mtd_attrs[] = {	&dev_attr_type.attr,	&dev_attr_flags.attr,	&dev_attr_size.attr,	&dev_attr_erasesize.attr,	&dev_attr_writesize.attr,	&dev_attr_subpagesize.attr,	&dev_attr_oobsize.attr,	&dev_attr_oobavail.attr,	&dev_attr_numeraseregions.attr,	&dev_attr_name.attr,	&dev_attr_ecc_strength.attr,	&dev_attr_ecc_step_size.attr,	&dev_attr_corrected_bits.attr,	&dev_attr_ecc_failures.attr,	&dev_attr_bad_blocks.attr,	&dev_attr_bbt_blocks.attr,	&dev_attr_bitflip_threshold.attr,	NULL,};ATTRIBUTE_GROUPS(mtd); static const struct device_type mtd_devtype = {	.name		= "mtd",	.groups		= mtd_groups,	.release	= mtd_release,}; static bool mtd_expert_analysis_mode; #ifdef CONFIG_DEBUG_FSbool mtd_check_expert_analysis_mode(void){	const char *mtd_expert_analysis_warning =		"Bad block checks have been entirely disabled.\n"		"This is only reserved for post-mortem forensics and debug purposes.\n"		"Never enable this mode if you do not know what you are doing!\n"; 	return WARN_ONCE(mtd_expert_analysis_mode, mtd_expert_analysis_warning);}EXPORT_SYMBOL_GPL(mtd_check_expert_analysis_mode);#endif static struct dentry *dfs_dir_mtd; static int mtd_ooblayout_show(struct seq_file *s, void *p,			      int (*iter)(struct mtd_info *, int section,					  struct mtd_oob_region *region)){	struct mtd_info *mtd = s->private;	int section; 	for (section = 0;; section++) {		struct mtd_oob_region region;		int err; 		err = iter(mtd, section, &region);		if (err) {			if (err == -ERANGE)				break; 			return err;		} 		seq_printf(s, "%-3d %4u %4u\n", section, region.offset,			   region.length);	} 	return 0;} static int mtd_ooblayout_ecc_show(struct seq_file *s, void *p){	return mtd_ooblayout_show(s, p, mtd_ooblayout_ecc);}DEFINE_SHOW_ATTRIBUTE(mtd_ooblayout_ecc); static int mtd_ooblayout_free_show(struct seq_file *s, void *p){	return mtd_ooblayout_show(s, p, mtd_ooblayout_free);}DEFINE_SHOW_ATTRIBUTE(mtd_ooblayout_free); static void mtd_debugfs_populate(struct mtd_info *mtd){	struct device *dev = &mtd->dev;	struct mtd_oob_region region; 	if (IS_ERR_OR_NULL(dfs_dir_mtd))		return; 	mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(dev), dfs_dir_mtd);	if (IS_ERR_OR_NULL(mtd->dbg.dfs_dir))		return; 	/* Create ooblayout files only if at least one region is present. */	if (mtd_ooblayout_ecc(mtd, 0, &region) == 0)		debugfs_create_file("ooblayout_ecc", 0444, mtd->dbg.dfs_dir,				    mtd, &mtd_ooblayout_ecc_fops); 	if (mtd_ooblayout_free(mtd, 0, &region) == 0)		debugfs_create_file("ooblayout_free", 0444, mtd->dbg.dfs_dir,				    mtd, &mtd_ooblayout_free_fops);} #ifndef CONFIG_MMUunsigned mtd_mmap_capabilities(struct mtd_info *mtd){	switch (mtd->type) {	case MTD_RAM:		return NOMMU_MAP_COPY | NOMMU_MAP_DIRECT | NOMMU_MAP_EXEC |			NOMMU_MAP_READ | NOMMU_MAP_WRITE;	case MTD_ROM:		return NOMMU_MAP_COPY | NOMMU_MAP_DIRECT | NOMMU_MAP_EXEC |			NOMMU_MAP_READ;	default:		return NOMMU_MAP_COPY;	}}EXPORT_SYMBOL_GPL(mtd_mmap_capabilities);#endif static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state,			       void *cmd){	struct mtd_info *mtd; 	mtd = container_of(n, struct mtd_info, reboot_notifier);	mtd->_reboot(mtd); 	return NOTIFY_DONE;} /** * mtd_wunit_to_pairing_info - get pairing information of a wunit * @mtd: pointer to new MTD device info structure * @wunit: write unit we are interested in * @info: returned pairing information * * Retrieve pairing information associated to the wunit. * This is mainly useful when dealing with MLC/TLC NANDs where pages can be * paired together, and where programming a page may influence the page it is * paired with. * The notion of page is replaced by the term wunit (write-unit) to stay * consistent with the ->writesize field. * * The @wunit argument can be extracted from an absolute offset using * mtd_offset_to_wunit(). @info is filled with the pairing information attached * to @wunit.

Lines 1–500 of 2675.