]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/serial/serial_s5p.c
dm: Use dev_get_addr() where possible
[karo-tx-uboot.git] / drivers / serial / serial_s5p.c
index 68b8d0101bfc7258c5f1605f9a23e88c879953b5..3f0b5882541bfe9a320e01972af6ff4c1e095d58 100644 (file)
@@ -5,37 +5,33 @@
  *
  * based on drivers/serial/s3c64xx.c
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
+ * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <linux/compiler.h>
 #include <asm/io.h>
-#include <asm/arch/uart.h>
 #include <asm/arch/clk.h>
+#include <asm/arch/uart.h>
 #include <serial.h>
 
-static inline struct s5p_uart *s5p_get_base_uart(int dev_index)
-{
-       u32 offset = dev_index * sizeof(struct s5p_uart);
+DECLARE_GLOBAL_DATA_PTR;
 
-       if (cpu_is_s5pc100())
-               return (struct s5p_uart *)(S5PC100_UART_BASE + offset);
-       else
-               return (struct s5p_uart *)(S5PC110_UART_BASE + offset);
-}
+#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)
+
+/* Information about a serial port */
+struct s5p_serial_platdata {
+       struct s5p_uart *reg;  /* address of registers in physical memory */
+       u8 port_id;     /* uart port number */
+};
 
 /*
  * The coefficient, used to calculate the baudrate on S5P UARTs is
@@ -63,44 +59,56 @@ static const int udivslot[] = {
        0xffdf,
 };
 
-void serial_setbrg_dev(const int dev_index)
+static void __maybe_unused s5p_serial_init(struct s5p_uart *uart)
+{
+       /* enable FIFOs, auto clear Rx FIFO */
+       writel(0x3, &uart->ufcon);
+       writel(0, &uart->umcon);
+       /* 8N1 */
+       writel(0x3, &uart->ulcon);
+       /* No interrupts, no DMA, pure polling */
+       writel(0x245, &uart->ucon);
+}
+
+static void __maybe_unused s5p_serial_baud(struct s5p_uart *uart, uint uclk,
+                                          int baudrate)
 {
-       DECLARE_GLOBAL_DATA_PTR;
-       struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
-       u32 pclk = get_pclk();
-       u32 baudrate = gd->baudrate;
        u32 val;
 
-       val = pclk / baudrate;
+       val = uclk / baudrate;
 
        writel(val / 16 - 1, &uart->ubrdiv);
-       writew(udivslot[val % 16], &uart->udivslot);
+
+       if (s5p_uart_divslot())
+               writew(udivslot[val % 16], &uart->rest.slot);
+       else
+               writeb(val % 16, &uart->rest.value);
 }
 
-/*
- * Initialise the serial port with the given baudrate. The settings
- * are always 8 data bits, no parity, 1 stop bit, no start bits.
- */
-int serial_init_dev(const int dev_index)
+#ifndef CONFIG_SPL_BUILD
+int s5p_serial_setbrg(struct udevice *dev, int baudrate)
 {
-       struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+       struct s5p_serial_platdata *plat = dev->platdata;
+       struct s5p_uart *const uart = plat->reg;
+       u32 uclk = get_uart_clk(plat->port_id);
 
-       /* reset and enable FIFOs, set triggers to the maximum */
-       writel(0, &uart->ufcon);
-       writel(0, &uart->umcon);
-       /* 8N1 */
-       writel(0x3, &uart->ulcon);
-       /* No interrupts, no DMA, pure polling */
-       writel(0x245, &uart->ucon);
+       s5p_serial_baud(uart, uclk, baudrate);
+
+       return 0;
+}
+
+static int s5p_serial_probe(struct udevice *dev)
+{
+       struct s5p_serial_platdata *plat = dev->platdata;
+       struct s5p_uart *const uart = plat->reg;
 
-       serial_setbrg_dev(dev_index);
+       s5p_serial_init(uart);
 
        return 0;
 }
 
