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

Ben Dooks ben-linux at fluff.org
Fri Aug 5 04:11:44 EDT 2011


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 at 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 at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

-- 
Ben Dooks, ben at fluff.org, http://www.fluff.org/ben/

Large Hadron Colada: A large Pina Colada that makes the universe disappear.




More information about the linux-arm-kernel mailing list