blob: af52f9004ad97280f1cf2324bc642dc0b03219d5 [file] [log] [blame] [edit]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
/*
* Copyright (C) 2014-2017 Amlogic, Inc. All rights reserved.
*
* All information contained herein is Amlogic confidential.
*
* This software is provided to you pursuant to Software License Agreement
* (SLA) with Amlogic Inc ("Amlogic"). This software may be used
* only in accordance with the terms of this agreement.
*
* Redistribution and use in source and binary forms, with or without
* modification is strictly prohibited without prior written permission from
* Amlogic.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <serial.h>
#include <io.h>
#include <stdint.h>
#include "platform_def.h"
#include "regs.h"
static int g_serial_disable;
int serial_disable(void)
{
g_serial_disable = 1;
return 0;
}
void __attribute__((weak)) plat_serial_setup(void)
{
serial_init(0);
}
int serial_tx_is_empty(void)
{
return (readl(P_UART_STATUS(UART_PORT_CONS)) & UART_STAT_MASK_TFIFO_EMPTY);
}
void serial_init(unsigned set)
{
/*
* Set UART MODE:
*
* - Stop bit = 1 bit
* - Parity check = disable
* - Character lengther = 8bit
* - TX, RX enable
* - Hold reset signals
*/
writel(0 | UART_STP_BIT | UART_PRTY_BIT | UART_CHAR_LEN
| UART_MODE_MASK_TX_EN | UART_MODE_MASK_RX_EN
| UART_MODE_RESET_MASK | UART_MODE_MASK_TWO_WIRE
, P_UART_MODE(UART_PORT_CONS));
if (set == 1) {
/*
* Set UART Baud rate = 115200
* - Use 24M XTAL clock /2 as ref clock
* - Enable New baud rete setting
* - XTAL clock sel = XTAL / 2
* - 104 = (24,000,000 / 2) / 115200 (Error = +0.16%)
*/
writel(readl(REG_MDUMP_TIMEBASE_CNTL) | BIT(0), REG_MDUMP_TIMEBASE_CNTL);
writel(104 | UART_CTRL_USE_XTAL_CLK | UART_CTRL_USE_NEW_BAUD_RATE
| UART_CTRL_XTAL_CLK_DIV2
, P_UART_CTRL(UART_PORT_CONS));
} else {
/*
* Set UART Baud rate = 921600
* - Use 24M XTAL clock /2 as ref clock
* - Enable New baud rete setting
* - XTAL clock sel = XTAL (1)
* - 13 = 12,000,000 / 921600 (Error = +0.16%)
*/
writel(readl(REG_MDUMP_TIMEBASE_CNTL) | BIT(0), REG_MDUMP_TIMEBASE_CNTL);
writel(13 | UART_CTRL_USE_XTAL_CLK | UART_CTRL_USE_NEW_BAUD_RATE
| UART_CTRL_XTAL_CLK_DIV2
, P_UART_CTRL(UART_PORT_CONS));
}
/*
* Enable pinmux
*/
setbits_le32(REG_MDUMP_SERIAL_PINMUX, 0x11);
/*
* Release UART reset signals, enable UART
*/
clrbits_le32(P_UART_MODE(UART_PORT_CONS), UART_MODE_RESET_MASK);
}
/*
* Read a single byte from the serial port. Returns 1 on success, 0
* otherwise 0.
*/
int serial_tstc(void)
{
return (readl(P_UART_STATUS(UART_PORT_CONS)) & UART_STAT_MASK_RFIFO_CNT);
}
int serial_putc(int c)
{
if (0)
return c;
if (c == '\n') {
while ((readl(P_UART_STATUS(UART_PORT_CONS)) & UART_STAT_MASK_TFIFO_FULL)) {
};
writel('\r', P_UART_WFIFO(UART_PORT_CONS));
}
/* Wait till dataTx register is not full */
while ((readl(P_UART_STATUS(UART_PORT_CONS)) & UART_STAT_MASK_TFIFO_FULL)) {
};
writel(c, P_UART_WFIFO(UART_PORT_CONS));
/* Wait till dataTx register is empty */
return c;
}
int serial_getc(void)
{
unsigned char ch = 0;
if (g_serial_disable)
return ch;
/* Wait till character is placed in fifo */
while ((readl(P_UART_STATUS(UART_PORT_CONS)) & UART_STAT_MASK_RFIFO_CNT) == 0) {
};
/* Also check for overflow errors */
if (readl(P_UART_STATUS(UART_PORT_CONS)) &
(UART_STAT_MASK_PRTY_ERR | UART_STAT_MASK_FRAM_ERR)) {
setbits_le32(P_UART_CONTROL(UART_PORT_CONS), UART_CNTL_MASK_CLR_ERR);
clrbits_le32(P_UART_CONTROL(UART_PORT_CONS), UART_CNTL_MASK_CLR_ERR);
}
ch = readl(P_UART_RFIFO(UART_PORT_CONS)) & 0x00ff;
return ((int)ch);
}
int serial_puts(const char *s)
{
while (*s)
serial_putc(*s++);
return 0;
}
void serial_put_hex(unsigned long data, unsigned int bitlen)
{
int i;
unsigned char s;
for (i = bitlen - 4; i >= 0; i -= 4) {
s = (data >> i) & 0xf;
if (s < 10)
serial_putc(0x30 + s);
else if (s < 16)
serial_putc(0x61 + s - 10);
}
}
void serial_put_dec(unsigned long data)
{
int i = 0;
char buff[32];
do {
buff[i++] = (data % 10) + 0x30;
data = data / 10;
} while (data && (i < (sizeof(buff) / sizeof(buff[0]))));
for (--i; i >= 0; --i)
serial_putc(buff[i]);
}
void bl2_print(const char *info, unsigned int value, unsigned int is_dec, const char *info2)
{
if (info)
serial_puts(info);
if (is_dec)
serial_put_dec(value);
else
serial_put_hex(value, 32);
if (info2)
serial_puts(info2);
}
#ifdef CONFIG_TEST_SERIAL_PRINT
typedef struct serial_print_test_pattern_s {
unsigned char t_uchar;
unsigned int t_uint;
unsigned long t_ulong;
} serial_print_test_pattern_t;
serial_print_test_pattern_t test_data[] = {
{ 0, 0, 0 },
{ 0xf, 0xf, 0xf },
{ 0xff, 0xff, 0xff },
{ ~0, 0xffff, 0xffff },
{ ~0, ~0, 0xffffffff },
{ ~0, ~0, ~0 },
};
void serial_print_test(void)
{
int i;
unsigned char t_uchar = 0;
unsigned int t_uint = 0;
unsigned long t_ulong = 0;
serial_puts("\nSerial Print Test\n");
serial_puts("sizeof(t_uchar)=");
serial_put_dec(sizeof(t_uchar));
serial_puts("\n");
serial_puts("sizeof(t_uint)=");
serial_put_dec(sizeof(t_uint));
serial_puts("\n");
serial_puts("sizeof(t_ulong)=");
serial_put_dec(sizeof(t_ulong));
serial_puts("\n");
for (i = 0; i < sizeof(test_data) / sizeof(serial_print_test_pattern_t); i++) {
bl2_print("\nTest Data #", i, VALUE_DEC, "\n");
t_uchar = test_data[i].t_uchar;
t_uint = test_data[i].t_uint;
t_ulong = test_data[i].t_ulong;
serial_puts("printf(t_uchar=%x)=");
serial_put_hex(t_uchar, 32);
serial_puts("\n");
serial_puts("printf(t_uchar=%x)=");
serial_put_hex(t_uchar, 64);
serial_puts("\n");
serial_puts("printf(t_uchar=%d)=");
serial_put_dec(t_uchar);
serial_puts("\n");
serial_puts("printf(t_uint=%x)=");
serial_put_hex(t_uint, 32);
serial_puts("\n");
serial_puts("printf(t_uint=%x)=");
serial_put_hex(t_uint, 64);
serial_puts("\n");
serial_puts("printf(t_uint=%d)=");
serial_put_dec(t_uint);
serial_puts("\n");
serial_puts("printf(t_ulong=%x)=");
serial_put_hex(t_ulong, 32);
serial_puts("\n");
serial_puts("printf(t_ulong=%x)=");
serial_put_hex(t_ulong, 64);
serial_puts("\n");
serial_puts("printf(t_ulong=%d)=");
serial_put_dec(t_ulong);
serial_puts("\n");
}
}
#endif /* CONFIG_TEST_SERIAL_PRINT */