]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/ipack/devices/ipoctal.c
ipack/devices/ipoctal: add rx_enable flag
[karo-tx-linux.git] / drivers / ipack / devices / ipoctal.c
index 576d53d926776cd270ce4572e6a3e0a1d48857f0..8666d2d05eef9ab30bfe8f62d5f28ac35e7cfe9e 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/serial.h>
 #include <linux/tty_flip.h>
 #include <linux/slab.h>
-#include <linux/atomic.h>
 #include <linux/io.h>
 #include <linux/ipack.h>
 #include "ipoctal.h"
@@ -38,21 +37,19 @@ struct ipoctal_channel {
        spinlock_t                      lock;
        unsigned int                    pointer_read;
        unsigned int                    pointer_write;
-       atomic_t                        open;
        struct tty_port                 tty_port;
        union scc2698_channel __iomem   *regs;
        union scc2698_block __iomem     *block_regs;
        unsigned int                    board_id;
-       unsigned char                   *board_write;
        u8                              isr_rx_rdy_mask;
        u8                              isr_tx_rdy_mask;
+       unsigned int                    rx_enable;
 };
 
 struct ipoctal {
        struct ipack_device             *dev;
        unsigned int                    board_id;
        struct ipoctal_channel          channel[NR_CHANNELS];
-       unsigned char                   write;
        struct tty_driver               *tty_drv;
        u8 __iomem                      *mem8_space;
        u8 __iomem                      *int_space;
@@ -64,28 +61,23 @@ static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty)
 
        channel = dev_get_drvdata(tty->dev);
 
+       /*
+        * Enable RX. TX will be enabled when
+        * there is something to send
+        */
        iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
+       channel->rx_enable = 1;
        return 0;
 }
 
 static int ipoctal_open(struct tty_struct *tty, struct file *file)
 {
-       int res;
        struct ipoctal_channel *channel;
 
        channel = dev_get_drvdata(tty->dev);
-
-       if (atomic_read(&channel->open))
-               return -EBUSY;
-
        tty->driver_data = channel;
 
-       res = tty_port_open(&channel->tty_port, tty, file);
-       if (res)
-               return res;
-
-       atomic_inc(&channel->open);
-       return 0;
+       return tty_port_open(&channel->tty_port, tty, file);
 }
 
 static void ipoctal_reset_stats(struct ipoctal_stats *stats)
@@ -111,9 +103,7 @@ static void ipoctal_close(struct tty_struct *tty, struct file *filp)
        struct ipoctal_channel *channel = tty->driver_data;
 
        tty_port_close(&channel->tty_port, tty, filp);
-
-       if (atomic_dec_and_test(&channel->open))
-               ipoctal_free_channel(channel);
+       ipoctal_free_channel(channel);
 }
 
 static int ipoctal_get_icount(struct tty_struct *tty,
@@ -137,11 +127,12 @@ static void ipoctal_irq_rx(struct ipoctal_channel *channel,
                           struct tty_struct *tty, u8 sr)
 {
        unsigned char value;
-       unsigned char flag = TTY_NORMAL;
+       unsigned char flag;
        u8 isr;
 
        do {
                value = ioread8(&channel->regs->r.rhr);
+               flag = TTY_NORMAL;
                /* Error: count statistics */
                if (sr & SR_ERROR) {
                        iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);
@@ -183,10 +174,8 @@ static void ipoctal_irq_tx(struct ipoctal_channel *channel)
        unsigned char value;
        unsigned int *pointer_write = &channel->pointer_write;
 
-       if (channel->nb_bytes <= 0) {
-               channel->nb_bytes = 0;
+       if (channel->nb_bytes == 0)
                return;
-       }
 
        value = channel->tty_port.xmit_buf[*pointer_write];
        iowrite8(value, &channel->regs->w.thr);
@@ -194,15 +183,6 @@ static void ipoctal_irq_tx(struct ipoctal_channel *channel)
        (*pointer_write)++;
        *pointer_write = *pointer_write % PAGE_SIZE;
        channel->nb_bytes--;
-
-       if ((channel->nb_bytes == 0) &&
-           (waitqueue_active(&channel->queue))) {
-
-               if (channel->board_id != IPACK1_DEVICE_ID_SBS_OCTAL_485) {
-                       *channel->board_write = 1;
-                       wake_up_interruptible(&channel->queue);
-               }
-       }
 }
 
 static void ipoctal_irq_channel(struct ipoctal_channel *channel)
@@ -210,27 +190,24 @@ static void ipoctal_irq_channel(struct ipoctal_channel *channel)
        u8 isr, sr;
        struct tty_struct *tty;
 
-       /* If there is no client, skip the check */
-       if (!atomic_read(&channel->open))
-               return;
-
        tty = tty_port_tty_get(&channel->tty_port);
        if (!tty)
                return;
+
+       spin_lock(&channel->lock);
        /* The HW is organized in pair of channels.  See which register we need
         * to read from */
        isr = ioread8(&channel->block_regs->r.isr);
        sr = ioread8(&channel->regs->r.sr);
 
-       /* In case of RS-485, change from TX to RX when finishing TX.
-        * Half-duplex. */
-       if ((channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) &&
-           (sr & SR_TX_EMPTY) && (channel->nb_bytes == 0)) {
+       if ((sr & SR_TX_EMPTY) && (channel->nb_bytes == 0)) {
                iowrite8(CR_DISABLE_TX, &channel->regs->w.cr);
-               iowrite8(CR_CMD_NEGATE_RTSN, &channel->regs->w.cr);
-               iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
-               *channel->board_write = 1;
-               wake_up_interruptible(&channel->queue);
+               /* In case of RS-485, change from TX to RX when finishing TX.
+                * Half-duplex. */
+               if (channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) {
+                       iowrite8(CR_CMD_NEGATE_RTSN, &channel->regs->w.cr);
+                       iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
+               }
        }
 
        /* RX data */
@@ -241,8 +218,8 @@ static void ipoctal_irq_channel(struct ipoctal_channel *channel)
        if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY))
                ipoctal_irq_tx(channel);
 
-       tty_flip_buffer_push(tty);
        tty_kref_put(tty);
+       spin_unlock(&channel->lock);
 }
 
 static irqreturn_t ipoctal_irq_handler(void *arg)
