summaryrefslogtreecommitdiff
path: root/core/drivers/s5p6818_uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/drivers/s5p6818_uart.c')
-rw-r--r--core/drivers/s5p6818_uart.c285
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;
+}
+