1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* drivers/mtd/maps/plat-ram.c 3 * 4 * (c) 2004-2005 Simtec Electronics 5 * http://www.simtec.co.uk/products/SWLINUX/ 6 * Ben Dooks <ben@simtec.co.uk> 7 * 8 * Generic platform device based RAM map 9 */ 10 11 #include <linux/module.h> 12 #include <linux/types.h> 13 #include <linux/kernel.h> 14 #include <linux/string.h> 15 #include <linux/ioport.h> 16 #include <linux/device.h> 17 #include <linux/slab.h> 18 #include <linux/platform_device.h> 19 20 #include <linux/mtd/mtd.h> 21 #include <linux/mtd/map.h> 22 #include <linux/mtd/partitions.h> 23 #include <linux/mtd/plat-ram.h> 24 25 #include <asm/io.h> 26 27 /* private structure for each mtd platform ram device created */ 28 29 struct platram_info { 30 struct device *dev; 31 struct mtd_info *mtd; 32 struct map_info map; 33 struct platdata_mtd_ram *pdata; 34 }; 35 36 /* to_platram_info() 37 * 38 * device private data to struct platram_info conversion 39 */ 40 41 static inline struct platram_info *to_platram_info(struct platform_device *dev) 42 { 43 return platform_get_drvdata(dev); 44 } 45 46 /* platram_setrw 47 * 48 * call the platform device's set rw/ro control 49 * 50 * to = 0 => read-only 51 * = 1 => read-write 52 */ 53 54 static inline void platram_setrw(struct platram_info *info, int to) 55 { 56 if (info->pdata == NULL) 57 return; 58 59 if (info->pdata->set_rw != NULL) 60 (info->pdata->set_rw)(info->dev, to); 61 } 62 63 /* platram_remove 64 * 65 * called to remove the device from the driver's control 66 */ 67 68 static int platram_remove(struct platform_device *pdev) 69 { 70 struct platram_info *info = to_platram_info(pdev); 71 72 dev_dbg(&pdev->dev, "removing device\n"); 73 74 if (info == NULL) 75 return 0; 76 77 if (info->mtd) { 78 mtd_device_unregister(info->mtd); 79 map_destroy(info->mtd); 80 } 81 82 /* ensure ram is left read-only */ 83 84 platram_setrw(info, PLATRAM_RO); 85 86 kfree(info); 87 88 return 0; 89 } 90 91 /* platram_probe 92 * 93 * called from device drive system when a device matching our 94 * driver is found. 95 */ 96 97 static int platram_probe(struct platform_device *pdev) 98 { 99 struct platdata_mtd_ram *pdata; 100 struct platram_info *info; 101 struct resource *res; 102 int err = 0; 103 104 dev_dbg(&pdev->dev, "probe entered\n"); 105 106 if (dev_get_platdata(&pdev->dev) == NULL) { 107 dev_err(&pdev->dev, "no platform data supplied\n"); 108 err = -ENOENT; 109 goto exit_error; 110 } 111 112 pdata = dev_get_platdata(&pdev->dev); 113 114 info = kzalloc(sizeof(*info), GFP_KERNEL); 115 if (info == NULL) { 116 err = -ENOMEM; 117 goto exit_error; 118 } 119 120 platform_set_drvdata(pdev, info); 121 122 info->dev = &pdev->dev; 123 info->pdata = pdata; 124 125 /* get the resource for the memory mapping */ 126 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 127 info->map.virt = devm_ioremap_resource(&pdev->dev, res); 128 if (IS_ERR(info->map.virt)) { 129 err = PTR_ERR(info->map.virt); 130 dev_err(&pdev->dev, "failed to ioremap() region\n"); 131 goto exit_free; 132 } 133 134 dev_dbg(&pdev->dev, "got platform resource %p (0x%llx)\n", res, 135 (unsigned long long)res->start); 136 137 /* setup map parameters */ 138 139 info->map.phys = res->start; 140 info->map.size = resource_size(res); 141 info->map.name = pdata->mapname != NULL ? 142 (char *)pdata->mapname : (char *)pdev->name; 143 info->map.bankwidth = pdata->bankwidth; 144 145 dev_dbg(&pdev->dev, "virt %p, %lu bytes\n", info->map.virt, info->map.size); 146 147 simple_map_init(&info->map); 148 149 dev_dbg(&pdev->dev, "initialised map, probing for mtd\n"); 150 151 /* probe for the right mtd map driver 152 * supplied by the platform_data struct */ 153 154 if (pdata->map_probes) { 155 const char * const *map_probes = pdata->map_probes; 156 157 for ( ; !info->mtd && *map_probes; map_probes++) 158 info->mtd = do_map_probe(*map_probes , &info->map); 159 } 160 /* fallback to map_ram */ 161 else 162 info->mtd = do_map_probe("map_ram", &info->map); 163 164 if (info->mtd == NULL) { 165 dev_err(&pdev->dev, "failed to probe for map_ram\n"); 166 err = -ENOMEM; 167 goto exit_free; 168 } 169 170 info->mtd->dev.parent = &pdev->dev; 171 172 platram_setrw(info, PLATRAM_RW); 173 174 /* check to see if there are any available partitions, or whether 175 * to add this device whole */ 176 177 err = mtd_device_parse_register(info->mtd, pdata->probes, NULL, 178 pdata->partitions, 179 pdata->nr_partitions); 180 if (err) { 181 dev_err(&pdev->dev, "failed to register mtd device\n"); 182 goto exit_free; 183 } 184 185 dev_info(&pdev->dev, "registered mtd device\n"); 186 187 if (pdata->nr_partitions) { 188 /* add the whole device. */ 189 err = mtd_device_register(info->mtd, NULL, 0); 190 if (err) { 191 dev_err(&pdev->dev, 192 "failed to register the entire device\n"); 193 goto exit_free; 194 } 195 } 196 197 return 0; 198 199 exit_free: 200 platram_remove(pdev); 201 exit_error: 202 return err; 203 } 204 205 /* device driver info */ 206 207 /* work with hotplug and coldplug */ 208 MODULE_ALIAS("platform:mtd-ram"); 209 210 static struct platform_driver platram_driver = { 211 .probe = platram_probe, 212 .remove = platram_remove, 213 .driver = { 214 .name = "mtd-ram", 215 }, 216 }; 217 218 module_platform_driver(platram_driver); 219 220 MODULE_LICENSE("GPL"); 221 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 222 MODULE_DESCRIPTION("MTD platform RAM map driver"); 223