1 #include <linux/kernel.h> 2 #include <linux/export.h> 3 #include <linux/spinlock_types.h> 4 #include <linux/init.h> 5 #include <asm/page.h> 6 #include <asm/setup.h> 7 #include <asm/io.h> 8 #include <asm/cpufeature.h> 9 #include <asm/special_insns.h> 10 #include <asm/pgtable.h> 11 #include <asm/olpc_ofw.h> 12 13 /* address of OFW callback interface; will be NULL if OFW isn't found */ 14 static int (*olpc_ofw_cif)(int *); 15 16 /* page dir entry containing OFW's pgdir table; filled in by head_32.S */ 17 u32 olpc_ofw_pgd __initdata; 18 19 static DEFINE_SPINLOCK(ofw_lock); 20 21 #define MAXARGS 10 22 23 void __init setup_olpc_ofw_pgd(void) 24 { 25 pgd_t *base, *ofw_pde; 26 27 if (!olpc_ofw_cif) 28 return; 29 30 /* fetch OFW's PDE */ 31 base = early_ioremap(olpc_ofw_pgd, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD); 32 if (!base) { 33 printk(KERN_ERR "failed to remap OFW's pgd - disabling OFW!\n"); 34 olpc_ofw_cif = NULL; 35 return; 36 } 37 ofw_pde = &base[OLPC_OFW_PDE_NR]; 38 39 /* install OFW's PDE permanently into the kernel's pgtable */ 40 set_pgd(&swapper_pg_dir[OLPC_OFW_PDE_NR], *ofw_pde); 41 /* implicit optimization barrier here due to uninline function return */ 42 43 early_iounmap(base, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD); 44 } 45 46 int __olpc_ofw(const char *name, int nr_args, const void **args, int nr_res, 47 void **res) 48 { 49 int ofw_args[MAXARGS + 3]; 50 unsigned long flags; 51 int ret, i, *p; 52 53 BUG_ON(nr_args + nr_res > MAXARGS); 54 55 if (!olpc_ofw_cif) 56 return -EIO; 57 58 ofw_args[0] = (int)name; 59 ofw_args[1] = nr_args; 60 ofw_args[2] = nr_res; 61 62 p = &ofw_args[3]; 63 for (i = 0; i < nr_args; i++, p++) 64 *p = (int)args[i]; 65 66 /* call into ofw */ 67 spin_lock_irqsave(&ofw_lock, flags); 68 ret = olpc_ofw_cif(ofw_args); 69 spin_unlock_irqrestore(&ofw_lock, flags); 70 71 if (!ret) { 72 for (i = 0; i < nr_res; i++, p++) 73 *((int *)res[i]) = *p; 74 } 75 76 return ret; 77 } 78 EXPORT_SYMBOL_GPL(__olpc_ofw); 79 80 bool olpc_ofw_present(void) 81 { 82 return olpc_ofw_cif != NULL; 83 } 84 EXPORT_SYMBOL_GPL(olpc_ofw_present); 85 86 /* OFW cif _should_ be above this address */ 87 #define OFW_MIN 0xff000000 88 89 /* OFW starts on a 1MB boundary */ 90 #define OFW_BOUND (1<<20) 91 92 void __init olpc_ofw_detect(void) 93 { 94 struct olpc_ofw_header *hdr = &boot_params.olpc_ofw_header; 95 unsigned long start; 96 97 /* ensure OFW booted us by checking for "OFW " string */ 98 if (hdr->ofw_magic != OLPC_OFW_SIG) 99 return; 100 101 olpc_ofw_cif = (int (*)(int *))hdr->cif_handler; 102 103 if ((unsigned long)olpc_ofw_cif < OFW_MIN) { 104 printk(KERN_ERR "OFW detected, but cif has invalid address 0x%lx - disabling.\n", 105 (unsigned long)olpc_ofw_cif); 106 olpc_ofw_cif = NULL; 107 return; 108 } 109 110 /* determine where OFW starts in memory */ 111 start = round_down((unsigned long)olpc_ofw_cif, OFW_BOUND); 112 printk(KERN_INFO "OFW detected in memory, cif @ 0x%lx (reserving top %ldMB)\n", 113 (unsigned long)olpc_ofw_cif, (-start) >> 20); 114 reserve_top_address(-start); 115 } 116 117 bool __init olpc_ofw_is_installed(void) 118 { 119 return olpc_ofw_cif != NULL; 120 } 121