18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Common code to handle absent "placeholder" devices
48c2ecf20Sopenharmony_ci * Copyright 2001 Resilience Corporation <ebrower@resilience.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This map driver is used to allocate "placeholder" MTD
78c2ecf20Sopenharmony_ci * devices on systems that have socketed/removable media.
88c2ecf20Sopenharmony_ci * Use of this driver as a fallback preserves the expected
98c2ecf20Sopenharmony_ci * registration of MTD device nodes regardless of probe outcome.
108c2ecf20Sopenharmony_ci * A usage example is as follows:
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *		my_dev[i] = do_map_probe("cfi", &my_map[i]);
138c2ecf20Sopenharmony_ci *		if(NULL == my_dev[i]) {
148c2ecf20Sopenharmony_ci *			my_dev[i] = do_map_probe("map_absent", &my_map[i]);
158c2ecf20Sopenharmony_ci *		}
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Any device 'probed' with this driver will return -ENODEV
188c2ecf20Sopenharmony_ci * upon open.
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/module.h>
228c2ecf20Sopenharmony_ci#include <linux/types.h>
238c2ecf20Sopenharmony_ci#include <linux/kernel.h>
248c2ecf20Sopenharmony_ci#include <linux/errno.h>
258c2ecf20Sopenharmony_ci#include <linux/slab.h>
268c2ecf20Sopenharmony_ci#include <linux/init.h>
278c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
288c2ecf20Sopenharmony_ci#include <linux/mtd/map.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int map_absent_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
318c2ecf20Sopenharmony_cistatic int map_absent_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
328c2ecf20Sopenharmony_cistatic int map_absent_erase (struct mtd_info *, struct erase_info *);
338c2ecf20Sopenharmony_cistatic void map_absent_sync (struct mtd_info *);
348c2ecf20Sopenharmony_cistatic struct mtd_info *map_absent_probe(struct map_info *map);
358c2ecf20Sopenharmony_cistatic void map_absent_destroy (struct mtd_info *);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic struct mtd_chip_driver map_absent_chipdrv = {
398c2ecf20Sopenharmony_ci	.probe		= map_absent_probe,
408c2ecf20Sopenharmony_ci	.destroy	= map_absent_destroy,
418c2ecf20Sopenharmony_ci	.name		= "map_absent",
428c2ecf20Sopenharmony_ci	.module		= THIS_MODULE
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic struct mtd_info *map_absent_probe(struct map_info *map)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
508c2ecf20Sopenharmony_ci	if (!mtd) {
518c2ecf20Sopenharmony_ci		return NULL;
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	map->fldrv 	= &map_absent_chipdrv;
558c2ecf20Sopenharmony_ci	mtd->priv 	= map;
568c2ecf20Sopenharmony_ci	mtd->name 	= map->name;
578c2ecf20Sopenharmony_ci	mtd->type 	= MTD_ABSENT;
588c2ecf20Sopenharmony_ci	mtd->size 	= map->size;
598c2ecf20Sopenharmony_ci	mtd->_erase 	= map_absent_erase;
608c2ecf20Sopenharmony_ci	mtd->_read 	= map_absent_read;
618c2ecf20Sopenharmony_ci	mtd->_write 	= map_absent_write;
628c2ecf20Sopenharmony_ci	mtd->_sync 	= map_absent_sync;
638c2ecf20Sopenharmony_ci	mtd->flags 	= 0;
648c2ecf20Sopenharmony_ci	mtd->erasesize  = PAGE_SIZE;
658c2ecf20Sopenharmony_ci	mtd->writesize  = 1;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	__module_get(THIS_MODULE);
688c2ecf20Sopenharmony_ci	return mtd;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int map_absent_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	return -ENODEV;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int map_absent_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	return -ENODEV;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int map_absent_erase(struct mtd_info *mtd, struct erase_info *instr)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	return -ENODEV;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic void map_absent_sync(struct mtd_info *mtd)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	/* nop */
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void map_absent_destroy(struct mtd_info *mtd)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	/* nop */
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int __init map_absent_init(void)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	register_mtd_chip_driver(&map_absent_chipdrv);
1008c2ecf20Sopenharmony_ci	return 0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void __exit map_absent_exit(void)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	unregister_mtd_chip_driver(&map_absent_chipdrv);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cimodule_init(map_absent_init);
1098c2ecf20Sopenharmony_cimodule_exit(map_absent_exit);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1128c2ecf20Sopenharmony_ciMODULE_AUTHOR("Resilience Corporation - Eric Brower <ebrower@resilience.com>");
1138c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Placeholder MTD chip driver for 'absent' chips");
114