blob: 6a61b4fa5dfdbc7cb523239eba77b365ef2b9f2a [file] [log] [blame]
Minkyu Kangfca30842009-10-01 17:20:28 +09001/*
2 * (C) Copyright 2009 SAMSUNG Electronics
3 * Minkyu Kang <mk7.kang@samsung.com>
4 * Heungjun Kim <riverful.kim@samsung.com>
5 *
6 * based on drivers/serial/s3c64xx.c
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <common.h>
25#include <asm/io.h>
26#include <asm/arch/uart.h>
27#include <asm/arch/clk.h>
28#include <serial.h>
29
Minkyu Kangbaa36882010-03-24 16:59:30 +090030static inline struct s5p_uart *s5p_get_base_uart(int dev_index)
Minkyu Kangfca30842009-10-01 17:20:28 +090031{
Minkyu Kangbaa36882010-03-24 16:59:30 +090032 u32 offset = dev_index * sizeof(struct s5p_uart);
Minkyu Kangc8189842010-08-13 16:07:35 +090033 return (struct s5p_uart *)(samsung_get_base_uart() + offset);
Minkyu Kangfca30842009-10-01 17:20:28 +090034}
35
36/*
Minkyu Kangbaa36882010-03-24 16:59:30 +090037 * The coefficient, used to calculate the baudrate on S5P UARTs is
Minkyu Kangfca30842009-10-01 17:20:28 +090038 * calculated as
39 * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT
40 * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1,
41 * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants:
42 */
43static const int udivslot[] = {
44 0,
45 0x0080,
46 0x0808,
47 0x0888,
48 0x2222,
49 0x4924,
50 0x4a52,
51 0x54aa,
52 0x5555,
53 0xd555,
54 0xd5d5,
55 0xddd5,
56 0xdddd,
57 0xdfdd,
58 0xdfdf,
59 0xffdf,
60};
61
62void serial_setbrg_dev(const int dev_index)
63{
64 DECLARE_GLOBAL_DATA_PTR;
Minkyu Kangbaa36882010-03-24 16:59:30 +090065 struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
Minkyu Kangfca30842009-10-01 17:20:28 +090066 u32 pclk = get_pclk();
67 u32 baudrate = gd->baudrate;
68 u32 val;
69
70 val = pclk / baudrate;
71
72 writel(val / 16 - 1, &uart->ubrdiv);
Minkyu Kangc6cb1842009-10-15 11:19:15 +090073 writew(udivslot[val % 16], &uart->udivslot);
Minkyu Kangfca30842009-10-01 17:20:28 +090074}
75
76/*
77 * Initialise the serial port with the given baudrate. The settings
78 * are always 8 data bits, no parity, 1 stop bit, no start bits.
79 */
80int serial_init_dev(const int dev_index)
81{
Minkyu Kangbaa36882010-03-24 16:59:30 +090082 struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
Minkyu Kangfca30842009-10-01 17:20:28 +090083
84 /* reset and enable FIFOs, set triggers to the maximum */
85 writel(0, &uart->ufcon);
86 writel(0, &uart->umcon);
87 /* 8N1 */
88 writel(0x3, &uart->ulcon);
89 /* No interrupts, no DMA, pure polling */
90 writel(0x245, &uart->ucon);
91
92 serial_setbrg_dev(dev_index);
93
94 return 0;
95}
96
Minkyu Kang9455aab2009-11-10 20:23:50 +090097static int serial_err_check(const int dev_index, int op)
Minkyu Kangfca30842009-10-01 17:20:28 +090098{
Minkyu Kangbaa36882010-03-24 16:59:30 +090099 struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
Minkyu Kang9455aab2009-11-10 20:23:50 +0900100 unsigned int mask;
Minkyu Kangfca30842009-10-01 17:20:28 +0900101
Minkyu Kang9455aab2009-11-10 20:23:50 +0900102 /*
103 * UERSTAT
104 * Break Detect [3]
105 * Frame Err [2] : receive operation
106 * Parity Err [1] : receive operation
107 * Overrun Err [0] : receive operation
108 */
109 if (op)
110 mask = 0x8;
111 else
112 mask = 0xf;
Minkyu Kangfca30842009-10-01 17:20:28 +0900113
Minkyu Kang9455aab2009-11-10 20:23:50 +0900114 return readl(&uart->uerstat) & mask;
Minkyu Kangfca30842009-10-01 17:20:28 +0900115}
116
117/*
118 * Read a single byte from the serial port. Returns 1 on success, 0
119 * otherwise. When the function is succesfull, the character read is
120 * written into its argument c.
121 */
122int serial_getc_dev(const int dev_index)
123{
Minkyu Kangbaa36882010-03-24 16:59:30 +0900124 struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
Minkyu Kangfca30842009-10-01 17:20:28 +0900125
126 /* wait for character to arrive */
127 while (!(readl(&uart->utrstat) & 0x1)) {
Minkyu Kang9455aab2009-11-10 20:23:50 +0900128 if (serial_err_check(dev_index, 0))
Minkyu Kangfca30842009-10-01 17:20:28 +0900129 return 0;
130 }
131
Minkyu Kang76229812010-07-06 20:08:29 +0900132 return (int)(readb(&uart->urxh) & 0xff);
Minkyu Kangfca30842009-10-01 17:20:28 +0900133}
134
135/*
136 * Output a single byte to the serial port.
137 */
138void serial_putc_dev(const char c, const int dev_index)
139{
Minkyu Kangbaa36882010-03-24 16:59:30 +0900140 struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
Minkyu Kangfca30842009-10-01 17:20:28 +0900141
142 /* wait for room in the tx FIFO */
143 while (!(readl(&uart->utrstat) & 0x2)) {
Minkyu Kang9455aab2009-11-10 20:23:50 +0900144 if (serial_err_check(dev_index, 1))
Minkyu Kangfca30842009-10-01 17:20:28 +0900145 return;
146 }
147
Minkyu Kang76229812010-07-06 20:08:29 +0900148 writeb(c, &uart->utxh);
Minkyu Kangfca30842009-10-01 17:20:28 +0900149
150 /* If \n, also do \r */
151 if (c == '\n')
152 serial_putc('\r');
153}
154
155/*
156 * Test whether a character is in the RX buffer
157 */
158int serial_tstc_dev(const int dev_index)
159{
Minkyu Kangbaa36882010-03-24 16:59:30 +0900160 struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
Minkyu Kangfca30842009-10-01 17:20:28 +0900161
162 return (int)(readl(&uart->utrstat) & 0x1);
163}
164
165void serial_puts_dev(const char *s, const int dev_index)
166{
167 while (*s)
168 serial_putc_dev(*s++, dev_index);
169}
170
171/* Multi serial device functions */
172#define DECLARE_S5P_SERIAL_FUNCTIONS(port) \
173int s5p_serial##port##_init(void) { return serial_init_dev(port); } \
174void s5p_serial##port##_setbrg(void) { serial_setbrg_dev(port); } \
175int s5p_serial##port##_getc(void) { return serial_getc_dev(port); } \
176int s5p_serial##port##_tstc(void) { return serial_tstc_dev(port); } \
177void s5p_serial##port##_putc(const char c) { serial_putc_dev(c, port); } \
178void s5p_serial##port##_puts(const char *s) { serial_puts_dev(s, port); }
179
180#define INIT_S5P_SERIAL_STRUCTURE(port, name, bus) { \
181 name, \
182 bus, \
183 s5p_serial##port##_init, \
Anatolij Gustschin99d25f92010-04-24 19:27:04 +0200184 NULL, \
Minkyu Kangfca30842009-10-01 17:20:28 +0900185 s5p_serial##port##_setbrg, \
186 s5p_serial##port##_getc, \
187 s5p_serial##port##_tstc, \
188 s5p_serial##port##_putc, \
189 s5p_serial##port##_puts, }
190
191DECLARE_S5P_SERIAL_FUNCTIONS(0);
Minkyu Kangbaa36882010-03-24 16:59:30 +0900192struct serial_device s5p_serial0_device =
Minkyu Kangfca30842009-10-01 17:20:28 +0900193 INIT_S5P_SERIAL_STRUCTURE(0, "s5pser0", "S5PUART0");
194DECLARE_S5P_SERIAL_FUNCTIONS(1);
Minkyu Kangbaa36882010-03-24 16:59:30 +0900195struct serial_device s5p_serial1_device =
Minkyu Kangfca30842009-10-01 17:20:28 +0900196 INIT_S5P_SERIAL_STRUCTURE(1, "s5pser1", "S5PUART1");
197DECLARE_S5P_SERIAL_FUNCTIONS(2);
Minkyu Kangbaa36882010-03-24 16:59:30 +0900198struct serial_device s5p_serial2_device =
Minkyu Kangfca30842009-10-01 17:20:28 +0900199 INIT_S5P_SERIAL_STRUCTURE(2, "s5pser2", "S5PUART2");
200DECLARE_S5P_SERIAL_FUNCTIONS(3);
Minkyu Kangbaa36882010-03-24 16:59:30 +0900201struct serial_device s5p_serial3_device =
Minkyu Kangfca30842009-10-01 17:20:28 +0900202 INIT_S5P_SERIAL_STRUCTURE(3, "s5pser3", "S5PUART3");