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;
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);
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;
}
}
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);
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)
{
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,
.verify_port = s3c24xx_serial_verify_port,
};
-
static struct uart_driver s3c24xx_uart_drv = {
.owner = THIS_MODULE,
.driver_name = "s3c2410_serial",
.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,
.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,
.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,
.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,
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;
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);