freebsd-dev/sys/contrib/octeon-sdk/cvmx-shmem.c
Juli Mallett dc4ee6ca91 Merge the Cavium Octeon SDK 2.3.0 Simple Executive code and update FreeBSD to
make use of it where possible.

This primarily brings in support for newer hardware, and FreeBSD is not yet
able to support the abundance of IRQs on new hardware and many features in the
Ethernet driver.

Because of the changes to IRQs in the Simple Executive, we have to maintain our
own list of Octeon IRQs now, which probably can be pared-down and be specific
to the CIU interrupt unit soon, and when other interrupt mechanisms are added
they can maintain their own definitions.

Remove unmasking of interrupts from within the UART device now that the
function used is no longer present in the Simple Executive.  The unmasking
seems to have been gratuitous as this is more properly handled by the buses
above the UART device, and seems to work on that basis.
2012-03-11 06:17:49 +00:00

749 lines
19 KiB
C

/***********************license start***************
* Copyright (c) 2003-2010 Cavium Inc. (support@cavium.com). All rights
* reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Cavium Inc. nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
* This Software, including technical data, may be subject to U.S. export control
* laws, including the U.S. Export Administration Act and its associated
* regulations, and may be subject to export or import regulations in other
* countries.
* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
* THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
* DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
* SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
* MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
* VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
* CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR
* PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
***********************license end**************************************/
/**
* @file
* cvmx-shmem supplies the cross application shared memory implementation
*
* <hr>$Revision: 41586 $<hr>
*/
#include "cvmx.h"
#include "cvmx-bootmem.h"
#include "cvmx-tlb.h"
#include "cvmx-shmem.h"
//#define DEBUG
struct cvmx_shmem_smdr *__smdr = NULL;
#ifdef CVMX_BUILD_FOR_LINUX_USER
static int __cvmx_shmem_devmemfd = 0; /* fd for /dev/mem */
#endif
#define __CHECK_APP_SMDR do { \
if (__smdr == NULL) { \
cvmx_dprintf("cvmx_shmem: %s is not set up, Quit line %d \n", \
CVMX_SHMEM_DSCPTR_NAME, __LINE__ ); \
exit(-1); \
} \
}while(0)
/**
* @INTERNAL
* Virtual sbrk, assigning virtual address in a global virtual address space.
*
* @param alignment alignment requirement in bytes
* @param size size in bytes
*/
static inline void *__cvmx_shmem_vsbrk_64(uint64_t alignment, uint64_t size)
{
uint64_t nbase_64 = CAST64(__smdr->break64);
void *nbase = NULL;
/* Skip unaligned bytes */
if (nbase_64 & alignment)
nbase_64 += ~(nbase_64 & alignment) + 1;
if (nbase_64 + size < CVMX_SHMEM_VADDR64_END)
{
nbase = CASTPTR(void *, nbase_64);
__smdr->break64 = nbase + size;
}
return nbase;
}
/**
* @INTERNAL
* Initialize all SMDR entries, only need to be called once
*
* @param smdr pointer to the SMDR
*/
static inline void __smdr_new(struct cvmx_shmem_smdr *smdr) {
if (smdr != NULL)
{
int i;
cvmx_spinlock_init (&smdr->lock);
cvmx_spinlock_lock (&smdr->lock);
for ( i = 0; i < CVMX_SHMEM_NUM_DSCPTR; i++ )
{
smdr -> shmd[i].owner = CVMX_SHMEM_OWNER_NONE;
smdr -> shmd[i].is_named_block = 0;
smdr -> shmd[i].use_count = 0;
smdr -> shmd[i].name = NULL;
smdr -> shmd[i].vaddr = NULL;
smdr -> shmd[i].paddr = 0;
smdr -> shmd[i].size = 0;
smdr -> shmd[i].alignment = 0;
};
/* Init vaddr */
smdr->break64 = (void *)CVMX_SHMEM_VADDR64_START;
cvmx_spinlock_unlock (&smdr->lock);
}
/* Make sure the shmem descriptor region is created */
__CHECK_APP_SMDR;
};
/**
* @INTERNAL
* Initialize __smdr pointer, if SMDR exits already. If not, create a new
* one. Once SMDR is created (as a bootmem named block), it is persistent.
*/
static inline struct cvmx_shmem_smdr *__smdr_init()
{
const cvmx_bootmem_named_block_desc_t *smdr_nblk = NULL;
size_t smdr_size = sizeof(*__smdr);
char *smdr_name = CVMX_SHMEM_DSCPTR_NAME;
__smdr = (struct cvmx_shmem_smdr *) cvmx_bootmem_alloc_named(smdr_size, 0x10000, smdr_name);
if (__smdr)
__smdr_new (__smdr);
else
{
/* Check if SMDR exists already */
smdr_nblk = cvmx_bootmem_find_named_block(smdr_name);
if (smdr_nblk)
{
__smdr = (struct cvmx_shmem_smdr *)
(cvmx_phys_to_ptr(smdr_nblk->base_addr));
cvmx_spinlock_lock (&__smdr->lock);
if (smdr_nblk->size != smdr_size)
{
cvmx_dprintf("SMDR named block is created by another "
"application with different size %lu, "
"expecting %lu \n",
(long unsigned int)smdr_nblk->size, (long unsigned int)smdr_size);
__smdr = NULL;
}
cvmx_spinlock_unlock (&__smdr->lock);
}
}
if (!__smdr)
cvmx_dprintf("cvmx_shmem: Failed to allocate or find SMDR from bootmem \n");
return __smdr;
};
/**
* @INTERNAL
* Generic Iterator function for all SMDR entries
*
* @param void(*f)(dscptr) the function to be invoked for every descriptor
* @param param
*
* @return the descriptor iterator stopped at.
*/
static struct cvmx_shmem_dscptr *__smdr_iterator(struct cvmx_shmem_dscptr *(*f)(struct cvmx_shmem_dscptr *dscptr, void *p), void *param )
{
struct cvmx_shmem_dscptr *d, *dscptr = NULL;
int i;
__CHECK_APP_SMDR;
for (i = 0; i < CVMX_SHMEM_NUM_DSCPTR; i++)
{
d = &__smdr->shmd[i];
if ((dscptr = (*f)(d, param)) != NULL)
break; /* stop iteration */
}
return dscptr;
}
/**
* @INTERNAL
* SMDR name match functor. to be used for iterator.
*
* @param dscptr descriptor passed in by the iterator
* @param name string to match against
*
* @return !NULL descriptor matched
* NULL not match
*/
static struct cvmx_shmem_dscptr *__cvmx_shmem_smdr_match_name(struct cvmx_shmem_dscptr *dscptr, void *name)
{
char *name_to_match = (char *) name;
struct cvmx_shmem_dscptr *ret = NULL;
if (dscptr->owner == CVMX_SHMEM_OWNER_NONE)
return NULL;
if (strcmp(dscptr->name, name_to_match) == 0)
ret = dscptr;
return ret;
}
/**
* @INTERNAL
* Find by name
*
* @param name string to match against
*
* @return !NULL descriptor matched
* NULL not match
*/
static struct cvmx_shmem_dscptr *__cvmx_shmem_smdr_find_by_name(char *name)
{
return __smdr_iterator( __cvmx_shmem_smdr_match_name, name);
}
/**
* @INTERNAL
* SMDR is free functor. to be used for iterator.
*
* @param dscptr descriptor passed in by the iterator
* @param nouse
*
* @return !NULL descriptor is free
* NULL descriptor is not free
*/
static struct cvmx_shmem_dscptr *__cvmx_shmem_smdr_is_free(struct cvmx_shmem_dscptr* dscptr, void *nouse)
{
if (dscptr->owner == CVMX_SHMEM_OWNER_NONE)
return dscptr;
else
return NULL;
}
/**
* @INTERNAL
* Search SMDR to find the first free descriptor
*
* @return !NULL free descriptor found
* NULL nothing found
*/
struct cvmx_shmem_dscptr *__cvmx_shmem_smdr_find_free_dscptr(void)
{
return __smdr_iterator(__cvmx_shmem_smdr_is_free, NULL);
}
/**
* @INTERNAL
* free a descriptor
*
* @param dscptr descriptor to be freed
*/
static void __cvmx_shmem_smdr_free(struct cvmx_shmem_dscptr *dscptr)
{
dscptr->owner = CVMX_SHMEM_OWNER_NONE;
}
/**
* Per core shmem init function
*
* @return cvmx_shmem_smdr* pointer to __smdr
*/
struct cvmx_shmem_smdr *cvmx_shmem_init()
{
return __smdr_init();
}
/**
* Open shared memory based on named block
*
* @return dscptr descriptor of the opened named block
*/
struct cvmx_shmem_dscptr *cvmx_shmem_named_block_open(char *name, uint32_t size, int oflag)
{
const cvmx_bootmem_named_block_desc_t *shmem_nblk = NULL;
struct cvmx_shmem_dscptr *dscptr = NULL;
int nblk_allocated = 0; /* Assume we don't need to allocate a new
bootmem block */
void *vaddr = NULL;
const uint64_t size_4k = 4*1024, size_512mb = 512*1024*1024;
__CHECK_APP_SMDR;
/* Check size, Make sure it is minimal 4K, no bigger than 512MB */
if (size > size_512mb) {
cvmx_dprintf("Shared memory size can not be bigger than 512MB \n");
return NULL;
}
if (size < size_4k)
size = size_4k;
size = __upper_power_of_two(size);
cvmx_spinlock_lock(&__smdr->lock);
shmem_nblk = cvmx_bootmem_find_named_block(name);
if ((shmem_nblk == NULL) && (oflag & CVMX_SHMEM_O_CREAT))
{
void *p;
/* The named block does not exist, create it if caller specifies
the O_CREAT flag */
nblk_allocated = 1;
p = cvmx_bootmem_alloc_named(size, size, name);
if (p)
shmem_nblk = cvmx_bootmem_find_named_block(name);
#ifdef DEBUG
cvmx_dprintf("cvmx-shmem-dbg:"
"creating a new block %s: blk %p, shmem_nblk %p \n",
name, p, shmem_nblk);
#endif
}
if (shmem_nblk == NULL)
goto err;
/* We are now holding a valid named block */
dscptr = __cvmx_shmem_smdr_find_by_name(name);
if (dscptr)
{
if (nblk_allocated)
{
/* name conflict between bootmem name space and SMDR name space */
cvmx_dprintf("cvmx-shmem: SMDR descriptor name conflict, %s \n", name);
goto err;
}
/* Make sure size and alignment matches with existing descriptor */
if ((size != dscptr->size) || (size != dscptr -> alignment))
goto err;
}
else
{
/* Create a new descriptor */
dscptr = __cvmx_shmem_smdr_find_free_dscptr();
if (dscptr)
goto init;
else
{
cvmx_dprintf("cvmx-shmem: SMDR out of descriptors \n");
goto err;
}
}
/* Maintain the reference count */
if (dscptr != NULL)
dscptr->use_count += 1;
cvmx_spinlock_unlock(&__smdr->lock);
return dscptr;
err:
#ifdef DEBUG
cvmx_dprintf("cvmx-shmem-dbg: named block open failed \n");
#endif
if (dscptr)
__cvmx_shmem_smdr_free(dscptr);
if (shmem_nblk && nblk_allocated)
cvmx_bootmem_free_named(name);
cvmx_spinlock_unlock(&__smdr->lock);
return NULL;
init:
#ifdef DEBUG
cvmx_dprintf("cvmx-shmem-dbg: init SMDR descriptor %p \n", dscptr);
#endif
/* Assign vaddr for single address space mapping */
vaddr = __cvmx_shmem_vsbrk_64(size, size);
if (vaddr == NULL) {
/* Failed to allocate virtual address, clean up */
goto err;
}
#ifdef DEBUG
cvmx_dprintf("cmvx-shmem-dbg: allocated vaddr %p \n", vaddr);
#endif
dscptr->vaddr = vaddr;
/* Store descriptor information, name, alignment,size... */
dscptr->owner = cvmx_get_core_num();
dscptr->is_named_block = 1;
dscptr->use_count = 1;
dscptr->name =shmem_nblk->name ;
dscptr->paddr = shmem_nblk->base_addr;
dscptr->size = size;
dscptr->alignment = size;
/* Store permission bits */
if (oflag & CVMX_SHMEM_O_WRONLY)
dscptr->p_wronly = 1;
if (oflag & CVMX_SHMEM_O_RDWR)
dscptr->p_rdwr = 1;
cvmx_spinlock_unlock(&__smdr->lock);
return dscptr;
}
/**
* @INTERNAL
*
* For stand along SE application only.
*
* Add TLB mapping to map the shared memory
*
* @param dscptr shared memory descriptor
* @param pflag protection flags
*
* @return vaddr the virtual address mapped for the shared memory
*/
#ifndef CVMX_BUILD_FOR_LINUX_USER
void *__cvmx_shmem_map_standalone(struct cvmx_shmem_dscptr *dscptr, int pflag)
{
int free_index;
/* Find a free tlb entry */
free_index = cvmx_tlb_allocate_runtime_entry();
if (free_index < 0 )
{
cvmx_dprintf("cvmx-shmem: shmem_map failed, out TLB entries \n");
return NULL;
}
#ifdef DEBUG
cvmx_dprintf("cmvx-shmem-dbg:"
"shmem_map TLB %d: vaddr %p paddr %lx, size %x \n",
free_index, dscptr->vaddr, dscptr->paddr, dscptr->size );
#endif
cvmx_tlb_write_runtime_entry(free_index, CAST64(dscptr->vaddr),
dscptr->paddr, dscptr->size,
TLB_DIRTY | TLB_VALID | TLB_GLOBAL);
return dscptr -> vaddr;
}
#endif
/**
* @INTERNAL
*
* For Linux user application only
*
* Add mmap the shared memory
*
* @param dscptr shared memory descriptor
* @param pflag protection flags
*
* @return vaddr the virtual address mapped for the shared memory
*/
#ifdef CVMX_BUILD_FOR_LINUX_USER
static inline void *__cvmx_shmem_map_linux(struct cvmx_shmem_dscptr *dscptr, int pflag)
{
void *vaddr = NULL;
if(__cvmx_shmem_devmemfd == 0)
{
__cvmx_shmem_devmemfd = open("/dev/mem", O_RDWR);
if (__cvmx_shmem_devmemfd < 0)
{
cvmx_dprintf("Failed to open /dev/mem\n");
exit(-1);
}
}
vaddr = mmap(dscptr->vaddr, dscptr->size, PROT_READ|PROT_WRITE,
MAP_SHARED, __cvmx_shmem_devmemfd, 0);
/* Make sure the mmap maps to the same virtual address specified in
* descriptor
*/
if ((vaddr!=NULL) && (vaddr != dscptr->vaddr))
{
munmap(vaddr, dscptr->size);
vaddr = NULL;
}
return vaddr;
}
#endif
/**
* cvmx_shmem API
*
* Add mapping for the shared memory
*
* @param dscptr shared memory descriptor
* @param pflag protection flags
*
* @return vaddr the virtual address mapped for the shared memory
*/
void *cvmx_shmem_map(struct cvmx_shmem_dscptr *dscptr, int pflag)
{
void *vaddr = NULL;
#ifdef CVMX_BUILD_FOR_LINUX_USER
vaddr = __cvmx_shmem_map_linux(dscptr, pflag);
#else
vaddr = __cvmx_shmem_map_standalone(dscptr, pflag);
#endif
return vaddr;
}
/**
* @INTERNAL
*
* For Linux user application only
*
* ummap the shared memory
*
* @param dscptr shared memory descriptor
*
*/
#ifdef CVMX_BUILD_FOR_LINUX_USER
static inline void __cvmx_shmem_unmap_linux(struct cvmx_shmem_dscptr* dscptr)
{
if (__cvmx_shmem_devmemfd && dscptr)
munmap(dscptr->vaddr, dscptr->size);
}
#endif
/**
* @INTERNAL
*
* For stand along SE application only.
*
* ummap the shared memory
*
* @param dscptr shared memory descriptor
*
*/
#ifndef CVMX_BUILD_FOR_LINUX_USER
static inline void
__cvmx_shmem_unmap_standalone(struct cvmx_shmem_dscptr *dscptr)
{
int index;
index = cvmx_tlb_lookup(CAST64(dscptr->vaddr));
#ifdef DEBUG
cvmx_dprintf("cmvx-shmem-dbg:"
"shmem_unmap TLB %d \n", index);
#endif
cvmx_tlb_free_runtime_entry(index);
}
#endif
/**
* ummap the shared memory
*
* @param dscptr shared memory descriptor
*
*/
void cvmx_shmem_unmap(struct cvmx_shmem_dscptr *dscptr)
{
#ifdef CVMX_BUILD_FOR_LINUX_USER
__cvmx_shmem_unmap_linux(dscptr);
#else
__cvmx_shmem_unmap_standalone(dscptr);
#endif
}
/**
* @INTERNAL
*
* Common implementation of closing a descriptor.
*
* @param dscptr shared memory descriptor
* @param remove 1: remove the descriptor and named block if this
* this is the last user of the descriptor
* 0: do not remove
* @return 0: Success
* !0: Failed
*
*/
static inline int __cvmx_shmem_close_dscptr(struct cvmx_shmem_dscptr *dscptr, int remove)
{
cvmx_spinlock_lock(&dscptr->lock);
if (dscptr->use_count >0)
dscptr->use_count-= 1;
if ((dscptr->use_count == 0) && remove)
{
/* Free this descriptor */
__cvmx_shmem_smdr_free(dscptr);
/* Free named block if this is the last user, and the block
is created by the application */
if (dscptr->is_named_block)
{
#ifdef DEBUG
cvmx_dprintf("cvmx-shmem-dbg: remove named block %s \n", dscptr->name);
#endif
cvmx_bootmem_phy_named_block_free(dscptr->name, 0);
}
}
cvmx_spinlock_unlock(&dscptr->lock);
return 0;
}
/**
* @INTERNAL
*
* For stand along SE application only.
*
* close a descriptor.
*
* @param dscptr shared memory descriptor
* @param remove 1: remove the descriptor and named block if this
* this is the last user of the descriptor
* 0: do not remove
* @return 0: Success
* !0: Failed
*
*/
#ifndef CVMX_BUILD_FOR_LINUX_USER
static inline int __cvmx_shmem_close_standalone(struct cvmx_shmem_dscptr *dscptr, int remove)
{
return __cvmx_shmem_close_dscptr(dscptr, remove);
}
#endif
/**
* @INTERNAL
*
* For Linux user application only.
*
* close a descriptor.
*
* @param dscptr shared memory descriptor
* @param remove 1: remove the descriptor and named block if this
* this is the last user of the descriptor
* 0: do not remove
* @return 0: Success
* !0: Failed
*
*/
#ifdef CVMX_BUILD_FOR_LINUX_USER
int __cvmx_shmem_close_linux(struct cvmx_shmem_dscptr *dscptr, int remove)
{
int ret;
ret = __cvmx_shmem_close_dscptr(dscptr, remove);
if (ret && __cvmx_shmem_devmemfd)
{
close(__cvmx_shmem_devmemfd);
__cvmx_shmem_devmemfd=0;
}
return ret;
}
#endif
/**
*
* close a descriptor.
*
* @param dscptr shared memory descriptor
* @param remove 1: remove the descriptor and named block if this
* this is the last user of the descriptor
* 0: do not remove
* @return 0: Success
* !0: Failed
*
*/
int cvmx_shmem_close(struct cvmx_shmem_dscptr *dscptr, int remove)
{
int ret;
#ifdef CVMX_BUILD_FOR_LINUX_USER
ret = __cvmx_shmem_close_linux(dscptr, remove);
#else
ret = __cvmx_shmem_close_standalone(dscptr, remove);
#endif
return ret;
}
#ifdef DEBUG
/**
* @INTERNAL
* SMDR non-free descriptor dump functor. to be used for iterator.
*
* @param dscptr descriptor passed in by the iterator
*
* @return NULL always
*/
static struct cvmx_shmem_dscptr *__cvmx_shmem_smdr_display_dscptr(struct cvmx_shmem_dscptr *dscptr, void *nouse)
{
if ((dscptr != NULL ) && (dscptr -> owner != CVMX_SHMEM_OWNER_NONE))
{
cvmx_dprintf(" %s: phy: %lx, size %d, alignment %lx, virt %p use_count %d\n",
dscptr->name, dscptr-> paddr,
dscptr->size, dscptr-> alignment,
dscptr->vaddr, dscptr->use_count);
}
return NULL;
}
#endif
/**
* SMDR descriptor show
*
* list all non-free descriptors
*/
void cvmx_shmem_show(void)
{
__CHECK_APP_SMDR;
#ifdef DEBUG
cvmx_dprintf("SMDR descriptor list: \n");
cvmx_spinlock_lock(&__smdr->lock);
__smdr_iterator(__cvmx_shmem_smdr_display_dscptr, NULL);
cvmx_spinlock_unlock(&__smdr->lock);
cvmx_dprintf("\n\n");
#endif
}