diff options
Diffstat (limited to 'core/drivers/s5p6818_uart.c')
-rw-r--r-- | core/drivers/s5p6818_uart.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/core/drivers/s5p6818_uart.c b/core/drivers/s5p6818_uart.c new file mode 100644 index 0000000..659831f --- /dev/null +++ b/core/drivers/s5p6818_uart.c @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * Author: Bon-gyu, KOO <freestyle@nexell.co.kr> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <compiler.h> +#include <console.h> +#include <drivers/s5p6818_uart.h> +#include <io.h> +#include <keep.h> +#include <util.h> +#include <platform_config.h> + +/* NX_UART Registers */ +#define UARTDLCON 0x00 /* Line Control */ +#define UARTRUCON 0x04 /* Control */ +#define UARTEFCON 0x08 /* FIFO Control */ +#define UARTFMCON 0x0C /* Modem Control */ +#define UARTTRSTAT 0x10 /* Tx/Rx Status */ +#define UARTERSTAT 0x14 /* Rx Error Status */ +#define UARTFSTAT 0x18 /* FIFO Status */ +#define UARTMSTAT 0x1C /* Modem Status */ +#define UARTTXH 0x20 /* Transmit Buffer */ +#define UARTRXH 0x24 /* Receive Buffer */ +#define UARTBRDR 0x28 /* Baud Rate Driver */ +#define UARTFRACVAL 0x2C /* Driver Fractional Value */ +#define UARTINTP 0x30 /* Instrrupt Pending */ +#define UARTINTSP 0x34 /* Instrrupt Source */ +#define UARTINTM 0x38 /* Instrrupt Mask */ + +#define NUMBER_OF_RESET_MODULE_PIN 69 +#define UART_RST_BASE 50 + +#define NX_UARTFSTAT_TXFF_BIT (1 << 24) + /* Transmit FIFO full bit in UARTFSTAT register */ +#define NX_UARTFSTAT_RXFE_BIT (1 << 2) + /* Receive FIFO empty bit in UARTFR register */ + +#define NX_UARTTRSTAT_TX_EMPTY_BIT (1 << 2) + /* Transmit empty bit in UARTTRSTAT register */ +#define NX_UARTTRSTAT_TXBE_BIT (1 << 1) + /* Transmit BUFFER empty bit in UARTTRSTAT register */ + +/* FIXME - Modify and use it after confirming the operation. */ +#ifdef CFG_S5P6818_TIEOFF +static int s5p6818_uart_get_ch_num(vaddr_t base) +{ + int ch = 0; + + switch (base) { + case UART0_BASE: + ch = 0; + break; + case UART1_BASE: + ch = 1; + break; + case UART2_BASE: + ch = 2; + break; + case UART3_BASE: + ch = 3; + break; + case UART4_BASE: + ch = 4; + break; + case UART5_BASE: + ch = 5; + break; + default: + break; + }; + return ch; +} + +/* + * FIXME modify after clock control interface. + */ +static vaddr_t get_clock_base(uint32_t ch) +{ + vaddr_t base = 0; + + switch (ch) { + case 0: + base = 0xc00A9000; + break; + case 1: + base = 0xc00A8000; + break; + case 2: + base = 0xc00AA000; + break; + case 3: + base = 0xc00AB000; + break; + case 4: + base = 0xc00E0000; + break; + case 5: + base = 0xc00B1000; + break; + default: + break; + }; + return base; +} + +static void uart_clk_enb(uint32_t ch) +{ + vaddr_t base = get_clock_base(ch); + + write32(0x4, base); + write32(0x68, base+0x4); +} + +/* For Reset control */ + +struct nx_rstcon_registerset { + uint32_t regrst[(NUMBER_OF_RESET_MODULE_PIN+31)>>5]; +}; + +static struct nx_rstcon_registerset *nx_rstcon += (struct nx_rstcon_registerset *)0xC0012000; + +static void nx_rstcon_setrst(uint32_t rstindex, uint32_t status) +{ + uint32_t regnum, bitpos, curstat; + + regnum = rstindex >> 5; + curstat = (uint32_t)read32((vaddr_t)(&nx_rstcon->regrst[regnum])); + bitpos = rstindex & 0x1f; + curstat &= ~(1UL << bitpos); + curstat |= (status & 0x01) << bitpos; + write32(curstat, (vaddr_t)(&nx_rstcon->regrst[regnum])); +} + +/* tieoff setting */ +static void uart_tieoff_set(uint32_t ch) +{ + switch (ch) { + case 0: + nx_tieoff_set(NX_TIEOFF_UART0_USESMC, 0); + nx_tieoff_set(NX_TIEOFF_UART0_SMCTXENB, 0); + nx_tieoff_set(NX_TIEOFF_UART0_SMCRXENB, 0); + break; + case 1: + nx_tieoff_set(NX_TIEOFF_UART1_USESMC, 0); + nx_tieoff_set(NX_TIEOFF_UART1_SMCTXENB, 0); + nx_tieoff_set(NX_TIEOFF_UART1_SMCRXENB, 0); + break; + case 2: + nx_tieoff_set(NX_TIEOFF_UART2_USESMC, 0); + nx_tieoff_set(NX_TIEOFF_UART2_SMCTXENB, 0); + nx_tieoff_set(NX_TIEOFF_UART2_SMCRXENB, 0); + break; + case 3: + nx_tieoff_set(NX_TIEOFF_UART3_USESMC, 0); + nx_tieoff_set(NX_TIEOFF_UART3_SMCTXENB, 0); + nx_tieoff_set(NX_TIEOFF_UART3_SMCRXENB, 0); + break; + case 4: + nx_tieoff_set(NX_TIEOFF_UART4_USESMC, 0); + nx_tieoff_set(NX_TIEOFF_UART4_SMCTXENB, 0); + nx_tieoff_set(NX_TIEOFF_UART4_SMCRXENB, 0); + break; + case 5: + nx_tieoff_set(NX_TIEOFF_UART5_USESMC, 0); + nx_tieoff_set(NX_TIEOFF_UART5_SMCTXENB, 0); + nx_tieoff_set(NX_TIEOFF_UART5_SMCRXENB, 0); + break; + default: + break; + + }; +} + +void s5p6818_uart_tieoff_init(paddr_t base, uint32_t uart_clk, uint32_t baud_rate) +{ + int ch = s5p6818_uart_get_ch_num(base); + + nx_rstcon_setrst(UART_RST_BASE+ch, 1); /* reset */ + uart_tieoff_set(ch); + uart_clk_enb(ch); + + write32(0x3, base + UARTEFCON); + write32(0, base + UARTFMCON); + /* 8N1 */ + write32(0x3, base + UARTDLCON); + /* No interrupts, no DMA, pure polling */ + write32(0x245, base + UARTRUCON); + + if (baud_rate) { + int divisor = uart_clk / baud_rate; + + write32(divisor / 16 - 1, base + UARTBRDR); + write32(divisor % 16, base + UARTFRACVAL); + } +} +#endif + +static vaddr_t chip_to_base(struct serial_chip *chip) +{ + struct s5p6818_uart_data *pd = + container_of(chip, struct s5p6818_uart_data, chip); + + return io_pa_or_va(&pd->base); +} + +static void s5p6818_uart_flush(struct serial_chip *chip) +{ + vaddr_t base = chip_to_base(chip); + + while (!(read32(base + UARTTRSTAT) & NX_UARTTRSTAT_TX_EMPTY_BIT)) + ; +} + +static bool s5p6818_uart_have_rx_data(struct serial_chip *chip) +{ + vaddr_t base = chip_to_base(chip); + + return !(read32(base + UARTFSTAT) & NX_UARTFSTAT_RXFE_BIT); +} + +static int s5p6818_uart_getchar(struct serial_chip *chip) +{ + vaddr_t base = chip_to_base(chip); + + while (!s5p6818_uart_have_rx_data(chip)) + ; + return read32(base + UARTRXH) & 0xff; +} + +static void s5p6818_uart_putc(struct serial_chip *chip, int ch) +{ + vaddr_t base = chip_to_base(chip); + + s5p6818_uart_flush(chip); + + /* Send the character */ + write32(ch, base + UARTTXH); +} + +static const struct serial_ops s5p6818_uart_ops = { + .flush = s5p6818_uart_flush, + .getchar = s5p6818_uart_getchar, + .have_rx_data = s5p6818_uart_have_rx_data, + .putc = s5p6818_uart_putc, +}; +KEEP_PAGER(s5p6818_uart_ops); + +void s5p6818_uart_init(struct s5p6818_uart_data *pd, paddr_t base, + uint32_t __unused uart_clk, + uint32_t __unused baud_rate) +{ + pd->base.pa = base; + pd->chip.ops = &s5p6818_uart_ops; + +#ifdef CFG_S5P6818_TIEOFF + s5p6818_uart_tieoff_init(base, uart_clk, baud_rate); +#endif + return; +} + |