Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 #include <linux/kernel.h>
0003 #include <linux/export.h>
0004 #include <linux/spinlock_types.h>
0005 #include <linux/init.h>
0006 #include <linux/pgtable.h>
0007 #include <asm/page.h>
0008 #include <asm/setup.h>
0009 #include <asm/io.h>
0010 #include <asm/cpufeature.h>
0011 #include <asm/special_insns.h>
0012 #include <asm/olpc_ofw.h>
0013 
0014 /* address of OFW callback interface; will be NULL if OFW isn't found */
0015 static int (*olpc_ofw_cif)(int *);
0016 
0017 /* page dir entry containing OFW's pgdir table; filled in by head_32.S */
0018 u32 olpc_ofw_pgd __initdata;
0019 
0020 static DEFINE_SPINLOCK(ofw_lock);
0021 
0022 #define MAXARGS 10
0023 
0024 void __init setup_olpc_ofw_pgd(void)
0025 {
0026     pgd_t *base, *ofw_pde;
0027 
0028     if (!olpc_ofw_cif)
0029         return;
0030 
0031     /* fetch OFW's PDE */
0032     base = early_ioremap(olpc_ofw_pgd, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);
0033     if (!base) {
0034         printk(KERN_ERR "failed to remap OFW's pgd - disabling OFW!\n");
0035         olpc_ofw_cif = NULL;
0036         return;
0037     }
0038     ofw_pde = &base[OLPC_OFW_PDE_NR];
0039 
0040     /* install OFW's PDE permanently into the kernel's pgtable */
0041     set_pgd(&swapper_pg_dir[OLPC_OFW_PDE_NR], *ofw_pde);
0042     /* implicit optimization barrier here due to uninline function return */
0043 
0044     early_iounmap(base, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);
0045 }
0046 
0047 int __olpc_ofw(const char *name, int nr_args, const void **args, int nr_res,
0048         void **res)
0049 {
0050     int ofw_args[MAXARGS + 3];
0051     unsigned long flags;
0052     int ret, i, *p;
0053 
0054     BUG_ON(nr_args + nr_res > MAXARGS);
0055 
0056     if (!olpc_ofw_cif)
0057         return -EIO;
0058 
0059     ofw_args[0] = (int)name;
0060     ofw_args[1] = nr_args;
0061     ofw_args[2] = nr_res;
0062 
0063     p = &ofw_args[3];
0064     for (i = 0; i < nr_args; i++, p++)
0065         *p = (int)args[i];
0066 
0067     /* call into ofw */
0068     spin_lock_irqsave(&ofw_lock, flags);
0069     ret = olpc_ofw_cif(ofw_args);
0070     spin_unlock_irqrestore(&ofw_lock, flags);
0071 
0072     if (!ret) {
0073         for (i = 0; i < nr_res; i++, p++)
0074             *((int *)res[i]) = *p;
0075     }
0076 
0077     return ret;
0078 }
0079 EXPORT_SYMBOL_GPL(__olpc_ofw);
0080 
0081 bool olpc_ofw_present(void)
0082 {
0083     return olpc_ofw_cif != NULL;
0084 }
0085 EXPORT_SYMBOL_GPL(olpc_ofw_present);
0086 
0087 /* OFW cif _should_ be above this address */
0088 #define OFW_MIN 0xff000000
0089 
0090 /* OFW starts on a 1MB boundary */
0091 #define OFW_BOUND (1<<20)
0092 
0093 void __init olpc_ofw_detect(void)
0094 {
0095     struct olpc_ofw_header *hdr = &boot_params.olpc_ofw_header;
0096     unsigned long start;
0097 
0098     /* ensure OFW booted us by checking for "OFW " string */
0099     if (hdr->ofw_magic != OLPC_OFW_SIG)
0100         return;
0101 
0102     olpc_ofw_cif = (int (*)(int *))hdr->cif_handler;
0103 
0104     if ((unsigned long)olpc_ofw_cif < OFW_MIN) {
0105         printk(KERN_ERR "OFW detected, but cif has invalid address 0x%lx - disabling.\n",
0106                 (unsigned long)olpc_ofw_cif);
0107         olpc_ofw_cif = NULL;
0108         return;
0109     }
0110 
0111     /* determine where OFW starts in memory */
0112     start = round_down((unsigned long)olpc_ofw_cif, OFW_BOUND);
0113     printk(KERN_INFO "OFW detected in memory, cif @ 0x%lx (reserving top %ldMB)\n",
0114             (unsigned long)olpc_ofw_cif, (-start) >> 20);
0115     reserve_top_address(-start);
0116 }
0117 
0118 bool __init olpc_ofw_is_installed(void)
0119 {
0120     return olpc_ofw_cif != NULL;
0121 }