lineage_kernel_xcoverpro/drivers/misc/samsung/scsc/miframman.c

289 lines
7.5 KiB
C
Executable File

/****************************************************************************
*
* Copyright (c) 2014 - 2016 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <scsc/scsc_logring.h>
#include "scsc_mif_abs.h"
#include "scsc/api/bt_audio.h"
#include "miframman.h"
#include "mifproc.h"
/* Caller should provide locking */
void miframman_init(struct miframman *ram, void *start_dram, size_t size_pool, void *start_region)
{
mutex_init(&ram->lock);
SCSC_TAG_INFO(MIF, "MIFRAMMAN_BLOCK_SIZE = %d\n", MIFRAMMAN_BLOCK_SIZE);
ram->num_blocks = size_pool / MIFRAMMAN_BLOCK_SIZE;
if (ram->num_blocks == 0) {
SCSC_TAG_ERR(MIF, "Pool size < BLOCK_SIZE\n");
return;
}
if (ram->num_blocks >= MIFRAMMAN_NUM_BLOCKS) {
SCSC_TAG_ERR(MIF, "Not enough memory\n");
return;
}
memset(ram->bitmap, BLOCK_FREE, sizeof(ram->bitmap));
ram->start_region = start_region; /* For monitoring purposes only */
ram->start_dram = start_dram;
ram->size_pool = size_pool;
ram->free_mem = ram->num_blocks * MIFRAMMAN_BLOCK_SIZE;
mifproc_create_ramman_proc_dir(ram);
}
void miframabox_init(struct mifabox *mifabox, void *start_aboxram)
{
/* No locking as not a shared resource */
mifabox->aboxram = (struct scsc_bt_audio_abox *)start_aboxram;
}
void *__miframman_alloc(struct miframman *ram, size_t nbytes, int tag)
{
unsigned int index = 0;
unsigned int mem_itr_index = 0;
unsigned int available;
unsigned int i;
unsigned int min_available_blocks = MIFRAMMAN_NUM_BLOCKS;
size_t num_blocks = 0;
void *free_mem = NULL;
bool has_available_blocks = false;
if (!nbytes || nbytes > ram->free_mem) {
SCSC_TAG_INFO(MIF, "nbytes > ram->free_mem");
goto end;
}
/* Number of blocks required (rounding up) */
num_blocks = nbytes / MIFRAMMAN_BLOCK_SIZE +
((nbytes % MIFRAMMAN_BLOCK_SIZE) > 0 ? 1 : 0);
if (num_blocks > ram->num_blocks) {
SCSC_TAG_INFO(MIF, "num_blocks > ram->num_blocks\n");
goto end;
}
/* Iterate through whole memory and find shortest contiguous memory region
* from where allocation can be made
*/
while (mem_itr_index <= (ram->num_blocks - num_blocks)) {
available = 0;
/* Search for next consecutive block */
for (i = 0; i < (ram->num_blocks - mem_itr_index); i++) {
if (ram->bitmap[i + mem_itr_index] != BLOCK_FREE)
break;
available++;
}
if (available >= num_blocks) {
has_available_blocks = true;
if (available < min_available_blocks) {
min_available_blocks = available;
index = mem_itr_index;
}
}
mem_itr_index = mem_itr_index + available + 1;
}
/* If we have found the memory we need */
if (has_available_blocks) {
free_mem = ram->start_dram +
MIFRAMMAN_BLOCK_SIZE * index;
/* Mark the block boundary as used */
ram->bitmap[index] = BLOCK_BOUND;
ram->bitmap[index] |= (u8)(tag << MIFRAMMAN_BLOCK_OWNER_SHIFT); /* Add owner tack for tracking */
index++;
/* Additional blocks in this allocation */
for (i = 1; i < num_blocks; i++) {
ram->bitmap[index] = BLOCK_INUSE;
ram->bitmap[index] |= (u8)(tag << MIFRAMMAN_BLOCK_OWNER_SHIFT); /* Add owner tack for tracking */
index++;
}
ram->free_mem -= num_blocks * MIFRAMMAN_BLOCK_SIZE;
goto exit;
}
end:
SCSC_TAG_INFO(MIF, "Not enough shared memory (nbytes %zd, free_mem %u, num_blocks %zd, ram_num_blocks %u)\n",
nbytes, ram->free_mem, num_blocks, ram->num_blocks);
return NULL;
exit:
return free_mem;
}
#define MIFRAMMAN_ALIGN(mem, align) \
((void *)((((uintptr_t)(mem) + (align + sizeof(void *))) \
& (~(uintptr_t)(align - 1)))))
#define MIFRAMMAN_PTR(mem) \
(*(((void **)((uintptr_t)(mem) & \
(~(uintptr_t)(sizeof(void *) - 1)))) - 1))
/*
* Allocate shared DRAM block
*
* Parameters:
* ram - pool identifier
* nbytes - allocation size
* align - allocation alignment
* tag - owner identifier (typically service ID), 4 bits.
*
* Returns
* Pointer to allocated area, or NULL
*/
void *miframman_alloc(struct miframman *ram, size_t nbytes, size_t align, int tag)
{
void *mem, *align_mem = NULL;
mutex_lock(&ram->lock);
if (!is_power_of_2(align) || nbytes == 0) {
SCSC_TAG_ERR(MIF, "Failed size/alignment check (nbytes %zd, align %zd)\n", nbytes, align);
goto end;
}
if (align < sizeof(void *))
align = sizeof(void *);
mem = __miframman_alloc(ram, nbytes + align + sizeof(void *), tag);
if (!mem)
goto end;
align_mem = MIFRAMMAN_ALIGN(mem, align);
/* Store allocated pointer */
MIFRAMMAN_PTR(align_mem) = mem;
end:
mutex_unlock(&ram->lock);
return align_mem;
}
/*
* Free shared DRAM block
*
* Parameters:
* ram - pool identifier
* mem - buffer to free
*/
void __miframman_free(struct miframman *ram, void *mem)
{
unsigned int index, num_blocks = 0;
if (ram->start_dram == NULL || !mem) {
SCSC_TAG_ERR(MIF, "Mem is NULL\n");
return;
}
/* Get block index */
index = (unsigned int)((mem - ram->start_dram)
/ MIFRAMMAN_BLOCK_SIZE);
/* Check */
if (index >= ram->num_blocks) {
SCSC_TAG_ERR(MIF, "Incorrect index %d\n", index);
return;
}
/* Check it is a Boundary block */
if ((ram->bitmap[index] & MIFRAMMAN_BLOCK_STATUS_MASK) != BLOCK_BOUND) {
SCSC_TAG_ERR(MIF, "Incorrect Block descriptor\n");
return;
}
ram->bitmap[index++] = BLOCK_FREE;
/* Free remaining blocks */
num_blocks++;
while (index < ram->num_blocks && (ram->bitmap[index] & MIFRAMMAN_BLOCK_STATUS_MASK) == BLOCK_INUSE) {
ram->bitmap[index++] = BLOCK_FREE;
num_blocks++;
}
ram->free_mem += num_blocks * MIFRAMMAN_BLOCK_SIZE;
}
void miframman_free(struct miframman *ram, void *mem)
{
mutex_lock(&ram->lock);
/* Restore allocated pointer */
if (mem)
__miframman_free(ram, MIFRAMMAN_PTR(mem));
mutex_unlock(&ram->lock);
}
/* Caller should provide locking */
void miframman_deinit(struct miframman *ram)
{
/* Mark all the blocks as INUSE (by Common) to prevent new allocations */
memset(ram->bitmap, BLOCK_INUSE, sizeof(ram->bitmap));
ram->num_blocks = 0;
ram->start_dram = NULL;
ram->size_pool = 0;
ram->free_mem = 0;
mifproc_remove_ramman_proc_dir(ram);
}
void miframabox_deinit(struct mifabox *mifabox)
{
/* not dynamic - so just mark as NULL */
/* Maybe this function should be empty? */
mifabox->aboxram = NULL;
}
/* Log current allocations in a ramman in proc */
void miframman_log(struct miframman *ram, struct seq_file *fd)
{
unsigned int b;
unsigned int i;
int tag;
size_t num_blocks = 0;
if (!ram)
return;
seq_printf(fd, "ramman: start_dram %p, size %zd, free_mem %u\n\n",
ram->start_region, ram->size_pool, ram->free_mem);
for (b = 0; b < ram->num_blocks; b++) {
if ((ram->bitmap[b] & MIFRAMMAN_BLOCK_STATUS_MASK) == BLOCK_BOUND) {
/* Found a boundary allocation */
num_blocks++;
tag = (ram->bitmap[b] & MIFRAMMAN_BLOCK_OWNER_MASK) >> MIFRAMMAN_BLOCK_OWNER_SHIFT;
/* Count subsequent blocks in this group */
for (i = 1;
i < ram->num_blocks && (ram->bitmap[b + i] & MIFRAMMAN_BLOCK_STATUS_MASK) == BLOCK_INUSE;
i++) {
/* Check owner matches boundary block */
int newtag = (ram->bitmap[b + i] & MIFRAMMAN_BLOCK_OWNER_MASK) >> MIFRAMMAN_BLOCK_OWNER_SHIFT;
if (newtag != tag) {
seq_printf(fd, "Allocated block tag %d doesn't match boundary tag %d, index %d, %p\n",
newtag, tag, b + i,
ram->start_dram + (b + i) * MIFRAMMAN_BLOCK_SIZE);
}
num_blocks++;
}
seq_printf(fd, "index %8d, svc %d, bytes %12d, blocks %10d, %p\n",
b, tag,
(i * MIFRAMMAN_BLOCK_SIZE),
i,
ram->start_dram + (b * MIFRAMMAN_BLOCK_SIZE));
}
}
}