|
|
|
|
@@ -14,24 +14,45 @@
|
|
|
|
|
#include <asm/global_data.h>
|
|
|
|
|
#include <linux/compiler.h>
|
|
|
|
|
#include <asm/io.h>
|
|
|
|
|
#if !CONFIG_IS_ENABLED(ARCH_APPLE)
|
|
|
|
|
#include <asm/arch/clk.h>
|
|
|
|
|
#endif
|
|
|
|
|
#include <asm/arch/uart.h>
|
|
|
|
|
#include <serial.h>
|
|
|
|
|
#include <clk.h>
|
|
|
|
|
|
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
|
|
|
|
|
|
#define RX_FIFO_COUNT_SHIFT 0
|
|
|
|
|
#define RX_FIFO_COUNT_MASK (0xff << RX_FIFO_COUNT_SHIFT)
|
|
|
|
|
#define RX_FIFO_FULL (1 << 8)
|
|
|
|
|
#define TX_FIFO_COUNT_SHIFT 16
|
|
|
|
|
#define TX_FIFO_COUNT_MASK (0xff << TX_FIFO_COUNT_SHIFT)
|
|
|
|
|
#define TX_FIFO_FULL (1 << 24)
|
|
|
|
|
enum {
|
|
|
|
|
PORT_S5P = 0,
|
|
|
|
|
PORT_S5L
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define S5L_RX_FIFO_COUNT_SHIFT 0
|
|
|
|
|
#define S5L_RX_FIFO_COUNT_MASK (0xf << S5L_RX_FIFO_COUNT_SHIFT)
|
|
|
|
|
#define S5L_RX_FIFO_FULL (1 << 8)
|
|
|
|
|
#define S5L_TX_FIFO_COUNT_SHIFT 4
|
|
|
|
|
#define S5L_TX_FIFO_COUNT_MASK (0xf << S5L_TX_FIFO_COUNT_SHIFT)
|
|
|
|
|
#define S5L_TX_FIFO_FULL (1 << 9)
|
|
|
|
|
|
|
|
|
|
#define S5P_RX_FIFO_COUNT_SHIFT 0
|
|
|
|
|
#define S5P_RX_FIFO_COUNT_MASK (0xff << S5P_RX_FIFO_COUNT_SHIFT)
|
|
|
|
|
#define S5P_RX_FIFO_FULL (1 << 8)
|
|
|
|
|
#define S5P_TX_FIFO_COUNT_SHIFT 16
|
|
|
|
|
#define S5P_TX_FIFO_COUNT_MASK (0xff << S5P_TX_FIFO_COUNT_SHIFT)
|
|
|
|
|
#define S5P_TX_FIFO_FULL (1 << 24)
|
|
|
|
|
|
|
|
|
|
/* Information about a serial port */
|
|
|
|
|
struct s5p_serial_plat {
|
|
|
|
|
struct s5p_uart *reg; /* address of registers in physical memory */
|
|
|
|
|
u8 reg_width; /* register width */
|
|
|
|
|
u8 port_id; /* uart port number */
|
|
|
|
|
u8 rx_fifo_count_shift;
|
|
|
|
|
u8 tx_fifo_count_shift;
|
|
|
|
|
u32 rx_fifo_count_mask;
|
|
|
|
|
u32 tx_fifo_count_mask;
|
|
|
|
|
u32 rx_fifo_full;
|
|
|
|
|
u32 tx_fifo_full;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
@@ -71,8 +92,8 @@ static void __maybe_unused s5p_serial_init(struct s5p_uart *uart)
|
|
|
|
|
writel(0x245, &uart->ucon);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, uint uclk,
|
|
|
|
|
int baudrate)
|
|
|
|
|
static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, u8 reg_width,
|
|
|
|
|
uint uclk, int baudrate)
|
|
|
|
|
{
|
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
|
|
@@ -82,6 +103,8 @@ static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, uint uclk,
|
|
|
|
|
|
|
|
|
|
if (s5p_uart_divslot())
|
|
|
|
|
writew(udivslot[val % 16], &uart->rest.slot);
|
|
|
|
|
else if (reg_width == 4)
|
|
|
|
|
writel(val % 16, &uart->rest.value);
|
|
|
|
|
else
|
|
|
|
|
writeb(val % 16, &uart->rest.value);
|
|
|
|
|
}
|
|
|
|
|
@@ -93,7 +116,7 @@ int s5p_serial_setbrg(struct udevice *dev, int baudrate)
|
|
|
|
|
struct s5p_uart *const uart = plat->reg;
|
|
|
|
|
u32 uclk;
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_CLK_EXYNOS
|
|
|
|
|
#if CONFIG_IS_ENABLED(CLK_EXYNOS) || CONFIG_IS_ENABLED(ARCH_APPLE)
|
|
|
|
|
struct clk clk;
|
|
|
|
|
u32 ret;
|
|
|
|
|
|
|
|
|
|
@@ -105,7 +128,7 @@ int s5p_serial_setbrg(struct udevice *dev, int baudrate)
|
|
|
|
|
uclk = get_uart_clk(plat->port_id);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
s5p_serial_baud(uart, uclk, baudrate);
|
|
|
|
|
s5p_serial_baud(uart, plat->reg_width, uclk, baudrate);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
@@ -144,11 +167,14 @@ static int s5p_serial_getc(struct udevice *dev)
|
|
|
|
|
struct s5p_serial_plat *plat = dev_get_plat(dev);
|
|
|
|
|
struct s5p_uart *const uart = plat->reg;
|
|
|
|
|
|
|
|
|
|
if (!(readl(&uart->ufstat) & RX_FIFO_COUNT_MASK))
|
|
|
|
|
if (!(readl(&uart->ufstat) & plat->rx_fifo_count_mask))
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
|
|
|
|
|
serial_err_check(uart, 0);
|
|
|
|
|
return (int)(readb(&uart->urxh) & 0xff);
|
|
|
|
|
if (plat->reg_width == 4)
|
|
|
|
|
return (int)(readl(&uart->urxh) & 0xff);
|
|
|
|
|
else
|
|
|
|
|
return (int)(readb(&uart->urxh) & 0xff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int s5p_serial_putc(struct udevice *dev, const char ch)
|
|
|
|
|
@@ -156,10 +182,13 @@ static int s5p_serial_putc(struct udevice *dev, const char ch)
|
|
|
|
|
struct s5p_serial_plat *plat = dev_get_plat(dev);
|
|
|
|
|
struct s5p_uart *const uart = plat->reg;
|
|
|
|
|
|
|
|
|
|
if (readl(&uart->ufstat) & TX_FIFO_FULL)
|
|
|
|
|
if (readl(&uart->ufstat) & plat->tx_fifo_full)
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
|
|
|
|
|
writeb(ch, &uart->utxh);
|
|
|
|
|
if (plat->reg_width == 4)
|
|
|
|
|
writel(ch, &uart->utxh);
|
|
|
|
|
else
|
|
|
|
|
writeb(ch, &uart->utxh);
|
|
|
|
|
serial_err_check(uart, 1);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
@@ -171,15 +200,19 @@ static int s5p_serial_pending(struct udevice *dev, bool input)
|
|
|
|
|
struct s5p_uart *const uart = plat->reg;
|
|
|
|
|
uint32_t ufstat = readl(&uart->ufstat);
|
|
|
|
|
|
|
|
|
|
if (input)
|
|
|
|
|
return (ufstat & RX_FIFO_COUNT_MASK) >> RX_FIFO_COUNT_SHIFT;
|
|
|
|
|
else
|
|
|
|
|
return (ufstat & TX_FIFO_COUNT_MASK) >> TX_FIFO_COUNT_SHIFT;
|
|
|
|
|
if (input) {
|
|
|
|
|
return (ufstat & plat->rx_fifo_count_mask) >>
|
|
|
|
|
plat->rx_fifo_count_shift;
|
|
|
|
|
} else {
|
|
|
|
|
return (ufstat & plat->tx_fifo_count_mask) >>
|
|
|
|
|
plat->tx_fifo_count_shift;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int s5p_serial_of_to_plat(struct udevice *dev)
|
|
|
|
|
{
|
|
|
|
|
struct s5p_serial_plat *plat = dev_get_plat(dev);
|
|
|
|
|
const ulong port_type = dev_get_driver_data(dev);
|
|
|
|
|
fdt_addr_t addr;
|
|
|
|
|
|
|
|
|
|
addr = dev_read_addr(dev);
|
|
|
|
|
@@ -187,8 +220,26 @@ static int s5p_serial_of_to_plat(struct udevice *dev)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
plat->reg = (struct s5p_uart *)addr;
|
|
|
|
|
plat->reg_width = dev_read_u32_default(dev, "reg-io-width", 1);
|
|
|
|
|
plat->port_id = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
|
|
|
|
|
"id", dev_seq(dev));
|
|
|
|
|
|
|
|
|
|
if (port_type == PORT_S5L) {
|
|
|
|
|
plat->rx_fifo_count_shift = S5L_RX_FIFO_COUNT_SHIFT;
|
|
|
|
|
plat->rx_fifo_count_mask = S5L_RX_FIFO_COUNT_MASK;
|
|
|
|
|
plat->rx_fifo_full = S5L_RX_FIFO_FULL;
|
|
|
|
|
plat->tx_fifo_count_shift = S5L_TX_FIFO_COUNT_SHIFT;
|
|
|
|
|
plat->tx_fifo_count_mask = S5L_TX_FIFO_COUNT_MASK;
|
|
|
|
|
plat->tx_fifo_full = S5L_TX_FIFO_FULL;
|
|
|
|
|
} else {
|
|
|
|
|
plat->rx_fifo_count_shift = S5P_RX_FIFO_COUNT_SHIFT;
|
|
|
|
|
plat->rx_fifo_count_mask = S5P_RX_FIFO_COUNT_MASK;
|
|
|
|
|
plat->rx_fifo_full = S5P_RX_FIFO_FULL;
|
|
|
|
|
plat->tx_fifo_count_shift = S5P_TX_FIFO_COUNT_SHIFT;
|
|
|
|
|
plat->tx_fifo_count_mask = S5P_TX_FIFO_COUNT_MASK;
|
|
|
|
|
plat->tx_fifo_full = S5P_TX_FIFO_FULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -200,7 +251,8 @@ static const struct dm_serial_ops s5p_serial_ops = {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct udevice_id s5p_serial_ids[] = {
|
|
|
|
|
{ .compatible = "samsung,exynos4210-uart" },
|
|
|
|
|
{ .compatible = "samsung,exynos4210-uart", .data = PORT_S5P },
|
|
|
|
|
{ .compatible = "apple,s5l-uart", .data = PORT_S5L },
|
|
|
|
|
{ }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -224,16 +276,24 @@ static inline void _debug_uart_init(void)
|
|
|
|
|
struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE;
|
|
|
|
|
|
|
|
|
|
s5p_serial_init(uart);
|
|
|
|
|
s5p_serial_baud(uart, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
|
|
|
|
|
#if CONFIG_IS_ENABLED(ARCH_APPLE)
|
|
|
|
|
s5p_serial_baud(uart, 4, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
|
|
|
|
|
#else
|
|
|
|
|
s5p_serial_baud(uart, 1, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void _debug_uart_putc(int ch)
|
|
|
|
|
{
|
|
|
|
|
struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE;
|
|
|
|
|
|
|
|
|
|
while (readl(&uart->ufstat) & TX_FIFO_FULL);
|
|
|
|
|
|
|
|
|
|
#if CONFIG_IS_ENABLED(ARCH_APPLE)
|
|
|
|
|
while (readl(&uart->ufstat) & S5L_TX_FIFO_FULL);
|
|
|
|
|
writel(ch, &uart->utxh);
|
|
|
|
|
#else
|
|
|
|
|
while (readl(&uart->ufstat) & S5P_TX_FIFO_FULL);
|
|
|
|
|
writeb(ch, &uart->utxh);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEBUG_UART_FUNCS
|
|
|
|
|
|