Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.1-rc10 1490 lines 35 kB view raw
1/* 2 * Driver core for Samsung SoC onboard UARTs. 3 * 4 * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics 5 * http://armlinux.simtec.co.uk/ 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10*/ 11 12/* Hote on 2410 error handling 13 * 14 * The s3c2410 manual has a love/hate affair with the contents of the 15 * UERSTAT register in the UART blocks, and keeps marking some of the 16 * error bits as reserved. Having checked with the s3c2410x01, 17 * it copes with BREAKs properly, so I am happy to ignore the RESERVED 18 * feature from the latter versions of the manual. 19 * 20 * If it becomes aparrent that latter versions of the 2410 remove these 21 * bits, then action will have to be taken to differentiate the versions 22 * and change the policy on BREAK 23 * 24 * BJD, 04-Nov-2004 25*/ 26 27#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) 28#define SUPPORT_SYSRQ 29#endif 30 31#include <linux/module.h> 32#include <linux/ioport.h> 33#include <linux/io.h> 34#include <linux/platform_device.h> 35#include <linux/init.h> 36#include <linux/sysrq.h> 37#include <linux/console.h> 38#include <linux/tty.h> 39#include <linux/tty_flip.h> 40#include <linux/serial_core.h> 41#include <linux/serial.h> 42#include <linux/delay.h> 43#include <linux/clk.h> 44#include <linux/cpufreq.h> 45 46#include <asm/irq.h> 47 48#include <mach/hardware.h> 49#include <mach/map.h> 50 51#include <plat/regs-serial.h> 52 53#include "samsung.h" 54 55/* UART name and device definitions */ 56 57#define S3C24XX_SERIAL_NAME "ttySAC" 58#define S3C24XX_SERIAL_MAJOR 204 59#define S3C24XX_SERIAL_MINOR 64 60 61/* macros to change one thing to another */ 62 63#define tx_enabled(port) ((port)->unused[0]) 64#define rx_enabled(port) ((port)->unused[1]) 65 66/* flag to ignore all characters coming in */ 67#define RXSTAT_DUMMY_READ (0x10000000) 68 69static inline struct s3c24xx_uart_port *to_ourport(struct uart_port *port) 70{ 71 return container_of(port, struct s3c24xx_uart_port, port); 72} 73 74/* translate a port to the device name */ 75 76static inline const char *s3c24xx_serial_portname(struct uart_port *port) 77{ 78 return to_platform_device(port->dev)->name; 79} 80 81static int s3c24xx_serial_txempty_nofifo(struct uart_port *port) 82{ 83 return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE); 84} 85 86static void s3c24xx_serial_rx_enable(struct uart_port *port) 87{ 88 unsigned long flags; 89 unsigned int ucon, ufcon; 90 int count = 10000; 91 92 spin_lock_irqsave(&port->lock, flags); 93 94 while (--count && !s3c24xx_serial_txempty_nofifo(port)) 95 udelay(100); 96 97 ufcon = rd_regl(port, S3C2410_UFCON); 98 ufcon |= S3C2410_UFCON_RESETRX; 99 wr_regl(port, S3C2410_UFCON, ufcon); 100 101 ucon = rd_regl(port, S3C2410_UCON); 102 ucon |= S3C2410_UCON_RXIRQMODE; 103 wr_regl(port, S3C2410_UCON, ucon); 104 105 rx_enabled(port) = 1; 106 spin_unlock_irqrestore(&port->lock, flags); 107} 108 109static void s3c24xx_serial_rx_disable(struct uart_port *port) 110{ 111 unsigned long flags; 112 unsigned int ucon; 113 114 spin_lock_irqsave(&port->lock, flags); 115 116 ucon = rd_regl(port, S3C2410_UCON); 117 ucon &= ~S3C2410_UCON_RXIRQMODE; 118 wr_regl(port, S3C2410_UCON, ucon); 119 120 rx_enabled(port) = 0; 121 spin_unlock_irqrestore(&port->lock, flags); 122} 123 124static void s3c24xx_serial_stop_tx(struct uart_port *port) 125{ 126 struct s3c24xx_uart_port *ourport = to_ourport(port); 127 128 if (tx_enabled(port)) { 129 disable_irq_nosync(ourport->tx_irq); 130 tx_enabled(port) = 0; 131 if (port->flags & UPF_CONS_FLOW) 132 s3c24xx_serial_rx_enable(port); 133 } 134} 135 136static void s3c24xx_serial_start_tx(struct uart_port *port) 137{ 138 struct s3c24xx_uart_port *ourport = to_ourport(port); 139 140 if (!tx_enabled(port)) { 141 if (port->flags & UPF_CONS_FLOW) 142 s3c24xx_serial_rx_disable(port); 143 144 enable_irq(ourport->tx_irq); 145 tx_enabled(port) = 1; 146 } 147} 148 149 150static void s3c24xx_serial_stop_rx(struct uart_port *port) 151{ 152 struct s3c24xx_uart_port *ourport = to_ourport(port); 153 154 if (rx_enabled(port)) { 155 dbg("s3c24xx_serial_stop_rx: port=%p\n", port); 156 disable_irq_nosync(ourport->rx_irq); 157 rx_enabled(port) = 0; 158 } 159} 160 161static void s3c24xx_serial_enable_ms(struct uart_port *port) 162{ 163} 164 165static inline struct s3c24xx_uart_info *s3c24xx_port_to_info(struct uart_port *port) 166{ 167 return to_ourport(port)->info; 168} 169 170static inline struct s3c2410_uartcfg *s3c24xx_port_to_cfg(struct uart_port *port) 171{ 172 if (port->dev == NULL) 173 return NULL; 174 175 return (struct s3c2410_uartcfg *)port->dev->platform_data; 176} 177 178static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport, 179 unsigned long ufstat) 180{ 181 struct s3c24xx_uart_info *info = ourport->info; 182 183 if (ufstat & info->rx_fifofull) 184 return info->fifosize; 185 186 return (ufstat & info->rx_fifomask) >> info->rx_fifoshift; 187} 188 189 190/* ? - where has parity gone?? */ 191#define S3C2410_UERSTAT_PARITY (0x1000) 192 193static irqreturn_t 194s3c24xx_serial_rx_chars(int irq, void *dev_id) 195{ 196 struct s3c24xx_uart_port *ourport = dev_id; 197 struct uart_port *port = &ourport->port; 198 struct tty_struct *tty = port->state->port.tty; 199 unsigned int ufcon, ch, flag, ufstat, uerstat; 200 int max_count = 64; 201 202 while (max_count-- > 0) { 203 ufcon = rd_regl(port, S3C2410_UFCON); 204 ufstat = rd_regl(port, S3C2410_UFSTAT); 205 206 if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0) 207 break; 208 209 uerstat = rd_regl(port, S3C2410_UERSTAT); 210 ch = rd_regb(port, S3C2410_URXH); 211 212 if (port->flags & UPF_CONS_FLOW) { 213 int txe = s3c24xx_serial_txempty_nofifo(port); 214 215 if (rx_enabled(port)) { 216 if (!txe) { 217 rx_enabled(port) = 0; 218 continue; 219 } 220 } else { 221 if (txe) { 222 ufcon |= S3C2410_UFCON_RESETRX; 223 wr_regl(port, S3C2410_UFCON, ufcon); 224 rx_enabled(port) = 1; 225 goto out; 226 } 227 continue; 228 } 229 } 230 231 /* insert the character into the buffer */ 232 233 flag = TTY_NORMAL; 234 port->icount.rx++; 235 236 if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) { 237 dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n", 238 ch, uerstat); 239 240 /* check for break */ 241 if (uerstat & S3C2410_UERSTAT_BREAK) { 242 dbg("break!\n"); 243 port->icount.brk++; 244 if (uart_handle_break(port)) 245 goto ignore_char; 246 } 247 248 if (uerstat & S3C2410_UERSTAT_FRAME) 249 port->icount.frame++; 250 if (uerstat & S3C2410_UERSTAT_OVERRUN) 251 port->icount.overrun++; 252 253 uerstat &= port->read_status_mask; 254 255 if (uerstat & S3C2410_UERSTAT_BREAK) 256 flag = TTY_BREAK; 257 else if (uerstat & S3C2410_UERSTAT_PARITY) 258 flag = TTY_PARITY; 259 else if (uerstat & (S3C2410_UERSTAT_FRAME | 260 S3C2410_UERSTAT_OVERRUN)) 261 flag = TTY_FRAME; 262 } 263 264 if (uart_handle_sysrq_char(port, ch)) 265 goto ignore_char; 266 267 uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, 268 ch, flag); 269 270 ignore_char: 271 continue; 272 } 273 tty_flip_buffer_push(tty); 274 275 out: 276 return IRQ_HANDLED; 277} 278 279static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) 280{ 281 struct s3c24xx_uart_port *ourport = id; 282 struct uart_port *port = &ourport->port; 283 struct circ_buf *xmit = &port->state->xmit; 284 int count = 256; 285 286 if (port->x_char) { 287 wr_regb(port, S3C2410_UTXH, port->x_char); 288 port->icount.tx++; 289 port->x_char = 0; 290 goto out; 291 } 292 293 /* if there isn't anything more to transmit, or the uart is now 294 * stopped, disable the uart and exit 295 */ 296 297 if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { 298 s3c24xx_serial_stop_tx(port); 299 goto out; 300 } 301 302 /* try and drain the buffer... */ 303 304 while (!uart_circ_empty(xmit) && count-- > 0) { 305 if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull) 306 break; 307 308 wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]); 309 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); 310 port->icount.tx++; 311 } 312 313 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 314 uart_write_wakeup(port); 315 316 if (uart_circ_empty(xmit)) 317 s3c24xx_serial_stop_tx(port); 318 319 out: 320 return IRQ_HANDLED; 321} 322 323static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port) 324{ 325 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); 326 unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT); 327 unsigned long ufcon = rd_regl(port, S3C2410_UFCON); 328 329 if (ufcon & S3C2410_UFCON_FIFOMODE) { 330 if ((ufstat & info->tx_fifomask) != 0 || 331 (ufstat & info->tx_fifofull)) 332 return 0; 333 334 return 1; 335 } 336 337 return s3c24xx_serial_txempty_nofifo(port); 338} 339 340/* no modem control lines */ 341static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port) 342{ 343 unsigned int umstat = rd_regb(port, S3C2410_UMSTAT); 344 345 if (umstat & S3C2410_UMSTAT_CTS) 346 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; 347 else 348 return TIOCM_CAR | TIOCM_DSR; 349} 350 351static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) 352{ 353 /* todo - possibly remove AFC and do manual CTS */ 354} 355 356static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state) 357{ 358 unsigned long flags; 359 unsigned int ucon; 360 361 spin_lock_irqsave(&port->lock, flags); 362 363 ucon = rd_regl(port, S3C2410_UCON); 364 365 if (break_state) 366 ucon |= S3C2410_UCON_SBREAK; 367 else 368 ucon &= ~S3C2410_UCON_SBREAK; 369 370 wr_regl(port, S3C2410_UCON, ucon); 371 372 spin_unlock_irqrestore(&port->lock, flags); 373} 374 375static void s3c24xx_serial_shutdown(struct uart_port *port) 376{ 377 struct s3c24xx_uart_port *ourport = to_ourport(port); 378 379 if (ourport->tx_claimed) { 380 free_irq(ourport->tx_irq, ourport); 381 tx_enabled(port) = 0; 382 ourport->tx_claimed = 0; 383 } 384 385 if (ourport->rx_claimed) { 386 free_irq(ourport->rx_irq, ourport); 387 ourport->rx_claimed = 0; 388 rx_enabled(port) = 0; 389 } 390} 391 392 393static int s3c24xx_serial_startup(struct uart_port *port) 394{ 395 struct s3c24xx_uart_port *ourport = to_ourport(port); 396 int ret; 397 398 dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n", 399 port->mapbase, port->membase); 400 401 rx_enabled(port) = 1; 402 403 ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0, 404 s3c24xx_serial_portname(port), ourport); 405 406 if (ret != 0) { 407 printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq); 408 return ret; 409 } 410 411 ourport->rx_claimed = 1; 412 413 dbg("requesting tx irq...\n"); 414 415 tx_enabled(port) = 1; 416 417 ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0, 418 s3c24xx_serial_portname(port), ourport); 419 420 if (ret) { 421 printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq); 422 goto err; 423 } 424 425 ourport->tx_claimed = 1; 426 427 dbg("s3c24xx_serial_startup ok\n"); 428 429 /* the port reset code should have done the correct 430 * register setup for the port controls */ 431 432 return ret; 433 434 err: 435 s3c24xx_serial_shutdown(port); 436 return ret; 437} 438 439/* power power management control */ 440 441static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, 442 unsigned int old) 443{ 444 struct s3c24xx_uart_port *ourport = to_ourport(port); 445 446 ourport->pm_level = level; 447 448 switch (level) { 449 case 3: 450 if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) 451 clk_disable(ourport->baudclk); 452 453 clk_disable(ourport->clk); 454 break; 455 456 case 0: 457 clk_enable(ourport->clk); 458 459 if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) 460 clk_enable(ourport->baudclk); 461 462 break; 463 default: 464 printk(KERN_ERR "s3c24xx_serial: unknown pm %d\n", level); 465 } 466} 467 468/* baud rate calculation 469 * 470 * The UARTs on the S3C2410/S3C2440 can take their clocks from a number 471 * of different sources, including the peripheral clock ("pclk") and an 472 * external clock ("uclk"). The S3C2440 also adds the core clock ("fclk") 473 * with a programmable extra divisor. 474 * 475 * The following code goes through the clock sources, and calculates the 476 * baud clocks (and the resultant actual baud rates) and then tries to 477 * pick the closest one and select that. 478 * 479*/ 480 481 482#define MAX_CLKS (8) 483 484static struct s3c24xx_uart_clksrc tmp_clksrc = { 485 .name = "pclk", 486 .min_baud = 0, 487 .max_baud = 0, 488 .divisor = 1, 489}; 490 491static inline int 492s3c24xx_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c) 493{ 494 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); 495 496 return (info->get_clksrc)(port, c); 497} 498 499static inline int 500s3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c) 501{ 502 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); 503 504 return (info->set_clksrc)(port, c); 505} 506 507struct baud_calc { 508 struct s3c24xx_uart_clksrc *clksrc; 509 unsigned int calc; 510 unsigned int divslot; 511 unsigned int quot; 512 struct clk *src; 513}; 514 515static int s3c24xx_serial_calcbaud(struct baud_calc *calc, 516 struct uart_port *port, 517 struct s3c24xx_uart_clksrc *clksrc, 518 unsigned int baud) 519{ 520 struct s3c24xx_uart_port *ourport = to_ourport(port); 521 unsigned long rate; 522 523 calc->src = clk_get(port->dev, clksrc->name); 524 if (calc->src == NULL || IS_ERR(calc->src)) 525 return 0; 526 527 rate = clk_get_rate(calc->src); 528 rate /= clksrc->divisor; 529 530 calc->clksrc = clksrc; 531 532 if (ourport->info->has_divslot) { 533 unsigned long div = rate / baud; 534 535 /* The UDIVSLOT register on the newer UARTs allows us to 536 * get a divisor adjustment of 1/16th on the baud clock. 537 * 538 * We don't keep the UDIVSLOT value (the 16ths we calculated 539 * by not multiplying the baud by 16) as it is easy enough 540 * to recalculate. 541 */ 542 543 calc->quot = div / 16; 544 calc->calc = rate / div; 545 } else { 546 calc->quot = (rate + (8 * baud)) / (16 * baud); 547 calc->calc = (rate / (calc->quot * 16)); 548 } 549 550 calc->quot--; 551 return 1; 552} 553 554static unsigned int s3c24xx_serial_getclk(struct uart_port *port, 555 struct s3c24xx_uart_clksrc **clksrc, 556 struct clk **clk, 557 unsigned int baud) 558{ 559 struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); 560 struct s3c24xx_uart_clksrc *clkp; 561 struct baud_calc res[MAX_CLKS]; 562 struct baud_calc *resptr, *best, *sptr; 563 int i; 564 565 clkp = cfg->clocks; 566 best = NULL; 567 568 if (cfg->clocks_size < 2) { 569 if (cfg->clocks_size == 0) 570 clkp = &tmp_clksrc; 571 572 /* check to see if we're sourcing fclk, and if so we're 573 * going to have to update the clock source 574 */ 575 576 if (strcmp(clkp->name, "fclk") == 0) { 577 struct s3c24xx_uart_clksrc src; 578 579 s3c24xx_serial_getsource(port, &src); 580 581 /* check that the port already using fclk, and if 582 * not, then re-select fclk 583 */ 584 585 if (strcmp(src.name, clkp->name) == 0) { 586 s3c24xx_serial_setsource(port, clkp); 587 s3c24xx_serial_getsource(port, &src); 588 } 589 590 clkp->divisor = src.divisor; 591 } 592 593 s3c24xx_serial_calcbaud(res, port, clkp, baud); 594 best = res; 595 resptr = best + 1; 596 } else { 597 resptr = res; 598 599 for (i = 0; i < cfg->clocks_size; i++, clkp++) { 600 if (s3c24xx_serial_calcbaud(resptr, port, clkp, baud)) 601 resptr++; 602 } 603 } 604 605 /* ok, we now need to select the best clock we found */ 606 607 if (!best) { 608 unsigned int deviation = (1<<30)|((1<<30)-1); 609 int calc_deviation; 610 611 for (sptr = res; sptr < resptr; sptr++) { 612 calc_deviation = baud - sptr->calc; 613 if (calc_deviation < 0) 614 calc_deviation = -calc_deviation; 615 616 if (calc_deviation < deviation) { 617 best = sptr; 618 deviation = calc_deviation; 619 } 620 } 621 } 622 623 /* store results to pass back */ 624 625 *clksrc = best->clksrc; 626 *clk = best->src; 627 628 return best->quot; 629} 630 631/* udivslot_table[] 632 * 633 * This table takes the fractional value of the baud divisor and gives 634 * the recommended setting for the UDIVSLOT register. 635 */ 636static u16 udivslot_table[16] = { 637 [0] = 0x0000, 638 [1] = 0x0080, 639 [2] = 0x0808, 640 [3] = 0x0888, 641 [4] = 0x2222, 642 [5] = 0x4924, 643 [6] = 0x4A52, 644 [7] = 0x54AA, 645 [8] = 0x5555, 646 [9] = 0xD555, 647 [10] = 0xD5D5, 648 [11] = 0xDDD5, 649 [12] = 0xDDDD, 650 [13] = 0xDFDD, 651 [14] = 0xDFDF, 652 [15] = 0xFFDF, 653}; 654 655static void s3c24xx_serial_set_termios(struct uart_port *port, 656 struct ktermios *termios, 657 struct ktermios *old) 658{ 659 struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); 660 struct s3c24xx_uart_port *ourport = to_ourport(port); 661 struct s3c24xx_uart_clksrc *clksrc = NULL; 662 struct clk *clk = NULL; 663 unsigned long flags; 664 unsigned int baud, quot; 665 unsigned int ulcon; 666 unsigned int umcon; 667 unsigned int udivslot = 0; 668 669 /* 670 * We don't support modem control lines. 671 */ 672 termios->c_cflag &= ~(HUPCL | CMSPAR); 673 termios->c_cflag |= CLOCAL; 674 675 /* 676 * Ask the core to calculate the divisor for us. 677 */ 678 679 baud = uart_get_baud_rate(port, termios, old, 0, 115200*8); 680 681 if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) 682 quot = port->custom_divisor; 683 else 684 quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud); 685 686 /* check to see if we need to change clock source */ 687 688 if (ourport->clksrc != clksrc || ourport->baudclk != clk) { 689 dbg("selecting clock %p\n", clk); 690 s3c24xx_serial_setsource(port, clksrc); 691 692 if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) { 693 clk_disable(ourport->baudclk); 694 ourport->baudclk = NULL; 695 } 696 697 clk_enable(clk); 698 699 ourport->clksrc = clksrc; 700 ourport->baudclk = clk; 701 ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; 702 } 703 704 if (ourport->info->has_divslot) { 705 unsigned int div = ourport->baudclk_rate / baud; 706 707 if (cfg->has_fracval) { 708 udivslot = (div & 15); 709 dbg("fracval = %04x\n", udivslot); 710 } else { 711 udivslot = udivslot_table[div & 15]; 712 dbg("udivslot = %04x (div %d)\n", udivslot, div & 15); 713 } 714 } 715 716 switch (termios->c_cflag & CSIZE) { 717 case CS5: 718 dbg("config: 5bits/char\n"); 719 ulcon = S3C2410_LCON_CS5; 720 break; 721 case CS6: 722 dbg("config: 6bits/char\n"); 723 ulcon = S3C2410_LCON_CS6; 724 break; 725 case CS7: 726 dbg("config: 7bits/char\n"); 727 ulcon = S3C2410_LCON_CS7; 728 break; 729 case CS8: 730 default: 731 dbg("config: 8bits/char\n"); 732 ulcon = S3C2410_LCON_CS8; 733 break; 734 } 735 736 /* preserve original lcon IR settings */ 737 ulcon |= (cfg->ulcon & S3C2410_LCON_IRM); 738 739 if (termios->c_cflag & CSTOPB) 740 ulcon |= S3C2410_LCON_STOPB; 741 742 umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0; 743 744 if (termios->c_cflag & PARENB) { 745 if (termios->c_cflag & PARODD) 746 ulcon |= S3C2410_LCON_PODD; 747 else 748 ulcon |= S3C2410_LCON_PEVEN; 749 } else { 750 ulcon |= S3C2410_LCON_PNONE; 751 } 752 753 spin_lock_irqsave(&port->lock, flags); 754 755 dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n", 756 ulcon, quot, udivslot); 757 758 wr_regl(port, S3C2410_ULCON, ulcon); 759 wr_regl(port, S3C2410_UBRDIV, quot); 760 wr_regl(port, S3C2410_UMCON, umcon); 761 762 if (ourport->info->has_divslot) 763 wr_regl(port, S3C2443_DIVSLOT, udivslot); 764 765 dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n", 766 rd_regl(port, S3C2410_ULCON), 767 rd_regl(port, S3C2410_UCON), 768 rd_regl(port, S3C2410_UFCON)); 769 770 /* 771 * Update the per-port timeout. 772 */ 773 uart_update_timeout(port, termios->c_cflag, baud); 774 775 /* 776 * Which character status flags are we interested in? 777 */ 778 port->read_status_mask = S3C2410_UERSTAT_OVERRUN; 779 if (termios->c_iflag & INPCK) 780 port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY; 781 782 /* 783 * Which character status flags should we ignore? 784 */ 785 port->ignore_status_mask = 0; 786 if (termios->c_iflag & IGNPAR) 787 port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN; 788 if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR) 789 port->ignore_status_mask |= S3C2410_UERSTAT_FRAME; 790 791 /* 792 * Ignore all characters if CREAD is not set. 793 */ 794 if ((termios->c_cflag & CREAD) == 0) 795 port->ignore_status_mask |= RXSTAT_DUMMY_READ; 796 797 spin_unlock_irqrestore(&port->lock, flags); 798} 799 800static const char *s3c24xx_serial_type(struct uart_port *port) 801{ 802 switch (port->type) { 803 case PORT_S3C2410: 804 return "S3C2410"; 805 case PORT_S3C2440: 806 return "S3C2440"; 807 case PORT_S3C2412: 808 return "S3C2412"; 809 case PORT_S3C6400: 810 return "S3C6400/10"; 811 default: 812 return NULL; 813 } 814} 815 816#define MAP_SIZE (0x100) 817 818static void s3c24xx_serial_release_port(struct uart_port *port) 819{ 820 release_mem_region(port->mapbase, MAP_SIZE); 821} 822 823static int s3c24xx_serial_request_port(struct uart_port *port) 824{ 825 const char *name = s3c24xx_serial_portname(port); 826 return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY; 827} 828 829static void s3c24xx_serial_config_port(struct uart_port *port, int flags) 830{ 831 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); 832 833 if (flags & UART_CONFIG_TYPE && 834 s3c24xx_serial_request_port(port) == 0) 835 port->type = info->type; 836} 837 838/* 839 * verify the new serial_struct (for TIOCSSERIAL). 840 */ 841static int 842s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser) 843{ 844 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); 845 846 if (ser->type != PORT_UNKNOWN && ser->type != info->type) 847 return -EINVAL; 848 849 return 0; 850} 851 852 853#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE 854 855static struct console s3c24xx_serial_console; 856 857#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console 858#else 859#define S3C24XX_SERIAL_CONSOLE NULL 860#endif 861 862static struct uart_ops s3c24xx_serial_ops = { 863 .pm = s3c24xx_serial_pm, 864 .tx_empty = s3c24xx_serial_tx_empty, 865 .get_mctrl = s3c24xx_serial_get_mctrl, 866 .set_mctrl = s3c24xx_serial_set_mctrl, 867 .stop_tx = s3c24xx_serial_stop_tx, 868 .start_tx = s3c24xx_serial_start_tx, 869 .stop_rx = s3c24xx_serial_stop_rx, 870 .enable_ms = s3c24xx_serial_enable_ms, 871 .break_ctl = s3c24xx_serial_break_ctl, 872 .startup = s3c24xx_serial_startup, 873 .shutdown = s3c24xx_serial_shutdown, 874 .set_termios = s3c24xx_serial_set_termios, 875 .type = s3c24xx_serial_type, 876 .release_port = s3c24xx_serial_release_port, 877 .request_port = s3c24xx_serial_request_port, 878 .config_port = s3c24xx_serial_config_port, 879 .verify_port = s3c24xx_serial_verify_port, 880}; 881 882 883static struct uart_driver s3c24xx_uart_drv = { 884 .owner = THIS_MODULE, 885 .driver_name = "s3c2410_serial", 886 .nr = CONFIG_SERIAL_SAMSUNG_UARTS, 887 .cons = S3C24XX_SERIAL_CONSOLE, 888 .dev_name = S3C24XX_SERIAL_NAME, 889 .major = S3C24XX_SERIAL_MAJOR, 890 .minor = S3C24XX_SERIAL_MINOR, 891}; 892 893static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = { 894 [0] = { 895 .port = { 896 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock), 897 .iotype = UPIO_MEM, 898 .irq = IRQ_S3CUART_RX0, 899 .uartclk = 0, 900 .fifosize = 16, 901 .ops = &s3c24xx_serial_ops, 902 .flags = UPF_BOOT_AUTOCONF, 903 .line = 0, 904 } 905 }, 906 [1] = { 907 .port = { 908 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock), 909 .iotype = UPIO_MEM, 910 .irq = IRQ_S3CUART_RX1, 911 .uartclk = 0, 912 .fifosize = 16, 913 .ops = &s3c24xx_serial_ops, 914 .flags = UPF_BOOT_AUTOCONF, 915 .line = 1, 916 } 917 }, 918#if CONFIG_SERIAL_SAMSUNG_UARTS > 2 919 920 [2] = { 921 .port = { 922 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock), 923 .iotype = UPIO_MEM, 924 .irq = IRQ_S3CUART_RX2, 925 .uartclk = 0, 926 .fifosize = 16, 927 .ops = &s3c24xx_serial_ops, 928 .flags = UPF_BOOT_AUTOCONF, 929 .line = 2, 930 } 931 }, 932#endif 933#if CONFIG_SERIAL_SAMSUNG_UARTS > 3 934 [3] = { 935 .port = { 936 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock), 937 .iotype = UPIO_MEM, 938 .irq = IRQ_S3CUART_RX3, 939 .uartclk = 0, 940 .fifosize = 16, 941 .ops = &s3c24xx_serial_ops, 942 .flags = UPF_BOOT_AUTOCONF, 943 .line = 3, 944 } 945 } 946#endif 947}; 948 949/* s3c24xx_serial_resetport 950 * 951 * wrapper to call the specific reset for this port (reset the fifos 952 * and the settings) 953*/ 954 955static inline int s3c24xx_serial_resetport(struct uart_port *port, 956 struct s3c2410_uartcfg *cfg) 957{ 958 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); 959 960 return (info->reset_port)(port, cfg); 961} 962 963 964#ifdef CONFIG_CPU_FREQ 965 966static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb, 967 unsigned long val, void *data) 968{ 969 struct s3c24xx_uart_port *port; 970 struct uart_port *uport; 971 972 port = container_of(nb, struct s3c24xx_uart_port, freq_transition); 973 uport = &port->port; 974 975 /* check to see if port is enabled */ 976 977 if (port->pm_level != 0) 978 return 0; 979 980 /* try and work out if the baudrate is changing, we can detect 981 * a change in rate, but we do not have support for detecting 982 * a disturbance in the clock-rate over the change. 983 */ 984 985 if (IS_ERR(port->clk)) 986 goto exit; 987 988 if (port->baudclk_rate == clk_get_rate(port->clk)) 989 goto exit; 990 991 if (val == CPUFREQ_PRECHANGE) { 992 /* we should really shut the port down whilst the 993 * frequency change is in progress. */ 994 995 } else if (val == CPUFREQ_POSTCHANGE) { 996 struct ktermios *termios; 997 struct tty_struct *tty; 998 999 if (uport->state == NULL) 1000 goto exit; 1001 1002 tty = uport->state->port.tty; 1003 1004 if (tty == NULL) 1005 goto exit; 1006 1007 termios = tty->termios; 1008 1009 if (termios == NULL) { 1010 printk(KERN_WARNING "%s: no termios?\n", __func__); 1011 goto exit; 1012 } 1013 1014 s3c24xx_serial_set_termios(uport, termios, NULL); 1015 } 1016 1017 exit: 1018 return 0; 1019} 1020 1021static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) 1022{ 1023 port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition; 1024 1025 return cpufreq_register_notifier(&port->freq_transition, 1026 CPUFREQ_TRANSITION_NOTIFIER); 1027} 1028 1029static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) 1030{ 1031 cpufreq_unregister_notifier(&port->freq_transition, 1032 CPUFREQ_TRANSITION_NOTIFIER); 1033} 1034 1035#else 1036static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) 1037{ 1038 return 0; 1039} 1040 1041static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) 1042{ 1043} 1044#endif 1045 1046/* s3c24xx_serial_init_port 1047 * 1048 * initialise a single serial port from the platform device given 1049 */ 1050 1051static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, 1052 struct s3c24xx_uart_info *info, 1053 struct platform_device *platdev) 1054{ 1055 struct uart_port *port = &ourport->port; 1056 struct s3c2410_uartcfg *cfg; 1057 struct resource *res; 1058 int ret; 1059 1060 dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev); 1061 1062 if (platdev == NULL) 1063 return -ENODEV; 1064 1065 cfg = s3c24xx_dev_to_cfg(&platdev->dev); 1066 1067 if (port->mapbase != 0) 1068 return 0; 1069 1070 if (cfg->hwport > CONFIG_SERIAL_SAMSUNG_UARTS) { 1071 printk(KERN_ERR "%s: port %d bigger than %d\n", __func__, 1072 cfg->hwport, CONFIG_SERIAL_SAMSUNG_UARTS); 1073 return -ERANGE; 1074 } 1075 1076 /* setup info for port */ 1077 port->dev = &platdev->dev; 1078 ourport->info = info; 1079 1080 /* copy the info in from provided structure */ 1081 ourport->port.fifosize = info->fifosize; 1082 1083 dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport); 1084 1085 port->uartclk = 1; 1086 1087 if (cfg->uart_flags & UPF_CONS_FLOW) { 1088 dbg("s3c24xx_serial_init_port: enabling flow control\n"); 1089 port->flags |= UPF_CONS_FLOW; 1090 } 1091 1092 /* sort our the physical and virtual addresses for each UART */ 1093 1094 res = platform_get_resource(platdev, IORESOURCE_MEM, 0); 1095 if (res == NULL) { 1096 printk(KERN_ERR "failed to find memory resource for uart\n"); 1097 return -EINVAL; 1098 } 1099 1100 dbg("resource %p (%lx..%lx)\n", res, res->start, res->end); 1101 1102 port->mapbase = res->start; 1103 port->membase = S3C_VA_UART + (res->start & 0xfffff); 1104 ret = platform_get_irq(platdev, 0); 1105 if (ret < 0) 1106 port->irq = 0; 1107 else { 1108 port->irq = ret; 1109 ourport->rx_irq = ret; 1110 ourport->tx_irq = ret + 1; 1111 } 1112 1113 ret = platform_get_irq(platdev, 1); 1114 if (ret > 0) 1115 ourport->tx_irq = ret; 1116 1117 ourport->clk = clk_get(&platdev->dev, "uart"); 1118 1119 dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n", 1120 port->mapbase, port->membase, port->irq, 1121 ourport->rx_irq, ourport->tx_irq, port->uartclk); 1122 1123 /* reset the fifos (and setup the uart) */ 1124 s3c24xx_serial_resetport(port, cfg); 1125 return 0; 1126} 1127 1128static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, 1129 struct device_attribute *attr, 1130 char *buf) 1131{ 1132 struct uart_port *port = s3c24xx_dev_to_port(dev); 1133 struct s3c24xx_uart_port *ourport = to_ourport(port); 1134 1135 return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->clksrc->name); 1136} 1137 1138static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL); 1139 1140/* Device driver serial port probe */ 1141 1142static int probe_index; 1143 1144int s3c24xx_serial_probe(struct platform_device *dev, 1145 struct s3c24xx_uart_info *info) 1146{ 1147 struct s3c24xx_uart_port *ourport; 1148 int ret; 1149 1150 dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index); 1151 1152 ourport = &s3c24xx_serial_ports[probe_index]; 1153 probe_index++; 1154 1155 dbg("%s: initialising port %p...\n", __func__, ourport); 1156 1157 ret = s3c24xx_serial_init_port(ourport, info, dev); 1158 if (ret < 0) 1159 goto probe_err; 1160 1161 dbg("%s: adding port\n", __func__); 1162 uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); 1163 platform_set_drvdata(dev, &ourport->port); 1164 1165 ret = device_create_file(&dev->dev, &dev_attr_clock_source); 1166 if (ret < 0) 1167 printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__); 1168 1169 ret = s3c24xx_serial_cpufreq_register(ourport); 1170 if (ret < 0) 1171 dev_err(&dev->dev, "failed to add cpufreq notifier\n"); 1172 1173 return 0; 1174 1175 probe_err: 1176 return ret; 1177} 1178 1179EXPORT_SYMBOL_GPL(s3c24xx_serial_probe); 1180 1181int __devexit s3c24xx_serial_remove(struct platform_device *dev) 1182{ 1183 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); 1184 1185 if (port) { 1186 s3c24xx_serial_cpufreq_deregister(to_ourport(port)); 1187 device_remove_file(&dev->dev, &dev_attr_clock_source); 1188 uart_remove_one_port(&s3c24xx_uart_drv, port); 1189 } 1190 1191 return 0; 1192} 1193 1194EXPORT_SYMBOL_GPL(s3c24xx_serial_remove); 1195 1196/* UART power management code */ 1197#ifdef CONFIG_PM_SLEEP 1198static int s3c24xx_serial_suspend(struct device *dev) 1199{ 1200 struct uart_port *port = s3c24xx_dev_to_port(dev); 1201 1202 if (port) 1203 uart_suspend_port(&s3c24xx_uart_drv, port); 1204 1205 return 0; 1206} 1207 1208static int s3c24xx_serial_resume(struct device *dev) 1209{ 1210 struct uart_port *port = s3c24xx_dev_to_port(dev); 1211 struct s3c24xx_uart_port *ourport = to_ourport(port); 1212 1213 if (port) { 1214 clk_enable(ourport->clk); 1215 s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port)); 1216 clk_disable(ourport->clk); 1217 1218 uart_resume_port(&s3c24xx_uart_drv, port); 1219 } 1220 1221 return 0; 1222} 1223 1224static const struct dev_pm_ops s3c24xx_serial_pm_ops = { 1225 .suspend = s3c24xx_serial_suspend, 1226 .resume = s3c24xx_serial_resume, 1227}; 1228#define SERIAL_SAMSUNG_PM_OPS (&s3c24xx_serial_pm_ops) 1229 1230#else /* !CONFIG_PM_SLEEP */ 1231 1232#define SERIAL_SAMSUNG_PM_OPS NULL 1233#endif /* CONFIG_PM_SLEEP */ 1234 1235int s3c24xx_serial_init(struct platform_driver *drv, 1236 struct s3c24xx_uart_info *info) 1237{ 1238 dbg("s3c24xx_serial_init(%p,%p)\n", drv, info); 1239 1240 drv->driver.pm = SERIAL_SAMSUNG_PM_OPS; 1241 1242 return platform_driver_register(drv); 1243} 1244 1245EXPORT_SYMBOL_GPL(s3c24xx_serial_init); 1246 1247/* module initialisation code */ 1248 1249static int __init s3c24xx_serial_modinit(void) 1250{ 1251 int ret; 1252 1253 ret = uart_register_driver(&s3c24xx_uart_drv); 1254 if (ret < 0) { 1255 printk(KERN_ERR "failed to register UART driver\n"); 1256 return -1; 1257 } 1258 1259 return 0; 1260} 1261 1262static void __exit s3c24xx_serial_modexit(void) 1263{ 1264 uart_unregister_driver(&s3c24xx_uart_drv); 1265} 1266 1267module_init(s3c24xx_serial_modinit); 1268module_exit(s3c24xx_serial_modexit); 1269 1270/* Console code */ 1271 1272#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE 1273 1274static struct uart_port *cons_uart; 1275 1276static int 1277s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon) 1278{ 1279 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); 1280 unsigned long ufstat, utrstat; 1281 1282 if (ufcon & S3C2410_UFCON_FIFOMODE) { 1283 /* fifo mode - check amount of data in fifo registers... */ 1284 1285 ufstat = rd_regl(port, S3C2410_UFSTAT); 1286 return (ufstat & info->tx_fifofull) ? 0 : 1; 1287 } 1288 1289 /* in non-fifo mode, we go and use the tx buffer empty */ 1290 1291 utrstat = rd_regl(port, S3C2410_UTRSTAT); 1292 return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0; 1293} 1294 1295static void 1296s3c24xx_serial_console_putchar(struct uart_port *port, int ch) 1297{ 1298 unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); 1299 while (!s3c24xx_serial_console_txrdy(port, ufcon)) 1300 barrier(); 1301 wr_regb(cons_uart, S3C2410_UTXH, ch); 1302} 1303 1304static void 1305s3c24xx_serial_console_write(struct console *co, const char *s, 1306 unsigned int count) 1307{ 1308 uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar); 1309} 1310 1311static void __init 1312s3c24xx_serial_get_options(struct uart_port *port, int *baud, 1313 int *parity, int *bits) 1314{ 1315 struct s3c24xx_uart_clksrc clksrc; 1316 struct clk *clk; 1317 unsigned int ulcon; 1318 unsigned int ucon; 1319 unsigned int ubrdiv; 1320 unsigned long rate; 1321 1322 ulcon = rd_regl(port, S3C2410_ULCON); 1323 ucon = rd_regl(port, S3C2410_UCON); 1324 ubrdiv = rd_regl(port, S3C2410_UBRDIV); 1325 1326 dbg("s3c24xx_serial_get_options: port=%p\n" 1327 "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n", 1328 port, ulcon, ucon, ubrdiv); 1329 1330 if ((ucon & 0xf) != 0) { 1331 /* consider the serial port configured if the tx/rx mode set */ 1332 1333 switch (ulcon & S3C2410_LCON_CSMASK) { 1334 case S3C2410_LCON_CS5: 1335 *bits = 5; 1336 break; 1337 case S3C2410_LCON_CS6: 1338 *bits = 6; 1339 break; 1340 case S3C2410_LCON_CS7: 1341 *bits = 7; 1342 break; 1343 default: 1344 case S3C2410_LCON_CS8: 1345 *bits = 8; 1346 break; 1347 } 1348 1349 switch (ulcon & S3C2410_LCON_PMASK) { 1350 case S3C2410_LCON_PEVEN: 1351 *parity = 'e'; 1352 break; 1353 1354 case S3C2410_LCON_PODD: 1355 *parity = 'o'; 1356 break; 1357 1358 case S3C2410_LCON_PNONE: 1359 default: 1360 *parity = 'n'; 1361 } 1362 1363 /* now calculate the baud rate */ 1364 1365 s3c24xx_serial_getsource(port, &clksrc); 1366 1367 clk = clk_get(port->dev, clksrc.name); 1368 if (!IS_ERR(clk) && clk != NULL) 1369 rate = clk_get_rate(clk) / clksrc.divisor; 1370 else 1371 rate = 1; 1372 1373 1374 *baud = rate / (16 * (ubrdiv + 1)); 1375 dbg("calculated baud %d\n", *baud); 1376 } 1377 1378} 1379 1380/* s3c24xx_serial_init_ports 1381 * 1382 * initialise the serial ports from the machine provided initialisation 1383 * data. 1384*/ 1385 1386static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info **info) 1387{ 1388 struct s3c24xx_uart_port *ptr = s3c24xx_serial_ports; 1389 struct platform_device **platdev_ptr; 1390 int i; 1391 1392 dbg("s3c24xx_serial_init_ports: initialising ports...\n"); 1393 1394 platdev_ptr = s3c24xx_uart_devs; 1395 1396 for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) { 1397 s3c24xx_serial_init_port(ptr, info[i], *platdev_ptr); 1398 } 1399 1400 return 0; 1401} 1402 1403static int __init 1404s3c24xx_serial_console_setup(struct console *co, char *options) 1405{ 1406 struct uart_port *port; 1407 int baud = 9600; 1408 int bits = 8; 1409 int parity = 'n'; 1410 int flow = 'n'; 1411 1412 dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n", 1413 co, co->index, options); 1414 1415 /* is this a valid port */ 1416 1417 if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS) 1418 co->index = 0; 1419 1420 port = &s3c24xx_serial_ports[co->index].port; 1421 1422 /* is the port configured? */ 1423 1424 if (port->mapbase == 0x0) 1425 return -ENODEV; 1426 1427 cons_uart = port; 1428 1429 dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index); 1430 1431 /* 1432 * Check whether an invalid uart number has been specified, and 1433 * if so, search for the first available port that does have 1434 * console support. 1435 */ 1436 if (options) 1437 uart_parse_options(options, &baud, &parity, &bits, &flow); 1438 else 1439 s3c24xx_serial_get_options(port, &baud, &parity, &bits); 1440 1441 dbg("s3c24xx_serial_console_setup: baud %d\n", baud); 1442 1443 return uart_set_options(port, co, baud, parity, bits, flow); 1444} 1445 1446/* s3c24xx_serial_initconsole 1447 * 1448 * initialise the console from one of the uart drivers 1449*/ 1450 1451static struct console s3c24xx_serial_console = { 1452 .name = S3C24XX_SERIAL_NAME, 1453 .device = uart_console_device, 1454 .flags = CON_PRINTBUFFER, 1455 .index = -1, 1456 .write = s3c24xx_serial_console_write, 1457 .setup = s3c24xx_serial_console_setup, 1458 .data = &s3c24xx_uart_drv, 1459}; 1460 1461int s3c24xx_serial_initconsole(struct platform_driver *drv, 1462 struct s3c24xx_uart_info **info) 1463 1464{ 1465 struct platform_device *dev = s3c24xx_uart_devs[0]; 1466 1467 dbg("s3c24xx_serial_initconsole\n"); 1468 1469 /* select driver based on the cpu */ 1470 1471 if (dev == NULL) { 1472 printk(KERN_ERR "s3c24xx: no devices for console init\n"); 1473 return 0; 1474 } 1475 1476 if (strcmp(dev->name, drv->driver.name) != 0) 1477 return 0; 1478 1479 s3c24xx_serial_console.data = &s3c24xx_uart_drv; 1480 s3c24xx_serial_init_ports(info); 1481 1482 register_console(&s3c24xx_serial_console); 1483 return 0; 1484} 1485 1486#endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */ 1487 1488MODULE_DESCRIPTION("Samsung SoC Serial port driver"); 1489MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 1490MODULE_LICENSE("GPL v2");