linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* MTD Mapping driver - out of vmalloc space
@ 2005-04-08 14:42 Chris Elston
  2005-04-08 17:51 ` Ho Lee
  0 siblings, 1 reply; 7+ messages in thread
From: Chris Elston @ 2005-04-08 14:42 UTC (permalink / raw)
  To: linuxppc-embedded

[-- Attachment #1: Type: text/plain, Size: 1774 bytes --]

Dear all,

I'm writing an MTD mapping driver for a board with (up to) 512M SDRAM, and
(up to) 256M Flash and I've hit a bit of a problem.  The standard way to write
a mapping driver seems to be to map the whole of the Flash into kernel virtual
memory space.  While this is not a problem for small Flash devices, I'm hitting
the limit of the vmalloc space.  

So I decided to modify my mapping driver to work through a smaller virtual 
addressing window.

Basically, all reads/writes are redirected through my custom functions which
first ensure that the correct physical address range is mapped into virtual
space and then drop through to the standard read/write functions.  The mapping
is achieved using ioremap/iounmap calls.  (code attached)

David Woodhouse pointed out that this approach is not viable because the reads
and writes must be atomic - and ioremap can potentially sleep.  So I'm stuck as
to how to proceed.

Ideally what I'd like is to be able to allocate a block of virtual addresses at 
init time and then dynamically modify the physical address range that it refers to.
Since I wouldn't be requesting any more vmalloc space - just changing the mapping 
for the space I have got - I'd hopefully be able to make this a non-blocking
operation.  Unfortunately I have no idea how to go about this - or even if it's
possible :)

Thanks in advance,

Chris.

________________________________________________________________________
This e-mail has been scanned for all viruses by Star. The
service is powered by MessageLabs. For more information on a proactive
anti-virus service working around the clock, around the globe, visit:
http://www.star.net.uk
________________________________________________________________________

[-- Attachment #2: rsppc7d-flash.c --]
[-- Type: application/octet-stream, Size: 8944 bytes --]

/*
 * Radstone Technology PPC7D Flash memory driver.
 * Copyright (C) 2005 Radstone Technology <chris.elston@radstone.co.uk>
 *
 * Based on: ceiva.c, which has the following copyright:
 * Ceiva flash memory driver.
 * Copyright (C) 2002 Rob Scott <rscott@mtrob.fdns.net>
 *
 * Based on: sa1100-flash.c, which has the following copyright:
 * Flash memory access on SA11x0 based devices
 *
 * (C) 2000 Nicolas Pitre <nico@cam.org>
 *
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/init.h>

#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/concat.h>

#include <asm/io.h>
#include <asm/ppcboot.h>

#include <platforms/radstone_ppc7d.h>

extern unsigned char __res[]; /* residual data structure */
static bd_t *binfo = (bd_t *)__res;

/*
 * See include/linux/mtd/partitions.h for definition of the mtd_partition
 * structure.
 */

#define RSPPC7D_BOOT_PARTITION_SIZE	0x800000
#define RSPPC7D_KERNEL_PARTITION_SIZE	0x400000

static struct mtd_partition rsppc7d_partitions[] = {
        {
                .name =         "User",
                .size =         0,	/* set at runtime */
                .offset =       0
        },{
                .name =         "Kernel",
                .size =         RSPPC7D_KERNEL_PARTITION_SIZE,
                .offset =       MTDPART_OFS_APPEND
        },{
                .name =         "PPCBoot",
                .size =         RSPPC7D_BOOT_PARTITION_SIZE,
                .offset =       MTDPART_OFS_APPEND,
                .mask_flags =   MTD_WRITEABLE  /* force read-only */
        }
};

static int __init rsppc7d_static_partitions(struct mtd_partition **parts)
{
	int nb_parts = 0;

	rsppc7d_partitions[0].size = binfo->bi_flashsize - 
				     RSPPC7D_KERNEL_PARTITION_SIZE - 
				     RSPPC7D_BOOT_PARTITION_SIZE;
	*parts       = rsppc7d_partitions;
	nb_parts     = ARRAY_SIZE(rsppc7d_partitions);

	return nb_parts;
}

struct rsppc7d_info {
	unsigned long base;
	unsigned long size;
	int width;
	void __iomem *vbase;
	struct map_info *map;
	struct mtd_info *mtd;
	struct resource *res;
};

static struct map_info *io_mapped_map = NULL;

/*
 * In order to avoid running out of vmalloc space, this
 * function is used to 'page' the map which is currently 
 * being used in and out of vmalloc.
 */
static void __iomem *rsppc7d_ioremap_flash(struct map_info *map)
{
	if (map != io_mapped_map) {
		if (io_mapped_map) {
			iounmap(io_mapped_map->virt);
			io_mapped_map->virt = NULL;
		}
		io_mapped_map = map;
		io_mapped_map->virt = ioremap(map->phys, map->size);
		if (!io_mapped_map->virt) {
			printk(KERN_ERR "rsppc7d_flash: unable to ioremap Flash\n");
		}
	}

	return io_mapped_map->virt;
}

/* 
 * Flash access functions - fall through to default 
 * functions after ioremapping the current map.
 */
static inline map_word rsppc7d_flash_read(struct map_info *map, unsigned long ofs)
{
	rsppc7d_ioremap_flash(map);
	return inline_map_read(map, ofs);
}

static void rsppc7d_flash_write(struct map_info *map, const map_word datum, unsigned long ofs)
{
	rsppc7d_ioremap_flash(map);
	inline_map_write(map, datum, ofs);	
}

static void rsppc7d_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
	rsppc7d_ioremap_flash(map);
	inline_map_copy_from(map, to, from, len);
}

static void rsppc7d_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
	rsppc7d_ioremap_flash(map);
	inline_map_copy_to(map, to, from, len);
}

#define MAX_NR_SUBMTD 4

static struct rsppc7d_info info[MAX_NR_SUBMTD];

static int __init rsppc7d_setup_mtd(struct rsppc7d_info *rsppc7d, int nr, struct mtd_info **rmtd)
{
	struct mtd_info *subdev[nr];
	struct map_info *maps;
	int i, found = 0, ret = 0;

	/*
	 * Allocate the map_info structs in one go.
	 */
	maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL);
	if (!maps)
		return -ENOMEM;
	memset(maps, 0, sizeof(struct map_info) * nr);

	/*
	 * Claim and then map the memory regions.
	 */
	for (i = 0; i < nr; i++) {
		if (rsppc7d[i].base == (unsigned long)-1)
			break;
		
		rsppc7d[i].res = request_mem_region(rsppc7d[i].base, rsppc7d[i].size, "rsppc7d flash");
		if (!rsppc7d[i].res) {
			ret = -EBUSY;
			break;
		}

		rsppc7d[i].map = maps + i;

		rsppc7d[i].map->name = "rsppc7d flash";
		rsppc7d[i].map->size = rsppc7d[i].size;
		rsppc7d[i].map->phys = rsppc7d[i].base;
		rsppc7d[i].map->bankwidth = rsppc7d[i].width;

		rsppc7d[i].map->read = rsppc7d_flash_read;
		rsppc7d[i].map->write = rsppc7d_flash_write;
		rsppc7d[i].map->copy_from = rsppc7d_flash_copy_from;
		rsppc7d[i].map->copy_to = rsppc7d_flash_copy_to;

		rsppc7d[i].mtd = do_map_probe("cfi_probe", rsppc7d[i].map);
		if (rsppc7d[i].mtd == NULL) {
			ret = -ENXIO;
			break;
		}
		rsppc7d[i].mtd->owner = THIS_MODULE;
		subdev[i] = rsppc7d[i].mtd;

		printk(KERN_INFO "rsppc7d flash: CFI device at 0x%08lx, %dMiB, "
			"%d-bit\n", rsppc7d[i].base, rsppc7d[i].mtd->size >> 20,
			rsppc7d[i].width * 8);
		found += 1;
	}

	/*
	 * ENXIO is special.  It means we didn't find a chip when
	 * we probed.  We need to tear down the mapping, free the
	 * resource and mark it as such.
	 */
	if (ret == -ENXIO) {
		release_resource(rsppc7d[i].res);
		rsppc7d[i].res = NULL;
	}

	/*
	 * If we found one device, don't bother with concat support.
	 * If we found multiple devices, use concat if we have it
	 * available, otherwise fail.
	 */
	if (ret == 0 || ret == -ENXIO) {
		if (found == 1) {
			*rmtd = subdev[0];
			ret = 0;
		} else if (found > 1) {
			/*
			 * We detected multiple devices.  Concatenate
			 * them together.
			 */
#ifdef CONFIG_MTD_CONCAT
			*rmtd = mtd_concat_create(subdev, found,
						  "rsppc7d flash");
			if (*rmtd == NULL)
				ret = -ENXIO;
#else
			printk(KERN_ERR "rsppc7d flash: multiple devices "
			       "found but MTD concat support disabled.\n");
			ret = -ENXIO;
#endif
		}
	}

	/*
	 * If we failed, clean up.
	 */
	if (ret) {
		do {
			if (rsppc7d[i].mtd)
				map_destroy(rsppc7d[i].mtd);
			if (rsppc7d[i].res)
				release_resource(rsppc7d[i].res);
		} while (i--);

		kfree(maps);
	}

	return ret;
}

static void __exit rsppc7d_destroy_mtd(struct rsppc7d_info *rsppc7d, struct mtd_info *mtd)
{
	int i;

	del_mtd_partitions(mtd);

	if (mtd != rsppc7d[0].mtd)
		mtd_concat_destroy(mtd);

	for (i = MAX_NR_SUBMTD; i >= 0; i--) {
		if (rsppc7d[i].mtd)
			map_destroy(rsppc7d[i].mtd);
		if (rsppc7d[i].res)
			release_resource(rsppc7d[i].res);
	}

	if (io_mapped_map)
		iounmap(io_mapped_map->virt);

	kfree(rsppc7d[0].map);
}

/*
 * We define the memory space, size, and width for the flash memory
 * space here.
 */

#define RSPPC7D_FLASH_BANK_WIDTH 4

static int __init rsppc7d_setup_flash(void)
{
	int nr, banksz, banknr;
	unsigned char memcfg;

        static int flash_sizes[4] = { 0x04000000, 0x02000000, 0, 0x01000000 };
        static int flash_banks[4] = { 4, 3, 2, 1 };

	memcfg = inb(PPC7D_CPLD_MEM_CONFIG_EXTEND);
	banksz = flash_sizes[memcfg & PPC7D_CPLD_FLASH_DEV_SIZE_MASK];
	banknr = flash_banks[(memcfg & PPC7D_CPLD_FLASH_BANK_NUM_MASK) >> 2];

	for (nr = 0; nr < banknr; nr++)
	{
		info[nr].base = binfo->bi_flashstart + (nr * banksz);
		info[nr].size = banksz;
		info[nr].width = RSPPC7D_FLASH_BANK_WIDTH;
	}

	return banknr;
}

static struct mtd_partition *parsed_parts;
static const char *probes[] = { "cmdlinepart", NULL };

static void __init rsppc7d_locate_partitions(struct mtd_info *mtd)
{
	const char *part_type = NULL;
	int nr_parts = 0;
	do {
		/*
		 * Partition selection stuff.
		 */
		nr_parts = parse_mtd_partitions(mtd, probes, &parsed_parts, 0);
		if (nr_parts > 0) {
			part_type = "command line";
			break;
		}
		nr_parts = rsppc7d_static_partitions(&parsed_parts);
		if (nr_parts > 0) {
			part_type = "static";
			break;
		}
		printk("found: %d partitions\n", nr_parts);
	} while (0);

	if (nr_parts == 0) {
		printk(KERN_NOTICE "rsppc7d flash: no partition info "
			"available, registering whole flash\n");
		add_mtd_device(mtd);
	} else {
		printk(KERN_NOTICE "rsppc7d flash: using %s partition "
			"definition\n", part_type);
		add_mtd_partitions(mtd, parsed_parts, nr_parts);
	}

	/* Always succeeds. */
}

static void __exit rsppc7d_destroy_partitions(void)
{
	if (parsed_parts)
		kfree(parsed_parts);
}

static struct mtd_info *mymtd;

static int __init rsppc7d_mtd_init(void)
{
	int ret;
	int nr;

	nr = rsppc7d_setup_flash();
	if (nr < 0)
		return nr;

	ret = rsppc7d_setup_mtd(info, nr, &mymtd);
	if (ret)
		return ret;

	rsppc7d_locate_partitions(mymtd);

	return 0;
}

static void __exit rsppc7d_mtd_cleanup(void)
{
	rsppc7d_destroy_mtd(info, mymtd);
	rsppc7d_destroy_partitions();
}

module_init(rsppc7d_mtd_init);
module_exit(rsppc7d_mtd_cleanup);

MODULE_AUTHOR("Chris Elston <chris.elston@radstone.co.uk>");
MODULE_DESCRIPTION("MTD map driver for Radstone PPC7D");
MODULE_LICENSE("GPL");

^ permalink raw reply	[flat|nested] 7+ messages in thread
* RE: MTD Mapping driver - out of vmalloc space
@ 2005-04-12 10:07 Chris Elston
  2005-04-15  5:45 ` Jörn Engel
  0 siblings, 1 reply; 7+ messages in thread
