diff options
author | Alexander Stein <alexander.stein@systec-electronic.com> | 2011-11-15 18:04:07 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-11-15 18:50:31 -0500 |
commit | e30f867d402d6dcc2d03d8dd5da3863f7c83572a (patch) | |
tree | b2f6486b5beb598878533bc17dfce275086ede51 /drivers/tty/serial/pch_uart.c | |
parent | b82e324b3c46a554595c12b45465d1943a57326c (diff) |
drivers/tty/serial/pch_uart.c: add console support
Add console support to pch_uart. To enable append e.g.
console=ttyPCH0,115200 to your kernel command line.
This is not expected work on CM-iTC boards due to their having a different
clock.
Signed-off-by: Alexander Stein <alexander.stein@systec-electronic.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/tty/serial/pch_uart.c')
-rw-r--r-- | drivers/tty/serial/pch_uart.c | 160 |
1 files changed, 159 insertions, 1 deletions
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 21febef926a..b950d059a78 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c | |||
@@ -25,6 +25,9 @@ | |||
25 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
26 | #include <linux/io.h> | 26 | #include <linux/io.h> |
27 | #include <linux/dmi.h> | 27 | #include <linux/dmi.h> |
28 | #include <linux/console.h> | ||
29 | #include <linux/nmi.h> | ||
30 | #include <linux/delay.h> | ||
28 | 31 | ||
29 | #include <linux/dmaengine.h> | 32 | #include <linux/dmaengine.h> |
30 | #include <linux/pch_dma.h> | 33 | #include <linux/pch_dma.h> |
@@ -198,6 +201,10 @@ enum { | |||
198 | 201 | ||
199 | #define PCI_VENDOR_ID_ROHM 0x10DB | 202 | #define PCI_VENDOR_ID_ROHM 0x10DB |
200 | 203 | ||
204 | #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) | ||
205 | |||
206 | #define DEFAULT_BAUD_RATE 1843200 /* 1.8432MHz */ | ||
207 | |||
201 | struct pch_uart_buffer { | 208 | struct pch_uart_buffer { |
202 | unsigned char *buf; | 209 | unsigned char *buf; |
203 | int size; | 210 | int size; |
@@ -272,6 +279,9 @@ static struct pch_uart_driver_data drv_dat[] = { | |||
272 | [pch_ml7223_uart1] = {PCH_UART_2LINE, 1}, | 279 | [pch_ml7223_uart1] = {PCH_UART_2LINE, 1}, |
273 | }; | 280 | }; |
274 | 281 | ||
282 | #ifdef CONFIG_SERIAL_PCH_UART_CONSOLE | ||
283 | static struct eg20t_port *pch_uart_ports[PCH_UART_NR]; | ||
284 | #endif | ||
275 | static unsigned int default_baud = 9600; | 285 | static unsigned int default_baud = 9600; |
276 | static const int trigger_level_256[4] = { 1, 64, 128, 224 }; | 286 | static const int trigger_level_256[4] = { 1, 64, 128, 224 }; |
277 | static const int trigger_level_64[4] = { 1, 16, 32, 56 }; | 287 | static const int trigger_level_64[4] = { 1, 16, 32, 56 }; |
@@ -1380,6 +1390,143 @@ static struct uart_ops pch_uart_ops = { | |||
1380 | .verify_port = pch_uart_verify_port | 1390 | .verify_port = pch_uart_verify_port |
1381 | }; | 1391 | }; |
1382 | 1392 | ||
1393 | #ifdef CONFIG_SERIAL_PCH_UART_CONSOLE | ||
1394 | |||
1395 | /* | ||
1396 | * Wait for transmitter & holding register to empty | ||
1397 | */ | ||
1398 | static void wait_for_xmitr(struct eg20t_port *up, int bits) | ||
1399 | { | ||
1400 | unsigned int status, tmout = 10000; | ||
1401 | |||
1402 | /* Wait up to 10ms for the character(s) to be sent. */ | ||
1403 | for (;;) { | ||
1404 | status = ioread8(up->membase + UART_LSR); | ||
1405 | |||
1406 | if ((status & bits) == bits) | ||
1407 | break; | ||
1408 | if (--tmout == 0) | ||
1409 | break; | ||
1410 | udelay(1); | ||
1411 | } | ||
1412 | |||
1413 | /* Wait up to 1s for flow control if necessary */ | ||
1414 | if (up->port.flags & UPF_CONS_FLOW) { | ||
1415 | unsigned int tmout; | ||
1416 | for (tmout = 1000000; tmout; tmout--) { | ||
1417 | unsigned int msr = ioread8(up->membase + UART_MSR); | ||
1418 | if (msr & UART_MSR_CTS) | ||
1419 | break; | ||
1420 | udelay(1); | ||
1421 | touch_nmi_watchdog(); | ||
1422 | } | ||
1423 | } | ||
1424 | } | ||
1425 | |||
1426 | static void pch_console_putchar(struct uart_port *port, int ch) | ||
1427 | { | ||
1428 | struct eg20t_port *priv = | ||
1429 | container_of(port, struct eg20t_port, port); | ||
1430 | |||
1431 | wait_for_xmitr(priv, UART_LSR_THRE); | ||
1432 | iowrite8(ch, priv->membase + PCH_UART_THR); | ||
1433 | } | ||
1434 | |||
1435 | /* | ||
1436 | * Print a string to the serial port trying not to disturb | ||
1437 | * any possible real use of the port... | ||
1438 | * | ||
1439 | * The console_lock must be held when we get here. | ||
1440 | */ | ||
1441 | static void | ||
1442 | pch_console_write(struct console *co, const char *s, unsigned int count) | ||
1443 | { | ||
1444 | struct eg20t_port *priv; | ||
1445 | |||
1446 | unsigned long flags; | ||
1447 | u8 ier; | ||
1448 | int locked = 1; | ||
1449 | |||
1450 | priv = pch_uart_ports[co->index]; | ||
1451 | |||
1452 | touch_nmi_watchdog(); | ||
1453 | |||
1454 | local_irq_save(flags); | ||
1455 | if (priv->port.sysrq) { | ||
1456 | /* serial8250_handle_port() already took the lock */ | ||
1457 | locked = 0; | ||
1458 | } else if (oops_in_progress) { | ||
1459 | locked = spin_trylock(&priv->port.lock); | ||
1460 | } else | ||
1461 | spin_lock(&priv->port.lock); | ||
1462 | |||
1463 | /* | ||
1464 | * First save the IER then disable the interrupts | ||
1465 | */ | ||
1466 | ier = ioread8(priv->membase + UART_IER); | ||
1467 | |||
1468 | pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT); | ||
1469 | |||
1470 | uart_console_write(&priv->port, s, count, pch_console_putchar); | ||
1471 | |||
1472 | /* | ||
1473 | * Finally, wait for transmitter to become empty | ||
1474 | * and restore the IER | ||
1475 | */ | ||
1476 | wait_for_xmitr(priv, BOTH_EMPTY); | ||
1477 | iowrite8(ier, priv->membase + UART_IER); | ||
1478 | |||
1479 | if (locked) | ||
1480 | spin_unlock(&priv->port.lock); | ||
1481 | local_irq_restore(flags); | ||
1482 | } | ||
1483 | |||
1484 | static int __init pch_console_setup(struct console *co, char *options) | ||
1485 | { | ||
1486 | struct uart_port *port; | ||
1487 | int baud = 9600; | ||
1488 | int bits = 8; | ||
1489 | int parity = 'n'; | ||
1490 | int flow = 'n'; | ||
1491 | |||
1492 | /* | ||
1493 | * Check whether an invalid uart number has been specified, and | ||
1494 | * if so, search for the first available port that does have | ||
1495 | * console support. | ||
1496 | */ | ||
1497 | if (co->index >= PCH_UART_NR) | ||
1498 | co->index = 0; | ||
1499 | port = &pch_uart_ports[co->index]->port; | ||
1500 | |||
1501 | if (!port || (!port->iobase && !port->membase)) | ||
1502 | return -ENODEV; | ||
1503 | |||
1504 | /* setup uartclock */ | ||
1505 | port->uartclk = DEFAULT_BAUD_RATE; | ||
1506 | |||
1507 | if (options) | ||
1508 | uart_parse_options(options, &baud, &parity, &bits, &flow); | ||
1509 | |||
1510 | return uart_set_options(port, co, baud, parity, bits, flow); | ||
1511 | } | ||
1512 | |||
1513 | static struct uart_driver pch_uart_driver; | ||
1514 | |||
1515 | static struct console pch_console = { | ||
1516 | .name = PCH_UART_DRIVER_DEVICE, | ||
1517 | .write = pch_console_write, | ||
1518 | .device = uart_console_device, | ||
1519 | .setup = pch_console_setup, | ||
1520 | .flags = CON_PRINTBUFFER | CON_ANYTIME, | ||
1521 | .index = -1, | ||
1522 | .data = &pch_uart_driver, | ||
1523 | }; | ||
1524 | |||
1525 | #define PCH_CONSOLE (&pch_console) | ||
1526 | #else | ||
1527 | #define PCH_CONSOLE NULL | ||
1528 | #endif | ||
1529 | |||
1383 | static struct uart_driver pch_uart_driver = { | 1530 | static struct uart_driver pch_uart_driver = { |
1384 | .owner = THIS_MODULE, | 1531 | .owner = THIS_MODULE, |
1385 | .driver_name = KBUILD_MODNAME, | 1532 | .driver_name = KBUILD_MODNAME, |
@@ -1387,6 +1534,7 @@ static struct uart_driver pch_uart_driver = { | |||
1387 | .major = 0, | 1534 | .major = 0, |
1388 | .minor = 0, | 1535 | .minor = 0, |
1389 | .nr = PCH_UART_NR, | 1536 | .nr = PCH_UART_NR, |
1537 | .cons = PCH_CONSOLE, | ||
1390 | }; | 1538 | }; |
1391 | 1539 | ||
1392 | static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, | 1540 | static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, |
@@ -1413,7 +1561,7 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, | |||
1413 | if (!rxbuf) | 1561 | if (!rxbuf) |
1414 | goto init_port_free_txbuf; | 1562 | goto init_port_free_txbuf; |
1415 | 1563 | ||
1416 | base_baud = 1843200; /* 1.8432MHz */ | 1564 | base_baud = DEFAULT_BAUD_RATE; |
1417 | 1565 | ||
1418 | /* quirk for CM-iTC board */ | 1566 | /* quirk for CM-iTC board */ |
1419 | board_name = dmi_get_system_info(DMI_BOARD_NAME); | 1567 | board_name = dmi_get_system_info(DMI_BOARD_NAME); |
@@ -1463,6 +1611,9 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, | |||
1463 | pci_set_drvdata(pdev, priv); | 1611 | pci_set_drvdata(pdev, priv); |
1464 | pch_uart_hal_request(pdev, fifosize, base_baud); | 1612 | pch_uart_hal_request(pdev, fifosize, base_baud); |
1465 | 1613 | ||
1614 | #ifdef CONFIG_SERIAL_PCH_UART_CONSOLE | ||
1615 | pch_uart_ports[board->line_no] = priv; | ||
1616 | #endif | ||
1466 | ret = uart_add_one_port(&pch_uart_driver, &priv->port); | 1617 | ret = uart_add_one_port(&pch_uart_driver, &priv->port); |
1467 | if (ret < 0) | 1618 | if (ret < 0) |
1468 | goto init_port_hal_free; | 1619 | goto init_port_hal_free; |
@@ -1470,6 +1621,9 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, | |||
1470 | return priv; | 1621 | return priv; |
1471 | 1622 | ||
1472 | init_port_hal_free: | 1623 | init_port_hal_free: |
1624 | #ifdef CONFIG_SERIAL_PCH_UART_CONSOLE | ||
1625 | pch_uart_ports[board->line_no] = NULL; | ||
1626 | #endif | ||
1473 | free_page((unsigned long)rxbuf); | 1627 | free_page((unsigned long)rxbuf); |
1474 | init_port_free_txbuf: | 1628 | init_port_free_txbuf: |
1475 | kfree(priv); | 1629 | kfree(priv); |
@@ -1492,6 +1646,10 @@ static void pch_uart_pci_remove(struct pci_dev *pdev) | |||
1492 | priv = (struct eg20t_port *)pci_get_drvdata(pdev); | 1646 | priv = (struct eg20t_port *)pci_get_drvdata(pdev); |
1493 | 1647 | ||
1494 | pci_disable_msi(pdev); | 1648 | pci_disable_msi(pdev); |
1649 | |||
1650 | #ifdef CONFIG_SERIAL_PCH_UART_CONSOLE | ||
1651 | pch_uart_ports[priv->port.line] = NULL; | ||
1652 | #endif | ||
1495 | pch_uart_exit_port(priv); | 1653 | pch_uart_exit_port(priv); |
1496 | pci_disable_device(pdev); | 1654 | pci_disable_device(pdev); |
1497 | kfree(priv); | 1655 | kfree(priv); |