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

From: Ben Dooks
Date: Fri Aug 05 2011 - 04:12:10 EST


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

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

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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/