-static int serial_err_check(const int dev_index, int op)
+static int serial_err_check(const struct s5p_uart *const uart, int op)
 {
-       struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
        unsigned int mask;
 
        /*
@@ -118,88 +126,104 @@ static int serial_err_check(const int dev_index, int op)
        return readl(&uart->uerstat) & mask;
 }
 
-/*
- * Read a single byte from the serial port. Returns 1 on success, 0
- * otherwise. When the function is succesfull, the character read is
- * written into its argument c.
- */
-int serial_getc_dev(const int dev_index)
+static int s5p_serial_getc(struct udevice *dev)
 {
-       struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+       struct s5p_serial_platdata *plat = dev->platdata;
+       struct s5p_uart *const uart = plat->reg;
 
-       /* wait for character to arrive */
-       while (!(readl(&uart->utrstat) & 0x1)) {
-               if (serial_err_check(dev_index, 0))
-                       return 0;
-       }
+       if (!(readl(&uart->ufstat) & RX_FIFO_COUNT_MASK))
+               return -EAGAIN;
 
-       return (int)(readl(&uart->urxh) & 0xff);
+       serial_err_check(uart, 0);
+       return (int)(readb(&uart->urxh) & 0xff);
 }
 
-/*
- * Output a single byte to the serial port.
- */
-void serial_putc_dev(const char c, const int dev_index)
+static int s5p_serial_putc(struct udevice *dev, const char ch)
 {
-       struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+       struct s5p_serial_platdata *plat = dev->platdata;
+       struct s5p_uart *const uart = plat->reg;
 
-       /* wait for room in the tx FIFO */
-       while (!(readl(&uart->utrstat) & 0x2)) {
-               if (serial_err_check(dev_index, 1))
-                       return;
-       }
+       if (readl(&uart->ufstat) & TX_FIFO_FULL)
+               return -EAGAIN;
 
-       writel(c, &uart->utxh);
+       writeb(ch, &uart->utxh);
+       serial_err_check(uart, 1);
 
-       /* If \n, also do \r */
-       if (c == '\n')
-               serial_putc('\r');
+       return 0;
 }
 
-/*
- * Test whether a character is in the RX buffer
- */
-int serial_tstc_dev(const int dev_index)
+static int s5p_serial_pending(struct udevice *dev, bool input)
 {
-       struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+       struct s5p_serial_platdata *plat = dev->platdata;
+       struct s5p_uart *const uart = plat->reg;
+       uint32_t ufstat = readl(&uart->ufstat);
 
-       return (int)(readl(&uart->utrstat) & 0x1);
+       if (input)
+               return (ufstat & RX_FIFO_COUNT_MASK) >> RX_FIFO_COUNT_SHIFT;
+       else
+               return (ufstat & TX_FIFO_COUNT_MASK) >> TX_FIFO_COUNT_SHIFT;
 }
 
-void serial_puts_dev(const char *s, const int dev_index)
+static int s5p_serial_ofdata_to_platdata(struct udevice *dev)
 {
-       while (*s)
-               serial_putc_dev(*s++, dev_index);
+       struct s5p_serial_platdata *plat = dev->platdata;
+       fdt_addr_t addr;
+
+       addr = dev_get_addr(dev);
+       if (addr == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       plat->reg = (struct s5p_uart *)addr;
+       plat->port_id = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "id", -1);
+
+       return 0;
 }
 
-/* Multi serial device functions */
-#define DECLARE_S5P_SERIAL_FUNCTIONS(port) \
-int s5p_serial##port##_init(void) { return serial_init_dev(port); } \
-void s5p_serial##port##_setbrg(void) { serial_setbrg_dev(port); } \
-int s5p_serial##port##_getc(void) { return serial_getc_dev(port); } \
-int s5p_serial##port##_tstc(void) { return serial_tstc_dev(port); } \
-void s5p_serial##port##_putc(const char c) { serial_putc_dev(c, port); } \
-void s5p_serial##port##_puts(const char *s) { serial_puts_dev(s, port); }
-
-#define INIT_S5P_SERIAL_STRUCTURE(port, name, bus) { \
-       name, \
-       bus, \
-       s5p_serial##port##_init, \
-       s5p_serial##port##_setbrg, \
-       s5p_serial##port##_getc, \
-       s5p_serial##port##_tstc, \
-       s5p_serial##port##_putc, \
-       s5p_serial##port##_puts, }
-
-DECLARE_S5P_SERIAL_FUNCTIONS(0);
-struct serial_device s5p_serial0_device =
-       INIT_S5P_SERIAL_STRUCTURE(0, "s5pser0", "S5PUART0");
-DECLARE_S5P_SERIAL_FUNCTIONS(1);
-struct serial_device s5p_serial1_device =
-       INIT_S5P_SERIAL_STRUCTURE(1, "s5pser1", "S5PUART1");
-DECLARE_S5P_SERIAL_FUNCTIONS(2);
-struct serial_device s5p_serial2_device =
-       INIT_S5P_SERIAL_STRUCTURE(2, "s5pser2", "S5PUART2");
-DECLARE_S5P_SERIAL_FUNCTIONS(3);
-struct serial_device s5p_serial3_device =
-       INIT_S5P_SERIAL_STRUCTURE(3, "s5pser3", "S5PUART3");
+static const struct dm_serial_ops s5p_serial_ops = {
+       .putc = s5p_serial_putc,
+       .pending = s5p_serial_pending,
+       .getc = s5p_serial_getc,
+       .setbrg = s5p_serial_setbrg,
+};
+
+static const struct udevice_id s5p_serial_ids[] = {
+       { .compatible = "samsung,exynos4210-uart" },
+       { }
+};
+
+U_BOOT_DRIVER(serial_s5p) = {
+       .name   = "serial_s5p",
+       .id     = UCLASS_SERIAL,
+       .of_match = s5p_serial_ids,
+       .ofdata_to_platdata = s5p_serial_ofdata_to_platdata,
+       .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata),
+       .probe = s5p_serial_probe,
+       .ops    = &s5p_serial_ops,
+       .flags = DM_FLAG_PRE_RELOC,
+};
+#endif
+
+#ifdef CONFIG_DEBUG_UART_S5P
+
+#include <debug_uart.h>
+
+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);
+}
+
+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);
+
+       writeb(ch, &uart->utxh);
+}
+
+DEBUG_UART_FUNCS
+
+#endif