146a3b5c8SMinkyu Kang /* 246a3b5c8SMinkyu Kang * (C) Copyright 2009 SAMSUNG Electronics 346a3b5c8SMinkyu Kang * Minkyu Kang <mk7.kang@samsung.com> 446a3b5c8SMinkyu Kang * Heungjun Kim <riverful.kim@samsung.com> 546a3b5c8SMinkyu Kang * 646a3b5c8SMinkyu Kang * based on drivers/serial/s3c64xx.c 746a3b5c8SMinkyu Kang * 81a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 946a3b5c8SMinkyu Kang */ 1046a3b5c8SMinkyu Kang 1146a3b5c8SMinkyu Kang #include <common.h> 1273e256c2SSimon Glass #include <dm.h> 1373e256c2SSimon Glass #include <errno.h> 14d4ec8f08SRajeshwari Shinde #include <fdtdec.h> 156c768ca7SMike Frysinger #include <linux/compiler.h> 1646a3b5c8SMinkyu Kang #include <asm/io.h> 1746a3b5c8SMinkyu Kang #include <asm/arch/clk.h> 1889ca9351SSimon Glass #include <asm/arch/uart.h> 1946a3b5c8SMinkyu Kang #include <serial.h> 20*cf75cdf9SThomas Abraham #include <clk.h> 2146a3b5c8SMinkyu Kang 2229565326SJohn Rigby DECLARE_GLOBAL_DATA_PTR; 2329565326SJohn Rigby 2473e256c2SSimon Glass #define RX_FIFO_COUNT_SHIFT 0 2573e256c2SSimon Glass #define RX_FIFO_COUNT_MASK (0xff << RX_FIFO_COUNT_SHIFT) 2673e256c2SSimon Glass #define RX_FIFO_FULL (1 << 8) 2773e256c2SSimon Glass #define TX_FIFO_COUNT_SHIFT 16 2873e256c2SSimon Glass #define TX_FIFO_COUNT_MASK (0xff << TX_FIFO_COUNT_SHIFT) 2973e256c2SSimon Glass #define TX_FIFO_FULL (1 << 24) 30ffbff1ddSAkshay Saraswat 31d4ec8f08SRajeshwari Shinde /* Information about a serial port */ 3273e256c2SSimon Glass struct s5p_serial_platdata { 3373e256c2SSimon Glass struct s5p_uart *reg; /* address of registers in physical memory */ 34d4ec8f08SRajeshwari Shinde u8 port_id; /* uart port number */ 3573e256c2SSimon Glass }; 3646a3b5c8SMinkyu Kang 3746a3b5c8SMinkyu Kang /* 3846a3b5c8SMinkyu Kang * The coefficient, used to calculate the baudrate on S5P UARTs is 3946a3b5c8SMinkyu Kang * calculated as 4046a3b5c8SMinkyu Kang * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT 4146a3b5c8SMinkyu Kang * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1, 4246a3b5c8SMinkyu Kang * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants: 4346a3b5c8SMinkyu Kang */ 4446a3b5c8SMinkyu Kang static const int udivslot[] = { 4546a3b5c8SMinkyu Kang 0, 4646a3b5c8SMinkyu Kang 0x0080, 4746a3b5c8SMinkyu Kang 0x0808, 4846a3b5c8SMinkyu Kang 0x0888, 4946a3b5c8SMinkyu Kang 0x2222, 5046a3b5c8SMinkyu Kang 0x4924, 5146a3b5c8SMinkyu Kang 0x4a52, 5246a3b5c8SMinkyu Kang 0x54aa, 5346a3b5c8SMinkyu Kang 0x5555, 5446a3b5c8SMinkyu Kang 0xd555, 5546a3b5c8SMinkyu Kang 0xd5d5, 5646a3b5c8SMinkyu Kang 0xddd5, 5746a3b5c8SMinkyu Kang 0xdddd, 5846a3b5c8SMinkyu Kang 0xdfdd, 5946a3b5c8SMinkyu Kang 0xdfdf, 6046a3b5c8SMinkyu Kang 0xffdf, 6146a3b5c8SMinkyu Kang }; 6246a3b5c8SMinkyu Kang 6389ca9351SSimon Glass static void __maybe_unused s5p_serial_init(struct s5p_uart *uart) 6446a3b5c8SMinkyu Kang { 6589ca9351SSimon Glass /* enable FIFOs, auto clear Rx FIFO */ 6689ca9351SSimon Glass writel(0x3, &uart->ufcon); 6789ca9351SSimon Glass writel(0, &uart->umcon); 6889ca9351SSimon Glass /* 8N1 */ 6989ca9351SSimon Glass writel(0x3, &uart->ulcon); 7089ca9351SSimon Glass /* No interrupts, no DMA, pure polling */ 7189ca9351SSimon Glass writel(0x245, &uart->ucon); 7289ca9351SSimon Glass } 7389ca9351SSimon Glass 7489ca9351SSimon Glass static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, uint uclk, 7589ca9351SSimon Glass int baudrate) 7689ca9351SSimon Glass { 7746a3b5c8SMinkyu Kang u32 val; 7846a3b5c8SMinkyu Kang 79f70409afSMinkyu Kang val = uclk / baudrate; 8046a3b5c8SMinkyu Kang 8146a3b5c8SMinkyu Kang writel(val / 16 - 1, &uart->ubrdiv); 821628cfc4SMinkyu Kang 83e0617c62SMinkyu Kang if (s5p_uart_divslot()) 841628cfc4SMinkyu Kang writew(udivslot[val % 16], &uart->rest.slot); 851628cfc4SMinkyu Kang else 861628cfc4SMinkyu Kang writeb(val % 16, &uart->rest.value); 8789ca9351SSimon Glass } 8889ca9351SSimon Glass 897fb57396SSimon Glass #ifndef CONFIG_SPL_BUILD 9089ca9351SSimon Glass int s5p_serial_setbrg(struct udevice *dev, int baudrate) 9189ca9351SSimon Glass { 9289ca9351SSimon Glass struct s5p_serial_platdata *plat = dev->platdata; 9389ca9351SSimon Glass struct s5p_uart *const uart = plat->reg; 94*cf75cdf9SThomas Abraham u32 uclk; 95*cf75cdf9SThomas Abraham 96*cf75cdf9SThomas Abraham #ifdef CONFIG_CLK_EXYNOS 97*cf75cdf9SThomas Abraham struct udevice *clk_dev; 98*cf75cdf9SThomas Abraham u32 ret; 99*cf75cdf9SThomas Abraham 100*cf75cdf9SThomas Abraham ret = clk_get_by_index(dev, 1, &clk_dev); 101*cf75cdf9SThomas Abraham if (ret < 0) 102*cf75cdf9SThomas Abraham return ret; 103*cf75cdf9SThomas Abraham uclk = clk_get_periph_rate(clk_dev, ret); 104*cf75cdf9SThomas Abraham #else 105*cf75cdf9SThomas Abraham uclk = get_uart_clk(plat->port_id); 106*cf75cdf9SThomas Abraham #endif 10789ca9351SSimon Glass 10889ca9351SSimon Glass s5p_serial_baud(uart, uclk, baudrate); 10973e256c2SSimon Glass 11073e256c2SSimon Glass return 0; 11146a3b5c8SMinkyu Kang } 11246a3b5c8SMinkyu Kang 11373e256c2SSimon Glass static int s5p_serial_probe(struct udevice *dev) 11446a3b5c8SMinkyu Kang { 11573e256c2SSimon Glass struct s5p_serial_platdata *plat = dev->platdata; 11673e256c2SSimon Glass struct s5p_uart *const uart = plat->reg; 11746a3b5c8SMinkyu Kang 11889ca9351SSimon Glass s5p_serial_init(uart); 11946a3b5c8SMinkyu Kang 12046a3b5c8SMinkyu Kang return 0; 12146a3b5c8SMinkyu Kang } 12246a3b5c8SMinkyu Kang 12373e256c2SSimon Glass static int serial_err_check(const struct s5p_uart *const uart, int op) 12446a3b5c8SMinkyu Kang { 12546a3b5c8SMinkyu Kang unsigned int mask; 12646a3b5c8SMinkyu Kang 12746a3b5c8SMinkyu Kang /* 12846a3b5c8SMinkyu Kang * UERSTAT 12946a3b5c8SMinkyu Kang * Break Detect [3] 13046a3b5c8SMinkyu Kang * Frame Err [2] : receive operation 13146a3b5c8SMinkyu Kang * Parity Err [1] : receive operation 13246a3b5c8SMinkyu Kang * Overrun Err [0] : receive operation 13346a3b5c8SMinkyu Kang */ 13446a3b5c8SMinkyu Kang if (op) 13546a3b5c8SMinkyu Kang mask = 0x8; 13646a3b5c8SMinkyu Kang else 13746a3b5c8SMinkyu Kang mask = 0xf; 13846a3b5c8SMinkyu Kang 13946a3b5c8SMinkyu Kang return readl(&uart->uerstat) & mask; 14046a3b5c8SMinkyu Kang } 14146a3b5c8SMinkyu Kang 14273e256c2SSimon Glass static int s5p_serial_getc(struct udevice *dev) 14346a3b5c8SMinkyu Kang { 14473e256c2SSimon Glass struct s5p_serial_platdata *plat = dev->platdata; 14573e256c2SSimon Glass struct s5p_uart *const uart = plat->reg; 14646a3b5c8SMinkyu Kang 14773e256c2SSimon Glass if (!(readl(&uart->ufstat) & RX_FIFO_COUNT_MASK)) 14873e256c2SSimon Glass return -EAGAIN; 149d4ec8f08SRajeshwari Shinde 15073e256c2SSimon Glass serial_err_check(uart, 0); 1511a4106ddSMinkyu Kang return (int)(readb(&uart->urxh) & 0xff); 15246a3b5c8SMinkyu Kang } 15346a3b5c8SMinkyu Kang 15473e256c2SSimon Glass static int s5p_serial_putc(struct udevice *dev, const char ch) 15546a3b5c8SMinkyu Kang { 15673e256c2SSimon Glass struct s5p_serial_platdata *plat = dev->platdata; 15773e256c2SSimon Glass struct s5p_uart *const uart = plat->reg; 15846a3b5c8SMinkyu Kang 15973e256c2SSimon Glass if (readl(&uart->ufstat) & TX_FIFO_FULL) 16073e256c2SSimon Glass return -EAGAIN; 161d4ec8f08SRajeshwari Shinde 16273e256c2SSimon Glass writeb(ch, &uart->utxh); 16373e256c2SSimon Glass serial_err_check(uart, 1); 164d4ec8f08SRajeshwari Shinde 165d4ec8f08SRajeshwari Shinde return 0; 166d4ec8f08SRajeshwari Shinde } 167d4ec8f08SRajeshwari Shinde 16873e256c2SSimon Glass static int s5p_serial_pending(struct udevice *dev, bool input) 1696c768ca7SMike Frysinger { 17073e256c2SSimon Glass struct s5p_serial_platdata *plat = dev->platdata; 17173e256c2SSimon Glass struct s5p_uart *const uart = plat->reg; 17273e256c2SSimon Glass uint32_t ufstat = readl(&uart->ufstat); 173d4ec8f08SRajeshwari Shinde 17473e256c2SSimon Glass if (input) 17573e256c2SSimon Glass return (ufstat & RX_FIFO_COUNT_MASK) >> RX_FIFO_COUNT_SHIFT; 17673e256c2SSimon Glass else 17773e256c2SSimon Glass return (ufstat & TX_FIFO_COUNT_MASK) >> TX_FIFO_COUNT_SHIFT; 178d4ec8f08SRajeshwari Shinde } 179d4ec8f08SRajeshwari Shinde 18073e256c2SSimon Glass static int s5p_serial_ofdata_to_platdata(struct udevice *dev) 181b4980515SMarek Vasut { 18273e256c2SSimon Glass struct s5p_serial_platdata *plat = dev->platdata; 18373e256c2SSimon Glass fdt_addr_t addr; 18473e256c2SSimon Glass 1854e9838c1SSimon Glass addr = dev_get_addr(dev); 18673e256c2SSimon Glass if (addr == FDT_ADDR_T_NONE) 18773e256c2SSimon Glass return -EINVAL; 18873e256c2SSimon Glass 18973e256c2SSimon Glass plat->reg = (struct s5p_uart *)addr; 1905ab6c4dfSThomas Abraham plat->port_id = fdtdec_get_int(gd->fdt_blob, dev->of_offset, 1915ab6c4dfSThomas Abraham "id", dev->seq); 19273e256c2SSimon Glass return 0; 193b4980515SMarek Vasut } 19473e256c2SSimon Glass 19573e256c2SSimon Glass static const struct dm_serial_ops s5p_serial_ops = { 19673e256c2SSimon Glass .putc = s5p_serial_putc, 19773e256c2SSimon Glass .pending = s5p_serial_pending, 19873e256c2SSimon Glass .getc = s5p_serial_getc, 19973e256c2SSimon Glass .setbrg = s5p_serial_setbrg, 20073e256c2SSimon Glass }; 20173e256c2SSimon Glass 20273e256c2SSimon Glass static const struct udevice_id s5p_serial_ids[] = { 20373e256c2SSimon Glass { .compatible = "samsung,exynos4210-uart" }, 20473e256c2SSimon Glass { } 20573e256c2SSimon Glass }; 20673e256c2SSimon Glass 20773e256c2SSimon Glass U_BOOT_DRIVER(serial_s5p) = { 20873e256c2SSimon Glass .name = "serial_s5p", 20973e256c2SSimon Glass .id = UCLASS_SERIAL, 21073e256c2SSimon Glass .of_match = s5p_serial_ids, 21173e256c2SSimon Glass .ofdata_to_platdata = s5p_serial_ofdata_to_platdata, 21273e256c2SSimon Glass .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata), 21373e256c2SSimon Glass .probe = s5p_serial_probe, 21473e256c2SSimon Glass .ops = &s5p_serial_ops, 21573e256c2SSimon Glass .flags = DM_FLAG_PRE_RELOC, 21673e256c2SSimon Glass }; 2177fb57396SSimon Glass #endif 218bf6e7022SSimon Glass 219bf6e7022SSimon Glass #ifdef CONFIG_DEBUG_UART_S5P 220bf6e7022SSimon Glass 221bf6e7022SSimon Glass #include <debug_uart.h> 222bf6e7022SSimon Glass 22397b05973SSimon Glass static inline void _debug_uart_init(void) 224bf6e7022SSimon Glass { 225bf6e7022SSimon Glass struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE; 226bf6e7022SSimon Glass 227bf6e7022SSimon Glass s5p_serial_init(uart); 228bf6e7022SSimon Glass s5p_serial_baud(uart, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); 229bf6e7022SSimon Glass } 230bf6e7022SSimon Glass 231bf6e7022SSimon Glass static inline void _debug_uart_putc(int ch) 232bf6e7022SSimon Glass { 233bf6e7022SSimon Glass struct s5p_uart *uart = (struct s5p_uart *)CONFIG_DEBUG_UART_BASE; 234bf6e7022SSimon Glass 235bf6e7022SSimon Glass while (readl(&uart->ufstat) & TX_FIFO_FULL); 236bf6e7022SSimon Glass 237bf6e7022SSimon Glass writeb(ch, &uart->utxh); 238bf6e7022SSimon Glass } 239bf6e7022SSimon Glass 240bf6e7022SSimon Glass DEBUG_UART_FUNCS 241bf6e7022SSimon Glass 242bf6e7022SSimon Glass #endif 243