@@ -250,14 +227,14 @@ static irqreturn_t ipoctal_irq_handler(void *arg)
        unsigned int i;
        struct ipoctal *ipoctal = (struct ipoctal *) arg;
 
-       /* Check all channels */
-       for (i = 0; i < NR_CHANNELS; i++)
-               ipoctal_irq_channel(&ipoctal->channel[i]);
-
        /* Clear the IPack device interrupt */
        readw(ipoctal->int_space + ACK_INT_REQ0);
        readw(ipoctal->int_space + ACK_INT_REQ1);
 
+       /* Check all channels */
+       for (i = 0; i < NR_CHANNELS; i++)
+               ipoctal_irq_channel(&ipoctal->channel[i]);
+
        return IRQ_HANDLED;
 }
 
@@ -324,7 +301,6 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
                struct ipoctal_channel *channel = &ipoctal->channel[i];
                channel->regs = chan_regs + i;
                channel->block_regs = block_regs + (i >> 1);
-               channel->board_write = &ipoctal->write;
                channel->board_id = ipoctal->board_id;
                if (i & 1) {
                        channel->isr_tx_rdy_mask = ISR_TxRDY_B;
@@ -335,6 +311,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
                }
 
                iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
+               channel->rx_enable = 0;
                iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr);
                iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr);
                iowrite8(MR1_CHRL_8_BITS | MR1_ERROR_CHAR | MR1_RxINT_RxRDY,
@@ -407,8 +384,6 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
 
                ipoctal_reset_stats(&channel->stats);
                channel->nb_bytes = 0;
-               init_waitqueue_head(&channel->queue);
-
                spin_lock_init(&channel->lock);
                channel->pointer_read = 0;
                channel->pointer_write = 0;
@@ -419,12 +394,6 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
                        continue;
                }
                dev_set_drvdata(tty_dev, channel);
-
-               /*
-                * Enable again the RX. TX will be enabled when
-                * there is something to send
-                */
-               iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
        }
 
        return 0;
@@ -464,6 +433,7 @@ static int ipoctal_write_tty(struct tty_struct *tty,
        /* As the IP-OCTAL 485 only supports half duplex, do it manually */
        if (channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) {
                iowrite8(CR_DISABLE_RX, &channel->regs->w.cr);
+               channel->rx_enable = 0;
                iowrite8(CR_CMD_ASSERT_RTSN, &channel->regs->w.cr);
        }
 
@@ -472,10 +442,6 @@ static int ipoctal_write_tty(struct tty_struct *tty,
         * operations
         */
        iowrite8(CR_ENABLE_TX, &channel->regs->w.cr);
-       wait_event_interruptible(channel->queue, *channel->board_write);
-       iowrite8(CR_DISABLE_TX, &channel->regs->w.cr);
-
-       *channel->board_write = 0;
        return char_copied;
 }
 
@@ -627,8 +593,9 @@ static void ipoctal_set_termios(struct tty_struct *tty,
        iowrite8(mr2, &channel->regs->w.mr);
        iowrite8(csr, &channel->regs->w.csr);
 
-       /* Enable again the RX */
-       iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
+       /* Enable again the RX, if it was before */
+       if (channel->rx_enable)
+               iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
 }
 
 static void ipoctal_hangup(struct tty_struct *tty)
@@ -648,6 +615,7 @@ static void ipoctal_hangup(struct tty_struct *tty)
        tty_port_hangup(&channel->tty_port);
 
        iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
+       channel->rx_enable = 0;
        iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr);
        iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr);
        iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);