1/* 2 * linux/drivers/char/clps711x.c 3 * 4 * Driver for CLPS711x serial ports 5 * 6 * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. 7 * 8 * Copyright 1999 ARM Limited 9 * Copyright (C) 2000 Deep Blue Solutions Ltd. 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2 of the License, or 14 * (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, write to the Free Software 23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 24 * 25 * $Id: clps711x.c,v 1.1.1.1 2007/08/03 18:53:00 Exp $ 26 * 27 */ 28 29#if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) 30#define SUPPORT_SYSRQ 31#endif 32 33#include <linux/module.h> 34#include <linux/ioport.h> 35#include <linux/init.h> 36#include <linux/console.h> 37#include <linux/sysrq.h> 38#include <linux/spinlock.h> 39#include <linux/device.h> 40#include <linux/tty.h> 41#include <linux/tty_flip.h> 42#include <linux/serial_core.h> 43#include <linux/serial.h> 44 45#include <asm/hardware.h> 46#include <asm/io.h> 47#include <asm/irq.h> 48#include <asm/hardware/clps7111.h> 49 50#define UART_NR 2 51 52#define SERIAL_CLPS711X_MAJOR 204 53#define SERIAL_CLPS711X_MINOR 40 54#define SERIAL_CLPS711X_NR UART_NR 55 56/* 57 * We use the relevant SYSCON register as a base address for these ports. 58 */ 59#define UBRLCR(port) ((port)->iobase + UBRLCR1 - SYSCON1) 60#define UARTDR(port) ((port)->iobase + UARTDR1 - SYSCON1) 61#define SYSFLG(port) ((port)->iobase + SYSFLG1 - SYSCON1) 62#define SYSCON(port) ((port)->iobase + SYSCON1 - SYSCON1) 63 64#define TX_IRQ(port) ((port)->irq) 65#define RX_IRQ(port) ((port)->irq + 1) 66 67#define UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR) 68 69#define tx_enabled(port) ((port)->unused[0]) 70 71static void clps711xuart_stop_tx(struct uart_port *port) 72{ 73 if (tx_enabled(port)) { 74 disable_irq(TX_IRQ(port)); 75 tx_enabled(port) = 0; 76 } 77} 78 79static void clps711xuart_start_tx(struct uart_port *port) 80{ 81 if (!tx_enabled(port)) { 82 enable_irq(TX_IRQ(port)); 83 tx_enabled(port) = 1; 84 } 85} 86 87static void clps711xuart_stop_rx(struct uart_port *port) 88{ 89 disable_irq(RX_IRQ(port)); 90} 91 92static void clps711xuart_enable_ms(struct uart_port *port) 93{ 94} 95 96static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id) 97{ 98 struct uart_port *port = dev_id; 99 struct tty_struct *tty = port->info->tty; 100 unsigned int status, ch, flg; 101 102 status = clps_readl(SYSFLG(port)); 103 while (!(status & SYSFLG_URXFE)) { 104 ch = clps_readl(UARTDR(port)); 105 106 port->icount.rx++; 107 108 flg = TTY_NORMAL; 109 110 /* 111 * Note that the error handling code is 112 * out of the main execution path 113 */ 114 if (unlikely(ch & UART_ANY_ERR)) { 115 if (ch & UARTDR_PARERR) 116 port->icount.parity++; 117 else if (ch & UARTDR_FRMERR) 118 port->icount.frame++; 119 if (ch & UARTDR_OVERR) 120 port->icount.overrun++; 121 122 ch &= port->read_status_mask; 123 124 if (ch & UARTDR_PARERR) 125 flg = TTY_PARITY; 126 else if (ch & UARTDR_FRMERR) 127 flg = TTY_FRAME; 128 129#ifdef SUPPORT_SYSRQ 130 port->sysrq = 0; 131#endif 132 } 133 134 if (uart_handle_sysrq_char(port, ch)) 135 goto ignore_char; 136 137 /* 138 * CHECK: does overrun affect the current character? 139 * ASSUMPTION: it does not. 140 */ 141 uart_insert_char(port, ch, UARTDR_OVERR, ch, flg); 142 143 ignore_char: 144 status = clps_readl(SYSFLG(port)); 145 } 146 tty_flip_buffer_push(tty); 147 return IRQ_HANDLED; 148} 149 150static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) 151{ 152 struct uart_port *port = dev_id; 153 struct circ_buf *xmit = &port->info->xmit; 154 int count; 155 156 if (port->x_char) { 157 clps_writel(port->x_char, UARTDR(port)); 158 port->icount.tx++; 159 port->x_char = 0; 160 return IRQ_HANDLED; 161 } 162 if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { 163 clps711xuart_stop_tx(port); 164 return IRQ_HANDLED; 165 } 166 167 count = port->fifosize >> 1; 168 do { 169 clps_writel(xmit->buf[xmit->tail], UARTDR(port)); 170 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); 171 port->icount.tx++; 172 if (uart_circ_empty(xmit)) 173 break; 174 } while (--count > 0); 175 176 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 177 uart_write_wakeup(port); 178 179 if (uart_circ_empty(xmit)) 180 clps711xuart_stop_tx(port); 181 182 return IRQ_HANDLED; 183} 184 185static unsigned int clps711xuart_tx_empty(struct uart_port *port) 186{ 187 unsigned int status = clps_readl(SYSFLG(port)); 188 return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT; 189} 190 191static unsigned int clps711xuart_get_mctrl(struct uart_port *port) 192{ 193 unsigned int port_addr; 194 unsigned int result = 0; 195 unsigned int status; 196 197 port_addr = SYSFLG(port); 198 if (port_addr == SYSFLG1) { 199 status = clps_readl(SYSFLG1); 200 if (status & SYSFLG1_DCD) 201 result |= TIOCM_CAR; 202 if (status & SYSFLG1_DSR) 203 result |= TIOCM_DSR; 204 if (status & SYSFLG1_CTS) 205 result |= TIOCM_CTS; 206 } 207 208 return result; 209} 210 211static void 212clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl) 213{ 214} 215 216static void clps711xuart_break_ctl(struct uart_port *port, int break_state) 217{ 218 unsigned long flags; 219 unsigned int ubrlcr; 220 221 spin_lock_irqsave(&port->lock, flags); 222 ubrlcr = clps_readl(UBRLCR(port)); 223 if (break_state == -1) 224 ubrlcr |= UBRLCR_BREAK; 225 else 226 ubrlcr &= ~UBRLCR_BREAK; 227 clps_writel(ubrlcr, UBRLCR(port)); 228 spin_unlock_irqrestore(&port->lock, flags); 229} 230 231static int clps711xuart_startup(struct uart_port *port) 232{ 233 unsigned int syscon; 234 int retval; 235 236 tx_enabled(port) = 1; 237 238 /* 239 * Allocate the IRQs 240 */ 241 retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0, 242 "clps711xuart_tx", port); 243 if (retval) 244 return retval; 245 246 retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0, 247 "clps711xuart_rx", port); 248 if (retval) { 249 free_irq(TX_IRQ(port), port); 250 return retval; 251 } 252 253 /* 254 * enable the port 255 */ 256 syscon = clps_readl(SYSCON(port)); 257 syscon |= SYSCON_UARTEN; 258 clps_writel(syscon, SYSCON(port)); 259 260 return 0; 261} 262 263static void clps711xuart_shutdown(struct uart_port *port) 264{ 265 unsigned int ubrlcr, syscon; 266 267 /* 268 * Free the interrupt 269 */ 270 free_irq(TX_IRQ(port), port); /* TX interrupt */ 271 free_irq(RX_IRQ(port), port); /* RX interrupt */ 272 273 /* 274 * disable the port 275 */ 276 syscon = clps_readl(SYSCON(port)); 277 syscon &= ~SYSCON_UARTEN; 278 clps_writel(syscon, SYSCON(port)); 279 280 /* 281 * disable break condition and fifos 282 */ 283 ubrlcr = clps_readl(UBRLCR(port)); 284 ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK); 285 clps_writel(ubrlcr, UBRLCR(port)); 286} 287 288static void 289clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, 290 struct ktermios *old) 291{ 292 unsigned int ubrlcr, baud, quot; 293 unsigned long flags; 294 295 /* 296 * We don't implement CREAD. 297 */ 298 termios->c_cflag |= CREAD; 299 300 /* 301 * Ask the core to calculate the divisor for us. 302 */ 303 baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 304 quot = uart_get_divisor(port, baud); 305 306 switch (termios->c_cflag & CSIZE) { 307 case CS5: 308 ubrlcr = UBRLCR_WRDLEN5; 309 break; 310 case CS6: 311 ubrlcr = UBRLCR_WRDLEN6; 312 break; 313 case CS7: 314 ubrlcr = UBRLCR_WRDLEN7; 315 break; 316 default: // CS8 317 ubrlcr = UBRLCR_WRDLEN8; 318 break; 319 } 320 if (termios->c_cflag & CSTOPB) 321 ubrlcr |= UBRLCR_XSTOP; 322 if (termios->c_cflag & PARENB) { 323 ubrlcr |= UBRLCR_PRTEN; 324 if (!(termios->c_cflag & PARODD)) 325 ubrlcr |= UBRLCR_EVENPRT; 326 } 327 if (port->fifosize > 1) 328 ubrlcr |= UBRLCR_FIFOEN; 329 330 spin_lock_irqsave(&port->lock, flags); 331 332 /* 333 * Update the per-port timeout. 334 */ 335 uart_update_timeout(port, termios->c_cflag, baud); 336 337 port->read_status_mask = UARTDR_OVERR; 338 if (termios->c_iflag & INPCK) 339 port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR; 340 341 /* 342 * Characters to ignore 343 */ 344 port->ignore_status_mask = 0; 345 if (termios->c_iflag & IGNPAR) 346 port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR; 347 if (termios->c_iflag & IGNBRK) { 348 /* 349 * If we're ignoring parity and break indicators, 350 * ignore overruns to (for real raw support). 351 */ 352 if (termios->c_iflag & IGNPAR) 353 port->ignore_status_mask |= UARTDR_OVERR; 354 } 355 356 quot -= 1; 357 358 clps_writel(ubrlcr | quot, UBRLCR(port)); 359 360 spin_unlock_irqrestore(&port->lock, flags); 361} 362 363static const char *clps711xuart_type(struct uart_port *port) 364{ 365 return port->type == PORT_CLPS711X ? "CLPS711x" : NULL; 366} 367 368/* 369 * Configure/autoconfigure the port. 370 */ 371static void clps711xuart_config_port(struct uart_port *port, int flags) 372{ 373 if (flags & UART_CONFIG_TYPE) 374 port->type = PORT_CLPS711X; 375} 376 377static void clps711xuart_release_port(struct uart_port *port) 378{ 379} 380 381static int clps711xuart_request_port(struct uart_port *port) 382{ 383 return 0; 384} 385 386static struct uart_ops clps711x_pops = { 387 .tx_empty = clps711xuart_tx_empty, 388 .set_mctrl = clps711xuart_set_mctrl_null, 389 .get_mctrl = clps711xuart_get_mctrl, 390 .stop_tx = clps711xuart_stop_tx, 391 .start_tx = clps711xuart_start_tx, 392 .stop_rx = clps711xuart_stop_rx, 393 .enable_ms = clps711xuart_enable_ms, 394 .break_ctl = clps711xuart_break_ctl, 395 .startup = clps711xuart_startup, 396 .shutdown = clps711xuart_shutdown, 397 .set_termios = clps711xuart_set_termios, 398 .type = clps711xuart_type, 399 .config_port = clps711xuart_config_port, 400 .release_port = clps711xuart_release_port, 401 .request_port = clps711xuart_request_port, 402}; 403 404static struct uart_port clps711x_ports[UART_NR] = { 405 { 406 .iobase = SYSCON1, 407 .irq = IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */ 408 .uartclk = 3686400, 409 .fifosize = 16, 410 .ops = &clps711x_pops, 411 .line = 0, 412 .flags = UPF_BOOT_AUTOCONF, 413 }, 414 { 415 .iobase = SYSCON2, 416 .irq = IRQ_UTXINT2, /* IRQ_URXINT2 */ 417 .uartclk = 3686400, 418 .fifosize = 16, 419 .ops = &clps711x_pops, 420 .line = 1, 421 .flags = UPF_BOOT_AUTOCONF, 422 } 423}; 424 425#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE 426static void clps711xuart_console_putchar(struct uart_port *port, int ch) 427{ 428 while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF) 429 barrier(); 430 clps_writel(ch, UARTDR(port)); 431} 432 433/* 434 * Print a string to the serial port trying not to disturb 435 * any possible real use of the port... 436 * 437 * The console_lock must be held when we get here. 438 * 439 * Note that this is called with interrupts already disabled 440 */ 441static void 442clps711xuart_console_write(struct console *co, const char *s, 443 unsigned int count) 444{ 445 struct uart_port *port = clps711x_ports + co->index; 446 unsigned int status, syscon; 447 448 /* 449 * Ensure that the port is enabled. 450 */ 451 syscon = clps_readl(SYSCON(port)); 452 clps_writel(syscon | SYSCON_UARTEN, SYSCON(port)); 453 454 uart_console_write(port, s, count, clps711xuart_console_putchar); 455 456 /* 457 * Finally, wait for transmitter to become empty 458 * and restore the uart state. 459 */ 460 do { 461 status = clps_readl(SYSFLG(port)); 462 } while (status & SYSFLG_UBUSY); 463 464 clps_writel(syscon, SYSCON(port)); 465} 466 467static void __init 468clps711xuart_console_get_options(struct uart_port *port, int *baud, 469 int *parity, int *bits) 470{ 471 if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) { 472 unsigned int ubrlcr, quot; 473 474 ubrlcr = clps_readl(UBRLCR(port)); 475 476 *parity = 'n'; 477 if (ubrlcr & UBRLCR_PRTEN) { 478 if (ubrlcr & UBRLCR_EVENPRT) 479 *parity = 'e'; 480 else 481 *parity = 'o'; 482 } 483 484 if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7) 485 *bits = 7; 486 else 487 *bits = 8; 488 489 quot = ubrlcr & UBRLCR_BAUD_MASK; 490 *baud = port->uartclk / (16 * (quot + 1)); 491 } 492} 493 494static int __init clps711xuart_console_setup(struct console *co, char *options) 495{ 496 struct uart_port *port; 497 int baud = 38400; 498 int bits = 8; 499 int parity = 'n'; 500 int flow = 'n'; 501 502 /* 503 * Check whether an invalid uart number has been specified, and 504 * if so, search for the first available port that does have 505 * console support. 506 */ 507 port = uart_get_console(clps711x_ports, UART_NR, co); 508 509 if (options) 510 uart_parse_options(options, &baud, &parity, &bits, &flow); 511 else 512 clps711xuart_console_get_options(port, &baud, &parity, &bits); 513 514 return uart_set_options(port, co, baud, parity, bits, flow); 515} 516 517static struct uart_driver clps711x_reg; 518static struct console clps711x_console = { 519 .name = "ttyCL", 520 .write = clps711xuart_console_write, 521 .device = uart_console_device, 522 .setup = clps711xuart_console_setup, 523 .flags = CON_PRINTBUFFER, 524 .index = -1, 525 .data = &clps711x_reg, 526}; 527 528static int __init clps711xuart_console_init(void) 529{ 530 register_console(&clps711x_console); 531 return 0; 532} 533console_initcall(clps711xuart_console_init); 534 535#define CLPS711X_CONSOLE &clps711x_console 536#else 537#define CLPS711X_CONSOLE NULL 538#endif 539 540static struct uart_driver clps711x_reg = { 541 .driver_name = "ttyCL", 542 .dev_name = "ttyCL", 543 .major = SERIAL_CLPS711X_MAJOR, 544 .minor = SERIAL_CLPS711X_MINOR, 545 .nr = UART_NR, 546 547 .cons = CLPS711X_CONSOLE, 548}; 549 550static int __init clps711xuart_init(void) 551{ 552 int ret, i; 553 554 printk(KERN_INFO "Serial: CLPS711x driver $Revision: 1.1.1.1 $\n"); 555 556 ret = uart_register_driver(&clps711x_reg); 557 if (ret) 558 return ret; 559 560 for (i = 0; i < UART_NR; i++) 561 uart_add_one_port(&clps711x_reg, &clps711x_ports[i]); 562 563 return 0; 564} 565 566static void __exit clps711xuart_exit(void) 567{ 568 int i; 569 570 for (i = 0; i < UART_NR; i++) 571 uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]); 572 573 uart_unregister_driver(&clps711x_reg); 574} 575 576module_init(clps711xuart_init); 577module_exit(clps711xuart_exit); 578 579MODULE_AUTHOR("Deep Blue Solutions Ltd"); 580MODULE_DESCRIPTION("CLPS-711x generic serial driver $Revision: 1.1.1.1 $"); 581MODULE_LICENSE("GPL"); 582MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR); 583