162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci#include <linux/kernel.h> 362306a36Sopenharmony_ci#include <linux/export.h> 462306a36Sopenharmony_ci#include <linux/spinlock_types.h> 562306a36Sopenharmony_ci#include <linux/init.h> 662306a36Sopenharmony_ci#include <linux/pgtable.h> 762306a36Sopenharmony_ci#include <asm/page.h> 862306a36Sopenharmony_ci#include <asm/setup.h> 962306a36Sopenharmony_ci#include <asm/io.h> 1062306a36Sopenharmony_ci#include <asm/cpufeature.h> 1162306a36Sopenharmony_ci#include <asm/special_insns.h> 1262306a36Sopenharmony_ci#include <asm/olpc_ofw.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* address of OFW callback interface; will be NULL if OFW isn't found */ 1562306a36Sopenharmony_cistatic int (*olpc_ofw_cif)(int *); 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* page dir entry containing OFW's pgdir table; filled in by head_32.S */ 1862306a36Sopenharmony_ciu32 olpc_ofw_pgd __initdata; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ofw_lock); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define MAXARGS 10 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_civoid __init setup_olpc_ofw_pgd(void) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci pgd_t *base, *ofw_pde; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (!olpc_ofw_cif) 2962306a36Sopenharmony_ci return; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci /* fetch OFW's PDE */ 3262306a36Sopenharmony_ci base = early_ioremap(olpc_ofw_pgd, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD); 3362306a36Sopenharmony_ci if (!base) { 3462306a36Sopenharmony_ci printk(KERN_ERR "failed to remap OFW's pgd - disabling OFW!\n"); 3562306a36Sopenharmony_ci olpc_ofw_cif = NULL; 3662306a36Sopenharmony_ci return; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci ofw_pde = &base[OLPC_OFW_PDE_NR]; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* install OFW's PDE permanently into the kernel's pgtable */ 4162306a36Sopenharmony_ci set_pgd(&swapper_pg_dir[OLPC_OFW_PDE_NR], *ofw_pde); 4262306a36Sopenharmony_ci /* implicit optimization barrier here due to uninline function return */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci early_iounmap(base, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciint __olpc_ofw(const char *name, int nr_args, const void **args, int nr_res, 4862306a36Sopenharmony_ci void **res) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci int ofw_args[MAXARGS + 3]; 5162306a36Sopenharmony_ci unsigned long flags; 5262306a36Sopenharmony_ci int ret, i, *p; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci BUG_ON(nr_args + nr_res > MAXARGS); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (!olpc_ofw_cif) 5762306a36Sopenharmony_ci return -EIO; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci ofw_args[0] = (int)name; 6062306a36Sopenharmony_ci ofw_args[1] = nr_args; 6162306a36Sopenharmony_ci ofw_args[2] = nr_res; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci p = &ofw_args[3]; 6462306a36Sopenharmony_ci for (i = 0; i < nr_args; i++, p++) 6562306a36Sopenharmony_ci *p = (int)args[i]; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* call into ofw */ 6862306a36Sopenharmony_ci spin_lock_irqsave(&ofw_lock, flags); 6962306a36Sopenharmony_ci ret = olpc_ofw_cif(ofw_args); 7062306a36Sopenharmony_ci spin_unlock_irqrestore(&ofw_lock, flags); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (!ret) { 7362306a36Sopenharmony_ci for (i = 0; i < nr_res; i++, p++) 7462306a36Sopenharmony_ci *((int *)res[i]) = *p; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return ret; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__olpc_ofw); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cibool olpc_ofw_present(void) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci return olpc_ofw_cif != NULL; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(olpc_ofw_present); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* OFW cif _should_ be above this address */ 8862306a36Sopenharmony_ci#define OFW_MIN 0xff000000 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* OFW starts on a 1MB boundary */ 9162306a36Sopenharmony_ci#define OFW_BOUND (1<<20) 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_civoid __init olpc_ofw_detect(void) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct olpc_ofw_header *hdr = &boot_params.olpc_ofw_header; 9662306a36Sopenharmony_ci unsigned long start; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* ensure OFW booted us by checking for "OFW " string */ 9962306a36Sopenharmony_ci if (hdr->ofw_magic != OLPC_OFW_SIG) 10062306a36Sopenharmony_ci return; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci olpc_ofw_cif = (int (*)(int *))hdr->cif_handler; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if ((unsigned long)olpc_ofw_cif < OFW_MIN) { 10562306a36Sopenharmony_ci printk(KERN_ERR "OFW detected, but cif has invalid address 0x%lx - disabling.\n", 10662306a36Sopenharmony_ci (unsigned long)olpc_ofw_cif); 10762306a36Sopenharmony_ci olpc_ofw_cif = NULL; 10862306a36Sopenharmony_ci return; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* determine where OFW starts in memory */ 11262306a36Sopenharmony_ci start = round_down((unsigned long)olpc_ofw_cif, OFW_BOUND); 11362306a36Sopenharmony_ci printk(KERN_INFO "OFW detected in memory, cif @ 0x%lx (reserving top %ldMB)\n", 11462306a36Sopenharmony_ci (unsigned long)olpc_ofw_cif, (-start) >> 20); 11562306a36Sopenharmony_ci reserve_top_address(-start); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cibool __init olpc_ofw_is_installed(void) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci return olpc_ofw_cif != NULL; 12162306a36Sopenharmony_ci} 122