18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * memory.c: PROM library functions for acquiring/using memory descriptors
48c2ecf20Sopenharmony_ci *	     given to us from the ARCS firmware.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 1996 by David S. Miller
78c2ecf20Sopenharmony_ci * Copyright (C) 1999, 2000, 2001 by Ralf Baechle
88c2ecf20Sopenharmony_ci * Copyright (C) 1999, 2000 by Silicon Graphics, Inc.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * PROM library functions for acquiring/using memory descriptors given to us
118c2ecf20Sopenharmony_ci * from the ARCS firmware.  This is only used when CONFIG_ARC_MEMORY is set
128c2ecf20Sopenharmony_ci * because on some machines like SGI IP27 the ARC memory configuration data
138c2ecf20Sopenharmony_ci * completely bogus and alternate easier to use mechanisms are available.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/types.h>
188c2ecf20Sopenharmony_ci#include <linux/sched.h>
198c2ecf20Sopenharmony_ci#include <linux/mm.h>
208c2ecf20Sopenharmony_ci#include <linux/memblock.h>
218c2ecf20Sopenharmony_ci#include <linux/swap.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <asm/sgialib.h>
248c2ecf20Sopenharmony_ci#include <asm/page.h>
258c2ecf20Sopenharmony_ci#include <asm/bootinfo.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#undef DEBUG
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define MAX_PROM_MEM 5
308c2ecf20Sopenharmony_cistatic phys_addr_t prom_mem_base[MAX_PROM_MEM] __initdata;
318c2ecf20Sopenharmony_cistatic phys_addr_t prom_mem_size[MAX_PROM_MEM] __initdata;
328c2ecf20Sopenharmony_cistatic unsigned int nr_prom_mem __initdata;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/*
358c2ecf20Sopenharmony_ci * For ARC firmware memory functions the unit of meassuring memory is always
368c2ecf20Sopenharmony_ci * a 4k page of memory
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci#define ARC_PAGE_SHIFT	12
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct linux_mdesc * __init ArcGetMemoryDescriptor(struct linux_mdesc *Current)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	return (struct linux_mdesc *) ARC_CALL1(get_mdesc, Current);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#ifdef DEBUG /* convenient for debugging */
468c2ecf20Sopenharmony_cistatic char *arcs_mtypes[8] = {
478c2ecf20Sopenharmony_ci	"Exception Block",
488c2ecf20Sopenharmony_ci	"ARCS Romvec Page",
498c2ecf20Sopenharmony_ci	"Free/Contig RAM",
508c2ecf20Sopenharmony_ci	"Generic Free RAM",
518c2ecf20Sopenharmony_ci	"Bad Memory",
528c2ecf20Sopenharmony_ci	"Standalone Program Pages",
538c2ecf20Sopenharmony_ci	"ARCS Temp Storage Area",
548c2ecf20Sopenharmony_ci	"ARCS Permanent Storage Area"
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic char *arc_mtypes[8] = {
588c2ecf20Sopenharmony_ci	"Exception Block",
598c2ecf20Sopenharmony_ci	"SystemParameterBlock",
608c2ecf20Sopenharmony_ci	"FreeMemory",
618c2ecf20Sopenharmony_ci	"Bad Memory",
628c2ecf20Sopenharmony_ci	"LoadedProgram",
638c2ecf20Sopenharmony_ci	"FirmwareTemporary",
648c2ecf20Sopenharmony_ci	"FirmwarePermanent",
658c2ecf20Sopenharmony_ci	"FreeContiguous"
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci#define mtypes(a) (prom_flags & PROM_FLAG_ARCS) ? arcs_mtypes[a.arcs] \
688c2ecf20Sopenharmony_ci						: arc_mtypes[a.arc]
698c2ecf20Sopenharmony_ci#endif
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cienum {
728c2ecf20Sopenharmony_ci	mem_free, mem_prom_used, mem_reserved
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic inline int memtype_classify_arcs(union linux_memtypes type)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	switch (type.arcs) {
788c2ecf20Sopenharmony_ci	case arcs_fcontig:
798c2ecf20Sopenharmony_ci	case arcs_free:
808c2ecf20Sopenharmony_ci		return mem_free;
818c2ecf20Sopenharmony_ci	case arcs_atmp:
828c2ecf20Sopenharmony_ci		return mem_prom_used;
838c2ecf20Sopenharmony_ci	case arcs_eblock:
848c2ecf20Sopenharmony_ci	case arcs_rvpage:
858c2ecf20Sopenharmony_ci	case arcs_bmem:
868c2ecf20Sopenharmony_ci	case arcs_prog:
878c2ecf20Sopenharmony_ci	case arcs_aperm:
888c2ecf20Sopenharmony_ci		return mem_reserved;
898c2ecf20Sopenharmony_ci	default:
908c2ecf20Sopenharmony_ci		BUG();
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci	while(1);				/* Nuke warning.  */
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic inline int memtype_classify_arc(union linux_memtypes type)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	switch (type.arc) {
988c2ecf20Sopenharmony_ci	case arc_free:
998c2ecf20Sopenharmony_ci	case arc_fcontig:
1008c2ecf20Sopenharmony_ci		return mem_free;
1018c2ecf20Sopenharmony_ci	case arc_atmp:
1028c2ecf20Sopenharmony_ci		return mem_prom_used;
1038c2ecf20Sopenharmony_ci	case arc_eblock:
1048c2ecf20Sopenharmony_ci	case arc_rvpage:
1058c2ecf20Sopenharmony_ci	case arc_bmem:
1068c2ecf20Sopenharmony_ci	case arc_prog:
1078c2ecf20Sopenharmony_ci	case arc_aperm:
1088c2ecf20Sopenharmony_ci		return mem_reserved;
1098c2ecf20Sopenharmony_ci	default:
1108c2ecf20Sopenharmony_ci		BUG();
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci	while(1);				/* Nuke warning.  */
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int __init prom_memtype_classify(union linux_memtypes type)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	if (prom_flags & PROM_FLAG_ARCS)	/* SGI is ``different'' ... */
1188c2ecf20Sopenharmony_ci		return memtype_classify_arcs(type);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return memtype_classify_arc(type);
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_civoid __weak __init prom_meminit(void)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct linux_mdesc *p;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci#ifdef DEBUG
1288c2ecf20Sopenharmony_ci	int i = 0;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	printk("ARCS MEMORY DESCRIPTOR dump:\n");
1318c2ecf20Sopenharmony_ci	p = ArcGetMemoryDescriptor(PROM_NULL_MDESC);
1328c2ecf20Sopenharmony_ci	while(p) {
1338c2ecf20Sopenharmony_ci		printk("[%d,%p]: base<%08lx> pages<%08lx> type<%s>\n",
1348c2ecf20Sopenharmony_ci		       i, p, p->base, p->pages, mtypes(p->type));
1358c2ecf20Sopenharmony_ci		p = ArcGetMemoryDescriptor(p);
1368c2ecf20Sopenharmony_ci		i++;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci#endif
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	nr_prom_mem = 0;
1418c2ecf20Sopenharmony_ci	p = PROM_NULL_MDESC;
1428c2ecf20Sopenharmony_ci	while ((p = ArcGetMemoryDescriptor(p))) {
1438c2ecf20Sopenharmony_ci		unsigned long base, size;
1448c2ecf20Sopenharmony_ci		long type;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		base = p->base << ARC_PAGE_SHIFT;
1478c2ecf20Sopenharmony_ci		size = p->pages << ARC_PAGE_SHIFT;
1488c2ecf20Sopenharmony_ci		type = prom_memtype_classify(p->type);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		/* ignore mirrored RAM on IP28/IP30 */
1518c2ecf20Sopenharmony_ci		if (base < PHYS_OFFSET)
1528c2ecf20Sopenharmony_ci			continue;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		memblock_add(base, size);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		if (type == mem_reserved)
1578c2ecf20Sopenharmony_ci			memblock_reserve(base, size);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		if (type == mem_prom_used) {
1608c2ecf20Sopenharmony_ci			memblock_reserve(base, size);
1618c2ecf20Sopenharmony_ci			if (nr_prom_mem >= 5) {
1628c2ecf20Sopenharmony_ci				pr_err("Too many ROM DATA regions");
1638c2ecf20Sopenharmony_ci				continue;
1648c2ecf20Sopenharmony_ci			}
1658c2ecf20Sopenharmony_ci			prom_mem_base[nr_prom_mem] = base;
1668c2ecf20Sopenharmony_ci			prom_mem_size[nr_prom_mem] = size;
1678c2ecf20Sopenharmony_ci			nr_prom_mem++;
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_civoid __weak __init prom_cleanup(void)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_civoid __weak __init prom_free_prom_memory(void)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	int i;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (prom_flags & PROM_FLAG_DONT_FREE_TEMP)
1818c2ecf20Sopenharmony_ci		return;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	for (i = 0; i < nr_prom_mem; i++) {
1848c2ecf20Sopenharmony_ci		free_init_pages("prom memory",
1858c2ecf20Sopenharmony_ci			prom_mem_base[i], prom_mem_base[i] + prom_mem_size[i]);
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci	/*
1888c2ecf20Sopenharmony_ci	 * at this point it isn't safe to call PROM functions
1898c2ecf20Sopenharmony_ci	 * give platforms a way to do PROM cleanups
1908c2ecf20Sopenharmony_ci	 */
1918c2ecf20Sopenharmony_ci	prom_cleanup();
1928c2ecf20Sopenharmony_ci}
193