From: Chris Elston @ 2005-04-12 10:07 UTC (permalink / raw)
  To: linuxppc-embedded

>=20>=20How=20about=20setting=20PAGE_OFFSET=20to=200x80000000,=20that=20is=
=202G/2G=20split?
>=20>=20It=20would=20make=20enough=20virtual=20address=20space.=20I've=20n=
ever=20tried=20on=20PPC,
>=20>=20so=20I=20don't=20know=20the=20side=20effect=20of=20this.=20
>=20
>=20This=20is=20the=20ideal=20solution.=20=20It=20works=20fine=20with=20th=
e=20caveat=20that
>=20you=20have=20to=20be=20very=20aware=20of=20the=20virtual=20memory=20ma=
p=20of=20your=20board
>=20port=20when=20doing=20this.=20=20On=20ppc32,=20we=20have=20very=20fine=
=20grained=20options
>=20for=20manipulating=20this=20and=20other=20parameters=20under=20Advance=
d=20Options.
>=20If=20the=20original=20poster=20can=20provide=20more=20details=20then=20=
we=20can=20tell
>=20him=20the=20exact=20settings=20to=20use.

Thanks=20for=20everyone's=20input=20on=20this,=20I've=20moved=20the=20kern=
el=20virtual
base=20address=20to=200xa0000000,=20and=20it=20works=20fine=20now.=20=20

