diff options
Diffstat (limited to 'drivers/ipack/devices/ipoctal.c')
-rw-r--r-- | drivers/ipack/devices/ipoctal.c | 130 |
1 files changed, 55 insertions, 75 deletions
diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c index 576d53d92677..141094e7c06e 100644 --- a/drivers/ipack/devices/ipoctal.c +++ b/drivers/ipack/devices/ipoctal.c @@ -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, @@ -133,15 +123,16 @@ static int ipoctal_get_icount(struct tty_struct *tty, return 0; } -static void ipoctal_irq_rx(struct ipoctal_channel *channel, - struct tty_struct *tty, u8 sr) +static void ipoctal_irq_rx(struct ipoctal_channel *channel, u8 sr) { + struct tty_port *port = &channel->tty_port; 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); @@ -149,7 +140,7 @@ static void ipoctal_irq_rx(struct ipoctal_channel *channel, if (sr & SR_OVERRUN_ERROR) { channel->stats.overrun_err++; /* Overrun doesn't affect the current character*/ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(port, 0, TTY_OVERRUN); } if (sr & SR_PARITY_ERROR) { channel->stats.parity_err++; @@ -165,7 +156,7 @@ static void ipoctal_irq_rx(struct ipoctal_channel *channel, flag = TTY_BREAK; } } - tty_insert_flip_char(tty, value, flag); + tty_insert_flip_char(port, value, flag); /* Check if there are more characters in RX FIFO * If there are more, the isr register for this channel @@ -175,7 +166,7 @@ static void ipoctal_irq_rx(struct ipoctal_channel *channel, sr = ioread8(&channel->regs->r.sr); } while (isr & channel->isr_rx_rdy_mask); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(port); } static void ipoctal_irq_tx(struct ipoctal_channel *channel) @@ -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,55 +183,38 @@ 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) { 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); + channel->rx_enable = 1; + } } /* RX data */ if ((isr & channel->isr_rx_rdy_mask) && (sr & SR_RX_READY)) - ipoctal_irq_rx(channel, tty, sr); + ipoctal_irq_rx(channel, sr); /* TX of each character */ 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 +222,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; } @@ -311,7 +283,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, ipoctal->mem8_space = devm_ioremap_nocache(&ipoctal->dev->dev, region->start, 0x8000); - if (!addr) { + if (!ipoctal->mem8_space) { dev_err(&ipoctal->dev->dev, "Unable to map slot [%d:%d] MEM8 space!\n", bus_nr, slot); @@ -324,7 +296,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 +306,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 +379,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 +389,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 +428,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 +437,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 +588,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 +610,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); @@ -657,6 +620,22 @@ static void ipoctal_hangup(struct tty_struct *tty) wake_up_interruptible(&channel->tty_port.open_wait); } +static void ipoctal_shutdown(struct tty_struct *tty) +{ + struct ipoctal_channel *channel = tty->driver_data; + + if (channel == NULL) + return; + + 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); + iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr); + clear_bit(ASYNCB_INITIALIZED, &channel->tty_port.flags); +} + static const struct tty_operations ipoctal_fops = { .ioctl = NULL, .open = ipoctal_open, @@ -667,6 +646,7 @@ static const struct tty_operations ipoctal_fops = { .chars_in_buffer = ipoctal_chars_in_buffer, .get_icount = ipoctal_get_icount, .hangup = ipoctal_hangup, + .shutdown = ipoctal_shutdown, }; static int ipoctal_probe(struct ipack_device *dev) |