X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=drivers%2Fserial%2Fserial_s5p.c;h=3f0b5882541bfe9a320e01972af6ff4c1e095d58;hp=68b8d0101bfc7258c5f1605f9a23e88c879953b5;hb=f0844820609e58eefc122700722d7c8c9f0cbe84;hpb=5aa02e4d6a0ef5995ded70dc37b7f06e35fa6e21 diff --git a/drivers/serial/serial_s5p.c b/drivers/serial/serial_s5p.c index 68b8d0101b..3f0b588254 100644 --- a/drivers/serial/serial_s5p.c +++ b/drivers/serial/serial_s5p.c @@ -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 +#include +#include +#include +#include #include -#include #include +#include #include -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 + +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