📄 s3c2410.c.txt
字号:
1 /*
2 * linux/drivers/serial/s3c2410.c
3 *
4 * Driver for onboard UARTs on the Samsung S3C24XX
5 *
6 * Based on drivers/char/serial.c and drivers/char/21285.c
7 *
8 * Ben Dooks, (c) 2003-2005 Simtec Electronics
9 * http://www.simtec.co.uk/products/SWLINUX/
10 *
11 * Changelog:
12 *
13 * 22-Jul-2004 BJD Finished off device rewrite
14 *
15 * 21-Jul-2004 BJD Thanks to <herbet@13thfloor.at> for pointing out
16 * problems with baud rate and loss of IR settings. Update
17 * to add configuration via platform_device structure
18 *
19 * 28-Sep-2004 BJD Re-write for the following items
20 * - S3C2410 and S3C2440 serial support
21 * - Power Management support
22 * - Fix console via IrDA devices
23 * - SysReq (Herbert P?tzl)
24 * - Break character handling (Herbert P?tzl)
25 * - spin-lock initialisation (Dimitry Andric)
26 * - added clock control
27 * - updated init code to use platform_device info
28 *
29 * 06-Mar-2005 BJD Add s3c2440 fclk clock source
30 *
31 * 09-Mar-2005 BJD Add s3c2400 support
32 *
33 * 10-Mar-2005 LCVR Changed S3C2410_VA_UART to S3C24XX_VA_UART
34 */
35
36 /* Note on 2440 fclk clock source handling
37 *
38 * Whilst it is possible to use the fclk as clock source, the method
39 * of properly switching too/from this is currently un-implemented, so
40 * whichever way is configured at startup is the one that will be used.
41 */
42
43 /* Hote on 2410 error handling
44 *
45 * The s3c2410 manual has a love/hate affair with the contents of the
46 * UERSTAT register in the UART blocks, and keeps marking some of the
47 * error bits as reserved. Having checked with the s3c2410x01,
48 * it copes with BREAKs properly, so I am happy to ignore the RESERVED
49 * feature from the latter versions of the manual.
50 *
51 * If it becomes aparrent that latter versions of the 2410 remove these
52 * bits, then action will have to be taken to differentiate the versions
53 * and change the policy on BREAK
54 *
55 * BJD, 04-Nov-2004
56 */
57
58 #include <linux/config.h>
59
60 #if defined(CONFIG_SERIAL_S3C2410_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
61 #define SUPPORT_SYSRQ
62 #endif
63
64 #include <linux/module.h>
65 #include <linux/ioport.h>
66 #include <linux/platform_device.h>
67 #include <linux/init.h>
68 #include <linux/sysrq.h>
69 #include <linux/console.h>
70 #include <linux/tty.h>
71 #include <linux/tty_flip.h>
72 #include <linux/serial_core.h>
73 #include <linux/serial.h>
74 #include <linux/delay.h>
75 #include <linux/clk.h>
76
77 #include <asm/io.h>
78 #include <asm/irq.h>
79
80 #include <asm/hardware.h>
81
82 #include <asm/arch/regs-serial.h>
83 #include <asm/arch/regs-gpio.h>
84
85 /* structures */
86
87 struct s3c24xx_uart_info {
88 char *name;
89 unsigned int type;
90 unsigned int fifosize;
91 unsigned long rx_fifomask;
92 unsigned long rx_fifoshift;
93 unsigned long rx_fifofull;
94 unsigned long tx_fifomask;
95 unsigned long tx_fifoshift;
96 unsigned long tx_fifofull;
97
98 /* clock source control */
99
100 int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
101 int (*set_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
102
103 /* uart controls */
104 int (*reset_port)(struct uart_port *, struct s3c2410_uartcfg *);
105 };
106
107 struct s3c24xx_uart_port {
108 unsigned char rx_claimed;
109 unsigned char tx_claimed;
110
111 struct s3c24xx_uart_info *info;
112 struct s3c24xx_uart_clksrc *clksrc;
113 struct clk *clk;
114 struct clk *baudclk;
115 struct uart_port port;
116 };
117
118
119 /* configuration defines */
120
121 #if 0
122 #if 1
123 /* send debug to the low-level output routines */
124
125 extern void printascii(const char *);
126
127 static void
128 s3c24xx_serial_dbg(const char *fmt, ...)
129 {
130 va_list va;
131 char buff[256];
132
133 va_start(va, fmt);
134 vsprintf(buff, fmt, va);
135 va_end(va);
136
137 printascii(buff);
138 }
139
140 #define dbg(x...) s3c24xx_serial_dbg(x)
141
142 #else
143 #define dbg(x...) printk(KERN_DEBUG "s3c24xx: ");
144 #endif
145 #else /* no debug */
146 #define dbg(x...) do {} while(0)
147 #endif
148
149 /* UART name and device definitions */
150
151 #define S3C24XX_SERIAL_NAME "ttySAC"
152 #define S3C24XX_SERIAL_DEVFS "tts/"
153 #define S3C24XX_SERIAL_MAJOR 204
154 #define S3C24XX_SERIAL_MINOR 64
155
156
157 /* conversion functions */
158
159 #define s3c24xx_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
160 #define s3c24xx_dev_to_cfg(__dev) (struct s3c2410_uartcfg *)((__dev)->platform_data)
161
162 /* we can support 3 uarts, but not always use them */
163
164 #ifdef CONFIG_CPU_S3C2400
165 #define NR_PORTS (2)
166 #else
167 #define NR_PORTS (3)
168 #endif
169
170 /* port irq numbers */
171
172 #define TX_IRQ(port) ((port)->irq + 1)
173 #define RX_IRQ(port) ((port)->irq)
174
175 /* register access controls */
176
177 #define portaddr(port, reg) ((port)->membase + (reg))
178
179 #define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
180 #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
181
182 #define wr_regb(port, reg, val) \
183 do { __raw_writeb(val, portaddr(port, reg)); } while(0)
184
185 #define wr_regl(port, reg, val) \
186 do { __raw_writel(val, portaddr(port, reg)); } while(0)
187
188 /* macros to change one thing to another */
189
190 #define tx_enabled(port) ((port)->unused[0])
191 #define rx_enabled(port) ((port)->unused[1])
192
193 /* flag to ignore all characters comming in */
194 #define RXSTAT_DUMMY_READ (0x10000000)
195
196 static inline struct s3c24xx_uart_port *to_ourport(struct uart_port *port)
197 {
198 return container_of(port, struct s3c24xx_uart_port, port);
199 }
200
201 /* translate a port to the device name */
202
203 static inline const char *s3c24xx_serial_portname(struct uart_port *port)
204 {
205 return to_platform_device(port->dev)->name;
206 }
207
208 static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
209 {
210 return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);
211 }
212
213 static void s3c24xx_serial_rx_enable(struct uart_port *port)
214 {
215 unsigned long flags;
216 unsigned int ucon, ufcon;
217 int count = 10000;
218
219 spin_lock_irqsave(&port->lock, flags);
220
221 while (--count && !s3c24xx_serial_txempty_nofifo(port))
222 udelay(100);
223
224 ufcon = rd_regl(port, S3C2410_UFCON);
225 ufcon |= S3C2410_UFCON_RESETRX;
226 wr_regl(port, S3C2410_UFCON, ufcon);
227
228 ucon = rd_regl(port, S3C2410_UCON);
229 ucon |= S3C2410_UCON_RXIRQMODE;
230 wr_regl(port, S3C2410_UCON, ucon);
231
232 rx_enabled(port) = 1;
233 spin_unlock_irqrestore(&port->lock, flags);
234 }
235
236 static void s3c24xx_serial_rx_disable(struct uart_port *port)
237 {
238 unsigned long flags;
239 unsigned int ucon;
240
241 spin_lock_irqsave(&port->lock, flags);
242
243 ucon = rd_regl(port, S3C2410_UCON);
244 ucon &= ~S3C2410_UCON_RXIRQMODE;
245 wr_regl(port, S3C2410_UCON, ucon);
246
247 rx_enabled(port) = 0;
248 spin_unlock_irqrestore(&port->lock, flags);
249 }
250
251 static void s3c24xx_serial_stop_tx(struct uart_port *port)
252 {
253 if (tx_enabled(port)) {
254 disable_irq(TX_IRQ(port));
255 tx_enabled(port) = 0;
256 if (port->flags & UPF_CONS_FLOW)
257 s3c24xx_serial_rx_enable(port);
258 }
259 }
260
261 static void s3c24xx_serial_start_tx(struct uart_port *port)
262 {
263 if (!tx_enabled(port)) {
264 if (port->flags & UPF_CONS_FLOW)
265 s3c24xx_serial_rx_disable(port);
266
267 enable_irq(TX_IRQ(port));
268 tx_enabled(port) = 1;
269 }
270 }
271
272
273 static void s3c24xx_serial_stop_rx(struct uart_port *port)
274 {
275 if (rx_enabled(port)) {
276 dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
277 disable_irq(RX_IRQ(port));
278 rx_enabled(port) = 0;
279 }
280 }
281
282 static void s3c24xx_serial_enable_ms(struct uart_port *port)
283 {
284 }
285
286 static inline struct s3c24xx_uart_info *s3c24xx_port_to_info(struct uart_port *port)
287 {
288 return to_ourport(port)->info;
289 }
290
291 static inline struct s3c2410_uartcfg *s3c24xx_port_to_cfg(struct uart_port *port)
292 {
293 if (port->dev == NULL)
294 return NULL;
295
296 return (struct s3c2410_uartcfg *)port->dev->platform_data;
297 }
298
299 static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
300 unsigned long ufstat)
301 {
302 struct s3c24xx_uart_info *info = ourport->info;
303
304 if (ufstat & info->rx_fifofull)
305 return info->fifosize;
306
307 return (ufstat & info->rx_fifomask) >> info->rx_fifoshift;
308 }
309
310
311 /* ? - where has parity gone?? */
312 #define S3C2410_UERSTAT_PARITY (0x1000)
313
314 static irqreturn_t
315 s3c24xx_serial_rx_chars(int irq, void *dev_id, struct pt_regs *regs)
316 {
317 struct s3c24xx_uart_port *ourport = dev_id;
318 struct uart_port *port = &ourport->port;
319 struct tty_struct *tty = port->info->tty;
320 unsigned int ufcon, ch, flag, ufstat, uerstat;
321 int max_count = 64;
322
323 while (max_count-- > 0) {
324 ufcon = rd_regl(port, S3C2410_UFCON);
325 ufstat = rd_regl(port, S3C2410_UFSTAT);
326
327 if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
328 break;
329
330 uerstat = rd_regl(port, S3C2410_UERSTAT);
331 ch = rd_regb(port, S3C2410_URXH);
332
333 if (port->flags & UPF_CONS_FLOW) {
334 int txe = s3c24xx_serial_txempty_nofifo(port);
335
336 if (rx_enabled(port)) {
337 if (!txe) {
338 rx_enabled(port) = 0;
339 continue;
340 }
341 } else {
342 if (txe) {
343 ufcon |= S3C2410_UFCON_RESETRX;
344 wr_regl(port, S3C2410_UFCON, ufcon);
345 rx_enabled(port) = 1;
346 goto out;
347 }
348 continue;
349 }
350 }
351
352 /* insert the character into the buffer */
353
354 flag = TTY_NORMAL;
355 port->icount.rx++;
356
357 if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
358 dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
359 ch, uerstat);
360
361 /* check for break */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -