diff mbox

[RFC,v1] ARM: olpc: Add support for calling into the XO-1.75's OpenFirmware (OFW)

Message ID 20110804162551.2b92b2cf@debxo (mailing list archive)
State New, archived
Headers show

Commit Message

Andres Salomon Aug. 4, 2011, 11:25 p.m. UTC
Add support for saving OFW's cif, and later calling into it to run OFW
commands from the kernel.  OFW remains resident in memory after boot,
and the physical/virtual addresses are passed in a boot tag.  We parse
that, and map the addresses.

This is currently only used by the OLPC XO-1.75, so it's named olpc_ofw().

Signed-off-by: Andres Salomon <dilinger@queued.net>
---
 Documentation/arm/Setup                  |    2 +
 arch/arm/Kconfig                         |    8 ++
 arch/arm/include/asm/olpc_ofw.h          |   23 ++++++
 arch/arm/include/asm/setup.h             |   24 ++++++
 arch/arm/mach-mmp/Makefile               |    1 +
 arch/arm/mach-mmp/include/mach/vmalloc.h |    2 +-
 arch/arm/mach-mmp/olpc-xo-1-75.c         |    7 ++
 arch/arm/mach-mmp/olpc_ofw.c             |  118 ++++++++++++++++++++++++++++++
 8 files changed, 184 insertions(+), 1 deletions(-)
 create mode 100644 arch/arm/include/asm/olpc_ofw.h
 create mode 100644 arch/arm/mach-mmp/olpc_ofw.c


I'm looking for input on our mechanism for calling into OLPC's openfirmware
on arm.  Some of the x86 folks are cc'd
as they had lots of comment when we did this for x86 OLPC machines.

There's a device tree patch on top of this that can be seen here:
http://dev.laptop.org/git/olpc-3.0/patch/?id=12377851f9a64a9e2098adf09f858bed7d3eae7c

Unlike the XO-1, I'd like to get this OFW communication mechanism ACKed prior to
OLPC's mass production of these units.

Comments

Eric Miao Aug. 4, 2011, 11:32 p.m. UTC | #1
On Fri, Aug 5, 2011 at 12:25 AM, Andres Salomon <dilinger@queued.net> wrote:
> Add support for saving OFW's cif, and later calling into it to run OFW
> commands from the kernel.  OFW remains resident in memory after boot,
> and the physical/virtual addresses are passed in a boot tag.  We parse
> that, and map the addresses.
>
> This is currently only used by the OLPC XO-1.75, so it's named olpc_ofw().
>
> Signed-off-by: Andres Salomon <dilinger@queued.net>
> ---
>  Documentation/arm/Setup                  |    2 +
>  arch/arm/Kconfig                         |    8 ++
>  arch/arm/include/asm/olpc_ofw.h          |   23 ++++++
>  arch/arm/include/asm/setup.h             |   24 ++++++
>  arch/arm/mach-mmp/Makefile               |    1 +
>  arch/arm/mach-mmp/include/mach/vmalloc.h |    2 +-
>  arch/arm/mach-mmp/olpc-xo-1-75.c         |    7 ++
>  arch/arm/mach-mmp/olpc_ofw.c             |  118 ++++++++++++++++++++++++++++++
>  8 files changed, 184 insertions(+), 1 deletions(-)
>  create mode 100644 arch/arm/include/asm/olpc_ofw.h
>  create mode 100644 arch/arm/mach-mmp/olpc_ofw.c
>
>
> I'm looking for input on our mechanism for calling into OLPC's openfirmware
> on arm.  Some of the x86 folks are cc'd
> as they had lots of comment when we did this for x86 OLPC machines.
>
> There's a device tree patch on top of this that can be seen here:
> http://dev.laptop.org/git/olpc-3.0/patch/?id=12377851f9a64a9e2098adf09f858bed7d3eae7c
>
> Unlike the XO-1, I'd like to get this OFW communication mechanism ACKed prior to
> OLPC's mass production of these units.

