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> 12d4ec8f08SRajeshwari Shinde #include <fdtdec.h> 136c768ca7SMike Frysinger #include <linux/compiler.h> 1446a3b5c8SMinkyu Kang #include <asm/io.h> 1546a3b5c8SMinkyu Kang #include <asm/arch/uart.h> 1646a3b5c8SMinkyu Kang #include <asm/arch/clk.h> 1746a3b5c8SMinkyu Kang #include <serial.h> 1846a3b5c8SMinkyu Kang 1929565326SJohn Rigby DECLARE_GLOBAL_DATA_PTR; 2029565326SJohn Rigby 21ffbff1ddSAkshay Saraswat #define RX_FIFO_COUNT_MASK 0xff 22ffbff1ddSAkshay Saraswat #define RX_FIFO_FULL_MASK (1 << 8) 23ffbff1ddSAkshay Saraswat #define TX_FIFO_FULL_MASK (1 << 24) 24ffbff1ddSAkshay Saraswat 25d4ec8f08SRajeshwari Shinde /* Information about a serial port */ 26d4ec8f08SRajeshwari Shinde struct fdt_serial { 27d4ec8f08SRajeshwari Shinde u32 base_addr; /* address of registers in physical memory */ 28d4ec8f08SRajeshwari Shinde u8 port_id; /* uart port number */ 29d4ec8f08SRajeshwari Shinde u8 enabled; /* 1 if enabled, 0 if disabled */ 30d4ec8f08SRajeshwari Shinde } config __attribute__ ((section(".data"))); 31d4ec8f08SRajeshwari Shinde 3246a3b5c8SMinkyu Kang static inline struct s5p_uart *s5p_get_base_uart(int dev_index) 3346a3b5c8SMinkyu Kang { 34d4ec8f08SRajeshwari Shinde #ifdef CONFIG_OF_CONTROL 35d4ec8f08SRajeshwari Shinde return (struct s5p_uart *)(config.base_addr); 36d4ec8f08SRajeshwari Shinde #else 3746a3b5c8SMinkyu Kang u32 offset = dev_index * sizeof(struct s5p_uart); 38d93d0f0cSMinkyu Kang return (struct s5p_uart *)(samsung_get_base_uart() + offset); 39d4ec8f08SRajeshwari Shinde #endif 4046a3b5c8SMinkyu Kang } 4146a3b5c8SMinkyu Kang 4246a3b5c8SMinkyu Kang /* 4346a3b5c8SMinkyu Kang * The coefficient, used to calculate the baudrate on S5P UARTs is 4446a3b5c8SMinkyu Kang * calculated as 4546a3b5c8SMinkyu Kang * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT 4646a3b5c8SMinkyu Kang * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1, 4746a3b5c8SMinkyu Kang * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants: 4846a3b5c8SMinkyu Kang */ 4946a3b5c8SMinkyu Kang static const int udivslot[] = { 5046a3b5c8SMinkyu Kang 0, 5146a3b5c8SMinkyu Kang 0x0080, 5246a3b5c8SMinkyu Kang 0x0808, 5346a3b5c8SMinkyu Kang 0x0888, 5446a3b5c8SMinkyu Kang 0x2222, 5546a3b5c8SMinkyu Kang 0x4924, 5646a3b5c8SMinkyu Kang 0x4a52, 5746a3b5c8SMinkyu Kang 0x54aa, 5846a3b5c8SMinkyu Kang 0x5555, 5946a3b5c8SMinkyu Kang 0xd555, 6046a3b5c8SMinkyu Kang 0xd5d5, 6146a3b5c8SMinkyu Kang 0xddd5, 6246a3b5c8SMinkyu Kang 0xdddd, 6346a3b5c8SMinkyu Kang 0xdfdd, 6446a3b5c8SMinkyu Kang 0xdfdf, 6546a3b5c8SMinkyu Kang 0xffdf, 6646a3b5c8SMinkyu Kang }; 6746a3b5c8SMinkyu Kang 68a81630e0SAxel Lin static void serial_setbrg_dev(const int dev_index) 6946a3b5c8SMinkyu Kang { 7046a3b5c8SMinkyu Kang struct s5p_uart *const uart = s5p_get_base_uart(dev_index); 71f70409afSMinkyu Kang u32 uclk = get_uart_clk(dev_index); 7246a3b5c8SMinkyu Kang u32 baudrate = gd->baudrate; 7346a3b5c8SMinkyu Kang u32 val; 7446a3b5c8SMinkyu Kang 75d4ec8f08SRajeshwari Shinde #if defined(CONFIG_SILENT_CONSOLE) && \ 76d4ec8f08SRajeshwari Shinde defined(CONFIG_OF_CONTROL) && \ 77d4ec8f08SRajeshwari Shinde !defined(CONFIG_SPL_BUILD) 78d4ec8f08SRajeshwari Shinde if (fdtdec_get_config_int(gd->fdt_blob, "silent_console", 0)) 79d4ec8f08SRajeshwari Shinde gd->flags |= GD_FLG_SILENT; 80d4ec8f08SRajeshwari Shinde #endif 81d4ec8f08SRajeshwari Shinde 82d4ec8f08SRajeshwari Shinde if (!config.enabled) 83d4ec8f08SRajeshwari Shinde return; 84d4ec8f08SRajeshwari Shinde 85f70409afSMinkyu Kang val = uclk / baudrate; 8646a3b5c8SMinkyu Kang 8746a3b5c8SMinkyu Kang writel(val / 16 - 1, &uart->ubrdiv); 881628cfc4SMinkyu Kang 89e0617c62SMinkyu Kang if (s5p_uart_divslot()) 901628cfc4SMinkyu Kang writew(udivslot[val % 16], &uart->rest.slot); 911628cfc4SMinkyu Kang else 921628cfc4SMinkyu Kang writeb(val % 16, &uart->rest.value); 9346a3b5c8SMinkyu Kang } 9446a3b5c8SMinkyu Kang 9546a3b5c8SMinkyu Kang /* 9646a3b5c8SMinkyu Kang * Initialise the serial port with the given baudrate. The settings 9746a3b5c8SMinkyu Kang * are always 8 data bits, no parity, 1 stop bit, no start bits. 9846a3b5c8SMinkyu Kang */ 99a81630e0SAxel Lin static int serial_init_dev(const int dev_index) 10046a3b5c8SMinkyu Kang { 10146a3b5c8SMinkyu Kang struct s5p_uart *const uart = s5p_get_base_uart(dev_index); 10246a3b5c8SMinkyu Kang 103*e6252fabSInha Song /* enable FIFOs, auto clear Rx FIFO */ 104*e6252fabSInha Song writel(0x3, &uart->ufcon); 10546a3b5c8SMinkyu Kang writel(0, &uart->umcon); 10646a3b5c8SMinkyu Kang /* 8N1 */ 10746a3b5c8SMinkyu Kang writel(0x3, &uart->ulcon); 10846a3b5c8SMinkyu Kang /* No interrupts, no DMA, pure polling */ 10946a3b5c8SMinkyu Kang writel(0x245, &uart->ucon); 11046a3b5c8SMinkyu Kang 11146a3b5c8SMinkyu Kang serial_setbrg_dev(dev_index); 11246a3b5c8SMinkyu Kang 11346a3b5c8SMinkyu Kang return 0; 11446a3b5c8SMinkyu Kang } 11546a3b5c8SMinkyu Kang 11646a3b5c8SMinkyu Kang static int serial_err_check(const int dev_index, int op) 11746a3b5c8SMinkyu Kang { 11846a3b5c8SMinkyu Kang struct s5p_uart *const uart = s5p_get_base_uart(dev_index); 11946a3b5c8SMinkyu Kang unsigned int mask; 12046a3b5c8SMinkyu Kang 12146a3b5c8SMinkyu Kang /* 12246a3b5c8SMinkyu Kang * UERSTAT 12346a3b5c8SMinkyu Kang * Break Detect [3] 12446a3b5c8SMinkyu Kang * Frame Err [2] : receive operation 12546a3b5c8SMinkyu Kang * Parity Err [1] : receive operation 12646a3b5c8SMinkyu Kang * Overrun Err [0] : receive operation 12746a3b5c8SMinkyu Kang */ 12846a3b5c8SMinkyu Kang if (op) 12946a3b5c8SMinkyu Kang mask = 0x8; 13046a3b5c8SMinkyu Kang else 13146a3b5c8SMinkyu Kang mask = 0xf; 13246a3b5c8SMinkyu Kang 13346a3b5c8SMinkyu Kang return readl(&uart->uerstat) & mask; 13446a3b5c8SMinkyu Kang } 13546a3b5c8SMinkyu Kang 13646a3b5c8SMinkyu Kang /* 13746a3b5c8SMinkyu Kang * Read a single byte from the serial port. Returns 1 on success, 0 13846a3b5c8SMinkyu Kang * otherwise. When the function is succesfull, the character read is 13946a3b5c8SMinkyu Kang * written into its argument c. 14046a3b5c8SMinkyu Kang */ 141a81630e0SAxel Lin static int serial_getc_dev(const int dev_index) 14246a3b5c8SMinkyu Kang { 14346a3b5c8SMinkyu Kang struct s5p_uart *const uart = s5p_get_base_uart(dev_index); 14446a3b5c8SMinkyu Kang 145d4ec8f08SRajeshwari Shinde if (!config.enabled) 146d4ec8f08SRajeshwari Shinde return 0; 147d4ec8f08SRajeshwari Shinde 14846a3b5c8SMinkyu Kang /* wait for character to arrive */ 149ffbff1ddSAkshay Saraswat while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK | 150ffbff1ddSAkshay Saraswat RX_FIFO_FULL_MASK))) { 15146a3b5c8SMinkyu Kang if (serial_err_check(dev_index, 0)) 15246a3b5c8SMinkyu Kang return 0; 15346a3b5c8SMinkyu Kang } 15446a3b5c8SMinkyu Kang 1551a4106ddSMinkyu Kang return (int)(readb(&uart->urxh) & 0xff); 15646a3b5c8SMinkyu Kang } 15746a3b5c8SMinkyu Kang 15846a3b5c8SMinkyu Kang /* 15946a3b5c8SMinkyu Kang * Output a single byte to the serial port. 16046a3b5c8SMinkyu Kang */ 161a81630e0SAxel Lin static void serial_putc_dev(const char c, const int dev_index) 16246a3b5c8SMinkyu Kang { 16346a3b5c8SMinkyu Kang struct s5p_uart *const uart = s5p_get_base_uart(dev_index); 16446a3b5c8SMinkyu Kang 165d4ec8f08SRajeshwari Shinde if (!config.enabled) 166d4ec8f08SRajeshwari Shinde return; 167d4ec8f08SRajeshwari Shinde 16846a3b5c8SMinkyu Kang /* wait for room in the tx FIFO */ 169ffbff1ddSAkshay Saraswat while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) { 17046a3b5c8SMinkyu Kang if (serial_err_check(dev_index, 1)) 17146a3b5c8SMinkyu Kang return; 17246a3b5c8SMinkyu Kang } 17346a3b5c8SMinkyu Kang 1741a4106ddSMinkyu Kang writeb(c, &uart->utxh); 17546a3b5c8SMinkyu Kang 17646a3b5c8SMinkyu Kang /* If \n, also do \r */ 17746a3b5c8SMinkyu Kang if (c == '\n') 17846a3b5c8SMinkyu Kang serial_putc('\r'); 17946a3b5c8SMinkyu Kang } 18046a3b5c8SMinkyu Kang 18146a3b5c8SMinkyu Kang /* 18246a3b5c8SMinkyu Kang * Test whether a character is in the RX buffer 18346a3b5c8SMinkyu Kang */ 184a81630e0SAxel Lin static int serial_tstc_dev(const int dev_index) 18546a3b5c8SMinkyu Kang { 18646a3b5c8SMinkyu Kang struct s5p_uart *const uart = s5p_get_base_uart(dev_index); 18746a3b5c8SMinkyu Kang 188d4ec8f08SRajeshwari Shinde if (!config.enabled) 189d4ec8f08SRajeshwari Shinde return 0; 190d4ec8f08SRajeshwari Shinde 19146a3b5c8SMinkyu Kang return (int)(readl(&uart->utrstat) & 0x1); 19246a3b5c8SMinkyu Kang } 19346a3b5c8SMinkyu Kang 194a81630e0SAxel Lin static void serial_puts_dev(const char *s, const int dev_index) 19546a3b5c8SMinkyu Kang { 19646a3b5c8SMinkyu Kang while (*s) 19746a3b5c8SMinkyu Kang serial_putc_dev(*s++, dev_index); 19846a3b5c8SMinkyu Kang } 19946a3b5c8SMinkyu Kang 20046a3b5c8SMinkyu Kang /* Multi serial device functions */ 20146a3b5c8SMinkyu Kang #define DECLARE_S5P_SERIAL_FUNCTIONS(port) \ 202a81630e0SAxel Lin static int s5p_serial##port##_init(void) { return serial_init_dev(port); } \ 203a81630e0SAxel Lin static void s5p_serial##port##_setbrg(void) { serial_setbrg_dev(port); } \ 204a81630e0SAxel Lin static int s5p_serial##port##_getc(void) { return serial_getc_dev(port); } \ 205a81630e0SAxel Lin static int s5p_serial##port##_tstc(void) { return serial_tstc_dev(port); } \ 206a81630e0SAxel Lin static void s5p_serial##port##_putc(const char c) { serial_putc_dev(c, port); } \ 207a81630e0SAxel Lin static void s5p_serial##port##_puts(const char *s) { serial_puts_dev(s, port); } 20846a3b5c8SMinkyu Kang 20990bad891SMarek Vasut #define INIT_S5P_SERIAL_STRUCTURE(port, __name) { \ 21090bad891SMarek Vasut .name = __name, \ 21190bad891SMarek Vasut .start = s5p_serial##port##_init, \ 21290bad891SMarek Vasut .stop = NULL, \ 21390bad891SMarek Vasut .setbrg = s5p_serial##port##_setbrg, \ 21490bad891SMarek Vasut .getc = s5p_serial##port##_getc, \ 21590bad891SMarek Vasut .tstc = s5p_serial##port##_tstc, \ 21690bad891SMarek Vasut .putc = s5p_serial##port##_putc, \ 21790bad891SMarek Vasut .puts = s5p_serial##port##_puts, \ 21890bad891SMarek Vasut } 21946a3b5c8SMinkyu Kang 22046a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(0); 22146a3b5c8SMinkyu Kang struct serial_device s5p_serial0_device = 2221c9a5606SMike Frysinger INIT_S5P_SERIAL_STRUCTURE(0, "s5pser0"); 22346a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(1); 22446a3b5c8SMinkyu Kang struct serial_device s5p_serial1_device = 2251c9a5606SMike Frysinger INIT_S5P_SERIAL_STRUCTURE(1, "s5pser1"); 22646a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(2); 22746a3b5c8SMinkyu Kang struct serial_device s5p_serial2_device = 2281c9a5606SMike Frysinger INIT_S5P_SERIAL_STRUCTURE(2, "s5pser2"); 22946a3b5c8SMinkyu Kang DECLARE_S5P_SERIAL_FUNCTIONS(3); 23046a3b5c8SMinkyu Kang struct serial_device s5p_serial3_device = 2311c9a5606SMike Frysinger INIT_S5P_SERIAL_STRUCTURE(3, "s5pser3"); 2326c768ca7SMike Frysinger 233d4ec8f08SRajeshwari Shinde #ifdef CONFIG_OF_CONTROL 234d4ec8f08SRajeshwari Shinde int fdtdec_decode_console(int *index, struct fdt_serial *uart) 235d4ec8f08SRajeshwari Shinde { 236d4ec8f08SRajeshwari Shinde const void *blob = gd->fdt_blob; 237d4ec8f08SRajeshwari Shinde int node; 238d4ec8f08SRajeshwari Shinde 239d4ec8f08SRajeshwari Shinde node = fdt_path_offset(blob, "console"); 240d4ec8f08SRajeshwari Shinde if (node < 0) 241d4ec8f08SRajeshwari Shinde return node; 242d4ec8f08SRajeshwari Shinde 243d4ec8f08SRajeshwari Shinde uart->base_addr = fdtdec_get_addr(blob, node, "reg"); 244d4ec8f08SRajeshwari Shinde if (uart->base_addr == FDT_ADDR_T_NONE) 245d4ec8f08SRajeshwari Shinde return -FDT_ERR_NOTFOUND; 246d4ec8f08SRajeshwari Shinde 247d4ec8f08SRajeshwari Shinde uart->port_id = fdtdec_get_int(blob, node, "id", -1); 248d4ec8f08SRajeshwari Shinde uart->enabled = fdtdec_get_is_enabled(blob, node); 249d4ec8f08SRajeshwari Shinde 250d4ec8f08SRajeshwari Shinde return 0; 251d4ec8f08SRajeshwari Shinde } 252d4ec8f08SRajeshwari Shinde #endif 253d4ec8f08SRajeshwari Shinde 2546c768ca7SMike Frysinger __weak struct serial_device *default_serial_console(void) 2556c768ca7SMike Frysinger { 256d4ec8f08SRajeshwari Shinde #ifdef CONFIG_OF_CONTROL 257d4ec8f08SRajeshwari Shinde int index = 0; 258d4ec8f08SRajeshwari Shinde 259d4ec8f08SRajeshwari Shinde if ((!config.base_addr) && (fdtdec_decode_console(&index, &config))) { 260d4ec8f08SRajeshwari Shinde debug("Cannot decode default console node\n"); 261d4ec8f08SRajeshwari Shinde return NULL; 262d4ec8f08SRajeshwari Shinde } 263d4ec8f08SRajeshwari Shinde 264d4ec8f08SRajeshwari Shinde switch (config.port_id) { 265d4ec8f08SRajeshwari Shinde case 0: 266d4ec8f08SRajeshwari Shinde return &s5p_serial0_device; 267d4ec8f08SRajeshwari Shinde case 1: 268d4ec8f08SRajeshwari Shinde return &s5p_serial1_device; 269d4ec8f08SRajeshwari Shinde case 2: 270d4ec8f08SRajeshwari Shinde return &s5p_serial2_device; 271d4ec8f08SRajeshwari Shinde case 3: 272d4ec8f08SRajeshwari Shinde return &s5p_serial3_device; 273d4ec8f08SRajeshwari Shinde default: 274d4ec8f08SRajeshwari Shinde debug("Unknown config.port_id: %d", config.port_id); 275d4ec8f08SRajeshwari Shinde break; 276d4ec8f08SRajeshwari Shinde } 277d4ec8f08SRajeshwari Shinde 278d4ec8f08SRajeshwari Shinde return NULL; 279d4ec8f08SRajeshwari Shinde #else 280d4ec8f08SRajeshwari Shinde config.enabled = 1; 2816c768ca7SMike Frysinger #if defined(CONFIG_SERIAL0) 2826c768ca7SMike Frysinger return &s5p_serial0_device; 2836c768ca7SMike Frysinger #elif defined(CONFIG_SERIAL1) 2846c768ca7SMike Frysinger return &s5p_serial1_device; 2856c768ca7SMike Frysinger #elif defined(CONFIG_SERIAL2) 2866c768ca7SMike Frysinger return &s5p_serial2_device; 2876c768ca7SMike Frysinger #elif defined(CONFIG_SERIAL3) 2886c768ca7SMike Frysinger return &s5p_serial3_device; 2896c768ca7SMike Frysinger #else 2906c768ca7SMike Frysinger #error "CONFIG_SERIAL? missing." 2916c768ca7SMike Frysinger #endif 292d4ec8f08SRajeshwari Shinde #endif 2936c768ca7SMike Frysinger } 294b4980515SMarek Vasut 295b4980515SMarek Vasut void s5p_serial_initialize(void) 296b4980515SMarek Vasut { 297b4980515SMarek Vasut serial_register(&s5p_serial0_device); 298b4980515SMarek Vasut serial_register(&s5p_serial1_device); 299b4980515SMarek Vasut serial_register(&s5p_serial2_device); 300b4980515SMarek Vasut serial_register(&s5p_serial3_device); 301b4980515SMarek Vasut } 302