physmap_of.c 8.8 KB
Newer Older
V
Vitaly Wool 已提交
1 2 3 4 5 6
/*
 * Normal mappings of chips in physical memory for OF devices
 *
 * Copyright (C) 2006 MontaVista Software Inc.
 * Author: Vitaly Wool <vwool@ru.mvista.com>
 *
7 8 9
 * Revised to handle newer style flash binding by:
 *   Copyright (C) 2007 David Gibson, IBM Corporation.
 *
V
Vitaly Wool 已提交
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/of_device.h>
#include <asm/of_platform.h>

struct physmap_flash_info {
	struct mtd_info		*mtd;
	struct map_info		map;
	struct resource		*res;
#ifdef CONFIG_MTD_PARTITIONS
	struct mtd_partition	*parts;
#endif
};

#ifdef CONFIG_MTD_PARTITIONS
41 42 43
static int parse_obsolete_partitions(struct of_device *dev,
				     struct physmap_flash_info *info,
				     struct device_node *dp)
V
Vitaly Wool 已提交
44
{
45 46 47 48 49
	int i, plen, nr_parts;
	const struct {
		u32 offset, len;
	} *part;
	const char *names;
V
Vitaly Wool 已提交
50

51 52 53
	part = of_get_property(dp, "partitions", &plen);
	if (!part)
		return -ENOENT;
V
Vitaly Wool 已提交
54

55 56 57 58 59 60
	dev_warn(&dev->dev, "Device tree uses obsolete partition map binding\n");

	nr_parts = plen / sizeof(part[0]);

	info->parts = kzalloc(nr_parts * sizeof(struct mtd_partition), GFP_KERNEL);
	if (!info->parts) {
V
Vitaly Wool 已提交
61
		printk(KERN_ERR "Can't allocate the flash partition data!\n");
62
		return -ENOMEM;
V
Vitaly Wool 已提交
63 64
	}

65
	names = of_get_property(dp, "partition-names", &plen);
V
Vitaly Wool 已提交
66

67 68 69 70 71
	for (i = 0; i < nr_parts; i++) {
		info->parts[i].offset = part->offset;
		info->parts[i].size   = part->len & ~1;
		if (part->len & 1) /* bit 0 set signifies read only partition */
			info->parts[i].mask_flags = MTD_WRITEABLE;
V
Vitaly Wool 已提交
72

73 74
		if (names && (plen > 0)) {
			int len = strlen(names) + 1;
V
Vitaly Wool 已提交
75

76
			info->parts[i].name = (char *)names;
V
Vitaly Wool 已提交
77
			plen -= len;
78 79 80 81 82 83
			names += len;
		} else {
			info->parts[i].name = "unnamed";
		}

		part++;
V
Vitaly Wool 已提交
84
	}
85 86

	return nr_parts;
V
Vitaly Wool 已提交
87
}
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164

static int __devinit process_partitions(struct physmap_flash_info *info,
					struct of_device *dev)
{
	const char *partname;
	static const char *part_probe_types[]
		= { "cmdlinepart", "RedBoot", NULL };
	struct device_node *dp = dev->node, *pp;
	int nr_parts, i;

	/* First look for RedBoot table or partitions on the command
	 * line, these take precedence over device tree information */
	nr_parts = parse_mtd_partitions(info->mtd, part_probe_types,
					&info->parts, 0);
	if (nr_parts > 0) {
		add_mtd_partitions(info->mtd, info->parts, nr_parts);
		return 0;
	}

	/* First count the subnodes */
	nr_parts = 0;
	for (pp = dp->child; pp; pp = pp->sibling)
		nr_parts++;

	if (nr_parts) {
		info->parts = kzalloc(nr_parts * sizeof(struct mtd_partition),
				      GFP_KERNEL);
		if (!info->parts) {
			printk(KERN_ERR "Can't allocate the flash partition data!\n");
			return -ENOMEM;
		}

		for (pp = dp->child, i = 0 ; pp; pp = pp->sibling, i++) {
			const u32 *reg;
			int len;

			reg = of_get_property(pp, "reg", &len);
			if (!reg || (len != 2*sizeof(u32))) {
				dev_err(&dev->dev, "Invalid 'reg' on %s\n",
					dp->full_name);
				kfree(info->parts);
				info->parts = NULL;
				return -EINVAL;
			}
			info->parts[i].offset = reg[0];
			info->parts[i].size = reg[1];

			partname = of_get_property(pp, "label", &len);
			if (!partname)
				partname = of_get_property(pp, "name", &len);
			info->parts[i].name = (char *)partname;

			if (of_get_property(pp, "read-only", &len))
				info->parts[i].mask_flags = MTD_WRITEABLE;
		}
	} else {
		nr_parts = parse_obsolete_partitions(dev, info, dp);
	}

	if (nr_parts < 0)
		return nr_parts;

	if (nr_parts > 0)
		add_mtd_partitions(info->mtd, info->parts, nr_parts);
	else
		add_mtd_device(info->mtd);

	return 0;
}
#else /* MTD_PARTITIONS */
static int __devinit process_partitions(struct physmap_flash_info *info,
					struct device_node *dev)
{
	add_mtd_device(info->mtd);
	return 0;
}
#endif /* MTD_PARTITIONS */
V
Vitaly Wool 已提交
165 166 167 168 169 170 171 172 173 174 175 176

static int of_physmap_remove(struct of_device *dev)
{
	struct physmap_flash_info *info;

	info = dev_get_drvdata(&dev->dev);
	if (info == NULL)
		return 0;
	dev_set_drvdata(&dev->dev, NULL);

	if (info->mtd != NULL) {
#ifdef CONFIG_MTD_PARTITIONS
177
		if (info->parts) {
V
Vitaly Wool 已提交
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
			del_mtd_partitions(info->mtd);
			kfree(info->parts);
		} else {
			del_mtd_device(info->mtd);
		}
#else
		del_mtd_device(info->mtd);
#endif
		map_destroy(info->mtd);
	}

	if (info->map.virt != NULL)
		iounmap(info->map.virt);

	if (info->res != NULL) {
		release_resource(info->res);
		kfree(info->res);
	}

	return 0;
}

