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> 12*73e256c2SSimon Glass #include <dm.h> 13*73e256c2SSimon 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/uart.h> 1846a3b5c8SMinkyu Kang #include <asm/arch/clk.h> 1946a3b5c8SMinkyu Kang #include <serial.h> 2046a3b5c8SMinkyu Kang 2129565326SJohn Rigby DECLARE_GLOBAL_DATA_PTR; 2229565326SJohn Rigby 23*73e256c2SSimon Glass #define RX_FIFO_COUNT_SHIFT 0 24*73e256c2SSimon Glass #define RX_FIFO_COUNT_MASK (0xff << RX_FIFO_COUNT_SHIFT) 25*73e256c2SSimon Glass #define RX_FIFO_FULL (1 << 8) 26*73e256c2SSimon Glass #define TX_FIFO_COUNT_SHIFT 16 27*73e256c2SSimon Glass #define TX_FIFO_COUNT_MASK (0xff << TX_FIFO_COUNT_SHIFT) 28*73e256c2SSimon Glass #define TX_FIFO_FULL (1 << 24) 29ffbff1ddSAkshay Saraswat 30d4ec8f08SRajeshwari Shinde /* Information about a serial port */ 31*73e256c2SSimon Glass struct s5p_serial_platdata { 32*73e256c2SSimon Glass struct s5p_uart *reg; /* address of registers in physical memory */ 33d4ec8f08SRajeshwari Shinde u8 port_id; /* uart port number */ 34*73e256c2SSimon Glass }; 3546a3b5c8SMinkyu Kang 3646a3b5c8SMinkyu Kang /* 3746a3b5c8SMinkyu Kang * The coefficient, used to calculate the baudrate on S5P UARTs is 3846a3b5c8SMinkyu Kang * calculated as 3946a3b5c8SMinkyu Kang * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT 4046a3b5c8SMinkyu Kang * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1, 4146a3b5c8SMinkyu Kang * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants: 4246a3b5c8SMinkyu Kang */ 4346a3b5c8SMinkyu Kang static const int udivslot[] = { 4446a3b5c8SMinkyu Kang 0, 4546a3b5c8SMinkyu Kang 0x0080, 4646a3b5c8SMinkyu Kang 0x0808, 4746a3b5c8SMinkyu Kang 0x0888, 4846a3b5c8SMinkyu Kang 0x2222, 4946a3b5c8SMinkyu Kang 0x4924, 5046a3b5c8SMinkyu Kang 0x4a52, 5146a3b5c8SMinkyu Kang 0x54aa, 5246a3b5c8SMinkyu Kang 0x5555, 5346a3b5c8SMinkyu Kang 0xd555, 5446a3b5c8SMinkyu Kang 0xd5d5, 5546a3b5c8SMinkyu Kang 0xddd5, 5646a3b5c8SMinkyu Kang 0xdddd, 5746a3b5c8SMinkyu Kang 0xdfdd, 5846a3b5c8SMinkyu Kang 0xdfdf, 5946a3b5c8SMinkyu Kang 0xffdf, 6046a3b5c8SMinkyu Kang }; 6146a3b5c8SMinkyu Kang 62*73e256c2SSimon Glass int s5p_serial_setbrg(struct udevice *dev, int baudrate) 6346a3b5c8SMinkyu Kang { 64*73e256c2SSimon Glass struct s5p_serial_platdata *plat = dev->platdata; 65*73e256c2SSimon Glass struct s5p_uart *const uart = plat->reg; 66*73e256c2SSimon Glass u32 uclk = get_uart_clk(plat->port_id); 6746a3b5c8SMinkyu Kang u32 val; 6846a3b5c8SMinkyu Kang 69f70409afSMinkyu Kang val = uclk / baudrate; 7046a3b5c8SMinkyu Kang 7146a3b5c8SMinkyu Kang writel(val / 16 - 1, &uart->ubrdiv); 721628cfc4SMinkyu Kang 73e0617c62SMinkyu Kang if (s5p_uart_divslot()) 741628cfc4SMinkyu Kang writew(udivslot[val % 16], &uart->rest.slot); 751628cfc4SMinkyu Kang else 761628cfc4SMinkyu Kang writeb(val % 16, &uart->rest.value); 77*73e256c2SSimon Glass 78*73e256c2SSimon Glass return 0; 7946a3b5c8SMinkyu Kang } 8046a3b5c8SMinkyu Kang 81*73e256c2SSimon Glass static int s5p_serial_probe(struct udevice *dev) 8246a3b5c8SMinkyu Kang { 83*73e256c2SSimon Glass struct s5p_serial_platdata *plat = dev->platdata; 84*73e256c2SSimon Glass struct s5p_uart *const uart = plat->reg; 8546a3b5c8SMinkyu Kang 86e6252fabSInha Song /* enable FIFOs, auto clear Rx FIFO */ 87e6252fabSInha Song writel(0x3, &uart->ufcon); 8846a3b5c8SMinkyu Kang writel(0, &uart->umcon); 8946a3b5c8SMinkyu Kang /* 8N1 */ 9046a3b5c8SMinkyu Kang writel(0x3, &uart->ulcon); 9146a3b5c8SMinkyu Kang /* No interrupts, no DMA, pure polling */ 9246a3b5c8SMinkyu Kang writel(0x245, &uart->ucon); 9346a3b5c8SMinkyu Kang 9446a3b5c8SMinkyu Kang return 0; 9546a3b5c8SMinkyu Kang } 9646a3b5c8SMinkyu Kang 97*73e256c2SSimon Glass static int serial_err_check(const struct s5p_uart *const uart, int op) 9846a3b5c8SMinkyu Kang { 9946a3b5c8SMinkyu Kang unsigned int mask; 10046a3b5c8SMinkyu Kang 10146a3b5c8SMinkyu Kang /* 10246a3b5c8SMinkyu Kang * UERSTAT 10346a3b5c8SMinkyu Kang * Break Detect [3] 10446a3b5c8SMinkyu Kang * Frame Err [2] : receive operation 10546a3b5c8SMinkyu Kang * Parity Err [1] : receive operation 10646a3b5c8SMinkyu Kang * Overrun Err [0] : receive operation 10746a3b5c8SMinkyu Kang */ 10846a3b5c8SMinkyu Kang if (op) 10946a3b5c8SMinkyu Kang mask = 0x8; 11046a3b5c8SMinkyu Kang else 11146a3b5c8SMinkyu Kang mask = 0xf; 11246a3b5c8SMinkyu Kang 11346a3b5c8SMinkyu Kang return readl(&uart->uerstat) & mask; 11446a3b5c8SMinkyu Kang } 11546a3b5c8SMinkyu Kang 116*73e256c2SSimon Glass static int s5p_serial_getc(struct udevice *dev) 11746a3b5c8SMinkyu Kang { 118*73e256c2SSimon Glass struct s5p_serial_platdata *plat = dev->platdata; 119*73e256c2SSimon Glass struct s5p_uart *const uart = plat->reg; 12046a3b5c8SMinkyu Kang 121*73e256c2SSimon Glass if (!(readl(&uart->ufstat) & RX_FIFO_COUNT_MASK)) 122*73e256c2SSimon Glass return -EAGAIN; 123d4ec8f08SRajeshwari Shinde 124*73e256c2SSimon Glass serial_err_check(uart, 0); 1251a4106ddSMinkyu Kang return (int)(readb(&uart->urxh) & 0xff); 12646a3b5c8SMinkyu Kang } 12746a3b5c8SMinkyu Kang 128*73e256c2SSimon Glass static int s5p_serial_putc(struct udevice *dev, const char ch) 12946a3b5c8SMinkyu Kang { 130*73e256c2SSimon Glass struct s5p_serial_platdata *plat = dev->platdata; 131*73e256c2SSimon Glass struct s5p_uart *const uart = plat->reg; 13246a3b5c8SMinkyu Kang 133*73e256c2SSimon Glass if (readl(&uart->ufstat) & TX_FIFO_FULL) 134*73e256c2SSimon Glass return -EAGAIN; 135d4ec8f08SRajeshwari Shinde 136*73e256c2SSimon Glass writeb(ch, &uart->utxh); 137*73e256c2SSimon Glass serial_err_check(uart, 1); 138d4ec8f08SRajeshwari Shinde 139d4ec8f08SRajeshwari Shinde return 0; 140d4ec8f08SRajeshwari Shinde } 141d4ec8f08SRajeshwari Shinde 142*73e256c2SSimon Glass static int s5p_serial_pending(struct udevice *dev, bool input) 1436c768ca7SMike Frysinger { 144*73e256c2SSimon Glass struct s5p_serial_platdata *plat = dev->platdata; 145*73e256c2SSimon Glass struct s5p_uart *const uart = plat->reg; 146*73e256c2SSimon Glass uint32_t ufstat = readl(&uart->ufstat); 147d4ec8f08SRajeshwari Shinde 148*73e256c2SSimon Glass if (input) 149*73e256c2SSimon Glass return (ufstat & RX_FIFO_COUNT_MASK) >> RX_FIFO_COUNT_SHIFT; 150*73e256c2SSimon Glass else 151*73e256c2SSimon Glass return (ufstat & TX_FIFO_COUNT_MASK) >> TX_FIFO_COUNT_SHIFT; 152d4ec8f08SRajeshwari Shinde } 153d4ec8f08SRajeshwari Shinde 154*73e256c2SSimon Glass static int s5p_serial_ofdata_to_platdata(struct udevice *dev) 155b4980515SMarek Vasut { 156*73e256c2SSimon Glass struct s5p_serial_platdata *plat = dev->platdata; 157*73e256c2SSimon Glass fdt_addr_t addr; 158*73e256c2SSimon Glass 159*73e256c2SSimon Glass addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); 160*73e256c2SSimon Glass if (addr == FDT_ADDR_T_NONE) 161*73e256c2SSimon Glass return -EINVAL; 162*73e256c2SSimon Glass 163*73e256c2SSimon Glass plat->reg = (struct s5p_uart *)addr; 164*73e256c2SSimon Glass plat->port_id = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "id", -1); 165*73e256c2SSimon Glass 166*73e256c2SSimon Glass return 0; 167b4980515SMarek Vasut } 168*73e256c2SSimon Glass 169*73e256c2SSimon Glass static const struct dm_serial_ops s5p_serial_ops = { 170*73e256c2SSimon Glass .putc = s5p_serial_putc, 171*73e256c2SSimon Glass .pending = s5p_serial_pending, 172*73e256c2SSimon Glass .getc = s5p_serial_getc, 173*73e256c2SSimon Glass .setbrg = s5p_serial_setbrg, 174*73e256c2SSimon Glass }; 175*73e256c2SSimon Glass 176*73e256c2SSimon Glass static const struct udevice_id s5p_serial_ids[] = { 177*73e256c2SSimon Glass { .compatible = "samsung,exynos4210-uart" }, 178*73e256c2SSimon Glass { } 179*73e256c2SSimon Glass }; 180*73e256c2SSimon Glass 181*73e256c2SSimon Glass U_BOOT_DRIVER(serial_s5p) = { 182*73e256c2SSimon Glass .name = "serial_s5p", 183*73e256c2SSimon Glass .id = UCLASS_SERIAL, 184*73e256c2SSimon Glass .of_match = s5p_serial_ids, 185*73e256c2SSimon Glass .ofdata_to_platdata = s5p_serial_ofdata_to_platdata, 186*73e256c2SSimon Glass .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata), 187*73e256c2SSimon Glass .probe = s5p_serial_probe, 188*73e256c2SSimon Glass .ops = &s5p_serial_ops, 189*73e256c2SSimon Glass .flags = DM_FLAG_PRE_RELOC, 190*73e256c2SSimon Glass }; 191