platform.c revision 266020
155714Skris/*- 255714Skris * Copyright (c) 2005 Peter Grehan 355714Skris * Copyright (c) 2009 Nathan Whitehorn 455714Skris * All rights reserved. 555714Skris * 655714Skris * Redistribution and use in source and binary forms, with or without 755714Skris * modification, are permitted provided that the following conditions 8280304Sjkim * are met: 955714Skris * 1. Redistributions of source code must retain the above copyright 1055714Skris * notice, this list of conditions and the following disclaimer. 1155714Skris * 2. Redistributions in binary form must reproduce the above copyright 1255714Skris * notice, this list of conditions and the following disclaimer in the 1355714Skris * documentation and/or other materials provided with the distribution. 1455714Skris * 15280304Sjkim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1655714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1755714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1855714Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1955714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2055714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2155714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22280304Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2355714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2455714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2555714Skris * SUCH DAMAGE. 2655714Skris * 2755714Skris */ 2855714Skris 2955714Skris#include <sys/cdefs.h> 3055714Skris__FBSDID("$FreeBSD: stable/10/sys/powerpc/powerpc/platform.c 266020 2014-05-14 14:17:51Z ian $"); 3155714Skris 3255714Skris/* 3355714Skris * Dispatch platform calls to the appropriate platform implementation 3455714Skris * through a previously registered kernel object. 3555714Skris */ 3655714Skris 37280304Sjkim#include <sys/param.h> 3855714Skris#include <sys/kernel.h> 3955714Skris#include <sys/lock.h> 40280304Sjkim#include <sys/ktr.h> 4155714Skris#include <sys/mutex.h> 4255714Skris#include <sys/systm.h> 4355714Skris#include <sys/smp.h> 4455714Skris#include <sys/sysctl.h> 4555714Skris#include <sys/types.h> 4655714Skris 4755714Skris#include <vm/vm.h> 4855714Skris#include <vm/vm_page.h> 4955714Skris 5055714Skris#include <machine/cpu.h> 5155714Skris#include <machine/md_var.h> 52280304Sjkim#include <machine/platform.h> 5355714Skris#include <machine/platformvar.h> 5455714Skris#include <machine/smp.h> 5555714Skris 5655714Skris#include "platform_if.h" 5755714Skris 5855714Skrisstatic platform_def_t *plat_def_impl; 5968651Skrisstatic platform_t plat_obj; 60280304Sjkimstatic struct kobj_ops plat_kernel_kops; 6168651Skrisstatic struct platform_kobj plat_kernel_obj; 6268651Skris 6368651Skrisstatic char plat_name[64] = ""; 6468651SkrisSYSCTL_STRING(_hw, OID_AUTO, platform, CTLFLAG_RD | CTLFLAG_TUN, 6572613Skris plat_name, 0, "Platform currently in use"); 6655714Skris 6755714Skrisstatic struct mem_region pregions[PHYS_AVAIL_SZ]; 6855714Skrisstatic struct mem_region aregions[PHYS_AVAIL_SZ]; 6955714Skrisstatic int npregions, naregions; 70280304Sjkim 7155714Skris/* 72160814Ssimon * Memory region utilities: determine if two regions overlap, 73160814Ssimon * and merge two overlapping regions into one 74280304Sjkim */ 75280304Sjkimstatic int 7655714Skrismemr_overlap(struct mem_region *r1, struct mem_region *r2) 7755714Skris{ 7855714Skris if ((r1->mr_start + r1->mr_size) < r2->mr_start || 7955714Skris (r2->mr_start + r2->mr_size) < r1->mr_start) 8055714Skris return (FALSE); 8155714Skris 8255714Skris return (TRUE); 8355714Skris} 84280304Sjkim 85280304Sjkimstatic void 86280304Sjkimmemr_merge(struct mem_region *from, struct mem_region *to) 87280304Sjkim{ 88280304Sjkim vm_offset_t end; 89280304Sjkim end = ulmax(to->mr_start + to->mr_size, from->mr_start + from->mr_size); 90280304Sjkim to->mr_start = ulmin(from->mr_start, to->mr_start); 91280304Sjkim to->mr_size = end - to->mr_start; 9255714Skris} 9355714Skris 94280304Sjkim/* 95280304Sjkim * Quick sort callout for comparing memory regions. 96280304Sjkim */ 97280304Sjkimstatic int 98280304Sjkimmr_cmp(const void *a, const void *b) 99280304Sjkim{ 100280304Sjkim const struct mem_region *regiona, *regionb; 101280304Sjkim 102280304Sjkim regiona = a; 103280304Sjkim regionb = b; 104280304Sjkim if (regiona->mr_start < regionb->mr_start) 105280304Sjkim return (-1); 106280304Sjkim else if (regiona->mr_start > regionb->mr_start) 107280304Sjkim return (1); 108280304Sjkim else 109280304Sjkim return (0); 110280304Sjkim} 111280304Sjkim 112280304Sjkimvoid 113280304Sjkimmem_regions(struct mem_region **phys, int *physsz, struct mem_region **avail, 114280304Sjkim int *availsz) 115280304Sjkim{ 116280304Sjkim int i, j, still_merging; 117280304Sjkim 118280304Sjkim if (npregions == 0) { 119280304Sjkim PLATFORM_MEM_REGIONS(plat_obj, &pregions[0], &npregions, 12055714Skris aregions, &naregions); 12155714Skris qsort(pregions, npregions, sizeof(*pregions), mr_cmp); 122280304Sjkim qsort(aregions, naregions, sizeof(*aregions), mr_cmp); 123280304Sjkim 124280304Sjkim /* Remove overlapping available regions */ 125280304Sjkim do { 126280304Sjkim still_merging = FALSE; 127280304Sjkim for (i = 0; i < naregions; i++) { 128280304Sjkim if (aregions[i].mr_size == 0) 129280304Sjkim continue; 130280304Sjkim for (j = i+1; j < naregions; j++) { 131280304Sjkim if (aregions[j].mr_size == 0) 132280304Sjkim continue; 133280304Sjkim if (!memr_overlap(&aregions[j], 134160814Ssimon &aregions[i])) 13555714Skris continue; 136109998Smarkm 137280304Sjkim memr_merge(&aregions[j], &aregions[i]); 138280304Sjkim /* mark inactive */ 139280304Sjkim aregions[j].mr_size = 0; 140280304Sjkim still_merging = TRUE; 14155714Skris } 142280304Sjkim } 143280304Sjkim } while (still_merging == TRUE); 14455714Skris 14555714Skris /* Collapse zero-length available regions */ 146280304Sjkim for (i = 0; i < naregions; i++) { 147280304Sjkim if (aregions[i].mr_size == 0) { 148280304Sjkim memcpy(&aregions[i], &aregions[i+1], 149280304Sjkim (naregions - i - 1)*sizeof(*aregions)); 150280304Sjkim naregions--; 151280304Sjkim } 152280304Sjkim } 153280304Sjkim } 154280304Sjkim 155280304Sjkim *phys = pregions; 156280304Sjkim *avail = aregions; 157280304Sjkim *physsz = npregions; 158280304Sjkim *availsz = naregions; 159280304Sjkim} 160280304Sjkim 161280304Sjkimint 162280304Sjkimmem_valid(vm_offset_t addr, int len) 163280304Sjkim{ 164280304Sjkim int i; 16555714Skris 16655714Skris if (npregions == 0) { 167280304Sjkim struct mem_region *p, *a; 168280304Sjkim int na, np; 169280304Sjkim mem_regions(&p, &np, &a, &na); 170280304Sjkim } 171280304Sjkim 172280304Sjkim for (i = 0; i < npregions; i++) 173280304Sjkim if ((addr >= pregions[i].mr_start) 174280304Sjkim && (addr + len <= pregions[i].mr_start + pregions[i].mr_size)) 175280304Sjkim return (0); 176280304Sjkim 177280304Sjkim return (EFAULT); 178280304Sjkim} 179280304Sjkim 18055714Skrisvm_offset_t 181280304Sjkimplatform_real_maxaddr(void) 182280304Sjkim{ 183280304Sjkim return (PLATFORM_REAL_MAXADDR(plat_obj)); 184280304Sjkim} 185280304Sjkim 186280304Sjkimconst char * 187280304Sjkiminstalled_platform() 188280304Sjkim{ 189280304Sjkim return (plat_def_impl->name); 190280304Sjkim} 191280304Sjkim 192280304Sjkimu_long 193280304Sjkimplatform_timebase_freq(struct cpuref *cpu) 194280304Sjkim{ 195280304Sjkim return (PLATFORM_TIMEBASE_FREQ(plat_obj, cpu)); 19655714Skris} 197280304Sjkim 19855714Skris/* 199280304Sjkim * Put the current CPU, as last step in suspend, to sleep 200280304Sjkim */ 201280304Sjkimvoid 202280304Sjkimplatform_sleep() 203280304Sjkim{ 204280304Sjkim PLATFORM_SLEEP(plat_obj); 20555714Skris} 206280304Sjkim 207238405Sjkimint 208280304Sjkimplatform_smp_first_cpu(struct cpuref *cpu) 209280304Sjkim{ 210280304Sjkim return (PLATFORM_SMP_FIRST_CPU(plat_obj, cpu)); 21155714Skris} 212280304Sjkim 213280304Sjkimint 214280304Sjkimplatform_smp_next_cpu(struct cpuref *cpu) 215280304Sjkim{ 21655714Skris return (PLATFORM_SMP_NEXT_CPU(plat_obj, cpu)); 21755714Skris} 218280304Sjkim 219280304Sjkimint 220280304Sjkimplatform_smp_get_bsp(struct cpuref *cpu) 22155714Skris{ 222280304Sjkim return (PLATFORM_SMP_GET_BSP(plat_obj, cpu)); 223280304Sjkim} 224280304Sjkim 225280304Sjkimint 22655714Skrisplatform_smp_start_cpu(struct pcpu *cpu) 22755714Skris{ 228280304Sjkim return (PLATFORM_SMP_START_CPU(plat_obj, cpu)); 229280304Sjkim} 23055714Skris 231280304Sjkimvoid 232280304Sjkimplatform_smp_ap_init() 233280304Sjkim{ 234280304Sjkim PLATFORM_SMP_AP_INIT(plat_obj); 235280304Sjkim} 236280304Sjkim 237280304Sjkim#ifdef SMP 238280304Sjkimstruct cpu_group * 239280304Sjkimcpu_topo(void) 240280304Sjkim{ 241280304Sjkim return (PLATFORM_SMP_TOPO(plat_obj)); 242280304Sjkim} 243280304Sjkim#endif 24455714Skris 24555714Skris/* 246280304Sjkim * Reset back to firmware. 247280304Sjkim */ 248280304Sjkimvoid 249280304Sjkimcpu_reset() 250280304Sjkim{ 251280304Sjkim PLATFORM_RESET(plat_obj); 252280304Sjkim} 253280304Sjkim 254280304Sjkim/* 255160814Ssimon * Platform install routines. Highest priority wins, using the same 256280304Sjkim * algorithm as bus attachment. 257160814Ssimon */ 258280304SjkimSET_DECLARE(platform_set, platform_def_t); 259280304Sjkim 260280304Sjkimvoid 26155714Skrisplatform_probe_and_attach() 26255714Skris{ 263280304Sjkim platform_def_t **platpp, *platp; 264280304Sjkim int prio, best_prio; 265280304Sjkim 266280304Sjkim plat_obj = &plat_kernel_obj; 26755714Skris best_prio = 0; 26855714Skris 269280304Sjkim /* 270280304Sjkim * Try to locate the best platform kobj 27155714Skris */ 272280304Sjkim SET_FOREACH(platpp, platform_set) { 273280304Sjkim platp = *platpp; 274280304Sjkim 275280304Sjkim /* 276280304Sjkim * Take care of compiling the selected class, and 277280304Sjkim * then statically initialise the MMU object 278280304Sjkim */ 279280304Sjkim kobj_class_compile_static(platp, &plat_kernel_kops); 280280304Sjkim kobj_init_static((kobj_t)plat_obj, platp); 281280304Sjkim 282280304Sjkim prio = PLATFORM_PROBE(plat_obj); 283280304Sjkim 28455714Skris /* Check for errors */ 285109998Smarkm if (prio > 0) 286109998Smarkm continue; 287109998Smarkm 288280304Sjkim /* 289280304Sjkim * Check if this module was specifically requested through 290280304Sjkim * the loader tunable we provide. 291280304Sjkim */ 29255714Skris if (strcmp(platp->name,plat_name) == 0) { 293280304Sjkim plat_def_impl = platp; 294160814Ssimon break; 295280304Sjkim } 296280304Sjkim 297280304Sjkim /* Otherwise, see if it is better than our current best */ 298280304Sjkim if (plat_def_impl == NULL || prio > best_prio) { 299280304Sjkim best_prio = prio; 300280304Sjkim plat_def_impl = platp; 301280304Sjkim } 302280304Sjkim 303280304Sjkim /* 304280304Sjkim * We can't free the KOBJ, since it is static. Reset the ops 305280304Sjkim * member of this class so that we can come back later. 306280304Sjkim */ 307280304Sjkim platp->ops = NULL; 308269686Sjkim } 309280304Sjkim 310280304Sjkim if (plat_def_impl == NULL) 311280304Sjkim panic("No platform module found!"); 312280304Sjkim 313280304Sjkim /* 314280304Sjkim * Recompile to make sure we ended with the 315280304Sjkim * correct one, and then attach. 316269686Sjkim */ 317269686Sjkim 318109998Smarkm kobj_class_compile_static(plat_def_impl, &plat_kernel_kops); 319280304Sjkim kobj_init_static((kobj_t)plat_obj, plat_def_impl); 320280304Sjkim 321280304Sjkim strlcpy(plat_name,plat_def_impl->name,sizeof(plat_name)); 322280304Sjkim 323280304Sjkim PLATFORM_ATTACH(plat_obj); 324280304Sjkim} 325280304Sjkim 326280304Sjkim