1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2018 Socionext Inc. 4 */ 5 6 #include <linux/clk.h> 7 #include <linux/console.h> 8 #include <linux/module.h> 9 #include <linux/of_irq.h> 10 #include <linux/platform_device.h> 11 #include <linux/serial_core.h> 12 #include <linux/tty.h> 13 #include <linux/tty_flip.h> 14 15 #define USIO_NAME "mlb-usio-uart" 16 #define USIO_UART_DEV_NAME "ttyUSI" 17 18 static struct uart_port mlb_usio_ports[CONFIG_SERIAL_MILBEAUT_USIO_PORTS]; 19 20 #define RX 0 21 #define TX 1 22 static int mlb_usio_irq[CONFIG_SERIAL_MILBEAUT_USIO_PORTS][2]; 23 24 #define MLB_USIO_REG_SMR 0 25 #define MLB_USIO_REG_SCR 1 26 #define MLB_USIO_REG_ESCR 2 27 #define MLB_USIO_REG_SSR 3 28 #define MLB_USIO_REG_DR 4 29 #define MLB_USIO_REG_BGR 6 30 #define MLB_USIO_REG_FCR 12 31 #define MLB_USIO_REG_FBYTE 14 32 33 #define MLB_USIO_SMR_SOE BIT(0) 34 #define MLB_USIO_SMR_SBL BIT(3) 35 #define MLB_USIO_SCR_TXE BIT(0) 36 #define MLB_USIO_SCR_RXE BIT(1) 37 #define MLB_USIO_SCR_TBIE BIT(2) 38 #define MLB_USIO_SCR_TIE BIT(3) 39 #define MLB_USIO_SCR_RIE BIT(4) 40 #define MLB_USIO_SCR_UPCL BIT(7) 41 #define MLB_USIO_ESCR_L_8BIT 0 42 #define MLB_USIO_ESCR_L_5BIT 1 43 #define MLB_USIO_ESCR_L_6BIT 2 44 #define MLB_USIO_ESCR_L_7BIT 3 45 #define MLB_USIO_ESCR_P BIT(3) 46 #define MLB_USIO_ESCR_PEN BIT(4) 47 #define MLB_USIO_ESCR_FLWEN BIT(7) 48 #define MLB_USIO_SSR_TBI BIT(0) 49 #define MLB_USIO_SSR_TDRE BIT(1) 50 #define MLB_USIO_SSR_RDRF BIT(2) 51 #define MLB_USIO_SSR_ORE BIT(3) 52 #define MLB_USIO_SSR_FRE BIT(4) 53 #define MLB_USIO_SSR_PE BIT(5) 54 #define MLB_USIO_SSR_REC BIT(7) 55 #define MLB_USIO_SSR_BRK BIT(8) 56 #define MLB_USIO_FCR_FE1 BIT(0) 57 #define MLB_USIO_FCR_FE2 BIT(1) 58 #define MLB_USIO_FCR_FCL1 BIT(2) 59 #define MLB_USIO_FCR_FCL2 BIT(3) 60 #define MLB_USIO_FCR_FSET BIT(4) 61 #define MLB_USIO_FCR_FTIE BIT(9) 62 #define MLB_USIO_FCR_FDRQ BIT(10) 63 #define MLB_USIO_FCR_FRIIE BIT(11) 64 65 static void mlb_usio_stop_tx(struct uart_port *port) 66 { 67 writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE, 68 port->membase + MLB_USIO_REG_FCR); 69 writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_TBIE, 70 port->membase + MLB_USIO_REG_SCR); 71 } 72 73 static void mlb_usio_tx_chars(struct uart_port *port) 74 { 75 struct circ_buf *xmit = &port->state->xmit; 76 int count; 77 78 writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE, 79 port->membase + MLB_USIO_REG_FCR); 80 writeb(readb(port->membase + MLB_USIO_REG_SCR) & 81 ~(MLB_USIO_SCR_TIE | MLB_USIO_SCR_TBIE), 82 port->membase + MLB_USIO_REG_SCR); 83 84 if (port->x_char) { 85 writew(port->x_char, port->membase + MLB_USIO_REG_DR); 86 port->icount.tx++; 87 port->x_char = 0; 88 return; 89 } 90 if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { 91 mlb_usio_stop_tx(port); 92 return; 93 } 94 95 count = port->fifosize - 96 (readw(port->membase + MLB_USIO_REG_FBYTE) & 0xff); 97 98 do { 99 writew(xmit->buf[xmit->tail], port->membase + MLB_USIO_REG_DR); 100 101 uart_xmit_advance(port, 1); 102 if (uart_circ_empty(xmit)) 103 break; 104 105 } while (--count > 0); 106 107 writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FDRQ, 108 port->membase + MLB_USIO_REG_FCR); 109 110 writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE, 111 port->membase + MLB_USIO_REG_SCR); 112 113 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 114 uart_write_wakeup(port); 115 116 if (uart_circ_empty(xmit)) 117 mlb_usio_stop_tx(port); 118 } 119 120 static void mlb_usio_start_tx(struct uart_port *port) 121 { 122 u16 fcr = readw(port->membase + MLB_USIO_REG_FCR); 123 124 writew(fcr | MLB_USIO_FCR_FTIE, port->membase + MLB_USIO_REG_FCR); 125 if (!(fcr & MLB_USIO_FCR_FDRQ)) 126 return; 127 128 writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE, 129 port->membase + MLB_USIO_REG_SCR); 130 131 if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) 132 mlb_usio_tx_chars(port); 133 } 134 135 static void mlb_usio_stop_rx(struct uart_port *port) 136 { 137 writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_RIE, 138 port->membase + MLB_USIO_REG_SCR); 139 } 140 141 static void mlb_usio_enable_ms(struct uart_port *port) 142 { 143 writeb(readb(port->membase + MLB_USIO_REG_SCR) | 144 MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE, 145 port->membase + MLB_USIO_REG_SCR); 146 } 147 148 static void mlb_usio_rx_chars(struct uart_port *port) 149 { 150 struct tty_port *ttyport = &port->state->port; 151 u8 flag = 0, ch = 0; 152 u8 status; 153 int max_count = 2; 154 155 while (max_count--) { 156 status = readb(port->membase + MLB_USIO_REG_SSR); 157 158 if (!(status & MLB_USIO_SSR_RDRF)) 159 break; 160 161 if (!(status & (MLB_USIO_SSR_ORE | MLB_USIO_SSR_FRE | 162 MLB_USIO_SSR_PE))) { 163 ch = readw(port->membase + MLB_USIO_REG_DR); 164 flag = TTY_NORMAL; 165 port->icount.rx++; 166 if (uart_handle_sysrq_char(port, ch)) 167 continue; 168 uart_insert_char(port, status, MLB_USIO_SSR_ORE, 169 ch, flag); 170 continue; 171 } 172 if (status & MLB_USIO_SSR_PE) 173 port->icount.parity++; 174 if (status & MLB_USIO_SSR_ORE) 175 port->icount.overrun++; 176 status &= port->read_status_mask; 177 if (status & MLB_USIO_SSR_BRK) { 178 flag = TTY_BREAK; 179 ch = 0; 180 } else 181 if (status & MLB_USIO_SSR_PE) { 182 flag = TTY_PARITY; 183 ch = 0; 184 } else 185 if (status & MLB_USIO_SSR_FRE) { 186 flag = TTY_FRAME; 187 ch = 0; 188 } 189 if (flag) 190 uart_insert_char(port, status, MLB_USIO_SSR_ORE, 191 ch, flag); 192 193 writeb(readb(port->membase + MLB_USIO_REG_SSR) | 194 MLB_USIO_SSR_REC, 195 port->membase + MLB_USIO_REG_SSR); 196 197 max_count = readw(port->membase + MLB_USIO_REG_FBYTE) >> 8; 198 writew(readw(port->membase + MLB_USIO_REG_FCR) | 199 MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE, 200 port->membase + MLB_USIO_REG_FCR); 201 } 202 203 tty_flip_buffer_push(ttyport); 204 } 205 206 static irqreturn_t mlb_usio_rx_irq(int irq, void *dev_id) 207 { 208 struct uart_port *port = dev_id; 209 210 spin_lock(&port->lock); 211 mlb_usio_rx_chars(port); 212 spin_unlock(&port->lock); 213 214 return IRQ_HANDLED; 215 } 216 217 static irqreturn_t mlb_usio_tx_irq(int irq, void *dev_id) 218 { 219 struct uart_port *port = dev_id; 220 221 spin_lock(&port->lock); 222 if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) 223 mlb_usio_tx_chars(port); 224 spin_unlock(&port->lock); 225 226 return IRQ_HANDLED; 227 } 228 229 static unsigned int mlb_usio_tx_empty(struct uart_port *port) 230 { 231 return (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) ? 232 TIOCSER_TEMT : 0; 233 } 234 235 static void mlb_usio_set_mctrl(struct uart_port *port, unsigned int mctrl) 236 { 237 } 238 239 static unsigned int mlb_usio_get_mctrl(struct uart_port *port) 240 { 241 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; 242 243 } 244 245 static void mlb_usio_break_ctl(struct uart_port *port, int break_state) 246 { 247 } 248 249 static int mlb_usio_startup(struct uart_port *port) 250 { 251 const char *portname = to_platform_device(port->dev)->name; 252 unsigned long flags; 253 int ret, index = port->line; 254 unsigned char escr; 255 256 ret = request_irq(mlb_usio_irq[index][RX], mlb_usio_rx_irq, 257 0, portname, port); 258 if (ret) 259 return ret; 260 ret = request_irq(mlb_usio_irq[index][TX], mlb_usio_tx_irq, 261 0, portname, port); 262 if (ret) { 263 free_irq(mlb_usio_irq[index][RX], port); 264 return ret; 265 } 266 267 escr = readb(port->membase + MLB_USIO_REG_ESCR); 268 if (of_property_read_bool(port->dev->of_node, "auto-flow-control")) 269 escr |= MLB_USIO_ESCR_FLWEN; 270 spin_lock_irqsave(&port->lock, flags); 271 writeb(0, port->membase + MLB_USIO_REG_SCR); 272 writeb(escr, port->membase + MLB_USIO_REG_ESCR); 273 writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR); 274 writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR); 275 writew(0, port->membase + MLB_USIO_REG_FCR); 276 writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2, 277 port->membase + MLB_USIO_REG_FCR); 278 writew(MLB_USIO_FCR_FE1 | MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE, 279 port->membase + MLB_USIO_REG_FCR); 280 writew(0, port->membase + MLB_USIO_REG_FBYTE); 281 writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE); 282 283 writeb(MLB_USIO_SCR_TXE | MLB_USIO_SCR_RIE | MLB_USIO_SCR_TBIE | 284 MLB_USIO_SCR_RXE, port->membase + MLB_USIO_REG_SCR); 285 spin_unlock_irqrestore(&port->lock, flags); 286 287 return 0; 288 } 289 290 static void mlb_usio_shutdown(struct uart_port *port) 291 { 292 int index = port->line; 293 294 free_irq(mlb_usio_irq[index][RX], port); 295 free_irq(mlb_usio_irq[index][TX], port); 296 } 297 298 static void mlb_usio_set_termios(struct uart_port *port, 299 struct ktermios *termios, 300 const struct ktermios *old) 301 { 302 unsigned int escr, smr = MLB_USIO_SMR_SOE; 303 unsigned long flags, baud, quot; 304 305 switch (termios->c_cflag & CSIZE) { 306 case CS5: 307 escr = MLB_USIO_ESCR_L_5BIT; 308 break; 309 case CS6: 310 escr = MLB_USIO_ESCR_L_6BIT; 311 break; 312 case CS7: 313 escr = MLB_USIO_ESCR_L_7BIT; 314 break; 315 case CS8: 316 default: 317 escr = MLB_USIO_ESCR_L_8BIT; 318 break; 319 } 320 321 if (termios->c_cflag & CSTOPB) 322 smr |= MLB_USIO_SMR_SBL; 323 324 if (termios->c_cflag & PARENB) { 325 escr |= MLB_USIO_ESCR_PEN; 326 if (termios->c_cflag & PARODD) 327 escr |= MLB_USIO_ESCR_P; 328 } 329 /* Set hard flow control */ 330 if (of_property_read_bool(port->dev->of_node, "auto-flow-control") || 331 (termios->c_cflag & CRTSCTS)) 332 escr |= MLB_USIO_ESCR_FLWEN; 333 334 baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk); 335 if (baud > 1) 336 quot = port->uartclk / baud - 1; 337 else 338 quot = 0; 339 340 spin_lock_irqsave(&port->lock, flags); 341 uart_update_timeout(port, termios->c_cflag, baud); 342 port->read_status_mask = MLB_USIO_SSR_ORE | MLB_USIO_SSR_RDRF | 343 MLB_USIO_SSR_TDRE; 344 if (termios->c_iflag & INPCK) 345 port->read_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE; 346 347 port->ignore_status_mask = 0; 348 if (termios->c_iflag & IGNPAR) 349 port->ignore_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE; 350 if ((termios->c_iflag & IGNBRK) && (termios->c_iflag & IGNPAR)) 351 port->ignore_status_mask |= MLB_USIO_SSR_ORE; 352 if ((termios->c_cflag & CREAD) == 0) 353 port->ignore_status_mask |= MLB_USIO_SSR_RDRF; 354 355 writeb(0, port->membase + MLB_USIO_REG_SCR); 356 writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR); 357 writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR); 358 writew(0, port->membase + MLB_USIO_REG_FCR); 359 writeb(smr, port->membase + MLB_USIO_REG_SMR); 360 writeb(escr, port->membase + MLB_USIO_REG_ESCR); 361 writew(quot, port->membase + MLB_USIO_REG_BGR); 362 writew(0, port->membase + MLB_USIO_REG_FCR); 363 writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2 | MLB_USIO_FCR_FE1 | 364 MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE, 365 port->membase + MLB_USIO_REG_FCR); 366 writew(0, port->membase + MLB_USIO_REG_FBYTE); 367 writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE); 368 writeb(MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE | MLB_USIO_SCR_TBIE | 369 MLB_USIO_SCR_TXE, port->membase + MLB_USIO_REG_SCR); 370 spin_unlock_irqrestore(&port->lock, flags); 371 } 372 373 static const char *mlb_usio_type(struct uart_port *port) 374 { 375 return ((port->type == PORT_MLB_USIO) ? USIO_NAME : NULL); 376 } 377 378 static void mlb_usio_config_port(struct uart_port *port, int flags) 379 { 380 if (flags & UART_CONFIG_TYPE) 381 port->type = PORT_MLB_USIO; 382 } 383 384 static const struct uart_ops mlb_usio_ops = { 385 .tx_empty = mlb_usio_tx_empty, 386 .set_mctrl = mlb_usio_set_mctrl, 387 .get_mctrl = mlb_usio_get_mctrl, 388 .stop_tx = mlb_usio_stop_tx, 389 .start_tx = mlb_usio_start_tx, 390 .stop_rx = mlb_usio_stop_rx, 391 .enable_ms = mlb_usio_enable_ms, 392 .break_ctl = mlb_usio_break_ctl, 393 .startup = mlb_usio_startup, 394 .shutdown = mlb_usio_shutdown, 395 .set_termios = mlb_usio_set_termios, 396 .type = mlb_usio_type, 397 .config_port = mlb_usio_config_port, 398 }; 399 400 #ifdef CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE 401 402 static void mlb_usio_console_putchar(struct uart_port *port, unsigned char c) 403 { 404 while (!(readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TDRE)) 405 cpu_relax(); 406 407 writew(c, port->membase + MLB_USIO_REG_DR); 408 } 409 410 static void mlb_usio_console_write(struct console *co, const char *s, 411 unsigned int count) 412 { 413 struct uart_port *port = &mlb_usio_ports[co->index]; 414 415 uart_console_write(port, s, count, mlb_usio_console_putchar); 416 } 417 418 static int __init mlb_usio_console_setup(struct console *co, char *options) 419 { 420 struct uart_port *port; 421 int baud = 115200; 422 int parity = 'n'; 423 int flow = 'n'; 424 int bits = 8; 425 426 if (co->index >= CONFIG_SERIAL_MILBEAUT_USIO_PORTS) 427 return -ENODEV; 428 429 port = &mlb_usio_ports[co->index]; 430 if (!port->membase) 431 return -ENODEV; 432 433 434 if (options) 435 uart_parse_options(options, &baud, &parity, &bits, &flow); 436 437 if (of_property_read_bool(port->dev->of_node, "auto-flow-control")) 438 flow = 'r'; 439 440 return uart_set_options(port, co, baud, parity, bits, flow); 441 } 442 443 444 static struct uart_driver mlb_usio_uart_driver; 445 static struct console mlb_usio_console = { 446 .name = USIO_UART_DEV_NAME, 447 .write = mlb_usio_console_write, 448 .device = uart_console_device, 449 .setup = mlb_usio_console_setup, 450 .flags = CON_PRINTBUFFER, 451 .index = -1, 452 .data = &mlb_usio_uart_driver, 453 }; 454 455 static int __init mlb_usio_console_init(void) 456 { 457 register_console(&mlb_usio_console); 458 return 0; 459 } 460 console_initcall(mlb_usio_console_init); 461 462 463 static void mlb_usio_early_console_write(struct console *co, const char *s, 464 u_int count) 465 { 466 struct earlycon_device *dev = co->data; 467 468 uart_console_write(&dev->port, s, count, mlb_usio_console_putchar); 469 } 470 471 static int __init mlb_usio_early_console_setup(struct earlycon_device *device, 472 const char *opt) 473 { 474 if (!device->port.membase) 475 return -ENODEV; 476 device->con->write = mlb_usio_early_console_write; 477 return 0; 478 } 479 480 OF_EARLYCON_DECLARE(mlb_usio, "socionext,milbeaut-usio-uart", 481 mlb_usio_early_console_setup); 482 483 #define USIO_CONSOLE (&mlb_usio_console) 484 #else 485 #define USIO_CONSOLE NULL 486 #endif 487 488 static struct uart_driver mlb_usio_uart_driver = { 489 .owner = THIS_MODULE, 490 .driver_name = USIO_NAME, 491 .dev_name = USIO_UART_DEV_NAME, 492 .cons = USIO_CONSOLE, 493 .nr = CONFIG_SERIAL_MILBEAUT_USIO_PORTS, 494 }; 495 496 static int mlb_usio_probe(struct platform_device *pdev) 497 { 498 struct clk *clk = devm_clk_get(&pdev->dev, NULL); 499 struct uart_port *port; 500 struct resource *res; 501 int index = 0; 502 int ret; 503 504 if (IS_ERR(clk)) { 505 dev_err(&pdev->dev, "Missing clock\n"); 506 return PTR_ERR(clk); 507 } 508 ret = clk_prepare_enable(clk); 509 if (ret) { 510 dev_err(&pdev->dev, "Clock enable failed: %d\n", ret); 511 return ret; 512 } 513 of_property_read_u32(pdev->dev.of_node, "index", &index); 514 port = &mlb_usio_ports[index]; 515 516 port->private_data = (void *)clk; 517 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 518 if (res == NULL) { 519 dev_err(&pdev->dev, "Missing regs\n"); 520 ret = -ENODEV; 521 goto failed; 522 } 523 port->membase = devm_ioremap(&pdev->dev, res->start, 524 resource_size(res)); 525 526 ret = platform_get_irq_byname(pdev, "rx"); 527 mlb_usio_irq[index][RX] = ret; 528 529 ret = platform_get_irq_byname(pdev, "tx"); 530 mlb_usio_irq[index][TX] = ret; 531 532 port->irq = mlb_usio_irq[index][RX]; 533 port->uartclk = clk_get_rate(clk); 534 port->fifosize = 128; 535 port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE); 536 port->iotype = UPIO_MEM32; 537 port->flags = UPF_BOOT_AUTOCONF | UPF_SPD_VHI; 538 port->line = index; 539 port->ops = &mlb_usio_ops; 540 port->dev = &pdev->dev; 541 542 ret = uart_add_one_port(&mlb_usio_uart_driver, port); 543 if (ret) { 544 dev_err(&pdev->dev, "Adding port failed: %d\n", ret); 545 goto failed; 546 } 547 return 0; 548 549 failed: 550 clk_disable_unprepare(clk); 551 552 return ret; 553 } 554 555 static int mlb_usio_remove(struct platform_device *pdev) 556 { 557 struct uart_port *port = &mlb_usio_ports[pdev->id]; 558 struct clk *clk = port->private_data; 559 560 uart_remove_one_port(&mlb_usio_uart_driver, port); 561 clk_disable_unprepare(clk); 562 563 return 0; 564 } 565 566 static const struct of_device_id mlb_usio_dt_ids[] = { 567 { .compatible = "socionext,milbeaut-usio-uart" }, 568 { /* sentinel */ } 569 }; 570 MODULE_DEVICE_TABLE(of, mlb_usio_dt_ids); 571 572 static struct platform_driver mlb_usio_driver = { 573 .probe = mlb_usio_probe, 574 .remove = mlb_usio_remove, 575 .driver = { 576 .name = USIO_NAME, 577 .of_match_table = mlb_usio_dt_ids, 578 }, 579 }; 580 581 static int __init mlb_usio_init(void) 582 { 583 int ret = uart_register_driver(&mlb_usio_uart_driver); 584 585 if (ret) { 586 pr_err("%s: uart registration failed: %d\n", __func__, ret); 587 return ret; 588 } 589 ret = platform_driver_register(&mlb_usio_driver); 590 if (ret) { 591 uart_unregister_driver(&mlb_usio_uart_driver); 592 pr_err("%s: drv registration failed: %d\n", __func__, ret); 593 return ret; 594 } 595 596 return 0; 597 } 598 599 static void __exit mlb_usio_exit(void) 600 { 601 platform_driver_unregister(&mlb_usio_driver); 602 uart_unregister_driver(&mlb_usio_uart_driver); 603 } 604 605 module_init(mlb_usio_init); 606 module_exit(mlb_usio_exit); 607 608 MODULE_AUTHOR("SOCIONEXT"); 609 MODULE_DESCRIPTION("MILBEAUT_USIO/UART Driver"); 610 MODULE_LICENSE("GPL"); 611