1*4882a593Smuzhiyun /***********************license start***************
2*4882a593Smuzhiyun * Author: Cavium Networks
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Contact: support@caviumnetworks.com
5*4882a593Smuzhiyun * This file is part of the OCTEON SDK
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Copyright (c) 2003-2008 Cavium Networks
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * This file is free software; you can redistribute it and/or modify
10*4882a593Smuzhiyun * it under the terms of the GNU General Public License, Version 2, as
11*4882a593Smuzhiyun * published by the Free Software Foundation.
12*4882a593Smuzhiyun *
13*4882a593Smuzhiyun * This file is distributed in the hope that it will be useful, but
14*4882a593Smuzhiyun * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
15*4882a593Smuzhiyun * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
16*4882a593Smuzhiyun * NONINFRINGEMENT. See the GNU General Public License for more
17*4882a593Smuzhiyun * details.
18*4882a593Smuzhiyun *
19*4882a593Smuzhiyun * You should have received a copy of the GNU General Public License
20*4882a593Smuzhiyun * along with this file; if not, write to the Free Software
21*4882a593Smuzhiyun * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22*4882a593Smuzhiyun * or visit http://www.gnu.org/licenses/.
23*4882a593Smuzhiyun *
24*4882a593Smuzhiyun * This file may also be available under a different license from Cavium.
25*4882a593Smuzhiyun * Contact Cavium Networks for more information
26*4882a593Smuzhiyun ***********************license end**************************************/
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun /*
29*4882a593Smuzhiyun * Simple allocate only memory allocator. Used to allocate memory at
30*4882a593Smuzhiyun * application start time.
31*4882a593Smuzhiyun */
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun #include <linux/export.h>
34*4882a593Smuzhiyun #include <linux/kernel.h>
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #include <asm/octeon/cvmx.h>
37*4882a593Smuzhiyun #include <asm/octeon/cvmx-spinlock.h>
38*4882a593Smuzhiyun #include <asm/octeon/cvmx-bootmem.h>
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun /*#define DEBUG */
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun static struct cvmx_bootmem_desc *cvmx_bootmem_desc;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun /* See header file for descriptions of functions */
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun /**
48*4882a593Smuzhiyun * This macro returns a member of the
49*4882a593Smuzhiyun * cvmx_bootmem_named_block_desc_t structure. These members can't
50*4882a593Smuzhiyun * be directly addressed as they might be in memory not directly
51*4882a593Smuzhiyun * reachable. In the case where bootmem is compiled with
52*4882a593Smuzhiyun * LINUX_HOST, the structure itself might be located on a remote
53*4882a593Smuzhiyun * Octeon. The argument "field" is the member name of the
54*4882a593Smuzhiyun * cvmx_bootmem_named_block_desc_t to read. Regardless of the type
55*4882a593Smuzhiyun * of the field, the return type is always a uint64_t. The "addr"
56*4882a593Smuzhiyun * parameter is the physical address of the structure.
57*4882a593Smuzhiyun */
58*4882a593Smuzhiyun #define CVMX_BOOTMEM_NAMED_GET_FIELD(addr, field) \
59*4882a593Smuzhiyun __cvmx_bootmem_desc_get(addr, \
60*4882a593Smuzhiyun offsetof(struct cvmx_bootmem_named_block_desc, field), \
61*4882a593Smuzhiyun sizeof_field(struct cvmx_bootmem_named_block_desc, field))
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun /**
64*4882a593Smuzhiyun * This function is the implementation of the get macros defined
65*4882a593Smuzhiyun * for individual structure members. The argument are generated
66*4882a593Smuzhiyun * by the macros inorder to read only the needed memory.
67*4882a593Smuzhiyun *
68*4882a593Smuzhiyun * @param base 64bit physical address of the complete structure
69*4882a593Smuzhiyun * @param offset Offset from the beginning of the structure to the member being
70*4882a593Smuzhiyun * accessed.
71*4882a593Smuzhiyun * @param size Size of the structure member.
72*4882a593Smuzhiyun *
73*4882a593Smuzhiyun * @return Value of the structure member promoted into a uint64_t.
74*4882a593Smuzhiyun */
__cvmx_bootmem_desc_get(uint64_t base,int offset,int size)75*4882a593Smuzhiyun static inline uint64_t __cvmx_bootmem_desc_get(uint64_t base, int offset,
76*4882a593Smuzhiyun int size)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun base = (1ull << 63) | (base + offset);
79*4882a593Smuzhiyun switch (size) {
80*4882a593Smuzhiyun case 4:
81*4882a593Smuzhiyun return cvmx_read64_uint32(base);
82*4882a593Smuzhiyun case 8:
83*4882a593Smuzhiyun return cvmx_read64_uint64(base);
84*4882a593Smuzhiyun default:
85*4882a593Smuzhiyun return 0;
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun /*
90*4882a593Smuzhiyun * Wrapper functions are provided for reading/writing the size and
91*4882a593Smuzhiyun * next block values as these may not be directly addressible (in 32
92*4882a593Smuzhiyun * bit applications, for instance.) Offsets of data elements in
93*4882a593Smuzhiyun * bootmem list, must match cvmx_bootmem_block_header_t.
94*4882a593Smuzhiyun */
95*4882a593Smuzhiyun #define NEXT_OFFSET 0
96*4882a593Smuzhiyun #define SIZE_OFFSET 8
97*4882a593Smuzhiyun
cvmx_bootmem_phy_set_size(uint64_t addr,uint64_t size)98*4882a593Smuzhiyun static void cvmx_bootmem_phy_set_size(uint64_t addr, uint64_t size)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun cvmx_write64_uint64((addr + SIZE_OFFSET) | (1ull << 63), size);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
cvmx_bootmem_phy_set_next(uint64_t addr,uint64_t next)103*4882a593Smuzhiyun static void cvmx_bootmem_phy_set_next(uint64_t addr, uint64_t next)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun cvmx_write64_uint64((addr + NEXT_OFFSET) | (1ull << 63), next);
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
cvmx_bootmem_phy_get_size(uint64_t addr)108*4882a593Smuzhiyun static uint64_t cvmx_bootmem_phy_get_size(uint64_t addr)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun return cvmx_read64_uint64((addr + SIZE_OFFSET) | (1ull << 63));
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun
cvmx_bootmem_phy_get_next(uint64_t addr)113*4882a593Smuzhiyun static uint64_t cvmx_bootmem_phy_get_next(uint64_t addr)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun return cvmx_read64_uint64((addr + NEXT_OFFSET) | (1ull << 63));
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun /**
119*4882a593Smuzhiyun * Allocate a block of memory from the free list that was
120*4882a593Smuzhiyun * passed to the application by the bootloader within a specified
121*4882a593Smuzhiyun * address range. This is an allocate-only algorithm, so
122*4882a593Smuzhiyun * freeing memory is not possible. Allocation will fail if
123*4882a593Smuzhiyun * memory cannot be allocated in the requested range.
124*4882a593Smuzhiyun *
125*4882a593Smuzhiyun * @size: Size in bytes of block to allocate
126*4882a593Smuzhiyun * @min_addr: defines the minimum address of the range
127*4882a593Smuzhiyun * @max_addr: defines the maximum address of the range
128*4882a593Smuzhiyun * @alignment: Alignment required - must be power of 2
129*4882a593Smuzhiyun * Returns pointer to block of memory, NULL on error
130*4882a593Smuzhiyun */
cvmx_bootmem_alloc_range(uint64_t size,uint64_t alignment,uint64_t min_addr,uint64_t max_addr)131*4882a593Smuzhiyun static void *cvmx_bootmem_alloc_range(uint64_t size, uint64_t alignment,
132*4882a593Smuzhiyun uint64_t min_addr, uint64_t max_addr)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun int64_t address;
135*4882a593Smuzhiyun address =
136*4882a593Smuzhiyun cvmx_bootmem_phy_alloc(size, min_addr, max_addr, alignment, 0);
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun if (address > 0)
139*4882a593Smuzhiyun return cvmx_phys_to_ptr(address);
140*4882a593Smuzhiyun else
141*4882a593Smuzhiyun return NULL;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun
cvmx_bootmem_alloc_address(uint64_t size,uint64_t address,uint64_t alignment)144*4882a593Smuzhiyun void *cvmx_bootmem_alloc_address(uint64_t size, uint64_t address,
145*4882a593Smuzhiyun uint64_t alignment)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun return cvmx_bootmem_alloc_range(size, alignment, address,
148*4882a593Smuzhiyun address + size);
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
cvmx_bootmem_alloc_named_range(uint64_t size,uint64_t min_addr,uint64_t max_addr,uint64_t align,char * name)151*4882a593Smuzhiyun void *cvmx_bootmem_alloc_named_range(uint64_t size, uint64_t min_addr,
152*4882a593Smuzhiyun uint64_t max_addr, uint64_t align,
153*4882a593Smuzhiyun char *name)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun int64_t addr;
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun addr = cvmx_bootmem_phy_named_block_alloc(size, min_addr, max_addr,
158*4882a593Smuzhiyun align, name, 0);
159*4882a593Smuzhiyun if (addr >= 0)
160*4882a593Smuzhiyun return cvmx_phys_to_ptr(addr);
161*4882a593Smuzhiyun else
162*4882a593Smuzhiyun return NULL;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
cvmx_bootmem_alloc_named(uint64_t size,uint64_t alignment,char * name)165*4882a593Smuzhiyun void *cvmx_bootmem_alloc_named(uint64_t size, uint64_t alignment, char *name)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun return cvmx_bootmem_alloc_named_range(size, 0, 0, alignment, name);
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun EXPORT_SYMBOL(cvmx_bootmem_alloc_named);
170*4882a593Smuzhiyun
cvmx_bootmem_lock(void)171*4882a593Smuzhiyun void cvmx_bootmem_lock(void)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun cvmx_spinlock_lock((cvmx_spinlock_t *) &(cvmx_bootmem_desc->lock));
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun
cvmx_bootmem_unlock(void)176*4882a593Smuzhiyun void cvmx_bootmem_unlock(void)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun cvmx_spinlock_unlock((cvmx_spinlock_t *) &(cvmx_bootmem_desc->lock));
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
cvmx_bootmem_init(void * mem_desc_ptr)181*4882a593Smuzhiyun int cvmx_bootmem_init(void *mem_desc_ptr)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun /* Here we set the global pointer to the bootmem descriptor
184*4882a593Smuzhiyun * block. This pointer will be used directly, so we will set
185*4882a593Smuzhiyun * it up to be directly usable by the application. It is set
186*4882a593Smuzhiyun * up as follows for the various runtime/ABI combinations:
187*4882a593Smuzhiyun *
188*4882a593Smuzhiyun * Linux 64 bit: Set XKPHYS bit
189*4882a593Smuzhiyun * Linux 32 bit: use mmap to create mapping, use virtual address
190*4882a593Smuzhiyun * CVMX 64 bit: use physical address directly
191*4882a593Smuzhiyun * CVMX 32 bit: use physical address directly
192*4882a593Smuzhiyun *
193*4882a593Smuzhiyun * Note that the CVMX environment assumes the use of 1-1 TLB
194*4882a593Smuzhiyun * mappings so that the physical addresses can be used
195*4882a593Smuzhiyun * directly
196*4882a593Smuzhiyun */
197*4882a593Smuzhiyun if (!cvmx_bootmem_desc) {
198*4882a593Smuzhiyun #if defined(CVMX_ABI_64)
199*4882a593Smuzhiyun /* Set XKPHYS bit */
200*4882a593Smuzhiyun cvmx_bootmem_desc = cvmx_phys_to_ptr(CAST64(mem_desc_ptr));
201*4882a593Smuzhiyun #else
202*4882a593Smuzhiyun cvmx_bootmem_desc = (struct cvmx_bootmem_desc *) mem_desc_ptr;
203*4882a593Smuzhiyun #endif
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun return 0;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun /*
210*4882a593Smuzhiyun * The cvmx_bootmem_phy* functions below return 64 bit physical
211*4882a593Smuzhiyun * addresses, and expose more features that the cvmx_bootmem_functions
212*4882a593Smuzhiyun * above. These are required for full memory space access in 32 bit
213*4882a593Smuzhiyun * applications, as well as for using some advance features. Most
214*4882a593Smuzhiyun * applications should not need to use these.
215*4882a593Smuzhiyun */
216*4882a593Smuzhiyun
cvmx_bootmem_phy_alloc(uint64_t req_size,uint64_t address_min,uint64_t address_max,uint64_t alignment,uint32_t flags)217*4882a593Smuzhiyun int64_t cvmx_bootmem_phy_alloc(uint64_t req_size, uint64_t address_min,
218*4882a593Smuzhiyun uint64_t address_max, uint64_t alignment,
219*4882a593Smuzhiyun uint32_t flags)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun uint64_t head_addr;
223*4882a593Smuzhiyun uint64_t ent_addr;
224*4882a593Smuzhiyun /* points to previous list entry, NULL current entry is head of list */
225*4882a593Smuzhiyun uint64_t prev_addr = 0;
226*4882a593Smuzhiyun uint64_t new_ent_addr = 0;
227*4882a593Smuzhiyun uint64_t desired_min_addr;
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun #ifdef DEBUG
230*4882a593Smuzhiyun cvmx_dprintf("cvmx_bootmem_phy_alloc: req_size: 0x%llx, "
231*4882a593Smuzhiyun "min_addr: 0x%llx, max_addr: 0x%llx, align: 0x%llx\n",
232*4882a593Smuzhiyun (unsigned long long)req_size,
233*4882a593Smuzhiyun (unsigned long long)address_min,
234*4882a593Smuzhiyun (unsigned long long)address_max,
235*4882a593Smuzhiyun (unsigned long long)alignment);
236*4882a593Smuzhiyun #endif
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun if (cvmx_bootmem_desc->major_version > 3) {
239*4882a593Smuzhiyun cvmx_dprintf("ERROR: Incompatible bootmem descriptor "
240*4882a593Smuzhiyun "version: %d.%d at addr: %p\n",
241*4882a593Smuzhiyun (int)cvmx_bootmem_desc->major_version,
242*4882a593Smuzhiyun (int)cvmx_bootmem_desc->minor_version,
243*4882a593Smuzhiyun cvmx_bootmem_desc);
244*4882a593Smuzhiyun goto error_out;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun /*
248*4882a593Smuzhiyun * Do a variety of checks to validate the arguments. The
249*4882a593Smuzhiyun * allocator code will later assume that these checks have
250*4882a593Smuzhiyun * been made. We validate that the requested constraints are
251*4882a593Smuzhiyun * not self-contradictory before we look through the list of
252*4882a593Smuzhiyun * available memory.
253*4882a593Smuzhiyun */
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun /* 0 is not a valid req_size for this allocator */
256*4882a593Smuzhiyun if (!req_size)
257*4882a593Smuzhiyun goto error_out;
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun /* Round req_size up to mult of minimum alignment bytes */
260*4882a593Smuzhiyun req_size = (req_size + (CVMX_BOOTMEM_ALIGNMENT_SIZE - 1)) &
261*4882a593Smuzhiyun ~(CVMX_BOOTMEM_ALIGNMENT_SIZE - 1);
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun /*
264*4882a593Smuzhiyun * Convert !0 address_min and 0 address_max to special case of
265*4882a593Smuzhiyun * range that specifies an exact memory block to allocate. Do
266*4882a593Smuzhiyun * this before other checks and adjustments so that this
267*4882a593Smuzhiyun * tranformation will be validated.
268*4882a593Smuzhiyun */
269*4882a593Smuzhiyun if (address_min && !address_max)
270*4882a593Smuzhiyun address_max = address_min + req_size;
271*4882a593Smuzhiyun else if (!address_min && !address_max)
272*4882a593Smuzhiyun address_max = ~0ull; /* If no limits given, use max limits */
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun /*
276*4882a593Smuzhiyun * Enforce minimum alignment (this also keeps the minimum free block
277*4882a593Smuzhiyun * req_size the same as the alignment req_size.
278*4882a593Smuzhiyun */
279*4882a593Smuzhiyun if (alignment < CVMX_BOOTMEM_ALIGNMENT_SIZE)
280*4882a593Smuzhiyun alignment = CVMX_BOOTMEM_ALIGNMENT_SIZE;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun /*
283*4882a593Smuzhiyun * Adjust address minimum based on requested alignment (round
284*4882a593Smuzhiyun * up to meet alignment). Do this here so we can reject
285*4882a593Smuzhiyun * impossible requests up front. (NOP for address_min == 0)
286*4882a593Smuzhiyun */
287*4882a593Smuzhiyun if (alignment)
288*4882a593Smuzhiyun address_min = ALIGN(address_min, alignment);
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun /*
291*4882a593Smuzhiyun * Reject inconsistent args. We have adjusted these, so this
292*4882a593Smuzhiyun * may fail due to our internal changes even if this check
293*4882a593Smuzhiyun * would pass for the values the user supplied.
294*4882a593Smuzhiyun */
295*4882a593Smuzhiyun if (req_size > address_max - address_min)
296*4882a593Smuzhiyun goto error_out;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun /* Walk through the list entries - first fit found is returned */
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
301*4882a593Smuzhiyun cvmx_bootmem_lock();
302*4882a593Smuzhiyun head_addr = cvmx_bootmem_desc->head_addr;
303*4882a593Smuzhiyun ent_addr = head_addr;
304*4882a593Smuzhiyun for (; ent_addr;
305*4882a593Smuzhiyun prev_addr = ent_addr,
306*4882a593Smuzhiyun ent_addr = cvmx_bootmem_phy_get_next(ent_addr)) {
307*4882a593Smuzhiyun uint64_t usable_base, usable_max;
308*4882a593Smuzhiyun uint64_t ent_size = cvmx_bootmem_phy_get_size(ent_addr);
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun if (cvmx_bootmem_phy_get_next(ent_addr)
311*4882a593Smuzhiyun && ent_addr > cvmx_bootmem_phy_get_next(ent_addr)) {
312*4882a593Smuzhiyun cvmx_dprintf("Internal bootmem_alloc() error: ent: "
313*4882a593Smuzhiyun "0x%llx, next: 0x%llx\n",
314*4882a593Smuzhiyun (unsigned long long)ent_addr,
315*4882a593Smuzhiyun (unsigned long long)
316*4882a593Smuzhiyun cvmx_bootmem_phy_get_next(ent_addr));
317*4882a593Smuzhiyun goto error_out;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun /*
321*4882a593Smuzhiyun * Determine if this is an entry that can satisify the
322*4882a593Smuzhiyun * request Check to make sure entry is large enough to
323*4882a593Smuzhiyun * satisfy request.
324*4882a593Smuzhiyun */
325*4882a593Smuzhiyun usable_base =
326*4882a593Smuzhiyun ALIGN(max(address_min, ent_addr), alignment);
327*4882a593Smuzhiyun usable_max = min(address_max, ent_addr + ent_size);
328*4882a593Smuzhiyun /*
329*4882a593Smuzhiyun * We should be able to allocate block at address
330*4882a593Smuzhiyun * usable_base.
331*4882a593Smuzhiyun */
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun desired_min_addr = usable_base;
334*4882a593Smuzhiyun /*
335*4882a593Smuzhiyun * Determine if request can be satisfied from the
336*4882a593Smuzhiyun * current entry.
337*4882a593Smuzhiyun */
338*4882a593Smuzhiyun if (!((ent_addr + ent_size) > usable_base
339*4882a593Smuzhiyun && ent_addr < address_max
340*4882a593Smuzhiyun && req_size <= usable_max - usable_base))
341*4882a593Smuzhiyun continue;
342*4882a593Smuzhiyun /*
343*4882a593Smuzhiyun * We have found an entry that has room to satisfy the
344*4882a593Smuzhiyun * request, so allocate it from this entry. If end
345*4882a593Smuzhiyun * CVMX_BOOTMEM_FLAG_END_ALLOC set, then allocate from
346*4882a593Smuzhiyun * the end of this block rather than the beginning.
347*4882a593Smuzhiyun */
348*4882a593Smuzhiyun if (flags & CVMX_BOOTMEM_FLAG_END_ALLOC) {
349*4882a593Smuzhiyun desired_min_addr = usable_max - req_size;
350*4882a593Smuzhiyun /*
351*4882a593Smuzhiyun * Align desired address down to required
352*4882a593Smuzhiyun * alignment.
353*4882a593Smuzhiyun */
354*4882a593Smuzhiyun desired_min_addr &= ~(alignment - 1);
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun /* Match at start of entry */
358*4882a593Smuzhiyun if (desired_min_addr == ent_addr) {
359*4882a593Smuzhiyun if (req_size < ent_size) {
360*4882a593Smuzhiyun /*
361*4882a593Smuzhiyun * big enough to create a new block
362*4882a593Smuzhiyun * from top portion of block.
363*4882a593Smuzhiyun */
364*4882a593Smuzhiyun new_ent_addr = ent_addr + req_size;
365*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(new_ent_addr,
366*4882a593Smuzhiyun cvmx_bootmem_phy_get_next(ent_addr));
367*4882a593Smuzhiyun cvmx_bootmem_phy_set_size(new_ent_addr,
368*4882a593Smuzhiyun ent_size -
369*4882a593Smuzhiyun req_size);
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun /*
372*4882a593Smuzhiyun * Adjust next pointer as following
373*4882a593Smuzhiyun * code uses this.
374*4882a593Smuzhiyun */
375*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(ent_addr,
376*4882a593Smuzhiyun new_ent_addr);
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun /*
380*4882a593Smuzhiyun * adjust prev ptr or head to remove this
381*4882a593Smuzhiyun * entry from list.
382*4882a593Smuzhiyun */
383*4882a593Smuzhiyun if (prev_addr)
384*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(prev_addr,
385*4882a593Smuzhiyun cvmx_bootmem_phy_get_next(ent_addr));
386*4882a593Smuzhiyun else
387*4882a593Smuzhiyun /*
388*4882a593Smuzhiyun * head of list being returned, so
389*4882a593Smuzhiyun * update head ptr.
390*4882a593Smuzhiyun */
391*4882a593Smuzhiyun cvmx_bootmem_desc->head_addr =
392*4882a593Smuzhiyun cvmx_bootmem_phy_get_next(ent_addr);
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
395*4882a593Smuzhiyun cvmx_bootmem_unlock();
396*4882a593Smuzhiyun return desired_min_addr;
397*4882a593Smuzhiyun }
398*4882a593Smuzhiyun /*
399*4882a593Smuzhiyun * block returned doesn't start at beginning of entry,
400*4882a593Smuzhiyun * so we know that we will be splitting a block off
401*4882a593Smuzhiyun * the front of this one. Create a new block from the
402*4882a593Smuzhiyun * beginning, add to list, and go to top of loop
403*4882a593Smuzhiyun * again.
404*4882a593Smuzhiyun *
405*4882a593Smuzhiyun * create new block from high portion of
406*4882a593Smuzhiyun * block, so that top block starts at desired
407*4882a593Smuzhiyun * addr.
408*4882a593Smuzhiyun */
409*4882a593Smuzhiyun new_ent_addr = desired_min_addr;
410*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(new_ent_addr,
411*4882a593Smuzhiyun cvmx_bootmem_phy_get_next
412*4882a593Smuzhiyun (ent_addr));
413*4882a593Smuzhiyun cvmx_bootmem_phy_set_size(new_ent_addr,
414*4882a593Smuzhiyun cvmx_bootmem_phy_get_size
415*4882a593Smuzhiyun (ent_addr) -
416*4882a593Smuzhiyun (desired_min_addr -
417*4882a593Smuzhiyun ent_addr));
418*4882a593Smuzhiyun cvmx_bootmem_phy_set_size(ent_addr,
419*4882a593Smuzhiyun desired_min_addr - ent_addr);
420*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(ent_addr, new_ent_addr);
421*4882a593Smuzhiyun /* Loop again to handle actual alloc from new block */
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun error_out:
424*4882a593Smuzhiyun /* We didn't find anything, so return error */
425*4882a593Smuzhiyun if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
426*4882a593Smuzhiyun cvmx_bootmem_unlock();
427*4882a593Smuzhiyun return -1;
428*4882a593Smuzhiyun }
429*4882a593Smuzhiyun
__cvmx_bootmem_phy_free(uint64_t phy_addr,uint64_t size,uint32_t flags)430*4882a593Smuzhiyun int __cvmx_bootmem_phy_free(uint64_t phy_addr, uint64_t size, uint32_t flags)
431*4882a593Smuzhiyun {
432*4882a593Smuzhiyun uint64_t cur_addr;
433*4882a593Smuzhiyun uint64_t prev_addr = 0; /* zero is invalid */
434*4882a593Smuzhiyun int retval = 0;
435*4882a593Smuzhiyun
436*4882a593Smuzhiyun #ifdef DEBUG
437*4882a593Smuzhiyun cvmx_dprintf("__cvmx_bootmem_phy_free addr: 0x%llx, size: 0x%llx\n",
438*4882a593Smuzhiyun (unsigned long long)phy_addr, (unsigned long long)size);
439*4882a593Smuzhiyun #endif
440*4882a593Smuzhiyun if (cvmx_bootmem_desc->major_version > 3) {
441*4882a593Smuzhiyun cvmx_dprintf("ERROR: Incompatible bootmem descriptor "
442*4882a593Smuzhiyun "version: %d.%d at addr: %p\n",
443*4882a593Smuzhiyun (int)cvmx_bootmem_desc->major_version,
444*4882a593Smuzhiyun (int)cvmx_bootmem_desc->minor_version,
445*4882a593Smuzhiyun cvmx_bootmem_desc);
446*4882a593Smuzhiyun return 0;
447*4882a593Smuzhiyun }
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun /* 0 is not a valid size for this allocator */
450*4882a593Smuzhiyun if (!size)
451*4882a593Smuzhiyun return 0;
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
454*4882a593Smuzhiyun cvmx_bootmem_lock();
455*4882a593Smuzhiyun cur_addr = cvmx_bootmem_desc->head_addr;
456*4882a593Smuzhiyun if (cur_addr == 0 || phy_addr < cur_addr) {
457*4882a593Smuzhiyun /* add at front of list - special case with changing head ptr */
458*4882a593Smuzhiyun if (cur_addr && phy_addr + size > cur_addr)
459*4882a593Smuzhiyun goto bootmem_free_done; /* error, overlapping section */
460*4882a593Smuzhiyun else if (phy_addr + size == cur_addr) {
461*4882a593Smuzhiyun /* Add to front of existing first block */
462*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(phy_addr,
463*4882a593Smuzhiyun cvmx_bootmem_phy_get_next
464*4882a593Smuzhiyun (cur_addr));
465*4882a593Smuzhiyun cvmx_bootmem_phy_set_size(phy_addr,
466*4882a593Smuzhiyun cvmx_bootmem_phy_get_size
467*4882a593Smuzhiyun (cur_addr) + size);
468*4882a593Smuzhiyun cvmx_bootmem_desc->head_addr = phy_addr;
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun } else {
471*4882a593Smuzhiyun /* New block before first block. OK if cur_addr is 0 */
472*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(phy_addr, cur_addr);
473*4882a593Smuzhiyun cvmx_bootmem_phy_set_size(phy_addr, size);
474*4882a593Smuzhiyun cvmx_bootmem_desc->head_addr = phy_addr;
475*4882a593Smuzhiyun }
476*4882a593Smuzhiyun retval = 1;
477*4882a593Smuzhiyun goto bootmem_free_done;
478*4882a593Smuzhiyun }
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun /* Find place in list to add block */
481*4882a593Smuzhiyun while (cur_addr && phy_addr > cur_addr) {
482*4882a593Smuzhiyun prev_addr = cur_addr;
483*4882a593Smuzhiyun cur_addr = cvmx_bootmem_phy_get_next(cur_addr);
484*4882a593Smuzhiyun }
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun if (!cur_addr) {
487*4882a593Smuzhiyun /*
488*4882a593Smuzhiyun * We have reached the end of the list, add on to end,
489*4882a593Smuzhiyun * checking to see if we need to combine with last
490*4882a593Smuzhiyun * block
491*4882a593Smuzhiyun */
492*4882a593Smuzhiyun if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) ==
493*4882a593Smuzhiyun phy_addr) {
494*4882a593Smuzhiyun cvmx_bootmem_phy_set_size(prev_addr,
495*4882a593Smuzhiyun cvmx_bootmem_phy_get_size
496*4882a593Smuzhiyun (prev_addr) + size);
497*4882a593Smuzhiyun } else {
498*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
499*4882a593Smuzhiyun cvmx_bootmem_phy_set_size(phy_addr, size);
500*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(phy_addr, 0);
501*4882a593Smuzhiyun }
502*4882a593Smuzhiyun retval = 1;
503*4882a593Smuzhiyun goto bootmem_free_done;
504*4882a593Smuzhiyun } else {
505*4882a593Smuzhiyun /*
506*4882a593Smuzhiyun * insert between prev and cur nodes, checking for
507*4882a593Smuzhiyun * merge with either/both.
508*4882a593Smuzhiyun */
509*4882a593Smuzhiyun if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) ==
510*4882a593Smuzhiyun phy_addr) {
511*4882a593Smuzhiyun /* Merge with previous */
512*4882a593Smuzhiyun cvmx_bootmem_phy_set_size(prev_addr,
513*4882a593Smuzhiyun cvmx_bootmem_phy_get_size
514*4882a593Smuzhiyun (prev_addr) + size);
515*4882a593Smuzhiyun if (phy_addr + size == cur_addr) {
516*4882a593Smuzhiyun /* Also merge with current */
517*4882a593Smuzhiyun cvmx_bootmem_phy_set_size(prev_addr,
518*4882a593Smuzhiyun cvmx_bootmem_phy_get_size(cur_addr) +
519*4882a593Smuzhiyun cvmx_bootmem_phy_get_size(prev_addr));
520*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(prev_addr,
521*4882a593Smuzhiyun cvmx_bootmem_phy_get_next(cur_addr));
522*4882a593Smuzhiyun }
523*4882a593Smuzhiyun retval = 1;
524*4882a593Smuzhiyun goto bootmem_free_done;
525*4882a593Smuzhiyun } else if (phy_addr + size == cur_addr) {
526*4882a593Smuzhiyun /* Merge with current */
527*4882a593Smuzhiyun cvmx_bootmem_phy_set_size(phy_addr,
528*4882a593Smuzhiyun cvmx_bootmem_phy_get_size
529*4882a593Smuzhiyun (cur_addr) + size);
530*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(phy_addr,
531*4882a593Smuzhiyun cvmx_bootmem_phy_get_next
532*4882a593Smuzhiyun (cur_addr));
533*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
534*4882a593Smuzhiyun retval = 1;
535*4882a593Smuzhiyun goto bootmem_free_done;
536*4882a593Smuzhiyun }
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun /* It is a standalone block, add in between prev and cur */
539*4882a593Smuzhiyun cvmx_bootmem_phy_set_size(phy_addr, size);
540*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(phy_addr, cur_addr);
541*4882a593Smuzhiyun cvmx_bootmem_phy_set_next(prev_addr, phy_addr);
542*4882a593Smuzhiyun
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun retval = 1;
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun bootmem_free_done:
547*4882a593Smuzhiyun if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
548*4882a593Smuzhiyun cvmx_bootmem_unlock();
549*4882a593Smuzhiyun return retval;
550*4882a593Smuzhiyun
551*4882a593Smuzhiyun }
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun /**
554*4882a593Smuzhiyun * Finds a named memory block by name.
555*4882a593Smuzhiyun * Also used for finding an unused entry in the named block table.
556*4882a593Smuzhiyun *
557*4882a593Smuzhiyun * @name: Name of memory block to find. If NULL pointer given, then
558*4882a593Smuzhiyun * finds unused descriptor, if available.
559*4882a593Smuzhiyun *
560*4882a593Smuzhiyun * @flags: Flags to control options for the allocation.
561*4882a593Smuzhiyun *
562*4882a593Smuzhiyun * Returns Pointer to memory block descriptor, NULL if not found.
563*4882a593Smuzhiyun * If NULL returned when name parameter is NULL, then no memory
564*4882a593Smuzhiyun * block descriptors are available.
565*4882a593Smuzhiyun */
566*4882a593Smuzhiyun static struct cvmx_bootmem_named_block_desc *
cvmx_bootmem_phy_named_block_find(char * name,uint32_t flags)567*4882a593Smuzhiyun cvmx_bootmem_phy_named_block_find(char *name, uint32_t flags)
568*4882a593Smuzhiyun {
569*4882a593Smuzhiyun unsigned int i;
570*4882a593Smuzhiyun struct cvmx_bootmem_named_block_desc *named_block_array_ptr;
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun #ifdef DEBUG
573*4882a593Smuzhiyun cvmx_dprintf("cvmx_bootmem_phy_named_block_find: %s\n", name);
574*4882a593Smuzhiyun #endif
575*4882a593Smuzhiyun /*
576*4882a593Smuzhiyun * Lock the structure to make sure that it is not being
577*4882a593Smuzhiyun * changed while we are examining it.
578*4882a593Smuzhiyun */
579*4882a593Smuzhiyun if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
580*4882a593Smuzhiyun cvmx_bootmem_lock();
581*4882a593Smuzhiyun
582*4882a593Smuzhiyun /* Use XKPHYS for 64 bit linux */
583*4882a593Smuzhiyun named_block_array_ptr = (struct cvmx_bootmem_named_block_desc *)
584*4882a593Smuzhiyun cvmx_phys_to_ptr(cvmx_bootmem_desc->named_block_array_addr);
585*4882a593Smuzhiyun
586*4882a593Smuzhiyun #ifdef DEBUG
587*4882a593Smuzhiyun cvmx_dprintf
588*4882a593Smuzhiyun ("cvmx_bootmem_phy_named_block_find: named_block_array_ptr: %p\n",
589*4882a593Smuzhiyun named_block_array_ptr);
590*4882a593Smuzhiyun #endif
591*4882a593Smuzhiyun if (cvmx_bootmem_desc->major_version == 3) {
592*4882a593Smuzhiyun for (i = 0;
593*4882a593Smuzhiyun i < cvmx_bootmem_desc->named_block_num_blocks; i++) {
594*4882a593Smuzhiyun if ((name && named_block_array_ptr[i].size
595*4882a593Smuzhiyun && !strncmp(name, named_block_array_ptr[i].name,
596*4882a593Smuzhiyun cvmx_bootmem_desc->named_block_name_len
597*4882a593Smuzhiyun - 1))
598*4882a593Smuzhiyun || (!name && !named_block_array_ptr[i].size)) {
599*4882a593Smuzhiyun if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
600*4882a593Smuzhiyun cvmx_bootmem_unlock();
601*4882a593Smuzhiyun
602*4882a593Smuzhiyun return &(named_block_array_ptr[i]);
603*4882a593Smuzhiyun }
604*4882a593Smuzhiyun }
605*4882a593Smuzhiyun } else {
606*4882a593Smuzhiyun cvmx_dprintf("ERROR: Incompatible bootmem descriptor "
607*4882a593Smuzhiyun "version: %d.%d at addr: %p\n",
608*4882a593Smuzhiyun (int)cvmx_bootmem_desc->major_version,
609*4882a593Smuzhiyun (int)cvmx_bootmem_desc->minor_version,
610*4882a593Smuzhiyun cvmx_bootmem_desc);
611*4882a593Smuzhiyun }
612*4882a593Smuzhiyun if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
613*4882a593Smuzhiyun cvmx_bootmem_unlock();
614*4882a593Smuzhiyun
615*4882a593Smuzhiyun return NULL;
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun
cvmx_bootmem_alloc_named_range_once(uint64_t size,uint64_t min_addr,uint64_t max_addr,uint64_t align,char * name,void (* init)(void *))618*4882a593Smuzhiyun void *cvmx_bootmem_alloc_named_range_once(uint64_t size, uint64_t min_addr,
619*4882a593Smuzhiyun uint64_t max_addr, uint64_t align,
620*4882a593Smuzhiyun char *name,
621*4882a593Smuzhiyun void (*init) (void *))
622*4882a593Smuzhiyun {
623*4882a593Smuzhiyun int64_t addr;
624*4882a593Smuzhiyun void *ptr;
625*4882a593Smuzhiyun uint64_t named_block_desc_addr;
626*4882a593Smuzhiyun
627*4882a593Smuzhiyun named_block_desc_addr = (uint64_t)
628*4882a593Smuzhiyun cvmx_bootmem_phy_named_block_find(name,
629*4882a593Smuzhiyun (uint32_t)CVMX_BOOTMEM_FLAG_NO_LOCKING);
630*4882a593Smuzhiyun
631*4882a593Smuzhiyun if (named_block_desc_addr) {
632*4882a593Smuzhiyun addr = CVMX_BOOTMEM_NAMED_GET_FIELD(named_block_desc_addr,
633*4882a593Smuzhiyun base_addr);
634*4882a593Smuzhiyun return cvmx_phys_to_ptr(addr);
635*4882a593Smuzhiyun }
636*4882a593Smuzhiyun
637*4882a593Smuzhiyun addr = cvmx_bootmem_phy_named_block_alloc(size, min_addr, max_addr,
638*4882a593Smuzhiyun align, name,
639*4882a593Smuzhiyun (uint32_t)CVMX_BOOTMEM_FLAG_NO_LOCKING);
640*4882a593Smuzhiyun
641*4882a593Smuzhiyun if (addr < 0)
642*4882a593Smuzhiyun return NULL;
643*4882a593Smuzhiyun ptr = cvmx_phys_to_ptr(addr);
644*4882a593Smuzhiyun
645*4882a593Smuzhiyun if (init)
646*4882a593Smuzhiyun init(ptr);
647*4882a593Smuzhiyun else
648*4882a593Smuzhiyun memset(ptr, 0, size);
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun return ptr;
651*4882a593Smuzhiyun }
652*4882a593Smuzhiyun EXPORT_SYMBOL(cvmx_bootmem_alloc_named_range_once);
653*4882a593Smuzhiyun
cvmx_bootmem_find_named_block(char * name)654*4882a593Smuzhiyun struct cvmx_bootmem_named_block_desc *cvmx_bootmem_find_named_block(char *name)
655*4882a593Smuzhiyun {
656*4882a593Smuzhiyun return cvmx_bootmem_phy_named_block_find(name, 0);
657*4882a593Smuzhiyun }
658*4882a593Smuzhiyun EXPORT_SYMBOL(cvmx_bootmem_find_named_block);
659*4882a593Smuzhiyun
660*4882a593Smuzhiyun /**
661*4882a593Smuzhiyun * Frees a named block.
662*4882a593Smuzhiyun *
663*4882a593Smuzhiyun * @name: name of block to free
664*4882a593Smuzhiyun * @flags: flags for passing options
665*4882a593Smuzhiyun *
666*4882a593Smuzhiyun * Returns 0 on failure
667*4882a593Smuzhiyun * 1 on success
668*4882a593Smuzhiyun */
cvmx_bootmem_phy_named_block_free(char * name,uint32_t flags)669*4882a593Smuzhiyun static int cvmx_bootmem_phy_named_block_free(char *name, uint32_t flags)
670*4882a593Smuzhiyun {
671*4882a593Smuzhiyun struct cvmx_bootmem_named_block_desc *named_block_ptr;
672*4882a593Smuzhiyun
673*4882a593Smuzhiyun if (cvmx_bootmem_desc->major_version != 3) {
674*4882a593Smuzhiyun cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: "
675*4882a593Smuzhiyun "%d.%d at addr: %p\n",
676*4882a593Smuzhiyun (int)cvmx_bootmem_desc->major_version,
677*4882a593Smuzhiyun (int)cvmx_bootmem_desc->minor_version,
678*4882a593Smuzhiyun cvmx_bootmem_desc);
679*4882a593Smuzhiyun return 0;
680*4882a593Smuzhiyun }
681*4882a593Smuzhiyun #ifdef DEBUG
682*4882a593Smuzhiyun cvmx_dprintf("cvmx_bootmem_phy_named_block_free: %s\n", name);
683*4882a593Smuzhiyun #endif
684*4882a593Smuzhiyun
685*4882a593Smuzhiyun /*
686*4882a593Smuzhiyun * Take lock here, as name lookup/block free/name free need to
687*4882a593Smuzhiyun * be atomic.
688*4882a593Smuzhiyun */
689*4882a593Smuzhiyun cvmx_bootmem_lock();
690*4882a593Smuzhiyun
691*4882a593Smuzhiyun named_block_ptr =
692*4882a593Smuzhiyun cvmx_bootmem_phy_named_block_find(name,
693*4882a593Smuzhiyun CVMX_BOOTMEM_FLAG_NO_LOCKING);
694*4882a593Smuzhiyun if (named_block_ptr) {
695*4882a593Smuzhiyun #ifdef DEBUG
696*4882a593Smuzhiyun cvmx_dprintf("cvmx_bootmem_phy_named_block_free: "
697*4882a593Smuzhiyun "%s, base: 0x%llx, size: 0x%llx\n",
698*4882a593Smuzhiyun name,
699*4882a593Smuzhiyun (unsigned long long)named_block_ptr->base_addr,
700*4882a593Smuzhiyun (unsigned long long)named_block_ptr->size);
701*4882a593Smuzhiyun #endif
702*4882a593Smuzhiyun __cvmx_bootmem_phy_free(named_block_ptr->base_addr,
703*4882a593Smuzhiyun named_block_ptr->size,
704*4882a593Smuzhiyun CVMX_BOOTMEM_FLAG_NO_LOCKING);
705*4882a593Smuzhiyun named_block_ptr->size = 0;
706*4882a593Smuzhiyun /* Set size to zero to indicate block not used. */
707*4882a593Smuzhiyun }
708*4882a593Smuzhiyun
709*4882a593Smuzhiyun cvmx_bootmem_unlock();
710*4882a593Smuzhiyun return named_block_ptr != NULL; /* 0 on failure, 1 on success */
711*4882a593Smuzhiyun }
712*4882a593Smuzhiyun
cvmx_bootmem_free_named(char * name)713*4882a593Smuzhiyun int cvmx_bootmem_free_named(char *name)
714*4882a593Smuzhiyun {
715*4882a593Smuzhiyun return cvmx_bootmem_phy_named_block_free(name, 0);
716*4882a593Smuzhiyun }
717*4882a593Smuzhiyun
cvmx_bootmem_phy_named_block_alloc(uint64_t size,uint64_t min_addr,uint64_t max_addr,uint64_t alignment,char * name,uint32_t flags)718*4882a593Smuzhiyun int64_t cvmx_bootmem_phy_named_block_alloc(uint64_t size, uint64_t min_addr,
719*4882a593Smuzhiyun uint64_t max_addr,
720*4882a593Smuzhiyun uint64_t alignment,
721*4882a593Smuzhiyun char *name,
722*4882a593Smuzhiyun uint32_t flags)
723*4882a593Smuzhiyun {
724*4882a593Smuzhiyun int64_t addr_allocated;
725*4882a593Smuzhiyun struct cvmx_bootmem_named_block_desc *named_block_desc_ptr;
726*4882a593Smuzhiyun
727*4882a593Smuzhiyun #ifdef DEBUG
728*4882a593Smuzhiyun cvmx_dprintf("cvmx_bootmem_phy_named_block_alloc: size: 0x%llx, min: "
729*4882a593Smuzhiyun "0x%llx, max: 0x%llx, align: 0x%llx, name: %s\n",
730*4882a593Smuzhiyun (unsigned long long)size,
731*4882a593Smuzhiyun (unsigned long long)min_addr,
732*4882a593Smuzhiyun (unsigned long long)max_addr,
733*4882a593Smuzhiyun (unsigned long long)alignment,
734*4882a593Smuzhiyun name);
735*4882a593Smuzhiyun #endif
736*4882a593Smuzhiyun if (cvmx_bootmem_desc->major_version != 3) {
737*4882a593Smuzhiyun cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: "
738*4882a593Smuzhiyun "%d.%d at addr: %p\n",
739*4882a593Smuzhiyun (int)cvmx_bootmem_desc->major_version,
740*4882a593Smuzhiyun (int)cvmx_bootmem_desc->minor_version,
741*4882a593Smuzhiyun cvmx_bootmem_desc);
742*4882a593Smuzhiyun return -1;
743*4882a593Smuzhiyun }
744*4882a593Smuzhiyun
745*4882a593Smuzhiyun /*
746*4882a593Smuzhiyun * Take lock here, as name lookup/block alloc/name add need to
747*4882a593Smuzhiyun * be atomic.
748*4882a593Smuzhiyun */
749*4882a593Smuzhiyun if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
750*4882a593Smuzhiyun cvmx_spinlock_lock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
751*4882a593Smuzhiyun
752*4882a593Smuzhiyun /* Get pointer to first available named block descriptor */
753*4882a593Smuzhiyun named_block_desc_ptr =
754*4882a593Smuzhiyun cvmx_bootmem_phy_named_block_find(NULL,
755*4882a593Smuzhiyun flags | CVMX_BOOTMEM_FLAG_NO_LOCKING);
756*4882a593Smuzhiyun
757*4882a593Smuzhiyun /*
758*4882a593Smuzhiyun * Check to see if name already in use, return error if name
759*4882a593Smuzhiyun * not available or no more room for blocks.
760*4882a593Smuzhiyun */
761*4882a593Smuzhiyun if (cvmx_bootmem_phy_named_block_find(name,
762*4882a593Smuzhiyun flags | CVMX_BOOTMEM_FLAG_NO_LOCKING) || !named_block_desc_ptr) {
763*4882a593Smuzhiyun if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
764*4882a593Smuzhiyun cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
765*4882a593Smuzhiyun return -1;
766*4882a593Smuzhiyun }
767*4882a593Smuzhiyun
768*4882a593Smuzhiyun
769*4882a593Smuzhiyun /*
770*4882a593Smuzhiyun * Round size up to mult of minimum alignment bytes We need
771*4882a593Smuzhiyun * the actual size allocated to allow for blocks to be
772*4882a593Smuzhiyun * coalesced when they are freed. The alloc routine does the
773*4882a593Smuzhiyun * same rounding up on all allocations.
774*4882a593Smuzhiyun */
775*4882a593Smuzhiyun size = ALIGN(size, CVMX_BOOTMEM_ALIGNMENT_SIZE);
776*4882a593Smuzhiyun
777*4882a593Smuzhiyun addr_allocated = cvmx_bootmem_phy_alloc(size, min_addr, max_addr,
778*4882a593Smuzhiyun alignment,
779*4882a593Smuzhiyun flags | CVMX_BOOTMEM_FLAG_NO_LOCKING);
780*4882a593Smuzhiyun if (addr_allocated >= 0) {
781*4882a593Smuzhiyun named_block_desc_ptr->base_addr = addr_allocated;
782*4882a593Smuzhiyun named_block_desc_ptr->size = size;
783*4882a593Smuzhiyun strncpy(named_block_desc_ptr->name, name,
784*4882a593Smuzhiyun cvmx_bootmem_desc->named_block_name_len);
785*4882a593Smuzhiyun named_block_desc_ptr->name[cvmx_bootmem_desc->named_block_name_len - 1] = 0;
786*4882a593Smuzhiyun }
787*4882a593Smuzhiyun
788*4882a593Smuzhiyun if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING))
789*4882a593Smuzhiyun cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock));
790*4882a593Smuzhiyun return addr_allocated;
791*4882a593Smuzhiyun }
792*4882a593Smuzhiyun
cvmx_bootmem_get_desc(void)793*4882a593Smuzhiyun struct cvmx_bootmem_desc *cvmx_bootmem_get_desc(void)
794*4882a593Smuzhiyun {
795*4882a593Smuzhiyun return cvmx_bootmem_desc;
796*4882a593Smuzhiyun }
797