1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2004
4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5 */
6
7#include <common.h>
8#include <init.h>
9#include <asm/global_data.h>
10#include <cpu_func.h>
11#include <stdint.h>
12
13DECLARE_GLOBAL_DATA_PTR;
14
15#ifdef CONFIG_SYS_CACHELINE_SIZE
16# define MEMSIZE_CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE
17#else
18/* Just use the greatest cache flush alignment requirement I'm aware of */
19# define MEMSIZE_CACHELINE_SIZE 128
20#endif
21
22#ifdef __PPC__
23/*
24 * At least on G2 PowerPC cores, sequential accesses to non-existent
25 * memory must be synchronized.
26 */
27# include <asm/io.h>	/* for sync() */
28#else
29# define sync()		/* nothing */
30#endif
31
32static void dcache_flush_invalidate(volatile long *p)
33{
34	uintptr_t start, stop;
35	start = ALIGN_DOWN((uintptr_t)p, MEMSIZE_CACHELINE_SIZE);
36	stop = start + MEMSIZE_CACHELINE_SIZE;
37	flush_dcache_range(start, stop);
38	invalidate_dcache_range(start, stop);
39}
40
41/*
42 * Check memory range for valid RAM. A simple memory test determines
43 * the actually available RAM size between addresses `base' and
44 * `base + maxsize'.
45 */
46long get_ram_size(long *base, long maxsize)
47{
48	volatile long *addr;
49	long           save[BITS_PER_LONG - 1];
50	long           save_base;
51	long           cnt;
52	long           val;
53	long           size;
54	int            i = 0;
55	int            dcache_en = dcache_status();
56
57	for (cnt = (maxsize / sizeof(long)) >> 1; cnt > 0; cnt >>= 1) {
58		addr = base + cnt;	/* pointer arith! */
59		sync();
60		save[i++] = *addr;
61		sync();
62		*addr = ~cnt;
63		if (dcache_en)
64			dcache_flush_invalidate(addr);
65	}
66
67	addr = base;
68	sync();
69	save_base = *addr;
70	sync();
71	*addr = 0;
72
73	sync();
74	if (dcache_en)
75		dcache_flush_invalidate(addr);
76
77	if ((val = *addr) != 0) {
78		/* Restore the original data before leaving the function. */
79		sync();
80		*base = save_base;
81		for (cnt = 1; cnt < maxsize / sizeof(long); cnt <<= 1) {
82			addr  = base + cnt;
83			sync();
84			*addr = save[--i];
85		}
86		return (0);
87	}
88
89	for (cnt = 1; cnt < maxsize / sizeof(long); cnt <<= 1) {
90		addr = base + cnt;	/* pointer arith! */
91		val = *addr;
92		*addr = save[--i];
93		if (val != ~cnt) {
94			size = cnt * sizeof(long);
95			/*
96			 * Restore the original data
97			 * before leaving the function.
98			 */
99			for (cnt <<= 1;
100			     cnt < maxsize / sizeof(long);
101			     cnt <<= 1) {
102				addr  = base + cnt;
103				*addr = save[--i];
104			}
105			/* warning: don't restore save_base in this case,
106			 * it is already done in the loop because
107			 * base and base+size share the same physical memory
108			 * and *base is saved after *(base+size) modification
109			 * in first loop
110			 */
111			return (size);
112		}
113	}
114	*base = save_base;
115
116	return (maxsize);
117}
118
119phys_size_t __weak get_effective_memsize(void)
120{
121	phys_size_t ram_size = gd->ram_size;
122
123#ifdef CONFIG_MPC85xx
124	/*
125	 * Check for overflow and limit ram size to some representable value.
126	 * It is required that ram_base + ram_size must be representable by
127	 * phys_size_t type and must be aligned by direct access, therefore
128	 * calculate it from last 4kB sector which should work as alignment
129	 * on any platform.
130	 */
131	if (gd->ram_base + ram_size < gd->ram_base)
132		ram_size = ((phys_size_t)~0xfffULL) - gd->ram_base;
133#endif
134
135#ifndef CFG_MAX_MEM_MAPPED
136	return ram_size;
137#else
138	/* limit stack to what we can reasonable map */
139	return ((ram_size > CFG_MAX_MEM_MAPPED) ?
140		CFG_MAX_MEM_MAPPED : ram_size);
141#endif
142}
143