diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-26 15:11:09 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-26 15:11:09 +0200 |
commit | efb8d21b2c6db3497655cc6a033ae8a9883e4063 (patch) | |
tree | a14a0dbb9fec3a6db5e542ba7ed4a49681706420 /drivers/tty/serial/samsung.c | |
parent | 3cb603284b3d256ae9ae9e65887cee8416bfef15 (diff) | |
parent | d208a3bf77f902283894f546b6b5383202cf7882 (diff) |
Merge branch 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
* 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (79 commits)
TTY: serial_core: Fix crash if DCD drop during suspend
tty/serial: atmel_serial: bootconsole removed from auto-enumerates
Revert "TTY: call tty_driver_lookup_tty unconditionally"
tty/serial: atmel_serial: add device tree support
tty/serial: atmel_serial: auto-enumerate ports
tty/serial: atmel_serial: whitespace and braces modifications
tty/serial: atmel_serial: change platform_data variable name
tty/serial: RS485 bindings for device tree
TTY: call tty_driver_lookup_tty unconditionally
TTY: pty, release tty in all ptmx_open fail paths
TTY: make tty_add_file non-failing
TTY: drop driver reference in tty_open fail path
8250_pci: Fix kernel panic when pch_uart is disabled
h8300: drivers/serial/Kconfig was moved
parport_pc: release IO region properly if unsupported ITE887x card is found
tty: Support compat_ioctl get/set termios_locked
hvc_console: display printk messages on console.
TTY: snyclinkmp: forever loop in tx_load_dma_buffer()
tty/n_gsm: avoid fifo overflow in gsm_dlci_data_output
tty/n_gsm: fix a bug in gsm_dlci_data_output (adaption = 2 case)
...
Fix up Conflicts in:
- drivers/tty/serial/8250_pci.c
Trivial conflict with removed duplicate device ID
- drivers/tty/serial/atmel_serial.c
Annoying silly conflict between "specify the port num via
platform_data" and other changes to atmel_console_init
Diffstat (limited to 'drivers/tty/serial/samsung.c')
-rw-r--r-- | drivers/tty/serial/samsung.c | 107 |
1 files changed, 95 insertions, 12 deletions
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 6edafb5ace1..b31f1c3a2c4 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -83,6 +83,16 @@ static int s3c24xx_serial_txempty_nofifo(struct uart_port *port) return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE); } +/* + * s3c64xx and later SoC's include the interrupt mask and status registers in + * the controller itself, unlike the s3c24xx SoC's which have these registers + * in the interrupt controller. Check if the port type is s3c64xx or higher. + */ +static int s3c24xx_serial_has_interrupt_mask(struct uart_port *port) +{ + return to_ourport(port)->info->type == PORT_S3C6400; +} + static void s3c24xx_serial_rx_enable(struct uart_port *port) { unsigned long flags; @@ -126,7 +136,11 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port) struct s3c24xx_uart_port *ourport = to_ourport(port); if (tx_enabled(port)) { - disable_irq_nosync(ourport->tx_irq); + if (s3c24xx_serial_has_interrupt_mask(port)) + __set_bit(S3C64XX_UINTM_TXD, + portaddrl(port, S3C64XX_UINTM)); + else + disable_irq_nosync(ourport->tx_irq); tx_enabled(port) = 0; if (port->flags & UPF_CONS_FLOW) s3c24xx_serial_rx_enable(port); @@ -141,19 +155,26 @@ static void s3c24xx_serial_start_tx(struct uart_port *port) if (port->flags & UPF_CONS_FLOW) s3c24xx_serial_rx_disable(port); - enable_irq(ourport->tx_irq); + if (s3c24xx_serial_has_interrupt_mask(port)) + __clear_bit(S3C64XX_UINTM_TXD, + portaddrl(port, S3C64XX_UINTM)); + else + enable_irq(ourport->tx_irq); tx_enabled(port) = 1; } } - static void s3c24xx_serial_stop_rx(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); if (rx_enabled(port)) { dbg("s3c24xx_serial_stop_rx: port=%p\n", port); - disable_irq_nosync(ourport->rx_irq); + if (s3c24xx_serial_has_interrupt_mask(port)) + __set_bit(S3C64XX_UINTM_RXD, + portaddrl(port, S3C64XX_UINTM)); + else + disable_irq_nosync(ourport->rx_irq); rx_enabled(port) = 0; } } @@ -320,6 +341,28 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) return IRQ_HANDLED; } +/* interrupt handler for s3c64xx and later SoC's.*/ +static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id) +{ + struct s3c24xx_uart_port *ourport = id; + struct uart_port *port = &ourport->port; + unsigned int pend = rd_regl(port, S3C64XX_UINTP); + unsigned long flags; + irqreturn_t ret = IRQ_HANDLED; + + spin_lock_irqsave(&port->lock, flags); + if (pend & S3C64XX_UINTM_RXD_MSK) { + ret = s3c24xx_serial_rx_chars(irq, id); + wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK); + } + if (pend & S3C64XX_UINTM_TXD_MSK) { + ret = s3c24xx_serial_tx_chars(irq, id); + wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK); + } + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port) { struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); @@ -377,18 +420,25 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) struct s3c24xx_uart_port *ourport = to_ourport(port); if (ourport->tx_claimed) { - free_irq(ourport->tx_irq, ourport); + if (!s3c24xx_serial_has_interrupt_mask(port)) + free_irq(ourport->tx_irq, ourport); tx_enabled(port) = 0; ourport->tx_claimed = 0; } if (ourport->rx_claimed) { - free_irq(ourport->rx_irq, ourport); + if (!s3c24xx_serial_has_interrupt_mask(port)) + free_irq(ourport->rx_irq, ourport); ourport->rx_claimed = 0; rx_enabled(port) = 0; } -} + /* Clear pending interrupts and mask all interrupts */ + if (s3c24xx_serial_has_interrupt_mask(port)) { + wr_regl(port, S3C64XX_UINTP, 0xf); + wr_regl(port, S3C64XX_UINTM, 0xf); + } +} static int s3c24xx_serial_startup(struct uart_port *port) { @@ -436,6 +486,33 @@ static int s3c24xx_serial_startup(struct uart_port *port) return ret; } +static int s3c64xx_serial_startup(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + int ret; + + dbg("s3c64xx_serial_startup: port=%p (%08lx,%p)\n", + port->mapbase, port->membase); + + ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED, + s3c24xx_serial_portname(port), ourport); + if (ret) { + printk(KERN_ERR "cannot get irq %d\n", port->irq); + return ret; + } + + /* For compatibility with s3c24xx Soc's */ + rx_enabled(port) = 1; + ourport->rx_claimed = 1; + tx_enabled(port) = 0; + ourport->tx_claimed = 1; + + /* Enable Rx Interrupt */ + __clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM)); + dbg("s3c64xx_serial_startup ok\n"); + return ret; +} + /* power power management control */ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, @@ -879,7 +956,6 @@ static struct uart_ops s3c24xx_serial_ops = { .verify_port = s3c24xx_serial_verify_port, }; - static struct uart_driver s3c24xx_uart_drv = { .owner = THIS_MODULE, .driver_name = "s3c2410_serial", @@ -895,7 +971,6 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock), .iotype = UPIO_MEM, - .irq = IRQ_S3CUART_RX0, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, @@ -907,7 +982,6 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock), .iotype = UPIO_MEM, - .irq = IRQ_S3CUART_RX1, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, @@ -921,7 +995,6 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock), .iotype = UPIO_MEM, - .irq = IRQ_S3CUART_RX2, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, @@ -935,7 +1008,6 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock), .iotype = UPIO_MEM, - .irq = IRQ_S3CUART_RX3, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, @@ -1077,6 +1149,10 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, port->dev = &platdev->dev; ourport->info = info; + /* Startup sequence is different for s3c64xx and higher SoC's */ + if (s3c24xx_serial_has_interrupt_mask(port)) + s3c24xx_serial_ops.startup = s3c64xx_serial_startup; + /* copy the info in from provided structure */ ourport->port.fifosize = info->fifosize; @@ -1116,6 +1192,13 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, ourport->clk = clk_get(&platdev->dev, "uart"); + /* Keep all interrupts masked and cleared */ + if (s3c24xx_serial_has_interrupt_mask(port)) { + wr_regl(port, S3C64XX_UINTM, 0xf); + wr_regl(port, S3C64XX_UINTP, 0xf); + wr_regl(port, S3C64XX_UINTSP, 0xf); + } + dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n", port->mapbase, port->membase, port->irq, ourport->rx_irq, ourport->tx_irq, port->uartclk); |