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