Not really sure if it's the standard way of calling back into GFW, Grant?
Ben Dooks Aug. 5, 2011, 8:11 a.m. UTC | #2
On Thu, Aug 04, 2011 at 04:25:51PM -0700, Andres Salomon wrote:
> Add support for saving OFW's cif, and later calling into it to run OFW
> commands from the kernel.  OFW remains resident in memory after boot,
> and the physical/virtual addresses are passed in a boot tag.  We parse
> that, and map the addresses.
> 
> This is currently only used by the OLPC XO-1.75, so it's named olpc_ofw().
> 
> Signed-off-by: Andres Salomon <dilinger@queued.net>
> ---
>  Documentation/arm/Setup                  |    2 +
>  arch/arm/Kconfig                         |    8 ++
>  arch/arm/include/asm/olpc_ofw.h          |   23 ++++++
>  arch/arm/include/asm/setup.h             |   24 ++++++
>  arch/arm/mach-mmp/Makefile               |    1 +
>  arch/arm/mach-mmp/include/mach/vmalloc.h |    2 +-
>  arch/arm/mach-mmp/olpc-xo-1-75.c         |    7 ++
>  arch/arm/mach-mmp/olpc_ofw.c             |  118 ++++++++++++++++++++++++++++++
>  8 files changed, 184 insertions(+), 1 deletions(-)
>  create mode 100644 arch/arm/include/asm/olpc_ofw.h
>  create mode 100644 arch/arm/mach-mmp/olpc_ofw.c
> 
> 
> I'm looking for input on our mechanism for calling into OLPC's openfirmware
> on arm.  Some of the x86 folks are cc'd
> as they had lots of comment when we did this for x86 OLPC machines.
> 
> There's a device tree patch on top of this that can be seen here:
> http://dev.laptop.org/git/olpc-3.0/patch/?id=12377851f9a64a9e2098adf09f858bed7d3eae7c
> 
> Unlike the XO-1, I'd like to get this OFW communication mechanism ACKed prior to
> OLPC's mass production of these units.
> 
> diff --git a/Documentation/arm/Setup b/Documentation/arm/Setup
> index 0cb1e64..e9272ff 100644
> --- a/Documentation/arm/Setup
> +++ b/Documentation/arm/Setup
> @@ -124,6 +124,8 @@ below:
>  
>     These are now obsolete, and should not be used.
>  
> +TODO: document olpc stuff
> +
>   commandline
>  
>     Kernel command line parameters.  Details can be found elsewhere.
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 5adeb8b..a1d9801 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -2028,6 +2028,14 @@ config ARCH_SUSPEND_POSSIBLE
>  
>  endmenu
>  
> +if ARCH_MMP
> +config OLPC
> +	bool "One Laptop Per Child support"
> +	---help---
> +	  Add support for detecting the unique features of the OLPC
> +	  XO hardware.
> +endif
> +

Please do not edit arch/arm/Kconfig, use arch/arm/mach-mmp's Kconfig
for machine entries.

Machine entries should be prefixed with MACH_ to make them easier to
identify.

  source "net/Kconfig"
>  
>  source "drivers/Kconfig"
> diff --git a/arch/arm/include/asm/olpc_ofw.h b/arch/arm/include/asm/olpc_ofw.h
> new file mode 100644
> index 0000000..5346e9f
> --- /dev/null
> +++ b/arch/arm/include/asm/olpc_ofw.h
> @@ -0,0 +1,23 @@
> +#ifndef _ASM_ARM_OLPC_OFW_H
> +#define _ASM_ARM_OLPC_OFW_H
> +
> +#ifdef CONFIG_OLPC
> +
> +/* run an OFW command by calling into the firmware */
> +#define olpc_ofw(name, args, res) \
> +	__olpc_ofw((name), ARRAY_SIZE(args), args, ARRAY_SIZE(res), res)
> +
> +extern int __olpc_ofw(const char *name, int nr_args, const void **args,
> +		int nr_res, void **res);
> +
> +/* map OFW's page tables into kernel memory */
> +extern void setup_olpc_ofw_mapping(void);
> +
> +/* check if OFW was detected during boot */
> +extern bool olpc_ofw_present(void);
> +
> +#else /* !CONFIG_OLPC */
> +static inline void setup_olpc_ofw_mapping(void) { }
> +#endif /* !CONFIG_OLPC */
> +
> +#endif /* _ASM_ARM_OLPC_OFW_H */
> diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h
> index ee2ad8a..c75889e 100644
> --- a/arch/arm/include/asm/setup.h
> +++ b/arch/arm/include/asm/setup.h
> @@ -143,6 +143,25 @@ struct tag_memclk {
>  	__u32 fmemclk;
>  };
>  
> +/* OLPC OpenFirmware callback parameters: see arch/arm/mach-mmp/olpc_ofw.c */
> +#define ATAG_OLPC_OFW	0x41000502
> +
> +struct tag_olpc_ofw {
> +	__u32 cif_handler;	/* callback into OFW */
> +
> +	/* map_desc one - OFW's main memory */
> +	__u32 ofw_va;
> +	__u32 ofw_pfn;		/* physical address's PFN */
> +	__u32 ofw_length;
> +	__u32 ofw_mem_type;	/* MT_ */
> +
> +	/* map_desc two - OFW's frame buffer */
> +	__u32 fb_va;
> +	__u32 fb_pfn;		/* physical address's PFN */
> +	__u32 fb_length;
> +	__u32 fb_mem_type;	/* MT_ */
> +};
> +

