blob: 7789580ee3d16dfa67da1c2960722fbea9f43678 [file] [log] [blame]
/*
* Copyright (C) 2018 Synaptics Incorporated. All rights reserved.
* (C) Copyright Marvell Semiconductors,Inc 2006 All rightes reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND
* SYNAPTICS EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE, AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY
* INTELLECTUAL PROPERTY RIGHTS. IN NO EVENT SHALL SYNAPTICS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, PUNITIVE, OR
* CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION WITH THE USE
* OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED AND
* BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF
* COMPETENT JURISDICTION DOES NOT PERMIT THE DISCLAIMER OF DIRECT
* DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' TOTAL CUMULATIVE LIABILITY
* TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. DOLLARS.
*/
#include <stdint.h>
#include "util.h"
#include "ctypes.h"
#include "string.h"
#include "debug.h"
#include "apb_perif_base.h"
#include "lgpl_printf.h"
/*
* retrieve top and bottom of heap
*/
extern int __heap_start, __heap_end;
inline void * __user_read_heap_bottom(void)
{
return &__heap_start;
}
inline void * __user_read_heap_top(void)
{
return &__heap_end;
}
/*
* heap start point
*/
void * UtilMemSet(void *s, int c, int n)
{
unsigned char * char_ptr = s;
unsigned int * int_ptr = s;
int remainer;
if((remainer = ((uintmax_t)s & 3)) != 0) {
while (remainer-- > 0) {
*char_ptr++ = (unsigned char)c;
n--;
}
int_ptr = (unsigned int *)char_ptr;
remainer = n & 3;
}
n >>= 2;
while (n-- > 0) {
*int_ptr++ = (unsigned int)c;
}
if (remainer != 0) {
unsigned int temp = *int_ptr;
unsigned char * temp_p = (unsigned char *)&temp;
while (remainer-- > 0) {
*temp_p++ = (unsigned char)c;
}
*int_ptr = temp;
}
return(s);
}
void * UtilMemCpy(void *s1, const void *s2, int n)
{
unsigned int * dst = s1;
const unsigned int * src = s2;
int remainer = n & 3;
if (((uintmax_t)s1 & 3) || ((uintmax_t)s2 & 3)) {
unsigned char * dst_char = s1;
const unsigned char * src_char = s2;
while (n-- > 0) {
*dst_char++ = *src_char++;
}
return s1;
}
n >>= 2;
while (n-- > 0) {
*dst++ = *src++;
}
if (remainer != 0) {
unsigned int temp = *dst;
unsigned char * temp_p = (unsigned char *)&temp;
const unsigned char * src_c = (const unsigned char *)src;
while (remainer-- > 0) {
*temp_p++ = (unsigned char)*src_c++;
}
*dst = temp;
}
return(s1);
}
int UtilMemCmp(const void *s1, const void *s2, int n)
{
int i = 0;
const unsigned char * ss1 = s1;
const unsigned char * ss2 = s2;
for ( ; i < n; i++) {
if (ss1[i] > ss2[i]) {
return(i + 1);
}
else if (ss1[i] < ss2[i]) {
return(-i - 1);
}
}
return 0;
}
typedef struct _mem_node{
void * ptr;
unsigned int size;
int is_used;
}mem_node;
#define MAX_ALLOC_NUM 128
static mem_node mem_table[MAX_ALLOC_NUM] = {{0}};
static int allocator_initialized = 0;
extern int __heap_start;
extern int __heap_end;
#define HEAP_START ((uintmax_t)(&__heap_start))
#define HEAP_END ((uintmax_t)(&__heap_end))
#define DCACHE_LINE_LEN 0x40
#define CACHE_MASK (0xFFFFFFFF&~(DCACHE_LINE_LEN - 1))
#define ALIGNED(addr) ((addr&CACHE_MASK) + DCACHE_LINE_LEN)
static void init_allocator(void)
{
UtilMemSet(&mem_table[0], 0, sizeof(mem_node)*MAX_ALLOC_NUM);
mem_table[0].ptr = (void *)ALIGNED(HEAP_START);// allocate addr always DCACHE_LINE_LEN alligned.
mem_table[0].size = ALIGNED(HEAP_END) - DCACHE_LINE_LEN - ALIGNED(HEAP_START);
mem_table[0].is_used = 0;
allocator_initialized = 1;
}
static mem_node * find_available_mem_node(void)
{
int i;
for (i = 0; i < MAX_ALLOC_NUM; i++) {
if (mem_table[i].ptr == NULL) {
return &mem_table[i];
}
}
return NULL;
}
void *malloc(size_t size)
{
if (allocator_initialized == 0) {
init_allocator();
}
if (size == 0) {
return 0;
}
int i;
size_t size_aligned = ALIGNED(size);
for (i = 0; i < MAX_ALLOC_NUM; i++) {
if (mem_table[i].is_used == 0 && mem_table[i].size >= size_aligned) {
mem_node *buddy = find_available_mem_node();
if (buddy != NULL) {
buddy->ptr = mem_table[i].ptr + size_aligned;
buddy->size = mem_table[i].size - size_aligned;
}
mem_table[i].size = size_aligned;
mem_table[i].is_used = 1;
return mem_table[i].ptr;
}
}
return 0;
}
void * UtilMemAllocZ(int size)
{
void *ptr = malloc(size);
if (ptr != NULL) {
UtilMemSet(ptr, 0, size);
}
return ptr;
}
static mem_node *find_left_buddy(mem_node *curr)
{
int i;
if(curr) {
for (i = 0; i < MAX_ALLOC_NUM; i++) {
if((mem_table[i].ptr + mem_table[i].size) == curr->ptr) {
return &mem_table[i];
}
}
}
return NULL;
}
static mem_node * merge_left_buddy(mem_node *l_buddy, mem_node *curr)
{
l_buddy->size += curr->size;
UtilMemSet(curr, 0, sizeof(mem_node));
return l_buddy;
}
static mem_node *find_right_buddy(mem_node * curr)
{
int i;
if(curr) {
for (i = 0; i < MAX_ALLOC_NUM; i++) {
if(mem_table[i].ptr == curr->ptr + curr->size) {
return &mem_table[i];
}
}
}
return NULL;
}
static mem_node * merge_right_buddy(mem_node *r_buddy, mem_node *curr)
{
curr->size += r_buddy->size;
UtilMemSet(r_buddy, 0, sizeof(mem_node));
return curr;
}
static void merge_buddy(mem_node *free_node)
{
mem_node *l_buddy, *r_buddy, *curr = free_node;
l_buddy = find_left_buddy(curr);
while (l_buddy != NULL && l_buddy->is_used == 0) {
curr = merge_left_buddy(l_buddy, curr);
l_buddy = find_left_buddy(curr);
}
r_buddy = find_right_buddy(curr);
while (r_buddy != NULL && r_buddy->is_used == 0) {
curr = merge_right_buddy(r_buddy, curr);
r_buddy = find_right_buddy(curr);
}
}
void free(void *ptr)
{
if (ptr == NULL) {
return;
}
int i;
for (i = 0; i < MAX_ALLOC_NUM; i++) {
if (mem_table[i].ptr == ptr) {
mem_table[i].is_used = 0;
merge_buddy(&mem_table[i]);
return;
}
}
}
void UtilMemReset(void)
{
allocator_initialized = 0;
}
void * calloc(size_t nmemb, size_t size)
{
void *ptr = UtilMemAllocZ(nmemb*size);
return ptr;
}
#define DBG_MEM (0x3000000)
#define DBG_PORT_BASE (APB_UART0_BASE)
#define PUT_CHAR(ch) BFM_HOST_Bus_Write32((DBG_PORT_BASE+0x00), (UNSG32)ch)
#define put_char PUT_CHAR
#define MAX_HISBUF_ENTRY 16
static char his_buf[MAX_HISBUF_ENTRY][80]; // for Historical commands support
static SIGN32 his_mode=0;
SIGN32 his_next_idx=0;
SIGN32 his_cur_idx=0;
SIGN32 his_cmd_cnt=0;
SIGN32 pre_his_cmd_length;
unsigned char GET_CHAR()
{
UNSG32 data32;
unsigned char data8;
BFM_HOST_Bus_Read32((DBG_PORT_BASE+0x00), &data32);
data8=data32&0xFF;
return data8;
}
////////////////////////////////////////////////////////////
// dbg port (uart) driver
SIGN32 dbg_port_tx_ready()
{
UNSG32 status=0;
do
{
BFM_HOST_Bus_Read32((DBG_PORT_BASE+0x7c),&status);
} while ((status&0x03)!=0x02);
// add extra protection below
do
{
BFM_HOST_Bus_Read32((DBG_PORT_BASE+0x80),&status);
} while (status>2); //delay control
return 1;
}
/////////////////////////////////////////////////////////////////////////
//
// showDbgPrompt() - Used by the debugger. Displays the appropriate
// debugger prompt.
//
/////////////////////////////////////////////////////////////////////////
void showDbgPrompt(char* prompt)
{
lgpl_printf("\n\n%s", prompt);
}
SIGN32 dbg_port_rx_ready()
{
UNSG32 read;
BFM_HOST_Bus_Read32((DBG_PORT_BASE+0x7c),&read); // status
if(read&0x08)
{
BFM_HOST_Bus_Read32((DBG_PORT_BASE+0x84),&read); // RFL
if (read>0)
return 1;
}
return 0;
}
void dbg_port_print( char* str)
{
char ch;
SIGN32 i;
SIGN32 len=strlen(str);
dbg_port_tx_ready();
for (i=0;i<len;i++)
{
ch=str[i];
put_char(ch);
if (ch=='\n')
{
ch='\r';
put_char(ch);
}
//extra delay
i=i;
len=len;
ch=str[i];
}
}
//////////////////////////////////////////////////////
// read command via UART (RS232)
#define CR_CHAR 13
#define LF_CHAR 10
#define BS_CHAR 8
#define UP_CHAR '.'
#define DIR1_CHAR 0x1B
#define DIR2_CHAR '['
#define UP3_CHAR 'A'
#define DOWN3_CHAR 'B'
int getline(char *pCmd)
{
unsigned char ch;
SIGN32 i;
#if 0
SIGN32 comment;
#endif
unsigned char arrow = 0;
int input_cmd_line_length = 0 ;
for (i = 0; i < CMD_LINE_BUFFER_SIZE; i++)
pCmd[i] = 0;
i = 0;
input_cmd_line_length = 0 ;
while (1)
{
while (dbg_port_rx_ready() == 0);
ch = GET_CHAR();
arrow += 2;
input_cmd_line_length++ ;
if(input_cmd_line_length>CMD_LINE_BUFFER_SIZE){
lgpl_printf("cmd line overflow, please input the command line again\n") ;
return 1 ;
}
switch (ch)
{
case CR_CHAR:
case LF_CHAR:
case '\0':
pCmd[i] = '\0';
#if 0
comment = 0;
if (his_mode == 0 && strlen(pCmd))
{ // only store new command that is not null
memset(his_buf[his_cur_idx], 0, 80); // clear buf
memcpy(his_buf[his_cur_idx], pCmd, strlen(pCmd));
if (his_cur_idx < MAX_HISBUF_ENTRY - 1)
{
his_next_idx = his_cur_idx;
his_cur_idx++;
}
else
{
his_next_idx = MAX_HISBUF_ENTRY - 1;
his_cur_idx = 0;
}
his_cmd_cnt = (his_cmd_cnt < MAX_HISBUF_ENTRY) ? (his_cmd_cnt + 1) : MAX_HISBUF_ENTRY;
}
his_mode = 0;
#endif
return 0 ;
#if 0
case ';':
case '/':
comment = 1;
break;
case BS_CHAR:
i--;
i--;
PUT_CHAR(ch);
PUT_CHAR(' ');
PUT_CHAR(ch);
his_mode = 0; // modify historical command
arrow = 0;
break;
#endif
default:
pCmd[i] = ch ;
// PUT_CHAR(ch);
PUT_CHAR('*');
#if 0
if (comment == 0)
{
pCmd[i] = ch;
if (ch == DIR1_CHAR)
arrow = 1;
else if (ch == DIR2_CHAR && arrow == 3)
arrow = arrow;
else if (ch == UP3_CHAR && arrow == 5)
{
arrow = 0;
PUT_CHAR(DIR1_CHAR);
PUT_CHAR(DIR2_CHAR);
PUT_CHAR(DOWN3_CHAR);
i = previous_cmd(pCmd, i, 0);
}
else if (ch == DOWN3_CHAR && arrow == 5)
{
arrow = 0;
i = next_cmd(pCmd, i);
}
else
{
his_mode = 0; // modify history command
arrow = 0;
}
}
#endif
break;
}
i++;
}
}
int previous_cmd(char *pCmd, int cur_cmd_length, unsigned int loop)
{
int i, j;
int erase;
if (his_cmd_cnt == 0)
return (cur_cmd_length - 3);
if (his_mode == 0)
{
his_mode = 1;
}
else
{
if (loop == 0)
{
if (his_next_idx == his_cur_idx + 1)
return (cur_cmd_length - 3);
if ((his_next_idx == 0) && (his_cur_idx == his_cmd_cnt))
return (cur_cmd_length - 3);
}
if (his_next_idx == 0)
his_next_idx = his_cmd_cnt - 1;
else
his_next_idx--;
}
// need to clear line, move cursor, and print prompt again
erase = strlen(PROMPT) + (pre_his_cmd_length > cur_cmd_length ? pre_his_cmd_length : cur_cmd_length);
put_char('\r');
for (j = 0; j <= erase ; j++)
{
put_char(' ');
}
put_char('\r'); // back to beginning of line
dbg_port_print(PROMPT); // show prompt
dbg_port_print(his_buf[his_next_idx]);
memcpy(pCmd, his_buf[his_next_idx], strlen(his_buf[his_next_idx]));
i = strlen(his_buf[his_next_idx]) - 1;
pre_his_cmd_length = i; // record the his command length
return i;
}
int next_cmd(char *pCmd, int cur_cmd_length)
{
int i, j;
int erase;
his_mode = 2;
if (his_cur_idx == his_next_idx + 1)
return (cur_cmd_length - 3);
if ((his_cur_idx == 0) && (his_next_idx == his_cmd_cnt))
return (cur_cmd_length - 3);
if (his_next_idx == his_cmd_cnt - 1)
his_next_idx = 0;
else
his_next_idx++;
// need to clear line, move cursor, and print prompt again
erase = strlen(PROMPT) + (pre_his_cmd_length > cur_cmd_length ? pre_his_cmd_length : cur_cmd_length);
put_char('\r');
for (j = 0; j <= erase; j++)
{
put_char(' ');
}
put_char('\r'); // back to beginning of line
dbg_port_print(PROMPT); // show prompt
dbg_port_print(his_buf[his_next_idx]);
memcpy(pCmd, his_buf[his_next_idx], strlen(his_buf[his_next_idx]));
i = strlen(his_buf[his_next_idx]) - 1;
pre_his_cmd_length = i; // record the his command length
return i;
}
/////////////////////////////////////////////////////////////////////////
//
// skipSpace() - Used by the debugger. Skips non-text characters at
// the beginning of a string
//
/////////////////////////////////////////////////////////////////////////
void skipSpace ( char **inStr )
{
char *s = *inStr;
while (( *s == ' ' ) || ( *s == '\t' ) || ( *s == '\r' ) || ( *s == '\n' ))
s++;
*inStr = s;
}
/////////////////////////////////////////////////////////////////////////
//
// getTokenLength() - Used by the debugger. Gets the length of the
// next token in a string
//
/////////////////////////////////////////////////////////////////////////
SIGN32 getTokenLength (char *s )
{
SIGN32 index;
char x;
for ( index = 0; s[ index] != 0; index++ )
{
x = s[ index];
if (( x == ' ' ) || ( x == '\t' ) || ( x == '\r' ) || ( x == '\n' ))
break;
}
return( index );
}
/////////////////////////////////////////////////////////////////////////
//
// getIntToken() - Used by the debugger Gets the next token from a string
//
/////////////////////////////////////////////////////////////////////////
char* getIntToken ( char *inStr, UNSG32 *outVal )
{
SIGN32 iSum = 0;
char c;
char *thisStr = inStr;
SIGN32 bPositive = 1;
// If first character is ';', we have a comment, so
// get out of here.
if ( *thisStr == ';' )
return thisStr;
// Process decimal characters
// a negative number?
if ( *thisStr == '-' )
{
bPositive =0;
thisStr ++;
}
while ( *thisStr )
{
c = *thisStr;
// Check for valid digits.
if ( !( c >= '0' && c <= '9' ) )
break;
// ASCII to decimal conversion
iSum = iSum * 10 + ( c - '0' );
thisStr ++;
}
// make it negative
if ( !bPositive )
iSum = -1 * iSum;
// Return updated pointer and decoded value.
*outVal = iSum;
return thisStr;
}
/////////////////////////////////////////////////////////////////////////
//
// getSignedIntToken() - Used by the debugger Gets the next token from a string
//
/////////////////////////////////////////////////////////////////////////
char* getSignedIntToken ( char *inStr, SIGN32 *outVal )
{
SIGN32 iSum = 0;
char c;
char *thisStr = inStr;
SIGN32 bPositive = 1;
// If first character is ';', we have a comment, so
// get out of here.
if ( *thisStr == ';' )
return thisStr;
// Process decimal characters
// a negative number?
if ( *thisStr == '-' )
{
bPositive =0;
thisStr ++;
}
while ( *thisStr )
{
c = *thisStr;
// Check for valid digits.
if ( !( c >= '0' && c <= '9' ) )
break;
// ASCII to decimal conversion
iSum = iSum * 10 + ( c - '0' );
thisStr ++;
}
// make it negative
if ( !bPositive )
iSum = -1 * iSum;
// Return updated pointer and decoded value.
*outVal = iSum;
return thisStr;
}
/////////////////////////////////////////////////////////////////////////
//
// getHexToken() - Used by the debugger Gets the next token from a string
//
/////////////////////////////////////////////////////////////////////////
char* getHexToken ( char *inStr, UNSG32 *outVal )
{
UNSG32 thisVal, tVal;
char x;
char *thisStr = inStr;
thisVal = 0;
// If first character is ';', we have a comment, so
// get out of here.
if ( *thisStr == ';' )
return( thisStr );
// Process hex characters.
while ( *thisStr )
{
// Do uppercase conversion if necessary.
x = *thisStr;
if (( x >= 'a') && ( x <= 'f'))
x &= ~0x20;
// Check for valid digits.
if ( !((( x >= '0') && (x <= '9')) || (( x >= 'A') && ( x <= 'F'))))
break;
// Hex ASCII to binary conversion.
tVal = x - '0';
if ( tVal > 9 )
tVal -= 7;
thisVal = (thisVal * 16) + tVal;
thisStr++;
}
// Return updated pointer and decoded value.
*outVal = thisVal;
return( thisStr );
}
/////////////////////////////////////////////////////////////////////////
//
// getToken() - Used by the debugger Gets the next token from a string
//
/////////////////////////////////////////////////////////////////////////
char* getToken ( char *inStr, char **outVal )
{
char *thisStr = inStr;
// If first character is ';', we have a comment, so
// get out of here.
if ( *thisStr == ';' )
return( thisStr );
while ( *thisStr != ' ' && *thisStr != '\t' && *thisStr != '\0' )
{
thisStr++;
}
if ( *thisStr != '\0' )
{
*thisStr = '\0';
thisStr ++;
}
*outVal = inStr;
return( thisStr );
}
// Qingwei Huang added for google's toolchain
int raise(int i)
{
(void)i;
return 0;
}
#ifndef CPUPLL
#define CPUPLL 1000 //MHz
#endif
unsigned berlin_delay_us(unsigned us)
{
register unsigned counter = 0;
register unsigned cycles = us * CPUPLL; // 25 cycles / us
register unsigned increment = 3; // looping 3 cycles
// assuming 1 cycle/instruction
// the looping is registers ADD + CMP + Branch = 3 instructions
// e.g., delay 1us needs 25 cycles take 9 loops
while (counter < cycles) {
counter += increment;
}
return counter;
}