162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * memory.c: PROM library functions for acquiring/using memory descriptors
462306a36Sopenharmony_ci *	     given to us from the ARCS firmware.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 1996 by David S. Miller
762306a36Sopenharmony_ci * Copyright (C) 1999, 2000, 2001 by Ralf Baechle
862306a36Sopenharmony_ci * Copyright (C) 1999, 2000 by Silicon Graphics, Inc.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * PROM library functions for acquiring/using memory descriptors given to us
1162306a36Sopenharmony_ci * from the ARCS firmware.  This is only used when CONFIG_ARC_MEMORY is set
1262306a36Sopenharmony_ci * because on some machines like SGI IP27 the ARC memory configuration data
1362306a36Sopenharmony_ci * completely bogus and alternate easier to use mechanisms are available.
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/types.h>
1862306a36Sopenharmony_ci#include <linux/sched.h>
1962306a36Sopenharmony_ci#include <linux/mm.h>
2062306a36Sopenharmony_ci#include <linux/memblock.h>
2162306a36Sopenharmony_ci#include <linux/swap.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <asm/sgialib.h>
2462306a36Sopenharmony_ci#include <asm/page.h>
2562306a36Sopenharmony_ci#include <asm/bootinfo.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#undef DEBUG
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define MAX_PROM_MEM 5
3062306a36Sopenharmony_cistatic phys_addr_t prom_mem_base[MAX_PROM_MEM] __initdata;
3162306a36Sopenharmony_cistatic phys_addr_t prom_mem_size[MAX_PROM_MEM] __initdata;
3262306a36Sopenharmony_cistatic unsigned int nr_prom_mem __initdata;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/*
3562306a36Sopenharmony_ci * For ARC firmware memory functions the unit of measuring memory is always
3662306a36Sopenharmony_ci * a 4k page of memory
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_ci#define ARC_PAGE_SHIFT	12
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct linux_mdesc * __init ArcGetMemoryDescriptor(struct linux_mdesc *Current)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	return (struct linux_mdesc *) ARC_CALL1(get_mdesc, Current);
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#ifdef DEBUG /* convenient for debugging */
4662306a36Sopenharmony_cistatic char *arcs_mtypes[8] = {
4762306a36Sopenharmony_ci	"Exception Block",
4862306a36Sopenharmony_ci	"ARCS Romvec Page",
4962306a36Sopenharmony_ci	"Free/Contig RAM",
5062306a36Sopenharmony_ci	"Generic Free RAM",
5162306a36Sopenharmony_ci	"Bad Memory",
5262306a36Sopenharmony_ci	"Standalone Program Pages",
5362306a36Sopenharmony_ci	"ARCS Temp Storage Area",
5462306a36Sopenharmony_ci	"ARCS Permanent Storage Area"
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic char *arc_mtypes[8] = {
5862306a36Sopenharmony_ci	"Exception Block",
5962306a36Sopenharmony_ci	"SystemParameterBlock",
6062306a36Sopenharmony_ci	"FreeMemory",
6162306a36Sopenharmony_ci	"Bad Memory",
6262306a36Sopenharmony_ci	"LoadedProgram",
6362306a36Sopenharmony_ci	"FirmwareTemporary",
6462306a36Sopenharmony_ci	"FirmwarePermanent",
6562306a36Sopenharmony_ci	"FreeContiguous"
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci#define mtypes(a) (prom_flags & PROM_FLAG_ARCS) ? arcs_mtypes[a.arcs] \
6862306a36Sopenharmony_ci						: arc_mtypes[a.arc]
6962306a36Sopenharmony_ci#endif
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cienum {
7262306a36Sopenharmony_ci	mem_free, mem_prom_used, mem_reserved
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic inline int memtype_classify_arcs(union linux_memtypes type)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	switch (type.arcs) {
7862306a36Sopenharmony_ci	case arcs_fcontig:
7962306a36Sopenharmony_ci	case arcs_free:
8062306a36Sopenharmony_ci		return mem_free;
8162306a36Sopenharmony_ci	case arcs_atmp:
8262306a36Sopenharmony_ci		return mem_prom_used;
8362306a36Sopenharmony_ci	case arcs_eblock:
8462306a36Sopenharmony_ci	case arcs_rvpage:
8562306a36Sopenharmony_ci	case arcs_bmem:
8662306a36Sopenharmony_ci	case arcs_prog:
8762306a36Sopenharmony_ci	case arcs_aperm:
8862306a36Sopenharmony_ci		return mem_reserved;
8962306a36Sopenharmony_ci	default:
9062306a36Sopenharmony_ci		BUG();
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci	while(1);				/* Nuke warning.  */
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic inline int memtype_classify_arc(union linux_memtypes type)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	switch (type.arc) {
9862306a36Sopenharmony_ci	case arc_free:
9962306a36Sopenharmony_ci	case arc_fcontig:
10062306a36Sopenharmony_ci		return mem_free;
10162306a36Sopenharmony_ci	case arc_atmp:
10262306a36Sopenharmony_ci		return mem_prom_used;
10362306a36Sopenharmony_ci	case arc_eblock:
10462306a36Sopenharmony_ci	case arc_rvpage:
10562306a36Sopenharmony_ci	case arc_bmem:
10662306a36Sopenharmony_ci	case arc_prog:
10762306a36Sopenharmony_ci	case arc_aperm:
10862306a36Sopenharmony_ci		return mem_reserved;
10962306a36Sopenharmony_ci	default:
11062306a36Sopenharmony_ci		BUG();
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci	while(1);				/* Nuke warning.  */
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic int __init prom_memtype_classify(union linux_memtypes type)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	if (prom_flags & PROM_FLAG_ARCS)	/* SGI is ``different'' ... */
11862306a36Sopenharmony_ci		return memtype_classify_arcs(type);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return memtype_classify_arc(type);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_civoid __weak __init prom_meminit(void)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	struct linux_mdesc *p;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci#ifdef DEBUG
12862306a36Sopenharmony_ci	int i = 0;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	printk("ARCS MEMORY DESCRIPTOR dump:\n");
13162306a36Sopenharmony_ci	p = ArcGetMemoryDescriptor(PROM_NULL_MDESC);
13262306a36Sopenharmony_ci	while(p) {
13362306a36Sopenharmony_ci		printk("[%d,%p]: base<%08lx> pages<%08lx> type<%s>\n",
13462306a36Sopenharmony_ci		       i, p, p->base, p->pages, mtypes(p->type));
13562306a36Sopenharmony_ci		p = ArcGetMemoryDescriptor(p);
13662306a36Sopenharmony_ci		i++;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci#endif
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	nr_prom_mem = 0;
14162306a36Sopenharmony_ci	p = PROM_NULL_MDESC;
14262306a36Sopenharmony_ci	while ((p = ArcGetMemoryDescriptor(p))) {
14362306a36Sopenharmony_ci		unsigned long base, size;
14462306a36Sopenharmony_ci		long type;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		base = p->base << ARC_PAGE_SHIFT;
14762306a36Sopenharmony_ci		size = p->pages << ARC_PAGE_SHIFT;
14862306a36Sopenharmony_ci		type = prom_memtype_classify(p->type);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		/* ignore mirrored RAM on IP28/IP30 */
15162306a36Sopenharmony_ci		if (base < PHYS_OFFSET)
15262306a36Sopenharmony_ci			continue;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		memblock_add(base, size);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		if (type == mem_reserved)
15762306a36Sopenharmony_ci			memblock_reserve(base, size);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci		if (type == mem_prom_used) {
16062306a36Sopenharmony_ci			memblock_reserve(base, size);
16162306a36Sopenharmony_ci			if (nr_prom_mem >= 5) {
16262306a36Sopenharmony_ci				pr_err("Too many ROM DATA regions");
16362306a36Sopenharmony_ci				continue;
16462306a36Sopenharmony_ci			}
16562306a36Sopenharmony_ci			prom_mem_base[nr_prom_mem] = base;
16662306a36Sopenharmony_ci			prom_mem_size[nr_prom_mem] = size;
16762306a36Sopenharmony_ci			nr_prom_mem++;
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_civoid __weak __init prom_cleanup(void)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_civoid __init prom_free_prom_memory(void)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	int i;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (prom_flags & PROM_FLAG_DONT_FREE_TEMP)
18162306a36Sopenharmony_ci		return;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	for (i = 0; i < nr_prom_mem; i++) {
18462306a36Sopenharmony_ci		free_init_pages("prom memory",
18562306a36Sopenharmony_ci			prom_mem_base[i], prom_mem_base[i] + prom_mem_size[i]);
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci	/*
18862306a36Sopenharmony_ci	 * at this point it isn't safe to call PROM functions
18962306a36Sopenharmony_ci	 * give platforms a way to do PROM cleanups
19062306a36Sopenharmony_ci	 */
19162306a36Sopenharmony_ci	prom_cleanup();
19262306a36Sopenharmony_ci}
193