summaryrefslogtreecommitdiffstats
path: root/arch/mips/fw/arc/memory.c
blob: 37625ae5e35d53ff435d604695894ba7131def61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// SPDX-License-Identifier: GPL-2.0
/*
 * memory.c: PROM library functions for acquiring/using memory descriptors
 *	     given to us from the ARCS firmware.
 *
 * Copyright (C) 1996 by David S. Miller
 * Copyright (C) 1999, 2000, 2001 by Ralf Baechle
 * Copyright (C) 1999, 2000 by Silicon Graphics, Inc.
 *
 * PROM library functions for acquiring/using memory descriptors given to us
 * from the ARCS firmware.  This is only used when CONFIG_ARC_MEMORY is set
 * because on some machines like SGI IP27 the ARC memory configuration data
 * completely bogus and alternate easier to use mechanisms are available.
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/memblock.h>
#include <linux/swap.h>

#include <asm/sgialib.h>
#include <asm/page.h>
#include <asm/bootinfo.h>

#undef DEBUG

#define MAX_PROM_MEM 5
static phys_addr_t prom_mem_base[MAX_PROM_MEM] __initdata;
static phys_addr_t prom_mem_size[MAX_PROM_MEM] __initdata;
static unsigned int nr_prom_mem __initdata;

/*
 * For ARC firmware memory functions the unit of meassuring memory is always
 * a 4k page of memory
 */
#define ARC_PAGE_SHIFT	12

struct linux_mdesc * __init ArcGetMemoryDescriptor(struct linux_mdesc *Current)
{
	return (struct linux_mdesc *) ARC_CALL1(get_mdesc, Current);
}

#ifdef DEBUG /* convenient for debugging */
static char *arcs_mtypes[8] = {
	"Exception Block",
	"ARCS Romvec Page",
	"Free/Contig RAM",
	"Generic Free RAM",
	"Bad Memory",
	"Standalone Program Pages",
	"ARCS Temp Storage Area",
	"ARCS Permanent Storage Area"
};

static char *arc_mtypes[8] = {
	"Exception Block",
	"SystemParameterBlock",
	"FreeMemory",
	"Bad Memory",
	"LoadedProgram",
	"FirmwareTemporary",
	"FirmwarePermanent",
	"FreeContiguous"
};
#define mtypes(a) (prom_flags & PROM_FLAG_ARCS) ? arcs_mtypes[a.arcs] \
						: arc_mtypes[a.arc]
#endif

enum {
	mem_free, mem_prom_used, mem_reserved
};

static inline int memtype_classify_arcs(union linux_memtypes type)
{
	switch (type.arcs) {
	case arcs_fcontig:
	case arcs_free:
		return mem_free;
	case arcs_atmp:
		return mem_prom_used;
	case arcs_eblock:
	case arcs_rvpage:
	case arcs_bmem:
	case arcs_prog:
	case arcs_aperm:
		return mem_reserved;
	default:
		BUG();
	}
	while(1);				/* Nuke warning.  */
}

static inline int memtype_classify_arc(union linux_memtypes type)
{
	switch (type.arc) {
	case arc_free:
	case arc_fcontig:
		return mem_free;
	case arc_atmp:
		return mem_prom_used;
	case arc_eblock:
	case arc_rvpage:
	case arc_bmem:
	case arc_prog:
	case arc_aperm:
		return mem_reserved;
	default:
		BUG();
	}
	while(1);				/* Nuke warning.  */
}

static int __init prom_memtype_classify(union linux_memtypes type)
{
	if (prom_flags & PROM_FLAG_ARCS)	/* SGI is ``different'' ... */
		return memtype_classify_arcs(type);

	return memtype_classify_arc(type);
}

void __weak __init prom_meminit(void)
{
	struct linux_mdesc *p;

#ifdef DEBUG
	int i = 0;

	printk("ARCS MEMORY DESCRIPTOR dump:\n");
	p = ArcGetMemoryDescriptor(PROM_NULL_MDESC);
	while(p) {
		printk("[%d,%p]: base<%08lx> pages<%08lx> type<%s>\n",
		       i, p, p->base, p->pages, mtypes(p->type));
		p = ArcGetMemoryDescriptor(p);
		i++;
	}
#endif

	nr_prom_mem = 0;
	p = PROM_NULL_MDESC;
	while ((p = ArcGetMemoryDescriptor(p))) {
		unsigned long base, size;
		long type;

		base = p->base << ARC_PAGE_SHIFT;
		size = p->pages << ARC_PAGE_SHIFT;
		type = prom_memtype_classify(p->type);

		/* ignore mirrored RAM on IP28/IP30 */
		if (base < PHYS_OFFSET)
			continue;

		memblock_add(base, size);

		if (type == mem_reserved)
			memblock_reserve(base, size);

		if (type == mem_prom_used) {
			memblock_reserve(base, size);
			if (nr_prom_mem >= 5) {
				pr_err("Too many ROM DATA regions");
				continue;
			}
			prom_mem_base[nr_prom_mem] = base;
			prom_mem_size[nr_prom_mem] = size;
			nr_prom_mem++;
		}
	}
}

void __weak __init prom_cleanup(void)
{
}

void __weak __init prom_free_prom_memory(void)
{
	int i;

	if (prom_flags & PROM_FLAG_DONT_FREE_TEMP)
		return;

	for (i = 0; i < nr_prom_mem; i++) {
		free_init_pages("prom memory",
			prom_mem_base[i], prom_mem_base[i] + prom_mem_size[i]);
	}
	/*
	 * at this point it isn't safe to call PROM functions
	 * give platforms a way to do PROM cleanups
	 */
	prom_cleanup();
}