ia64/linux-2.6.18-xen.hg

annotate drivers/serial/serial_lh7a40x.c @ 897:329ea0ccb344

balloon: try harder to balloon up under memory pressure.

Currently if the balloon driver is unable to increase the guest's
reservation it assumes the failure was due to reaching its full
allocation, gives up on the ballooning operation and records the limit
it reached as the "hard limit". The driver will not try again until
the target is set again (even to the same value).

However it is possible that ballooning has in fact failed due to
memory pressure in the host and therefore it is desirable to keep
attempting to reach the target in case memory becomes available. The
most likely scenario is that some guests are ballooning down while
others are ballooning up and therefore there is temporary memory
pressure while things stabilise. You would not expect a well behaved
toolstack to ask a domain to balloon to more than its allocation nor
would you expect it to deliberately over-commit memory by setting
balloon targets which exceed the total host memory.

This patch drops the concept of a hard limit and causes the balloon
driver to retry increasing the reservation on a timer in the same
manner as when decreasing the reservation.

Also if we partially succeed in increasing the reservation
(i.e. receive less pages than we asked for) then we may as well keep
those pages rather than returning them to Xen.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jun 05 14:01:20 2009 +0100 (2009-06-05)
parents 831230e53067
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");