ia64/linux-2.6.18-xen.hg

annotate drivers/serial/serial_lh7a40x.c @ 0:831230e53067

Import 2.6.18 from kernel.org tarball.
author Ian Campbell <ian.campbell@xensource.com>
date Wed Apr 11 14:15:44 2007 +0100 (2007-04-11)
parents
children
rev   line source
ian@0 1 /* drivers/serial/serial_lh7a40x.c
ian@0 2 *
ian@0 3 * Copyright (C) 2004 Coastal Environmental Systems
ian@0 4 *
ian@0 5 * This program is free software; you can redistribute it and/or
ian@0 6 * modify it under the terms of the GNU General Public License
ian@0 7 * version 2 as published by the Free Software Foundation.
ian@0 8 *
ian@0 9 */
ian@0 10
ian@0 11 /* Driver for Sharp LH7A40X embedded serial ports
ian@0 12 *
ian@0 13 * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
ian@0 14 * Based on drivers/serial/amba.c, by Deep Blue Solutions Ltd.
ian@0 15 *
ian@0 16 * ---
ian@0 17 *
ian@0 18 * This driver supports the embedded UARTs of the Sharp LH7A40X series
ian@0 19 * CPUs. While similar to the 16550 and other UART chips, there is
ian@0 20 * nothing close to register compatibility. Moreover, some of the
ian@0 21 * modem control lines are not available, either in the chip or they
ian@0 22 * are lacking in the board-level implementation.
ian@0 23 *
ian@0 24 * - Use of SIRDIS
ian@0 25 * For simplicity, we disable the IR functions of any UART whenever
ian@0 26 * we enable it.
ian@0 27 *
ian@0 28 */
ian@0 29
ian@0 30
ian@0 31 #if defined(CONFIG_SERIAL_LH7A40X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
ian@0 32 #define SUPPORT_SYSRQ
ian@0 33 #endif
ian@0 34
ian@0 35 #include <linux/module.h>
ian@0 36 #include <linux/ioport.h>
ian@0 37 #include <linux/init.h>
ian@0 38 #include <linux/console.h>
ian@0 39 #include <linux/sysrq.h>
ian@0 40 #include <linux/tty.h>
ian@0 41 #include <linux/tty_flip.h>
ian@0 42 #include <linux/serial_core.h>
ian@0 43 #include <linux/serial.h>
ian@0 44
ian@0 45 #include <asm/io.h>
ian@0 46 #include <asm/irq.h>
ian@0 47
ian@0 48 #define DEV_MAJOR 204
ian@0 49 #define DEV_MINOR 16
ian@0 50 #define DEV_NR 3
ian@0 51
ian@0 52 #define ISR_LOOP_LIMIT 256
ian@0 53
ian@0 54 #define UR(p,o) _UR ((p)->membase, o)
ian@0 55 #define _UR(b,o) (*((volatile unsigned int*)(((unsigned char*) b) + (o))))
ian@0 56 #define BIT_CLR(p,o,m) UR(p,o) = UR(p,o) & (~(unsigned int)m)
ian@0 57 #define BIT_SET(p,o,m) UR(p,o) = UR(p,o) | ( (unsigned int)m)
ian@0 58
ian@0 59 #define UART_REG_SIZE 32
ian@0 60
ian@0 61 #define UART_R_DATA (0x00)
ian@0 62 #define UART_R_FCON (0x04)
ian@0 63 #define UART_R_BRCON (0x08)
ian@0 64 #define UART_R_CON (0x0c)
ian@0 65 #define UART_R_STATUS (0x10)
ian@0 66 #define UART_R_RAWISR (0x14)
ian@0 67 #define UART_R_INTEN (0x18)
ian@0 68 #define UART_R_ISR (0x1c)
ian@0 69
ian@0 70 #define UARTEN (0x01) /* UART enable */
ian@0 71 #define SIRDIS (0x02) /* Serial IR disable (UART1 only) */
ian@0 72
ian@0 73 #define RxEmpty (0x10)
ian@0 74 #define TxEmpty (0x80)
ian@0 75 #define TxFull (0x20)
ian@0 76 #define nRxRdy RxEmpty
ian@0 77 #define nTxRdy TxFull
ian@0 78 #define TxBusy (0x08)
ian@0 79
ian@0 80 #define RxBreak (0x0800)
ian@0 81 #define RxOverrunError (0x0400)
ian@0 82 #define RxParityError (0x0200)
ian@0 83 #define RxFramingError (0x0100)
ian@0 84 #define RxError (RxBreak | RxOverrunError | RxParityError | RxFramingError)
ian@0 85
ian@0 86 #define DCD (0x04)
ian@0 87 #define DSR (0x02)
ian@0 88 #define CTS (0x01)
ian@0 89
ian@0 90 #define RxInt (0x01)
ian@0 91 #define TxInt (0x02)
ian@0 92 #define ModemInt (0x04)
ian@0 93 #define RxTimeoutInt (0x08)
ian@0 94
ian@0 95 #define MSEOI (0x10)
ian@0 96
ian@0 97 #define WLEN_8 (0x60)
ian@0 98 #define WLEN_7 (0x40)
ian@0 99 #define WLEN_6 (0x20)
ian@0 100 #define WLEN_5 (0x00)
ian@0 101 #define WLEN (0x60) /* Mask for all word-length bits */
ian@0 102 #define STP2 (0x08)
ian@0 103 #define PEN (0x02) /* Parity Enable */
ian@0 104 #define EPS (0x04) /* Even Parity Set */
ian@0 105 #define FEN (0x10) /* FIFO Enable */
ian@0 106 #define BRK (0x01) /* Send Break */
ian@0 107
ian@0 108
ian@0 109 struct uart_port_lh7a40x {
ian@0 110 struct uart_port port;
ian@0 111 unsigned int statusPrev; /* Most recently read modem status */
ian@0 112 };
ian@0 113
ian@0 114 static void lh7a40xuart_stop_tx (struct uart_port* port)
ian@0 115 {
ian@0 116 BIT_CLR (port, UART_R_INTEN, TxInt);
ian@0 117 }
ian@0 118
ian@0 119 static void lh7a40xuart_start_tx (struct uart_port* port)
ian@0 120 {
ian@0 121 BIT_SET (port, UART_R_INTEN, TxInt);
ian@0 122
ian@0 123 /* *** FIXME: do I need to check for startup of the
ian@0 124 transmitter? The old driver did, but AMBA
ian@0 125 doesn't . */
ian@0 126 }
ian@0 127
ian@0 128 static void lh7a40xuart_stop_rx (struct uart_port* port)
ian@0 129 {
ian@0 130 BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
ian@0 131 }
ian@0 132
ian@0 133 static void lh7a40xuart_enable_ms (struct uart_port* port)
ian@0 134 {
ian@0 135 BIT_SET (port, UART_R_INTEN, ModemInt);
ian@0 136 }
ian@0 137
ian@0 138 static void
ian@0 139 #ifdef SUPPORT_SYSRQ
ian@0 140 lh7a40xuart_rx_chars (struct uart_port* port, struct pt_regs* regs)
ian@0 141 #else
ian@0 142 lh7a40xuart_rx_chars (struct uart_port* port)
ian@0 143 #endif
ian@0 144 {
ian@0 145 struct tty_struct* tty = port->info->tty;
ian@0 146 int cbRxMax = 256; /* (Gross) limit on receive */
ian@0 147 unsigned int data; /* Received data and status */
ian@0 148 unsigned int flag;
ian@0 149
ian@0 150 while (!(UR (port, UART_R_STATUS) & nRxRdy) && --cbRxMax) {
ian@0 151 data = UR (port, UART_R_DATA);
ian@0 152 flag = TTY_NORMAL;
ian@0 153 ++port->icount.rx;
ian@0 154
ian@0 155 if (unlikely(data & RxError)) {
ian@0 156 if (data & RxBreak) {
ian@0 157 data &= ~(RxFramingError | RxParityError);
ian@0 158 ++port->icount.brk;
ian@0 159 if (uart_handle_break (port))
ian@0 160 continue;
ian@0 161 }
ian@0 162 else if (data & RxParityError)
ian@0 163 ++port->icount.parity;
ian@0 164 else if (data & RxFramingError)
ian@0 165 ++port->icount.frame;
ian@0 166 if (data & RxOverrunError)
ian@0 167 ++port->icount.overrun;
ian@0 168
ian@0 169 /* Mask by termios, leave Rx'd byte */
ian@0 170 data &= port->read_status_mask | 0xff;
ian@0 171
ian@0 172 if (data & RxBreak)
ian@0 173 flag = TTY_BREAK;
ian@0 174 else if (data & RxParityError)
ian@0 175 flag = TTY_PARITY;
ian@0 176 else if (data & RxFramingError)
ian@0 177 flag = TTY_FRAME;
ian@0 178 }
ian@0 179
ian@0 180 if (uart_handle_sysrq_char (port, (unsigned char) data, regs))
ian@0 181 continue;
ian@0 182
ian@0 183 uart_insert_char(port, data, RxOverrunError, data, flag);
ian@0 184 }
ian@0 185 tty_flip_buffer_push (tty);
ian@0 186 return;
ian@0 187 }
ian@0 188
ian@0 189 static void lh7a40xuart_tx_chars (struct uart_port* port)
ian@0 190 {
ian@0 191 struct circ_buf* xmit = &port->info->xmit;
ian@0 192 int cbTxMax = port->fifosize;
ian@0 193
ian@0 194 if (port->x_char) {
ian@0 195 UR (port, UART_R_DATA) = port->x_char;
ian@0 196 ++port->icount.tx;
ian@0 197 port->x_char = 0;
ian@0 198 return;
ian@0 199 }
ian@0 200 if (uart_circ_empty (xmit) || uart_tx_stopped (port)) {
ian@0 201 lh7a40xuart_stop_tx (port);
ian@0 202 return;
ian@0 203 }
ian@0 204
ian@0 205 /* Unlike the AMBA UART, the lh7a40x UART does not guarantee
ian@0 206 that at least half of the FIFO is empty. Instead, we check
ian@0 207 status for every character. Using the AMBA method causes
ian@0 208 the transmitter to drop characters. */
ian@0 209
ian@0 210 do {
ian@0 211 UR (port, UART_R_DATA) = xmit->buf[xmit->tail];
ian@0 212 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
ian@0 213 ++port->icount.tx;
ian@0 214 if (uart_circ_empty(xmit))
ian@0 215 break;
ian@0 216 } while (!(UR (port, UART_R_STATUS) & nTxRdy)
ian@0 217 && cbTxMax--);
ian@0 218
ian@0 219 if (uart_circ_chars_pending (xmit) < WAKEUP_CHARS)
ian@0 220 uart_write_wakeup (port);
ian@0 221
ian@0 222 if (uart_circ_empty (xmit))
ian@0 223 lh7a40xuart_stop_tx (port);
ian@0 224 }
ian@0 225
ian@0 226 static void lh7a40xuart_modem_status (struct uart_port* port)
ian@0 227 {
ian@0 228 unsigned int status = UR (port, UART_R_STATUS);
ian@0 229 unsigned int delta
ian@0 230 = status ^ ((struct uart_port_lh7a40x*) port)->statusPrev;
ian@0 231
ian@0 232 BIT_SET (port, UART_R_RAWISR, MSEOI); /* Clear modem status intr */
ian@0 233
ian@0 234 if (!delta) /* Only happens if we missed 2 transitions */
ian@0 235 return;
ian@0 236
ian@0 237 ((struct uart_port_lh7a40x*) port)->statusPrev = status;
ian@0 238
ian@0 239 if (delta & DCD)
ian@0 240 uart_handle_dcd_change (port, status & DCD);
ian@0 241
ian@0 242 if (delta & DSR)
ian@0 243 ++port->icount.dsr;
ian@0 244
ian@0 245 if (delta & CTS)
ian@0 246 uart_handle_cts_change (port, status & CTS);
ian@0 247
ian@0 248 wake_up_interruptible (&port->info->delta_msr_wait);
ian@0 249 }
ian@0 250
ian@0 251 static irqreturn_t lh7a40xuart_int (int irq, void* dev_id,
ian@0 252 struct pt_regs* regs)
ian@0 253 {
ian@0 254 struct uart_port* port = dev_id;
ian@0 255 unsigned int cLoopLimit = ISR_LOOP_LIMIT;
ian@0 256 unsigned int isr = UR (port, UART_R_ISR);
ian@0 257
ian@0 258
ian@0 259 do {
ian@0 260 if (isr & (RxInt | RxTimeoutInt))
ian@0 261 #ifdef SUPPORT_SYSRQ
ian@0 262 lh7a40xuart_rx_chars(port, regs);
ian@0 263 #else
ian@0 264 lh7a40xuart_rx_chars(port);
ian@0 265 #endif
ian@0 266 if (isr & ModemInt)
ian@0 267 lh7a40xuart_modem_status (port);
ian@0 268 if (isr & TxInt)
ian@0 269 lh7a40xuart_tx_chars (port);
ian@0 270
ian@0 271 if (--cLoopLimit == 0)
ian@0 272 break;
ian@0 273
ian@0 274 isr = UR (port, UART_R_ISR);
ian@0 275 } while (isr & (RxInt | TxInt | RxTimeoutInt));
ian@0 276
ian@0 277 return IRQ_HANDLED;
ian@0 278 }
ian@0 279
ian@0 280 static unsigned int lh7a40xuart_tx_empty (struct uart_port* port)
ian@0 281 {
ian@0 282 return (UR (port, UART_R_STATUS) & TxEmpty) ? TIOCSER_TEMT : 0;
ian@0 283 }
ian@0 284
ian@0 285 static unsigned int lh7a40xuart_get_mctrl (struct uart_port* port)
ian@0 286 {
ian@0 287 unsigned int result = 0;
ian@0 288 unsigned int status = UR (port, UART_R_STATUS);
ian@0 289
ian@0 290 if (status & DCD)
ian@0 291 result |= TIOCM_CAR;
ian@0 292 if (status & DSR)
ian@0 293 result |= TIOCM_DSR;
ian@0 294 if (status & CTS)
ian@0 295 result |= TIOCM_CTS;
ian@0 296
ian@0 297 return result;
ian@0 298 }
ian@0 299
ian@0 300 static void lh7a40xuart_set_mctrl (struct uart_port* port, unsigned int mctrl)
ian@0 301 {
ian@0 302 /* None of the ports supports DTR. UART1 supports RTS through GPIO. */
ian@0 303 /* Note, kernel appears to be setting DTR and RTS on console. */
ian@0 304
ian@0 305 /* *** FIXME: this deserves more work. There's some work in
ian@0 306 tracing all of the IO pins. */
ian@0 307 #if 0
ian@0 308 if( port->mapbase == UART1_PHYS) {
ian@0 309 gpioRegs_t *gpio = (gpioRegs_t *)IO_ADDRESS(GPIO_PHYS);
ian@0 310
ian@0 311 if (mctrl & TIOCM_RTS)
ian@0 312 gpio->pbdr &= ~GPIOB_UART1_RTS;
ian@0 313 else
ian@0 314 gpio->pbdr |= GPIOB_UART1_RTS;
ian@0 315 }
ian@0 316 #endif
ian@0 317 }
ian@0 318
ian@0 319 static void lh7a40xuart_break_ctl (struct uart_port* port, int break_state)
ian@0 320 {
ian@0 321 unsigned long flags;
ian@0 322
ian@0 323 spin_lock_irqsave(&port->lock, flags);
ian@0 324 if (break_state == -1)
ian@0 325 BIT_SET (port, UART_R_FCON, BRK); /* Assert break */
ian@0 326 else
ian@0 327 BIT_CLR (port, UART_R_FCON, BRK); /* Deassert break */
ian@0 328 spin_unlock_irqrestore(&port->lock, flags);
ian@0 329 }
ian@0 330
ian@0 331 static int lh7a40xuart_startup (struct uart_port* port)
ian@0 332 {
ian@0 333 int retval;
ian@0 334
ian@0 335 retval = request_irq (port->irq, lh7a40xuart_int, 0,
ian@0 336 "serial_lh7a40x", port);
ian@0 337 if (retval)
ian@0 338 return retval;
ian@0 339
ian@0 340 /* Initial modem control-line settings */
ian@0 341 ((struct uart_port_lh7a40x*) port)->statusPrev
ian@0 342 = UR (port, UART_R_STATUS);
ian@0 343
ian@0 344 /* There is presently no configuration option to enable IR.
ian@0 345 Thus, we always disable it. */
ian@0 346
ian@0 347 BIT_SET (port, UART_R_CON, UARTEN | SIRDIS);
ian@0 348 BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
ian@0 349
ian@0 350 return 0;
ian@0 351 }
ian@0 352
ian@0 353 static void lh7a40xuart_shutdown (struct uart_port* port)
ian@0 354 {
ian@0 355 free_irq (port->irq, port);
ian@0 356 BIT_CLR (port, UART_R_FCON, BRK | FEN);
ian@0 357 BIT_CLR (port, UART_R_CON, UARTEN);
ian@0 358 }
ian@0 359
ian@0 360 static void lh7a40xuart_set_termios (struct uart_port* port,
ian@0 361 struct termios* termios,
ian@0 362 struct termios* old)
ian@0 363 {
ian@0 364 unsigned int con;
ian@0 365 unsigned int inten;
ian@0 366 unsigned int fcon;
ian@0 367 unsigned long flags;
ian@0 368 unsigned int baud;
ian@0 369 unsigned int quot;
ian@0 370
ian@0 371 baud = uart_get_baud_rate (port, termios, old, 8, port->uartclk/16);
ian@0 372 quot = uart_get_divisor (port, baud); /* -1 performed elsewhere */
ian@0 373
ian@0 374 switch (termios->c_cflag & CSIZE) {
ian@0 375 case CS5:
ian@0 376 fcon = WLEN_5;
ian@0 377 break;
ian@0 378 case CS6:
ian@0 379 fcon = WLEN_6;
ian@0 380 break;
ian@0 381 case CS7:
ian@0 382 fcon = WLEN_7;
ian@0 383 break;
ian@0 384 case CS8:
ian@0 385 default:
ian@0 386 fcon = WLEN_8;
ian@0 387 break;
ian@0 388 }
ian@0 389 if (termios->c_cflag & CSTOPB)
ian@0 390 fcon |= STP2;
ian@0 391 if (termios->c_cflag & PARENB) {
ian@0 392 fcon |= PEN;
ian@0 393 if (!(termios->c_cflag & PARODD))
ian@0 394 fcon |= EPS;
ian@0 395 }
ian@0 396 if (port->fifosize > 1)
ian@0 397 fcon |= FEN;
ian@0 398
ian@0 399 spin_lock_irqsave (&port->lock, flags);
ian@0 400
ian@0 401 uart_update_timeout (port, termios->c_cflag, baud);
ian@0 402
ian@0 403 port->read_status_mask = RxOverrunError;
ian@0 404 if (termios->c_iflag & INPCK)
ian@0 405 port->read_status_mask |= RxFramingError | RxParityError;
ian@0 406 if (termios->c_iflag & (BRKINT | PARMRK))
ian@0 407 port->read_status_mask |= RxBreak;
ian@0 408
ian@0 409 /* Figure mask for status we ignore */
ian@0 410 port->ignore_status_mask = 0;
ian@0 411 if (termios->c_iflag & IGNPAR)
ian@0 412 port->ignore_status_mask |= RxFramingError | RxParityError;
ian@0 413 if (termios->c_iflag & IGNBRK) {
ian@0 414 port->ignore_status_mask |= RxBreak;
ian@0 415 /* Ignore overrun when ignorning parity */
ian@0 416 /* *** FIXME: is this in the right place? */
ian@0 417 if (termios->c_iflag & IGNPAR)
ian@0 418 port->ignore_status_mask |= RxOverrunError;
ian@0 419 }
ian@0 420
ian@0 421 /* Ignore all receive errors when receive disabled */
ian@0 422 if ((termios->c_cflag & CREAD) == 0)
ian@0 423 port->ignore_status_mask |= RxError;
ian@0 424
ian@0 425 con = UR (port, UART_R_CON);
ian@0 426 inten = (UR (port, UART_R_INTEN) & ~ModemInt);
ian@0 427
ian@0 428 if (UART_ENABLE_MS (port, termios->c_cflag))
ian@0 429 inten |= ModemInt;
ian@0 430
ian@0 431 BIT_CLR (port, UART_R_CON, UARTEN); /* Disable UART */
ian@0 432 UR (port, UART_R_INTEN) = 0; /* Disable interrupts */
ian@0 433 UR (port, UART_R_BRCON) = quot - 1; /* Set baud rate divisor */
ian@0 434 UR (port, UART_R_FCON) = fcon; /* Set FIFO and frame ctrl */
ian@0 435 UR (port, UART_R_INTEN) = inten; /* Enable interrupts */
ian@0 436 UR (port, UART_R_CON) = con; /* Restore UART mode */
ian@0 437
ian@0 438 spin_unlock_irqrestore(&port->lock, flags);
ian@0 439 }
ian@0 440
ian@0 441 static const char* lh7a40xuart_type (struct uart_port* port)
ian@0 442 {
ian@0 443 return port->type == PORT_LH7A40X ? "LH7A40X" : NULL;
ian@0 444 }
ian@0 445
ian@0 446 static void lh7a40xuart_release_port (struct uart_port* port)
ian@0 447 {
ian@0 448 release_mem_region (port->mapbase, UART_REG_SIZE);
ian@0 449 }
ian@0 450
ian@0 451 static int lh7a40xuart_request_port (struct uart_port* port)
ian@0 452 {
ian@0 453 return request_mem_region (port->mapbase, UART_REG_SIZE,
ian@0 454 "serial_lh7a40x") != NULL
ian@0 455 ? 0 : -EBUSY;
ian@0 456 }
ian@0 457
ian@0 458 static void lh7a40xuart_config_port (struct uart_port* port, int flags)
ian@0 459 {
ian@0 460 if (flags & UART_CONFIG_TYPE) {
ian@0 461 port->type = PORT_LH7A40X;
ian@0 462 lh7a40xuart_request_port (port);
ian@0 463 }
ian@0 464 }
ian@0 465
ian@0 466 static int lh7a40xuart_verify_port (struct uart_port* port,
ian@0 467 struct serial_struct* ser)
ian@0 468 {
ian@0 469 int ret = 0;
ian@0 470
ian@0 471 if (ser->type != PORT_UNKNOWN && ser->type != PORT_LH7A40X)
ian@0 472 ret = -EINVAL;
ian@0 473 if (ser->irq < 0 || ser->irq >= NR_IRQS)
ian@0 474 ret = -EINVAL;
ian@0 475 if (ser->baud_base < 9600) /* *** FIXME: is this true? */
ian@0 476 ret = -EINVAL;
ian@0 477 return ret;
ian@0 478 }
ian@0 479
ian@0 480 static struct uart_ops lh7a40x_uart_ops = {
ian@0 481 .tx_empty = lh7a40xuart_tx_empty,
ian@0 482 .set_mctrl = lh7a40xuart_set_mctrl,
ian@0 483 .get_mctrl = lh7a40xuart_get_mctrl,
ian@0 484 .stop_tx = lh7a40xuart_stop_tx,
ian@0 485 .start_tx = lh7a40xuart_start_tx,
ian@0 486 .stop_rx = lh7a40xuart_stop_rx,
ian@0 487 .enable_ms = lh7a40xuart_enable_ms,
ian@0 488 .break_ctl = lh7a40xuart_break_ctl,
ian@0 489 .startup = lh7a40xuart_startup,
ian@0 490 .shutdown = lh7a40xuart_shutdown,
ian@0 491 .set_termios = lh7a40xuart_set_termios,
ian@0 492 .type = lh7a40xuart_type,
ian@0 493 .release_port = lh7a40xuart_release_port,
ian@0 494 .request_port = lh7a40xuart_request_port,
ian@0 495 .config_port = lh7a40xuart_config_port,
ian@0 496 .verify_port = lh7a40xuart_verify_port,
ian@0 497 };
ian@0 498
ian@0 499 static struct uart_port_lh7a40x lh7a40x_ports[DEV_NR] = {
ian@0 500 {
ian@0 501 .port = {
ian@0 502 .membase = (void*) io_p2v (UART1_PHYS),
ian@0 503 .mapbase = UART1_PHYS,
ian@0 504 .iotype = UPIO_MEM,
ian@0 505 .irq = IRQ_UART1INTR,
ian@0 506 .uartclk = 14745600/2,
ian@0 507 .fifosize = 16,
ian@0 508 .ops = &lh7a40x_uart_ops,
ian@0 509 .flags = UPF_BOOT_AUTOCONF,
ian@0 510 .line = 0,
ian@0 511 },
ian@0 512 },
ian@0 513 {
ian@0 514 .port = {
ian@0 515 .membase = (void*) io_p2v (UART2_PHYS),
ian@0 516 .mapbase = UART2_PHYS,
ian@0 517 .iotype = UPIO_MEM,
ian@0 518 .irq = IRQ_UART2INTR,
ian@0 519 .uartclk = 14745600/2,
ian@0 520 .fifosize = 16,
ian@0 521 .ops = &lh7a40x_uart_ops,
ian@0 522 .flags = UPF_BOOT_AUTOCONF,
ian@0 523 .line = 1,
ian@0 524 },
ian@0 525 },
ian@0 526 {
ian@0 527 .port = {
ian@0 528 .membase = (void*) io_p2v (UART3_PHYS),
ian@0 529 .mapbase = UART3_PHYS,
ian@0 530 .iotype = UPIO_MEM,
ian@0 531 .irq = IRQ_UART3INTR,
ian@0 532 .uartclk = 14745600/2,
ian@0 533 .fifosize = 16,
ian@0 534 .ops = &lh7a40x_uart_ops,
ian@0 535 .flags = UPF_BOOT_AUTOCONF,
ian@0 536 .line = 2,
ian@0 537 },
ian@0 538 },
ian@0 539 };
ian@0 540
ian@0 541 #ifndef CONFIG_SERIAL_LH7A40X_CONSOLE
ian@0 542 # define LH7A40X_CONSOLE NULL
ian@0 543 #else
ian@0 544 # define LH7A40X_CONSOLE &lh7a40x_console
ian@0 545
ian@0 546 static void lh7a40xuart_console_putchar(struct uart_port *port, int ch)
ian@0 547 {
ian@0 548 while (UR(port, UART_R_STATUS) & nTxRdy)
ian@0 549 ;
ian@0 550 UR(port, UART_R_DATA) = ch;
ian@0 551 }
ian@0 552
ian@0 553 static void lh7a40xuart_console_write (struct console* co,
ian@0 554 const char* s,
ian@0 555 unsigned int count)
ian@0 556 {
ian@0 557 struct uart_port* port = &lh7a40x_ports[co->index].port;
ian@0 558 unsigned int con = UR (port, UART_R_CON);
ian@0 559 unsigned int inten = UR (port, UART_R_INTEN);
ian@0 560
ian@0 561
ian@0 562 UR (port, UART_R_INTEN) = 0; /* Disable all interrupts */
ian@0 563 BIT_SET (port, UART_R_CON, UARTEN | SIRDIS); /* Enable UART */
ian@0 564
ian@0 565 uart_console_write(port, s, count, lh7a40xuart_console_putchar);
ian@0 566
ian@0 567 /* Wait until all characters are sent */
ian@0 568 while (UR (port, UART_R_STATUS) & TxBusy)
ian@0 569 ;
ian@0 570
ian@0 571 /* Restore control and interrupt mask */
ian@0 572 UR (port, UART_R_CON) = con;
ian@0 573 UR (port, UART_R_INTEN) = inten;
ian@0 574 }
ian@0 575
ian@0 576 static void __init lh7a40xuart_console_get_options (struct uart_port* port,
ian@0 577 int* baud,
ian@0 578 int* parity,
ian@0 579 int* bits)
ian@0 580 {
ian@0 581 if (UR (port, UART_R_CON) & UARTEN) {
ian@0 582 unsigned int fcon = UR (port, UART_R_FCON);
ian@0 583 unsigned int quot = UR (port, UART_R_BRCON) + 1;
ian@0 584
ian@0 585 switch (fcon & (PEN | EPS)) {
ian@0 586 default: *parity = 'n'; break;
ian@0 587 case PEN: *parity = 'o'; break;
ian@0 588 case PEN | EPS: *parity = 'e'; break;
ian@0 589 }
ian@0 590
ian@0 591 switch (fcon & WLEN) {
ian@0 592 default:
ian@0 593 case WLEN_8: *bits = 8; break;
ian@0 594 case WLEN_7: *bits = 7; break;
ian@0 595 case WLEN_6: *bits = 6; break;
ian@0 596 case WLEN_5: *bits = 5; break;
ian@0 597 }
ian@0 598
ian@0 599 *baud = port->uartclk/(16*quot);
ian@0 600 }
ian@0 601 }
ian@0 602
ian@0 603 static int __init lh7a40xuart_console_setup (struct console* co, char* options)
ian@0 604 {
ian@0 605 struct uart_port* port;
ian@0 606 int baud = 38400;
ian@0 607 int bits = 8;
ian@0 608 int parity = 'n';
ian@0 609 int flow = 'n';
ian@0 610
ian@0 611 if (co->index >= DEV_NR) /* Bounds check on device number */
ian@0 612 co->index = 0;
ian@0 613 port = &lh7a40x_ports[co->index].port;
ian@0 614
ian@0 615 if (options)
ian@0 616 uart_parse_options (options, &baud, &parity, &bits, &flow);
ian@0 617 else
ian@0 618 lh7a40xuart_console_get_options (port, &baud, &parity, &bits);
ian@0 619
ian@0 620 return uart_set_options (port, co, baud, parity, bits, flow);
ian@0 621 }
ian@0 622
ian@0 623 static struct uart_driver lh7a40x_reg;
ian@0 624 static struct console lh7a40x_console = {
ian@0 625 .name = "ttyAM",
ian@0 626 .write = lh7a40xuart_console_write,
ian@0 627 .device = uart_console_device,
ian@0 628 .setup = lh7a40xuart_console_setup,
ian@0 629 .flags = CON_PRINTBUFFER,
ian@0 630 .index = -1,
ian@0 631 .data = &lh7a40x_reg,
ian@0 632 };
ian@0 633
ian@0 634 static int __init lh7a40xuart_console_init(void)
ian@0 635 {
ian@0 636 register_console (&lh7a40x_console);
ian@0 637 return 0;
ian@0 638 }
ian@0 639
ian@0 640 console_initcall (lh7a40xuart_console_init);
ian@0 641
ian@0 642 #endif
ian@0 643
ian@0 644 static struct uart_driver lh7a40x_reg = {
ian@0 645 .owner = THIS_MODULE,
ian@0 646 .driver_name = "ttyAM",
ian@0 647 .dev_name = "ttyAM",
ian@0 648 .major = DEV_MAJOR,
ian@0 649 .minor = DEV_MINOR,
ian@0 650 .nr = DEV_NR,
ian@0 651 .cons = LH7A40X_CONSOLE,
ian@0 652 };
ian@0 653
ian@0 654 static int __init lh7a40xuart_init(void)
ian@0 655 {
ian@0 656 int ret;
ian@0 657
ian@0 658 printk (KERN_INFO "serial: LH7A40X serial driver\n");
ian@0 659
ian@0 660 ret = uart_register_driver (&lh7a40x_reg);
ian@0 661
ian@0 662 if (ret == 0) {
ian@0 663 int i;
ian@0 664
ian@0 665 for (i = 0; i < DEV_NR; i++) {
ian@0 666 /* UART3, when used, requires GPIO pin reallocation */
ian@0 667 if (lh7a40x_ports[i].port.mapbase == UART3_PHYS)
ian@0 668 GPIO_PINMUX |= 1<<3;
ian@0 669 uart_add_one_port (&lh7a40x_reg,
ian@0 670 &lh7a40x_ports[i].port);
ian@0 671 }
ian@0 672 }
ian@0 673 return ret;
ian@0 674 }
ian@0 675
ian@0 676 static void __exit lh7a40xuart_exit(void)
ian@0 677 {
ian@0 678 int i;
ian@0 679
ian@0 680 for (i = 0; i < DEV_NR; i++)
ian@0 681 uart_remove_one_port (&lh7a40x_reg, &lh7a40x_ports[i].port);
ian@0 682
ian@0 683 uart_unregister_driver (&lh7a40x_reg);
ian@0 684 }
ian@0 685
ian@0 686 module_init (lh7a40xuart_init);
ian@0 687 module_exit (lh7a40xuart_exit);
ian@0 688
ian@0 689 MODULE_AUTHOR ("Marc Singer");
ian@0 690 MODULE_DESCRIPTION ("Sharp LH7A40X serial port driver");
ian@0 691 MODULE_LICENSE ("GPL");