I'm=20still=20not=20convinced=20that=20this=20is=20a=20future=20proof=20so=
lution=20
though.=20=20What=20happens=20when=20I=20get=20a=20board=20with=20512MB=20=
Flash=201GB=20SDRAM?
I=20can=20push=20the=20top=20of=20the=20SDRAM=20out=20to=20the=20high=20me=
m=20area,=20but=20I'll=20
have=20to=20encroach=20further=20into=20user=20space=20to=20map=20the=20Fl=
ash.=20=20There's
no=20good=20reason=20that=20the=20whole=20of=20the=20Flash=20need=20be=20m=
apped=20at=20the=20same
time.=20(Perhaps=20performance?)

Thanks,

Chris.

________________________________________________________________________
This=20e-mail=20has=20been=20scanned=20for=20all=20viruses=20by=20Star.=20=
The
service=20is=20powered=20by=20MessageLabs.=20For=20more=20information=20on=
=20a=20proactive
anti-virus=20service=20working=20around=20the=20clock,=20around=20the=20gl=
obe,=20visit:
http://www.star.net.uk
________________________________________________________________________

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2005-04-15  5:45 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-04-08 14:42 MTD Mapping driver - out of vmalloc space Chris Elston
2005-04-08 17:51 ` Ho Lee
2005-04-08 18:39   ` Matt Porter
2005-04-08 18:39   ` Mark A. Greer
2005-04-08 18:48   ` Eugene Surovegin
  -- strict thread matches above, loose matches on Subject: below --
2005-04-12 10:07 Chris Elston
2005-04-15  5:45 ` Jörn Engel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).