| // 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 */ |