X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=drivers%2Fchar%2Fisicom.c;h=43ab9edc76f540ce254f07f827e00414bd70abc2;hb=574009c1a895aeeb85eaab29c235d75852b09eb8;hp=51fa317e695ca790ccd45a2e26e0740bfe16fecd;hpb=18234f88bc27473f15293a03c83bcb53da9018b4;p=powerpc.git diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 51fa317e69..43ab9edc76 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -172,16 +172,18 @@ static struct pci_driver isicom_driver = { static int prev_card = 3; /* start servicing isi_card[0] */ static struct tty_driver *isicom_normal; -static struct timer_list tx; +static DECLARE_COMPLETION(isi_timerdone); static char re_schedule = 1; static void isicom_tx(unsigned long _data); static void isicom_start(struct tty_struct *tty); +static DEFINE_TIMER(tx, isicom_tx, 0, 0); + /* baud index mappings from linux defns to isi */ static signed char linuxb_to_isib[] = { - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19 + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21 }; struct isi_board { @@ -195,6 +197,7 @@ struct isi_board { signed char count; spinlock_t card_lock; /* Card wide lock 11/5/00 -sameer */ unsigned long flags; + unsigned int index; }; struct isi_port { @@ -210,8 +213,6 @@ struct isi_port { struct tty_struct * tty; wait_queue_head_t close_wait; wait_queue_head_t open_wait; - struct work_struct hangup_tq; - struct work_struct bh_tqueue; unsigned char * xmit_buf; int xmit_head; int xmit_tail; @@ -227,6 +228,20 @@ static struct isi_port isi_ports[PORT_COUNT]; * it wants to talk. */ +static inline int WaitTillCardIsFree(u16 base) +{ + unsigned int count = 0; + unsigned int a = in_atomic(); /* do we run under spinlock? */ + + while (!(inw(base + 0xe) & 0x1) && count++ < 100) + if (a) + mdelay(1); + else + msleep(1); + + return !(inw(base + 0xe) & 0x1); +} + static int lock_card(struct isi_board *card) { char retries; @@ -273,69 +288,71 @@ static void unlock_card(struct isi_board *card) * ISI Card specific ops ... */ +/* card->lock HAS to be held */ static void raise_dtr(struct isi_port *port) { struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; - if (!lock_card(card)) + if (WaitTillCardIsFree(base)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0504, base); InterruptTheCard(base); port->status |= ISI_DTR; - unlock_card(card); } +/* card->lock HAS to be held */ static inline void drop_dtr(struct isi_port *port) { struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; - if (!lock_card(card)) + if (WaitTillCardIsFree(base)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0404, base); InterruptTheCard(base); port->status &= ~ISI_DTR; - unlock_card(card); } +/* card->lock HAS to be held */ static inline void raise_rts(struct isi_port *port) { struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; - if (!lock_card(card)) + if (WaitTillCardIsFree(base)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0a04, base); InterruptTheCard(base); port->status |= ISI_RTS; - unlock_card(card); } + +/* card->lock HAS to be held */ static inline void drop_rts(struct isi_port *port) { struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; - if (!lock_card(card)) + if (WaitTillCardIsFree(base)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0804, base); InterruptTheCard(base); port->status &= ~ISI_RTS; - unlock_card(card); } +/* card->lock MUST NOT be held */ static inline void raise_dtr_rts(struct isi_port *port) { struct isi_board *card = port->card; @@ -352,35 +369,20 @@ static inline void raise_dtr_rts(struct isi_port *port) unlock_card(card); } +/* card->lock HAS to be held */ static void drop_dtr_rts(struct isi_port *port) { struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; - if (!lock_card(card)) + if (WaitTillCardIsFree(base)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0c04, base); InterruptTheCard(base); port->status &= ~(ISI_RTS | ISI_DTR); - unlock_card(card); -} - -static inline void kill_queue(struct isi_port *port, short queue) -{ - struct isi_board *card = port->card; - unsigned long base = card->base; - u16 channel = port->channel; - - if (!lock_card(card)) - return; - - outw(0x8000 | (channel << card->shift_count) | 0x02, base); - outw((queue << 8) | 0x06, base); - InterruptTheCard(base); - unlock_card(card); } /* @@ -506,39 +508,18 @@ static void isicom_tx(unsigned long _data) if (port->xmit_cnt <= 0) port->status &= ~ISI_TXOK; if (port->xmit_cnt <= WAKEUP_CHARS) - schedule_work(&port->bh_tqueue); + tty_wakeup(tty); unlock_card(&isi_card[card]); } /* schedule another tx for hopefully in about 10ms */ sched_again: if (!re_schedule) { - re_schedule = 2; + complete(&isi_timerdone); return; } - init_timer(&tx); - tx.expires = jiffies + HZ/100; - tx.data = 0; - tx.function = isicom_tx; - add_timer(&tx); - - return; -} - -/* Interrupt handlers */ - - -static void isicom_bottomhalf(struct work_struct *work) -{ - struct isi_port *port = container_of(work, struct isi_port, bh_tqueue); - struct tty_struct *tty = port->tty; - - if (!tty) - return; - - tty_wakeup(tty); - wake_up_interruptible(&tty->write_wait); + mod_timer(&tx, jiffies + msecs_to_jiffies(10)); } /* @@ -559,6 +540,11 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) return IRQ_NONE; base = card->base; + + /* did the card interrupt us? */ + if (!(inw(base + 0x0e) & 0x02)) + return IRQ_NONE; + spin_lock(&card->card_lock); /* @@ -583,6 +569,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) port = card->ports + channel; if (!(port->flags & ASYNC_INITIALIZED)) { outw(0x0000, base+0x04); /* enable interrupts */ + spin_unlock(&card->card_lock); return IRQ_HANDLED; } @@ -611,7 +598,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) pr_dbg("interrupt: DCD->low.\n" ); port->status &= ~ISI_DCD; - schedule_work(&port->hangup_tq); + tty_hangup(tty); } } else if (header & ISI_DCD) { /* Carrier has been detected */ @@ -633,7 +620,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) /* start tx ing */ port->status |= (ISI_TXOK | ISI_CTS); - schedule_work(&port->bh_tqueue); + tty_wakeup(tty); } } else if (!(header & ISI_CTS)) { port->tty->hw_stopped = 1; @@ -697,6 +684,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) tty_flip_buffer_push(tty); } outw(0x0000, base+0x04); /* enable interrupts */ + spin_unlock(&card->card_lock); return IRQ_HANDLED; } @@ -722,7 +710,8 @@ static void isicom_config_port(struct isi_port *port) * respectively. */ - if (baud < 1 || baud > 2) + /* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */ + if (baud < 1 || baud > 4) port->tty->termios->c_cflag &= ~CBAUDEX; else baud += 15; @@ -738,6 +727,10 @@ static void isicom_config_port(struct isi_port *port) baud++; /* 57.6 Kbps */ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baud +=2; /* 115 Kbps */ + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + baud += 3; /* 230 kbps*/ + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + baud += 4; /* 460 kbps*/ } if (linuxb_to_isib[baud] == -1) { /* hang up */ @@ -747,7 +740,7 @@ static void isicom_config_port(struct isi_port *port) else raise_dtr(port); - if (lock_card(card)) { + if (WaitTillCardIsFree(base) == 0) { outw(0x8000 | (channel << shift_count) |0x03, base); outw(linuxb_to_isib[baud] << 8 | 0x03, base); channel_setup = 0; @@ -775,7 +768,6 @@ static void isicom_config_port(struct isi_port *port) } outw(channel_setup, base); InterruptTheCard(base); - unlock_card(card); } if (C_CLOCAL(tty)) port->flags &= ~ASYNC_CHECK_CD; @@ -794,12 +786,11 @@ static void isicom_config_port(struct isi_port *port) if (I_IXOFF(tty)) flow_ctrl |= ISICOM_INITIATE_XONXOFF; - if (lock_card(card)) { + if (WaitTillCardIsFree(base) == 0) { outw(0x8000 | (channel << shift_count) |0x04, base); outw(flow_ctrl << 8 | 0x05, base); outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base); InterruptTheCard(base); - unlock_card(card); } /* rx enabled -> enable port for rx on the card */ @@ -824,10 +815,9 @@ static inline void isicom_setup_board(struct isi_board *bp) } port = bp->ports; bp->status |= BOARD_ACTIVE; - spin_unlock_irqrestore(&bp->card_lock, flags); for (channel = 0; channel < bp->port_count; channel++, port++) drop_dtr_rts(port); - return; + spin_unlock_irqrestore(&bp->card_lock, flags); } static int isicom_setup_port(struct isi_port *port) @@ -860,7 +850,12 @@ static int isicom_setup_port(struct isi_port *port) port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; /* discard any residual data */ - kill_queue(port, ISICOM_KILLTX | ISICOM_KILLRX); + if (WaitTillCardIsFree(card->base) == 0) { + outw(0x8000 | (port->channel << card->shift_count) | 0x02, + card->base); + outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base); + InterruptTheCard(card->base); + } isicom_config_port(port); port->flags |= ASYNC_INITIALIZED; @@ -949,8 +944,8 @@ static int isicom_open(struct tty_struct *tty, struct file *filp) { struct isi_port *port; struct isi_board *card; - unsigned int line, board; - int error; + unsigned int board; + int error, line; line = tty->index; if (line < 0 || line > PORT_COUNT-1) @@ -986,28 +981,22 @@ static int isicom_open(struct tty_struct *tty, struct file *filp) static inline void isicom_shutdown_board(struct isi_board *bp) { - unsigned long flags; - - spin_lock_irqsave(&bp->card_lock, flags); if (bp->status & BOARD_ACTIVE) { bp->status &= ~BOARD_ACTIVE; } - spin_unlock_irqrestore(&bp->card_lock, flags); } +/* card->lock HAS to be held */ static void isicom_shutdown_port(struct isi_port *port) { struct isi_board *card = port->card; struct tty_struct *tty; - unsigned long flags; tty = port->tty; - spin_lock_irqsave(&card->card_lock, flags); - if (!(port->flags & ASYNC_INITIALIZED)) { - spin_unlock_irqrestore(&card->card_lock, flags); + if (!(port->flags & ASYNC_INITIALIZED)) return; - } + if (port->xmit_buf) { free_page((unsigned long) port->xmit_buf); port->xmit_buf = NULL; @@ -1015,7 +1004,6 @@ static void isicom_shutdown_port(struct isi_port *port) port->flags &= ~ASYNC_INITIALIZED; /* 3rd October 2000 : Vinayak P Risbud */ port->tty = NULL; - spin_unlock_irqrestore(&card->card_lock, flags); /*Fix done by Anil .S on 30-04-2001 remote login through isi port has dtr toggle problem @@ -1261,10 +1249,12 @@ static int isicom_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { struct isi_port *port = tty->driver_data; + unsigned long flags; if (isicom_paranoia_check(port, tty->name, "isicom_ioctl")) return -ENODEV; + spin_lock_irqsave(&port->card->card_lock, flags); if (set & TIOCM_RTS) raise_rts(port); if (set & TIOCM_DTR) @@ -1274,6 +1264,7 @@ static int isicom_tiocmset(struct tty_struct *tty, struct file *file, drop_rts(port); if (clear & TIOCM_DTR) drop_dtr(port); + spin_unlock_irqrestore(&port->card->card_lock, flags); return 0; } @@ -1306,7 +1297,10 @@ static int isicom_set_serial_info(struct isi_port *port, (newinfo.flags & ASYNC_FLAGS)); } if (reconfig_port) { + unsigned long flags; + spin_lock_irqsave(&port->card->card_lock, flags); isicom_config_port(port); + spin_unlock_irqrestore(&port->card->card_lock, flags); } return 0; } @@ -1387,6 +1381,7 @@ static void isicom_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct isi_port *port = tty->driver_data; + unsigned long flags; if (isicom_paranoia_check(port, tty->name, "isicom_set_termios")) return; @@ -1395,7 +1390,9 @@ static void isicom_set_termios(struct tty_struct *tty, tty->termios->c_iflag == old_termios->c_iflag) return; + spin_lock_irqsave(&port->card->card_lock, flags); isicom_config_port(port); + spin_unlock_irqrestore(&port->card->card_lock, flags); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { @@ -1458,25 +1455,18 @@ static void isicom_start(struct tty_struct *tty) port->status |= ISI_TXOK; } -/* hangup et all */ -static void do_isicom_hangup(struct work_struct *work) -{ - struct isi_port *port = container_of(work, struct isi_port, hangup_tq); - struct tty_struct *tty; - - tty = port->tty; - if (tty) - tty_hangup(tty); -} - static void isicom_hangup(struct tty_struct *tty) { struct isi_port *port = tty->driver_data; + unsigned long flags; if (isicom_paranoia_check(port, tty->name, "isicom_hangup")) return; + spin_lock_irqsave(&port->card->card_lock, flags); isicom_shutdown_port(port); + spin_unlock_irqrestore(&port->card->card_lock, flags); + port->count = 0; port->flags &= ~ASYNC_NORMAL_ACTIVE; port->tty = NULL; @@ -1497,7 +1487,6 @@ static void isicom_flush_buffer(struct tty_struct *tty) port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; spin_unlock_irqrestore(&card->card_lock, flags); - wake_up_interruptible(&tty->write_wait); tty_wakeup(tty); } @@ -1530,7 +1519,7 @@ static int __devinit reset_card(struct pci_dev *pdev, { struct isi_board *board = pci_get_drvdata(pdev); unsigned long base = board->base; - unsigned int portcount = 0; + unsigned int sig, portcount = 0; int retval = 0; dev_dbg(&pdev->dev, "ISILoad:Resetting Card%d at 0x%lx\n", card + 1, @@ -1538,27 +1527,35 @@ static int __devinit reset_card(struct pci_dev *pdev, inw(base + 0x8); - mdelay(10); + msleep(10); outw(0, base + 0x8); /* Reset */ - msleep(3000); + msleep(1000); - *signature = inw(base + 0x4) & 0xff; + sig = inw(base + 0x4) & 0xff; + + if (sig != 0xa5 && sig != 0xbb && sig != 0xcc && sig != 0xdd && + sig != 0xee) { + dev_warn(&pdev->dev, "ISILoad:Card%u reset failure (Possible " + "bad I/O Port Address 0x%lx).\n", card + 1, base); + dev_dbg(&pdev->dev, "Sig=0x%x\n", sig); + retval = -EIO; + goto end; + } + + msleep(10); portcount = inw(base + 0x2); - if (!(inw(base + 0xe) & 0x1) || ((portcount != 0) && - (portcount != 4) && (portcount != 8))) { - dev_dbg(&pdev->dev, "base+0x2=0x%lx, base+0xe=0x%lx\n", - inw(base + 0x2), inw(base + 0xe)); - dev_err(&pdev->dev, "ISILoad:PCI Card%d reset failure " - "(Possible bad I/O Port Address 0x%lx).\n", - card + 1, base); + if (!inw(base + 0xe) & 0x1 || (portcount != 0 && portcount != 4 && + portcount != 8 && portcount != 16)) { + dev_err(&pdev->dev, "ISILoad:PCI Card%d reset failure.", + card + 1); retval = -EIO; goto end; } - switch (*signature) { + switch (sig) { case 0xa5: case 0xbb: case 0xdd: @@ -1566,31 +1563,18 @@ static int __devinit reset_card(struct pci_dev *pdev, board->shift_count = 12; break; case 0xcc: + case 0xee: board->port_count = 16; board->shift_count = 11; break; - default: - dev_warn(&pdev->dev, "ISILoad:Card%d reset failure (Possible " - "bad I/O Port Address 0x%lx).\n", card + 1, base); - dev_dbg(&pdev->dev, "Sig=0x%lx\n", signature); - retval = -EIO; } dev_info(&pdev->dev, "-Done\n"); + *signature = sig; end: return retval; } -static inline int WaitTillCardIsFree(u16 base) -{ - unsigned long count = 0; - - while (!(inw(base + 0xe) & 0x1) && count++ < 100) - msleep(5); - - return !(inw(base + 0xe) & 0x1); -} - static int __devinit load_firmware(struct pci_dev *pdev, const unsigned int index, const unsigned int signature) { @@ -1708,6 +1692,11 @@ static int __devinit load_firmware(struct pci_dev *pdev, } data = kmalloc(word_count * 2, GFP_KERNEL); + if (data == NULL) { + dev_err(&pdev->dev, "Card%d, firmware upload " + "failed, not enough memory\n", index + 1); + goto errrelfw; + } inw(base); insw(base, data, word_count); InterruptTheCard(base); @@ -1756,7 +1745,7 @@ end: /* * Insmod can set static symbols so keep these static */ -static int card; +static unsigned int card_count; static int __devinit isicom_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -1766,7 +1755,7 @@ static int __devinit isicom_probe(struct pci_dev *pdev, u8 pciirq; struct isi_board *board = NULL; - if (card >= BOARD_COUNT) + if (card_count >= BOARD_COUNT) goto err; ioaddr = pci_resource_start(pdev, 3); @@ -1781,18 +1770,20 @@ static int __devinit isicom_probe(struct pci_dev *pdev, break; } + board->index = index; board->base = ioaddr; board->irq = pciirq; - card++; + card_count++; pci_set_drvdata(pdev, board); - if (!request_region(board->base, 16, ISICOM_NAME)) { + retval = pci_request_region(pdev, 3, ISICOM_NAME); + if (retval) { dev_err(&pdev->dev, "I/O Region 0x%lx-0x%lx is busy. Card%d " "will be disabled.\n", board->base, board->base + 15, index + 1); retval = -EBUSY; - goto err; + goto errdec; } retval = request_irq(board->irq, isicom_interrupt, @@ -1811,23 +1802,35 @@ static int __devinit isicom_probe(struct pci_dev *pdev, if (retval < 0) goto errunri; + for (index = 0; index < board->port_count; index++) + tty_register_device(isicom_normal, board->index * 16 + index, + &pdev->dev); + return 0; errunri: free_irq(board->irq, board); errunrr: - release_region(board->base, 16); -err: + pci_release_region(pdev, 3); +errdec: board->base = 0; + card_count--; +err: return retval; } static void __devexit isicom_remove(struct pci_dev *pdev) { struct isi_board *board = pci_get_drvdata(pdev); + unsigned int i; + + for (i = 0; i < board->port_count; i++) + tty_unregister_device(isicom_normal, board->index * 16 + i); free_irq(board->irq, board); - release_region(board->base, 16); + pci_release_region(pdev, 3); + board->base = 0; + card_count--; } static int __init isicom_init(void) @@ -1835,9 +1838,6 @@ static int __init isicom_init(void) int retval, idx, channel; struct isi_port *port; - card = 0; - memset(isi_ports, 0, sizeof(isi_ports)); - for(idx = 0; idx < BOARD_COUNT; idx++) { port = &isi_ports[idx * 16]; isi_card[idx].ports = port; @@ -1848,8 +1848,6 @@ static int __init isicom_init(void) port->channel = channel; port->close_delay = 50 * HZ/100; port->closing_wait = 3000 * HZ/100; - INIT_WORK(&port->hangup_tq, do_isicom_hangup); - INIT_WORK(&port->bh_tqueue, isicom_bottomhalf); port->status = 0; init_waitqueue_head(&port->open_wait); init_waitqueue_head(&port->close_wait); @@ -1875,7 +1873,8 @@ static int __init isicom_init(void) isicom_normal->init_termios = tty_std_termios; isicom_normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - isicom_normal->flags = TTY_DRIVER_REAL_RAW; + isicom_normal->flags = TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV; tty_set_operations(isicom_normal, &isicom_ops); retval = tty_register_driver(isicom_normal); @@ -1890,12 +1889,7 @@ static int __init isicom_init(void) goto err_unrtty; } - init_timer(&tx); - tx.expires = jiffies + 1; - tx.data = 0; - tx.function = isicom_tx; - re_schedule = 1; - add_timer(&tx); + mod_timer(&tx, jiffies + 1); return 0; err_unrtty: @@ -1908,12 +1902,9 @@ error: static void __exit isicom_exit(void) { - unsigned int index = 0; - re_schedule = 0; - while (re_schedule != 2 && index++ < 100) - msleep(10); + wait_for_completion_timeout(&isi_timerdone, HZ); pci_unregister_driver(&isicom_driver); tty_unregister_driver(isicom_normal);