From: Francois Romieu Workaround for lack of true reset: - devices/ports are put in silent mode at ifconfig down time but some state is kept around to allow 'ifconfig up' issuing at a later time. Device specific structures are allocated when the pci asic is probed: dscc4_init_ring() moves from dscc4_open() to dscc4_found1(). - try to use reset related board-specific feature at module removal time if available. Comments in the code explain the whole story. Not a complicated feature but it is unavailable on the cards I own and no user gave me feedback -> currently untested. It shouldn't harm anyway. 25-akpm/drivers/net/wan/Kconfig | 17 +++++- 25-akpm/drivers/net/wan/dscc4.c | 108 +++++++++++++++++++++++++++++++++++----- 2 files changed, 109 insertions(+), 16 deletions(-) diff -puN drivers/net/wan/dscc4.c~dscc4-5 drivers/net/wan/dscc4.c --- 25/drivers/net/wan/dscc4.c~dscc4-5 Fri Aug 15 13:54:21 2003 +++ 25-akpm/drivers/net/wan/dscc4.c Fri Aug 15 13:54:21 2003 @@ -112,6 +112,11 @@ static const char version[] = "$Id: dscc static int debug; static int quartz; +#ifdef CONFIG_DSCC4_PCI_RST +static DECLARE_MUTEX(dscc4_sem); +static u32 dscc4_pci_config_store[16]; +#endif + #define DRV_NAME "dscc4" #undef DSCC4_POLLING @@ -263,6 +268,10 @@ struct dscc4_dev_priv { #define IMR 0x54 #define ISR 0x58 +#define GPDIR 0x0400 +#define GPDATA 0x0404 +#define GPIM 0x0408 + /* Bit masks */ #define EncodingMask 0x00700000 #define CrcMask 0x00000003 @@ -328,10 +337,13 @@ struct dscc4_dev_priv { #define Arf 0x00000002 #define ArAck 0x00000001 -/* Misc */ +/* State flags */ +#define Ready 0x00000000 #define NeedIDR 0x00000001 #define NeedIDT 0x00000002 #define RdoSet 0x00000004 +#define FakeReset 0x00000008 + /* Don't mask RDO. Ever. */ #ifdef DSCC4_POLLING #define EventsMask 0xfffeef7f @@ -581,15 +593,18 @@ static inline int dscc4_xpr_ack(struct d return (i >= 0 ) ? i : -EAGAIN; } -/* Requires protection against interrupt */ static void dscc4_rx_reset(struct dscc4_dev_priv *dpriv, struct net_device *dev) { + unsigned long flags; + + spin_lock_irqsave(&dpriv->pci_priv->lock, flags); /* Cf errata DS5 p.6 */ writel(0x00000000, dev->base_addr + CH0LRDA + dpriv->dev_id*4); scc_patchl(PowerUp, 0, dpriv, dev, CCR0); readl(dev->base_addr + CH0LRDA + dpriv->dev_id*4); writel(MTFi|Rdr, dev->base_addr + dpriv->dev_id*0x0c + CH0CFG); writel(Action, dev->base_addr + GCMDR); + spin_unlock_irqrestore(&dpriv->pci_priv->lock, flags); } static void dscc4_tx_reset(struct dscc4_dev_priv *dpriv, struct net_device *dev) @@ -893,6 +908,10 @@ static int dscc4_found1(struct pci_dev * dscc4_init_registers(dpriv, d); dpriv->parity = PARITY_CRC16_PR0_CCITT; dpriv->encoding = ENCODING_NRZ; + if (dscc4_init_ring(d)) { + unregister_hdlc_device(hdlc); + goto err_unregister; + } } if (dscc4_set_quartz(root, quartz) < 0) goto err_unregister; @@ -902,8 +921,10 @@ static int dscc4_found1(struct pci_dev * return 0; err_unregister: - while (--i >= 0) + while (--i >= 0) { + dscc4_release_ring(root + i); unregister_hdlc_device(&root[i].hdlc); + } kfree(ppriv); err_free_dev: kfree(root); @@ -942,6 +963,46 @@ static int dscc4_loopback_check(struct d return 0; } +#ifdef CONFIG_DSCC4_PCI_RST +/* + * Some DSCC4-based cards wires the GPIO port and the PCI #RST pin together + * so as to provide a safe way to reset the asic while not the whole machine + * rebooting. + * + * This code doesn't need to be efficient. Keep It Simple + */ +static void dscc4_pci_reset(struct pci_dev *pdev, u32 ioaddr) +{ + int i; + + down(&dscc4_sem); + for (i = 0; i < 16; i++) + pci_read_config_dword(pdev, i << 2, dscc4_pci_config_store + i); + + /* Maximal LBI clock divider (who cares ?) and whole GPIO range. */ + writel(0x001c0000, ioaddr + GMODE); + /* Configure GPIO port as output */ + writel(0x0000ffff, ioaddr + GPDIR); + /* Disable interruption */ + writel(0x0000ffff, ioaddr + GPIM); + + writel(0x0000ffff, ioaddr + GPDATA); + writel(0x00000000, ioaddr + GPDATA); + + /* Flush posted writes */ + readl(ioaddr + GSTAR); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(10); + + for (i = 0; i < 16; i++) + pci_write_config_dword(pdev, i << 2, dscc4_pci_config_store[i]); + up(&dscc4_sem); +} +#else +#define dscc4_pci_reset(pdev,ioaddr) do {} while (0) +#endif /* CONFIG_DSCC4_PCI_RST */ + static int dscc4_open(struct net_device *dev) { struct dscc4_dev_priv *dpriv = dscc4_priv(dev); @@ -957,8 +1018,23 @@ static int dscc4_open(struct net_device ppriv = dpriv->pci_priv; - if ((ret = dscc4_init_ring(dev))) - goto err_out; + /* + * Due to various bugs, there is no way to reliably reset a + * specific port (manufacturer's dependant special PCI #RST wiring + * apart: it affects all ports). Thus the device goes in the best + * silent mode possible at dscc4_close() time and simply claims to + * be up if it's opened again. It still isn't possible to change + * the HDLC configuration without rebooting but at least the ports + * can be up/down ifconfig'ed without killing the host. + */ + if (dpriv->flags & FakeReset) { + dpriv->flags &= ~FakeReset; + scc_patchl(0, PowerUp, dpriv, dev, CCR0); + scc_patchl(0, 0x00050000, dpriv, dev, CCR2); + scc_writel(EventsMask, dpriv, dev, IMR); + printk(KERN_INFO "%s: up again.\n", dev->name); + goto done; + } /* IDT+IDR during XPR */ dpriv->flags = NeedIDR | NeedIDT; @@ -975,7 +1051,7 @@ static int dscc4_open(struct net_device if (scc_readl_star(dpriv, dev) & SccBusy) { printk(KERN_ERR "%s busy. Try later\n", dev->name); ret = -EAGAIN; - goto err_free_ring; + goto err_out; } else printk(KERN_INFO "%s: available. Good\n", dev->name); @@ -1002,6 +1078,7 @@ static int dscc4_open(struct net_device if (debug > 2) dscc4_tx_print(dev, dpriv, "Open"); +done: netif_start_queue(dev); init_timer(&dpriv->timer); @@ -1072,20 +1149,16 @@ static int dscc4_close(struct net_device { struct dscc4_dev_priv *dpriv = dscc4_priv(dev); hdlc_device *hdlc = dev_to_hdlc(dev); - unsigned long flags; del_timer_sync(&dpriv->timer); netif_stop_queue(dev); - spin_lock_irqsave(&dpriv->pci_priv->lock, flags); - dscc4_rx_reset(dpriv, dev); - spin_unlock_irqrestore(&dpriv->pci_priv->lock, flags); - - dscc4_tx_reset(dpriv, dev); - scc_patchl(PowerUp | Vis, 0, dpriv, dev, CCR0); + scc_patchl(0x00050000, 0, dpriv, dev, CCR2); scc_writel(0xffffffff, dpriv, dev, IMR); + dpriv->flags |= FakeReset; + hdlc_close(hdlc); dscc4_release_ring(dpriv); @@ -1228,6 +1301,11 @@ static int dscc4_ioctl(struct net_device if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (dpriv->flags & FakeReset) { + printk(KERN_INFO "%s: please reset the device" + "before this command\n", dev->name); + return -EPERM; + } if (copy_from_user(&dpriv->settings, line, size)) return -EFAULT; ret = dscc4_set_iface(dpriv, dev); @@ -1878,12 +1956,16 @@ static void __devexit dscc4_remove_one(s root = ppriv->root; ioaddr = hdlc_to_dev(&root->hdlc)->base_addr; + + dscc4_pci_reset(pdev, ioaddr); + free_irq(pdev->irq, root); pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), ppriv->iqcfg, ppriv->iqcfg_dma); for (i = 0; i < dev_per_card; i++) { struct dscc4_dev_priv *dpriv = root + i; + dscc4_release_ring(dpriv); pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), dpriv->iqrx, dpriv->iqrx_dma); pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), diff -puN drivers/net/wan/Kconfig~dscc4-5 drivers/net/wan/Kconfig --- 25/drivers/net/wan/Kconfig~dscc4-5 Fri Aug 15 13:54:21 2003 +++ 25-akpm/drivers/net/wan/Kconfig Fri Aug 15 13:54:21 2003 @@ -172,9 +172,6 @@ config COMX_PROTO_FR . The module will be called comx-proto-fr. -# -# The Etinc driver has not been tested as non-modular yet. -# config DSCC4 tristate "Etinc PCISYNC serial board support" depends on WAN && PCI && m @@ -189,6 +186,20 @@ config DSCC4 The module will be called dscc4. For general information about modules read . +config DSCC4_PCI_RST + bool "Hard reset support" + depends on DSCC4 + help + Various DSCC4 bugs forbid any reliable software reset of the asic. + As a replacement, some vendors provide a way to assert the PCI #RST + pin of DSCC4 through the GPIO port of the card. If you choose Y, + the driver will make use of this feature before module removal + (i.e. rmmod). + The feature is known to be available on Commtech's cards. + Contact your manufacturer for details. + + Say Y if your card supports this feature. + # # Lan Media's board. Currently 1000, 1200, 5200, 5245 # _