Commit 87cfb955 authored by Jens Taprogge's avatar Jens Taprogge Committed by Greg Kroah-Hartman

Staging: ipack/devices/ipoctal: Split interrupt service routine.

Split the IRQ service routing in TX part and RX part.
Signed-off-by: default avatarJens Taprogge <jens.taprogge@taprogge.org>
Signed-off-by: default avatarSamuel Iglesias Gonsalvez <siglesias@igalia.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4e4732ac
......@@ -163,109 +163,115 @@ static int ipoctal_get_icount(struct tty_struct *tty,
return 0;
}
static int ipoctal_irq_handler(void *arg)
static void ipoctal_irq_rx(struct ipoctal_channel *channel,
struct tty_struct *tty, u8 sr)
{
unsigned char value = ioread8(&channel->regs->r.rhr);
unsigned char flag = TTY_NORMAL;
/* Error: count statistics */
if (sr & SR_ERROR) {
iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);
if (sr & SR_OVERRUN_ERROR) {
channel->stats.overrun_err++;
/* Overrun doesn't affect the current character*/
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
}
if (sr & SR_PARITY_ERROR) {
channel->stats.parity_err++;
flag = TTY_PARITY;
}
if (sr & SR_FRAMING_ERROR) {
channel->stats.framing_err++;
flag = TTY_FRAME;
}
if (sr & SR_RECEIVED_BREAK) {
channel->stats.rcv_break++;
flag = TTY_BREAK;
}
}
tty_insert_flip_char(tty, value, flag);
}
static void ipoctal_irq_tx(struct ipoctal_channel *channel)
{
unsigned int ichannel;
unsigned char isr;
unsigned char sr;
unsigned char value;
unsigned char flag;
struct tty_struct *tty;
struct ipoctal *ipoctal = (struct ipoctal *) arg;
struct ipoctal_channel *channel;
unsigned int *pointer_write = &channel->pointer_write;
/* Check all channels */
for (ichannel = 0; ichannel < NR_CHANNELS; ichannel++) {
channel = &ipoctal->channel[ichannel];
/* If there is no client, skip the check */
if (!atomic_read(&channel->open))
continue;
if (channel->nb_bytes <= 0) {
channel->nb_bytes = 0;
return;
}
tty = tty_port_tty_get(&channel->tty_port);
if (!tty)
continue;
value = channel->tty_port.xmit_buf[*pointer_write];
iowrite8(value, &channel->regs->w.thr);
channel->stats.tx++;
channel->count_wr++;
(*pointer_write)++;
*pointer_write = *pointer_write % PAGE_SIZE;
channel->nb_bytes--;
/*
* 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);
if ((channel->nb_bytes == 0) &&
(waitqueue_active(&channel->queue))) {
/* In case of RS-485, change from TX to RX when finishing TX.
* Half-duplex.
*/
if ((ipoctal->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) &&
(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);
ipoctal->write = 1;
if (channel->board_id != IPACK1_DEVICE_ID_SBS_OCTAL_485) {
*channel->board_write = 1;
wake_up_interruptible(&channel->queue);
}
}
}
/* RX data */
if ((isr && channel->isr_rx_rdy_mask) && (sr & SR_RX_READY)) {
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);
if (sr & SR_OVERRUN_ERROR) {
channel->stats.overrun_err++;
/* Overrun doesn't affect the current character*/
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
}
if (sr & SR_PARITY_ERROR) {
channel->stats.parity_err++;
flag = TTY_PARITY;
}
if (sr & SR_FRAMING_ERROR) {
channel->stats.framing_err++;
flag = TTY_FRAME;
}
if (sr & SR_RECEIVED_BREAK) {
channel->stats.rcv_break++;
flag = TTY_BREAK;
}
}
tty_insert_flip_char(tty, value, flag);
}
static void ipoctal_irq_channel(struct ipoctal_channel *channel)
{
u8 isr, sr;
struct tty_struct *tty;
/* TX of each character */
if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY)) {
unsigned int *pointer_write = &channel->pointer_write;
if (channel->nb_bytes <= 0) {
channel->nb_bytes = 0;
continue;
}
value = channel->tty_port.xmit_buf[*pointer_write];
iowrite8(value, &channel->regs->w.thr);
channel->stats.tx++;
channel->count_wr++;
(*pointer_write)++;
*pointer_write = *pointer_write % PAGE_SIZE;
channel->nb_bytes--;
if ((channel->nb_bytes == 0) &&
(waitqueue_active(&channel->queue))) {
if (ipoctal->board_id != IPACK1_DEVICE_ID_SBS_OCTAL_485) {
ipoctal->write = 1;
wake_up_interruptible(&channel->queue);
}
}
}
/* If there is no client, skip the check */
if (!atomic_read(&channel->open))
return;
tty_flip_buffer_push(tty);
tty_kref_put(tty);
tty = tty_port_tty_get(&channel->tty_port);
if (!tty)
return;
/* 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)) {
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);
}
/* RX data */
if ((isr & channel->isr_rx_rdy_mask) && (sr & SR_RX_READY))
ipoctal_irq_rx(channel, tty, 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);
}
static int 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]);
return IRQ_HANDLED;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment