1/* 2 * Normal mappings of chips in physical memory for OF devices 3 * 4 * Copyright (C) 2006 MontaVista Software Inc. 5 * Author: Vitaly Wool <vwool@ru.mvista.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2 of the License, or (at your 10 * option) any later version. 11 */ 12 13#include <linux/module.h> 14#include <linux/types.h> 15#include <linux/kernel.h> 16#include <linux/init.h> 17#include <linux/slab.h> 18#include <linux/device.h> 19#include <linux/mtd/mtd.h> 20#include <linux/mtd/map.h> 21#include <linux/mtd/partitions.h> 22#include <linux/mtd/physmap.h> 23#include <asm/io.h> 24#include <asm/prom.h> 25#include <asm/of_device.h> 26#include <asm/of_platform.h> 27 28struct physmap_flash_info { 29 struct mtd_info *mtd; 30 struct map_info map; 31 struct resource *res; 32#ifdef CONFIG_MTD_PARTITIONS 33 int nr_parts; 34 struct mtd_partition *parts; 35#endif 36}; 37 38static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; 39#ifdef CONFIG_MTD_PARTITIONS 40static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; 41#endif 42 43#ifdef CONFIG_MTD_PARTITIONS 44static int parse_flash_partitions(struct device_node *node, 45 struct mtd_partition **parts) 46{ 47 int i, plen, retval = -ENOMEM; 48 const u32 *part; 49 const char *name; 50 51 part = of_get_property(node, "partitions", &plen); 52 if (part == NULL) 53 goto err; 54 55 retval = plen / (2 * sizeof(u32)); 56 *parts = kzalloc(retval * sizeof(struct mtd_partition), GFP_KERNEL); 57 if (*parts == NULL) { 58 printk(KERN_ERR "Can't allocate the flash partition data!\n"); 59 goto err; 60 } 61 62 name = of_get_property(node, "partition-names", &plen); 63 64 for (i = 0; i < retval; i++) { 65 (*parts)[i].offset = *part++; 66 (*parts)[i].size = *part & ~1; 67 if (*part++ & 1) /* bit 0 set signifies read only partition */ 68 (*parts)[i].mask_flags = MTD_WRITEABLE; 69 70 if (name != NULL && plen > 0) { 71 int len = strlen(name) + 1; 72 73 (*parts)[i].name = (char *)name; 74 plen -= len; 75 name += len; 76 } else 77 (*parts)[i].name = "unnamed"; 78 } 79err: 80 return retval; 81} 82#endif 83 84static int of_physmap_remove(struct of_device *dev) 85{ 86 struct physmap_flash_info *info; 87 88 info = dev_get_drvdata(&dev->dev); 89 if (info == NULL) 90 return 0; 91 dev_set_drvdata(&dev->dev, NULL); 92 93 if (info->mtd != NULL) { 94#ifdef CONFIG_MTD_PARTITIONS 95 if (info->nr_parts) { 96 del_mtd_partitions(info->mtd); 97 kfree(info->parts); 98 } else { 99 del_mtd_device(info->mtd); 100 } 101#else 102 del_mtd_device(info->mtd); 103#endif 104 map_destroy(info->mtd); 105 } 106 107 if (info->map.virt != NULL) 108 iounmap(info->map.virt); 109 110 if (info->res != NULL) { 111 release_resource(info->res); 112 kfree(info->res); 113 } 114 115 return 0; 116} 117 118static int __devinit of_physmap_probe(struct of_device *dev, const struct of_device_id *match) 119{ 120 struct device_node *dp = dev->node; 121 struct resource res; 122 struct physmap_flash_info *info; 123 const char **probe_type; 124 const char *of_probe; 125 const u32 *width; 126 int err; 127 128 129 if (of_address_to_resource(dp, 0, &res)) { 130 dev_err(&dev->dev, "Can't get the flash mapping!\n"); 131 err = -EINVAL; 132 goto err_out; 133 } 134 135 dev_dbg(&dev->dev, "physmap flash device: %.8llx at %.8llx\n", 136 (unsigned long long)res.end - res.start + 1, 137 (unsigned long long)res.start); 138 139 info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); 140 if (info == NULL) { 141 err = -ENOMEM; 142 goto err_out; 143 } 144 memset(info, 0, sizeof(*info)); 145 146 dev_set_drvdata(&dev->dev, info); 147 148 info->res = request_mem_region(res.start, res.end - res.start + 1, 149 dev->dev.bus_id); 150 if (info->res == NULL) { 151 dev_err(&dev->dev, "Could not reserve memory region\n"); 152 err = -ENOMEM; 153 goto err_out; 154 } 155 156 width = of_get_property(dp, "bank-width", NULL); 157 if (width == NULL) { 158 dev_err(&dev->dev, "Can't get the flash bank width!\n"); 159 err = -EINVAL; 160 goto err_out; 161 } 162 163 info->map.name = dev->dev.bus_id; 164 info->map.phys = res.start; 165 info->map.size = res.end - res.start + 1; 166 info->map.bankwidth = *width; 167 168 info->map.virt = ioremap(info->map.phys, info->map.size); 169 if (info->map.virt == NULL) { 170 dev_err(&dev->dev, "Failed to ioremap flash region\n"); 171 err = EIO; 172 goto err_out; 173 } 174 175 simple_map_init(&info->map); 176 177 of_probe = of_get_property(dp, "probe-type", NULL); 178 if (of_probe == NULL) { 179 probe_type = rom_probe_types; 180 for (; info->mtd == NULL && *probe_type != NULL; probe_type++) 181 info->mtd = do_map_probe(*probe_type, &info->map); 182 } else if (!strcmp(of_probe, "CFI")) 183 info->mtd = do_map_probe("cfi_probe", &info->map); 184 else if (!strcmp(of_probe, "JEDEC")) 185 info->mtd = do_map_probe("jedec_probe", &info->map); 186 else { 187 if (strcmp(of_probe, "ROM")) 188 dev_dbg(&dev->dev, "map_probe: don't know probe type " 189 "'%s', mapping as rom\n", of_probe); 190 info->mtd = do_map_probe("mtd_rom", &info->map); 191 } 192 if (info->mtd == NULL) { 193 dev_err(&dev->dev, "map_probe failed\n"); 194 err = -ENXIO; 195 goto err_out; 196 } 197 info->mtd->owner = THIS_MODULE; 198 199#ifdef CONFIG_MTD_PARTITIONS 200 err = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0); 201 if (err > 0) { 202 add_mtd_partitions(info->mtd, info->parts, err); 203 } else if ((err = parse_flash_partitions(dp, &info->parts)) > 0) { 204 dev_info(&dev->dev, "Using OF partition information\n"); 205 add_mtd_partitions(info->mtd, info->parts, err); 206 info->nr_parts = err; 207 } else 208#endif 209 210 add_mtd_device(info->mtd); 211 return 0; 212 213err_out: 214 of_physmap_remove(dev); 215 return err; 216 217 return 0; 218 219 220} 221 222static struct of_device_id of_physmap_match[] = { 223 { 224 .type = "rom", 225 .compatible = "direct-mapped" 226 }, 227 { }, 228}; 229 230MODULE_DEVICE_TABLE(of, of_physmap_match); 231 232 233static struct of_platform_driver of_physmap_flash_driver = { 234 .name = "physmap-flash", 235 .match_table = of_physmap_match, 236 .probe = of_physmap_probe, 237 .remove = of_physmap_remove, 238}; 239 240static int __init of_physmap_init(void) 241{ 242 return of_register_platform_driver(&of_physmap_flash_driver); 243} 244 245static void __exit of_physmap_exit(void) 246{ 247 of_unregister_platform_driver(&of_physmap_flash_driver); 248} 249 250module_init(of_physmap_init); 251module_exit(of_physmap_exit); 252 253MODULE_LICENSE("GPL"); 254MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>"); 255MODULE_DESCRIPTION("Configurable MTD map driver for OF"); 256