1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Registration for chip drivers |
4 | * |
5 | */ |
6 | |
7 | #include <linux/kernel.h> |
8 | #include <linux/module.h> |
9 | #include <linux/kmod.h> |
10 | #include <linux/spinlock.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/mtd/map.h> |
13 | #include <linux/mtd/mtd.h> |
14 | |
15 | static DEFINE_SPINLOCK(chip_drvs_lock); |
16 | static LIST_HEAD(chip_drvs_list); |
17 | |
18 | void register_mtd_chip_driver(struct mtd_chip_driver *drv) |
19 | { |
20 | spin_lock(lock: &chip_drvs_lock); |
21 | list_add(new: &drv->list, head: &chip_drvs_list); |
22 | spin_unlock(lock: &chip_drvs_lock); |
23 | } |
24 | |
25 | void unregister_mtd_chip_driver(struct mtd_chip_driver *drv) |
26 | { |
27 | spin_lock(lock: &chip_drvs_lock); |
28 | list_del(entry: &drv->list); |
29 | spin_unlock(lock: &chip_drvs_lock); |
30 | } |
31 | |
32 | static struct mtd_chip_driver *get_mtd_chip_driver (const char *name) |
33 | { |
34 | struct mtd_chip_driver *ret = NULL, *this; |
35 | |
36 | spin_lock(lock: &chip_drvs_lock); |
37 | |
38 | list_for_each_entry(this, &chip_drvs_list, list) { |
39 | if (!strcmp(this->name, name)) { |
40 | ret = this; |
41 | break; |
42 | } |
43 | } |
44 | if (ret && !try_module_get(module: ret->module)) |
45 | ret = NULL; |
46 | |
47 | spin_unlock(lock: &chip_drvs_lock); |
48 | |
49 | return ret; |
50 | } |
51 | |
52 | /* Hide all the horrid details, like some silly person taking |
53 | get_module_symbol() away from us, from the caller. */ |
54 | |
55 | struct mtd_info *do_map_probe(const char *name, struct map_info *map) |
56 | { |
57 | struct mtd_chip_driver *drv; |
58 | struct mtd_info *ret; |
59 | |
60 | drv = get_mtd_chip_driver(name); |
61 | |
62 | if (!drv && !request_module("%s" , name)) |
63 | drv = get_mtd_chip_driver(name); |
64 | |
65 | if (!drv) |
66 | return NULL; |
67 | |
68 | ret = drv->probe(map); |
69 | |
70 | /* We decrease the use count here. It may have been a |
71 | probe-only module, which is no longer required from this |
72 | point, having given us a handle on (and increased the use |
73 | count of) the actual driver code. |
74 | */ |
75 | module_put(module: drv->module); |
76 | |
77 | return ret; |
78 | } |
79 | /* |
80 | * Destroy an MTD device which was created for a map device. |
81 | * Make sure the MTD device is already unregistered before calling this |
82 | */ |
83 | void map_destroy(struct mtd_info *mtd) |
84 | { |
85 | struct map_info *map = mtd->priv; |
86 | |
87 | if (map->fldrv->destroy) |
88 | map->fldrv->destroy(mtd); |
89 | |
90 | module_put(module: map->fldrv->module); |
91 | |
92 | kfree(objp: mtd); |
93 | } |
94 | |
95 | EXPORT_SYMBOL(register_mtd_chip_driver); |
96 | EXPORT_SYMBOL(unregister_mtd_chip_driver); |
97 | EXPORT_SYMBOL(do_map_probe); |
98 | EXPORT_SYMBOL(map_destroy); |
99 | |
100 | MODULE_LICENSE("GPL" ); |
101 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>" ); |
102 | MODULE_DESCRIPTION("Core routines for registering and invoking MTD chip drivers" ); |
103 | |