The new booting method for OF on ARM is pass a pointer to the dtb
via r2 instead of the ATAGs. You can get rid of ATAGs completely
at this point.

>  struct tag {
>  	struct tag_header hdr;
>  	union {
> @@ -165,6 +184,11 @@ struct tag {
>  		 * DC21285 specific
>  		 */
>  		struct tag_memclk	memclk;
> +
> +		/*
> +		 * OLPC specific - for OLPC's OpenFirmware implementation
> +		 */
> +		struct tag_olpc_ofw	olpc_ofw;
>  	} u;
>  };
>  
> diff --git a/arch/arm/mach-mmp/Makefile b/arch/arm/mach-mmp/Makefile
> index 00a4954..398da1d 100644
> --- a/arch/arm/mach-mmp/Makefile
> +++ b/arch/arm/mach-mmp/Makefile
> @@ -23,3 +23,4 @@ obj-$(CONFIG_MACH_FLINT)	+= flint.o
>  obj-$(CONFIG_MACH_MARVELL_JASPER) += jasper.o
>  obj-$(CONFIG_MACH_TETON_BGA)	+= teton_bga.o
>  obj-$(CONFIG_MACH_OLPC_XO_1_75)	+= olpc-xo-1-75.o
> +obj-$(CONFIG_OLPC)		+= olpc_ofw.o
> diff --git a/arch/arm/mach-mmp/include/mach/vmalloc.h b/arch/arm/mach-mmp/include/mach/vmalloc.h
> index 1d0bac0..3801e26 100644
> --- a/arch/arm/mach-mmp/include/mach/vmalloc.h
> +++ b/arch/arm/mach-mmp/include/mach/vmalloc.h
> @@ -2,4 +2,4 @@
>   * linux/arch/arm/mach-mmp/include/mach/vmalloc.h
>   */
>  
> -#define VMALLOC_END	0xfe000000UL
> +#define VMALLOC_END	0xfd600000UL
> diff --git a/arch/arm/mach-mmp/olpc-xo-1-75.c b/arch/arm/mach-mmp/olpc-xo-1-75.c
> index 46261db..9efb351 100644
> --- a/arch/arm/mach-mmp/olpc-xo-1-75.c
> +++ b/arch/arm/mach-mmp/olpc-xo-1-75.c
> @@ -29,6 +29,7 @@
>  #include <video/pxa168fb.h>
>  #include <plat/sdhci.h>
>  #include <linux/mmc/sdhci.h>
> +#include <asm/olpc_ofw.h>
>  #include "common.h"
>  
>  struct olpc_platform_t olpc_platform_info;
> @@ -369,6 +370,11 @@ void olpc_xo_1_75_restart(char mode, const char *cmd)
>  	olpc_xo_1_75_ec_cmd(ec_cmd, NULL, 0);
>  }
>  
> +static void __init olpc_init_early(void)
> +{
> +	setup_olpc_ofw_mapping();
> +}
> +
>  static void __init olpc_xo_1_75_init(void)
>  {
>  	u32 twsi6_lcr;
> @@ -451,4 +457,5 @@ MACHINE_START(OLPC_XO_1_75, "OLPC XO-1.75")
>  	.map_io		= mmp_map_io,
>  	.init_irq	= mmp2_init_irq,
>  	.timer		= &mmp2_timer,
> +	.init_early	= olpc_init_early,
>  MACHINE_END
> diff --git a/arch/arm/mach-mmp/olpc_ofw.c b/arch/arm/mach-mmp/olpc_ofw.c
> new file mode 100644
> index 0000000..55da24e
> --- /dev/null
> +++ b/arch/arm/mach-mmp/olpc_ofw.c
> @@ -0,0 +1,118 @@
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <asm/page.h>
> +#include <asm/setup.h>
> +#include <asm/io.h>
> +#include <asm/pgtable.h>
> +#include <asm/mach/map.h>
> +#include <asm/olpc_ofw.h>
> +#include <asm/memory.h>
> +
> +static struct map_desc olpc_ofw_map[] __initdata = {
> +	{
> +		.type		= MT_MEMORY,
> +	},
> +	{
> +		.type		= MT_MEMORY,
> +	},
> +};
> +
> +/* address of OFW callback interface; will be NULL if OFW isn't found */
> +static int (*olpc_ofw_cif)(int *);
> +
> +static DEFINE_SPINLOCK(ofw_lock);
> +
> +#define MAXARGS 10
> +
> +void __init setup_olpc_ofw_mapping(void)
> +{
> +	u32 *cif;
> +
> +	if (!olpc_ofw_cif)
> +		return;
> +
> +	iotable_init(olpc_ofw_map, ARRAY_SIZE(olpc_ofw_map));
> +
> +	/* verify page table setup worked; *cif should point somewhere sane */
> +	cif = (u32*)olpc_ofw_cif;
> +	if (*cif == 0x0 || *cif > 0xffff0000) {
> +		pr_err("OFW page tables are invalid (*cif=%x) - disabling.\n",
> +				*cif);
> +		olpc_ofw_cif = NULL;
> +	}
> +}
> +
> +int __olpc_ofw(const char *name, int nr_args, const void **args, int nr_res,
> +		void **res)
> +{
> +	int ofw_args[MAXARGS + 3];
> +	unsigned long flags;
> +	int ret, i, *p;
> +
> +	BUG_ON(nr_args + nr_res > MAXARGS);
> +
> +	if (!olpc_ofw_cif)
> +		return -EIO;
> +
> +	ofw_args[0] = (int)name;
> +	ofw_args[1] = nr_args;
> +	ofw_args[2] = nr_res;
> +
> +	p = &ofw_args[3];
> +	for (i = 0; i < nr_args; i++, p++)
> +		*p = (int)args[i];
> +
> +	/* call into ofw */
> +	spin_lock_irqsave(&ofw_lock, flags);
> +	ret = olpc_ofw_cif(ofw_args);
> +	spin_unlock_irqrestore(&ofw_lock, flags);
> +
> +	if (!ret) {
> +		for (i = 0; i < nr_res; i++, p++)
> +			*((int *)res[i]) = *p;
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(__olpc_ofw);
> +
> +bool olpc_ofw_present(void)
> +{
> +	return olpc_ofw_cif != NULL;
> +}
> +EXPORT_SYMBOL_GPL(olpc_ofw_present);
> +
> +static int __init parse_tag_olpc_ofw(const struct tag *tag)
> +{
> +	int i;
> +
> +	olpc_ofw_cif = (int (*)(int *))tag->u.olpc_ofw.cif_handler;
> +
> +	olpc_ofw_map[0].virtual = tag->u.olpc_ofw.ofw_va;
> +	olpc_ofw_map[0].pfn = tag->u.olpc_ofw.ofw_pfn;
> +	olpc_ofw_map[0].length = tag->u.olpc_ofw.ofw_length;
> +
> +	olpc_ofw_map[1].virtual = tag->u.olpc_ofw.fb_va;
> +	olpc_ofw_map[1].pfn = tag->u.olpc_ofw.fb_pfn;
> +	olpc_ofw_map[1].length = tag->u.olpc_ofw.fb_length;
> +
> +	if (olpc_ofw_map[0].virtual < VMALLOC_END) {
> +		pr_err("Invalid start address for OFW (%lx), disabling.\n",
> +				olpc_ofw_map[0].virtual);
> +		olpc_ofw_cif = NULL;
> +		return -EIO;
> +	}
> +
> +	pr_info("OFW detected in memory, cif @ 0x%p:\n", olpc_ofw_cif);
> +	for (i = 0; i < ARRAY_SIZE(olpc_ofw_map); i++) {
> +		struct map_desc *desc = &olpc_ofw_map[i];
> +		pr_info("  0x%lx - 0x%lx (%luMB)\n", desc->virtual,
> +				desc->virtual + desc->length - 1,
> +				desc->length >> 20);
> +	}
> +
> +	return 0;
> +}
> +
> +__tagtable(ATAG_OLPC_OFW, parse_tag_olpc_ofw);
> -- 
> 1.7.2.5
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Andres Salomon Aug. 5, 2011, 1:44 p.m. UTC | #3
On Fri, 5 Aug 2011 09:11:44 +0100
Ben Dooks <ben-linux@fluff.org> wrote:

> On Thu, Aug 04, 2011 at 04:25:51PM -0700, Andres Salomon wrote:
> > Add support for saving OFW's cif, and later calling into it to run
> > OFW commands from the kernel.  OFW remains resident in memory after
> > boot, and the physical/virtual addresses are passed in a boot tag.
> > We parse that, and map the addresses.
> > 
> > This is currently only used by the OLPC XO-1.75, so it's named
> > olpc_ofw().
> > 
> > Signed-off-by: Andres Salomon <dilinger@queued.net>
> > ---
> >  Documentation/arm/Setup                  |    2 +
> >  arch/arm/Kconfig                         |    8 ++
> >  arch/arm/include/asm/olpc_ofw.h          |   23 ++++++
> >  arch/arm/include/asm/setup.h             |   24 ++++++
> >  arch/arm/mach-mmp/Makefile               |    1 +
> >  arch/arm/mach-mmp/include/mach/vmalloc.h |    2 +-
> >  arch/arm/mach-mmp/olpc-xo-1-75.c         |    7 ++
> >  arch/arm/mach-mmp/olpc_ofw.c             |  118
> > ++++++++++++++++++++++++++++++ 8 files changed, 184 insertions(+),
> > 1 deletions(-) create mode 100644 arch/arm/include/asm/olpc_ofw.h
> >  create mode 100644 arch/arm/mach-mmp/olpc_ofw.c
> > 
> > 
> > I'm looking for input on our mechanism for calling into OLPC's
> > openfirmware on arm.  Some of the x86 folks are cc'd
> > as they had lots of comment when we did this for x86 OLPC machines.
> > 
> > There's a device tree patch on top of this that can be seen here:
> > http://dev.laptop.org/git/olpc-3.0/patch/?id=12377851f9a64a9e2098adf09f858bed7d3eae7c
> > 
> > Unlike the XO-1, I'd like to get this OFW communication mechanism
> > ACKed prior to OLPC's mass production of these units.
> > 
[...]
> >  
> >  source "drivers/Kconfig"
> > diff --git a/arch/arm/include/asm/olpc_ofw.h
> > b/arch/arm/include/asm/olpc_ofw.h new file mode 100644
> > index 0000000..5346e9f
> > --- /dev/null
> > +++ b/arch/arm/include/asm/olpc_ofw.h
> > @@ -0,0 +1,23 @@
> > +#ifndef _ASM_ARM_OLPC_OFW_H
> > +#define _ASM_ARM_OLPC_OFW_H
> > +
> > +#ifdef CONFIG_OLPC
> > +
> > +/* run an OFW command by calling into the firmware */
> > +#define olpc_ofw(name, args, res) \
> > +	__olpc_ofw((name), ARRAY_SIZE(args), args,
> > ARRAY_SIZE(res), res) +
> > +extern int __olpc_ofw(const char *name, int nr_args, const void
> > **args,
> > +		int nr_res, void **res);
> > +
> > +/* map OFW's page tables into kernel memory */
> > +extern void setup_olpc_ofw_mapping(void);
> > +
> > +/* check if OFW was detected during boot */
> > +extern bool olpc_ofw_present(void);
> > +
> > +#else /* !CONFIG_OLPC */
> > +static inline void setup_olpc_ofw_mapping(void) { }
> > +#endif /* !CONFIG_OLPC */
> > +
> > +#endif /* _ASM_ARM_OLPC_OFW_H */
> > diff --git a/arch/arm/include/asm/setup.h
> > b/arch/arm/include/asm/setup.h index ee2ad8a..c75889e 100644
> > --- a/arch/arm/include/asm/setup.h
> > +++ b/arch/arm/include/asm/setup.h
> > @@ -143,6 +143,25 @@ struct tag_memclk {
> >  	__u32 fmemclk;
> >  };
> >  
> > +/* OLPC OpenFirmware callback parameters: see
> > arch/arm/mach-mmp/olpc_ofw.c */ +#define ATAG_OLPC_OFW
> > 0x41000502 +
> > +struct tag_olpc_ofw {
> > +	__u32 cif_handler;	/* callback into OFW */
> > +
> > +	/* map_desc one - OFW's main memory */
> > +	__u32 ofw_va;
> > +	__u32 ofw_pfn;		/* physical address's PFN */
> > +	__u32 ofw_length;
> > +	__u32 ofw_mem_type;	/* MT_ */
> > +
> > +	/* map_desc two - OFW's frame buffer */
> > +	__u32 fb_va;
> > +	__u32 fb_pfn;		/* physical address's PFN */
> > +	__u32 fb_length;
> > +	__u32 fb_mem_type;	/* MT_ */
> > +};
> > +
> 
> The new booting method for OF on ARM is pass a pointer to the dtb
> via r2 instead of the ATAGs. You can get rid of ATAGs completely
> at this point.
> 

This is not (only) about the device tree.  It's about calling into
OFW.  For example, one could break into OFW to debug crashed hardware
(or software, I support) by hooking up a sysrq to call
olpc_ofw("enter").  Building the device tree is just one of the things
that we do.

> >  struct tag {
> >  	struct tag_header hdr;
> >  	union {
> > @@ -165,6 +184,11 @@ struct tag {
> >  		 * DC21285 specific
> >  		 */
> >  		struct tag_memclk	memclk;
> > +
> > +		/*
> > +		 * OLPC specific - for OLPC's OpenFirmware
> > implementation
> > +		 */
> > +		struct tag_olpc_ofw	olpc_ofw;
> >  	} u;
> >  };
> >
Russell King - ARM Linux Aug. 5, 2011, 7:22 p.m. UTC | #4
On Thu, Aug 04, 2011 at 04:25:51PM -0700, Andres Salomon wrote:
> Add support for saving OFW's cif, and later calling into it to run OFW
> commands from the kernel.  OFW remains resident in memory after boot,
> and the physical/virtual addresses are passed in a boot tag.

If you have open firmware, then you probably have a device tree being
passed to the kernel.  In that case, there aren't any boot tags being
passed.  So I think this patch is rather inconsistent.

Also I believe we had decided that ARM is not going to support the
runtime bits of DT/open firmware stuff.  Unless open firmware is aware
of the setup of the kernel mappings (which would then tie it directly
to the running kernel) it can't access any hardware resources.  I'm
not willing to have external binary blobs (whether or not the source
is available) having a dependency on the setup of the kernel page
tables.  Given that we are consolidating across all platforms, it's
likely that we will want to change the virtual address layout over time
and to have some external binary blob is just going to be a complete
nightmare.

So I really don't like this idea.  At all.
diff mbox

Patch

diff --git a/Documentation/arm/Setup b/Documentation/arm/Setup
index 0cb1e64..e9272ff 100644
--- a/Documentation/arm/Setup
+++ b/Documentation/arm/Setup
@@ -124,6 +124,8 @@  below:
 
    These are now obsolete, and should not be used.
 
+TODO: document olpc stuff
+
  commandline
 
    Kernel command line parameters.  Details can be found elsewhere.
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 5adeb8b..a1d9801 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -2028,6 +2028,14 @@  config ARCH_SUSPEND_POSSIBLE
 
 endmenu
 
+if ARCH_MMP
+config OLPC
+	bool "One Laptop Per Child support"
+	---help---
+	  Add support for detecting the unique features of the OLPC
+	  XO hardware.
+endif
+
 source "net/Kconfig"
 
 source "drivers/Kconfig"
diff --git a/arch/arm/include/asm/olpc_ofw.h b/arch/arm/include/asm/olpc_ofw.h
new file mode 100644
index 0000000..5346e9f
--- /dev/null
+++ b/arch/arm/include/asm/olpc_ofw.h
@@ -0,0 +1,23 @@ 
+#ifndef _ASM_ARM_OLPC_OFW_H
+#define _ASM_ARM_OLPC_OFW_H
+
+#ifdef CONFIG_OLPC
+
+/* run an OFW command by calling into the firmware */
+#define olpc_ofw(name, args, res) \
+	__olpc_ofw((name), ARRAY_SIZE(args), args, ARRAY_SIZE(res), res)
+
+extern int __olpc_ofw(const char *name, int nr_args, const void **args,
+		int nr_res, void **res);
+
+/* map OFW's page tables into kernel memory */
+extern void setup_olpc_ofw_mapping(void);
+
+/* check if OFW was detected during boot */
+extern bool olpc_ofw_present(void);
+
+#else /* !CONFIG_OLPC */
+static inline void setup_olpc_ofw_mapping(void) { }
+#endif /* !CONFIG_OLPC */
+
+#endif /* _ASM_ARM_OLPC_OFW_H */
diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h
index ee2ad8a..c75889e 100644
--- a/arch/arm/include/asm/setup.h
+++ b/arch/arm/include/asm/setup.h
@@ -143,6 +143,25 @@  struct tag_memclk {
 	__u32 fmemclk;
 };
 
+/* OLPC OpenFirmware callback parameters: see arch/arm/mach-mmp/olpc_ofw.c */
+#define ATAG_OLPC_OFW	0x41000502
+
+struct tag_olpc_ofw {
+	__u32 cif_handler;	/* callback into OFW */
+
+	/* map_desc one - OFW's main memory */
+	__u32 ofw_va;
+	__u32 ofw_pfn;		/* physical address's PFN */
+	__u32 ofw_length;
+	__u32 ofw_mem_type;	/* MT_ */
+
+	/* map_desc two - OFW's frame buffer */
+	__u32 fb_va;
+	__u32 fb_pfn;		/* physical address's PFN */
+	__u32 fb_length;
+	__u32 fb_mem_type;	/* MT_ */
+};
+
 struct tag {
 	struct tag_header hdr;
 	union {
@@ -165,6 +184,11 @@  struct tag {
 		 * DC21285 specific
 		 */
 		struct tag_memclk	memclk;
+
+		/*
+		 * OLPC specific - for OLPC's OpenFirmware implementation
+		 */
+		struct tag_olpc_ofw	olpc_ofw;
 	} u;
 };
 
diff --git a/arch/arm/mach-mmp/Makefile b/arch/arm/mach-mmp/Makefile
index 00a4954..398da1d 100644
--- a/arch/arm/mach-mmp/Makefile
+++ b/arch/arm/mach-mmp/Makefile
@@ -23,3 +23,4 @@  obj-$(CONFIG_MACH_FLINT)	+= flint.o
 obj-$(CONFIG_MACH_MARVELL_JASPER) += jasper.o
 obj-$(CONFIG_MACH_TETON_BGA)	+= teton_bga.o
 obj-$(CONFIG_MACH_OLPC_XO_1_75)	+= olpc-xo-1-75.o
+obj-$(CONFIG_OLPC)		+= olpc_ofw.o
diff --git a/arch/arm/mach-mmp/include/mach/vmalloc.h b/arch/arm/mach-mmp/include/mach/vmalloc.h
index 1d0bac0..3801e26 100644
--- a/arch/arm/mach-mmp/include/mach/vmalloc.h
+++ b/arch/arm/mach-mmp/include/mach/vmalloc.h
@@ -2,4 +2,4 @@ 
  * linux/arch/arm/mach-mmp/include/mach/vmalloc.h
  */
 
-#define VMALLOC_END	0xfe000000UL
+#define VMALLOC_END	0xfd600000UL
diff --git a/arch/arm/mach-mmp/olpc-xo-1-75.c b/arch/arm/mach-mmp/olpc-xo-1-75.c
index 46261db..9efb351 100644
--- a/arch/arm/mach-mmp/olpc-xo-1-75.c
+++ b/arch/arm/mach-mmp/olpc-xo-1-75.c
@@ -29,6 +29,7 @@ 
 #include <video/pxa168fb.h>
 #include <plat/sdhci.h>
 #include <linux/mmc/sdhci.h>
+#include <asm/olpc_ofw.h>
 #include "common.h"
 
 struct olpc_platform_t olpc_platform_info;
@@ -369,6 +370,11 @@  void olpc_xo_1_75_restart(char mode, const char *cmd)
 	olpc_xo_1_75_ec_cmd(ec_cmd, NULL, 0);
 }
 
+static void __init olpc_init_early(void)
+{
+	setup_olpc_ofw_mapping();
+}
+
 static void __init olpc_xo_1_75_init(void)
 {
 	u32 twsi6_lcr;
@@ -451,4 +457,5 @@  MACHINE_START(OLPC_XO_1_75, "OLPC XO-1.75")
 	.map_io		= mmp_map_io,
 	.init_irq	= mmp2_init_irq,
 	.timer		= &mmp2_timer,
+	.init_early	= olpc_init_early,
 MACHINE_END
diff --git a/arch/arm/mach-mmp/olpc_ofw.c b/arch/arm/mach-mmp/olpc_ofw.c
new file mode 100644
index 0000000..55da24e
--- /dev/null
+++ b/arch/arm/mach-mmp/olpc_ofw.c
@@ -0,0 +1,118 @@ 
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/page.h>
+#include <asm/setup.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/mach/map.h>
+#include <asm/olpc_ofw.h>
+#include <asm/memory.h>
+
+static struct map_desc olpc_ofw_map[] __initdata = {
+	{
+		.type		= MT_MEMORY,
+	},
+	{
+		.type		= MT_MEMORY,
+	},
+};
+
+/* address of OFW callback interface; will be NULL if OFW isn't found */
+static int (*olpc_ofw_cif)(int *);
+
+static DEFINE_SPINLOCK(ofw_lock);
+
+#define MAXARGS 10
+
+void __init setup_olpc_ofw_mapping(void)
+{
+	u32 *cif;
+
+	if (!olpc_ofw_cif)
+		return;
+
+	iotable_init(olpc_ofw_map, ARRAY_SIZE(olpc_ofw_map));
+
+	/* verify page table setup worked; *cif should point somewhere sane */
+	cif = (u32*)olpc_ofw_cif;
+	if (*cif == 0x0 || *cif > 0xffff0000) {
+		pr_err("OFW page tables are invalid (*cif=%x) - disabling.\n",
+				*cif);
+		olpc_ofw_cif = NULL;
+	}
+}
+
+int __olpc_ofw(const char *name, int nr_args, const void **args, int nr_res,
+		void **res)
+{
+	int ofw_args[MAXARGS + 3];
+	unsigned long flags;
+	int ret, i, *p;
+
+	BUG_ON(nr_args + nr_res > MAXARGS);
+
+	if (!olpc_ofw_cif)
+		return -EIO;
+
+	ofw_args[0] = (int)name;
+	ofw_args[1] = nr_args;
+	ofw_args[2] = nr_res;
+
+	p = &ofw_args[3];
+	for (i = 0; i < nr_args; i++, p++)
+		*p = (int)args[i];
+
+	/* call into ofw */
+	spin_lock_irqsave(&ofw_lock, flags);
+	ret = olpc_ofw_cif(ofw_args);
+	spin_unlock_irqrestore(&ofw_lock, flags);
+
+	if (!ret) {
+		for (i = 0; i < nr_res; i++, p++)
+			*((int *)res[i]) = *p;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(__olpc_ofw);
+
+bool olpc_ofw_present(void)
+{
+	return olpc_ofw_cif != NULL;
+}
+EXPORT_SYMBOL_GPL(olpc_ofw_present);
+
+static int __init parse_tag_olpc_ofw(const struct tag *tag)
+{
+	int i;
+
+	olpc_ofw_cif = (int (*)(int *))tag->u.olpc_ofw.cif_handler;
+
+	olpc_ofw_map[0].virtual = tag->u.olpc_ofw.ofw_va;
+	olpc_ofw_map[0].pfn = tag->u.olpc_ofw.ofw_pfn;
+	olpc_ofw_map[0].length = tag->u.olpc_ofw.ofw_length;
+
+	olpc_ofw_map[1].virtual = tag->u.olpc_ofw.fb_va;
+	olpc_ofw_map[1].pfn = tag->u.olpc_ofw.fb_pfn;
+	olpc_ofw_map[1].length = tag->u.olpc_ofw.fb_length;
+
+	if (olpc_ofw_map[0].virtual < VMALLOC_END) {
+		pr_err("Invalid start address for OFW (%lx), disabling.\n",
+				olpc_ofw_map[0].virtual);
+		olpc_ofw_cif = NULL;
+		return -EIO;
+	}
+
+	pr_info("OFW detected in memory, cif @ 0x%p:\n", olpc_ofw_cif);
+	for (i = 0; i < ARRAY_SIZE(olpc_ofw_map); i++) {
+		struct map_desc *desc = &olpc_ofw_map[i];
+		pr_info("  0x%lx - 0x%lx (%luMB)\n", desc->virtual,
+				desc->virtual + desc->length - 1,
+				desc->length >> 20);
+	}
+
+	return 0;
+}
+
+__tagtable(ATAG_OLPC_OFW, parse_tag_olpc_ofw);