162306a36Sopenharmony_ci/***********************license start*************** 262306a36Sopenharmony_ci * Author: Cavium Networks 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Contact: support@caviumnetworks.com 562306a36Sopenharmony_ci * This file is part of the OCTEON SDK 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2003-2008 Cavium Networks 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This file is free software; you can redistribute it and/or modify 1062306a36Sopenharmony_ci * it under the terms of the GNU General Public License, Version 2, as 1162306a36Sopenharmony_ci * published by the Free Software Foundation. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This file is distributed in the hope that it will be useful, but 1462306a36Sopenharmony_ci * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty 1562306a36Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or 1662306a36Sopenharmony_ci * NONINFRINGEMENT. See the GNU General Public License for more 1762306a36Sopenharmony_ci * details. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 2062306a36Sopenharmony_ci * along with this file; if not, write to the Free Software 2162306a36Sopenharmony_ci * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 2262306a36Sopenharmony_ci * or visit http://www.gnu.org/licenses/. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * This file may also be available under a different license from Cavium. 2562306a36Sopenharmony_ci * Contact Cavium Networks for more information 2662306a36Sopenharmony_ci ***********************license end**************************************/ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Simple allocate only memory allocator. Used to allocate memory at 3062306a36Sopenharmony_ci * application start time. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <linux/export.h> 3462306a36Sopenharmony_ci#include <linux/kernel.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <asm/octeon/cvmx.h> 3762306a36Sopenharmony_ci#include <asm/octeon/cvmx-spinlock.h> 3862306a36Sopenharmony_ci#include <asm/octeon/cvmx-bootmem.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/*#define DEBUG */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct cvmx_bootmem_desc *cvmx_bootmem_desc; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* See header file for descriptions of functions */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * This macro returns a member of the 4962306a36Sopenharmony_ci * cvmx_bootmem_named_block_desc_t structure. These members can't 5062306a36Sopenharmony_ci * be directly addressed as they might be in memory not directly 5162306a36Sopenharmony_ci * reachable. In the case where bootmem is compiled with 5262306a36Sopenharmony_ci * LINUX_HOST, the structure itself might be located on a remote 5362306a36Sopenharmony_ci * Octeon. The argument "field" is the member name of the 5462306a36Sopenharmony_ci * cvmx_bootmem_named_block_desc_t to read. Regardless of the type 5562306a36Sopenharmony_ci * of the field, the return type is always a uint64_t. The "addr" 5662306a36Sopenharmony_ci * parameter is the physical address of the structure. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci#define CVMX_BOOTMEM_NAMED_GET_FIELD(addr, field) \ 5962306a36Sopenharmony_ci __cvmx_bootmem_desc_get(addr, \ 6062306a36Sopenharmony_ci offsetof(struct cvmx_bootmem_named_block_desc, field), \ 6162306a36Sopenharmony_ci sizeof_field(struct cvmx_bootmem_named_block_desc, field)) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * This function is the implementation of the get macros defined 6562306a36Sopenharmony_ci * for individual structure members. The argument are generated 6662306a36Sopenharmony_ci * by the macros inorder to read only the needed memory. 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * @param base 64bit physical address of the complete structure 6962306a36Sopenharmony_ci * @param offset Offset from the beginning of the structure to the member being 7062306a36Sopenharmony_ci * accessed. 7162306a36Sopenharmony_ci * @param size Size of the structure member. 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * @return Value of the structure member promoted into a uint64_t. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_cistatic inline uint64_t __cvmx_bootmem_desc_get(uint64_t base, int offset, 7662306a36Sopenharmony_ci int size) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci base = (1ull << 63) | (base + offset); 7962306a36Sopenharmony_ci switch (size) { 8062306a36Sopenharmony_ci case 4: 8162306a36Sopenharmony_ci return cvmx_read64_uint32(base); 8262306a36Sopenharmony_ci case 8: 8362306a36Sopenharmony_ci return cvmx_read64_uint64(base); 8462306a36Sopenharmony_ci default: 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci * Wrapper functions are provided for reading/writing the size and 9162306a36Sopenharmony_ci * next block values as these may not be directly addressible (in 32 9262306a36Sopenharmony_ci * bit applications, for instance.) Offsets of data elements in 9362306a36Sopenharmony_ci * bootmem list, must match cvmx_bootmem_block_header_t. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci#define NEXT_OFFSET 0 9662306a36Sopenharmony_ci#define SIZE_OFFSET 8 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void cvmx_bootmem_phy_set_size(uint64_t addr, uint64_t size) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci cvmx_write64_uint64((addr + SIZE_OFFSET) | (1ull << 63), size); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void cvmx_bootmem_phy_set_next(uint64_t addr, uint64_t next) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci cvmx_write64_uint64((addr + NEXT_OFFSET) | (1ull << 63), next); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic uint64_t cvmx_bootmem_phy_get_size(uint64_t addr) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci return cvmx_read64_uint64((addr + SIZE_OFFSET) | (1ull << 63)); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic uint64_t cvmx_bootmem_phy_get_next(uint64_t addr) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci return cvmx_read64_uint64((addr + NEXT_OFFSET) | (1ull << 63)); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* 11962306a36Sopenharmony_ci * Allocate a block of memory from the free list that was 12062306a36Sopenharmony_ci * passed to the application by the bootloader within a specified 12162306a36Sopenharmony_ci * address range. This is an allocate-only algorithm, so 12262306a36Sopenharmony_ci * freeing memory is not possible. Allocation will fail if 12362306a36Sopenharmony_ci * memory cannot be allocated in the requested range. 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * @size: Size in bytes of block to allocate 12662306a36Sopenharmony_ci * @min_addr: defines the minimum address of the range 12762306a36Sopenharmony_ci * @max_addr: defines the maximum address of the range 12862306a36Sopenharmony_ci * @alignment: Alignment required - must be power of 2 12962306a36Sopenharmony_ci * Returns pointer to block of memory, NULL on error 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistatic void *cvmx_bootmem_alloc_range(uint64_t size, uint64_t alignment, 13262306a36Sopenharmony_ci uint64_t min_addr, uint64_t max_addr) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci int64_t address; 13562306a36Sopenharmony_ci address = 13662306a36Sopenharmony_ci cvmx_bootmem_phy_alloc(size, min_addr, max_addr, alignment, 0); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (address > 0) 13962306a36Sopenharmony_ci return cvmx_phys_to_ptr(address); 14062306a36Sopenharmony_ci else 14162306a36Sopenharmony_ci return NULL; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_civoid *cvmx_bootmem_alloc_address(uint64_t size, uint64_t address, 14562306a36Sopenharmony_ci uint64_t alignment) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci return cvmx_bootmem_alloc_range(size, alignment, address, 14862306a36Sopenharmony_ci address + size); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_civoid *cvmx_bootmem_alloc_named_range(uint64_t size, uint64_t min_addr, 15262306a36Sopenharmony_ci uint64_t max_addr, uint64_t align, 15362306a36Sopenharmony_ci char *name) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int64_t addr; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci addr = cvmx_bootmem_phy_named_block_alloc(size, min_addr, max_addr, 15862306a36Sopenharmony_ci align, name, 0); 15962306a36Sopenharmony_ci if (addr >= 0) 16062306a36Sopenharmony_ci return cvmx_phys_to_ptr(addr); 16162306a36Sopenharmony_ci else 16262306a36Sopenharmony_ci return NULL; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_civoid *cvmx_bootmem_alloc_named(uint64_t size, uint64_t alignment, char *name) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci return cvmx_bootmem_alloc_named_range(size, 0, 0, alignment, name); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ciEXPORT_SYMBOL(cvmx_bootmem_alloc_named); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_civoid cvmx_bootmem_lock(void) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci cvmx_spinlock_lock((cvmx_spinlock_t *) &(cvmx_bootmem_desc->lock)); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_civoid cvmx_bootmem_unlock(void) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci cvmx_spinlock_unlock((cvmx_spinlock_t *) &(cvmx_bootmem_desc->lock)); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ciint cvmx_bootmem_init(void *mem_desc_ptr) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci /* Here we set the global pointer to the bootmem descriptor 18462306a36Sopenharmony_ci * block. This pointer will be used directly, so we will set 18562306a36Sopenharmony_ci * it up to be directly usable by the application. It is set 18662306a36Sopenharmony_ci * up as follows for the various runtime/ABI combinations: 18762306a36Sopenharmony_ci * 18862306a36Sopenharmony_ci * Linux 64 bit: Set XKPHYS bit 18962306a36Sopenharmony_ci * Linux 32 bit: use mmap to create mapping, use virtual address 19062306a36Sopenharmony_ci * CVMX 64 bit: use physical address directly 19162306a36Sopenharmony_ci * CVMX 32 bit: use physical address directly 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * Note that the CVMX environment assumes the use of 1-1 TLB 19462306a36Sopenharmony_ci * mappings so that the physical addresses can be used 19562306a36Sopenharmony_ci * directly 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci if (!cvmx_bootmem_desc) { 19862306a36Sopenharmony_ci#if defined(CVMX_ABI_64) 19962306a36Sopenharmony_ci /* Set XKPHYS bit */ 20062306a36Sopenharmony_ci cvmx_bootmem_desc = cvmx_phys_to_ptr(CAST64(mem_desc_ptr)); 20162306a36Sopenharmony_ci#else 20262306a36Sopenharmony_ci cvmx_bootmem_desc = (struct cvmx_bootmem_desc *) mem_desc_ptr; 20362306a36Sopenharmony_ci#endif 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* 21062306a36Sopenharmony_ci * The cvmx_bootmem_phy* functions below return 64 bit physical 21162306a36Sopenharmony_ci * addresses, and expose more features that the cvmx_bootmem_functions 21262306a36Sopenharmony_ci * above. These are required for full memory space access in 32 bit 21362306a36Sopenharmony_ci * applications, as well as for using some advance features. Most 21462306a36Sopenharmony_ci * applications should not need to use these. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ciint64_t cvmx_bootmem_phy_alloc(uint64_t req_size, uint64_t address_min, 21862306a36Sopenharmony_ci uint64_t address_max, uint64_t alignment, 21962306a36Sopenharmony_ci uint32_t flags) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci uint64_t head_addr; 22362306a36Sopenharmony_ci uint64_t ent_addr; 22462306a36Sopenharmony_ci /* points to previous list entry, NULL current entry is head of list */ 22562306a36Sopenharmony_ci uint64_t prev_addr = 0; 22662306a36Sopenharmony_ci uint64_t new_ent_addr = 0; 22762306a36Sopenharmony_ci uint64_t desired_min_addr; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci#ifdef DEBUG 23062306a36Sopenharmony_ci cvmx_dprintf("cvmx_bootmem_phy_alloc: req_size: 0x%llx, " 23162306a36Sopenharmony_ci "min_addr: 0x%llx, max_addr: 0x%llx, align: 0x%llx\n", 23262306a36Sopenharmony_ci (unsigned long long)req_size, 23362306a36Sopenharmony_ci (unsigned long long)address_min, 23462306a36Sopenharmony_ci (unsigned long long)address_max, 23562306a36Sopenharmony_ci (unsigned long long)alignment); 23662306a36Sopenharmony_ci#endif 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (cvmx_bootmem_desc->major_version > 3) { 23962306a36Sopenharmony_ci cvmx_dprintf("ERROR: Incompatible bootmem descriptor " 24062306a36Sopenharmony_ci "version: %d.%d at addr: %p\n", 24162306a36Sopenharmony_ci (int)cvmx_bootmem_desc->major_version, 24262306a36Sopenharmony_ci (int)cvmx_bootmem_desc->minor_version, 24362306a36Sopenharmony_ci cvmx_bootmem_desc); 24462306a36Sopenharmony_ci goto error_out; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* 24862306a36Sopenharmony_ci * Do a variety of checks to validate the arguments. The 24962306a36Sopenharmony_ci * allocator code will later assume that these checks have 25062306a36Sopenharmony_ci * been made. We validate that the requested constraints are 25162306a36Sopenharmony_ci * not self-contradictory before we look through the list of 25262306a36Sopenharmony_ci * available memory. 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* 0 is not a valid req_size for this allocator */ 25662306a36Sopenharmony_ci if (!req_size) 25762306a36Sopenharmony_ci goto error_out; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* Round req_size up to mult of minimum alignment bytes */ 26062306a36Sopenharmony_ci req_size = (req_size + (CVMX_BOOTMEM_ALIGNMENT_SIZE - 1)) & 26162306a36Sopenharmony_ci ~(CVMX_BOOTMEM_ALIGNMENT_SIZE - 1); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* 26462306a36Sopenharmony_ci * Convert !0 address_min and 0 address_max to special case of 26562306a36Sopenharmony_ci * range that specifies an exact memory block to allocate. Do 26662306a36Sopenharmony_ci * this before other checks and adjustments so that this 26762306a36Sopenharmony_ci * tranformation will be validated. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ci if (address_min && !address_max) 27062306a36Sopenharmony_ci address_max = address_min + req_size; 27162306a36Sopenharmony_ci else if (!address_min && !address_max) 27262306a36Sopenharmony_ci address_max = ~0ull; /* If no limits given, use max limits */ 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* 27662306a36Sopenharmony_ci * Enforce minimum alignment (this also keeps the minimum free block 27762306a36Sopenharmony_ci * req_size the same as the alignment req_size. 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ci if (alignment < CVMX_BOOTMEM_ALIGNMENT_SIZE) 28062306a36Sopenharmony_ci alignment = CVMX_BOOTMEM_ALIGNMENT_SIZE; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * Adjust address minimum based on requested alignment (round 28462306a36Sopenharmony_ci * up to meet alignment). Do this here so we can reject 28562306a36Sopenharmony_ci * impossible requests up front. (NOP for address_min == 0) 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ci if (alignment) 28862306a36Sopenharmony_ci address_min = ALIGN(address_min, alignment); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * Reject inconsistent args. We have adjusted these, so this 29262306a36Sopenharmony_ci * may fail due to our internal changes even if this check 29362306a36Sopenharmony_ci * would pass for the values the user supplied. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci if (req_size > address_max - address_min) 29662306a36Sopenharmony_ci goto error_out; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Walk through the list entries - first fit found is returned */ 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) 30162306a36Sopenharmony_ci cvmx_bootmem_lock(); 30262306a36Sopenharmony_ci head_addr = cvmx_bootmem_desc->head_addr; 30362306a36Sopenharmony_ci ent_addr = head_addr; 30462306a36Sopenharmony_ci for (; ent_addr; 30562306a36Sopenharmony_ci prev_addr = ent_addr, 30662306a36Sopenharmony_ci ent_addr = cvmx_bootmem_phy_get_next(ent_addr)) { 30762306a36Sopenharmony_ci uint64_t usable_base, usable_max; 30862306a36Sopenharmony_ci uint64_t ent_size = cvmx_bootmem_phy_get_size(ent_addr); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (cvmx_bootmem_phy_get_next(ent_addr) 31162306a36Sopenharmony_ci && ent_addr > cvmx_bootmem_phy_get_next(ent_addr)) { 31262306a36Sopenharmony_ci cvmx_dprintf("Internal bootmem_alloc() error: ent: " 31362306a36Sopenharmony_ci "0x%llx, next: 0x%llx\n", 31462306a36Sopenharmony_ci (unsigned long long)ent_addr, 31562306a36Sopenharmony_ci (unsigned long long) 31662306a36Sopenharmony_ci cvmx_bootmem_phy_get_next(ent_addr)); 31762306a36Sopenharmony_ci goto error_out; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* 32162306a36Sopenharmony_ci * Determine if this is an entry that can satisfy the 32262306a36Sopenharmony_ci * request Check to make sure entry is large enough to 32362306a36Sopenharmony_ci * satisfy request. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci usable_base = 32662306a36Sopenharmony_ci ALIGN(max(address_min, ent_addr), alignment); 32762306a36Sopenharmony_ci usable_max = min(address_max, ent_addr + ent_size); 32862306a36Sopenharmony_ci /* 32962306a36Sopenharmony_ci * We should be able to allocate block at address 33062306a36Sopenharmony_ci * usable_base. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci desired_min_addr = usable_base; 33462306a36Sopenharmony_ci /* 33562306a36Sopenharmony_ci * Determine if request can be satisfied from the 33662306a36Sopenharmony_ci * current entry. 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ci if (!((ent_addr + ent_size) > usable_base 33962306a36Sopenharmony_ci && ent_addr < address_max 34062306a36Sopenharmony_ci && req_size <= usable_max - usable_base)) 34162306a36Sopenharmony_ci continue; 34262306a36Sopenharmony_ci /* 34362306a36Sopenharmony_ci * We have found an entry that has room to satisfy the 34462306a36Sopenharmony_ci * request, so allocate it from this entry. If end 34562306a36Sopenharmony_ci * CVMX_BOOTMEM_FLAG_END_ALLOC set, then allocate from 34662306a36Sopenharmony_ci * the end of this block rather than the beginning. 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_ci if (flags & CVMX_BOOTMEM_FLAG_END_ALLOC) { 34962306a36Sopenharmony_ci desired_min_addr = usable_max - req_size; 35062306a36Sopenharmony_ci /* 35162306a36Sopenharmony_ci * Align desired address down to required 35262306a36Sopenharmony_ci * alignment. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci desired_min_addr &= ~(alignment - 1); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* Match at start of entry */ 35862306a36Sopenharmony_ci if (desired_min_addr == ent_addr) { 35962306a36Sopenharmony_ci if (req_size < ent_size) { 36062306a36Sopenharmony_ci /* 36162306a36Sopenharmony_ci * big enough to create a new block 36262306a36Sopenharmony_ci * from top portion of block. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci new_ent_addr = ent_addr + req_size; 36562306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(new_ent_addr, 36662306a36Sopenharmony_ci cvmx_bootmem_phy_get_next(ent_addr)); 36762306a36Sopenharmony_ci cvmx_bootmem_phy_set_size(new_ent_addr, 36862306a36Sopenharmony_ci ent_size - 36962306a36Sopenharmony_ci req_size); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* 37262306a36Sopenharmony_ci * Adjust next pointer as following 37362306a36Sopenharmony_ci * code uses this. 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(ent_addr, 37662306a36Sopenharmony_ci new_ent_addr); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* 38062306a36Sopenharmony_ci * adjust prev ptr or head to remove this 38162306a36Sopenharmony_ci * entry from list. 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci if (prev_addr) 38462306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(prev_addr, 38562306a36Sopenharmony_ci cvmx_bootmem_phy_get_next(ent_addr)); 38662306a36Sopenharmony_ci else 38762306a36Sopenharmony_ci /* 38862306a36Sopenharmony_ci * head of list being returned, so 38962306a36Sopenharmony_ci * update head ptr. 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_ci cvmx_bootmem_desc->head_addr = 39262306a36Sopenharmony_ci cvmx_bootmem_phy_get_next(ent_addr); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) 39562306a36Sopenharmony_ci cvmx_bootmem_unlock(); 39662306a36Sopenharmony_ci return desired_min_addr; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci /* 39962306a36Sopenharmony_ci * block returned doesn't start at beginning of entry, 40062306a36Sopenharmony_ci * so we know that we will be splitting a block off 40162306a36Sopenharmony_ci * the front of this one. Create a new block from the 40262306a36Sopenharmony_ci * beginning, add to list, and go to top of loop 40362306a36Sopenharmony_ci * again. 40462306a36Sopenharmony_ci * 40562306a36Sopenharmony_ci * create new block from high portion of 40662306a36Sopenharmony_ci * block, so that top block starts at desired 40762306a36Sopenharmony_ci * addr. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci new_ent_addr = desired_min_addr; 41062306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(new_ent_addr, 41162306a36Sopenharmony_ci cvmx_bootmem_phy_get_next 41262306a36Sopenharmony_ci (ent_addr)); 41362306a36Sopenharmony_ci cvmx_bootmem_phy_set_size(new_ent_addr, 41462306a36Sopenharmony_ci cvmx_bootmem_phy_get_size 41562306a36Sopenharmony_ci (ent_addr) - 41662306a36Sopenharmony_ci (desired_min_addr - 41762306a36Sopenharmony_ci ent_addr)); 41862306a36Sopenharmony_ci cvmx_bootmem_phy_set_size(ent_addr, 41962306a36Sopenharmony_ci desired_min_addr - ent_addr); 42062306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(ent_addr, new_ent_addr); 42162306a36Sopenharmony_ci /* Loop again to handle actual alloc from new block */ 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_cierror_out: 42462306a36Sopenharmony_ci /* We didn't find anything, so return error */ 42562306a36Sopenharmony_ci if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) 42662306a36Sopenharmony_ci cvmx_bootmem_unlock(); 42762306a36Sopenharmony_ci return -1; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ciint __cvmx_bootmem_phy_free(uint64_t phy_addr, uint64_t size, uint32_t flags) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci uint64_t cur_addr; 43362306a36Sopenharmony_ci uint64_t prev_addr = 0; /* zero is invalid */ 43462306a36Sopenharmony_ci int retval = 0; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci#ifdef DEBUG 43762306a36Sopenharmony_ci cvmx_dprintf("__cvmx_bootmem_phy_free addr: 0x%llx, size: 0x%llx\n", 43862306a36Sopenharmony_ci (unsigned long long)phy_addr, (unsigned long long)size); 43962306a36Sopenharmony_ci#endif 44062306a36Sopenharmony_ci if (cvmx_bootmem_desc->major_version > 3) { 44162306a36Sopenharmony_ci cvmx_dprintf("ERROR: Incompatible bootmem descriptor " 44262306a36Sopenharmony_ci "version: %d.%d at addr: %p\n", 44362306a36Sopenharmony_ci (int)cvmx_bootmem_desc->major_version, 44462306a36Sopenharmony_ci (int)cvmx_bootmem_desc->minor_version, 44562306a36Sopenharmony_ci cvmx_bootmem_desc); 44662306a36Sopenharmony_ci return 0; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* 0 is not a valid size for this allocator */ 45062306a36Sopenharmony_ci if (!size) 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) 45462306a36Sopenharmony_ci cvmx_bootmem_lock(); 45562306a36Sopenharmony_ci cur_addr = cvmx_bootmem_desc->head_addr; 45662306a36Sopenharmony_ci if (cur_addr == 0 || phy_addr < cur_addr) { 45762306a36Sopenharmony_ci /* add at front of list - special case with changing head ptr */ 45862306a36Sopenharmony_ci if (cur_addr && phy_addr + size > cur_addr) 45962306a36Sopenharmony_ci goto bootmem_free_done; /* error, overlapping section */ 46062306a36Sopenharmony_ci else if (phy_addr + size == cur_addr) { 46162306a36Sopenharmony_ci /* Add to front of existing first block */ 46262306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(phy_addr, 46362306a36Sopenharmony_ci cvmx_bootmem_phy_get_next 46462306a36Sopenharmony_ci (cur_addr)); 46562306a36Sopenharmony_ci cvmx_bootmem_phy_set_size(phy_addr, 46662306a36Sopenharmony_ci cvmx_bootmem_phy_get_size 46762306a36Sopenharmony_ci (cur_addr) + size); 46862306a36Sopenharmony_ci cvmx_bootmem_desc->head_addr = phy_addr; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci } else { 47162306a36Sopenharmony_ci /* New block before first block. OK if cur_addr is 0 */ 47262306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(phy_addr, cur_addr); 47362306a36Sopenharmony_ci cvmx_bootmem_phy_set_size(phy_addr, size); 47462306a36Sopenharmony_ci cvmx_bootmem_desc->head_addr = phy_addr; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci retval = 1; 47762306a36Sopenharmony_ci goto bootmem_free_done; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* Find place in list to add block */ 48162306a36Sopenharmony_ci while (cur_addr && phy_addr > cur_addr) { 48262306a36Sopenharmony_ci prev_addr = cur_addr; 48362306a36Sopenharmony_ci cur_addr = cvmx_bootmem_phy_get_next(cur_addr); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (!cur_addr) { 48762306a36Sopenharmony_ci /* 48862306a36Sopenharmony_ci * We have reached the end of the list, add on to end, 48962306a36Sopenharmony_ci * checking to see if we need to combine with last 49062306a36Sopenharmony_ci * block 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_ci if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) == 49362306a36Sopenharmony_ci phy_addr) { 49462306a36Sopenharmony_ci cvmx_bootmem_phy_set_size(prev_addr, 49562306a36Sopenharmony_ci cvmx_bootmem_phy_get_size 49662306a36Sopenharmony_ci (prev_addr) + size); 49762306a36Sopenharmony_ci } else { 49862306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(prev_addr, phy_addr); 49962306a36Sopenharmony_ci cvmx_bootmem_phy_set_size(phy_addr, size); 50062306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(phy_addr, 0); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci retval = 1; 50362306a36Sopenharmony_ci goto bootmem_free_done; 50462306a36Sopenharmony_ci } else { 50562306a36Sopenharmony_ci /* 50662306a36Sopenharmony_ci * insert between prev and cur nodes, checking for 50762306a36Sopenharmony_ci * merge with either/both. 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_ci if (prev_addr + cvmx_bootmem_phy_get_size(prev_addr) == 51062306a36Sopenharmony_ci phy_addr) { 51162306a36Sopenharmony_ci /* Merge with previous */ 51262306a36Sopenharmony_ci cvmx_bootmem_phy_set_size(prev_addr, 51362306a36Sopenharmony_ci cvmx_bootmem_phy_get_size 51462306a36Sopenharmony_ci (prev_addr) + size); 51562306a36Sopenharmony_ci if (phy_addr + size == cur_addr) { 51662306a36Sopenharmony_ci /* Also merge with current */ 51762306a36Sopenharmony_ci cvmx_bootmem_phy_set_size(prev_addr, 51862306a36Sopenharmony_ci cvmx_bootmem_phy_get_size(cur_addr) + 51962306a36Sopenharmony_ci cvmx_bootmem_phy_get_size(prev_addr)); 52062306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(prev_addr, 52162306a36Sopenharmony_ci cvmx_bootmem_phy_get_next(cur_addr)); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci retval = 1; 52462306a36Sopenharmony_ci goto bootmem_free_done; 52562306a36Sopenharmony_ci } else if (phy_addr + size == cur_addr) { 52662306a36Sopenharmony_ci /* Merge with current */ 52762306a36Sopenharmony_ci cvmx_bootmem_phy_set_size(phy_addr, 52862306a36Sopenharmony_ci cvmx_bootmem_phy_get_size 52962306a36Sopenharmony_ci (cur_addr) + size); 53062306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(phy_addr, 53162306a36Sopenharmony_ci cvmx_bootmem_phy_get_next 53262306a36Sopenharmony_ci (cur_addr)); 53362306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(prev_addr, phy_addr); 53462306a36Sopenharmony_ci retval = 1; 53562306a36Sopenharmony_ci goto bootmem_free_done; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* It is a standalone block, add in between prev and cur */ 53962306a36Sopenharmony_ci cvmx_bootmem_phy_set_size(phy_addr, size); 54062306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(phy_addr, cur_addr); 54162306a36Sopenharmony_ci cvmx_bootmem_phy_set_next(prev_addr, phy_addr); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci retval = 1; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cibootmem_free_done: 54762306a36Sopenharmony_ci if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) 54862306a36Sopenharmony_ci cvmx_bootmem_unlock(); 54962306a36Sopenharmony_ci return retval; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci/* 55462306a36Sopenharmony_ci * Finds a named memory block by name. 55562306a36Sopenharmony_ci * Also used for finding an unused entry in the named block table. 55662306a36Sopenharmony_ci * 55762306a36Sopenharmony_ci * @name: Name of memory block to find. If NULL pointer given, then 55862306a36Sopenharmony_ci * finds unused descriptor, if available. 55962306a36Sopenharmony_ci * 56062306a36Sopenharmony_ci * @flags: Flags to control options for the allocation. 56162306a36Sopenharmony_ci * 56262306a36Sopenharmony_ci * Returns Pointer to memory block descriptor, NULL if not found. 56362306a36Sopenharmony_ci * If NULL returned when name parameter is NULL, then no memory 56462306a36Sopenharmony_ci * block descriptors are available. 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_cistatic struct cvmx_bootmem_named_block_desc * 56762306a36Sopenharmony_ci cvmx_bootmem_phy_named_block_find(char *name, uint32_t flags) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci unsigned int i; 57062306a36Sopenharmony_ci struct cvmx_bootmem_named_block_desc *named_block_array_ptr; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci#ifdef DEBUG 57362306a36Sopenharmony_ci cvmx_dprintf("cvmx_bootmem_phy_named_block_find: %s\n", name); 57462306a36Sopenharmony_ci#endif 57562306a36Sopenharmony_ci /* 57662306a36Sopenharmony_ci * Lock the structure to make sure that it is not being 57762306a36Sopenharmony_ci * changed while we are examining it. 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) 58062306a36Sopenharmony_ci cvmx_bootmem_lock(); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* Use XKPHYS for 64 bit linux */ 58362306a36Sopenharmony_ci named_block_array_ptr = (struct cvmx_bootmem_named_block_desc *) 58462306a36Sopenharmony_ci cvmx_phys_to_ptr(cvmx_bootmem_desc->named_block_array_addr); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci#ifdef DEBUG 58762306a36Sopenharmony_ci cvmx_dprintf 58862306a36Sopenharmony_ci ("cvmx_bootmem_phy_named_block_find: named_block_array_ptr: %p\n", 58962306a36Sopenharmony_ci named_block_array_ptr); 59062306a36Sopenharmony_ci#endif 59162306a36Sopenharmony_ci if (cvmx_bootmem_desc->major_version == 3) { 59262306a36Sopenharmony_ci for (i = 0; 59362306a36Sopenharmony_ci i < cvmx_bootmem_desc->named_block_num_blocks; i++) { 59462306a36Sopenharmony_ci if ((name && named_block_array_ptr[i].size 59562306a36Sopenharmony_ci && !strncmp(name, named_block_array_ptr[i].name, 59662306a36Sopenharmony_ci cvmx_bootmem_desc->named_block_name_len 59762306a36Sopenharmony_ci - 1)) 59862306a36Sopenharmony_ci || (!name && !named_block_array_ptr[i].size)) { 59962306a36Sopenharmony_ci if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) 60062306a36Sopenharmony_ci cvmx_bootmem_unlock(); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return &(named_block_array_ptr[i]); 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci } else { 60662306a36Sopenharmony_ci cvmx_dprintf("ERROR: Incompatible bootmem descriptor " 60762306a36Sopenharmony_ci "version: %d.%d at addr: %p\n", 60862306a36Sopenharmony_ci (int)cvmx_bootmem_desc->major_version, 60962306a36Sopenharmony_ci (int)cvmx_bootmem_desc->minor_version, 61062306a36Sopenharmony_ci cvmx_bootmem_desc); 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) 61362306a36Sopenharmony_ci cvmx_bootmem_unlock(); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci return NULL; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_civoid *cvmx_bootmem_alloc_named_range_once(uint64_t size, uint64_t min_addr, 61962306a36Sopenharmony_ci uint64_t max_addr, uint64_t align, 62062306a36Sopenharmony_ci char *name, 62162306a36Sopenharmony_ci void (*init) (void *)) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci int64_t addr; 62462306a36Sopenharmony_ci void *ptr; 62562306a36Sopenharmony_ci uint64_t named_block_desc_addr; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci named_block_desc_addr = (uint64_t) 62862306a36Sopenharmony_ci cvmx_bootmem_phy_named_block_find(name, 62962306a36Sopenharmony_ci (uint32_t)CVMX_BOOTMEM_FLAG_NO_LOCKING); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (named_block_desc_addr) { 63262306a36Sopenharmony_ci addr = CVMX_BOOTMEM_NAMED_GET_FIELD(named_block_desc_addr, 63362306a36Sopenharmony_ci base_addr); 63462306a36Sopenharmony_ci return cvmx_phys_to_ptr(addr); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci addr = cvmx_bootmem_phy_named_block_alloc(size, min_addr, max_addr, 63862306a36Sopenharmony_ci align, name, 63962306a36Sopenharmony_ci (uint32_t)CVMX_BOOTMEM_FLAG_NO_LOCKING); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (addr < 0) 64262306a36Sopenharmony_ci return NULL; 64362306a36Sopenharmony_ci ptr = cvmx_phys_to_ptr(addr); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (init) 64662306a36Sopenharmony_ci init(ptr); 64762306a36Sopenharmony_ci else 64862306a36Sopenharmony_ci memset(ptr, 0, size); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return ptr; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ciEXPORT_SYMBOL(cvmx_bootmem_alloc_named_range_once); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistruct cvmx_bootmem_named_block_desc *cvmx_bootmem_find_named_block(char *name) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci return cvmx_bootmem_phy_named_block_find(name, 0); 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ciEXPORT_SYMBOL(cvmx_bootmem_find_named_block); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci/* 66162306a36Sopenharmony_ci * Frees a named block. 66262306a36Sopenharmony_ci * 66362306a36Sopenharmony_ci * @name: name of block to free 66462306a36Sopenharmony_ci * @flags: flags for passing options 66562306a36Sopenharmony_ci * 66662306a36Sopenharmony_ci * Returns 0 on failure 66762306a36Sopenharmony_ci * 1 on success 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_cistatic int cvmx_bootmem_phy_named_block_free(char *name, uint32_t flags) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct cvmx_bootmem_named_block_desc *named_block_ptr; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (cvmx_bootmem_desc->major_version != 3) { 67462306a36Sopenharmony_ci cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: " 67562306a36Sopenharmony_ci "%d.%d at addr: %p\n", 67662306a36Sopenharmony_ci (int)cvmx_bootmem_desc->major_version, 67762306a36Sopenharmony_ci (int)cvmx_bootmem_desc->minor_version, 67862306a36Sopenharmony_ci cvmx_bootmem_desc); 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci#ifdef DEBUG 68262306a36Sopenharmony_ci cvmx_dprintf("cvmx_bootmem_phy_named_block_free: %s\n", name); 68362306a36Sopenharmony_ci#endif 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* 68662306a36Sopenharmony_ci * Take lock here, as name lookup/block free/name free need to 68762306a36Sopenharmony_ci * be atomic. 68862306a36Sopenharmony_ci */ 68962306a36Sopenharmony_ci cvmx_bootmem_lock(); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci named_block_ptr = 69262306a36Sopenharmony_ci cvmx_bootmem_phy_named_block_find(name, 69362306a36Sopenharmony_ci CVMX_BOOTMEM_FLAG_NO_LOCKING); 69462306a36Sopenharmony_ci if (named_block_ptr) { 69562306a36Sopenharmony_ci#ifdef DEBUG 69662306a36Sopenharmony_ci cvmx_dprintf("cvmx_bootmem_phy_named_block_free: " 69762306a36Sopenharmony_ci "%s, base: 0x%llx, size: 0x%llx\n", 69862306a36Sopenharmony_ci name, 69962306a36Sopenharmony_ci (unsigned long long)named_block_ptr->base_addr, 70062306a36Sopenharmony_ci (unsigned long long)named_block_ptr->size); 70162306a36Sopenharmony_ci#endif 70262306a36Sopenharmony_ci __cvmx_bootmem_phy_free(named_block_ptr->base_addr, 70362306a36Sopenharmony_ci named_block_ptr->size, 70462306a36Sopenharmony_ci CVMX_BOOTMEM_FLAG_NO_LOCKING); 70562306a36Sopenharmony_ci named_block_ptr->size = 0; 70662306a36Sopenharmony_ci /* Set size to zero to indicate block not used. */ 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci cvmx_bootmem_unlock(); 71062306a36Sopenharmony_ci return named_block_ptr != NULL; /* 0 on failure, 1 on success */ 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ciint cvmx_bootmem_free_named(char *name) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci return cvmx_bootmem_phy_named_block_free(name, 0); 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ciint64_t cvmx_bootmem_phy_named_block_alloc(uint64_t size, uint64_t min_addr, 71962306a36Sopenharmony_ci uint64_t max_addr, 72062306a36Sopenharmony_ci uint64_t alignment, 72162306a36Sopenharmony_ci char *name, 72262306a36Sopenharmony_ci uint32_t flags) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci int64_t addr_allocated; 72562306a36Sopenharmony_ci struct cvmx_bootmem_named_block_desc *named_block_desc_ptr; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci#ifdef DEBUG 72862306a36Sopenharmony_ci cvmx_dprintf("cvmx_bootmem_phy_named_block_alloc: size: 0x%llx, min: " 72962306a36Sopenharmony_ci "0x%llx, max: 0x%llx, align: 0x%llx, name: %s\n", 73062306a36Sopenharmony_ci (unsigned long long)size, 73162306a36Sopenharmony_ci (unsigned long long)min_addr, 73262306a36Sopenharmony_ci (unsigned long long)max_addr, 73362306a36Sopenharmony_ci (unsigned long long)alignment, 73462306a36Sopenharmony_ci name); 73562306a36Sopenharmony_ci#endif 73662306a36Sopenharmony_ci if (cvmx_bootmem_desc->major_version != 3) { 73762306a36Sopenharmony_ci cvmx_dprintf("ERROR: Incompatible bootmem descriptor version: " 73862306a36Sopenharmony_ci "%d.%d at addr: %p\n", 73962306a36Sopenharmony_ci (int)cvmx_bootmem_desc->major_version, 74062306a36Sopenharmony_ci (int)cvmx_bootmem_desc->minor_version, 74162306a36Sopenharmony_ci cvmx_bootmem_desc); 74262306a36Sopenharmony_ci return -1; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* 74662306a36Sopenharmony_ci * Take lock here, as name lookup/block alloc/name add need to 74762306a36Sopenharmony_ci * be atomic. 74862306a36Sopenharmony_ci */ 74962306a36Sopenharmony_ci if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) 75062306a36Sopenharmony_ci cvmx_spinlock_lock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock)); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* Get pointer to first available named block descriptor */ 75362306a36Sopenharmony_ci named_block_desc_ptr = 75462306a36Sopenharmony_ci cvmx_bootmem_phy_named_block_find(NULL, 75562306a36Sopenharmony_ci flags | CVMX_BOOTMEM_FLAG_NO_LOCKING); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* 75862306a36Sopenharmony_ci * Check to see if name already in use, return error if name 75962306a36Sopenharmony_ci * not available or no more room for blocks. 76062306a36Sopenharmony_ci */ 76162306a36Sopenharmony_ci if (cvmx_bootmem_phy_named_block_find(name, 76262306a36Sopenharmony_ci flags | CVMX_BOOTMEM_FLAG_NO_LOCKING) || !named_block_desc_ptr) { 76362306a36Sopenharmony_ci if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) 76462306a36Sopenharmony_ci cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock)); 76562306a36Sopenharmony_ci return -1; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* 77062306a36Sopenharmony_ci * Round size up to mult of minimum alignment bytes We need 77162306a36Sopenharmony_ci * the actual size allocated to allow for blocks to be 77262306a36Sopenharmony_ci * coalesced when they are freed. The alloc routine does the 77362306a36Sopenharmony_ci * same rounding up on all allocations. 77462306a36Sopenharmony_ci */ 77562306a36Sopenharmony_ci size = ALIGN(size, CVMX_BOOTMEM_ALIGNMENT_SIZE); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci addr_allocated = cvmx_bootmem_phy_alloc(size, min_addr, max_addr, 77862306a36Sopenharmony_ci alignment, 77962306a36Sopenharmony_ci flags | CVMX_BOOTMEM_FLAG_NO_LOCKING); 78062306a36Sopenharmony_ci if (addr_allocated >= 0) { 78162306a36Sopenharmony_ci named_block_desc_ptr->base_addr = addr_allocated; 78262306a36Sopenharmony_ci named_block_desc_ptr->size = size; 78362306a36Sopenharmony_ci strscpy(named_block_desc_ptr->name, name, 78462306a36Sopenharmony_ci cvmx_bootmem_desc->named_block_name_len); 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (!(flags & CVMX_BOOTMEM_FLAG_NO_LOCKING)) 78862306a36Sopenharmony_ci cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock)); 78962306a36Sopenharmony_ci return addr_allocated; 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistruct cvmx_bootmem_desc *cvmx_bootmem_get_desc(void) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci return cvmx_bootmem_desc; 79562306a36Sopenharmony_ci} 796