200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
/* Helper function to handle probing of the obsolete "direct-mapped"
 * compatible binding, which has an extra "probe-type" property
 * describing the type of flash probe necessary. */
static struct mtd_info * __devinit obsolete_probe(struct of_device *dev,
						  struct map_info *map)
{
	struct device_node *dp = dev->node;
	const char *of_probe;
	struct mtd_info *mtd;
	static const char *rom_probe_types[]
		= { "cfi_probe", "jedec_probe", "map_rom"};
	int i;

	dev_warn(&dev->dev, "Device tree uses obsolete \"direct-mapped\" "
		 "flash binding\n");

	of_probe = of_get_property(dp, "probe-type", NULL);
	if (!of_probe) {
		for (i = 0; i < ARRAY_SIZE(rom_probe_types); i++) {
			mtd = do_map_probe(rom_probe_types[i], map);
			if (mtd)
				return mtd;
		}
		return NULL;
	} else if (strcmp(of_probe, "CFI") == 0) {
		return do_map_probe("cfi_probe", map);
	} else if (strcmp(of_probe, "JEDEC") == 0) {
		return do_map_probe("jedec_probe", map);
	} else {
		if (strcmp(of_probe, "ROM") != 0)
			dev_dbg(&dev->dev, "obsolete_probe: don't know probe type "
				"'%s', mapping as rom\n", of_probe);
		return do_map_probe("mtd_rom", map);
	}
}

V
Vitaly Wool 已提交
236 237 238 239 240
static int __devinit of_physmap_probe(struct of_device *dev, const struct of_device_id *match)
{
	struct device_node *dp = dev->node;
	struct resource res;
	struct physmap_flash_info *info;
241
	const char *probe_type = (const char *)match->data;
V
Vitaly Wool 已提交
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
	const u32 *width;
	int err;

	if (of_address_to_resource(dp, 0, &res)) {
		dev_err(&dev->dev, "Can't get the flash mapping!\n");
		err = -EINVAL;
		goto err_out;
	}

       	dev_dbg(&dev->dev, "physmap flash device: %.8llx at %.8llx\n",
	    (unsigned long long)res.end - res.start + 1,
	    (unsigned long long)res.start);

	info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL);
	if (info == NULL) {
		err = -ENOMEM;
		goto err_out;
	}
	memset(info, 0, sizeof(*info));

	dev_set_drvdata(&dev->dev, info);

	info->res = request_mem_region(res.start, res.end - res.start + 1,
			dev->dev.bus_id);
	if (info->res == NULL) {
		dev_err(&dev->dev, "Could not reserve memory region\n");
		err = -ENOMEM;
		goto err_out;
	}

272
	width = of_get_property(dp, "bank-width", NULL);
V
Vitaly Wool 已提交
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
	if (width == NULL) {
		dev_err(&dev->dev, "Can't get the flash bank width!\n");
		err = -EINVAL;
		goto err_out;
	}

	info->map.name = dev->dev.bus_id;
	info->map.phys = res.start;
	info->map.size = res.end - res.start + 1;
	info->map.bankwidth = *width;

	info->map.virt = ioremap(info->map.phys, info->map.size);
	if (info->map.virt == NULL) {
		dev_err(&dev->dev, "Failed to ioremap flash region\n");
		err = EIO;
		goto err_out;
	}

	simple_map_init(&info->map);

293 294 295 296 297
	if (probe_type)
		info->mtd = do_map_probe(probe_type, &info->map);
	else
		info->mtd = obsolete_probe(dev, &info->map);

V
Vitaly Wool 已提交
298 299 300 301 302 303 304
	if (info->mtd == NULL) {
		dev_err(&dev->dev, "map_probe failed\n");
		err = -ENXIO;
		goto err_out;
	}
	info->mtd->owner = THIS_MODULE;

305
	return process_partitions(info, dev);
V
Vitaly Wool 已提交
306 307 308 309 310 311 312 313 314 315 316

err_out:
	of_physmap_remove(dev);
	return err;

	return 0;


}

static struct of_device_id of_physmap_match[] = {
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
	{
		.compatible	= "cfi-flash",
		.data		= (void *)"cfi_probe",
	},
	{
		/* FIXME: JEDEC chips can't be safely and reliably
		 * probed, although the mtd code gets it right in
		 * practice most of the time.  We should use the
		 * vendor and device ids specified by the binding to
		 * bypass the heuristic probe code, but the mtd layer
		 * provides, at present, no interface for doing so
		 * :(. */
		.compatible	= "jedec-flash",
		.data		= (void *)"jedec_probe",
	},
V
Vitaly Wool 已提交
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
	{
		.type		= "rom",
		.compatible	= "direct-mapped"
	},
	{ },
};

MODULE_DEVICE_TABLE(of, of_physmap_match);


static struct of_platform_driver of_physmap_flash_driver = {
	.name		= "physmap-flash",
	.match_table	= of_physmap_match,
	.probe		= of_physmap_probe,
	.remove		= of_physmap_remove,
};

static int __init of_physmap_init(void)
{
	return of_register_platform_driver(&of_physmap_flash_driver);
}

static void __exit of_physmap_exit(void)
{
	of_unregister_platform_driver(&of_physmap_flash_driver);
}

module_init(of_physmap_init);
module_exit(of_physmap_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>");
MODULE_DESCRIPTION("Configurable MTD map driver for OF");