+static void cyy_intr_chip(struct cyclades_card *cinfo, int chip,
+ void __iomem *base_addr, int status, int index)
+{
+ struct cyclades_port *info;
+ struct tty_struct *tty;
+ volatile int char_count;
+ int i, j, len, mdm_change, mdm_status, outch;
+ int save_xir, channel, save_car;
+ char data;
+
+ if (status & CySRReceive) { /* reception interrupt */
+#ifdef CY_DEBUG_INTERRUPTS
+ printk("cyy_interrupt: rcvd intr, chip %d\n\r", chip);
+#endif
+ /* determine the channel & change to that context */
+ spin_lock(&cinfo->card_lock);
+ save_xir = (u_char) cy_readb(base_addr+(CyRIR<<index));
+ channel = (u_short ) (save_xir & CyIRChannel);
+ i = channel + chip * 4 + cinfo->first_line;
+ info = &cy_port[i];
+ info->last_active = jiffies;
+ save_car = cy_readb(base_addr+(CyCAR<<index));
+ cy_writeb(base_addr+(CyCAR<<index), save_xir);
+
+ /* if there is nowhere to put the data, discard it */
+ if(info->tty == 0){
+ j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
+ if ( j == CyIVRRxEx ) { /* exception */
+ data = cy_readb(base_addr+(CyRDSR<<index));
+ } else { /* normal character reception */
+ char_count = cy_readb(base_addr+(CyRDCR<<index));
+ while(char_count--){
+ data = cy_readb(base_addr+(CyRDSR<<index));
+ }
+ }
+ }else{ /* there is an open port for this data */
+ tty = info->tty;
+ j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
+ if ( j == CyIVRRxEx ) { /* exception */
+ data = cy_readb(base_addr+(CyRDSR<<index));
+
+ /* For statistics only */
+ if (data & CyBREAK)
+ info->icount.brk++;
+ else if(data & CyFRAME)
+ info->icount.frame++;
+ else if(data & CyPARITY)
+ info->icount.parity++;
+ else if(data & CyOVERRUN)
+ info->icount.overrun++;
+
+ if(data & info->ignore_status_mask){
+ info->icount.rx++;
+ return;
+ }
+ if (tty_buffer_request_room(tty, 1)) {
+ if (data & info->read_status_mask){
+ if(data & CyBREAK){
+ tty_insert_flip_char(tty, cy_readb(base_addr+(CyRDSR<<index)), TTY_BREAK);
+ info->icount.rx++;
+ if (info->flags & ASYNC_SAK){
+ do_SAK(tty);
+ }
+ }else if(data & CyFRAME){
+ tty_insert_flip_char(tty, cy_readb(base_addr+(CyRDSR<<index)), TTY_FRAME);
+ info->icount.rx++;
+ info->idle_stats.frame_errs++;
+ }else if(data & CyPARITY){
+ /* Pieces of seven... */
+ tty_insert_flip_char(tty, cy_readb(base_addr+(CyRDSR<<index)), TTY_PARITY);
+ info->icount.rx++;
+ info->idle_stats.parity_errs++;
+ }else if(data & CyOVERRUN){
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ info->icount.rx++;
+ /* If the flip buffer itself is
+ overflowing, we still lose
+ the next incoming character.
+ */
+ tty_insert_flip_char(tty, cy_readb(base_addr+(CyRDSR<<index)), TTY_FRAME);
+ info->icount.rx++;
+ info->idle_stats.overruns++;
+ /* These two conditions may imply */
+ /* a normal read should be done. */
+ /* }else if(data & CyTIMEOUT){ */
+ /* }else if(data & CySPECHAR){ */
+ }else {
+ tty_insert_flip_char(tty, 0, TTY_NORMAL);
+ info->icount.rx++;
+ }
+ }else{
+ tty_insert_flip_char(tty, 0, TTY_NORMAL);
+ info->icount.rx++;
+ }
+ }else{
+ /* there was a software buffer
+ overrun and nothing could be
+ done about it!!! */
+ info->icount.buf_overrun++;
+ info->idle_stats.overruns++;
+ }
+ } else { /* normal character reception */
+ /* load # chars available from the chip */
+ char_count = cy_readb(base_addr+(CyRDCR<<index));
+
+#ifdef CY_ENABLE_MONITORING
+ ++info->mon.int_count;
+ info->mon.char_count += char_count;
+ if (char_count > info->mon.char_max)
+ info->mon.char_max = char_count;
+ info->mon.char_last = char_count;
+#endif
+ len = tty_buffer_request_room(tty, char_count);
+ while(len--){
+ data = cy_readb(base_addr+(CyRDSR<<index));
+ tty_insert_flip_char(tty, data, TTY_NORMAL);
+ info->idle_stats.recv_bytes++;
+ info->icount.rx++;
+#ifdef CY_16Y_HACK
+ udelay(10L);
+#endif
+ }
+ info->idle_stats.recv_idle = jiffies;
+ }
+ tty_schedule_flip(tty);
+ }
+ /* end of service */
+ cy_writeb(base_addr+(CyRIR<<index), (save_xir & 0x3f));
+ cy_writeb(base_addr+(CyCAR<<index), (save_car));
+ spin_unlock(&cinfo->card_lock);
+ }
+
+
+ if (status & CySRTransmit) { /* transmission interrupt */
+ /* Since we only get here when the transmit buffer
+ is empty, we know we can always stuff a dozen
+ characters. */
+#ifdef CY_DEBUG_INTERRUPTS
+ printk("cyy_interrupt: xmit intr, chip %d\n\r", chip);
+#endif
+
+ /* determine the channel & change to that context */
+ spin_lock(&cinfo->card_lock);
+ save_xir = (u_char) cy_readb(base_addr+(CyTIR<<index));
+ channel = (u_short ) (save_xir & CyIRChannel);
+ i = channel + chip * 4 + cinfo->first_line;
+ save_car = cy_readb(base_addr+(CyCAR<<index));
+ cy_writeb(base_addr+(CyCAR<<index), save_xir);
+
+ /* validate the port# (as configured and open) */
+ if( (i < 0) || (NR_PORTS <= i) ){
+ cy_writeb(base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxRdy);
+ goto txend;
+ }
+ info = &cy_port[i];
+ info->last_active = jiffies;
+ if(info->tty == 0){
+ cy_writeb(base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxRdy);
+ goto txdone;
+ }
+
+ /* load the on-chip space for outbound data */
+ char_count = info->xmit_fifo_size;
+
+ if(info->x_char) { /* send special char */
+ outch = info->x_char;
+ cy_writeb(base_addr+(CyTDR<<index), outch);
+ char_count--;
+ info->icount.tx++;
+ info->x_char = 0;
+ }
+
+ if (info->breakon || info->breakoff) {
+ if (info->breakon) {
+ cy_writeb(base_addr + (CyTDR<<index), 0);
+ cy_writeb(base_addr + (CyTDR<<index), 0x81);
+ info->breakon = 0;
+ char_count -= 2;
+ }
+ if (info->breakoff) {
+ cy_writeb(base_addr + (CyTDR<<index), 0);
+ cy_writeb(base_addr + (CyTDR<<index), 0x83);
+ info->breakoff = 0;
+ char_count -= 2;
+ }
+ }
+
+ while (char_count-- > 0){
+ if (!info->xmit_cnt){
+ if (cy_readb(base_addr+(CySRER<<index))&CyTxMpty) {
+ cy_writeb(base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) &
+ ~CyTxMpty);
+ } else {
+ cy_writeb(base_addr+(CySRER<<index),
+ ((cy_readb(base_addr+(CySRER<<index))
+ & ~CyTxRdy)
+ | CyTxMpty));
+ }
+ goto txdone;
+ }
+ if (info->xmit_buf == 0){
+ cy_writeb(base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) &
+ ~CyTxRdy);
+ goto txdone;
+ }
+ if (info->tty->stopped || info->tty->hw_stopped){
+ cy_writeb(base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) &
+ ~CyTxRdy);
+ goto txdone;
+ }
+ /* Because the Embedded Transmit Commands have
+ been enabled, we must check to see if the
+ escape character, NULL, is being sent. If it
+ is, we must ensure that there is room for it
+ to be doubled in the output stream. Therefore
+ we no longer advance the pointer when the
+ character is fetched, but rather wait until
+ after the check for a NULL output character.
+ This is necessary because there may not be
+ room for the two chars needed to send a NULL.)
+ */
+ outch = info->xmit_buf[info->xmit_tail];
+ if( outch ){
+ info->xmit_cnt--;
+ info->xmit_tail = (info->xmit_tail + 1)
+ & (SERIAL_XMIT_SIZE - 1);
+ cy_writeb(base_addr+(CyTDR<<index), outch);
+ info->icount.tx++;
+ }else{
+ if(char_count > 1){
+ info->xmit_cnt--;
+ info->xmit_tail = (info->xmit_tail + 1)
+ & (SERIAL_XMIT_SIZE - 1);
+ cy_writeb(base_addr+(CyTDR<<index),
+ outch);
+ cy_writeb(base_addr+(CyTDR<<index), 0);
+ info->icount.tx++;
+ char_count--;
+ }else{
+ }
+ }
+ }
+
+txdone:
+ if (info->xmit_cnt < WAKEUP_CHARS) {
+ cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+ }
+txend:
+ /* end of service */
+ cy_writeb(base_addr+(CyTIR<<index),
+ (save_xir & 0x3f));
+ cy_writeb(base_addr+(CyCAR<<index), (save_car));
+ spin_unlock(&cinfo->card_lock);
+ }
+
+ if (status & CySRModem) { /* modem interrupt */
+
+ /* determine the channel & change to that context */
+ spin_lock(&cinfo->card_lock);
+ save_xir = (u_char) cy_readb(base_addr+(CyMIR<<index));
+ channel = (u_short ) (save_xir & CyIRChannel);
+ info = &cy_port[channel + chip * 4
+ + cinfo->first_line];
+ info->last_active = jiffies;
+ save_car = cy_readb(base_addr+(CyCAR<<index));
+ cy_writeb(base_addr+(CyCAR<<index), save_xir);
+
+ mdm_change = cy_readb(base_addr+(CyMISR<<index));
+ mdm_status = cy_readb(base_addr+(CyMSVR1<<index));
+
+ if(info->tty == 0){/* no place for data, ignore it*/
+ ;
+ }else{
+ if (mdm_change & CyANY_DELTA) {
+ /* For statistics only */
+ if (mdm_change & CyDCD) info->icount.dcd++;
+ if (mdm_change & CyCTS) info->icount.cts++;
+ if (mdm_change & CyDSR) info->icount.dsr++;
+ if (mdm_change & CyRI) info->icount.rng++;
+
+ cy_sched_event(info, Cy_EVENT_DELTA_WAKEUP);
+ }
+
+ if((mdm_change & CyDCD)
+ && (info->flags & ASYNC_CHECK_CD)){
+ if(mdm_status & CyDCD){
+ cy_sched_event(info,
+ Cy_EVENT_OPEN_WAKEUP);
+ }else{
+ cy_sched_event(info,
+ Cy_EVENT_HANGUP);
+ }
+ }
+ if((mdm_change & CyCTS)
+ && (info->flags & ASYNC_CTS_FLOW)){
+ if(info->tty->hw_stopped){
+ if(mdm_status & CyCTS){
+ /* cy_start isn't used
+ because... !!! */
+ info->tty->hw_stopped = 0;
+ cy_writeb(base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) |
+ CyTxRdy);
+ cy_sched_event(info,
+ Cy_EVENT_WRITE_WAKEUP);
+ }
+ }else{
+ if(!(mdm_status & CyCTS)){
+ /* cy_stop isn't used
+ because ... !!! */
+ info->tty->hw_stopped = 1;
+ cy_writeb(base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) &
+ ~CyTxRdy);
+ }
+ }
+ }
+ if(mdm_change & CyDSR){
+ }
+ if(mdm_change & CyRI){
+ }
+ }
+ /* end of service */
+ cy_writeb(base_addr+(CyMIR<<index),
+ (save_xir & 0x3f));
+ cy_writeb(base_addr+(CyCAR<<index), save_car);
+ spin_unlock(&cinfo->card_lock);
+ }
+}
+