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